提交 5a7a613a 编写于 作者: T Trond Myklebust

NFS: Don't allow NFS silly-renamed files to be deleted, no signal

Commit 73ca1001 broke the code that prevents the client from deleting
a silly renamed dentry.  This affected "delete on last close"
semantics as after that commit, nothing prevented removal of
silly-renamed files.  As a result, a process holding a file open
could easily get an ESTALE on the file in a directory where some
other process issued 'rm -rf some_dir_containing_the_file' twice.
Before the commit, any attempt at unlinking silly renamed files would
fail inside may_delete() with -EBUSY because of the
DCACHE_NFSFS_RENAMED flag.  The following testcase demonstrates
the problem:
  tail -f /nfsmnt/dir/file &
  rm -rf /nfsmnt/dir
  rm -rf /nfsmnt/dir
  # second removal does not fail, 'tail' process receives ESTALE

The problem with the above commit is that it unhashes the old and
new dentries from the lookup path, even in the normal case when
a signal is not encountered and it would have been safe to call
d_move.  Unfortunately the old dentry has the special
DCACHE_NFSFS_RENAMED flag set on it.  Unhashing has the
side-effect that future lookups call d_alloc(), allocating a new
dentry without the special flag for any silly-renamed files.  As a
result, subsequent calls to unlink silly renamed files do not fail
but allow the removal to go through.  This will result in ESTALE
errors for any other process doing operations on the file.

To fix this, go back to using d_move on success.
For the signal case, it's unclear what we may safely do beyond d_drop.
Reported-by: NDave Wysochanski <dwysocha@redhat.com>
Signed-off-by: NTrond Myklebust <Trond.Myklebust@netapp.com>
Acked-by: NJeff Layton <jlayton@redhat.com>
Cc: stable@vger.kernel.org
上级 666b3d80
...@@ -335,20 +335,14 @@ static void nfs_async_rename_done(struct rpc_task *task, void *calldata) ...@@ -335,20 +335,14 @@ static void nfs_async_rename_done(struct rpc_task *task, void *calldata)
struct inode *old_dir = data->old_dir; struct inode *old_dir = data->old_dir;
struct inode *new_dir = data->new_dir; struct inode *new_dir = data->new_dir;
struct dentry *old_dentry = data->old_dentry; struct dentry *old_dentry = data->old_dentry;
struct dentry *new_dentry = data->new_dentry;
if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) { if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
} }
if (task->tk_status != 0) { if (task->tk_status != 0)
nfs_cancel_async_unlink(old_dentry); nfs_cancel_async_unlink(old_dentry);
return;
}
d_drop(old_dentry);
d_drop(new_dentry);
} }
/** /**
...@@ -549,6 +543,18 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) ...@@ -549,6 +543,18 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
error = rpc_wait_for_completion_task(task); error = rpc_wait_for_completion_task(task);
if (error == 0) if (error == 0)
error = task->tk_status; error = task->tk_status;
switch (error) {
case 0:
/* The rename succeeded */
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
d_move(dentry, sdentry);
break;
case -ERESTARTSYS:
/* The result of the rename is unknown. Play it safe by
* forcing a new lookup */
d_drop(dentry);
d_drop(sdentry);
}
rpc_put_task(task); rpc_put_task(task);
out_dput: out_dput:
dput(sdentry); dput(sdentry);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册