nfs: clean up sillyrenaming in nfs_rename()
[safe/jmp/linux-2.6] / fs / nfs / dir.c
index 78bf72f..2c5ace4 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/pagemap.h>
-#include <linux/smp_lock.h>
 #include <linux/pagevec.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
@@ -1026,12 +1025,12 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
                                res = NULL;
                                goto out;
                        /* This turned out not to be a regular file */
-                       case -EISDIR:
                        case -ENOTDIR:
                                goto no_open;
                        case -ELOOP:
                                if (!(nd->intent.open.flags & O_NOFOLLOW))
                                        goto no_open;
+                       /* case -EISDIR: */
                        /* case -EINVAL: */
                        default:
                                goto out;
@@ -1537,6 +1536,8 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
                old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                dentry->d_parent->d_name.name, dentry->d_name.name);
 
+       nfs_inode_return_delegation(inode);
+
        d_drop(dentry);
        error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
        if (error == 0) {
@@ -1578,56 +1579,46 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct dentry *dentry = NULL, *rehash = NULL;
        int error = -EBUSY;
 
-       /*
-        * To prevent any new references to the target during the rename,
-        * we unhash the dentry and free the inode in advance.
-        */
-       if (!d_unhashed(new_dentry)) {
-               d_drop(new_dentry);
-               rehash = new_dentry;
-       }
-
        dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
                 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
                 atomic_read(&new_dentry->d_count));
 
        /*
-        * First check whether the target is busy ... we can't
-        * safely do _any_ rename if the target is in use.
-        *
-        * For files, make a copy of the dentry and then do a 
-        * silly-rename. If the silly-rename succeeds, the
-        * copied dentry is hashed and becomes the new target.
+        * For non-directories, check whether the target is busy and if so,
+        * make a copy of the dentry and then do a silly-rename. If the
+        * silly-rename succeeds, the copied dentry is hashed and becomes
+        * the new target.
         */
-       if (!new_inode)
-               goto go_ahead;
-       if (S_ISDIR(new_inode->i_mode)) {
-               error = -EISDIR;
-               if (!S_ISDIR(old_inode->i_mode))
-                       goto out;
-       } else if (atomic_read(&new_dentry->d_count) > 2) {
-               int err;
-               /* copy the target dentry's name */
-               dentry = d_alloc(new_dentry->d_parent,
-                                &new_dentry->d_name);
-               if (!dentry)
-                       goto out;
+       if (new_inode && !S_ISDIR(new_inode->i_mode)) {
+               /*
+                * To prevent any new references to the target during the
+                * rename, we unhash the dentry in advance.
+                */
+               if (!d_unhashed(new_dentry)) {
+                       d_drop(new_dentry);
+                       rehash = new_dentry;
+               }
 
-               /* silly-rename the existing target ... */
-               err = nfs_sillyrename(new_dir, new_dentry);
-               if (!err) {
-                       new_dentry = rehash = dentry;
+               if (atomic_read(&new_dentry->d_count) > 2) {
+                       int err;
+
+                       /* copy the target dentry's name */
+                       dentry = d_alloc(new_dentry->d_parent,
+                                        &new_dentry->d_name);
+                       if (!dentry)
+                               goto out;
+
+                       /* silly-rename the existing target ... */
+                       err = nfs_sillyrename(new_dir, new_dentry);
+                       if (err)
+                               goto out;
+
+                       new_dentry = dentry;
                        new_inode = NULL;
-                       /* instantiate the replacement target */
-                       d_instantiate(new_dentry, NULL);
-               } else if (atomic_read(&new_dentry->d_count) > 1)
-                       /* dentry still busy? */
-                       goto out;
-       } else
-               nfs_drop_nlink(new_inode);
+               }
+       }
 
-go_ahead:
        /*
         * ... prune child dentries and writebacks if needed.
         */
@@ -1638,10 +1629,8 @@ go_ahead:
        }
        nfs_inode_return_delegation(old_inode);
 
-       if (new_inode != NULL) {
+       if (new_inode != NULL)
                nfs_inode_return_delegation(new_inode);
-               d_delete(new_dentry);
-       }
 
        error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
                                           new_dir, &new_dentry->d_name);
@@ -1650,6 +1639,8 @@ out:
        if (rehash)
                d_rehash(rehash);
        if (!error) {
+               if (new_inode != NULL)
+                       nfs_drop_nlink(new_inode);
                d_move(old_dentry, new_dentry);
                nfs_set_verifier(new_dentry,
                                        nfs_save_change_attribute(new_dir));
@@ -1944,7 +1935,8 @@ int nfs_permission(struct inode *inode, int mask)
                case S_IFREG:
                        /* NFSv4 has atomic_open... */
                        if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)
-                                       && (mask & MAY_OPEN))
+                                       && (mask & MAY_OPEN)
+                                       && !(mask & MAY_EXEC))
                                goto out;
                        break;
                case S_IFDIR: