Commit e46e9c89 authored by Trond Myklebust's avatar Trond Myklebust

NFSv2/v3/v4: ESTALE should not be a permanent condition on directories.

Although it usually means that someone has deleted a file on the server,
the ESTALE error may also indicate that the sysadmin has used exportfs to
deny our client access to the server. Most NFS implementations therefore
consider it a non-permanent condition, and allow inodes to "recover" when
the sysadmin re-enables access.
If, however, you want to work with broken servers, like unfsd, that reuse
filehandles for new files after the original file gets deleted, then
"recovery" is impossible, since it may be that the filehandle now points
to a different file. Note that this is broken server behaviour that may
happen even without us ever seeing the ESTALE error.
In order to minimize (but we can never eliminate entirely) this race
condition on unfsd servers, Linux has traditionally made ESTALE a
permanent condition on all filehandles except the root filehandle.

The problem is that if we apply this strict staleness criterion to
directories (particularly so for he current directory), then all
processes will need to re-walk the path starting from the mount point,
in order to recover from the sysadmin intervention case. As this is not
usual on other *NIX implementations, and may in any case be undermined by
caching rules etc, this is being seen as a usability problem.

This patch makes ESTALE a non-permanent condition on directories, but
preserves the current behaviour for non-directories.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@fys.uio.no>
parent a635179e
......@@ -605,7 +605,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
return 0;
if (nfs_compare_fh(NFS_FH(inode), fh))
return 0;
if (is_bad_inode(inode))
if (is_bad_inode(inode) || NFS_STALE(inode))
return 0;
return 1;
}
......@@ -949,7 +949,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
lock_kernel();
if (!inode || is_bad_inode(inode))
goto out_nowait;
if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode)
if (NFS_STALE(inode))
goto out_nowait;
while (NFS_REVALIDATING(inode)) {
......@@ -973,9 +973,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status);
if (status == -ESTALE) {
NFS_FLAGS(inode) |= NFS_INO_STALE;
if (inode != inode->i_sb->s_root->d_inode)
remove_inode_hash(inode);
nfs_zap_caches(inode);
if (!S_ISDIR(inode->i_mode))
NFS_FLAGS(inode) |= NFS_INO_STALE;
}
goto out;
}
......@@ -1014,7 +1014,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
NFS_FLAGS(inode) &= ~NFS_INO_STALE;
out:
NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
wake_up(&nfsi->nfs_i_wait);
......@@ -1335,7 +1334,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
*/
nfs_invalidate_inode(inode);
out_err:
return -EIO;
NFS_FLAGS(inode) |= NFS_INO_STALE;
return -ESTALE;
}
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment