Hey, I would like to apply the following patch to the rawhide kernel that will make exports for NFS v4 mount work just list exports for v3 and v2 mounts.
In a nutshell, for NFS v4 mounts to work like v3/2 mounts the '/ *(ro,fsid=0)' entry has to be added to the /etc/exports file. This patch eliminates the need for that entry. The history, reasoning and evolution of this patch can be found at: http://linux-nfs.org/pipermail/nfsv4/2009-June/010724.html Over the weekend, I pounded on this code with 4 clients continuously running the cthon test suite, which tests every part of the protocol including mounting... In the end, this is the first major step toward making NFS v4 the default protocol. steved. Date: Tue Jul 7, 2009 Author: Steve Dickson Kernel support needed to implement dynamic pseudo root support which will allow v3 and v2 exports accessible to NFS v4 clients without any configuration changes. Signed-Off-By: Steve Dickson <ste...@redhat.com> ---------------------------------------------------- diff -up linux-2.6.30.noarch/fs/nfsd/export.c.save linux-2.6.30.noarch/fs/nfsd/export.c --- linux-2.6.30.noarch/fs/nfsd/export.c.save 2009-07-02 11:34:38.000000000 -0400 +++ linux-2.6.30.noarch/fs/nfsd/export.c 2009-07-02 11:35:44.000000000 -0400 @@ -104,6 +104,7 @@ static int expkey_parse(struct cache_det if (mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; + dprintk("expkey_parse: '%s'\n", mesg); buf = kmalloc(PAGE_SIZE, GFP_KERNEL); err = -ENOMEM; @@ -181,6 +182,8 @@ static int expkey_parse(struct cache_det if (dom) auth_domain_put(dom); kfree(buf); + if (err) + dprintk("expkey_parse: err %d\n", err); return err; } @@ -351,7 +354,10 @@ static void svc_export_request(struct ca (*bpp)[0] = '\n'; return; } + qword_add(bpp, blen, pth); + dprintk("svc_export_request: pth %s\n", pth); + (*bpp)[-1] = '\n'; } @@ -500,6 +506,7 @@ static int svc_export_parse(struct cache if (mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; + dprintk("svc_export_parse: '%s'\n", mesg); buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) @@ -619,6 +626,8 @@ out1: auth_domain_put(dom); out: kfree(buf); + if (err) + dprintk("svc_export_parse: err %d\n", err); return err; } @@ -1413,6 +1422,7 @@ static struct flags { { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, + { NFSEXP_V4ROOT, {"v4root", ""}}, #ifdef MSNFS { NFSEXP_MSNFS, {"msnfs", ""}}, #endif @@ -1493,7 +1503,7 @@ static int e_show(struct seq_file *m, vo struct svc_export *exp = container_of(cp, struct svc_export, h); if (p == SEQ_START_TOKEN) { - seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Version 1.2\n"); seq_puts(m, "# Path Client(Flags) # IPs\n"); return 0; } diff -up linux-2.6.30.noarch/fs/nfsd/nfs4xdr.c.save linux-2.6.30.noarch/fs/nfsd/nfs4xdr.c --- linux-2.6.30.noarch/fs/nfsd/nfs4xdr.c.save 2009-07-02 11:34:38.000000000 -0400 +++ linux-2.6.30.noarch/fs/nfsd/nfs4xdr.c 2009-07-02 11:35:31.000000000 -0400 @@ -2176,28 +2176,62 @@ static inline int attributes_need_mount( return 0; } -static __be32 -nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, - const char *name, int namlen, __be32 *p, int *buflen) +struct dentry * +nfsd_check_export(struct nfsd4_readdir *cd, const char *name, int namlen) { struct svc_export *exp = cd->rd_fhp->fh_export; struct dentry *dentry; - __be32 nfserr; - int ignore_crossmnt = 0; + int err; dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen); if (IS_ERR(dentry)) - return nfserrno(PTR_ERR(dentry)); + return dentry; if (!dentry->d_inode) { - /* - * nfsd_buffered_readdir drops the i_mutex between - * readdir and calling this callback, leaving a window - * where this directory entry could have gone away. - */ dput(dentry); - return nfserr_noent; + return ERR_PTR(-ENOENT); + } + + /* + * Check to see if this dentry is part + * of the psuedo root + */ + if ((exp->ex_flags & NFSEXP_V4ROOT) == 0) + return dentry; + + /* + * Only exported directories are visable + * on psuedo exports + */ + if (!S_ISDIR(dentry->d_inode->i_mode)) { + dput(dentry); + return ERR_PTR(-ENOENT); } + /* + * Make the upcall to see if this directory + * is exported. + */ + exp_get(exp); + err = nfsd_export_lookup(cd->rd_rqstp, dentry, exp); + if (err) { + exp_put(exp); + dput(dentry); + return ERR_PTR(err); + } + exp_put(exp); + + return dentry; +} + +static __be32 +nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, + struct dentry *dentry, __be32 *p, int *buflen) +{ + struct svc_export *exp = cd->rd_fhp->fh_export; + __be32 nfserr; + int ignore_crossmnt = 0; + int err, v4root = (exp->ex_flags & NFSEXP_V4ROOT); + exp_get(exp); /* * In the case of a mountpoint, the client may be asking for @@ -2208,18 +2242,29 @@ nfsd4_encode_dirent_fattr(struct nfsd4_r */ if (d_mountpoint(dentry) && !attributes_need_mount(cd->rd_bmval)) ignore_crossmnt = 1; - else if (d_mountpoint(dentry)) { - int err; - + else if (d_mountpoint(dentry) || v4root) { + /* + * Make sure the dentry is viewable on the psuedo export + */ + v4root = (dentry->d_inode && v4root); + if (v4root) { + err = nfsd_export_lookup(cd->rd_rqstp, dentry, exp); + if (err) { + nfserr = nfserrno(err); + goto out_put; + } + } /* * Why the heck aren't we just using nfsd_lookup?? * Different "."/".." handling? Something else? * At least, add a comment here to explain.... */ - err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp); - if (err) { - nfserr = nfserrno(err); - goto out_put; + if (d_mountpoint(dentry) || v4root) { + err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp); + if (err) { + nfserr = nfserrno(err); + goto out_put; + } } nfserr = check_nfsd_access(exp, cd->rd_rqstp); if (nfserr) @@ -2258,6 +2303,7 @@ nfsd4_encode_dirent(void *ccdv, const ch struct readdir_cd *ccd = ccdv; struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); int buflen; + struct dentry *dentry; __be32 *p = cd->buffer; __be32 *cookiep; __be32 nfserr = nfserr_toosmall; @@ -2268,19 +2314,40 @@ nfsd4_encode_dirent(void *ccdv, const ch return 0; } + /* + * Do the lookup and make sure the dentry is + * visible on the exported directory + */ + dentry = nfsd_check_export(cd, name, namlen); + if (IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -ENOENT) { + cd->common.err = nfs_ok; + return 0; + } + cd->common.err = nfserrno(PTR_ERR(dentry)); + return -EINVAL; + } + if (cd->offset) xdr_encode_hyper(cd->offset, (u64) offset); buflen = cd->buflen - 4 - XDR_QUADLEN(namlen); - if (buflen < 0) + if (buflen < 0) { + dput(dentry); goto fail; + } *p++ = xdr_one; /* mark entry present */ cookiep = p; p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ p = xdr_encode_array(p, name, namlen); /* name length & name */ - nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen); + /* + * Note: the dput() on the dentry is done in + * nfsd4_encode_dirent_fattr() since the dentry can + * change when crossing a mount point. + */ + nfserr = nfsd4_encode_dirent_fattr(cd, dentry, p, &buflen); switch (nfserr) { case nfs_ok: p += buflen; diff -up linux-2.6.30.noarch/fs/nfsd/nfsfh.c.save linux-2.6.30.noarch/fs/nfsd/nfsfh.c --- linux-2.6.30.noarch/fs/nfsd/nfsfh.c.save 2009-07-02 11:34:38.000000000 -0400 +++ linux-2.6.30.noarch/fs/nfsd/nfsfh.c 2009-07-02 11:35:48.000000000 -0400 @@ -109,6 +109,34 @@ static __be32 nfsd_setuser_and_check_por return nfserrno(nfsd_setuser(rqstp, exp)); } +static inline __be32 check_pseudo_root(struct svc_rqst *rqstp, + struct dentry *dentry, struct svc_export *exp) +{ + int error; + + /* + * Only interested in pseudo roots + */ + if (!(exp->ex_flags & NFSEXP_V4ROOT)) + return nfs_ok; + + /* + * Only directories should be on the pseudo root + */ + if (unlikely(!S_ISDIR(dentry->d_inode->i_mode))) + return nfserr_stale; + /* + * Check non-root directories to make sure + * they are truly exported + */ + if (unlikely(dentry->d_name.len > 1)) { + error = nfsd_export_lookup(rqstp, dentry, exp); + return nfserrno(error); + } + + return nfs_ok; +} + /* * Use the given filehandle to look up the corresponding export and * dentry. On success, the results are used to set fh_export and @@ -315,6 +343,14 @@ fh_verify(struct svc_rqst *rqstp, struct error = nfsd_setuser_and_check_port(rqstp, exp); if (error) goto out; + + /* + * Do some spoof checking if we are on the pseudo root + */ + error = check_pseudo_root(rqstp, dentry, exp); + if (error) + goto out; + } error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type); diff -up linux-2.6.30.noarch/fs/nfsd/vfs.c.save linux-2.6.30.noarch/fs/nfsd/vfs.c --- linux-2.6.30.noarch/fs/nfsd/vfs.c.save 2009-07-02 11:34:38.000000000 -0400 +++ linux-2.6.30.noarch/fs/nfsd/vfs.c 2009-07-02 11:35:39.000000000 -0400 @@ -89,6 +89,12 @@ struct raparm_hbucket { #define RAPARM_HASH_MASK (RAPARM_HASH_SIZE-1) static struct raparm_hbucket raparm_hash[RAPARM_HASH_SIZE]; +static inline int +nfsd_v4client(struct svc_rqst *rq) +{ + return((rq->rq_prog == NFS_PROGRAM) && (rq->rq_vers == 4)); +} + /* * Called from nfsd_lookup and encode_dirent. Check if we have crossed * a mount point. @@ -115,7 +121,8 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, s path_put(&path); goto out; } - if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { + if (nfsd_v4client(rqstp) || + (exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { /* successfully crossed mount point */ /* * This is subtle: path.dentry is *not* on path.mnt @@ -134,6 +141,55 @@ out: return err; } +/* + * Lookup the export the dentry is on. To be + * viewable on an pseudo export, the dentry + * has to be an exported directory. + */ +int +nfsd_export_lookup(struct svc_rqst *rqstp, struct dentry *dentry, + struct svc_export *exp) +{ + struct svc_export *exp2 = NULL; + struct path path; + int err = 0; + + if ((exp->ex_flags & NFSEXP_V4ROOT) == 0) + return 0; + + /* + * Make sure the export is the parent of the dentry + */ + if (dentry->d_parent != exp->ex_path.dentry) + return 0; + + /* + * Only directories are seen on psuedo exports + */ + if (!S_ISDIR(dentry->d_inode->i_mode)) + return -ENOENT; + + /* + * Make the upcall + */ + path.mnt = mntget(exp->ex_path.mnt); + path.dentry = dget(dentry); + while (d_mountpoint(path.dentry) && follow_down(&path)); + + exp2 = rqst_exp_get_by_name(rqstp, &path); + if (IS_ERR(exp2)) + err = PTR_ERR(exp2); + else { + /* + * The export exist so allow the access + */ + exp_put(exp2); + } + + dput(path.dentry); + mntput(path.mnt); + return err; +} __be32 nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, unsigned int len, @@ -143,7 +199,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqst struct dentry *dparent; struct dentry *dentry; __be32 err; - int host_err; + int host_err, v4root; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); @@ -155,6 +211,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqst dparent = fhp->fh_dentry; exp = fhp->fh_export; exp_get(exp); + v4root = (exp->ex_flags & NFSEXP_V4ROOT); /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { @@ -199,9 +256,21 @@ nfsd_lookup_dentry(struct svc_rqst *rqst if (IS_ERR(dentry)) goto out_nfserr; /* + * The export is a pseudo one, make sure the + * dentry is accessible + */ + v4root = (dentry->d_inode && v4root); + if (v4root) { + host_err = nfsd_export_lookup(rqstp, dentry, exp); + if (host_err) { + dput(dentry); + goto out_nfserr; + } + } + /* * check if we have crossed a mount point ... */ - if (d_mountpoint(dentry)) { + if (d_mountpoint(dentry) || v4root) { if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { dput(dentry); goto out_nfserr; diff -up linux-2.6.30.noarch/include/linux/nfsd/export.h.save linux-2.6.30.noarch/include/linux/nfsd/export.h --- linux-2.6.30.noarch/include/linux/nfsd/export.h.save 2009-07-02 11:34:38.000000000 -0400 +++ linux-2.6.30.noarch/include/linux/nfsd/export.h 2009-07-02 11:35:22.000000000 -0400 @@ -39,7 +39,8 @@ #define NFSEXP_FSID 0x2000 #define NFSEXP_CROSSMOUNT 0x4000 #define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */ -#define NFSEXP_ALLFLAGS 0xFE3F +#define NFSEXP_V4ROOT 0x10000 +#define NFSEXP_ALLFLAGS 0x1FE3F /* The flags that may vary depending on security flavor: */ #define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \ diff -up linux-2.6.30.noarch/include/linux/nfsd/nfsd.h.save linux-2.6.30.noarch/include/linux/nfsd/nfsd.h --- linux-2.6.30.noarch/include/linux/nfsd/nfsd.h.save 2009-07-02 11:34:38.000000000 -0400 +++ linux-2.6.30.noarch/include/linux/nfsd/nfsd.h 2009-07-02 11:35:27.000000000 -0400 @@ -76,6 +76,8 @@ int nfsd_racache_init(int); void nfsd_racache_shutdown(void); int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, struct svc_export **expp); +int nfsd_export_lookup(struct svc_rqst *rqstp, struct dentry *dpp, + struct svc_export *exp); __be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *, const char *, unsigned int, struct svc_fh *); __be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *, _______________________________________________ Fedora-kernel-list mailing list Fedora-kernel-list@redhat.com https://www.redhat.com/mailman/listinfo/fedora-kernel-list