From 2ac835d0f9fbbe762f718688a59530cd8e6bff99 Mon Sep 17 00:00:00 2001
From: Dan McDonald <danmcd@joyent.com>
Date: Wed, 21 Aug 2019 22:03:23 -0400
Subject: [PATCH 25/30] Try to remove assumption that zone's root vnode is
 marked VROOT

---
 usr/src/uts/common/fs/nfs/nfs3_srv.c      |  2 +-
 usr/src/uts/common/fs/nfs/nfs4_srv.c      |  4 +--
 usr/src/uts/common/fs/nfs/nfs4_srv_attr.c | 10 +++----
 usr/src/uts/common/fs/nfs/nfs4_srv_ns.c   | 36 +++++++++++++----------
 usr/src/uts/common/fs/nfs/nfs_export.c    |  3 +-
 usr/src/uts/common/fs/nfs/nfs_srv.c       |  4 +--
 usr/src/uts/common/nfs/export.h           |  8 +++--
 usr/src/uts/common/sys/zone.h             | 12 ++++++--
 8 files changed, 49 insertions(+), 30 deletions(-)

diff --git a/usr/src/uts/common/fs/nfs/nfs3_srv.c b/usr/src/uts/common/fs/nfs/nfs3_srv.c
index a9814689c1..cededd1790 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_srv.c
@@ -439,7 +439,7 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
 	if (strcmp(args->what.name, "..") == 0 &&
 	    EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {
 		if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
-		    (dvp->v_flag & VROOT)) {
+		    ((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp))) {
 			/*
 			 * special case for ".." and 'nohide'exported root
 			 */
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv.c b/usr/src/uts/common/fs/nfs/nfs4_srv.c
index eec31aff6c..5ac7c9efa4 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c
@@ -958,7 +958,7 @@ do_rfs4_op_secinfo(struct compound_state *cs, char *nm, SECINFO4res *resp)
 		 * need to traverse back to the mounted-on filesystem
 		 * and do the dotdot lookup there.
 		 */
-		if (cs->vp->v_flag & VROOT) {
+		if ((cs->vp->v_flag & VROOT) || VN_IS_CURZONEROOT(cs->vp)) {
 
 			/*
 			 * If at the system root, then can
@@ -2705,7 +2705,7 @@ do_rfs4_op_lookup(char *nm, struct svc_req *req, struct compound_state *cs)
 		 * need to traverse back to the mounted-on filesystem
 		 * and do the dotdot lookup there.
 		 */
-		if (cs->vp->v_flag & VROOT) {
+		if ((cs->vp->v_flag & VROOT) || VN_IS_CURZONEROOT(cs->vp)) {
 
 			/*
 			 * If at the system root, then can
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c
index 52f254c98c..9ae5132353 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c
@@ -1303,15 +1303,15 @@ rfs4_get_mntdfileid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg)
 	vp = sarg->cs->vp;
 	sarg->mntdfid_set = FALSE;
 
-	/* VROOT object, must untraverse */
-	if (vp->v_flag & VROOT) {
+	/* VROOT object or zone's root, must untraverse */
+	if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
 
 		/* extra hold for vp since untraverse might rele */
 		VN_HOLD(vp);
 		stubvp = untraverse(vp);
 
 		/*
-		 * If vp/stubvp are same, we must be at system
+		 * If vp/stubvp are same, we must be at system-or-zone
 		 * root because untraverse returned same vp
 		 * for a VROOT object.  sarg->vap was setup
 		 * before we got here, so there's no need to do
@@ -1377,10 +1377,10 @@ rfs4_fattr4_mounted_on_fileid(nfs4_attr_cmd_t cmd,
 		break;		/* this attr is supported */
 	case NFS4ATTR_GETIT:
 	case NFS4ATTR_VERIT:
-		if (! sarg->mntdfid_set)
+		if (!sarg->mntdfid_set)
 			error = rfs4_get_mntdfileid(cmd, sarg);
 
-		if (! error && sarg->mntdfid_set) {
+		if (!error && sarg->mntdfid_set) {
 			if (cmd == NFS4ATTR_GETIT)
 				na->mounted_on_fileid = sarg->mounted_on_fileid;
 			else
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
index 6d027797c0..0781c1e96c 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
@@ -142,7 +142,7 @@ nfs4_vget_pseudo(struct exportinfo *exi, vnode_t **vpp, fid_t *fidp)
  *
  * A visible list has a per-file-system scope.  Any exportinfo
  * struct (real or pseudo) can have a visible list as long as
- * a) its export root is VROOT
+ * a) its export root is VROOT, or is the zone's root for in-zone NFS service
  * b) a descendant of the export root is shared
  */
 struct exportinfo *
@@ -658,10 +658,11 @@ treeclimb_export(struct exportinfo *exip)
 			break;
 
 		/*
-		 * The root of the file system needs special handling
+		 * The root of the file system, or the zone's root for
+		 * in-zone NFS service needs special handling
 		 */
-		if (vp->v_flag & VROOT) {
-			if (! exportdir) {
+		if (vp->v_flag & VROOT || VN_IS_CURZONEROOT(vp)) {
+			if (!exportdir) {
 				struct exportinfo *exi;
 
 				/*
@@ -695,7 +696,7 @@ treeclimb_export(struct exportinfo *exip)
 				vis_head = NULL;
 			}
 
-			if (VN_CMP(vp, ZONE_ROOTVP())) {
+			if (VN_IS_CURZONEROOT(vp)) {
 				/* at system root */
 				/*
 				 * If sharing "/", new_exi is shared exportinfo
@@ -825,7 +826,7 @@ treeclimb_export(struct exportinfo *exip)
  * node was a leaf node.
  * Deleting of nodes will finish when we reach a node which
  * has children or is a real export, then we might still need
- * to continue releasing visibles, until we reach VROOT node.
+ * to continue releasing visibles, until we reach VROOT or zone's root node.
  */
 void
 treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
@@ -847,7 +848,10 @@ treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
 
 	while (tnode != NULL) {
 
-		/* Stop at VROOT node which is exported or has child */
+		/*
+		 * Stop at VROOT (or zone root) node which is exported or has
+		 * child.
+		 */
 		if (TREE_ROOT(tnode) &&
 		    (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
 			break;
@@ -895,7 +899,7 @@ untraverse(vnode_t *vp)
 
 	tvp = vp;
 	for (;;) {
-		if (! (tvp->v_flag & VROOT))
+		if (!(tvp->v_flag & VROOT) && !VN_IS_CURZONEROOT(tvp))
 			break;
 
 		/* lock vfs to prevent unmount of this vfs */
@@ -926,7 +930,7 @@ untraverse(vnode_t *vp)
 
 /*
  * Given an exportinfo, climb up to find the exportinfo for the VROOT
- * of the filesystem.
+ * (or zone root) of the filesystem.
  *
  * e.g.         /
  *              |
@@ -975,12 +979,14 @@ has_visible(struct exportinfo *exi, vnode_t *vp)
 	vp_is_exported = VN_CMP(vp, exi->exi_vp);
 
 	/*
-	 * An exported root vnode has a sub-dir shared if it has a visible list.
-	 * i.e. if it does not have a visible list, then there is no node in
-	 * this filesystem leads to any other shared node.
+	 * An exported root vnode has a sub-dir shared if it has a visible
+	 * list.  i.e. if it does not have a visible list, then there is no
+	 * node in this filesystem leads to any other shared node.
 	 */
-	if (vp_is_exported && (vp->v_flag & VROOT))
+	if (vp_is_exported &&
+	    ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp))) {
 		return (exi->exi_visible ? 1 : 0);
+	}
 
 	/*
 	 * Only the exportinfo of a fs root node may have a visible list.
@@ -1053,7 +1059,7 @@ nfs_visible(struct exportinfo *exi, vnode_t *vp, int *expseudo)
 	 * Only a PSEUDO node has a visible list or an exported VROOT
 	 * node may have a visible list.
 	 */
-	if (! PSEUDO(exi))
+	if (!PSEUDO(exi))
 		exi = get_root_export(exi);
 
 	/* Get the fid of the vnode */
@@ -1161,7 +1167,7 @@ nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
 	 * Only a PSEUDO node has a visible list or an exported VROOT
 	 * node may have a visible list.
 	 */
-	if (! PSEUDO(exi))
+	if (!PSEUDO(exi))
 		exi = get_root_export(exi);
 
 	for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
diff --git a/usr/src/uts/common/fs/nfs/nfs_export.c b/usr/src/uts/common/fs/nfs/nfs_export.c
index 5467a676a8..5ed07d5083 100644
--- a/usr/src/uts/common/fs/nfs/nfs_export.c
+++ b/usr/src/uts/common/fs/nfs/nfs_export.c
@@ -730,6 +730,7 @@ srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec,
 	 * transferred from the PSEUDO export in exportfs()
 	 */
 	if (isadd && !(exip->exi_vp->v_flag & VROOT) &&
+	    !VN_IS_CURZONEROOT(exip->exi_vp) &&
 	    tnode->tree_vis->vis_seccnt > 0) {
 		srv_secinfo_add(&exip->exi_export.ex_secinfo,
 		    &exip->exi_export.ex_seccnt, tnode->tree_vis->vis_secinfo,
@@ -2076,7 +2077,7 @@ nfs_vptoexi(vnode_t *dvp, vnode_t *vp, cred_t *cr, int *walk,
 		 * If we're at the root of this filesystem, then
 		 * it's time to stop (with failure).
 		 */
-		if (vp->v_flag & VROOT) {
+		if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
 			error = EINVAL;
 			break;
 		}
diff --git a/usr/src/uts/common/fs/nfs/nfs_srv.c b/usr/src/uts/common/fs/nfs/nfs_srv.c
index eb1c7e775b..3074d078d5 100644
--- a/usr/src/uts/common/fs/nfs/nfs_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs_srv.c
@@ -411,7 +411,7 @@ rfs_climb_crossmnt(vnode_t **dvpp, struct exportinfo **exip, cred_t *cr)
 	struct exportinfo *exi;
 	vnode_t *dvp = *dvpp;
 
-	ASSERT(dvp->v_flag & VROOT);
+	ASSERT((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp));
 
 	VN_HOLD(dvp);
 	dvp = untraverse(dvp);
@@ -490,7 +490,7 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
 	if (strcmp(da->da_name, "..") == 0 &&
 	    EQFID(&exi->exi_fid, (fid_t *)&fhp->fh_len)) {
 		if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
-		    (dvp->v_flag & VROOT)) {
+		    ((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp))) {
 			/*
 			 * special case for ".." and 'nohide'exported root
 			 */
diff --git a/usr/src/uts/common/nfs/export.h b/usr/src/uts/common/nfs/export.h
index 9f11b02ae7..5dd21aac4c 100644
--- a/usr/src/uts/common/nfs/export.h
+++ b/usr/src/uts/common/nfs/export.h
@@ -38,6 +38,7 @@
 #include <nfs/nfs4.h>
 #include <sys/kiconv.h>
 #include <sys/avl.h>
+#include <sys/zone.h>
 
 #ifdef _KERNEL
 #include <sys/pkp_hash.h> /* for PKP_HASH_SIZE */
@@ -472,12 +473,15 @@ typedef struct treenode {
 } treenode_t;
 
 /*
- * TREE_ROOT checks if the node corresponds to a filesystem root
+ * TREE_ROOT checks if the node corresponds to a filesystem root or
+ * the zone's root directory.
  * TREE_EXPORTED checks if the node is explicitly shared
  */
 
 #define	TREE_ROOT(t) \
-	((t)->tree_exi && (t)->tree_exi->exi_vp->v_flag & VROOT)
+	((t)->tree_exi != NULL && \
+	(((t)->tree_exi->exi_vp->v_flag & VROOT) || \
+	VN_IS_CURZONEROOT((t)->tree_exi->exi_vp)))
 
 #define	TREE_EXPORTED(t) \
 	((t)->tree_exi && !PSEUDO((t)->tree_exi))
diff --git a/usr/src/uts/common/sys/zone.h b/usr/src/uts/common/sys/zone.h
index 353f5ce822..eccd0e20cb 100644
--- a/usr/src/uts/common/sys/zone.h
+++ b/usr/src/uts/common/sys/zone.h
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Igor Kozhukhov <ikozhukhov@gmail.com>.
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  * Copyright 2019 Nexenta Systems, Inc. All rights reserved.
  */
 
@@ -783,7 +783,15 @@ struct zsd_entry {
 /*
  * A root vnode of the current zone.
  */
-#define	ZONE_ROOTVP()	(curproc->p_zone->zone_rootvp)
+#define	ZONE_ROOTVP()	(curzone->zone_rootvp)
+
+/*
+ * Since a zone's root isn't necessarily an actual filesystem boundary
+ * (i.e. VROOT may not be set on zone->zone_rootvp) we need to not assume it.
+ * This macro helps in checking if a vnode is the current zone's rootvp.
+ * NOTE:  Using the VN_ prefix, even though it's defined here in zone.h.
+ */
+#define	VN_IS_CURZONEROOT(vp)   (VN_CMP(vp, ZONE_ROOTVP()))
 
 /*
  * Zone-safe version of thread_create() to be used when the caller wants to
-- 
2.21.0

