Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[pandora-kernel.git] / fs / locks.c
index 29fa5da..11956b6 100644 (file)
@@ -1591,7 +1591,8 @@ out:
 /* Apply the lock described by l to an open file descriptor.
  * This implements both the F_SETLK and F_SETLKW commands of fcntl().
  */
-int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l)
+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;
@@ -1620,6 +1621,7 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l)
                goto out;
        }
 
+again:
        error = flock_to_posix_lock(filp, file_lock, &flock);
        if (error)
                goto out;
@@ -1648,25 +1650,33 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l)
        if (error)
                goto out;
 
-       if (filp->f_op && filp->f_op->lock != NULL) {
+       if (filp->f_op && filp->f_op->lock != NULL)
                error = filp->f_op->lock(filp, cmd, file_lock);
-               goto out;
-       }
+       else {
+               for (;;) {
+                       error = __posix_lock_file(inode, file_lock);
+                       if ((error != -EAGAIN) || (cmd == F_SETLK))
+                               break;
+                       error = wait_event_interruptible(file_lock->fl_wait,
+                                       !file_lock->fl_next);
+                       if (!error)
+                               continue;
 
-       for (;;) {
-               error = __posix_lock_file(inode, file_lock);
-               if ((error != -EAGAIN) || (cmd == F_SETLK))
+                       locks_delete_block(file_lock);
                        break;
-               error = wait_event_interruptible(file_lock->fl_wait,
-                               !file_lock->fl_next);
-               if (!error)
-                       continue;
+               }
+       }
 
-               locks_delete_block(file_lock);
-               break;
+       /*
+        * Attempt to detect a close/fcntl race and recover by
+        * releasing the lock that was just acquired.
+        */
+       if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) {
+               flock.l_type = F_UNLCK;
+               goto again;
        }
 
- out:
+out:
        locks_free_lock(file_lock);
        return error;
 }
@@ -1724,7 +1734,8 @@ out:
 /* Apply the lock described by l to an open file descriptor.
  * This implements both the F_SETLK and F_SETLKW commands of fcntl().
  */
-int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
+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;
@@ -1753,6 +1764,7 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
                goto out;
        }
 
+again:
        error = flock64_to_posix_lock(filp, file_lock, &flock);
        if (error)
                goto out;
@@ -1781,22 +1793,30 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
        if (error)
                goto out;
 
-       if (filp->f_op && filp->f_op->lock != NULL) {
+       if (filp->f_op && filp->f_op->lock != NULL)
                error = filp->f_op->lock(filp, cmd, file_lock);
-               goto out;
-       }
+       else {
+               for (;;) {
+                       error = __posix_lock_file(inode, file_lock);
+                       if ((error != -EAGAIN) || (cmd == F_SETLK64))
+                               break;
+                       error = wait_event_interruptible(file_lock->fl_wait,
+                                       !file_lock->fl_next);
+                       if (!error)
+                               continue;
 
-       for (;;) {
-               error = __posix_lock_file(inode, file_lock);
-               if ((error != -EAGAIN) || (cmd == F_SETLK64))
+                       locks_delete_block(file_lock);
                        break;
-               error = wait_event_interruptible(file_lock->fl_wait,
-                               !file_lock->fl_next);
-               if (!error)
-                       continue;
+               }
+       }
 
-               locks_delete_block(file_lock);
-               break;
+       /*
+        * Attempt to detect a close/fcntl race and recover by
+        * releasing the lock that was just acquired.
+        */
+       if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) {
+               flock.l_type = F_UNLCK;
+               goto again;
        }
 
 out:
@@ -1888,12 +1908,7 @@ void locks_remove_flock(struct file *filp)
 
        while ((fl = *before) != NULL) {
                if (fl->fl_file == filp) {
-                       /*
-                        * 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)) {
+                       if (IS_FLOCK(fl)) {
                                locks_delete_lock(before);
                                continue;
                        }