Stale NFS file handles and race conditions...

Trond Myklebust (trond.myklebust@fys.uio.no)
Fri, 27 Aug 1999 15:18:12 +0200 (CEST)


>>>>> " " == Steve Mcclure <smcclure@emc.com> writes:

nfs1> mkdir foo
nfs2> ls -sl foo
total 0
nfs1> rm -rf foo mkdir foo
nfs2> /bin/sh -c "( cd foo ; ps -ef > a ) & ( cd foo ; ps -ef > b )"
/bin/sh: a: Stale NFS file handle /bin/sh: b: Stale NFS file handle
nfs2>

> As you can see, that doesn't work. If you notice, I've created
> two processes which both change directory into the same
> directory, then try to create files within that directory.

Urgh. The problem is that 'foo' is being revalidated twice. This means
that when we pass the following section in
dir.c:nfs_lookup_revalidate(), we decide to keep the dentry for 'foo'
since d_count == 2.

/* Filehandle matches? */
if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
if (!list_empty(&dentry->d_subdirs))
shrink_dcache_parent(dentry);
if (dentry->d_count < 2)
goto out_bad;
}

At the time (linux-2.2.4), the protection against d_count < 2 was
necessary because of a bug in the linux NFS servers which caused
filehandles to change over the course of the lifetime of the file (I
believe there was a case of timestamping). At the time, the 'stale
inode' verification code in nfs_fhget() couldn't handle unhashed
dentries, which meant that open files could suddenly find that their
filehandle was set to zero.

Since then, the stale inode code has changed (and hopefully
improved) so as to be able to handle unhashed dentries. Perhaps it is
better to relax the above code, and to trust nfs_fhget to get things
right?

Cheers,
Trond

--- fs/nfs/inode.c.orig Mon Aug 9 21:05:05 1999
+++ fs/nfs/inode.c Fri Aug 27 14:38:39 1999
@@ -408,6 +408,8 @@
unhashed = 0;
while ((tmp = tmp->next) != head) {
struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+ if (!list_empty(&dentry->d_subdirs))
+ shrink_dcache_parent(dentry);
dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
dentry->d_count, !list_empty(&dentry->d_hash));
@@ -417,7 +419,7 @@
dput(dentry);
goto restart;
}
- if (!list_empty(&dentry->d_hash))
+ if (list_empty(&dentry->d_hash))
unhashed++;
}
return unhashed;
--- fs/nfs/dir.c.orig Mon Aug 9 21:04:57 1999
+++ fs/nfs/dir.c Fri Aug 27 14:37:43 1999
@@ -462,12 +462,8 @@
goto out_bad;

/* Filehandle matches? */
- if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
- if (!list_empty(&dentry->d_subdirs))
- shrink_dcache_parent(dentry);
- if (dentry->d_count < 2)
- goto out_bad;
- }
+ if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh)))
+ goto out_bad;

/* Ok, remeber that we successfully checked it.. */
nfs_renew_times(dentry);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/