diff options
author | Benjamin Coddington <bcodding@redhat.com> | 2017-02-01 00:00:07 -0500 |
---|---|---|
committer | Anna Schumaker <Anna.Schumaker@Netapp.com> | 2017-02-08 17:02:44 -0500 |
commit | 920b4530fb80430ff30ef83efe21ba1fa5623731 (patch) | |
tree | e09a3508c339c4f6feeba20f14a7104e77370a03 | |
parent | 68e33bd6bbb79819e5cb7bce26559191b144c465 (diff) |
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>
-rw-r--r-- | fs/nfs/dir.c | 36 |
1 files changed, 25 insertions, 11 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index fad81041f5ab..fb499a3f21b5 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -2002,6 +2002,29 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) | |||
2002 | } | 2002 | } |
2003 | EXPORT_SYMBOL_GPL(nfs_link); | 2003 | EXPORT_SYMBOL_GPL(nfs_link); |
2004 | 2004 | ||
2005 | static void | ||
2006 | nfs_complete_rename(struct rpc_task *task, struct nfs_renamedata *data) | ||
2007 | { | ||
2008 | struct dentry *old_dentry = data->old_dentry; | ||
2009 | struct dentry *new_dentry = data->new_dentry; | ||
2010 | struct inode *old_inode = d_inode(old_dentry); | ||
2011 | struct inode *new_inode = d_inode(new_dentry); | ||
2012 | |||
2013 | nfs_mark_for_revalidate(old_inode); | ||
2014 | |||
2015 | switch (task->tk_status) { | ||
2016 | case 0: | ||
2017 | if (new_inode != NULL) | ||
2018 | nfs_drop_nlink(new_inode); | ||
2019 | d_move(old_dentry, new_dentry); | ||
2020 | nfs_set_verifier(new_dentry, | ||
2021 | nfs_save_change_attribute(data->new_dir)); | ||
2022 | break; | ||
2023 | case -ENOENT: | ||
2024 | nfs_dentry_handle_enoent(old_dentry); | ||
2025 | } | ||
2026 | } | ||
2027 | |||
2005 | /* | 2028 | /* |
2006 | * RENAME | 2029 | * RENAME |
2007 | * FIXME: Some nfsds, like the Linux user space nfsd, may generate a | 2030 | * 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, | |||
2084 | if (new_inode != NULL) | 2107 | if (new_inode != NULL) |
2085 | NFS_PROTO(new_inode)->return_delegation(new_inode); | 2108 | NFS_PROTO(new_inode)->return_delegation(new_inode); |
2086 | 2109 | ||
2087 | task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL); | 2110 | task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, |
2111 | nfs_complete_rename); | ||
2088 | if (IS_ERR(task)) { | 2112 | if (IS_ERR(task)) { |
2089 | error = PTR_ERR(task); | 2113 | error = PTR_ERR(task); |
2090 | goto out; | 2114 | goto out; |
@@ -2094,21 +2118,11 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
2094 | if (error == 0) | 2118 | if (error == 0) |
2095 | error = task->tk_status; | 2119 | error = task->tk_status; |
2096 | rpc_put_task(task); | 2120 | rpc_put_task(task); |
2097 | nfs_mark_for_revalidate(old_inode); | ||
2098 | out: | 2121 | out: |
2099 | if (rehash) | 2122 | if (rehash) |
2100 | d_rehash(rehash); | 2123 | d_rehash(rehash); |
2101 | trace_nfs_rename_exit(old_dir, old_dentry, | 2124 | trace_nfs_rename_exit(old_dir, old_dentry, |
2102 | new_dir, new_dentry, error); | 2125 | new_dir, new_dentry, error); |
2103 | if (!error) { | ||
2104 | if (new_inode != NULL) | ||
2105 | nfs_drop_nlink(new_inode); | ||
2106 | d_move(old_dentry, new_dentry); | ||
2107 | nfs_set_verifier(new_dentry, | ||
2108 | nfs_save_change_attribute(new_dir)); | ||
2109 | } else if (error == -ENOENT) | ||
2110 | nfs_dentry_handle_enoent(old_dentry); | ||
2111 | |||
2112 | /* new dentry created? */ | 2126 | /* new dentry created? */ |
2113 | if (dentry) | 2127 | if (dentry) |
2114 | dput(dentry); | 2128 | dput(dentry); |