diff options
Diffstat (limited to 'fs/nfsd/export.c')
-rw-r--r-- | fs/nfsd/export.c | 204 |
1 files changed, 146 insertions, 58 deletions
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index e30953bb2..81bd84bcf 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -33,7 +33,10 @@ typedef struct svc_client svc_client; 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 svc_export * exp_child(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 +93,16 @@ exp_get(svc_client *clp, kdev_t dev, ino_t ino) 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; } /* @@ -126,22 +137,80 @@ nfsd_parentdev(kdev_t *devp) } /* - * Find the parent export entry for a given fs. This function is used - * only by the export syscall to keep the export tree consistent. + * Find the export entry for a given dentry. <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; + + if (clp == NULL || dentry == NULL) + return NULL; do { - exp = exp_find(clp, xdev); - if (exp) - return exp; - } while (nfsd_parentdev(&xdev)); + xdev = dev; + do { + exp = clp->cl_export[EXPORT_HASH(xdev)]; + if (exp) + do { + ndentry = exp->ex_dentry; + if (ndentry == xdentry) { +#ifdef NFSD_PARANOIA +if (dev == xdev) + dprintk("nfsd: exp_parent submount over mount.\n"); +else + dprintk("nfsd: exp_parent found.\n"); +#endif + goto out; + } + } while (NULL != (exp = exp->ex_next)); + } while (nfsd_parentdev(&xdev)); + if (xdentry == xdentry->d_parent) { + break; + } + } while ((xdentry = xdentry->d_parent)); + exp = NULL; +out: + return exp; +} - return NULL; +/* + * Find the child export entry for a given fs. This function is used + * only by the export syscall to keep the export tree consistent. + * <gam3@acm.org> + */ +static svc_export * +exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry) +{ + svc_export *exp; + struct dentry *xdentry = dentry; + struct dentry *ndentry = NULL; + + if (clp == NULL || dentry == 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) { +#ifdef NFSD_PARANOIA +dprintk("nfsd: exp_child mount under submount.\n"); +#endif + goto out; + } + if (ndentry == ndentry->d_parent) + break; + } + } while (NULL != (exp = exp->ex_next)); + exp = NULL; +out: + return exp; } /* @@ -160,9 +229,10 @@ exp_export(struct nfsctl_export *nxp) 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 +253,11 @@ exp_export(struct nfsctl_export *nxp) * 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 +269,32 @@ exp_export(struct nfsctl_export *nxp) 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) { - 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 ((parent = exp_child(clp, dev, dentry)) != NULL) { + dprintk("exp_export: export not valid (Rule 3).\n"); + goto finish; + } + /* Is this is a sub-export, must be a proper subset of FS */ + if ((parent = exp_parent(clp, dev, dentry)) != NULL) { + if (dev == parent->ex_dev) { + dprintk("exp_export: sub-export not valid (Rule 2).\n"); goto finish; } } @@ -306,7 +372,7 @@ exp_do_unexport(svc_export *unexp) */ if (!exp_device_in_use(unexp->ex_dev)) { printk("exp_do_unexport: %s last use, flushing cache\n", -kdevname(unexp->ex_dev)); + kdevname(unexp->ex_dev)); nfsd_fh_flush(unexp->ex_dev); } @@ -362,18 +428,15 @@ exp_unexport(struct nfsctl_export *nxp) err = -EINVAL; clp = exp_getclientbyname(nxp->ex_client); if (clp) { -printk("exp_unexport: found client %s\n", nxp->ex_client); expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev); while ((exp = *expp) != NULL) { 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); + if (exp->ex_ino == nxp->ex_ino) { + *expp = exp->ex_next; + exp_do_unexport(exp); + err = 0; break; } - *expp = exp->ex_next; - exp_do_unexport(exp); - err = 0; - break; } expp = &(exp->ex_next); } @@ -390,28 +453,50 @@ out: * 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; - struct dentry *dentry; + struct dentry *dentry = NULL; struct inode *inode; struct svc_fh fh; + int err; - dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino); - - exp = exp_get(clp, dev, ino); - if (!exp) - return -EPERM; + err = -EPERM; + if (path) { + if (!(dentry = lookup_dentry(path, NULL, 0))) { + printk("nfsd: exp_rootfh path not found %s", path); + return -EPERM; + } + dev = dentry->d_inode->i_dev; + ino = dentry->d_inode->i_ino; + + dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n", + path, dentry, clp->cl_ident, dev, ino); + exp = exp_parent(clp, dev, dentry); + } else { + dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", + clp->cl_ident, dev, ino); + if ((exp = exp_get(clp, dev, ino))) + if (!(dentry = dget(exp->ex_dentry))) { + printk("exp_rootfh: Aieee, NULL dentry\n"); + return -EPERM; + } + } + if (!exp) { + dprintk("nfsd: exp_rootfh export not found.\n"); + goto out; + } - dentry = exp->ex_dentry; inode = dentry->d_inode; - if(!inode) { + if (!inode) { printk("exp_rootfh: Aieee, NULL d_inode\n"); - return -EPERM; + goto out; } - if(inode->i_dev != dev || inode->i_ino != ino) { + if (inode->i_dev != dev || inode->i_ino != ino) { printk("exp_rootfh: Aieee, ino/dev mismatch\n"); - printk("exp_rootfh: arg[dev(%x):ino(%ld)] inode[dev(%x):ino(%ld)]\n", + printk("exp_rootfh: arg[dev(%x):ino(%ld)]" + " inode[dev(%x):ino(%ld)]\n", dev, ino, inode->i_dev, inode->i_ino); } @@ -419,11 +504,14 @@ exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f) * fh must be initialized before calling fh_compose */ fh_init(&fh); - fh_compose(&fh, exp, dget(dentry)); + fh_compose(&fh, exp, dentry); memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); fh_put(&fh); - return 0; + +out: + dput(dentry); + return err; } /* |