diff -urpNX dontdiff linux-2.5.49/fs/lockd/clntproc.c linux-2.5.49-flock/fs/lockd/clntproc.c --- linux-2.5.49/fs/lockd/clntproc.c 2002-11-19 21:33:24.000000000 -0800 +++ linux-2.5.49-flock/fs/lockd/clntproc.c 2002-11-25 02:41:46.000000000 -0800 @@ -386,27 +386,25 @@ nlmclnt_test(struct nlm_rqst *req, struc status = req->a_res.status; if (status == NLM_LCK_GRANTED) { - fl->fl_type = F_UNLCK; - } if (status == NLM_LCK_DENIED) { + return 0; + } else if (status == NLM_LCK_DENIED) { /* * Report the conflicting lock back to the application. * FIXME: Is it OK to report the pid back as well? */ locks_copy_lock(fl, &req->a_res.lock.fl); /* fl->fl_pid = 0; */ + return 1; } else { - return nlm_stat_to_errno(req->a_res.status); + return nlm_stat_to_errno(status); } - - return 0; } -static void nlmclnt_insert_lock_callback(struct file_lock *fl) { nlm_get_host(fl->fl_u.nfs_fl.host); } -static + void nlmclnt_remove_lock_callback(struct file_lock *fl) { if (fl->fl_u.nfs_fl.host) { @@ -462,8 +460,6 @@ nlmclnt_lock(struct nlm_rqst *req, struc fl->fl_u.nfs_fl.state = host->h_state; fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED; fl->fl_u.nfs_fl.host = host; - fl->fl_insert = nlmclnt_insert_lock_callback; - fl->fl_remove = nlmclnt_remove_lock_callback; } return nlm_stat_to_errno(resp->status); diff -urpNX dontdiff linux-2.5.49/fs/lockd/svclock.c linux-2.5.49-flock/fs/lockd/svclock.c --- linux-2.5.49/fs/lockd/svclock.c 2002-09-16 07:51:17.000000000 -0700 +++ linux-2.5.49-flock/fs/lockd/svclock.c 2002-11-24 10:28:26.000000000 -0800 @@ -293,7 +293,6 @@ u32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) { - struct file_lock *conflock; struct nlm_block *block; int error; @@ -312,59 +311,57 @@ nlmsvc_lock(struct svc_rqst *rqstp, stru block = nlmsvc_lookup_block(file, lock, 0); lock->fl.fl_flags |= FL_LOCKD; + if (wait) + lock->fl.fl_flags |= FL_SLEEP; -again: - if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) { - error = posix_lock_file(&file->f_file, &lock->fl); + error = vfs_setlock_posix(&file->f_file, &lock->fl); + dprintk("lockd: vfs_setlock_posix returned %d\n", -error); + if ((error != -EAGAIN) || !wait) + goto out; - if (block) - nlmsvc_delete_block(block, 0); - up(&file->f_sema); - - dprintk("lockd: posix_lock_file returned %d\n", -error); - switch(-error) { - case 0: - return nlm_granted; - case EDEADLK: - return nlm_deadlock; - case EAGAIN: - return nlm_lck_denied; - default: /* includes ENOLCK */ - return nlm_lck_denied_nolocks; + /* If we don't have a block, create and initialize it. */ + if (!block) { + dprintk("lockd: blocking on this lock (allocating).\n"); + block = nlmsvc_create_block(rqstp, file, lock, cookie); + if (!block) { + error = -ENOLCK; + goto out; } } - if (!wait) { - up(&file->f_sema); - return nlm_lck_denied; - } + /* Append to list of blocked locks */ + nlmsvc_insert_block(block, NLM_NEVER); - if (posix_locks_deadlock(&lock->fl, conflock)) { - up(&file->f_sema); - return nlm_deadlock; + /* + * There's a race a mile wide there. vfs_setlock_posix() adds us + * to the blocked list, but we may have just slept to allocate + * memory. So check to see whether we're still blocked and issue + * a wakeup if we're not. + */ + if (!lock->fl.fl_next) { + nlmsvc_insert_block(block, 0); + svc_wake_up(block->b_daemon); } + block = NULL; - /* If we don't have a block, create and initialize it. Then - * retry because we may have slept in kmalloc. */ - if (block == NULL) { - dprintk("lockd: blocking on this lock (allocating).\n"); - if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie))) - return nlm_lck_denied_nolocks; - goto again; - } + out: + up(&file->f_sema); - /* Append to list of blocked */ - nlmsvc_insert_block(block, NLM_NEVER); + if (block) + nlmsvc_delete_block(block, 0); - if (list_empty(&block->b_call.a_args.lock.fl.fl_block)) { - /* Now add block to block list of the conflicting lock - if we haven't done so. */ - dprintk("lockd: blocking on this lock.\n"); - posix_block_lock(conflock, &block->b_call.a_args.lock.fl); + if (!error) { + return nlm_granted; + } else if (error == -EDEADLK) { + return nlm_deadlock; + } else if (error == -EAGAIN) { + if (wait) + return nlm_lck_blocked; + else + return nlm_lck_denied; + } else { + return nlm_lck_denied_nolocks; } - - up(&file->f_sema); - return nlm_lck_blocked; } /* @@ -374,8 +371,6 @@ u32 nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, struct nlm_lock *conflock) { - struct file_lock *fl; - dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", file->f_file.f_dentry->d_inode->i_sb->s_id, file->f_file.f_dentry->d_inode->i_ino, @@ -383,13 +378,15 @@ nlmsvc_testlock(struct nlm_file *file, s (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); - if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) { + conflock->fl = lock->fl; /* a struct copy */ + vfs_testlock_posix(&file->f_file, &conflock->fl); + if (conflock->fl.fl_type != F_UNLCK) { dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", - fl->fl_type, (long long)fl->fl_start, - (long long)fl->fl_end); + conflock->fl.fl_type, + (long long)conflock->fl.fl_start, + (long long)conflock->fl.fl_end); conflock->caller = "somehost"; /* FIXME */ conflock->oh.len = 0; /* don't return OH info */ - conflock->fl = *fl; return nlm_lck_denied; } @@ -419,7 +416,7 @@ nlmsvc_unlock(struct nlm_file *file, str nlmsvc_cancel_blocked(file, lock); lock->fl.fl_type = F_UNLCK; - error = posix_lock_file(&file->f_file, &lock->fl); + error = vfs_setlock_posix(&file->f_file, &lock->fl); return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; } @@ -490,7 +487,6 @@ nlmsvc_grant_blocked(struct nlm_block *b { struct nlm_file *file = block->b_file; struct nlm_lock *lock = &block->b_call.a_args.lock; - struct file_lock *conflock; int error; dprintk("lockd: grant blocked lock %p\n", block); @@ -506,32 +502,13 @@ nlmsvc_grant_blocked(struct nlm_block *b * binding */ if (block->b_granted) { nlm_rebind_host(block->b_host); - goto callback; - } - - /* Try the lock operation again */ - if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) { - /* Bummer, we blocked again */ - dprintk("lockd: lock still blocked\n"); - nlmsvc_insert_block(block, NLM_NEVER); - posix_block_lock(conflock, &lock->fl); - up(&file->f_sema); - return; - } - - /* Alright, no conflicting lock. Now lock it for real. If the - * following yields an error, this is most probably due to low - * memory. Retry the lock in a few seconds. - */ - if ((error = posix_lock_file(&file->f_file, &lock->fl)) < 0) { - printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", - -error, __FUNCTION__); - nlmsvc_insert_block(block, 10 * HZ); - up(&file->f_sema); - return; + } else { + /* Try the lock operation again */ + error = vfs_setlock_posix(&file->f_file, &lock->fl); + if (error) + goto err; } -callback: /* Lock was granted by VFS. */ dprintk("lockd: GRANTing blocked lock.\n"); block->b_granted = 1; @@ -546,6 +523,18 @@ callback: nlmsvc_grant_callback) < 0) nlm_release_host(block->b_call.a_host); up(&file->f_sema); + return; + + err: + dprintk("lockd: blocked lock retry returned %d\n", error); + if (error = -EAGAIN) { + /* We actually blocked, so go back to sleep */ + nlmsvc_insert_block(block, NLM_NEVER); + } else { + /* Some other error. We must retry later. */ + nlmsvc_insert_block(block, 10 * HZ); + } + up(&file->f_sema); } /* diff -urpNX dontdiff linux-2.5.49/fs/lockd/svcsubs.c linux-2.5.49-flock/fs/lockd/svcsubs.c --- linux-2.5.49/fs/lockd/svcsubs.c 2002-09-16 07:51:17.000000000 -0700 +++ linux-2.5.49-flock/fs/lockd/svcsubs.c 2002-11-24 10:28:26.000000000 -0800 @@ -176,7 +176,7 @@ again: lock.fl_type = F_UNLCK; lock.fl_start = 0; lock.fl_end = OFFSET_MAX; - if (posix_lock_file(&file->f_file, &lock) < 0) { + if (vfs_setlock_posix(&file->f_file, &lock) < 0) { printk("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__); return 1; diff -urpNX dontdiff linux-2.5.49/fs/locks.c linux-2.5.49-flock/fs/locks.c --- linux-2.5.49/fs/locks.c 2002-11-24 08:18:53.000000000 -0800 +++ linux-2.5.49-flock/fs/locks.c 2002-11-25 07:42:12.000000000 -0800 @@ -114,16 +114,18 @@ * Stephen Rothwell , June, 2000. */ -#include +#include #include -#include +#include #include -#include -#include +#include +#include +#include +#include #include -#include +#include +#include -#include #include #define IS_POSIX(fl) (fl->fl_flags & FL_POSIX) @@ -187,8 +189,6 @@ void locks_init_lock(struct file_lock *f fl->fl_type = 0; fl->fl_start = fl->fl_end = 0; fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; } /* @@ -219,8 +219,6 @@ void locks_copy_lock(struct file_lock *n 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; } @@ -321,8 +319,6 @@ static int flock_to_posix_lock(struct fi fl->fl_file = filp; fl->fl_flags = FL_POSIX; fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; return assign_type(fl, l->l_type); } @@ -361,8 +357,6 @@ static int flock64_to_posix_lock(struct fl->fl_file = filp; fl->fl_flags = FL_POSIX; fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; switch (l->l_type) { case F_RDLCK: @@ -397,8 +391,6 @@ static int lease_alloc(struct file *filp fl->fl_start = 0; fl->fl_end = OFFSET_MAX; fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; *flp = fl; return 0; @@ -474,14 +466,17 @@ static void locks_wake_up_blocks(struct */ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) { + struct lock_operations *l_op = NULL; list_add(&fl->fl_link, &file_lock_list); /* insert into file's list */ fl->fl_next = *pos; *pos = fl; - if (fl->fl_insert) - fl->fl_insert(fl); + if (fl->fl_file->f_op) + l_op = fl->fl_file->f_op->lock; + if (l_op && l_op->lock_insert) + l_op->lock_insert(fl); } /* @@ -493,6 +488,7 @@ static void locks_insert_lock(struct fil static void locks_delete_lock(struct file_lock **thisfl_p) { struct file_lock *fl = *thisfl_p; + struct lock_operations *l_op = NULL; *thisfl_p = fl->fl_next; fl->fl_next = NULL; @@ -504,8 +500,10 @@ static void locks_delete_lock(struct fil fl->fl_fasync = NULL; } - if (fl->fl_remove) - fl->fl_remove(fl); + if (fl->fl_file->f_op) + l_op = fl->fl_file->f_op->lock; + if (l_op && l_op->lock_remove) + l_op->lock_remove(fl); locks_wake_up_blocks(fl); locks_free_lock(fl); @@ -516,53 +514,47 @@ static void locks_delete_lock(struct fil */ static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - switch (caller_fl->fl_type) { - case F_RDLCK: - return (sys_fl->fl_type == F_WRLCK); + if (caller_fl->fl_type == F_WRLCK) + return 1; - case F_WRLCK: - return (1); + if (sys_fl->fl_type == F_WRLCK) + return 1; - default: - printk(KERN_ERR "locks_conflict(): impossible lock type - %d\n", - caller_fl->fl_type); - break; - } - return (0); /* This should never happen */ + return 0; } /* Determine if lock sys_fl blocks lock caller_fl. POSIX specific - * checking before calling the locks_conflict(). + * checking before calling locks_conflict(). */ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { /* POSIX locks owned by the same process do not conflict with * each other. */ - if (!IS_POSIX(sys_fl) || posix_same_owner(caller_fl, sys_fl)) - return (0); + if (posix_same_owner(caller_fl, sys_fl)) + return 0; /* Check whether they overlap */ if (!locks_overlap(caller_fl, sys_fl)) return 0; - return (locks_conflict(caller_fl, sys_fl)); + return locks_conflict(caller_fl, sys_fl); } /* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific - * checking before calling the locks_conflict(). + * checking before calling locks_conflict(). */ static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { /* FLOCK locks referring to the same filp do not conflict with * each other. */ - if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) - return (0); + 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)); + return locks_conflict(caller_fl, sys_fl); } static int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, int timeout) @@ -592,21 +584,32 @@ static int locks_block_on_timeout(struct return result; } -struct file_lock * -posix_test_lock(struct file *filp, struct file_lock *fl) +/** + * posix_test_lock - finds a conflicting lock + * @filp: the file to search + * @fl: the template to check + * + * This function overwrites @fl with any lock which overlaps it and is + * owned by a different task. Returns 1 for success, 0 for failure. + */ +static int posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; + int result = 0; lock_kernel(); for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!IS_POSIX(cfl)) continue; - if (posix_locks_conflict(cfl, fl)) - break; + if (!posix_locks_conflict(cfl, fl)) + continue; + + *fl = *cfl; + result = 1; + break; } unlock_kernel(); - - return (cfl); + return result; } /* This function tests for deadlock condition before putting a process to @@ -623,7 +626,7 @@ posix_test_lock(struct file *filp, struc * from a broken NFS client. But broken NFS clients have a lot more to * worry about than proper deadlock detection anyway... --okir */ -int posix_locks_deadlock(struct file_lock *caller_fl, +static int posix_locks_deadlock(struct file_lock *caller_fl, struct file_lock *block_fl) { struct list_head *tmp; @@ -651,63 +654,6 @@ next_task: return 0; } -int locks_mandatory_locked(struct inode *inode) -{ - fl_owner_t owner = current->files; - struct file_lock *fl; - - /* - * Search the lock list for this inode for any POSIX locks. - */ - lock_kernel(); - for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { - if (!IS_POSIX(fl)) - continue; - if (fl->fl_owner != owner) - break; - } - unlock_kernel(); - return fl ? -EAGAIN : 0; -} - -int locks_mandatory_area(int read_write, struct inode *inode, - struct file *filp, loff_t offset, - size_t count) -{ - struct file_lock fl; - int error; - - fl.fl_owner = current->files; - fl.fl_pid = current->tgid; - fl.fl_file = filp; - fl.fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP; - fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; - fl.fl_start = offset; - fl.fl_end = offset + count - 1; - - for (;;) { - error = posix_lock_file(filp, &fl); - if (error != -EAGAIN) - break; - error = wait_event_interruptible(fl.fl_wait, !fl.fl_next); - if (!error) { - /* - * If we've been sleeping someone might have - * changed the permissions behind our back. - */ - if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) - continue; - } - - lock_kernel(); - locks_delete_block(&fl); - unlock_kernel(); - break; - } - - return error; -} - /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks * at the head of the list, but that's secret knowledge known only to * flock_lock_file and posix_lock_file. @@ -787,7 +733,7 @@ out: * To all purists: Yes, I use a few goto's. Just pass on to the next function. */ -int posix_lock_file(struct file *filp, struct file_lock *request) +static int posix_lock_file(struct file *filp, struct file_lock *request) { struct file_lock *fl; struct file_lock *new_fl, *new_fl2; @@ -1102,6 +1048,63 @@ out: return error; } +int locks_mandatory_locked(struct inode *inode) +{ + fl_owner_t owner = current->files; + struct file_lock *fl; + + /* + * Search the lock list for this inode for any POSIX locks. + */ + lock_kernel(); + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!IS_POSIX(fl)) + continue; + if (fl->fl_owner != owner) + break; + } + unlock_kernel(); + return fl ? -EAGAIN : 0; +} + +int locks_mandatory_area(int read_write, struct inode *inode, + struct file *filp, loff_t offset, + size_t count) +{ + struct file_lock fl; + int error; + + fl.fl_owner = current->files; + fl.fl_pid = current->tgid; + fl.fl_file = filp; + fl.fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP; + fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; + fl.fl_start = offset; + fl.fl_end = offset + count - 1; + + for (;;) { + error = posix_lock_file(filp, &fl); + if (error != -EAGAIN) + break; + error = wait_event_interruptible(fl.fl_wait, !fl.fl_next); + if (!error) { + /* + * If we've been sleeping someone might have + * changed the permissions behind our back. + */ + if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) + continue; + } + + lock_kernel(); + locks_delete_block(&fl); + unlock_kernel(); + break; + } + + return error; +} + /** * lease_get_mtime * @inode: the inode @@ -1327,12 +1330,95 @@ asmlinkage long sys_flock(unsigned int f return error; } +/** + * vfs_setlock_posix - apply a POSIX lock to a file + * @filp: the file to apply the lock to + * @fl: the lock to apply + * + * This function contains the common code to apply a lock to a file. + * It's called by the 32- and 64- bit versions of fcntl_setlk and lockd. + * If the underlying filesystem provides a locking method, we call that. + * Otherwise we lock locally. + */ +int vfs_setlock_posix(struct file *filp, struct file_lock *fl) +{ + struct lock_operations *l_op = NULL; + int (*lock_file)(struct file *, struct file_lock *) = posix_lock_file; + int error; + + switch (fl->fl_type) { + case F_RDLCK: + if (!(filp->f_mode & FMODE_READ)) + return -EBADF; + break; + case F_WRLCK: + if (!(filp->f_mode & FMODE_WRITE)) + return -EBADF; + break; + case F_UNLCK: + break; + default: + return -EINVAL; + } + + error = security_ops->file_lock(filp, fl->fl_type); + if (error) + return error; + + if (filp->f_op) + l_op = filp->f_op->lock; + if (l_op && l_op->set_lock) + lock_file = l_op->set_lock; + + for (;;) { + error = lock_file(filp, fl); + if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + break; + if (fl->fl_flags & FL_LOCKD) + break; + error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); + if (!error) + continue; + + lock_kernel(); + locks_delete_block(fl); + unlock_kernel(); + break; + } + + return error; +} + +/** + * vfs_testlock_posix - Report any conflicting lock + * @filp: the file to examine + * @fl: The lock to report any conflicts against + * + * This function contains the common code to test for conflicting locks. + * It's called by the 32- and 64- bit versions of fcntl getlk and by lockd. + * Note that this is an inherently racy interface and the only reason to use + * it is to implement the user interface. In-kernel users should attempt to + * apply the lock and deal with the failure. + */ +int vfs_testlock_posix(struct file *filp, struct file_lock *fl) +{ + struct lock_operations *l_op = NULL; + + if (filp->f_op) + l_op = filp->f_op->lock; + if (l_op && l_op->get_lock) { + return l_op->get_lock(filp, fl); + } else { + return posix_test_lock(filp, fl); + } +} + /* Report the first existing lock that would conflict with l. * This implements the F_GETLK command of fcntl(). */ int fcntl_getlk(struct file *filp, struct flock *l) { - struct file_lock *fl, file_lock; + struct file_lock file_lock; struct flock flock; int error; @@ -1343,49 +1429,43 @@ int fcntl_getlk(struct file *filp, struc if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) goto out; + locks_init_lock(&file_lock); error = flock_to_posix_lock(filp, &file_lock, &flock); if (error) goto out; - if (filp->f_op && filp->f_op->lock) { - 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 { - fl = posix_test_lock(filp, &file_lock); - } - - flock.l_type = F_UNLCK; - if (fl != NULL) { - flock.l_pid = fl->fl_pid; + error = vfs_testlock_posix(filp, &file_lock); + if (error < 0) + goto out; + + if (error == 1) { #if BITS_PER_LONG == 32 /* * Make sure we can represent the posix lock via * legacy 32bit flock. */ error = -EOVERFLOW; - if (fl->fl_start > OFFT_OFFSET_MAX) + if (file_lock.fl_start > OFFT_OFFSET_MAX) goto out; - if ((fl->fl_end != OFFSET_MAX) - && (fl->fl_end > OFFT_OFFSET_MAX)) + if ((file_lock.fl_end != OFFSET_MAX) + && (file_lock.fl_end > OFFT_OFFSET_MAX)) goto out; #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_type = file_lock.fl_type; + flock.l_pid = file_lock.fl_pid; + flock.l_start = file_lock.fl_start; + flock.l_len = file_lock.fl_end == OFFSET_MAX ? 0 : + file_lock.fl_end - file_lock.fl_start + 1; flock.l_whence = 0; - flock.l_type = fl->fl_type; + } else { + flock.l_type = F_UNLCK; } + error = -EFAULT; if (!copy_to_user(l, &flock, sizeof(flock))) error = 0; -out: + out: return error; } @@ -1394,14 +1474,11 @@ out: */ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock *l) { - struct file_lock *file_lock = locks_alloc_lock(0); + struct file_lock file_lock; struct flock flock; struct inode *inode; int error; - if (file_lock == NULL) - return -ENOLCK; - /* * This might block, so we do it before checking the inode. */ @@ -1411,7 +1488,6 @@ int fcntl_setlk(struct file *filp, unsig inode = filp->f_dentry->d_inode; -#ifdef CONFIG_MMU /* Don't allow mandatory locks on files that may be memory mapped * and shared. */ @@ -1424,59 +1500,18 @@ int fcntl_setlk(struct file *filp, unsig goto out; } } -#endif - error = flock_to_posix_lock(filp, file_lock, &flock); + locks_init_lock(&file_lock); + error = flock_to_posix_lock(filp, &file_lock, &flock); if (error) goto out; if (cmd == F_SETLKW) { - file_lock->fl_flags |= FL_SLEEP; + file_lock.fl_flags |= FL_SLEEP; } - error = -EBADF; - switch (flock.l_type) { - case F_RDLCK: - if (!(filp->f_mode & FMODE_READ)) - goto out; - break; - case F_WRLCK: - if (!(filp->f_mode & FMODE_WRITE)) - goto out; - break; - case F_UNLCK: - break; - default: - error = -EINVAL; - goto out; - } - - error = security_ops->file_lock(filp, file_lock->fl_type); - if (error) - goto out; - - if (filp->f_op && filp->f_op->lock != NULL) { - error = filp->f_op->lock(filp, cmd, file_lock); - if (error < 0) - goto out; - } - - for (;;) { - error = posix_lock_file(filp, file_lock); - if ((error != -EAGAIN) || (cmd == F_SETLK)) - break; - error = wait_event_interruptible(file_lock->fl_wait, - !file_lock->fl_next); - if (!error) - continue; - - lock_kernel(); - locks_delete_block(file_lock); - unlock_kernel(); - break; - } + error = vfs_setlock_posix(filp, &file_lock); out: - locks_free_lock(file_lock); return error; } @@ -1486,7 +1521,7 @@ int fcntl_setlk(struct file *filp, unsig */ int fcntl_getlk64(struct file *filp, struct flock64 *l) { - struct file_lock *fl, file_lock; + struct file_lock file_lock; struct flock64 flock; int error; @@ -1497,31 +1532,24 @@ int fcntl_getlk64(struct file *filp, str if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) goto out; + locks_init_lock(&file_lock); error = flock64_to_posix_lock(filp, &file_lock, &flock); if (error) goto out; - if (filp->f_op && filp->f_op->lock) { - 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 { - fl = posix_test_lock(filp, &file_lock); - } - - flock.l_type = F_UNLCK; - if (fl != NULL) { - flock.l_pid = 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; + error = vfs_testlock_posix(filp, &file_lock); + if (error < 0) + goto out; + + if (error == 1) { + flock.l_type = file_lock.fl_type; + flock.l_pid = file_lock.fl_pid; + flock.l_start = file_lock.fl_start; + flock.l_len = file_lock.fl_end == OFFSET_MAX ? 0 : + file_lock.fl_end - file_lock.fl_start + 1; flock.l_whence = 0; - flock.l_type = fl->fl_type; + } else { + flock.l_type = F_UNLCK; } error = -EFAULT; if (!copy_to_user(l, &flock, sizeof(flock))) @@ -1536,14 +1564,11 @@ out: */ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 *l) { - struct file_lock *file_lock = locks_alloc_lock(0); + struct file_lock file_lock; struct flock64 flock; struct inode *inode; int error; - if (file_lock == NULL) - return -ENOLCK; - /* * This might block, so we do it before checking the inode. */ @@ -1566,57 +1591,17 @@ int fcntl_setlk64(struct file *filp, uns } } - error = flock64_to_posix_lock(filp, file_lock, &flock); + locks_init_lock(&file_lock); + error = flock64_to_posix_lock(filp, &file_lock, &flock); if (error) goto out; if (cmd == F_SETLKW64) { - file_lock->fl_flags |= FL_SLEEP; - } - - error = -EBADF; - switch (flock.l_type) { - case F_RDLCK: - if (!(filp->f_mode & FMODE_READ)) - goto out; - break; - case F_WRLCK: - if (!(filp->f_mode & FMODE_WRITE)) - goto out; - break; - case F_UNLCK: - break; - default: - error = -EINVAL; - goto out; + file_lock.fl_flags |= FL_SLEEP; } - error = security_ops->file_lock(filp, file_lock->fl_type); - if (error) - goto out; - - if (filp->f_op && filp->f_op->lock != NULL) { - error = filp->f_op->lock(filp, cmd, file_lock); - if (error < 0) - goto out; - } - - for (;;) { - error = posix_lock_file(filp, file_lock); - if ((error != -EAGAIN) || (cmd == F_SETLK64)) - break; - error = wait_event_interruptible(file_lock->fl_wait, - !file_lock->fl_next); - if (!error) - continue; - - lock_kernel(); - locks_delete_block(file_lock); - unlock_kernel(); - break; - } + error = vfs_setlock_posix(filp, &file_lock); -out: - locks_free_lock(file_lock); + out: return error; } #endif /* BITS_PER_LONG == 32 */ @@ -1628,30 +1613,34 @@ out: */ void locks_remove_posix(struct file *filp, fl_owner_t owner) { - struct file_lock lock; + struct file_lock *fl, **before; + struct lock_operations *l_op = NULL; + struct inode *inode = filp->f_dentry->d_inode; /* - * If there are no locks held on this file, we don't need to call - * posix_lock_file(). Another process could be setting a lock on this - * file at the same time, but we wouldn't remove that lock anyway. + * If there are no locks held on this file, we don't need to do + * anything. Another thread could be setting a lock on this + * file at the same time, but it's a race we just won. */ - if (!filp->f_dentry->d_inode->i_flock) + if (!inode->i_flock) return; - lock.fl_type = F_UNLCK; - lock.fl_flags = FL_POSIX; - lock.fl_start = 0; - lock.fl_end = OFFSET_MAX; - lock.fl_owner = owner; - lock.fl_pid = current->tgid; - lock.fl_file = filp; - - 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 */ + if (filp->f_op) + l_op = filp->f_op->lock; + if (l_op && l_op->remove_posix) { + l_op->remove_posix(filp, owner); + } else { + lock_kernel(); + before = &inode->i_flock; + while ((fl = *before) != NULL) { + if (IS_POSIX(fl) && fl->fl_owner == owner) { + locks_delete_lock(before); + continue; + } + before = &fl->fl_next; + } + unlock_kernel(); } - - posix_lock_file(filp, &lock); } /* @@ -1765,19 +1754,18 @@ static void lock_get_status(char* out, s ? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ " : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); } -#if WE_CAN_BREAK_LSLK_NOW if (inode) { +#if WE_CAN_BREAK_LSLK_NOW out += sprintf(out, "%d %s:%ld ", fl->fl_pid, inode->i_sb->s_id, inode->i_ino); +#else + out += sprintf(out, "%d %02x:%02x:%ld ", fl->fl_pid, + MAJOR(inode->i_sb->s_dev), + MINOR(inode->i_sb->s_dev), inode->i_ino); +#endif } else { out += sprintf(out, "%d :0 ", fl->fl_pid); } -#else - /* kdevname is a broken interface. but we expose it to userspace */ - out += sprintf(out, "%d %s:%ld ", fl->fl_pid, - inode ? kdevname(to_kdev_t(inode->i_sb->s_dev)) : "", - inode ? inode->i_ino : 0); -#endif if (IS_POSIX(fl)) { if (fl->fl_end == OFFSET_MAX) out += sprintf(out, "%Ld EOF\n", fl->fl_start); diff -urpNX dontdiff linux-2.5.49/fs/nfs/file.c linux-2.5.49-flock/fs/nfs/file.c --- linux-2.5.49/fs/nfs/file.c 2002-11-15 05:42:21.000000000 -0800 +++ linux-2.5.49-flock/fs/nfs/file.c 2002-11-25 08:15:40.000000000 -0800 @@ -40,20 +40,6 @@ static ssize_t nfs_file_write(struct kio static int nfs_file_flush(struct file *); static int nfs_fsync(struct file *, struct dentry *dentry, int datasync); -struct file_operations nfs_file_operations = { - .llseek = remote_llseek, - .read = do_sync_read, - .write = do_sync_write, - .aio_read = nfs_file_read, - .aio_write = nfs_file_write, - .mmap = nfs_file_mmap, - .open = nfs_open, - .flush = nfs_file_flush, - .release = nfs_release, - .fsync = nfs_fsync, - .lock = nfs_lock, -}; - struct inode_operations nfs_file_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, @@ -211,15 +197,30 @@ out_swapfile: goto out; } +static int nfs_flush_pending_writes(struct inode *inode) +{ + int status1, status2, status3; + + status1 = filemap_fdatawrite(inode->i_mapping); + down(&inode->i_sem); + status2 = nfs_wb_all(inode); + up(&inode->i_sem); + status3 = filemap_fdatawait(inode->i_mapping); + + if (status1) + return status1; + if (status2) + return status2; + return status3; +} + /* * Lock a (portion of) a file */ -int -nfs_lock(struct file *filp, int cmd, struct file_lock *fl) +static int nfs_set_lock(struct file *filp, struct file_lock *fl) { struct inode * inode = filp->f_dentry->d_inode; - int status = 0; - int status2; + int status; dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n", inode->i_sb->s_id, inode->i_ino, @@ -239,13 +240,6 @@ nfs_lock(struct file *filp, int cmd, str if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) return -ENOLCK; - /* Fake OK code if mounted without NLM support */ - if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { - if (IS_GETLK(cmd)) - status = LOCK_USE_CLNT; - goto out_ok; - } - /* * No BSD flocks over NFS allowed. * Note: we could try to fake a POSIX lock request here by @@ -257,41 +251,93 @@ nfs_lock(struct file *filp, int cmd, str return -ENOLCK; /* - * Flush all pending writes before doing anything - * with locks.. + * Flush all pending writes before doing anything with locks.. */ - status = filemap_fdatawrite(inode->i_mapping); - down(&inode->i_sem); - status2 = nfs_wb_all(inode); - if (!status) - status = status2; - up(&inode->i_sem); - status2 = filemap_fdatawait(inode->i_mapping); - if (!status) - status = status2; + status = nfs_flush_pending_writes(inode); if (status < 0) return status; - lock_kernel(); - status = nlmclnt_proc(inode, cmd, fl); - unlock_kernel(); - if (status < 0) - return status; - - status = 0; + if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) { + lock_kernel(); + if (fl->fl_flags & FL_SLEEP) { + status = nlmclnt_proc(inode, F_SETLKW, fl); + } else { + status = nlmclnt_proc(inode, F_SETLK, fl); + } + unlock_kernel(); + if (status < 0) + return status; + /* If this fails, the NFS server is broken */ + status = vfs_setlock_posix(filp, fl); + if (status) + printk(KERN_CRIT "nfs: File lock succeeded remotely " + "but failed locally\n"); + } else { + status = vfs_setlock_posix(filp, fl); + } /* * Make sure we clear the cache whenever we try to get the lock. * This makes locking act as a cache coherency point. */ out_ok: - if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { - filemap_fdatawrite(inode->i_mapping); - down(&inode->i_sem); - nfs_wb_all(inode); /* we may have slept */ - up(&inode->i_sem); - filemap_fdatawait(inode->i_mapping); + if (fl->fl_type != F_UNLCK) { + nfs_flush_pending_writes(inode); nfs_zap_caches(inode); } return status; } + +static int nfs_get_lock(struct file *filp, struct file_lock *fl) +{ + int status; + struct inode *inode = filp->f_dentry->d_inode; + + BUG_ON(!inode); + + if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { + status = vfs_testlock_posix(filp, fl); + } else { + status = nfs_flush_pending_writes(inode); + if (status < 0) + goto out; + lock_kernel(); + status = nlmclnt_proc(inode, F_GETLK, fl); + unlock_kernel(); + } + + out: + return status; +} + +#if 0 +static int nfs_remove_posix(struct file *filp, fl_owner_t id) +{ + printk("remove posix support not yet implemented\n"); +} +#endif + +extern void nlmclnt_insert_lock_callback(struct file_lock *fl); +extern void nlmclnt_remove_lock_callback(struct file_lock *fl); + +static struct lock_operations nfs_client_lops = { + .set_lock = nfs_set_lock, + .get_lock = nfs_get_lock, +/* .remove_posix = nfs_remove_posix, */ + .lock_insert = nlmclnt_insert_lock_callback, + .lock_remove = nlmclnt_remove_lock_callback, +}; + +struct file_operations nfs_file_operations = { + .llseek = remote_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = nfs_file_read, + .aio_write = nfs_file_write, + .mmap = nfs_file_mmap, + .open = nfs_open, + .flush = nfs_file_flush, + .release = nfs_release, + .fsync = nfs_fsync, + .lock = &nfs_client_lops, +}; diff -urpNX dontdiff linux-2.5.49/include/linux/fs.h linux-2.5.49-flock/include/linux/fs.h --- linux-2.5.49/include/linux/fs.h 2002-11-24 08:18:54.000000000 -0800 +++ linux-2.5.49-flock/include/linux/fs.h 2002-11-25 01:40:57.000000000 -0800 @@ -536,8 +536,6 @@ struct file_lock { loff_t fl_end; void (*fl_notify)(struct file_lock *); /* unblock callback */ - void (*fl_insert)(struct file_lock *); /* lock insertion callback */ - void (*fl_remove)(struct file_lock *); /* lock removal callback */ struct fasync_struct * fl_fasync; /* for lease break notifications */ unsigned long fl_break_time; /* for nonblocking lease breaks */ @@ -567,13 +565,12 @@ extern int fcntl_setlk64(struct file *, /* fs/locks.c */ extern void locks_init_lock(struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *); +extern int vfs_setlock_posix(struct file *, struct file_lock *); +extern int vfs_testlock_posix(struct file *, struct file_lock *); extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); -extern struct file_lock *posix_test_lock(struct file *, struct file_lock *); -extern int posix_lock_file(struct file *, struct file_lock *); extern void posix_block_lock(struct file_lock *, struct file_lock *); extern void posix_unblock_lock(struct file *, struct file_lock *); -extern int posix_locks_deadlock(struct file_lock *, struct file_lock *); extern int __break_lease(struct inode *inode, unsigned int flags); extern void lease_get_mtime(struct inode *, struct timespec *time); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); @@ -730,6 +727,42 @@ typedef struct { typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long); +/** + * struct lock_operations - filesystem hooks for file locking + * + * This struct is a work in progress. It is intended to be per-filesystem; + * indeed it could be part of f_ops were it not pure bloat for non-network + * filesystems. I suspect lock_insert and lock_remove are now unnecessary, + * but need feedback from FS maintainers. + * + * @set_lock: + * Attempt to set a new lock. BKL not held, may sleep. + * @get_lock: + * Return any lock which would conflict with the incoming lock. + * No locks held, may sleep. + * @remove_posix: + * A process closed a file descriptor. Any locks on this @filp owned + * by @owner should be removed. BKL not held, may sleep. + * @remove_flock: + * This @filp has been closed. All locks owned by this process should + * be removed. BKL not held, may sleep. + * @lock_insert: + * Notification that @fl, which was previously blocked, is now being + * inserted. BKL might not be held. Must not sleep. + * @lock_remove: + * Notification that @fl, which was an active lock, is now being + * removed from the @filp. BKL might not be held. Must not sleep. + */ + +struct lock_operations { + int (*set_lock) (struct file *filp, struct file_lock *fl); + int (*get_lock) (struct file *filp, struct file_lock *fl); + void (*remove_posix) (struct file *filp, fl_owner_t owner); + void (*remove_flock) (struct file *filp); + void (*lock_insert) (struct file_lock *fl); + void (*lock_remove) (struct file_lock *fl); +}; + /* * NOTE: * read, write, poll, fsync, readv, writev can be called @@ -753,7 +786,7 @@ struct file_operations { int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); - int (*lock) (struct file *, int, struct file_lock *); + struct lock_operations *lock; ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *); @@ -1000,11 +1033,6 @@ extern long do_mount(char *, char *, cha extern int vfs_statfs(struct super_block *, struct statfs *); -/* Return value for VFS lock functions - tells locks.c to lock conventionally - * REALLY kosha for root NFS and nfs_lock - */ -#define LOCK_USE_CLNT 1 - #define FLOCK_VERIFY_READ 1 #define FLOCK_VERIFY_WRITE 2 diff -urpNX dontdiff linux-2.5.49/kernel/ksyms.c linux-2.5.49-flock/kernel/ksyms.c --- linux-2.5.49/kernel/ksyms.c 2002-11-24 08:18:55.000000000 -0800 +++ linux-2.5.49-flock/kernel/ksyms.c 2002-11-24 10:28:26.000000000 -0800 @@ -229,11 +229,10 @@ EXPORT_SYMBOL(generic_ro_fops); EXPORT_SYMBOL(file_lock_list); EXPORT_SYMBOL(locks_init_lock); EXPORT_SYMBOL(locks_copy_lock); -EXPORT_SYMBOL(posix_lock_file); -EXPORT_SYMBOL(posix_test_lock); +EXPORT_SYMBOL(vfs_setlock_posix); +EXPORT_SYMBOL(vfs_testlock_posix); EXPORT_SYMBOL(posix_block_lock); EXPORT_SYMBOL(posix_unblock_lock); -EXPORT_SYMBOL(posix_locks_deadlock); EXPORT_SYMBOL(locks_mandatory_area); EXPORT_SYMBOL(dput); EXPORT_SYMBOL(have_submounts); .