Skip to content

Commit 818a8db

Browse files
bcodding-rhamschuma-ntap
authored andcommitted
NFS: nfs_rename() - revalidate directories on -ERESTARTSYS
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 <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent a7a3b1e commit 818a8db

3 files changed

Lines changed: 19 additions & 1 deletion

File tree

fs/nfs/dir.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2056,7 +2056,11 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
20562056
}
20572057

20582058
error = rpc_wait_for_completion_task(task);
2059-
if (error == 0)
2059+
if (error != 0) {
2060+
((struct nfs_renamedata *)task->tk_calldata)->cancelled = 1;
2061+
/* Paired with the atomic_dec_and_test() barrier in rpc_do_put_task() */
2062+
smp_wmb();
2063+
} else
20602064
error = task->tk_status;
20612065
rpc_put_task(task);
20622066
out:

fs/nfs/unlink.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,19 @@ static void nfs_async_rename_release(void *calldata)
288288
if (d_really_is_positive(data->old_dentry))
289289
nfs_mark_for_revalidate(d_inode(data->old_dentry));
290290

291+
/* The result of the rename is unknown. Play it safe by
292+
* forcing a new lookup */
293+
if (data->cancelled) {
294+
spin_lock(&data->old_dir->i_lock);
295+
nfs_force_lookup_revalidate(data->old_dir);
296+
spin_unlock(&data->old_dir->i_lock);
297+
if (data->new_dir != data->old_dir) {
298+
spin_lock(&data->new_dir->i_lock);
299+
nfs_force_lookup_revalidate(data->new_dir);
300+
spin_unlock(&data->new_dir->i_lock);
301+
}
302+
}
303+
291304
dput(data->old_dentry);
292305
dput(data->new_dentry);
293306
iput(data->old_dir);

include/linux/nfs_xdr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,7 @@ struct nfs_renamedata {
15331533
struct nfs_fattr new_fattr;
15341534
void (*complete)(struct rpc_task *, struct nfs_renamedata *);
15351535
long timeout;
1536+
bool cancelled;
15361537
};
15371538

15381539
struct nfs_access_entry;

0 commit comments

Comments
 (0)