NFS: only invalidate dentrys that are clearly invalid.
Subsystems (3)
PATCH 1/1 NFS: only invalidate dentrys that are clearly invalid.
Since commit bafc9b754f75 ("vfs: More precise tests in d_invalidate") in v3.18, a return of '0' from ->d_revalidate() will cause the dentry to be invalidated even if it has filesystems mounted on or it or on a descendant. The mounted filesystem is unmounted.
This means we need to be careful not to return 0 unless the directory referred to truly is invalid. So -ESTALE or -ENOENT should invalidate the directory. Other errors such a -EPERM or -ERESTARTSYS should be returned from ->d_revalidate() so they are propagated to the caller.
A particular problem can be demonstrated by:
1/ mount an NFS filesystem using NFSv3 on /mnt
2/ mount any other filesystem on /mnt/foo
3/ ls /mnt/foo
4/ turn off network, or otherwise make the server unable to respond 5/ ls /mnt/foo &
6/ cat /proc/$!/stack # note that nfs_lookup_revalidate is in the call stack 7/ kill -9 $! # this results in -ERESTARTSYS being returned
8/ observe that /mnt/foo has been unmounted.
This patch changes nfs_lookup_revalidate() to only treat
-ESTALE from nfs_lookup_verify_inode() and -ESTALE or -ENOENT from ->lookup()
as indicating an invalid inode. Other errors are returned.
Also nfs_check_inode_attributes() is changed to return -ESTALE rather than -EIO. This is consistent with the error returned in similar circumstances from nfs_update_inode().
As this bug allows any user to unmount a filesystem mounted on an NFS filesystem, this fix is suitable for stable kernels.
--- fs/nfs/dir.c | 12 ++++++++---- fs/nfs/inode.c | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-)
fs/nfs/dir.c +8 -4
@@ -1115,11 +1115,13 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)/* Force a full look up iff the parent directory has changed */ if (!nfs_is_exclusive_create (dir, flags) &&nfs_check_verifier (dir, dentry, flags &LOOKUP_RCU )) { - -if (nfs_lookup_verify_inode (inode, flags)) { + error =nfs_lookup_verify_inode (inode, flags); +if (error) {if (flags &LOOKUP_RCU )return -ECHILD ; - goto out_zap_parent; +if (error == -ESTALE ) + goto out_zap_parent; + goto out_error; }nfs_advise_use_readdirplus (dir); goto out_valid;
@@ -1144,8 +1146,10 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)trace_nfs_lookup_revalidate_enter (dir, dentry, flags); error =NFS_PROTO (dir)->lookup (dir, &dentry->d_name, fhandle, fattr, label);trace_nfs_lookup_revalidate_exit (dir, dentry, flags, error); -if (error) +if (error == -ESTALE || error == -ENOENT ) goto out_bad; +if (error) + goto out_error;if (nfs_compare_fh (NFS_FH (inode), fhandle)) goto out_bad;if ((error =nfs_refresh_inode (inode, fattr)) !=0 )
fs/nfs/inode.c +2 -2
@@ -1321,9 +1321,9 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fatreturn 0 ;/* Has the inode gone and changed behind our back? */ if ((fattr->valid &NFS_ATTR_FATTR_FILEID ) && nfsi->fileid != fattr->fileid) -return -EIO ; +return -ESTALE ;if ((fattr->valid &NFS_ATTR_FATTR_TYPE ) && (inode->i_mode &S_IFMT ) != (fattr->mode &S_IFMT )) -return -EIO ; +return -ESTALE ;if (!nfs_file_has_buffered_writers (nfsi)) {/* Verify a few of the more important attributes */
Signature · 1 line
2.12.2