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

Reply via email to