OSDN Git Service

NFS: unlink/rmdir shouldn't call d_delete() twice on ENOENT
authorNeilBrown <neilb@suse.de>
Thu, 18 Aug 2022 23:55:59 +0000 (09:55 +1000)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Sat, 20 Aug 2022 00:31:36 +0000 (20:31 -0400)
commitf16857e62bac60786104c020ad7c86e2163b2c5b
tree1f34d39d7ebcd810007bfc6bb304289ab0857fa6
parent5f6277a0c15e1ea54b6fd3d78c9fff7bfe42556c
NFS: unlink/rmdir shouldn't call d_delete() twice on ENOENT

nfs_unlink() calls d_delete() twice if it receives ENOENT from the
server - once in nfs_dentry_handle_enoent() from nfs_safe_remove and
once in nfs_dentry_remove_handle_error().

nfs_rmddir() also calls it twice - the nfs_dentry_handle_enoent() call
is direct and inside a region locked with ->rmdir_sem

It is safe to call d_delete() twice if the refcount > 1 as the dentry is
simply unhashed.
If the refcount is 1, the first call sets d_inode to NULL and the second
call crashes.

This patch guards the d_delete() call from nfs_dentry_handle_enoent()
leaving the one under ->remdir_sem in case that is important.

In mainline it would be safe to remove the d_delete() call.  However in
older kernels to which this might be backported, that would change the
behaviour of nfs_unlink().  nfs_unlink() used to unhash the dentry which
resulted in nfs_dentry_handle_enoent() not calling d_delete().  So in
older kernels we need the d_delete() in nfs_dentry_remove_handle_error()
when called from nfs_unlink() but not when called from nfs_rmdir().

To make the code work correctly for old and new kernels, and from both
nfs_unlink() and nfs_rmdir(), we protect the d_delete() call with
simple_positive().  This ensures it is never called in a circumstance
where it could crash.

Fixes: 3c59366c207e ("NFS: don't unhash dentry during unlink/rename")
Fixes: 9019fb391de0 ("NFS: Label the dentry with a verifier in nfs_rmdir() and nfs_unlink()")
Signed-off-by: NeilBrown <neilb@suse.de>
Tested-by: Olga Kornievskaia <aglo@umich.edu>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/dir.c