NFS: nfs_rename() - revalidate directories on -ERESTARTSYS
authorBenjamin Coddington <bcodding@redhat.com>
Fri, 16 Jun 2017 15:13:00 +0000 (11:13 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Thu, 13 Jul 2017 19:58:04 +0000 (15:58 -0400)
An interrupted rename will leave the old dentry behind if the rename
succeeds.  Fix this by forcing a lookup the next time through
->d_revalidate.

A previous attempt at solving this problem took the approach to complete
the work of the rename asynchronously, however that approach was wrong
since it would allow the d_move() to occur after the directory's i_mutex
had been dropped by the original process.

Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/dir.c
fs/nfs/unlink.c
include/linux/nfs_xdr.h

index 9688e9bb13dc2692ab5ab78e903ba826bf5c6ade..ab69ebb483507e28391bda84f643e8a2ca2737fc 100644 (file)
@@ -2056,7 +2056,11 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        }
 
        error = rpc_wait_for_completion_task(task);
-       if (error == 0)
+       if (error != 0) {
+               ((struct nfs_renamedata *)task->tk_calldata)->cancelled = 1;
+               /* Paired with the atomic_dec_and_test() barrier in rpc_do_put_task() */
+               smp_wmb();
+       } else
                error = task->tk_status;
        rpc_put_task(task);
 out:
index 191aa577dd1f3a2d31b23fd430748e58bd079138..e3949d93085c8bc53692df7e8a4fd6b49052280c 100644 (file)
@@ -288,6 +288,19 @@ static void nfs_async_rename_release(void *calldata)
        if (d_really_is_positive(data->old_dentry))
                nfs_mark_for_revalidate(d_inode(data->old_dentry));
 
+       /* The result of the rename is unknown. Play it safe by
+        * forcing a new lookup */
+       if (data->cancelled) {
+               spin_lock(&data->old_dir->i_lock);
+               nfs_force_lookup_revalidate(data->old_dir);
+               spin_unlock(&data->old_dir->i_lock);
+               if (data->new_dir != data->old_dir) {
+                       spin_lock(&data->new_dir->i_lock);
+                       nfs_force_lookup_revalidate(data->new_dir);
+                       spin_unlock(&data->new_dir->i_lock);
+               }
+       }
+
        dput(data->old_dentry);
        dput(data->new_dentry);
        iput(data->old_dir);
index 9b42bffbe07b03f79bd5a1992e519390b7220866..9463eeff9e3c8057ea3ae0395dc197271cfe97ba 100644 (file)
@@ -1533,6 +1533,7 @@ struct nfs_renamedata {
        struct nfs_fattr        new_fattr;
        void (*complete)(struct rpc_task *, struct nfs_renamedata *);
        long timeout;
+       bool cancelled;
 };
 
 struct nfs_access_entry;