X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Flocks.c;h=3098b3f622b6f897f88cb01bfd765751ebf2d1f4;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=da593493962c037ea03bca134ab3e1ef39a6b985;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/locks.c b/fs/locks.c index da5934939..3098b3f62 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -60,7 +60,7 @@ * * Initial implementation of mandatory locks. SunOS turned out to be * a rotten model, so I implemented the "obvious" semantics. - * See 'linux/Documentation/mandatory.txt' for details. + * See 'Documentation/mandatory.txt' for details. * Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996. * * Don't allow mandatory locks on mmap()'ed files. Added simple functions to @@ -122,7 +122,9 @@ #include #include #include +#include #include +#include #include #include @@ -148,12 +150,16 @@ static kmem_cache_t *filelock_cache; /* Allocate an empty lock structure. */ static struct file_lock *locks_alloc_lock(void) { + if (!vx_locks_avail(1)) + return NULL; return kmem_cache_alloc(filelock_cache, SLAB_KERNEL); } /* Free a lock which is not in use. */ static inline void locks_free_lock(struct file_lock *fl) { + vx_locks_dec(fl); + if (fl == NULL) { BUG(); return; @@ -167,6 +173,18 @@ static inline void locks_free_lock(struct file_lock *fl) if (!list_empty(&fl->fl_link)) panic("Attempting to free lock on active lock list"); + 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->fl_release_private) + fl->fl_lmops->fl_release_private(fl); + fl->fl_lmops = NULL; + } + kmem_cache_free(filelock_cache, fl); } @@ -177,15 +195,15 @@ void locks_init_lock(struct file_lock *fl) init_waitqueue_head(&fl->fl_wait); fl->fl_next = NULL; fl->fl_fasync = NULL; - fl->fl_owner = 0; + fl->fl_owner = NULL; fl->fl_pid = 0; fl->fl_file = NULL; fl->fl_flags = 0; fl->fl_type = 0; fl->fl_start = fl->fl_end = 0; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; + fl->fl_xid = -1; } EXPORT_SYMBOL(locks_init_lock); @@ -217,10 +235,14 @@ void locks_copy_lock(struct file_lock *new, struct file_lock *fl) new->fl_type = fl->fl_type; new->fl_start = fl->fl_start; new->fl_end = fl->fl_end; - new->fl_notify = fl->fl_notify; - new->fl_insert = fl->fl_insert; - new->fl_remove = fl->fl_remove; - new->fl_u = fl->fl_u; + new->fl_ops = fl->fl_ops; + new->fl_lmops = fl->fl_lmops; + if (fl->fl_ops && fl->fl_ops->fl_copy_lock) + fl->fl_ops->fl_copy_lock(new, fl); + if (fl->fl_lmops && fl->fl_lmops->fl_copy_lock) + fl->fl_lmops->fl_copy_lock(new, fl); + + new->fl_xid = fl->fl_xid; } EXPORT_SYMBOL(locks_copy_lock); @@ -257,6 +279,10 @@ static int flock_make_lock(struct file *filp, struct file_lock **lock, fl->fl_flags = FL_FLOCK; fl->fl_type = type; fl->fl_end = OFFSET_MAX; + + /* check agains file xid maybe ? */ + fl->fl_xid = vx_current_xid(); + vx_locks_inc(fl); *lock = fl; return 0; @@ -321,9 +347,8 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_pid = current->tgid; fl->fl_file = filp; fl->fl_flags = FL_POSIX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; return assign_type(fl, l->l_type); } @@ -361,9 +386,8 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_pid = current->tgid; fl->fl_file = filp; fl->fl_flags = FL_POSIX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; switch (l->l_type) { case F_RDLCK: @@ -379,13 +403,31 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, } #endif -/* Allocate a file_lock initialised to this type of lease */ -static int lease_alloc(struct file *filp, int type, struct file_lock **flp) +/* default lease lock manager operations */ +static void lease_break_callback(struct file_lock *fl) { - struct file_lock *fl = locks_alloc_lock(); - if (fl == NULL) - return -ENOMEM; + kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG); +} + +static void lease_release_private_callback(struct file_lock *fl) +{ + if (!fl->fl_file) + return; + + f_delown(fl->fl_file); + fl->fl_file->f_owner.signum = 0; +} + +struct lock_manager_operations lease_manager_ops = { + .fl_break = lease_break_callback, + .fl_release_private = lease_release_private_callback, +}; +/* + * Initialize a lease, use the default lock manager operations + */ +static int lease_init(struct file *filp, int type, struct file_lock *fl) + { fl->fl_owner = current->files; fl->fl_pid = current->tgid; @@ -397,10 +439,25 @@ static int lease_alloc(struct file *filp, int type, struct file_lock **flp) } fl->fl_start = 0; fl->fl_end = OFFSET_MAX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = &lease_manager_ops; + return 0; +} + +/* Allocate a file_lock initialised to this type of lease */ +static int lease_alloc(struct file *filp, int type, struct file_lock **flp) +{ + struct file_lock *fl = locks_alloc_lock(); + int error; + if (fl == NULL) + return -ENOMEM; + + fl->fl_xid = vx_current_xid(); + vx_locks_inc(fl); + error = lease_init(filp, type, fl); + if (error) + return error; *flp = fl; return 0; } @@ -414,14 +471,15 @@ static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2) } /* - * Check whether two locks have the same owner. The apparently superfluous - * check for fl_pid enables us to distinguish between locks set by lockd. + * Check whether two locks have the same owner. */ static inline int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) { - return (fl1->fl_owner == fl2->fl_owner) && - (fl1->fl_pid == fl2->fl_pid); + if (fl1->fl_lmops && fl1->fl_lmops->fl_compare_owner) + return fl2->fl_lmops == fl1->fl_lmops && + fl1->fl_lmops->fl_compare_owner(fl1, fl2); + return fl1->fl_owner == fl2->fl_owner; } /* Remove waiter from blocker's block list. @@ -459,7 +517,8 @@ static void locks_insert_block(struct file_lock *blocker, } list_add_tail(&waiter->fl_block, &blocker->fl_block); waiter->fl_next = blocker; - list_add(&waiter->fl_link, &blocked_list); + if (IS_POSIX(blocker)) + list_add(&waiter->fl_link, &blocked_list); } /* Wake up processes blocked waiting for blocker. @@ -472,8 +531,8 @@ static void locks_wake_up_blocks(struct file_lock *blocker) struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block); __locks_delete_block(waiter); - if (waiter->fl_notify) - waiter->fl_notify(waiter); + if (waiter->fl_lmops && waiter->fl_lmops->fl_notify) + waiter->fl_lmops->fl_notify(waiter); else wake_up(&waiter->fl_wait); } @@ -490,8 +549,8 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) fl->fl_next = *pos; *pos = fl; - if (fl->fl_insert) - fl->fl_insert(fl); + if (fl->fl_ops && fl->fl_ops->fl_insert) + fl->fl_ops->fl_insert(fl); } /* @@ -514,8 +573,8 @@ static void locks_delete_lock(struct file_lock **thisfl_p) fl->fl_fasync = NULL; } - if (fl->fl_remove) - fl->fl_remove(fl); + if (fl->fl_ops && fl->fl_ops->fl_remove) + fl->fl_ops->fl_remove(fl); locks_wake_up_blocks(fl); locks_free_lock(fl); @@ -631,24 +690,15 @@ int posix_locks_deadlock(struct file_lock *caller_fl, struct file_lock *block_fl) { struct list_head *tmp; - fl_owner_t caller_owner, blocked_owner; - unsigned int caller_pid, blocked_pid; - - caller_owner = caller_fl->fl_owner; - caller_pid = caller_fl->fl_pid; - blocked_owner = block_fl->fl_owner; - blocked_pid = block_fl->fl_pid; next_task: - if (caller_owner == blocked_owner && caller_pid == blocked_pid) + if (posix_same_owner(caller_fl, block_fl)) return 1; list_for_each(tmp, &blocked_list) { struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - if ((fl->fl_owner == blocked_owner) - && (fl->fl_pid == blocked_pid)) { + if (posix_same_owner(fl, block_fl)) { fl = fl->fl_next; - blocked_owner = fl->fl_owner; - blocked_pid = fl->fl_pid; + block_fl = fl; goto next_task; } } @@ -734,7 +784,11 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request) * so we get them in advance to avoid races. */ new_fl = locks_alloc_lock(); + new_fl->fl_xid = vx_current_xid(); + vx_locks_inc(new_fl); new_fl2 = locks_alloc_lock(); + new_fl2->fl_xid = vx_current_xid(); + vx_locks_inc(new_fl2); lock_kernel(); if (request->fl_type != F_UNLCK) { @@ -911,6 +965,34 @@ int posix_lock_file(struct file *filp, struct file_lock *fl) return __posix_lock_file(filp->f_dentry->d_inode, fl); } +/** + * posix_lock_file_wait - Apply a POSIX-style lock to a file + * @filp: The file to apply the lock to + * @fl: The lock to be applied + * + * Add a POSIX style lock to a file. + * We merge adjacent & overlapping locks whenever possible. + * POSIX locks are sorted by owner task, then by starting address + */ +int posix_lock_file_wait(struct file *filp, struct file_lock *fl) +{ + int error; + might_sleep (); + for (;;) { + error = __posix_lock_file(filp->f_dentry->d_inode, fl); + if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + break; + error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); + if (!error) + continue; + + locks_delete_block(fl); + break; + } + return error; +} +EXPORT_SYMBOL(posix_lock_file_wait); + /** * locks_mandatory_locked - Check for an active lock * @inode: the file to check @@ -947,7 +1029,7 @@ int locks_mandatory_locked(struct inode *inode) * @count: length of area to check * * Searches the inode's list of locks to find any POSIX locks which conflict. - * This function is called from locks_verify_area() and + * This function is called from rw_verify_area() and * locks_verify_truncate(). */ int locks_mandatory_area(int read_write, struct inode *inode, @@ -1002,13 +1084,8 @@ static int lease_modify(struct file_lock **before, int arg) if (error) return error; locks_wake_up_blocks(fl); - if (arg == F_UNLCK) { - struct file *filp = fl->fl_file; - - f_delown(filp); - filp->f_owner.signum = 0; + if (arg == F_UNLCK) locks_delete_lock(before); - } return 0; } @@ -1031,6 +1108,24 @@ static void time_out_leases(struct inode *inode) } } + /** +* remove_lease - let time_out_leases remove the lease. +* @@file_lock: the lease to remove +*/ +void remove_lease(struct file_lock *fl) +{ + lock_kernel(); + if (!fl || !IS_LEASE(fl)) + goto out; + fl->fl_type = F_UNLCK | F_INPROGRESS; + fl->fl_break_time = jiffies - 10; + time_out_leases(fl->fl_file->f_dentry->d_inode); +out: + unlock_kernel(); +} + +EXPORT_SYMBOL(remove_lease); + /** * __break_lease - revoke all outstanding leases on file * @inode: the inode of the file to return @@ -1095,7 +1190,10 @@ int __break_lease(struct inode *inode, unsigned int mode) if (fl->fl_type != future) { fl->fl_type = future; fl->fl_break_time = break_time; - kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG); + if (fl->fl_lmops && fl->fl_lmops->fl_break) + fl->fl_lmops->fl_break(fl); + else /* lease must have lmops break callback */ + BUG(); } } @@ -1146,7 +1244,7 @@ void lease_get_mtime(struct inode *inode, struct timespec *time) { struct file_lock *flock = inode->i_flock; if (flock && IS_LEASE(flock) && (flock->fl_type & F_WRLCK)) - *time = CURRENT_TIME; + *time = current_fs_time(inode->i_sb); else *time = inode->i_mtime; } @@ -1195,45 +1293,36 @@ int fcntl_getlease(struct file *filp) } /** - * fcntl_setlease - sets a lease on an open file - * @fd: open file descriptor + * __setlease - sets a lease on an open file * @filp: file pointer * @arg: type of lease to obtain + * @flp: input - file_lock to use, output - file_lock inserted * - * Call this fcntl to establish a lease on the file. - * Note that you also need to call %F_SETSIG to - * receive a signal when the lease is broken. + * The (input) flp->fl_lmops->fl_break function is required + * by break_lease(). + * + * Called with kernel lock held. */ -int fcntl_setlease(unsigned int fd, struct file *filp, long arg) +int __setlease(struct file *filp, long arg, struct file_lock **flp) { - struct file_lock *fl, **before, **my_before = NULL; - struct dentry *dentry; - struct inode *inode; + struct file_lock *fl, **before, **my_before = NULL, *lease = *flp; + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; int error, rdlease_count = 0, wrlease_count = 0; - dentry = filp->f_dentry; - inode = dentry->d_inode; - - if ((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; - - lock_kernel(); - time_out_leases(inode); - /* - * FIXME: What about F_RDLCK and files open for writing? - */ + error = -EINVAL; + if (!flp || !(*flp) || !(*flp)->fl_lmops || !(*flp)->fl_lmops->fl_break) + goto out; + error = -EAGAIN; + if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) + goto out; if ((arg == F_WRLCK) && ((atomic_read(&dentry->d_count) > 1) || (atomic_read(&inode->i_count) > 1))) - goto out_unlock; + goto out; /* * At this point, we know that if there is an exclusive @@ -1261,39 +1350,145 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) if ((arg == F_RDLCK && (wrlease_count > 0)) || (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) - goto out_unlock; + goto out; if (my_before != NULL) { error = lease_modify(my_before, arg); - goto out_unlock; + goto out; } error = 0; if (arg == F_UNLCK) - goto out_unlock; + goto out; error = -EINVAL; if (!leases_enable) - goto out_unlock; + goto out; error = lease_alloc(filp, arg, &fl); + if (error) + goto out; + + locks_copy_lock(fl, lease); + + locks_insert_lock(before, fl); + + *flp = fl; +out: + return error; +} + + /** + * setlease - sets a lease on an open file + * @filp: file pointer + * @arg: type of lease to obtain + * @lease: file_lock to use + * + * Call this to establish a lease on the file. + * The fl_lmops fl_break function is required by break_lease + */ + +int setlease(struct file *filp, long arg, struct file_lock **lease) +{ + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + int error; + + if ((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; + + lock_kernel(); + error = __setlease(filp, arg, lease); + unlock_kernel(); + + return error; +} + +EXPORT_SYMBOL(setlease); + +/** + * fcntl_setlease - sets a lease on an open file + * @fd: open file descriptor + * @filp: file pointer + * @arg: type of lease to obtain + * + * Call this fcntl to establish a lease on the file. + * Note that you also need to call %F_SETSIG to + * receive a signal when the lease is broken. + */ +int fcntl_setlease(unsigned int fd, struct file *filp, long arg) +{ + struct file_lock fl, *flp = &fl; + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + int error; + + if ((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; + + locks_init_lock(&fl); + error = lease_init(filp, arg, &fl); + if (error) + return error; + + lock_kernel(); + + error = __setlease(filp, arg, &flp); if (error) goto out_unlock; - error = fasync_helper(fd, filp, 1, &fl->fl_fasync); + error = fasync_helper(fd, filp, 1, &flp->fl_fasync); if (error < 0) { - locks_free_lock(fl); + /* remove lease just inserted by __setlease */ + flp->fl_type = F_UNLCK | F_INPROGRESS; + flp->fl_break_time = jiffies- 10; + time_out_leases(inode); goto out_unlock; } - locks_insert_lock(before, fl); - error = f_setown(filp, current->pid, 0); out_unlock: unlock_kernel(); return error; } +/** + * flock_lock_file_wait - Apply a FLOCK-style lock to a file + * @filp: The file to apply the lock to + * @fl: The lock to be applied + * + * Add a FLOCK style lock to a file. + */ +int flock_lock_file_wait(struct file *filp, struct file_lock *fl) +{ + int error; + might_sleep(); + for (;;) { + error = flock_lock_file(filp, fl); + if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + break; + error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); + if (!error) + continue; + + locks_delete_block(fl); + break; + } + return error; +} + +EXPORT_SYMBOL(flock_lock_file_wait); + /** * sys_flock: - flock() system call. * @fd: the file descriptor to lock. @@ -1342,17 +1537,12 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) if (error) goto out_free; - for (;;) { - error = flock_lock_file(filp, lock); - if ((error != -EAGAIN) || !can_sleep) - break; - error = wait_event_interruptible(lock->fl_wait, !lock->fl_next); - if (!error) - continue; - - locks_delete_block(lock); - break; - } + if (filp->f_op && filp->f_op->flock) + error = filp->f_op->flock(filp, + (can_sleep) ? F_SETLKW : F_SETLK, + lock); + else + error = flock_lock_file_wait(filp, lock); out_free: if (list_empty(&lock->fl_link)) { @@ -1389,9 +1579,6 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out; - else if (error == LOCK_USE_CLNT) - /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, &file_lock); else fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { @@ -1422,7 +1609,6 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) error = -EFAULT; if (!copy_to_user(l, &flock, sizeof(flock))) error = 0; - out: return error; } @@ -1440,6 +1626,9 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l) if (file_lock == NULL) return -ENOLCK; + file_lock->fl_xid = vx_current_xid(); + vx_locks_inc(file_lock); + /* * This might block, so we do it before checking the inode. */ @@ -1489,8 +1678,7 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l) if (filp->f_op && filp->f_op->lock != NULL) { error = filp->f_op->lock(filp, cmd, file_lock); - if (error < 0) - goto out; + goto out; } for (;;) { @@ -1536,9 +1724,6 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out; - else if (error == LOCK_USE_CLNT) - /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, &file_lock); else fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { @@ -1575,6 +1760,9 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l) if (file_lock == NULL) return -ENOLCK; + file_lock->fl_xid = vx_current_xid(); + vx_locks_inc(file_lock); + /* * This might block, so we do it before checking the inode. */ @@ -1624,8 +1812,7 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l) if (filp->f_op && filp->f_op->lock != NULL) { error = filp->f_op->lock(filp, cmd, file_lock); - if (error < 0) - goto out; + goto out; } for (;;) { @@ -1672,10 +1859,12 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner) lock.fl_owner = owner; lock.fl_pid = current->tgid; lock.fl_file = filp; + lock.fl_ops = NULL; + lock.fl_lmops = NULL; if (filp->f_op && filp->f_op->lock != NULL) { filp->f_op->lock(filp, F_SETLK, &lock); - /* Ignore any error -- we must remove the locks anyway */ + goto out; } /* Can't use posix_lock_file here; we need to remove it no matter @@ -1684,13 +1873,16 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner) lock_kernel(); while (*before != NULL) { struct file_lock *fl = *before; - if (IS_POSIX(fl) && (fl->fl_owner == owner)) { + if (IS_POSIX(fl) && posix_same_owner(fl, &lock)) { locks_delete_lock(before); continue; } before = &fl->fl_next; } unlock_kernel(); +out: + if (lock.fl_ops && lock.fl_ops->fl_release_private) + lock.fl_ops->fl_release_private(&lock); } EXPORT_SYMBOL(locks_remove_posix); @@ -1707,12 +1899,23 @@ void locks_remove_flock(struct file *filp) if (!inode->i_flock) return; + if (filp->f_op && filp->f_op->flock) { + struct file_lock fl = { .fl_flags = FL_FLOCK, + .fl_type = F_UNLCK }; + filp->f_op->flock(filp, F_SETLKW, &fl); + } + lock_kernel(); before = &inode->i_flock; while ((fl = *before) != NULL) { if (fl->fl_file == filp) { - if (IS_FLOCK(fl)) { + /* + * We might have a POSIX lock that was created at the same time + * the filp was closed for the last time. Just remove that too, + * regardless of ownership, since nobody can own it. + */ + if (IS_FLOCK(fl) || IS_POSIX(fl)) { locks_delete_lock(before); continue; } @@ -1720,6 +1923,7 @@ void locks_remove_flock(struct file *filp) lease_modify(before, F_UNLCK); continue; } + /* What? */ BUG(); } before = &fl->fl_next; @@ -1813,7 +2017,7 @@ static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); } if (inode) { -#if WE_CAN_BREAK_LSLK_NOW +#ifdef WE_CAN_BREAK_LSLK_NOW out += sprintf(out, "%d %s:%ld ", fl->fl_pid, inode->i_sb->s_id, inode->i_ino); #else @@ -1979,31 +2183,60 @@ int lock_may_write(struct inode *inode, loff_t start, unsigned long len) EXPORT_SYMBOL(lock_may_write); +static inline void __steal_locks(struct file *file, fl_owner_t from) +{ + struct inode *inode = file->f_dentry->d_inode; + struct file_lock *fl = inode->i_flock; + + while (fl) { + if (fl->fl_file == file && fl->fl_owner == from) + fl->fl_owner = current->files; + fl = fl->fl_next; + } +} + +/* When getting ready for executing a binary, we make sure that current + * has a files_struct on its own. Before dropping the old files_struct, + * we take over ownership of all locks for all file descriptors we own. + * Note that we may accidentally steal a lock for a file that a sibling + * has created since the unshare_files() call. + */ void steal_locks(fl_owner_t from) { - struct list_head *tmp; + struct files_struct *files = current->files; + int i, j; - if (from == current->files) + if (from == files) return; lock_kernel(); - list_for_each(tmp, &file_lock_list) { - struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - if (fl->fl_owner == from) - fl->fl_owner = current->files; + j = 0; + for (;;) { + unsigned long set; + i = j * __NFDBITS; + if (i >= files->max_fdset || i >= files->max_fds) + break; + set = files->open_fds->fds_bits[j++]; + while (set) { + if (set & 1) { + struct file *file = files->fd[i]; + if (file) + __steal_locks(file, from); + } + i++; + set >>= 1; + } } unlock_kernel(); } - EXPORT_SYMBOL(steal_locks); static int __init filelock_init(void) { filelock_cache = kmem_cache_create("file_lock_cache", - sizeof(struct file_lock), 0, 0, init_once, NULL); - if (!filelock_cache) - panic("cannot create file lock slab cache"); + sizeof(struct file_lock), 0, SLAB_PANIC, + init_once, NULL); return 0; } -module_init(filelock_init) +core_initcall(filelock_init);