Skip to content

Commit d2fa405

Browse files
NeilBrowngregkh
authored andcommitted
NFS: only invalidate dentrys that are clearly invalid.
commit cc89684c9a265828ce061037f1f79f4a68ccd3f7 upstream. Since commit bafc9b7 ("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. Fixes: bafc9b7 ("vfs: More precise tests in d_invalidate") Signed-off-by: NeilBrown <neilb@suse.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent bba6b69 commit d2fa405

2 files changed

Lines changed: 10 additions & 6 deletions

File tree

fs/nfs/dir.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,11 +1135,13 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
11351135
/* Force a full look up iff the parent directory has changed */
11361136
if (!nfs_is_exclusive_create(dir, flags) &&
11371137
nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
1138-
1139-
if (nfs_lookup_verify_inode(inode, flags)) {
1138+
error = nfs_lookup_verify_inode(inode, flags);
1139+
if (error) {
11401140
if (flags & LOOKUP_RCU)
11411141
return -ECHILD;
1142-
goto out_zap_parent;
1142+
if (error == -ESTALE)
1143+
goto out_zap_parent;
1144+
goto out_error;
11431145
}
11441146
goto out_valid;
11451147
}
@@ -1163,8 +1165,10 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
11631165
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
11641166
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
11651167
trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error);
1166-
if (error)
1168+
if (error == -ESTALE || error == -ENOENT)
11671169
goto out_bad;
1170+
if (error)
1171+
goto out_error;
11681172
if (nfs_compare_fh(NFS_FH(inode), fhandle))
11691173
goto out_bad;
11701174
if ((error = nfs_refresh_inode(inode, fattr)) != 0)

fs/nfs/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,9 +1241,9 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
12411241
return 0;
12421242
/* Has the inode gone and changed behind our back? */
12431243
if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
1244-
return -EIO;
1244+
return -ESTALE;
12451245
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
1246-
return -EIO;
1246+
return -ESTALE;
12471247

12481248
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
12491249
inode->i_version != fattr->change_attr)

0 commit comments

Comments
 (0)