Patch from Faik Uygur This patch fixes the truncate/ftruncate oopses with mandatory locking enabled. The problem with ftruncate is that the local variable fl is not initialized properly in locks_mandatory_area that it misbehaves at various places like locks_insert_block. And the problem with truncate is that the filp variable is NULL at posix_lock_file. The NULL value comes from do_sys_truncate. Update from "Robert Williamson" locks_mandatory_area needed a bit more tweaking to allow correct error handling, as well as adherence to the O_NONBLOCK flag if/when used. Merged the original patch with my updates, and updated the bug report. fs/lockd/svclock.c | 6 ++--- fs/lockd/svcsubs.c | 2 - fs/locks.c | 63 ++++++++++++++++++++++++++++++++++++----------------- include/linux/fs.h | 2 - 4 files changed, 48 insertions(+), 25 deletions(-) diff -puN fs/lockd/svclock.c~mandlock-oops-fix fs/lockd/svclock.c --- 25/fs/lockd/svclock.c~mandlock-oops-fix Thu Feb 6 17:56:49 2003 +++ 25-akpm/fs/lockd/svclock.c Thu Feb 6 17:56:49 2003 @@ -315,7 +315,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, stru again: if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) { - error = posix_lock_file(&file->f_file, &lock->fl); + error = posix_lock_file(file->f_file.f_dentry->d_inode, &lock->fl); if (block) nlmsvc_delete_block(block, 0); @@ -419,7 +419,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 = posix_lock_file(file->f_file.f_dentry->d_inode, &lock->fl); return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; } @@ -523,7 +523,7 @@ nlmsvc_grant_blocked(struct nlm_block *b * 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) { + if ((error = posix_lock_file(file->f_file.f_dentry->d_inode, &lock->fl)) < 0) { printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", -error, __FUNCTION__); nlmsvc_insert_block(block, 10 * HZ); diff -puN fs/lockd/svcsubs.c~mandlock-oops-fix fs/lockd/svcsubs.c --- 25/fs/lockd/svcsubs.c~mandlock-oops-fix Thu Feb 6 17:56:49 2003 +++ 25-akpm/fs/lockd/svcsubs.c Thu Feb 6 17:56:49 2003 @@ -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 (posix_lock_file(file->f_file.f_dentry->d_inode, &lock) < 0) { printk("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__); return 1; diff -puN fs/locks.c~mandlock-oops-fix fs/locks.c --- 25/fs/locks.c~mandlock-oops-fix Thu Feb 6 17:56:49 2003 +++ 25-akpm/fs/locks.c Thu Feb 6 17:56:49 2003 @@ -678,27 +678,52 @@ int locks_mandatory_area(int read_write, struct file_lock fl; int error; + INIT_LIST_HEAD(&fl.fl_link); + INIT_LIST_HEAD(&fl.fl_block); + init_waitqueue_head(&fl.fl_wait); + fl.fl_owner = current->files; fl.fl_pid = current->tgid; fl.fl_file = filp; - fl.fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP; + + /* If the O_NONBLOCK flag is used don't sleep while */ + /* the lock is present -RW */ + if ((filp != NULL) && (filp->f_flags & O_NONBLOCK)) + fl.fl_flags = FL_POSIX | FL_ACCESS; + else + 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; + fl.fl_next = NULL; + fl.fl_notify = NULL; + fl.fl_insert = NULL; + fl.fl_remove = NULL; + fl.fl_fasync = NULL; for (;;) { - error = posix_lock_file(filp, &fl); - if (error != -EAGAIN) + error = posix_lock_file(inode, &fl); + + /* If the returned error is EAGAIN, filp is */ + /* not NULL, and O_NONBLOCK was specified */ + /* then return the EAGAIN error -RW */ + if ((error == -EAGAIN) && (filp != NULL) && (filp->f_flags & O_NONBLOCK)) break; + + /* posix_lock_file may return EDEADLK, so */ + /* we should pass it on if this occurs -RW */ + if (error == -EDEADLK) + 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; - } + + /* + * If we've been sleeping someone might have + * changed the permissions behind our back. + */ + if ((!error) && ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)) + break; lock_kernel(); locks_delete_block(&fl); @@ -772,9 +797,8 @@ out: /** * posix_lock_file: - * @filp: The file to apply the lock to - * @caller: The lock to be applied - * @wait: 1 to retry automatically, 0 to return -EAGAIN + * @inode: The inode of file to apply the lock to + * @request: The lock to be applied * * Add a POSIX style lock to a file. * We merge adjacent locks whenever possible. POSIX locks are sorted by owner @@ -788,14 +812,13 @@ 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) +int posix_lock_file(struct inode *inode, struct file_lock *request) { struct file_lock *fl; struct file_lock *new_fl, *new_fl2; struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; - struct inode * inode = filp->f_dentry->d_inode; int error, added = 0; /* @@ -1460,7 +1483,7 @@ int fcntl_setlk(struct file *filp, unsig } for (;;) { - error = posix_lock_file(filp, file_lock); + error = posix_lock_file(inode, file_lock); if ((error != -EAGAIN) || (cmd == F_SETLK)) break; error = wait_event_interruptible(file_lock->fl_wait, @@ -1600,7 +1623,7 @@ int fcntl_setlk64(struct file *filp, uns } for (;;) { - error = posix_lock_file(filp, file_lock); + error = posix_lock_file(inode, file_lock); if ((error != -EAGAIN) || (cmd == F_SETLK64)) break; error = wait_event_interruptible(file_lock->fl_wait, @@ -1650,7 +1673,7 @@ void locks_remove_posix(struct file *fil /* Ignore any error -- we must remove the locks anyway */ } - posix_lock_file(filp, &lock); + posix_lock_file(filp->f_dentry->d_inode, &lock); } /* @@ -1717,7 +1740,7 @@ posix_unblock_lock(struct file *filp, st } else { unlock_kernel(); waiter->fl_type = F_UNLCK; - posix_lock_file(filp, waiter); + posix_lock_file(filp->f_dentry->d_inode, waiter); } } diff -puN include/linux/fs.h~mandlock-oops-fix include/linux/fs.h --- 25/include/linux/fs.h~mandlock-oops-fix Thu Feb 6 17:56:49 2003 +++ 25-akpm/include/linux/fs.h Thu Feb 6 17:56:49 2003 @@ -534,7 +534,7 @@ extern void locks_copy_lock(struct file_ 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 int posix_lock_file(struct inode *, 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 *); _