NFS: nfs_rename() handle -ERESTARTSYS dentry left behind

An interrupted rename will leave the old dentry behind if the rename
succeeds.  Fix this by moving the final local work of the rename to
rpc_call_done so that the results of the RENAME can always be handled,
even if the original process has already returned with -ERESTARTSYS.

Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
Benjamin Coddington 2017-02-01 00:00:07 -05:00 committed by Anna Schumaker
parent 68e33bd6bb
commit 920b4530fb

View File

@ -2002,6 +2002,29 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
} }
EXPORT_SYMBOL_GPL(nfs_link); EXPORT_SYMBOL_GPL(nfs_link);
static void
nfs_complete_rename(struct rpc_task *task, struct nfs_renamedata *data)
{
struct dentry *old_dentry = data->old_dentry;
struct dentry *new_dentry = data->new_dentry;
struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry);
nfs_mark_for_revalidate(old_inode);
switch (task->tk_status) {
case 0:
if (new_inode != NULL)
nfs_drop_nlink(new_inode);
d_move(old_dentry, new_dentry);
nfs_set_verifier(new_dentry,
nfs_save_change_attribute(data->new_dir));
break;
case -ENOENT:
nfs_dentry_handle_enoent(old_dentry);
}
}
/* /*
* RENAME * RENAME
* FIXME: Some nfsds, like the Linux user space nfsd, may generate a * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
@ -2084,7 +2107,8 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_inode != NULL) if (new_inode != NULL)
NFS_PROTO(new_inode)->return_delegation(new_inode); NFS_PROTO(new_inode)->return_delegation(new_inode);
task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL); task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry,
nfs_complete_rename);
if (IS_ERR(task)) { if (IS_ERR(task)) {
error = PTR_ERR(task); error = PTR_ERR(task);
goto out; goto out;
@ -2094,21 +2118,11 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (error == 0) if (error == 0)
error = task->tk_status; error = task->tk_status;
rpc_put_task(task); rpc_put_task(task);
nfs_mark_for_revalidate(old_inode);
out: out:
if (rehash) if (rehash)
d_rehash(rehash); d_rehash(rehash);
trace_nfs_rename_exit(old_dir, old_dentry, trace_nfs_rename_exit(old_dir, old_dentry,
new_dir, new_dentry, error); new_dir, new_dentry, error);
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));
} else if (error == -ENOENT)
nfs_dentry_handle_enoent(old_dentry);
/* new dentry created? */ /* new dentry created? */
if (dentry) if (dentry)
dput(dentry); dput(dentry);