123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832 |
- #include <linux/capability.h>
- #include <linux/file.h>
- #include <linux/fdtable.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/security.h>
- #include <linux/slab.h>
- #include <linux/syscalls.h>
- #include <linux/time.h>
- #include <linux/rcupdate.h>
- #include <linux/pid_namespace.h>
- #include <linux/hashtable.h>
- #include <linux/percpu.h>
- #define CREATE_TRACE_POINTS
- #include <trace/events/filelock.h>
- #include <asm/uaccess.h>
- #define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
- #define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
- #define IS_LEASE(fl) (fl->fl_flags & (FL_LEASE|FL_DELEG|FL_LAYOUT))
- #define IS_OFDLCK(fl) (fl->fl_flags & FL_OFDLCK)
- static inline bool is_remote_lock(struct file *filp)
- {
- return likely(!(filp->f_path.dentry->d_sb->s_flags & MS_NOREMOTELOCK));
- }
- static bool lease_breaking(struct file_lock *fl)
- {
- return fl->fl_flags & (FL_UNLOCK_PENDING | FL_DOWNGRADE_PENDING);
- }
- static int target_leasetype(struct file_lock *fl)
- {
- if (fl->fl_flags & FL_UNLOCK_PENDING)
- return F_UNLCK;
- if (fl->fl_flags & FL_DOWNGRADE_PENDING)
- return F_RDLCK;
- return fl->fl_type;
- }
- int leases_enable = 1;
- int lease_break_time = 45;
- struct file_lock_list_struct {
- spinlock_t lock;
- struct hlist_head hlist;
- };
- static DEFINE_PER_CPU(struct file_lock_list_struct, file_lock_list);
- DEFINE_STATIC_PERCPU_RWSEM(file_rwsem);
- #define BLOCKED_HASH_BITS 7
- static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS);
- static DEFINE_SPINLOCK(blocked_lock_lock);
- static struct kmem_cache *flctx_cache __read_mostly;
- static struct kmem_cache *filelock_cache __read_mostly;
- static struct file_lock_context *
- locks_get_lock_context(struct inode *inode, int type)
- {
- struct file_lock_context *ctx;
-
- ctx = smp_load_acquire(&inode->i_flctx);
- if (likely(ctx) || type == F_UNLCK)
- goto out;
- ctx = kmem_cache_alloc(flctx_cache, GFP_KERNEL);
- if (!ctx)
- goto out;
- spin_lock_init(&ctx->flc_lock);
- INIT_LIST_HEAD(&ctx->flc_flock);
- INIT_LIST_HEAD(&ctx->flc_posix);
- INIT_LIST_HEAD(&ctx->flc_lease);
-
- if (cmpxchg(&inode->i_flctx, NULL, ctx)) {
- kmem_cache_free(flctx_cache, ctx);
- ctx = smp_load_acquire(&inode->i_flctx);
- }
- out:
- trace_locks_get_lock_context(inode, type, ctx);
- return ctx;
- }
- static void
- locks_dump_ctx_list(struct list_head *list, char *list_type)
- {
- struct file_lock *fl;
- list_for_each_entry(fl, list, fl_list) {
- pr_warn("%s: fl_owner=%p fl_flags=0x%x fl_type=0x%x fl_pid=%u\n", list_type, fl->fl_owner, fl->fl_flags, fl->fl_type, fl->fl_pid);
- }
- }
- static void
- locks_check_ctx_lists(struct inode *inode)
- {
- struct file_lock_context *ctx = inode->i_flctx;
- if (unlikely(!list_empty(&ctx->flc_flock) ||
- !list_empty(&ctx->flc_posix) ||
- !list_empty(&ctx->flc_lease))) {
- pr_warn("Leaked locks on dev=0x%x:0x%x ino=0x%lx:\n",
- MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev),
- inode->i_ino);
- locks_dump_ctx_list(&ctx->flc_flock, "FLOCK");
- locks_dump_ctx_list(&ctx->flc_posix, "POSIX");
- locks_dump_ctx_list(&ctx->flc_lease, "LEASE");
- }
- }
- void
- locks_free_lock_context(struct inode *inode)
- {
- struct file_lock_context *ctx = inode->i_flctx;
- if (unlikely(ctx)) {
- locks_check_ctx_lists(inode);
- kmem_cache_free(flctx_cache, ctx);
- }
- }
- static void locks_init_lock_heads(struct file_lock *fl)
- {
- INIT_HLIST_NODE(&fl->fl_link);
- INIT_LIST_HEAD(&fl->fl_list);
- INIT_LIST_HEAD(&fl->fl_block);
- init_waitqueue_head(&fl->fl_wait);
- }
- struct file_lock *locks_alloc_lock(void)
- {
- struct file_lock *fl = kmem_cache_zalloc(filelock_cache, GFP_KERNEL);
- if (fl)
- locks_init_lock_heads(fl);
- return fl;
- }
- EXPORT_SYMBOL_GPL(locks_alloc_lock);
- void locks_release_private(struct file_lock *fl)
- {
- if (fl->fl_ops) {
- if (fl->fl_ops->fl_release_private)
- fl->fl_ops->fl_release_private(fl);
- fl->fl_ops = NULL;
- }
- if (fl->fl_lmops) {
- if (fl->fl_lmops->lm_put_owner) {
- fl->fl_lmops->lm_put_owner(fl->fl_owner);
- fl->fl_owner = NULL;
- }
- fl->fl_lmops = NULL;
- }
- }
- EXPORT_SYMBOL_GPL(locks_release_private);
- void locks_free_lock(struct file_lock *fl)
- {
- BUG_ON(waitqueue_active(&fl->fl_wait));
- BUG_ON(!list_empty(&fl->fl_list));
- BUG_ON(!list_empty(&fl->fl_block));
- BUG_ON(!hlist_unhashed(&fl->fl_link));
- locks_release_private(fl);
- kmem_cache_free(filelock_cache, fl);
- }
- EXPORT_SYMBOL(locks_free_lock);
- static void
- locks_dispose_list(struct list_head *dispose)
- {
- struct file_lock *fl;
- while (!list_empty(dispose)) {
- fl = list_first_entry(dispose, struct file_lock, fl_list);
- list_del_init(&fl->fl_list);
- locks_free_lock(fl);
- }
- }
- void locks_init_lock(struct file_lock *fl)
- {
- memset(fl, 0, sizeof(struct file_lock));
- locks_init_lock_heads(fl);
- }
- EXPORT_SYMBOL(locks_init_lock);
- void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
- {
- new->fl_owner = fl->fl_owner;
- new->fl_pid = fl->fl_pid;
- new->fl_file = NULL;
- new->fl_flags = fl->fl_flags;
- new->fl_type = fl->fl_type;
- new->fl_start = fl->fl_start;
- new->fl_end = fl->fl_end;
- new->fl_lmops = fl->fl_lmops;
- new->fl_ops = NULL;
- if (fl->fl_lmops) {
- if (fl->fl_lmops->lm_get_owner)
- fl->fl_lmops->lm_get_owner(fl->fl_owner);
- }
- }
- EXPORT_SYMBOL(locks_copy_conflock);
- void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
- {
-
- WARN_ON_ONCE(new->fl_ops);
- locks_copy_conflock(new, fl);
- new->fl_file = fl->fl_file;
- new->fl_ops = fl->fl_ops;
- if (fl->fl_ops) {
- if (fl->fl_ops->fl_copy_lock)
- fl->fl_ops->fl_copy_lock(new, fl);
- }
- }
- EXPORT_SYMBOL(locks_copy_lock);
- static inline int flock_translate_cmd(int cmd) {
- if (cmd & LOCK_MAND)
- return cmd & (LOCK_MAND | LOCK_RW);
- switch (cmd) {
- case LOCK_SH:
- return F_RDLCK;
- case LOCK_EX:
- return F_WRLCK;
- case LOCK_UN:
- return F_UNLCK;
- }
- return -EINVAL;
- }
- static struct file_lock *
- flock_make_lock(struct file *filp, unsigned int cmd)
- {
- struct file_lock *fl;
- int type = flock_translate_cmd(cmd);
- if (type < 0)
- return ERR_PTR(type);
-
- fl = locks_alloc_lock();
- if (fl == NULL)
- return ERR_PTR(-ENOMEM);
- fl->fl_file = filp;
- fl->fl_owner = filp;
- fl->fl_pid = current->tgid;
- fl->fl_flags = FL_FLOCK;
- fl->fl_type = type;
- fl->fl_end = OFFSET_MAX;
-
- return fl;
- }
- static int assign_type(struct file_lock *fl, long type)
- {
- switch (type) {
- case F_RDLCK:
- case F_WRLCK:
- case F_UNLCK:
- fl->fl_type = type;
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
- struct flock64 *l)
- {
- switch (l->l_whence) {
- case SEEK_SET:
- fl->fl_start = 0;
- break;
- case SEEK_CUR:
- fl->fl_start = filp->f_pos;
- break;
- case SEEK_END:
- fl->fl_start = i_size_read(file_inode(filp));
- break;
- default:
- return -EINVAL;
- }
- if (l->l_start > OFFSET_MAX - fl->fl_start)
- return -EOVERFLOW;
- fl->fl_start += l->l_start;
- if (fl->fl_start < 0)
- return -EINVAL;
-
- if (l->l_len > 0) {
- if (l->l_len - 1 > OFFSET_MAX - fl->fl_start)
- return -EOVERFLOW;
- fl->fl_end = fl->fl_start + l->l_len - 1;
- } else if (l->l_len < 0) {
- if (fl->fl_start + l->l_len < 0)
- return -EINVAL;
- fl->fl_end = fl->fl_start - 1;
- fl->fl_start += l->l_len;
- } else
- fl->fl_end = OFFSET_MAX;
- fl->fl_owner = current->files;
- fl->fl_pid = current->tgid;
- fl->fl_file = filp;
- fl->fl_flags = FL_POSIX;
- fl->fl_ops = NULL;
- fl->fl_lmops = NULL;
- return assign_type(fl, l->l_type);
- }
- static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
- struct flock *l)
- {
- struct flock64 ll = {
- .l_type = l->l_type,
- .l_whence = l->l_whence,
- .l_start = l->l_start,
- .l_len = l->l_len,
- };
- return flock64_to_posix_lock(filp, fl, &ll);
- }
- static bool
- lease_break_callback(struct file_lock *fl)
- {
- kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG);
- return false;
- }
- static void
- lease_setup(struct file_lock *fl, void **priv)
- {
- struct file *filp = fl->fl_file;
- struct fasync_struct *fa = *priv;
-
- if (!fasync_insert_entry(fa->fa_fd, filp, &fl->fl_fasync, fa))
- *priv = NULL;
- __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
- }
- static const struct lock_manager_operations lease_manager_ops = {
- .lm_break = lease_break_callback,
- .lm_change = lease_modify,
- .lm_setup = lease_setup,
- };
- static int lease_init(struct file *filp, long type, struct file_lock *fl)
- {
- if (assign_type(fl, type) != 0)
- return -EINVAL;
- fl->fl_owner = filp;
- fl->fl_pid = current->tgid;
- fl->fl_file = filp;
- fl->fl_flags = FL_LEASE;
- fl->fl_start = 0;
- fl->fl_end = OFFSET_MAX;
- fl->fl_ops = NULL;
- fl->fl_lmops = &lease_manager_ops;
- return 0;
- }
- static struct file_lock *lease_alloc(struct file *filp, long type)
- {
- struct file_lock *fl = locks_alloc_lock();
- int error = -ENOMEM;
- if (fl == NULL)
- return ERR_PTR(error);
- error = lease_init(filp, type, fl);
- if (error) {
- locks_free_lock(fl);
- return ERR_PTR(error);
- }
- return fl;
- }
- static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
- {
- return ((fl1->fl_end >= fl2->fl_start) &&
- (fl2->fl_end >= fl1->fl_start));
- }
- static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
- {
- if (fl1->fl_lmops && fl1->fl_lmops->lm_compare_owner)
- return fl2->fl_lmops == fl1->fl_lmops &&
- fl1->fl_lmops->lm_compare_owner(fl1, fl2);
- return fl1->fl_owner == fl2->fl_owner;
- }
- static void locks_insert_global_locks(struct file_lock *fl)
- {
- struct file_lock_list_struct *fll = this_cpu_ptr(&file_lock_list);
- percpu_rwsem_assert_held(&file_rwsem);
- spin_lock(&fll->lock);
- fl->fl_link_cpu = smp_processor_id();
- hlist_add_head(&fl->fl_link, &fll->hlist);
- spin_unlock(&fll->lock);
- }
- static void locks_delete_global_locks(struct file_lock *fl)
- {
- struct file_lock_list_struct *fll;
- percpu_rwsem_assert_held(&file_rwsem);
-
- if (hlist_unhashed(&fl->fl_link))
- return;
- fll = per_cpu_ptr(&file_lock_list, fl->fl_link_cpu);
- spin_lock(&fll->lock);
- hlist_del_init(&fl->fl_link);
- spin_unlock(&fll->lock);
- }
- static unsigned long
- posix_owner_key(struct file_lock *fl)
- {
- if (fl->fl_lmops && fl->fl_lmops->lm_owner_key)
- return fl->fl_lmops->lm_owner_key(fl);
- return (unsigned long)fl->fl_owner;
- }
- static void locks_insert_global_blocked(struct file_lock *waiter)
- {
- lockdep_assert_held(&blocked_lock_lock);
- hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter));
- }
- static void locks_delete_global_blocked(struct file_lock *waiter)
- {
- lockdep_assert_held(&blocked_lock_lock);
- hash_del(&waiter->fl_link);
- }
- static void __locks_delete_block(struct file_lock *waiter)
- {
- locks_delete_global_blocked(waiter);
- list_del_init(&waiter->fl_block);
- waiter->fl_next = NULL;
- }
- static void locks_delete_block(struct file_lock *waiter)
- {
- spin_lock(&blocked_lock_lock);
- __locks_delete_block(waiter);
- spin_unlock(&blocked_lock_lock);
- }
- static void __locks_insert_block(struct file_lock *blocker,
- struct file_lock *waiter)
- {
- BUG_ON(!list_empty(&waiter->fl_block));
- waiter->fl_next = blocker;
- list_add_tail(&waiter->fl_block, &blocker->fl_block);
- if (IS_POSIX(blocker) && !IS_OFDLCK(blocker))
- locks_insert_global_blocked(waiter);
- }
- static void locks_insert_block(struct file_lock *blocker,
- struct file_lock *waiter)
- {
- spin_lock(&blocked_lock_lock);
- __locks_insert_block(blocker, waiter);
- spin_unlock(&blocked_lock_lock);
- }
- static void locks_wake_up_blocks(struct file_lock *blocker)
- {
-
- if (list_empty(&blocker->fl_block))
- return;
- spin_lock(&blocked_lock_lock);
- while (!list_empty(&blocker->fl_block)) {
- struct file_lock *waiter;
- waiter = list_first_entry(&blocker->fl_block,
- struct file_lock, fl_block);
- __locks_delete_block(waiter);
- if (waiter->fl_lmops && waiter->fl_lmops->lm_notify)
- waiter->fl_lmops->lm_notify(waiter);
- else
- wake_up(&waiter->fl_wait);
- }
- spin_unlock(&blocked_lock_lock);
- }
- static void
- locks_insert_lock_ctx(struct file_lock *fl, struct list_head *before)
- {
- fl->fl_nspid = get_pid(task_tgid(current));
- list_add_tail(&fl->fl_list, before);
- locks_insert_global_locks(fl);
- }
- static void
- locks_unlink_lock_ctx(struct file_lock *fl)
- {
- locks_delete_global_locks(fl);
- list_del_init(&fl->fl_list);
- if (fl->fl_nspid) {
- put_pid(fl->fl_nspid);
- fl->fl_nspid = NULL;
- }
- locks_wake_up_blocks(fl);
- }
- static void
- locks_delete_lock_ctx(struct file_lock *fl, struct list_head *dispose)
- {
- locks_unlink_lock_ctx(fl);
- if (dispose)
- list_add(&fl->fl_list, dispose);
- else
- locks_free_lock(fl);
- }
- static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
- {
- if (sys_fl->fl_type == F_WRLCK)
- return 1;
- if (caller_fl->fl_type == F_WRLCK)
- return 1;
- return 0;
- }
- static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
- {
-
- if (posix_same_owner(caller_fl, sys_fl))
- return (0);
-
- if (!locks_overlap(caller_fl, sys_fl))
- return 0;
- return (locks_conflict(caller_fl, sys_fl));
- }
- static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
- {
-
- if (caller_fl->fl_file == sys_fl->fl_file)
- return (0);
- if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
- return 0;
- return (locks_conflict(caller_fl, sys_fl));
- }
- void
- posix_test_lock(struct file *filp, struct file_lock *fl)
- {
- struct file_lock *cfl;
- struct file_lock_context *ctx;
- struct inode *inode = locks_inode(filp);
- ctx = smp_load_acquire(&inode->i_flctx);
- if (!ctx || list_empty_careful(&ctx->flc_posix)) {
- fl->fl_type = F_UNLCK;
- return;
- }
- spin_lock(&ctx->flc_lock);
- list_for_each_entry(cfl, &ctx->flc_posix, fl_list) {
- if (posix_locks_conflict(fl, cfl)) {
- locks_copy_conflock(fl, cfl);
- if (cfl->fl_nspid)
- fl->fl_pid = pid_vnr(cfl->fl_nspid);
- goto out;
- }
- }
- fl->fl_type = F_UNLCK;
- out:
- spin_unlock(&ctx->flc_lock);
- return;
- }
- EXPORT_SYMBOL(posix_test_lock);
- #define MAX_DEADLK_ITERATIONS 10
- static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl)
- {
- struct file_lock *fl;
- hash_for_each_possible(blocked_hash, fl, fl_link, posix_owner_key(block_fl)) {
- if (posix_same_owner(fl, block_fl))
- return fl->fl_next;
- }
- return NULL;
- }
- static int posix_locks_deadlock(struct file_lock *caller_fl,
- struct file_lock *block_fl)
- {
- int i = 0;
- lockdep_assert_held(&blocked_lock_lock);
-
- if (IS_OFDLCK(caller_fl))
- return 0;
- while ((block_fl = what_owner_is_waiting_for(block_fl))) {
- if (i++ > MAX_DEADLK_ITERATIONS)
- return 0;
- if (posix_same_owner(caller_fl, block_fl))
- return 1;
- }
- return 0;
- }
- static int flock_lock_inode(struct inode *inode, struct file_lock *request)
- {
- struct file_lock *new_fl = NULL;
- struct file_lock *fl;
- struct file_lock_context *ctx;
- int error = 0;
- bool found = false;
- LIST_HEAD(dispose);
- ctx = locks_get_lock_context(inode, request->fl_type);
- if (!ctx) {
- if (request->fl_type != F_UNLCK)
- return -ENOMEM;
- return (request->fl_flags & FL_EXISTS) ? -ENOENT : 0;
- }
- if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) {
- new_fl = locks_alloc_lock();
- if (!new_fl)
- return -ENOMEM;
- }
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
- if (request->fl_flags & FL_ACCESS)
- goto find_conflict;
- list_for_each_entry(fl, &ctx->flc_flock, fl_list) {
- if (request->fl_file != fl->fl_file)
- continue;
- if (request->fl_type == fl->fl_type)
- goto out;
- found = true;
- locks_delete_lock_ctx(fl, &dispose);
- break;
- }
- if (request->fl_type == F_UNLCK) {
- if ((request->fl_flags & FL_EXISTS) && !found)
- error = -ENOENT;
- goto out;
- }
- find_conflict:
- list_for_each_entry(fl, &ctx->flc_flock, fl_list) {
- if (!flock_locks_conflict(request, fl))
- continue;
- error = -EAGAIN;
- if (!(request->fl_flags & FL_SLEEP))
- goto out;
- error = FILE_LOCK_DEFERRED;
- locks_insert_block(fl, request);
- goto out;
- }
- if (request->fl_flags & FL_ACCESS)
- goto out;
- locks_copy_lock(new_fl, request);
- locks_insert_lock_ctx(new_fl, &ctx->flc_flock);
- new_fl = NULL;
- error = 0;
- out:
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
- if (new_fl)
- locks_free_lock(new_fl);
- locks_dispose_list(&dispose);
- return error;
- }
- static int posix_lock_inode(struct inode *inode, struct file_lock *request,
- struct file_lock *conflock)
- {
- struct file_lock *fl, *tmp;
- struct file_lock *new_fl = NULL;
- struct file_lock *new_fl2 = NULL;
- struct file_lock *left = NULL;
- struct file_lock *right = NULL;
- struct file_lock_context *ctx;
- int error;
- bool added = false;
- LIST_HEAD(dispose);
- ctx = locks_get_lock_context(inode, request->fl_type);
- if (!ctx)
- return (request->fl_type == F_UNLCK) ? 0 : -ENOMEM;
-
- if (!(request->fl_flags & FL_ACCESS) &&
- (request->fl_type != F_UNLCK ||
- request->fl_start != 0 || request->fl_end != OFFSET_MAX)) {
- new_fl = locks_alloc_lock();
- new_fl2 = locks_alloc_lock();
- }
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
-
- if (request->fl_type != F_UNLCK) {
- list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
- if (!posix_locks_conflict(request, fl))
- continue;
- if (conflock)
- locks_copy_conflock(conflock, fl);
- error = -EAGAIN;
- if (!(request->fl_flags & FL_SLEEP))
- goto out;
-
- error = -EDEADLK;
- spin_lock(&blocked_lock_lock);
- if (likely(!posix_locks_deadlock(request, fl))) {
- error = FILE_LOCK_DEFERRED;
- __locks_insert_block(fl, request);
- }
- spin_unlock(&blocked_lock_lock);
- goto out;
- }
- }
-
- error = 0;
- if (request->fl_flags & FL_ACCESS)
- goto out;
-
- list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
- if (posix_same_owner(request, fl))
- break;
- }
-
- list_for_each_entry_safe_from(fl, tmp, &ctx->flc_posix, fl_list) {
- if (!posix_same_owner(request, fl))
- break;
-
- if (request->fl_type == fl->fl_type) {
-
- if (fl->fl_end < request->fl_start - 1)
- continue;
-
- if (fl->fl_start - 1 > request->fl_end)
- break;
-
- if (fl->fl_start > request->fl_start)
- fl->fl_start = request->fl_start;
- else
- request->fl_start = fl->fl_start;
- if (fl->fl_end < request->fl_end)
- fl->fl_end = request->fl_end;
- else
- request->fl_end = fl->fl_end;
- if (added) {
- locks_delete_lock_ctx(fl, &dispose);
- continue;
- }
- request = fl;
- added = true;
- } else {
-
- if (fl->fl_end < request->fl_start)
- continue;
- if (fl->fl_start > request->fl_end)
- break;
- if (request->fl_type == F_UNLCK)
- added = true;
- if (fl->fl_start < request->fl_start)
- left = fl;
-
- if (fl->fl_end > request->fl_end) {
- right = fl;
- break;
- }
- if (fl->fl_start >= request->fl_start) {
-
- if (added) {
- locks_delete_lock_ctx(fl, &dispose);
- continue;
- }
-
- error = -ENOLCK;
- if (!new_fl)
- goto out;
- locks_copy_lock(new_fl, request);
- request = new_fl;
- new_fl = NULL;
- locks_insert_lock_ctx(request, &fl->fl_list);
- locks_delete_lock_ctx(fl, &dispose);
- added = true;
- }
- }
- }
-
- error = -ENOLCK;
- if (right && left == right && !new_fl2)
- goto out;
- error = 0;
- if (!added) {
- if (request->fl_type == F_UNLCK) {
- if (request->fl_flags & FL_EXISTS)
- error = -ENOENT;
- goto out;
- }
- if (!new_fl) {
- error = -ENOLCK;
- goto out;
- }
- locks_copy_lock(new_fl, request);
- locks_insert_lock_ctx(new_fl, &fl->fl_list);
- fl = new_fl;
- new_fl = NULL;
- }
- if (right) {
- if (left == right) {
-
- left = new_fl2;
- new_fl2 = NULL;
- locks_copy_lock(left, right);
- locks_insert_lock_ctx(left, &fl->fl_list);
- }
- right->fl_start = request->fl_end + 1;
- locks_wake_up_blocks(right);
- }
- if (left) {
- left->fl_end = request->fl_start - 1;
- locks_wake_up_blocks(left);
- }
- out:
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
-
- if (new_fl)
- locks_free_lock(new_fl);
- if (new_fl2)
- locks_free_lock(new_fl2);
- locks_dispose_list(&dispose);
- trace_posix_lock_inode(inode, request, error);
- return error;
- }
- int posix_lock_file(struct file *filp, struct file_lock *fl,
- struct file_lock *conflock)
- {
- return posix_lock_inode(locks_inode(filp), fl, conflock);
- }
- EXPORT_SYMBOL(posix_lock_file);
- static int posix_lock_inode_wait(struct inode *inode, struct file_lock *fl)
- {
- int error;
- might_sleep ();
- for (;;) {
- error = posix_lock_inode(inode, fl, NULL);
- if (error != FILE_LOCK_DEFERRED)
- break;
- error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
- if (!error)
- continue;
- locks_delete_block(fl);
- break;
- }
- return error;
- }
- #ifdef CONFIG_MANDATORY_FILE_LOCKING
- int locks_mandatory_locked(struct file *file)
- {
- int ret;
- struct inode *inode = locks_inode(file);
- struct file_lock_context *ctx;
- struct file_lock *fl;
- ctx = smp_load_acquire(&inode->i_flctx);
- if (!ctx || list_empty_careful(&ctx->flc_posix))
- return 0;
-
- spin_lock(&ctx->flc_lock);
- ret = 0;
- list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
- if (fl->fl_owner != current->files &&
- fl->fl_owner != file) {
- ret = -EAGAIN;
- break;
- }
- }
- spin_unlock(&ctx->flc_lock);
- return ret;
- }
- int locks_mandatory_area(struct inode *inode, struct file *filp, loff_t start,
- loff_t end, unsigned char type)
- {
- struct file_lock fl;
- int error;
- bool sleep = false;
- locks_init_lock(&fl);
- fl.fl_pid = current->tgid;
- fl.fl_file = filp;
- fl.fl_flags = FL_POSIX | FL_ACCESS;
- if (filp && !(filp->f_flags & O_NONBLOCK))
- sleep = true;
- fl.fl_type = type;
- fl.fl_start = start;
- fl.fl_end = end;
- for (;;) {
- if (filp) {
- fl.fl_owner = filp;
- fl.fl_flags &= ~FL_SLEEP;
- error = posix_lock_inode(inode, &fl, NULL);
- if (!error)
- break;
- }
- if (sleep)
- fl.fl_flags |= FL_SLEEP;
- fl.fl_owner = current->files;
- error = posix_lock_inode(inode, &fl, NULL);
- if (error != FILE_LOCK_DEFERRED)
- break;
- error = wait_event_interruptible(fl.fl_wait, !fl.fl_next);
- if (!error) {
-
- if (__mandatory_lock(inode))
- continue;
- }
- locks_delete_block(&fl);
- break;
- }
- return error;
- }
- EXPORT_SYMBOL(locks_mandatory_area);
- #endif
- static void lease_clear_pending(struct file_lock *fl, int arg)
- {
- switch (arg) {
- case F_UNLCK:
- fl->fl_flags &= ~FL_UNLOCK_PENDING;
-
- case F_RDLCK:
- fl->fl_flags &= ~FL_DOWNGRADE_PENDING;
- }
- }
- int lease_modify(struct file_lock *fl, int arg, struct list_head *dispose)
- {
- int error = assign_type(fl, arg);
- if (error)
- return error;
- lease_clear_pending(fl, arg);
- locks_wake_up_blocks(fl);
- if (arg == F_UNLCK) {
- struct file *filp = fl->fl_file;
- f_delown(filp);
- filp->f_owner.signum = 0;
- fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
- if (fl->fl_fasync != NULL) {
- printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
- fl->fl_fasync = NULL;
- }
- locks_delete_lock_ctx(fl, dispose);
- }
- return 0;
- }
- EXPORT_SYMBOL(lease_modify);
- static bool past_time(unsigned long then)
- {
- if (!then)
-
- return false;
- return time_after(jiffies, then);
- }
- static void time_out_leases(struct inode *inode, struct list_head *dispose)
- {
- struct file_lock_context *ctx = inode->i_flctx;
- struct file_lock *fl, *tmp;
- lockdep_assert_held(&ctx->flc_lock);
- list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list) {
- trace_time_out_leases(inode, fl);
- if (past_time(fl->fl_downgrade_time))
- lease_modify(fl, F_RDLCK, dispose);
- if (past_time(fl->fl_break_time))
- lease_modify(fl, F_UNLCK, dispose);
- }
- }
- static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
- {
- if ((breaker->fl_flags & FL_LAYOUT) != (lease->fl_flags & FL_LAYOUT))
- return false;
- if ((breaker->fl_flags & FL_DELEG) && (lease->fl_flags & FL_LEASE))
- return false;
- return locks_conflict(breaker, lease);
- }
- static bool
- any_leases_conflict(struct inode *inode, struct file_lock *breaker)
- {
- struct file_lock_context *ctx = inode->i_flctx;
- struct file_lock *fl;
- lockdep_assert_held(&ctx->flc_lock);
- list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
- if (leases_conflict(fl, breaker))
- return true;
- }
- return false;
- }
- int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
- {
- int error = 0;
- struct file_lock_context *ctx;
- struct file_lock *new_fl, *fl, *tmp;
- unsigned long break_time;
- int want_write = (mode & O_ACCMODE) != O_RDONLY;
- LIST_HEAD(dispose);
- new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
- if (IS_ERR(new_fl))
- return PTR_ERR(new_fl);
- new_fl->fl_flags = type;
-
- ctx = smp_load_acquire(&inode->i_flctx);
- if (!ctx) {
- WARN_ON_ONCE(1);
- return error;
- }
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
- time_out_leases(inode, &dispose);
- if (!any_leases_conflict(inode, new_fl))
- goto out;
- break_time = 0;
- if (lease_break_time > 0) {
- break_time = jiffies + lease_break_time * HZ;
- if (break_time == 0)
- break_time++;
- }
- list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list) {
- if (!leases_conflict(fl, new_fl))
- continue;
- if (want_write) {
- if (fl->fl_flags & FL_UNLOCK_PENDING)
- continue;
- fl->fl_flags |= FL_UNLOCK_PENDING;
- fl->fl_break_time = break_time;
- } else {
- if (lease_breaking(fl))
- continue;
- fl->fl_flags |= FL_DOWNGRADE_PENDING;
- fl->fl_downgrade_time = break_time;
- }
- if (fl->fl_lmops->lm_break(fl))
- locks_delete_lock_ctx(fl, &dispose);
- }
- if (list_empty(&ctx->flc_lease))
- goto out;
- if (mode & O_NONBLOCK) {
- trace_break_lease_noblock(inode, new_fl);
- error = -EWOULDBLOCK;
- goto out;
- }
- restart:
- fl = list_first_entry(&ctx->flc_lease, struct file_lock, fl_list);
- break_time = fl->fl_break_time;
- if (break_time != 0)
- break_time -= jiffies;
- if (break_time == 0)
- break_time++;
- locks_insert_block(fl, new_fl);
- trace_break_lease_block(inode, new_fl);
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
- locks_dispose_list(&dispose);
- error = wait_event_interruptible_timeout(new_fl->fl_wait,
- !new_fl->fl_next, break_time);
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
- trace_break_lease_unblock(inode, new_fl);
- locks_delete_block(new_fl);
- if (error >= 0) {
-
- if (error == 0)
- time_out_leases(inode, &dispose);
- if (any_leases_conflict(inode, new_fl))
- goto restart;
- error = 0;
- }
- out:
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
- locks_dispose_list(&dispose);
- locks_free_lock(new_fl);
- return error;
- }
- EXPORT_SYMBOL(__break_lease);
- void lease_get_mtime(struct inode *inode, struct timespec *time)
- {
- bool has_lease = false;
- struct file_lock_context *ctx;
- struct file_lock *fl;
- ctx = smp_load_acquire(&inode->i_flctx);
- if (ctx && !list_empty_careful(&ctx->flc_lease)) {
- spin_lock(&ctx->flc_lock);
- fl = list_first_entry_or_null(&ctx->flc_lease,
- struct file_lock, fl_list);
- if (fl && (fl->fl_type == F_WRLCK))
- has_lease = true;
- spin_unlock(&ctx->flc_lock);
- }
- if (has_lease)
- *time = current_time(inode);
- else
- *time = inode->i_mtime;
- }
- EXPORT_SYMBOL(lease_get_mtime);
- int fcntl_getlease(struct file *filp)
- {
- struct file_lock *fl;
- struct inode *inode = locks_inode(filp);
- struct file_lock_context *ctx;
- int type = F_UNLCK;
- LIST_HEAD(dispose);
- ctx = smp_load_acquire(&inode->i_flctx);
- if (ctx && !list_empty_careful(&ctx->flc_lease)) {
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
- time_out_leases(inode, &dispose);
- list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
- if (fl->fl_file != filp)
- continue;
- type = target_leasetype(fl);
- break;
- }
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
- locks_dispose_list(&dispose);
- }
- return type;
- }
- static int
- check_conflicting_open(const struct dentry *dentry, const long arg, int flags)
- {
- int ret = 0;
- struct inode *inode = dentry->d_inode;
- if (flags & FL_LAYOUT)
- return 0;
- if ((arg == F_RDLCK) &&
- (atomic_read(&d_real_inode(dentry)->i_writecount) > 0))
- return -EAGAIN;
- if ((arg == F_WRLCK) && ((d_count(dentry) > 1) ||
- (atomic_read(&inode->i_count) > 1)))
- ret = -EAGAIN;
- return ret;
- }
- static int
- generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **priv)
- {
- struct file_lock *fl, *my_fl = NULL, *lease;
- struct dentry *dentry = filp->f_path.dentry;
- struct inode *inode = dentry->d_inode;
- struct file_lock_context *ctx;
- bool is_deleg = (*flp)->fl_flags & FL_DELEG;
- int error;
- LIST_HEAD(dispose);
- lease = *flp;
- trace_generic_add_lease(inode, lease);
-
- ctx = locks_get_lock_context(inode, arg);
- if (!ctx)
- return -ENOMEM;
-
- if (is_deleg && !inode_trylock(inode))
- return -EAGAIN;
- if (is_deleg && arg == F_WRLCK) {
-
- inode_unlock(inode);
- WARN_ON_ONCE(1);
- return -EINVAL;
- }
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
- time_out_leases(inode, &dispose);
- error = check_conflicting_open(dentry, arg, lease->fl_flags);
- if (error)
- goto out;
-
- error = -EAGAIN;
- list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
- if (fl->fl_file == filp &&
- fl->fl_owner == lease->fl_owner) {
- my_fl = fl;
- continue;
- }
-
- if (arg == F_WRLCK)
- goto out;
-
- if (fl->fl_flags & FL_UNLOCK_PENDING)
- goto out;
- }
- if (my_fl != NULL) {
- lease = my_fl;
- error = lease->fl_lmops->lm_change(lease, arg, &dispose);
- if (error)
- goto out;
- goto out_setup;
- }
- error = -EINVAL;
- if (!leases_enable)
- goto out;
- locks_insert_lock_ctx(lease, &ctx->flc_lease);
-
- smp_mb();
- error = check_conflicting_open(dentry, arg, lease->fl_flags);
- if (error) {
- locks_unlink_lock_ctx(lease);
- goto out;
- }
- out_setup:
- if (lease->fl_lmops->lm_setup)
- lease->fl_lmops->lm_setup(lease, priv);
- out:
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
- locks_dispose_list(&dispose);
- if (is_deleg)
- inode_unlock(inode);
- if (!error && !my_fl)
- *flp = NULL;
- return error;
- }
- static int generic_delete_lease(struct file *filp, void *owner)
- {
- int error = -EAGAIN;
- struct file_lock *fl, *victim = NULL;
- struct inode *inode = locks_inode(filp);
- struct file_lock_context *ctx;
- LIST_HEAD(dispose);
- ctx = smp_load_acquire(&inode->i_flctx);
- if (!ctx) {
- trace_generic_delete_lease(inode, NULL);
- return error;
- }
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
- list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
- if (fl->fl_file == filp &&
- fl->fl_owner == owner) {
- victim = fl;
- break;
- }
- }
- trace_generic_delete_lease(inode, victim);
- if (victim)
- error = fl->fl_lmops->lm_change(victim, F_UNLCK, &dispose);
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
- locks_dispose_list(&dispose);
- return error;
- }
- int generic_setlease(struct file *filp, long arg, struct file_lock **flp,
- void **priv)
- {
- struct inode *inode = locks_inode(filp);
- int error;
- if ((!uid_eq(current_fsuid(), inode->i_uid)) && !capable(CAP_LEASE))
- return -EACCES;
- if (!S_ISREG(inode->i_mode))
- return -EINVAL;
- error = security_file_lock(filp, arg);
- if (error)
- return error;
- switch (arg) {
- case F_UNLCK:
- return generic_delete_lease(filp, *priv);
- case F_RDLCK:
- case F_WRLCK:
- if (!(*flp)->fl_lmops->lm_break) {
- WARN_ON_ONCE(1);
- return -ENOLCK;
- }
- return generic_add_lease(filp, arg, flp, priv);
- default:
- return -EINVAL;
- }
- }
- EXPORT_SYMBOL(generic_setlease);
- int
- vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **priv)
- {
- if (filp->f_op->setlease && is_remote_lock(filp))
- return filp->f_op->setlease(filp, arg, lease, priv);
- else
- return generic_setlease(filp, arg, lease, priv);
- }
- EXPORT_SYMBOL_GPL(vfs_setlease);
- static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
- {
- struct file_lock *fl;
- struct fasync_struct *new;
- int error;
- fl = lease_alloc(filp, arg);
- if (IS_ERR(fl))
- return PTR_ERR(fl);
- new = fasync_alloc();
- if (!new) {
- locks_free_lock(fl);
- return -ENOMEM;
- }
- new->fa_fd = fd;
- error = vfs_setlease(filp, arg, &fl, (void **)&new);
- if (fl)
- locks_free_lock(fl);
- if (new)
- fasync_free(new);
- return error;
- }
- int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
- {
- if (arg == F_UNLCK)
- return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
- return do_fcntl_add_lease(fd, filp, arg);
- }
- static int flock_lock_inode_wait(struct inode *inode, struct file_lock *fl)
- {
- int error;
- might_sleep();
- for (;;) {
- error = flock_lock_inode(inode, fl);
- if (error != FILE_LOCK_DEFERRED)
- break;
- error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
- if (!error)
- continue;
- locks_delete_block(fl);
- break;
- }
- return error;
- }
- int locks_lock_inode_wait(struct inode *inode, struct file_lock *fl)
- {
- int res = 0;
- switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
- case FL_POSIX:
- res = posix_lock_inode_wait(inode, fl);
- break;
- case FL_FLOCK:
- res = flock_lock_inode_wait(inode, fl);
- break;
- default:
- BUG();
- }
- return res;
- }
- EXPORT_SYMBOL(locks_lock_inode_wait);
- SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
- {
- struct fd f = fdget(fd);
- struct file_lock *lock;
- int can_sleep, unlock;
- int error;
- error = -EBADF;
- if (!f.file)
- goto out;
- can_sleep = !(cmd & LOCK_NB);
- cmd &= ~LOCK_NB;
- unlock = (cmd == LOCK_UN);
- if (!unlock && !(cmd & LOCK_MAND) &&
- !(f.file->f_mode & (FMODE_READ|FMODE_WRITE)))
- goto out_putf;
- lock = flock_make_lock(f.file, cmd);
- if (IS_ERR(lock)) {
- error = PTR_ERR(lock);
- goto out_putf;
- }
- if (can_sleep)
- lock->fl_flags |= FL_SLEEP;
- error = security_file_lock(f.file, lock->fl_type);
- if (error)
- goto out_free;
- if (f.file->f_op->flock && is_remote_lock(f.file))
- error = f.file->f_op->flock(f.file,
- (can_sleep) ? F_SETLKW : F_SETLK,
- lock);
- else
- error = locks_lock_file_wait(f.file, lock);
- out_free:
- locks_free_lock(lock);
- out_putf:
- fdput(f);
- out:
- return error;
- }
- int vfs_test_lock(struct file *filp, struct file_lock *fl)
- {
- if (filp->f_op->lock && is_remote_lock(filp))
- return filp->f_op->lock(filp, F_GETLK, fl);
- posix_test_lock(filp, fl);
- return 0;
- }
- EXPORT_SYMBOL_GPL(vfs_test_lock);
- static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
- {
- flock->l_pid = IS_OFDLCK(fl) ? -1 : fl->fl_pid;
- #if BITS_PER_LONG == 32
-
- if (fl->fl_start > OFFT_OFFSET_MAX)
- return -EOVERFLOW;
- if (fl->fl_end != OFFSET_MAX && fl->fl_end > OFFT_OFFSET_MAX)
- return -EOVERFLOW;
- #endif
- flock->l_start = fl->fl_start;
- flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
- fl->fl_end - fl->fl_start + 1;
- flock->l_whence = 0;
- flock->l_type = fl->fl_type;
- return 0;
- }
- #if BITS_PER_LONG == 32
- static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
- {
- flock->l_pid = IS_OFDLCK(fl) ? -1 : fl->fl_pid;
- flock->l_start = fl->fl_start;
- flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
- fl->fl_end - fl->fl_start + 1;
- flock->l_whence = 0;
- flock->l_type = fl->fl_type;
- }
- #endif
- int fcntl_getlk(struct file *filp, unsigned int cmd, struct flock __user *l)
- {
- struct file_lock file_lock;
- struct flock flock;
- int error;
- error = -EFAULT;
- if (copy_from_user(&flock, l, sizeof(flock)))
- goto out;
- error = -EINVAL;
- if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
- goto out;
- error = flock_to_posix_lock(filp, &file_lock, &flock);
- if (error)
- goto out;
- if (cmd == F_OFD_GETLK) {
- error = -EINVAL;
- if (flock.l_pid != 0)
- goto out;
- cmd = F_GETLK;
- file_lock.fl_flags |= FL_OFDLCK;
- file_lock.fl_owner = filp;
- }
- error = vfs_test_lock(filp, &file_lock);
- if (error)
- goto out;
-
- flock.l_type = file_lock.fl_type;
- if (file_lock.fl_type != F_UNLCK) {
- error = posix_lock_to_flock(&flock, &file_lock);
- if (error)
- goto rel_priv;
- }
- error = -EFAULT;
- if (!copy_to_user(l, &flock, sizeof(flock)))
- error = 0;
- rel_priv:
- locks_release_private(&file_lock);
- out:
- return error;
- }
- int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf)
- {
- if (filp->f_op->lock && is_remote_lock(filp))
- return filp->f_op->lock(filp, cmd, fl);
- else
- return posix_lock_file(filp, fl, conf);
- }
- EXPORT_SYMBOL_GPL(vfs_lock_file);
- static int do_lock_file_wait(struct file *filp, unsigned int cmd,
- struct file_lock *fl)
- {
- int error;
- error = security_file_lock(filp, fl->fl_type);
- if (error)
- return error;
- for (;;) {
- error = vfs_lock_file(filp, cmd, fl, NULL);
- if (error != FILE_LOCK_DEFERRED)
- break;
- error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
- if (!error)
- continue;
- locks_delete_block(fl);
- break;
- }
- return error;
- }
- static int
- check_fmode_for_setlk(struct file_lock *fl)
- {
- switch (fl->fl_type) {
- case F_RDLCK:
- if (!(fl->fl_file->f_mode & FMODE_READ))
- return -EBADF;
- break;
- case F_WRLCK:
- if (!(fl->fl_file->f_mode & FMODE_WRITE))
- return -EBADF;
- }
- return 0;
- }
- int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
- struct flock __user *l)
- {
- struct file_lock *file_lock = locks_alloc_lock();
- struct flock flock;
- struct inode *inode;
- struct file *f;
- int error;
- if (file_lock == NULL)
- return -ENOLCK;
- inode = locks_inode(filp);
-
- error = -EFAULT;
- if (copy_from_user(&flock, l, sizeof(flock)))
- goto out;
-
- if (mandatory_lock(inode) && mapping_writably_mapped(filp->f_mapping)) {
- error = -EAGAIN;
- goto out;
- }
- error = flock_to_posix_lock(filp, file_lock, &flock);
- if (error)
- goto out;
- error = check_fmode_for_setlk(file_lock);
- if (error)
- goto out;
-
- switch (cmd) {
- case F_OFD_SETLK:
- error = -EINVAL;
- if (flock.l_pid != 0)
- goto out;
- cmd = F_SETLK;
- file_lock->fl_flags |= FL_OFDLCK;
- file_lock->fl_owner = filp;
- break;
- case F_OFD_SETLKW:
- error = -EINVAL;
- if (flock.l_pid != 0)
- goto out;
- cmd = F_SETLKW;
- file_lock->fl_flags |= FL_OFDLCK;
- file_lock->fl_owner = filp;
-
- case F_SETLKW:
- file_lock->fl_flags |= FL_SLEEP;
- }
- error = do_lock_file_wait(filp, cmd, file_lock);
-
- if (!error && file_lock->fl_type != F_UNLCK &&
- !(file_lock->fl_flags & FL_OFDLCK)) {
-
- spin_lock(¤t->files->file_lock);
- f = fcheck(fd);
- spin_unlock(¤t->files->file_lock);
- if (f != filp) {
- file_lock->fl_type = F_UNLCK;
- error = do_lock_file_wait(filp, cmd, file_lock);
- WARN_ON_ONCE(error);
- error = -EBADF;
- }
- }
- out:
- trace_fcntl_setlk(inode, file_lock, error);
- locks_free_lock(file_lock);
- return error;
- }
- #if BITS_PER_LONG == 32
- int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
- {
- struct file_lock file_lock;
- struct flock64 flock;
- int error;
- error = -EFAULT;
- if (copy_from_user(&flock, l, sizeof(flock)))
- goto out;
- error = -EINVAL;
- if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
- goto out;
- error = flock64_to_posix_lock(filp, &file_lock, &flock);
- if (error)
- goto out;
- if (cmd == F_OFD_GETLK) {
- error = -EINVAL;
- if (flock.l_pid != 0)
- goto out;
- cmd = F_GETLK64;
- file_lock.fl_flags |= FL_OFDLCK;
- file_lock.fl_owner = filp;
- }
- error = vfs_test_lock(filp, &file_lock);
- if (error)
- goto out;
- flock.l_type = file_lock.fl_type;
- if (file_lock.fl_type != F_UNLCK)
- posix_lock_to_flock64(&flock, &file_lock);
- error = -EFAULT;
- if (!copy_to_user(l, &flock, sizeof(flock)))
- error = 0;
- locks_release_private(&file_lock);
- out:
- return error;
- }
- int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
- struct flock64 __user *l)
- {
- struct file_lock *file_lock = locks_alloc_lock();
- struct flock64 flock;
- struct inode *inode;
- struct file *f;
- int error;
- if (file_lock == NULL)
- return -ENOLCK;
-
- error = -EFAULT;
- if (copy_from_user(&flock, l, sizeof(flock)))
- goto out;
- inode = locks_inode(filp);
-
- if (mandatory_lock(inode) && mapping_writably_mapped(filp->f_mapping)) {
- error = -EAGAIN;
- goto out;
- }
- error = flock64_to_posix_lock(filp, file_lock, &flock);
- if (error)
- goto out;
- error = check_fmode_for_setlk(file_lock);
- if (error)
- goto out;
-
- switch (cmd) {
- case F_OFD_SETLK:
- error = -EINVAL;
- if (flock.l_pid != 0)
- goto out;
- cmd = F_SETLK64;
- file_lock->fl_flags |= FL_OFDLCK;
- file_lock->fl_owner = filp;
- break;
- case F_OFD_SETLKW:
- error = -EINVAL;
- if (flock.l_pid != 0)
- goto out;
- cmd = F_SETLKW64;
- file_lock->fl_flags |= FL_OFDLCK;
- file_lock->fl_owner = filp;
-
- case F_SETLKW64:
- file_lock->fl_flags |= FL_SLEEP;
- }
- error = do_lock_file_wait(filp, cmd, file_lock);
-
- if (!error && file_lock->fl_type != F_UNLCK &&
- !(file_lock->fl_flags & FL_OFDLCK)) {
-
- spin_lock(¤t->files->file_lock);
- f = fcheck(fd);
- spin_unlock(¤t->files->file_lock);
- if (f != filp) {
- file_lock->fl_type = F_UNLCK;
- error = do_lock_file_wait(filp, cmd, file_lock);
- WARN_ON_ONCE(error);
- error = -EBADF;
- }
- }
- out:
- locks_free_lock(file_lock);
- return error;
- }
- #endif
- void locks_remove_posix(struct file *filp, fl_owner_t owner)
- {
- int error;
- struct inode *inode = locks_inode(filp);
- struct file_lock lock;
- struct file_lock_context *ctx;
-
- ctx = smp_load_acquire(&inode->i_flctx);
- if (!ctx || list_empty(&ctx->flc_posix))
- return;
- lock.fl_type = F_UNLCK;
- lock.fl_flags = FL_POSIX | FL_CLOSE;
- lock.fl_start = 0;
- lock.fl_end = OFFSET_MAX;
- lock.fl_owner = owner;
- lock.fl_pid = current->tgid;
- lock.fl_file = filp;
- lock.fl_ops = NULL;
- lock.fl_lmops = NULL;
- error = vfs_lock_file(filp, F_SETLK, &lock, NULL);
- if (lock.fl_ops && lock.fl_ops->fl_release_private)
- lock.fl_ops->fl_release_private(&lock);
- trace_locks_remove_posix(inode, &lock, error);
- }
- EXPORT_SYMBOL(locks_remove_posix);
- static void
- locks_remove_flock(struct file *filp, struct file_lock_context *flctx)
- {
- struct file_lock fl = {
- .fl_owner = filp,
- .fl_pid = current->tgid,
- .fl_file = filp,
- .fl_flags = FL_FLOCK,
- .fl_type = F_UNLCK,
- .fl_end = OFFSET_MAX,
- };
- struct inode *inode = locks_inode(filp);
- if (list_empty(&flctx->flc_flock))
- return;
- if (filp->f_op->flock && is_remote_lock(filp))
- filp->f_op->flock(filp, F_SETLKW, &fl);
- else
- flock_lock_inode(inode, &fl);
- if (fl.fl_ops && fl.fl_ops->fl_release_private)
- fl.fl_ops->fl_release_private(&fl);
- }
- static void
- locks_remove_lease(struct file *filp, struct file_lock_context *ctx)
- {
- struct file_lock *fl, *tmp;
- LIST_HEAD(dispose);
- if (list_empty(&ctx->flc_lease))
- return;
- percpu_down_read_preempt_disable(&file_rwsem);
- spin_lock(&ctx->flc_lock);
- list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list)
- if (filp == fl->fl_file)
- lease_modify(fl, F_UNLCK, &dispose);
- spin_unlock(&ctx->flc_lock);
- percpu_up_read_preempt_enable(&file_rwsem);
- locks_dispose_list(&dispose);
- }
- void locks_remove_file(struct file *filp)
- {
- struct file_lock_context *ctx;
- ctx = smp_load_acquire(&locks_inode(filp)->i_flctx);
- if (!ctx)
- return;
-
- locks_remove_posix(filp, filp);
-
- locks_remove_flock(filp, ctx);
-
- locks_remove_lease(filp, ctx);
- }
- int
- posix_unblock_lock(struct file_lock *waiter)
- {
- int status = 0;
- spin_lock(&blocked_lock_lock);
- if (waiter->fl_next)
- __locks_delete_block(waiter);
- else
- status = -ENOENT;
- spin_unlock(&blocked_lock_lock);
- return status;
- }
- EXPORT_SYMBOL(posix_unblock_lock);
- int vfs_cancel_lock(struct file *filp, struct file_lock *fl)
- {
- if (filp->f_op->lock && is_remote_lock(filp))
- return filp->f_op->lock(filp, F_CANCELLK, fl);
- return 0;
- }
- EXPORT_SYMBOL_GPL(vfs_cancel_lock);
- #ifdef CONFIG_PROC_FS
- #include <linux/proc_fs.h>
- #include <linux/seq_file.h>
- struct locks_iterator {
- int li_cpu;
- loff_t li_pos;
- };
- static void lock_get_status(struct seq_file *f, struct file_lock *fl,
- loff_t id, char *pfx)
- {
- struct inode *inode = NULL;
- unsigned int fl_pid;
- if (fl->fl_nspid) {
- struct pid_namespace *proc_pidns = file_inode(f->file)->i_sb->s_fs_info;
-
- fl_pid = pid_nr_ns(fl->fl_nspid, proc_pidns);
-
- if (fl_pid == 0)
- return;
- } else
- fl_pid = fl->fl_pid;
- if (fl->fl_file != NULL)
- inode = locks_inode(fl->fl_file);
- seq_printf(f, "%lld:%s ", id, pfx);
- if (IS_POSIX(fl)) {
- if (fl->fl_flags & FL_ACCESS)
- seq_puts(f, "ACCESS");
- else if (IS_OFDLCK(fl))
- seq_puts(f, "OFDLCK");
- else
- seq_puts(f, "POSIX ");
- seq_printf(f, " %s ",
- (inode == NULL) ? "*NOINODE*" :
- mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
- } else if (IS_FLOCK(fl)) {
- if (fl->fl_type & LOCK_MAND) {
- seq_puts(f, "FLOCK MSNFS ");
- } else {
- seq_puts(f, "FLOCK ADVISORY ");
- }
- } else if (IS_LEASE(fl)) {
- if (fl->fl_flags & FL_DELEG)
- seq_puts(f, "DELEG ");
- else
- seq_puts(f, "LEASE ");
- if (lease_breaking(fl))
- seq_puts(f, "BREAKING ");
- else if (fl->fl_file)
- seq_puts(f, "ACTIVE ");
- else
- seq_puts(f, "BREAKER ");
- } else {
- seq_puts(f, "UNKNOWN UNKNOWN ");
- }
- if (fl->fl_type & LOCK_MAND) {
- seq_printf(f, "%s ",
- (fl->fl_type & LOCK_READ)
- ? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ "
- : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
- } else {
- seq_printf(f, "%s ",
- (lease_breaking(fl))
- ? (fl->fl_type == F_UNLCK) ? "UNLCK" : "READ "
- : (fl->fl_type == F_WRLCK) ? "WRITE" : "READ ");
- }
- if (inode) {
-
- seq_printf(f, "%d %02x:%02x:%ld ", fl_pid,
- MAJOR(inode->i_sb->s_dev),
- MINOR(inode->i_sb->s_dev), inode->i_ino);
- } else {
- seq_printf(f, "%d <none>:0 ", fl_pid);
- }
- if (IS_POSIX(fl)) {
- if (fl->fl_end == OFFSET_MAX)
- seq_printf(f, "%Ld EOF\n", fl->fl_start);
- else
- seq_printf(f, "%Ld %Ld\n", fl->fl_start, fl->fl_end);
- } else {
- seq_puts(f, "0 EOF\n");
- }
- }
- static int locks_show(struct seq_file *f, void *v)
- {
- struct locks_iterator *iter = f->private;
- struct file_lock *fl, *bfl;
- struct pid_namespace *proc_pidns = file_inode(f->file)->i_sb->s_fs_info;
- fl = hlist_entry(v, struct file_lock, fl_link);
- if (fl->fl_nspid && !pid_nr_ns(fl->fl_nspid, proc_pidns))
- return 0;
- lock_get_status(f, fl, iter->li_pos, "");
- list_for_each_entry(bfl, &fl->fl_block, fl_block)
- lock_get_status(f, bfl, iter->li_pos, " ->");
- return 0;
- }
- static void __show_fd_locks(struct seq_file *f,
- struct list_head *head, int *id,
- struct file *filp, struct files_struct *files)
- {
- struct file_lock *fl;
- list_for_each_entry(fl, head, fl_list) {
- if (filp != fl->fl_file)
- continue;
- if (fl->fl_owner != files &&
- fl->fl_owner != filp)
- continue;
- (*id)++;
- seq_puts(f, "lock:\t");
- lock_get_status(f, fl, *id, "");
- }
- }
- void show_fd_locks(struct seq_file *f,
- struct file *filp, struct files_struct *files)
- {
- struct inode *inode = locks_inode(filp);
- struct file_lock_context *ctx;
- int id = 0;
- ctx = smp_load_acquire(&inode->i_flctx);
- if (!ctx)
- return;
- spin_lock(&ctx->flc_lock);
- __show_fd_locks(f, &ctx->flc_flock, &id, filp, files);
- __show_fd_locks(f, &ctx->flc_posix, &id, filp, files);
- __show_fd_locks(f, &ctx->flc_lease, &id, filp, files);
- spin_unlock(&ctx->flc_lock);
- }
- static void *locks_start(struct seq_file *f, loff_t *pos)
- __acquires(&blocked_lock_lock)
- {
- struct locks_iterator *iter = f->private;
- iter->li_pos = *pos + 1;
- percpu_down_write(&file_rwsem);
- spin_lock(&blocked_lock_lock);
- return seq_hlist_start_percpu(&file_lock_list.hlist, &iter->li_cpu, *pos);
- }
- static void *locks_next(struct seq_file *f, void *v, loff_t *pos)
- {
- struct locks_iterator *iter = f->private;
- ++iter->li_pos;
- return seq_hlist_next_percpu(v, &file_lock_list.hlist, &iter->li_cpu, pos);
- }
- static void locks_stop(struct seq_file *f, void *v)
- __releases(&blocked_lock_lock)
- {
- spin_unlock(&blocked_lock_lock);
- percpu_up_write(&file_rwsem);
- }
- static const struct seq_operations locks_seq_operations = {
- .start = locks_start,
- .next = locks_next,
- .stop = locks_stop,
- .show = locks_show,
- };
- static int locks_open(struct inode *inode, struct file *filp)
- {
- return seq_open_private(filp, &locks_seq_operations,
- sizeof(struct locks_iterator));
- }
- static const struct file_operations proc_locks_operations = {
- .open = locks_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release_private,
- };
- static int __init proc_locks_init(void)
- {
- proc_create("locks", 0, NULL, &proc_locks_operations);
- return 0;
- }
- fs_initcall(proc_locks_init);
- #endif
- static int __init filelock_init(void)
- {
- int i;
- flctx_cache = kmem_cache_create("file_lock_ctx",
- sizeof(struct file_lock_context), 0, SLAB_PANIC, NULL);
- filelock_cache = kmem_cache_create("file_lock_cache",
- sizeof(struct file_lock), 0, SLAB_PANIC, NULL);
- for_each_possible_cpu(i) {
- struct file_lock_list_struct *fll = per_cpu_ptr(&file_lock_list, i);
- spin_lock_init(&fll->lock);
- INIT_HLIST_HEAD(&fll->hlist);
- }
- return 0;
- }
- core_initcall(filelock_init);
|