lkml.org 
[lkml]   [1998]   [Sep]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subject[PATCH] for knfsd (Please test!!!)
Date
From
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
};
\
 
 \ /
  Last update: 2005-03-22 13:44    [from the cache]
©2003-2014 Jasper Spaans. Advertise on this site