Print this page
2988 nfssrv: need ability to go to submounts for v3 and v2 protocols
Portions contributed by: Marcel Telka <marcel.telka@nexenta.com>
Portions contributed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Change-Id: I6fdf110cc17e789353c4442b83a46cb80643456e

@@ -16,13 +16,14 @@
  * fields enclosed by brackets "[]" replaced with your own identifying
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2016 by Delphix. All rights reserved.
  */
 
 /*
  *      Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.

@@ -327,11 +328,85 @@
 rfs_setattr_getfh(struct nfssaargs *args)
 {
         return (&args->saa_fh);
 }
 
+/* Change and release @exip and @vpp only in success */
+int
+rfs_cross_mnt(vnode_t **vpp, struct exportinfo **exip)
+{
+        struct exportinfo *exi;
+        vnode_t *vp = *vpp;
+        fid_t fid;
+        int error;
+
+        VN_HOLD(vp);
+
+        if ((error = traverse(&vp)) != 0) {
+                VN_RELE(vp);
+                return (error);
+        }
+
+        bzero(&fid, sizeof (fid));
+        fid.fid_len = MAXFIDSZ;
+        error = VOP_FID(vp, &fid, NULL);
+        if (error) {
+                VN_RELE(vp);
+                return (error);
+        }
+
+        exi = checkexport(&vp->v_vfsp->vfs_fsid, &fid);
+        if (exi == NULL ||
+            (exi->exi_export.ex_flags & EX_NOHIDE) == 0) {
+                /*
+                 * It is not error, just subdir is not exported
+                 * or "nohide" is not set
+                 */
+                if (exi != NULL)
+                        exi_rele(exi);
+                VN_RELE(vp);
+        } else {
+                /* go to submount */
+                exi_rele(*exip);
+                *exip = exi;
+
+                VN_RELE(*vpp);
+                *vpp = vp;
+        }
+
+        return (0);
+}
+
 /*
+ * Given mounted "dvp" and "exi", go upper mountpoint
+ * with dvp/exi correction
+ * Return 0 in success
+ */
+int
+rfs_climb_crossmnt(vnode_t **dvpp, struct exportinfo **exip, cred_t *cr)
+{
+        struct exportinfo *exi;
+        vnode_t *dvp = *dvpp;
+
+        ASSERT(dvp->v_flag & VROOT);
+
+        VN_HOLD(dvp);
+        dvp = untraverse(dvp);
+        exi = nfs_vptoexi(NULL, dvp, cr, NULL, NULL, FALSE);
+        if (exi == NULL) {
+                VN_RELE(dvp);
+                return (-1);
+        }
+
+        exi_rele(*exip);
+        *exip = exi;
+        VN_RELE(*dvpp);
+        *dvpp = dvp;
+
+        return (0);
+}
+/*
  * Directory lookup.
  * Returns an fhandle and file attributes for file name in a directory.
  */
 /* ARGSUSED */
 void

@@ -379,29 +454,41 @@
                         dr->dr_status = NFSERR_STALE;
                         return;
                 }
         }
 
+        exi_hold(exi);
+
         /*
          * Not allow lookup beyond root.
          * If the filehandle matches a filehandle of the exi,
          * then the ".." refers beyond the root of an exported filesystem.
          */
         if (strcmp(da->da_name, "..") == 0 &&
             EQFID(&exi->exi_fid, (fid_t *)&fhp->fh_len)) {
-                VN_RELE(dvp);
-                dr->dr_status = NFSERR_NOENT;
-                return;
+                if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
+                    (dvp->v_flag & VROOT)) {
+                        /*
+                         * special case for ".." and 'nohide'exported root
+                         */
+                        if (rfs_climb_crossmnt(&dvp, &exi, cr) != 0) {
+                                error = NFSERR_ACCES;
+                                goto out;
         }
+                } else  {
+                        error = NFSERR_NOENT;
+                        goto out;
+                }
+        }
 
         ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
         name = nfscmd_convname(ca, exi, da->da_name, NFSCMD_CONV_INBOUND,
             MAXPATHLEN);
 
         if (name == NULL) {
-                dr->dr_status = NFSERR_ACCES;
-                return;
+                error = NFSERR_ACCES;
+                goto out;
         }
 
         /*
          * If the public filehandle is used then allow
          * a multi-component lookup, i.e. evaluate

@@ -411,10 +498,13 @@
          * This may result in a vnode in another filesystem
          * which is OK as long as the filesystem is exported.
          */
         if (PUBLIC_FH2(fhp)) {
                 publicfh_flag = TRUE;
+
+                exi_rele(exi);
+
                 error = rfs_publicfh_mclookup(name, dvp, cr, &vp, &exi,
                     &sec);
         } else {
                 /*
                  * Do a normal single component lookup.

@@ -424,10 +514,15 @@
         }
 
         if (name != da->da_name)
                 kmem_free(name, MAXPATHLEN);
 
+        if (error == 0 && vn_ismntpt(vp)) {
+                error = rfs_cross_mnt(&vp, &exi);
+                if (error)
+                        VN_RELE(vp);
+        }
 
         if (!error) {
                 va.va_mask = AT_ALL;    /* we want everything */
 
                 error = rfs4_delegated_getattr(vp, &va, 0, cr);

@@ -450,19 +545,14 @@
                         }
                 }
                 VN_RELE(vp);
         }
 
+out:
         VN_RELE(dvp);
 
-        /*
-         * If publicfh_flag is true then we have called rfs_publicfh_mclookup
-         * and have obtained a new exportinfo in exi which needs to be
-         * released. Note the the original exportinfo pointed to by exi
-         * will be released by the caller, comon_dispatch.
-         */
-        if (publicfh_flag && exi != NULL)
+        if (exi != NULL)
                 exi_rele(exi);
 
         /*
          * If it's public fh, no 0x81, and client's flavor is
          * invalid, set WebNFS status to WNFSERR_CLNT_FLAVOR now.