[PATCH] for knfsd (Please test!!!)

G. Allen Morris III (gam3@dharma.sehda.com)
Thu, 10 Sep 1998 00:20:18 -0700


This is a multipart MIME message.

--==_Exmh_8372288280
Content-Type: text/plain; charset=us-ascii

Attached is a patch for fs/nfsd. It attempts to fix 4 problems:

1) knfsd will allow multiple exports of a single partition. The mounts
must be proper subsets of the partition.

NOTE: It may be possible to access any file on a partition using
the least restrictive export permissions.

2) knfsd will allow mounts above the export point. That is if you
export / and /usr is in the same partition you can mount /usr.

3) You can now export and mount regular files. (It is not clear if
they can be used for swap.)

4) knfsd does not return EACCESS when you try to access a `covered'
directory. Instead knfsd allows nfs to access these directories.
(I believe that this is what SunOS 4 does.)

In order for #2 above to work you must get a patched copy of
mountd that used the net getfd system call.

--
Allen Morris <gam3@acm.org>

--==_Exmh_8372288280 Content-Type: text/plain; name="knfsd-patch"; charset=us-ascii Content-Description: knfsd-patch Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="knfsd-patch"

--- linux/fs/nfsd/export.c 1998/09/04 01:44:30 1.1 +++ linux/fs/nfsd/export.c 1998/09/10 06:43:24 @@ -33,7 +33,7 @@ typedef struct svc_export svc_export; =

static svc_export * exp_find(svc_client *clp, kdev_t dev); -static svc_export * exp_parent(svc_client *clp, kdev_t dev); +static svc_export * exp_parent(svc_client *clp, kdev_t dev, struct dentr= y *dentry); static void exp_unexport_all(svc_client *clp); static void exp_do_unexport(svc_export *unexp); static svc_client * exp_getclientbyname(char *name); @@ -90,8 +90,16 @@ =

if (!clp) return NULL; - exp =3D exp_find(clp, dev); - return (exp && exp->ex_ino =3D=3D ino)? exp : NULL; + + exp =3D clp->cl_export[EXPORT_HASH(dev)]; + if (exp) + do { + if (exp->ex_ino =3D=3D ino && exp->ex_dev =3D=3D dev) + goto out; + } while (NULL !=3D (exp =3D exp->ex_next)); + exp =3D NULL; +out: + return exp; } =

/* @@ -129,19 +137,59 @@ * Find the parent export entry for a given fs. This function is used * only by the export syscall to keep the export tree consistent. */ +/* =

+ * We can use this to find exports if we have it look at the first + * dentry. This makes it less efficient. <gam3@acm.org> + */ static svc_export * -exp_parent(svc_client *clp, kdev_t dev) +exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry) { - svc_export *exp; + svc_export *exp; kdev_t xdev =3D dev; + struct dentry *xdentry =3D dentry; + struct dentry *ndentry =3D NULL; =

- do { - exp =3D exp_find(clp, xdev); - if (exp) - return exp; - } while (nfsd_parentdev(&xdev)); + if (clp =3D=3D NULL || dentry =3D=3D NULL) + return NULL; =

- return NULL; + exp =3D clp->cl_export[EXPORT_HASH(dev)]; + if (exp) + do { + ndentry =3D exp->ex_dentry; + if (ndentry) + while (ndentry =3D ndentry->d_parent) { + if (ndentry =3D=3D xdentry) { +dprintk("nfsd: exp_parent mount under submount.\n"); + goto out; + } + if (ndentry =3D=3D ndentry->d_parent) + break; + } + } while (NULL !=3D (exp =3D exp->ex_next)); + do { + xdev =3D dev; + do { + exp =3D clp->cl_export[EXPORT_HASH(xdev)]; + if (exp) + do { + ndentry =3D exp->ex_dentry; + if (ndentry =3D=3D xdentry) { +if (dev =3D=3D xdev) + dprintk("nfsd: exp_parent submount over mount.\n"); +else + dprintk("nfsd: exp_parent found.\n"); + goto out; + } + } while (NULL !=3D (exp =3D exp->ex_next)); + } while (nfsd_parentdev(&xdev)); +/* It should be possible to move this to the top of this loop */ + if (xdentry =3D=3D xdentry->d_parent) { + break; + } + } while (xdentry =3D xdentry->d_parent); + exp =3D NULL; +out: + return exp; } =

/* @@ -160,9 +208,10 @@ ino_t ino; =

/* Consistency check */ + err =3D -EINVAL; if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) - return -EINVAL; + goto out; =

dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", nxp->ex_client, nxp->ex_path, @@ -183,15 +232,11 @@ * If there's already an export for this file, assume this * is just a flag update. */ - if ((exp =3D exp_find(clp, dev)) !=3D NULL) { - /* Ensure there's only one export per FS. */ - err =3D -EPERM; - if (exp->ex_ino =3D=3D ino) { - exp->ex_flags =3D nxp->ex_flags; - exp->ex_anon_uid =3D nxp->ex_anon_uid; - exp->ex_anon_gid =3D nxp->ex_anon_gid; - err =3D 0; - } + if ((exp =3D exp_get(clp, dev, ino)) !=3D NULL) { + exp->ex_flags =3D nxp->ex_flags; + exp->ex_anon_uid =3D nxp->ex_anon_uid; + exp->ex_anon_gid =3D nxp->ex_anon_gid; + err =3D 0; goto out_unlock; } =

@@ -203,32 +248,29 @@ =

err =3D -ENOENT; inode =3D dentry->d_inode; - if(!inode) + if (!inode) goto finish; err =3D -EINVAL; - if(inode->i_dev !=3D dev || inode->i_ino !=3D nxp->ex_ino) { - + if (inode->i_dev !=3D dev || inode->i_ino !=3D nxp->ex_ino) { printk(KERN_DEBUG "exp_export: i_dev =3D %x, dev =3D %x\n", inode->i_dev, dev); =

/* I'm just being paranoid... */ goto finish; } =

- /* We currently export only dirs. */ + /* We currently export only dirs and regular files. + * This is what umountd does. + */ err =3D -ENOTDIR; - if (!S_ISDIR(inode->i_mode)) + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)) goto finish; =

/* If this is a sub-export, must be root of FS */ err =3D -EINVAL; - if ((parent =3D exp_parent(clp, dev)) !=3D NULL) { + if ((parent =3D exp_parent(clp, dev, dentry)) !=3D NULL) { struct super_block *sb =3D inode->i_sb; - - if (inode !=3D sb->s_root->d_inode) { -#ifdef NFSD_PARANOIA -printk("exp_export: sub-export %s not root of device %s\n", -nxp->ex_path, kdevname(sb->s_dev)); -#endif + if (dev =3D parent->ex_dev) { +dprintk("exp_export: sub-export not valid.\n"); goto finish; } } @@ -368,7 +410,6 @@ if (exp->ex_dev =3D=3D nxp->ex_dev) { if (exp->ex_ino !=3D nxp->ex_ino) { printk("exp_unexport: ino mismatch, %ld not %ld\n", exp->ex_ino, nxp->ex= _ino); - break; } *expp =3D exp->ex_next; exp_do_unexport(exp); @@ -390,23 +431,35 @@ * since its harder to fool a kernel module than a user space program. */ int -exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh= *f) +exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, char *path, st= ruct knfs_fh *f) { - struct svc_export *exp =3D NULL; - struct svc_fh fh; + struct svc_export *exp; struct dentry *dentry; struct inode *inode; + struct svc_fh fh; + int err; =

- dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino); + if (path) { + dentry =3D lookup_dentry(path, NULL, 0); =

- if (!(exp =3D exp_get(clp, dev, ino))) - return -EPERM; + dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n", path, dentry, clp->cl= _ident, dev, ino); + dev =3D dentry->d_inode->i_dev; + ino =3D dentry->d_inode->i_ino; + =

+ exp =3D exp_parent(clp, dev, dentry); + } else { + dprintk("nfsd: exp_rootfh(%s %s:%x/%ld)\n", path, clp->cl_ident, dev, = ino); + exp =3D exp_get(clp, dev, ino); + dentry =3D dget(exp->ex_dentry); + } + err =3D -EPERM; + if (!exp) + goto out; =

- dentry =3D exp->ex_dentry; inode =3D dentry->d_inode; if(!inode) { printk("exp_rootfh: Aieee, NULL d_inode\n"); - return -EPERM; + goto out; } if(inode->i_dev !=3D dev || inode->i_ino !=3D ino) { printk("exp_rootfh: Aieee, ino/dev mismatch\n"); @@ -414,12 +467,17 @@ dev, ino, inode->i_dev, inode->i_ino); } =

- dget(dentry); - fh_compose(&fh, exp, dentry); + /* + * fh must be initialized before calling fh_compose + */ + fh_init(&fh); + fh_compose(&fh, exp, dget(dentry)); memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); fh_put(&fh); - - return 0; + err =3D 0; +out: + dput(dentry); + return err; } =

/* --- linux/fs/nfsd/nfsctl.c 1998/09/04 01:44:30 1.1 +++ linux/fs/nfsd/nfsctl.c 1998/09/09 22:22:47 @@ -5,6 +5,7 @@ * * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ +#define NFS_GETFH_NEW =

#include <linux/config.h> #include <linux/module.h> @@ -47,6 +48,7 @@ static int nfsctl_export(struct nfsctl_export *data); static int nfsctl_unexport(struct nfsctl_export *data); static int nfsctl_getfh(struct nfsctl_fhparm *, struct knfs_fh *); +static int nfsctl_getfd(struct nfsctl_fdparm *, struct knfs_fh *); /* static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); */ =

static int initialized =3D 0; @@ -108,6 +110,29 @@ #endif =

static inline int +nfsctl_getfd(struct nfsctl_fdparm *data, struct knfs_fh *res) +{ + struct sockaddr_in *sin; + struct svc_client *clp; + int err =3D 0; + + if (data->gd_addr.sa_family !=3D AF_INET) + return -EPROTONOSUPPORT; + if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) + return -EINVAL; + sin =3D (struct sockaddr_in *)&data->gd_addr; + + exp_readlock(); + if (!(clp =3D exp_getclient(sin))) + err =3D -EPERM; + else + err =3D exp_rootfh(clp, 0, 0, data->gd_path, res); + exp_unlock(); + + return err; +} + +static inline int nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res) { struct sockaddr_in *sin; @@ -124,7 +149,7 @@ if (!(clp =3D exp_getclient(sin))) err =3D -EPERM; else - err =3D exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, res); + err =3D exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, r= es); exp_unlock(); =

return err; @@ -193,6 +218,9 @@ #endif case NFSCTL_GETFH: err =3D nfsctl_getfh(&arg->ca_getfh, &res->cr_getfh); + break; + case NFSCTL_GETFD: + err =3D nfsctl_getfd(&arg->ca_getfd, &res->cr_getfh); break; default: err =3D -EINVAL; --- linux/fs/nfsd/vfs.c 1998/09/04 01:44:30 1.1 +++ linux/fs/nfsd/vfs.c 1998/09/10 05:16:01 @@ -39,6 +39,7 @@ #endif =

#define NFSDDBG_FACILITY NFSDDBG_FILEOP +#define NFSD_PARANOIA =

/* Open mode for nfsd_open */ #define OPEN_READ 0 @@ -171,10 +172,19 @@ * Make sure we haven't crossed a mount point ... */ if (dchild->d_sb !=3D dparent->d_sb) { + struct dentry *tdentry; #ifdef NFSD_PARANOIA printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name= , name); #endif - goto out_dput; + tdentry =3D dchild->d_covers; + if (tdentry =3D=3D dchild) + goto out_dput; + dput(dchild); + dchild =3D dget(tdentry); + if (dchild->d_sb !=3D dparent->d_sb) { +printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name= , dchild->d_name.name); + goto out_dput; + } } =

/* --- linux/include/linux/nfsd/export.h 1998/09/04 01:44:49 1.1 +++ linux/include/linux/nfsd/export.h 1998/09/09 02:23:07 @@ -86,6 +86,7 @@ void exp_putclient(struct svc_client *clp); struct svc_export * exp_get(struct svc_client *clp, kdev_t dev, ino_t in= o); int exp_rootfh(struct svc_client *, kdev_t, ino_t, + char *path, struct knfs_fh *); int nfserrno(int errno); void exp_nlmdetach(void); --- linux/include/linux/nfsd/syscall.h 1998/09/04 01:44:49 1.1 +++ linux/include/linux/nfsd/syscall.h 1998/09/09 22:25:23 @@ -32,8 +32,8 @@ #define NFSCTL_EXPORT 3 /* export a file system. */ #define NFSCTL_UNEXPORT 4 /* unexport a file system. */ #define NFSCTL_UGIDUPDATE 5 /* update a client's uid/gid map. */ -#define NFSCTL_GETFH 6 /* get an fh (used by mountd) */ - +#define NFSCTL_GETFH 6 /* get an fh by ino (used by mountd) */ +#define NFSCTL_GETFD 7 /* get an fh by path (used by mountd) */ =

/* SVC */ struct nfsctl_svc { @@ -81,6 +81,13 @@ int gf_version; }; =

+/* GETFD */ +struct nfsctl_fdparm { + struct sockaddr gd_addr; + char gd_path[NFS_MAXPATHLEN+1]; + int gd_version; +}; + /* * This is the argument union. */ @@ -92,6 +99,7 @@ struct nfsctl_export u_export; struct nfsctl_uidmap u_umap; struct nfsctl_fhparm u_getfh; + struct nfsctl_fdparm u_getfd; unsigned int u_debug; } u; #define ca_svc u.u_svc @@ -99,6 +107,7 @@ #define ca_export u.u_export #define ca_umap u.u_umap #define ca_getfh u.u_getfh +#define ca_getfd u.u_getfd #define ca_authd u.u_authd #define ca_debug u.u_debug };

--==_Exmh_8372288280--

- 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/faq.html