locks: break delegations on rename
authorJ. Bruce Fields <bfields@redhat.com>
Tue, 20 Sep 2011 20:59:58 +0000 (16:59 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 9 Nov 2013 05:16:43 +0000 (00:16 -0500)
Cc: David Howells <dhowells@redhat.com>
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
fs/cachefiles/namei.c
fs/ecryptfs/inode.c
fs/namei.c
fs/nfsd/vfs.c
include/linux/fs.h

index 9243dfab43d393f360a0f78c7dd7b8677455676f..80b019bf969ca1b22431a418ff4afcc5c2fb523e 100644 (file)
@@ -105,8 +105,8 @@ static inline void ll_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt,
 #define ll_vfs_unlink(inode,entry,mnt)   vfs_unlink(inode,entry)
 #define ll_vfs_mknod(dir,entry,mnt,mode,dev)    vfs_mknod(dir,entry,mode,dev)
 #define ll_security_inode_unlink(dir,entry,mnt) security_inode_unlink(dir,entry)
-#define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1) \
-               vfs_rename(old,old_dir,new,new_dir)
+#define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1,delegated_inode) \
+               vfs_rename(old,old_dir,new,new_dir,delegated_inode)
 
 #define cfs_bio_io_error(a,b)   bio_io_error((a))
 #define cfs_bio_endio(a,b,c)    bio_endio((a),(c))
index 18e1b47a1d65aaebf5c3312670e43017232780f8..4ed7c9f0a8be700a3b31e260cb5aaca414a825cf 100644 (file)
@@ -220,7 +220,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt,
                GOTO(put_old, err = PTR_ERR(dchild_new));
 
        err = ll_vfs_rename(dir->d_inode, dchild_old, mnt,
-                           dir->d_inode, dchild_new, mnt);
+                           dir->d_inode, dchild_new, mnt, NULL);
 
        dput(dchild_new);
 put_old:
index 31d480c0e046ac6673ffff6dcafbf5f9574425db..ca65f39dc8dc38823bb4a6070c717671acffba72 100644 (file)
@@ -396,7 +396,7 @@ try_again:
                cachefiles_io_error(cache, "Rename security error %d", ret);
        } else {
                ret = vfs_rename(dir->d_inode, rep,
-                                cache->graveyard->d_inode, grave);
+                                cache->graveyard->d_inode, grave, NULL);
                if (ret != 0 && ret != -ENOMEM)
                        cachefiles_io_error(cache,
                                            "Rename failed with error %d", ret);
index dc60b8bd09ecd6c782a4c924ddae02b2488579e2..c23b01bb7e04bf5276240e6f02be98f628479261 100644 (file)
@@ -640,7 +640,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto out_lock;
        }
        rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
-                       lower_new_dir_dentry->d_inode, lower_new_dentry);
+                       lower_new_dir_dentry->d_inode, lower_new_dentry,
+                       NULL);
        if (rc)
                goto out_lock;
        if (target_inode)
index cfaeaae0f2dbb2039cf8dbea6d14f70eff89f551..ce7e580e4e14ba825c9486aa957761eb38143794 100644 (file)
@@ -4022,7 +4022,8 @@ out:
 }
 
 static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
-                           struct inode *new_dir, struct dentry *new_dentry)
+                           struct inode *new_dir, struct dentry *new_dentry,
+                           struct inode **delegated_inode)
 {
        struct inode *target = new_dentry->d_inode;
        struct inode *source = old_dentry->d_inode;
@@ -4039,6 +4040,14 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
        if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
                goto out;
 
+       error = try_break_deleg(source, delegated_inode);
+       if (error)
+               goto out;
+       if (target) {
+               error = try_break_deleg(target, delegated_inode);
+               if (error)
+                       goto out;
+       }
        error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
        if (error)
                goto out;
@@ -4053,8 +4062,30 @@ out:
        return error;
 }
 
+/**
+ * vfs_rename - rename a filesystem object
+ * @old_dir:   parent of source
+ * @old_dentry:        source
+ * @new_dir:   parent of destination
+ * @new_dentry:        destination
+ * @delegated_inode: returns an inode needing a delegation break
+ *
+ * The caller must hold multiple mutexes--see lock_rename()).
+ *
+ * If vfs_rename discovers a delegation in need of breaking at either
+ * the source or destination, it will return -EWOULDBLOCK and return a
+ * reference to the inode in delegated_inode.  The caller should then
+ * break the delegation and retry.  Because breaking a delegation may
+ * take a long time, the caller should drop all locks before doing
+ * so.
+ *
+ * Alternatively, a caller may pass NULL for delegated_inode.  This may
+ * be appropriate for callers that expect the underlying filesystem not
+ * to be NFS exported.
+ */
 int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
-              struct inode *new_dir, struct dentry *new_dentry)
+              struct inode *new_dir, struct dentry *new_dentry,
+              struct inode **delegated_inode)
 {
        int error;
        int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry);
@@ -4082,7 +4113,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (is_dir)
                error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
        else
-               error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+               error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry,delegated_inode);
        if (!error)
                fsnotify_move(old_dir, new_dir, old_name, is_dir,
                              new_dentry->d_inode, old_dentry);
@@ -4098,6 +4129,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
        struct dentry *old_dentry, *new_dentry;
        struct dentry *trap;
        struct nameidata oldnd, newnd;
+       struct inode *delegated_inode = NULL;
        struct filename *from;
        struct filename *to;
        unsigned int lookup_flags = 0;
@@ -4137,6 +4169,7 @@ retry:
        newnd.flags &= ~LOOKUP_PARENT;
        newnd.flags |= LOOKUP_RENAME_TARGET;
 
+retry_deleg:
        trap = lock_rename(new_dir, old_dir);
 
        old_dentry = lookup_hash(&oldnd);
@@ -4173,13 +4206,19 @@ retry:
        if (error)
                goto exit5;
        error = vfs_rename(old_dir->d_inode, old_dentry,
-                                  new_dir->d_inode, new_dentry);
+                                  new_dir->d_inode, new_dentry,
+                                  &delegated_inode);
 exit5:
        dput(new_dentry);
 exit4:
        dput(old_dentry);
 exit3:
        unlock_rename(new_dir, old_dir);
+       if (delegated_inode) {
+               error = break_deleg_wait(&delegated_inode);
+               if (!error)
+                       goto retry_deleg;
+       }
        mnt_drop_write(oldnd.path.mnt);
 exit2:
        if (retry_estale(error, lookup_flags))
index 7a810235d5994a69d3c395e9299a26a405bc3840..45bf0295894db1c5a005206e1b1bd2d9b8cae20b 100644 (file)
@@ -1837,7 +1837,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
                if (host_err)
                        goto out_dput_new;
        }
-       host_err = vfs_rename(fdir, odentry, tdir, ndentry);
+       host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL);
        if (!host_err) {
                host_err = commit_metadata(tfhp);
                if (!host_err)
index 931f919f44e110f1cf2af1829dec19b4e670033d..5bcff883fa90ec6fa983411db674daa231bd01d6 100644 (file)
@@ -1456,7 +1456,7 @@ extern int vfs_symlink(struct inode *, struct dentry *, const char *);
 extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **);
 
 /*
  * VFS dentry helper functions.