2.2.7 NFS v2 server "mutant" filehandles hurt on Solaris

Philippe Troin (phil@fifi.org)
05 May 1999 01:36:26 -0700


--Multipart_Wed_May__5_01:36:26_1999-1
Content-Type: text/plain; charset=US-ASCII

I had quite a few troubles connecting Solaris NFS v2 clients on linux
servers. Some typical behavior:

o Mounting goes fine for a while...
o After some time, various clients get error in getpwd().
o If the mount point is not at the root of the exported fs, one can
chdir() above the mount point but under the exported region.
Eg: linux exports linux:/foo
linux:/foo/bar is mounted on solaris:/mnt
chdir(/mnt/junk)
chdir(..)
chdir(..)
now you're in linux:/foo rather than being in solaris:/

o When this happens, the tcpdump shows that .. is looked up in the
root of the exported filesystem (linux:/foo), and linux returns
NFSERR_ACCESS (note that solaris returns NFSERR_NOENT).

o The trouble also happens when the linux server is rebooted.

My scenario to explain the problem is that solaris uses the root fh to
remember when to switch back to the local fs. A linux client doesn't
suffer from the problem because of the VFS . and .. magic (I think).

I came to the enclosed patch (not fully tested). It works by mapping
the root of the exported directory to a special dentry
(NFS_ROOTFH_MAGIC) when xdr'ing out the filehandles (and also while
getting the root fh in nfsdcrl), and by remapping this special cookie
back to the root dentry (as stored in the export entry) when
validating the filehandle (I'm not sure that the export root dentry is
reupdated when it changes in the export entry).

One remaining problem is that it won't work when mounting
subdirectories within the mount point. I don't know how to handle this
except by creating additional export entries for every requested mount
point :-(

I can also think of other approaches:
- marking all svc_fh that are requested by mountd with a don't prune
from fhcache flag. (Problem: the dentry might vary accross reboots)
- locking the dent in the dcache (suffers from the same problem, plus
the dcache pruning might invalidate the dent).

I'm sure this patch is suboptimal. Anyone has a better idea ?
However it seems to do the trick for mounting the exported directory.

NOTE: THIS IS NOT VERY WELL TESTED.

Phil.

--Multipart_Wed_May__5_01:36:26_1999-1
Content-Type: application/octet-stream; type=patch
Content-Disposition: attachment; filename="nfsd.patch"
Content-Transfer-Encoding: quoted-printable

diff -ru /usr/local/src/linux-2.2.7/fs/nfsd/export.c linux/fs/nfsd/export=
=2Ec
--- /usr/local/src/linux-2.2.7/fs/nfsd/export.c Tue Dec 29 11:42:25 1998
+++ linux/fs/nfsd/export.c Wed May 5 01:08:42 1999
@@ -506,6 +506,7 @@
fh_init(&fh);
fh_compose(&fh, exp, dentry);
memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
+ fh_encode_root(f, &fh);
fh_put(&fh);
return 0;
=

diff -ru /usr/local/src/linux-2.2.7/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c=

--- /usr/local/src/linux-2.2.7/fs/nfsd/nfsfh.c Sat Mar 20 12:28:14 1999
+++ linux/fs/nfsd/nfsfh.c Wed May 5 01:13:26 1999
@@ -70,6 +70,13 @@
char name[1];
};
=

+/* This is the magic root fh passed when root of the exported fs is pass=
ed
+ or to the mounting host.
+ The idea is that the mount point always has the same fh. The contrary=
=

+ breaks solaris (when the dentry changes).
+*/
+#define FH_ROOTFH_MAGIC ((struct dentry*)0xf4001de1)
+
static struct nfsd_fixup * find_cached_lookup(kdev_t dev, ino_t dir, ino=
_t ino)
{
struct list_head *tmp =3D fixup_head.next;
@@ -1073,6 +1080,14 @@
/* Set user creds if we haven't done so already. */
nfsd_setuser(rqstp, exp);
=

+ /* Map the export point dentry */
+ if (exp->ex_dev =3D=3D u32_to_kdev_t(fh->fh_dev)
+ && exp->ex_ino =3D=3D u32_to_ino_t(fh->fh_ino)
+ && fh->fh_dcookie =3D=3D FH_ROOTFH_MAGIC) {
+ dprintk(KERN_WARNING "nfsd: mapped root dentry\n");
+ fh->fh_dcookie =3D exp->ex_dentry;
+ }
+
/*
* Look up the dentry using the NFS file handle.
*/
@@ -1391,4 +1406,25 @@
printk(KERN_INFO =

"NFSD: ino_t is %d bytes, using lower 4 bytes\n",
sizeof(ino_t));
+}
+
+void
+fh_encode_root(struct knfs_fh *mfh, const struct svc_fh *fh)
+{
+ struct inode *ino;
+ struct svc_export *exp;
+
+ if ((struct knfs_fh*)fh =3D=3D mfh) {
+ printk(KERN_ERR "fh_encode_root: called with mfh=3D=3Dfh\n");
+ return;
+ } =

+
+ ino =3D fh->fh_handle.fh_dcookie->d_inode;
+ exp =3D fh->fh_export;
+
+ if (ino->i_ino =3D=3D exp->ex_ino && ino->i_dev =3D=3D exp->ex_dev) {
+ dprintk(KERN_WARNING "fh_encode_root root dentry in %s\n",
+ exp->ex_path);
+ mfh->fh_dcookie =3D FH_ROOTFH_MAGIC;
+ }
}
diff -ru /usr/local/src/linux-2.2.7/fs/nfsd/nfsxdr.c linux/fs/nfsd/nfsxdr=
=2Ec
--- /usr/local/src/linux-2.2.7/fs/nfsd/nfsxdr.c Wed Nov 26 13:08:38 1997
+++ linux/fs/nfsd/nfsxdr.c Wed May 5 00:57:52 1999
@@ -86,6 +86,7 @@
encode_fh(u32 *p, struct svc_fh *fhp)
{
memcpy(p, &fhp->fh_handle, sizeof(struct knfs_fh));
+ fh_encode_root((struct knfs_fh*)p, fhp);
return p + (sizeof(struct knfs_fh) >> 2);
}
=

diff -ru /usr/local/src/linux-2.2.7/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- /usr/local/src/linux-2.2.7/fs/nfsd/vfs.c Mon Apr 12 10:03:45 1999
+++ linux/fs/nfsd/vfs.c Tue May 4 23:41:21 1999
@@ -554,7 +554,7 @@
/* clear setuid/setgid flag after write */
if (err >=3D 0 && (inode->i_mode & (S_ISUID | S_ISGID))) {
struct iattr ia;
- kernel_cap_t saved_cap;
+ kernel_cap_t saved_cap=3D0;
=

ia.ia_valid =3D ATTR_MODE;
ia.ia_mode =3D inode->i_mode & ~(S_ISUID | S_ISGID);
@@ -759,7 +759,7 @@
struct inode *inode;
struct iattr newattrs;
int err;
- kernel_cap_t saved_cap;
+ kernel_cap_t saved_cap=3D0;
=

err =3D fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE | MAY_TRUNC);
if (err)
@@ -1314,7 +1314,7 @@
{
struct inode *inode =3D dentry->d_inode;
int err;
- kernel_cap_t saved_cap;
+ kernel_cap_t saved_cap=3D0;
=

if (acc =3D=3D MAY_NOP)
return 0;
diff -ru /usr/local/src/linux-2.2.7/include/linux/nfsd/nfsfh.h linux/incl=
ude/linux/nfsd/nfsfh.h
--- /usr/local/src/linux-2.2.7/include/linux/nfsd/nfsfh.h Thu Apr 29 23:4=
5:42 1999
+++ linux/include/linux/nfsd/nfsfh.h Wed May 5 01:06:09 1999
@@ -99,6 +99,7 @@
/*
* Function prototypes
*/
+void fh_encode_root(struct knfs_fh *, const struct svc_fh *);
u32 fh_verify(struct svc_rqst *, struct svc_fh *, int, int);
void fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
void fh_update(struct svc_fh *);

--Multipart_Wed_May__5_01:36:26_1999-1--

-
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/