Messages in this thread Patch in this message | | | Subject | [PATCH] for knfsd (Please test!!!) | Date | Thu, 10 Sep 1998 00:20:18 -0700 | From | "G. Allen Morris III" <> |
| |
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>
--- 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 dentry *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 = exp_find(clp, dev); - return (exp && exp->ex_ino == ino)? exp : NULL; + + exp = clp->cl_export[EXPORT_HASH(dev)]; + if (exp) + do { + if (exp->ex_ino == ino && exp->ex_dev == dev) + goto out; + } while (NULL != (exp = exp->ex_next)); + exp = 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 = dev; + struct dentry *xdentry = dentry; + struct dentry *ndentry = NULL; - do { - exp = exp_find(clp, xdev); - if (exp) - return exp; - } while (nfsd_parentdev(&xdev)); + if (clp == NULL || dentry == NULL) + return NULL; - return NULL; + exp = clp->cl_export[EXPORT_HASH(dev)]; + if (exp) + do { + ndentry = exp->ex_dentry; + if (ndentry) + while (ndentry = ndentry->d_parent) { + if (ndentry == xdentry) { +dprintk("nfsd: exp_parent mount under submount.\n"); + goto out; + } + if (ndentry == ndentry->d_parent) + break; + } + } while (NULL != (exp = exp->ex_next)); + do { + xdev = dev; + do { + exp = clp->cl_export[EXPORT_HASH(xdev)]; + if (exp) + do { + ndentry = exp->ex_dentry; + if (ndentry == xdentry) { +if (dev == xdev) + dprintk("nfsd: exp_parent submount over mount.\n"); +else + dprintk("nfsd: exp_parent found.\n"); + goto out; + } + } while (NULL != (exp = exp->ex_next)); + } while (nfsd_parentdev(&xdev)); +/* It should be possible to move this to the top of this loop */ + if (xdentry == xdentry->d_parent) { + break; + } + } while (xdentry = xdentry->d_parent); + exp = NULL; +out: + return exp; } /* @@ -160,9 +208,10 @@ ino_t ino; /* Consistency check */ + err = -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 = exp_find(clp, dev)) != NULL) { - /* Ensure there's only one export per FS. */ - err = -EPERM; - if (exp->ex_ino == ino) { - exp->ex_flags = nxp->ex_flags; - exp->ex_anon_uid = nxp->ex_anon_uid; - exp->ex_anon_gid = nxp->ex_anon_gid; - err = 0; - } + if ((exp = exp_get(clp, dev, ino)) != NULL) { + exp->ex_flags = nxp->ex_flags; + exp->ex_anon_uid = nxp->ex_anon_uid; + exp->ex_anon_gid = nxp->ex_anon_gid; + err = 0; goto out_unlock; } @@ -203,32 +248,29 @@ err = -ENOENT; inode = dentry->d_inode; - if(!inode) + if (!inode) goto finish; err = -EINVAL; - if(inode->i_dev != dev || inode->i_ino != nxp->ex_ino) { - + if (inode->i_dev != dev || inode->i_ino != nxp->ex_ino) { printk(KERN_DEBUG "exp_export: i_dev = %x, dev = %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 = -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 = -EINVAL; - if ((parent = exp_parent(clp, dev)) != NULL) { + if ((parent = exp_parent(clp, dev, dentry)) != NULL) { struct super_block *sb = inode->i_sb; - - if (inode != 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 = parent->ex_dev) { +dprintk("exp_export: sub-export not valid.\n"); goto finish; } } @@ -368,7 +410,6 @@ if (exp->ex_dev == nxp->ex_dev) { if (exp->ex_ino != nxp->ex_ino) { printk("exp_unexport: ino mismatch, %ld not %ld\n", exp->ex_ino, nxp->ex_ino); - break; } *expp = 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, struct knfs_fh *f) { - struct svc_export *exp = 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 = lookup_dentry(path, NULL, 0); - if (!(exp = 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 = dentry->d_inode->i_dev; + ino = dentry->d_inode->i_ino; + + exp = exp_parent(clp, dev, dentry); + } else { + dprintk("nfsd: exp_rootfh(%s %s:%x/%ld)\n", path, clp->cl_ident, dev, ino); + exp = exp_get(clp, dev, ino); + dentry = dget(exp->ex_dentry); + } + err = -EPERM; + if (!exp) + goto out; - dentry = exp->ex_dentry; inode = dentry->d_inode; if(!inode) { printk("exp_rootfh: Aieee, NULL d_inode\n"); - return -EPERM; + goto out; } if(inode->i_dev != dev || inode->i_ino != 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 = 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 = 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 = 0; + + if (data->gd_addr.sa_family != AF_INET) + return -EPROTONOSUPPORT; + if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) + return -EINVAL; + sin = (struct sockaddr_in *)&data->gd_addr; + + exp_readlock(); + if (!(clp = exp_getclient(sin))) + err = -EPERM; + else + err = 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 = exp_getclient(sin))) err = -EPERM; else - err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, res); + err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, res); exp_unlock(); return err; @@ -193,6 +218,9 @@ #endif case NFSCTL_GETFH: err = nfsctl_getfh(&arg->ca_getfh, &res->cr_getfh); + break; + case NFSCTL_GETFD: + err = nfsctl_getfd(&arg->ca_getfd, &res->cr_getfh); break; default: err = -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 != 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 = dchild->d_covers; + if (tdentry == dchild) + goto out_dput; + dput(dchild); + dchild = dget(tdentry); + if (dchild->d_sb != 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 ino); 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 }; | |