--- old/usr/src/cmd/zfs/zfs_main.c	Thu Oct 17 20:20:01 2019
+++ new/usr/src/cmd/zfs/zfs_main.c	Thu Oct 17 20:20:01 2019
@@ -6854,22 +6854,20 @@
 		 * per shared dataset downstream.
 		 */
 		if (op == OP_SHARE) {
-			zfs_handle_t **dslist = NULL;
-			size_t count = 0;
-			get_all_datasets(&dslist, &count, B_FALSE);
+			get_all_cb_t dslist = { 0 };
+			get_all_datasets(&dslist, B_FALSE);
 
-			if (count > 0) {
+			if (dslist.cb_used != 0) {
 				sa_init_selective_arg_t sharearg;
-				sharearg.zhandle_arr = dslist;
-				sharearg.zhandle_len = count;
-				if ((ret = zfs_init_libshare_arg(
-				    zfs_get_handle(dslist[0]),
-				    SA_INIT_SHARE_API_SELECTIVE, &sharearg))
-				    != SA_OK) {
+				sharearg.zhandle_arr = dslist.cb_handles;
+				sharearg.zhandle_len = dslist.cb_used;
+				if ((ret = zfs_init_libshare_arg(g_zfs,
+				    SA_INIT_SHARE_API_SELECTIVE, &sharearg)) !=
+				        SA_OK) {
 					(void) fprintf(stderr, gettext(
-					    "Could not initialize libshare,"
+					    "Could not initialize libshare, "
 					    "%d"), ret);
-					return (1);
+					return (ret);
 				}
 			}
 		}
--- old/usr/src/lib/libdtrace/common/nfs.d	Thu Oct 17 20:20:01 2019
+++ new/usr/src/lib/libdtrace/common/nfs.d	Thu Oct 17 20:20:01 2019
@@ -108,7 +108,7 @@
 	noi_curpath = (P->vp == NULL) ? "<unknown>" : P->vp->v_path;
 	noi_shrpath = (P->exi == NULL || P->exi->exi_export.ex_path == NULL) ?
 	    "<unknown>" : P->exi->exi_export.ex_path;
-	noi_zoneid = (P->exi == NULL) ? -1 : P->exi->exi_zoneid;
+	noi_zoneid = (P->exi == NULL) ? -1 : P->exi->exi_zone->zone_id;
 };
 
 typedef struct nfsv3opinfo {
@@ -131,5 +131,5 @@
 	    (arg3 == 0 || ((exportinfo_t *)arg3)->exi_export.ex_path == NULL) ?
 	    "<unknown>" : ((exportinfo_t *)arg3)->exi_export.ex_path;
 	noi_zoneid =
-	    (arg3 == 0) ? -1 : ((exportinfo_t *)arg3)->exi_zoneid;
+	    (arg3 == 0) ? -1 : ((exportinfo_t *)arg3)->exi_zone->zone_id;
 };
--- old/usr/src/uts/common/fs/nfs/nfs3_srv.c	Thu Oct 17 20:20:01 2019
+++ new/usr/src/uts/common/fs/nfs/nfs3_srv.c	Thu Oct 17 20:20:01 2019
@@ -411,6 +411,7 @@
 	 * location of the public filehandle.
 	 */
 	if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
+		ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 		dvp = ZONE_ROOTVP();
 		VN_HOLD(dvp);
 
@@ -444,10 +445,11 @@
 	}
 
 	fhp = &args->what.dir;
+	ASSERT3U(curzone->zone_id, ==, exi->exi_zoneid); /* exi is non-NULL */
 	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
 			 */
@@ -478,6 +480,7 @@
 		publicfh_flag = TRUE;
 
 		exi_rele(exi);
+		exi = NULL;
 
 		error = rfs_publicfh_mclookup(name, dvp, cr, &vp,
 		    &exi, &sec);
@@ -561,7 +564,6 @@
 	va.va_mask = AT_ALL;
 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 
-	exi_rele(exi);
 	VN_RELE(vp);
 
 	resp->status = NFS3_OK;
@@ -580,6 +582,7 @@
 	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
 	    LOOKUP3res *, resp);
 	VN_RELE(dvp);
+	exi_rele(exi);
 
 	return;
 
@@ -1333,7 +1336,9 @@
 		goto err;
 	}
 
+	ASSERT3U(curzone->zone_id, ==, exi->exi_zoneid); /* exi is non-NULL. */
 	ns = nfs3_get_srv();
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -2671,6 +2676,7 @@
 		goto err1;
 	}
 
+	ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 	error = VOP_RMDIR(vp, name, ZONE_ROOTVP(), cr, NULL, 0);
 
 	if (name != args->object.name)
@@ -4133,6 +4139,7 @@
 		goto out;
 	}
 
+	ASSERT3U(curzone->zone_id, ==, exi->exi_zoneid); /* exi is non-NULL. */
 	ns = nfs3_get_srv();
 	bva.va_mask = AT_ALL;
 	error = VOP_GETATTR(vp, &bva, 0, cr, NULL);
--- old/usr/src/uts/common/fs/nfs/nfs4_srv.c	Thu Oct 17 20:20:02 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_srv.c	Thu Oct 17 20:20:02 2019
@@ -818,8 +818,8 @@
 	    sizeof (rfs4_dss_path_t *), KM_SLEEP);
 
 	for (i = 0; i < dss_npaths; i++) {
-		/* CSTYLED */
-		sip->dss_paths[i] = rfs4_dss_newpath(nsrv4, sip, dss_paths[i], i);
+		sip->dss_paths[i] =
+		    rfs4_dss_newpath(nsrv4, sip, dss_paths[i], i);
 	}
 
 	mutex_enter(&nsrv4->servinst_lock);
@@ -857,9 +857,25 @@
 		rw_destroy(&sip->rwlock);
 		if (sip->oldstate)
 			kmem_free(sip->oldstate, sizeof (rfs4_oldstate_t));
-		if (sip->dss_paths)
+		if (sip->dss_paths) {
+			int i = sip->dss_npaths;
+
+			while (i > 0) {
+				i--;
+				if (sip->dss_paths[i] != NULL) {
+					char *path = sip->dss_paths[i]->path;
+
+					if (path != NULL) {
+						kmem_free(path,
+						    strlen(path) + 1);
+					}
+					kmem_free(sip->dss_paths[i],
+					    sizeof (rfs4_dss_path_t));
+				}
+			}
 			kmem_free(sip->dss_paths,
 			    sip->dss_npaths * sizeof (rfs4_dss_path_t *));
+		}
 		kmem_free(sip, sizeof (rfs4_servinst_t));
 #ifdef DEBUG
 		n++;
@@ -937,7 +953,7 @@
 {
 	int error, different_export = 0;
 	vnode_t *dvp, *vp;
-	struct exportinfo *exi = NULL;
+	struct exportinfo *exi;
 	fid_t fid;
 	uint_t count, i;
 	secinfo4 *resok_val;
@@ -948,6 +964,8 @@
 	nfs_export_t *ne = nfs_get_export();
 
 	dvp = cs->vp;
+	exi = cs->exi;
+	ASSERT(exi != NULL);
 	dotdot = (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0');
 
 	/*
@@ -955,13 +973,13 @@
 	 * root of a filesystem, or above an export point.
 	 */
 	if (dotdot) {
-
+		ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 		/*
 		 * If dotdotting at the root of a filesystem, then
 		 * need to traverse back to the mounted-on filesystem
 		 * and do the dotdot lookup there.
 		 */
-		if (cs->vp->v_flag & VROOT) {
+		if ((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp)) {
 
 			/*
 			 * If at the system root, then can
@@ -973,7 +991,7 @@
 			/*
 			 * Traverse back to the mounted-on filesystem
 			 */
-			dvp = untraverse(cs->vp);
+			dvp = untraverse(dvp);
 
 			/*
 			 * Set the different_export flag so we remember
@@ -987,7 +1005,7 @@
 			 * If dotdotting above an export point then set
 			 * the different_export to get new export info.
 			 */
-			different_export = nfs_exported(cs->exi, cs->vp);
+			different_export = nfs_exported(exi, dvp);
 		}
 	}
 
@@ -1006,9 +1024,9 @@
 	 * check whether this vnode is visible.
 	 */
 	if (!different_export &&
-	    (PSEUDO(cs->exi) || ! is_exported_sec(cs->nfsflavor, cs->exi) ||
+	    (PSEUDO(exi) || !is_exported_sec(cs->nfsflavor, exi) ||
 	    cs->access & CS_ACCESS_LIMITED)) {
-		if (! nfs_visible(cs->exi, vp, &different_export)) {
+		if (! nfs_visible(exi, vp, &different_export)) {
 			VN_RELE(vp);
 			return (puterrno4(ENOENT));
 		}
@@ -1050,6 +1068,7 @@
 			return (puterrno4(error));
 		}
 
+		/* We'll need to reassign "exi". */
 		if (dotdot)
 			exi = nfs_vptoexi(NULL, vp, cs->cr, &walk, NULL, TRUE);
 		else
@@ -1070,8 +1089,6 @@
 				return (puterrno4(EACCES));
 			}
 		}
-	} else {
-		exi = cs->exi;
 	}
 	ASSERT(exi != NULL);
 
@@ -2702,13 +2719,14 @@
 	 * export point.
 	 */
 	if (dotdot) {
-
+		ASSERT(cs->exi != NULL);
+		ASSERT3U(cs->exi->exi_zoneid, ==, curzone->zone_id);
 		/*
 		 * If dotdotting at the root of a filesystem, then
 		 * 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
--- old/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c	Thu Oct 17 20:20:03 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c	Thu Oct 17 20:20:03 2019
@@ -135,7 +135,12 @@
 	struct statvfs64 sb;
 
 	rfs4_init_compound_state(&cs);
-	cs.vp = ZONE_ROOTVP();
+	/*
+	 * This is global state checking, called once. We might be in
+	 * non-global-zone context here (say a modload happens from a zone
+	 * process) so in this case, we want the global-zone root vnode.
+	 */
+	cs.vp = rootvp;
 	cs.fh.nfs_fh4_val = NULL;
 	cs.cr = kcred;
 
@@ -1303,8 +1308,15 @@
 	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.
+	 *
+	 * NOTE: Not doing reality checks on curzone vs. compound
+	 * state vnode because it will mismatch once at initialization
+	 * if a non-global-zone triggers the module load, BUT in that case
+	 * the vp is literally "/" which has VROOT set.
+	 */
+	if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
 
 		/* extra hold for vp since untraverse might rele */
 		VN_HOLD(vp);
@@ -1311,7 +1323,7 @@
 		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
@@ -1318,7 +1330,7 @@
 		 * another getattr -- just use the one in sarg.
 		 */
 		if (VN_CMP(vp, stubvp)) {
-			ASSERT(VN_CMP(vp, ZONE_ROOTVP()));
+			ASSERT(VN_IS_CURZONEROOT(vp));
 			vap = sarg->vap;
 		} else {
 			va.va_mask = AT_NODEID;
@@ -1377,10 +1389,10 @@
 		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
--- old/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c	Thu Oct 17 20:20:03 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c	Thu Oct 17 20:20:03 2019
@@ -142,12 +142,12 @@
  *
  * 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 *
-pseudo_exportfs(nfs_export_t *ne, vnode_t *vp, fid_t *fid, struct exp_visible *vis_head,
-    struct exportdata *exdata)
+pseudo_exportfs(nfs_export_t *ne, vnode_t *vp, fid_t *fid,
+    struct exp_visible *vis_head, struct exportdata *exdata)
 {
 	struct exportinfo *exi;
 	struct exportdata *kex;
@@ -165,10 +165,10 @@
 	VN_HOLD(exi->exi_vp);
 	exi->exi_visible = vis_head;
 	exi->exi_count = 1;
+	exi->exi_zoneid = ne->ne_globals->nfs_zoneid;
 	exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
 	    VSW_VOLATILEDEV) ? 1 : 0;
 	mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
-	exi->exi_zoneid = ne->ne_globals->nfs_zoneid;
 
 	/*
 	 * Build up the template fhandle
@@ -640,8 +640,10 @@
 	struct vattr va;
 	treenode_t *tree_head = NULL;
 	timespec_t now;
-	nfs_export_t *ne = nfs_get_export();
+	nfs_export_t *ne;
 
+	ne = exip->exi_ne;
+	ASSERT3P(ne, ==, nfs_get_export());	/* curzone reality check */
 	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 
 	gethrestime(&now);
@@ -658,11 +660,14 @@
 		if (error)
 			break;
 
+		/* XXX KEBE ASKS DO WE NEED THIS?!? */
+		ASSERT3U(exip->exi_zoneid, ==, curzone->zone_id);
 		/*
-		 * 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 || vp == EXI_TO_ZONEROOTVP(exip)) {
+			if (!exportdir) {
 				struct exportinfo *exi;
 
 				/*
@@ -696,7 +701,7 @@
 				vis_head = NULL;
 			}
 
-			if (VN_CMP(vp, ZONE_ROOTVP())) {
+			if (VN_IS_CURZONEROOT(vp)) {
 				/* at system root */
 				/*
 				 * If sharing "/", new_exi is shared exportinfo
@@ -826,7 +831,7 @@
  * 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)
@@ -835,6 +840,8 @@
 	treenode_t *connect_point = NULL;
 
 	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
+	ASSERT(curzone->zone_id == exip->exi_zoneid ||
+	    curzone->zone_id == global_zone->zone_id);
 
 	/*
 	 * exi_tree can be null for the zone root
@@ -859,7 +866,10 @@
 
 	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;
@@ -872,6 +882,7 @@
 			mutex_exit(&nfs_exi_id_lock);
 			export_unlink(ne, tnode->tree_exi);
 			exi_rele(tnode->tree_exi);
+			tnode->tree_exi = NULL;
 		}
 
 		/* Release visible in parent's exportinfo */
@@ -899,6 +910,8 @@
  * Traverse backward across mountpoint from the
  * root vnode of a filesystem to its mounted-on
  * vnode.
+ *
+ * Callers to this function have confirmed the use of curzone is safe here.
  */
 vnode_t *
 untraverse(vnode_t *vp)
@@ -907,7 +920,7 @@
 
 	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 */
@@ -938,7 +951,7 @@
 
 /*
  * Given an exportinfo, climb up to find the exportinfo for the VROOT
- * of the filesystem.
+ * (or zone root) of the filesystem.
  *
  * e.g.         /
  *              |
@@ -955,7 +968,7 @@
  *
  * If d is shared, then c will be put into a's visible list.
  * Note: visible list is per filesystem and is attached to the
- * VROOT exportinfo.
+ * VROOT exportinfo.  Returned exi does NOT have a new hold.
  */
 struct exportinfo *
 get_root_export(struct exportinfo *exip)
@@ -987,12 +1000,15 @@
 	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))
+	ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
+	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.
@@ -1065,7 +1081,7 @@
 	 * 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 */
@@ -1173,7 +1189,7 @@
 	 * 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)
--- old/usr/src/uts/common/fs/nfs/nfs4_state.c	Thu Oct 17 20:20:03 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_state.c	Thu Oct 17 20:20:03 2019
@@ -1496,6 +1496,14 @@
 
 	rfs4_set_deleg_policy(nsrv4, SRV_NEVER_DELEGATE);
 
+	/*
+	 * Clean up any dangling stable storage structures BEFORE calling
+	 * rfs4_servinst_destroy_all() so there are no dangling structures
+	 * (i.e. the srvinsts are all cleared of danglers BEFORE they get
+	 * freed).
+	 */
+	rfs4_ss_fini(nsrv4);
+
 	mutex_enter(&nsrv4->state_lock);
 
 	if (nsrv4->nfs4_server_state == NULL) {
@@ -1517,12 +1525,14 @@
 	/* First stop all of the reaper threads in the database */
 	rfs4_database_shutdown(dbp);
 	/*
-	 * XXX workaround
-	 * Skip destrying the state database yet just in case there
-	 * are unfinished operations depending on it.
+	 * WARNING: There may be consumers of the rfs4 database still
+	 * active as we destroy these.  IF that's the case, consider putting
+	 * some of their _zone_fini()-like functions into the zsd key as
+	 * ~~SHUTDOWN~~ functions instead of ~~DESTROY~~ functions.  We can
+	 * maintain some ordering guarantees better that way.
 	 */
 	/* Now destroy/release the database tables */
-	/* rfs4_database_destroy(dbp); */
+	rfs4_database_destroy(dbp);
 
 	/* Reset the cache timers for next time */
 	nsrv4->rfs4_client_cache_time = 0;
@@ -1534,9 +1544,6 @@
 	nsrv4->rfs4_deleg_state_cache_time = 0;
 
 	mutex_exit(&nsrv4->state_lock);
-
-	/* clean up any dangling stable storage structures */
-	rfs4_ss_fini(nsrv4);
 }
 
 typedef union {
@@ -4123,11 +4130,11 @@
 		return;
 	}
 
-	/* CSTYLED */
-	rfs4_dbe_walk(nsrv4->rfs4_lo_state_tab, rfs4_lo_state_walk_callout, exi);
+	rfs4_dbe_walk(nsrv4->rfs4_lo_state_tab,
+	    rfs4_lo_state_walk_callout, exi);
 	rfs4_dbe_walk(nsrv4->rfs4_state_tab, rfs4_state_walk_callout, exi);
-	/* CSTYLED */
-	rfs4_dbe_walk(nsrv4->rfs4_deleg_state_tab, rfs4_deleg_state_walk_callout, exi);
+	rfs4_dbe_walk(nsrv4->rfs4_deleg_state_tab,
+	    rfs4_deleg_state_walk_callout, exi);
 	rfs4_dbe_walk(nsrv4->rfs4_file_tab, rfs4_file_walk_callout, exi);
 
 	mutex_exit(&nsrv4->state_lock);
--- old/usr/src/uts/common/fs/nfs/nfs_auth.c	Thu Oct 17 20:20:04 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_auth.c	Thu Oct 17 20:20:04 2019
@@ -281,6 +281,9 @@
 	cv_destroy(&nag->refreshq_cv);
 	mutex_destroy(&nag->refreshq_lock);
 	mutex_destroy(&nag->mountd_lock);
+	/* Extra cleanup. */
+	if (nag->mountd_dh != NULL)
+		door_ki_rele(nag->mountd_dh);
 	kmem_free(nag, sizeof (*nag));
 }
 
@@ -875,6 +878,7 @@
 
 	ASSERT(cr != NULL);
 
+	ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
 	nag = nfsauth_get_zg();
 
 	/*
--- old/usr/src/uts/common/fs/nfs/nfs_export.c	Thu Oct 17 20:20:04 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_export.c	Thu Oct 17 20:20:04 2019
@@ -702,7 +702,8 @@
 		}
 	}
 
-	ASSERT(exi_ret); /* Every visible should have its home exportinfo */
+	/* Every visible should have its home exportinfo */
+	ASSERT(exi_ret != NULL);
 	return (exi_ret);
 }
 
@@ -711,7 +712,7 @@
  * Add or remove the newly exported or unexported security flavors of the
  * given exportinfo from its ancestors upto the system root.
  */
-void
+static void
 srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec,
     int seccnt, bool_t isadd)
 {
@@ -742,6 +743,7 @@
 	 * transferred from the PSEUDO export in exportfs()
 	 */
 	if (isadd && !(exip->exi_vp->v_flag & VROOT) &&
+	    !VN_CMP(exip->exi_vp, EXI_TO_ZONEROOTVP(exip)) &&
 	    tnode->tree_vis->vis_seccnt > 0) {
 		srv_secinfo_add(&exip->exi_export.ex_secinfo,
 		    &exip->exi_export.ex_seccnt, tnode->tree_vis->vis_secinfo,
@@ -807,7 +809,6 @@
 	exportinfo_t **bckt;
 
 	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
-	ASSERT(exi->exi_zoneid == ne->ne_globals->nfs_zoneid);
 
 	bckt = &ne->exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
 	exp_hash_link(exi, fid_hash, bckt);
@@ -815,6 +816,7 @@
 	bckt = &ne->exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path,
 	    strlen(exi->exi_export.ex_path))];
 	exp_hash_link(exi, path_hash, bckt);
+	exi->exi_ne = ne;
 }
 
 /*
@@ -891,6 +893,7 @@
 {
 	int i;
 	nfs_export_t *ne;
+	zone_t *zone;
 
 	ne = kmem_zalloc(sizeof (*ne), KM_SLEEP);
 
@@ -915,7 +918,18 @@
 	ne->exi_root->exi_count = 1;
 	mutex_init(&ne->exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
 
-	ne->exi_root->exi_vp = ZONE_ROOTVP();
+	/*
+	 * Because we cannot:
+	 *	ASSERT(curzone->zone_id == ng->nfs_zoneid);
+	 * We grab the zone pointer explicitly (like netstacks do) and
+	 * set the rootvp here.
+	 *
+	 * Subsequent exportinfo_t's that get export_link()ed to "ne" also
+	 * will backpoint to "ne" such that exi->exi_ne->exi_root->exi_vp
+	 * will get the zone's rootvp for a given exportinfo_t.
+	 */
+	zone = zone_find_by_id_nolock(ng->nfs_zoneid);
+	ne->exi_root->exi_vp = zone->zone_rootvp;
 	ne->exi_root->exi_zoneid = ng->nfs_zoneid;
 
 	/*
@@ -1395,6 +1409,7 @@
 	exi->exi_vp = vp;
 	exi->exi_count = 1;
 	exi->exi_zoneid = crgetzoneid(cr);
+	ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 	exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
 	    VSW_VOLATILEDEV) ? 1 : 0;
 	mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
@@ -1864,6 +1879,8 @@
 
 	exp_hash_unlink(exi, fid_hash);
 	exp_hash_unlink(exi, path_hash);
+	ASSERT3P(exi->exi_ne, ==, ne);
+	exi->exi_ne = NULL;
 }
 
 /*
@@ -1937,6 +1954,11 @@
 	 * If this was a public export, restore
 	 * the public filehandle to the root.
 	 */
+
+	/*
+	 * XXX KEBE ASKS --> Should CRED() instead be
+	 * exi->exi_zone->zone_kcred?
+	 */
 	if (exi == ne->exi_public) {
 		ne->exi_public = ne->exi_root;
 
@@ -2171,7 +2193,8 @@
 		 * If we're at the root of this filesystem, then
 		 * it's time to stop (with failure).
 		 */
-		if (vp->v_flag & VROOT) {
+		ASSERT3P(vp->v_vfsp->vfs_zone, ==, curzone);
+		if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
 			error = EINVAL;
 			break;
 		}
--- old/usr/src/uts/common/fs/nfs/nfs_server.c	Thu Oct 17 20:20:04 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_server.c	Thu Oct 17 20:20:04 2019
@@ -2947,11 +2947,13 @@
 			/* Release the reference on the old exi value */
 			ASSERT(*exi != NULL);
 			exi_rele(*exi);
+			*exi = NULL;
 
 			if (error = nfs_check_vpexi(mc_dvp, *vpp, kcred, exi)) {
 				VN_RELE(*vpp);
 				goto publicfh_done;
 			}
+			/* Have a new *exi */
 		}
 	}
 
@@ -2978,6 +2980,8 @@
 	struct pathname pn;
 	int error;
 
+	ASSERT3U(crgetzoneid(cr), ==, curzone->zone_id);
+
 	/*
 	 * If pathname starts with '/', then set startdvp to root.
 	 */
--- old/usr/src/uts/common/fs/nfs/nfs_srv.c	Thu Oct 17 20:20:05 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_srv.c	Thu Oct 17 20:20:05 2019
@@ -417,7 +417,8 @@
 	struct exportinfo *exi;
 	vnode_t *dvp = *dvpp;
 
-	ASSERT(dvp->v_flag & VROOT);
+	ASSERT3U((*exip)->exi_zoneid, ==, curzone->zone_id);
+	ASSERT((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp));
 
 	VN_HOLD(dvp);
 	dvp = untraverse(dvp);
@@ -427,6 +428,7 @@
 		return (-1);
 	}
 
+	ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 	exi_rele(*exip);
 	*exip = exi;
 	VN_RELE(*dvpp);
@@ -487,6 +489,7 @@
 	}
 
 	exi_hold(exi);
+	ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 
 	/*
 	 * Not allow lookup beyond root.
@@ -496,7 +499,7 @@
 	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
 			 */
@@ -532,6 +535,7 @@
 		publicfh_flag = TRUE;
 
 		exi_rele(exi);
+		exi = NULL;
 
 		error = rfs_publicfh_mclookup(name, dvp, cr, &vp, &exi,
 		    &sec);
@@ -1318,6 +1322,7 @@
 	caller_context_t ct;
 	nfs_srv_t *nsrv;
 
+	ASSERT(exi == NULL || exi->exi_zoneid == curzone->zone_id);
 	nsrv = nfs_get_srv();
 	if (!nsrv->write_async) {
 		rfs_write_sync(wa, ns, exi, req, cr, ro);
--- old/usr/src/uts/common/fs/sharefs/sharetab.c	Thu Oct 17 20:20:05 2019
+++ new/usr/src/uts/common/fs/sharefs/sharetab.c	Thu Oct 17 20:20:05 2019
@@ -25,6 +25,7 @@
 
 /*
  * Copyright 2018 Nexenta Systems, Inc.
+ * Copyright 2019 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -309,6 +310,27 @@
 	rw_destroy(&sg->sharefs_lock);
 	rw_destroy(&sg->sharetab_lock);
 
+	/* ALL of the allocated things must be cleaned before we free sg. */
+	while (sg->sharefs_sharetab != NULL) {
+		int i;
+		sharetab_t *freeing = sg->sharefs_sharetab;
+
+		sg->sharefs_sharetab = freeing->s_next;
+		kmem_free(freeing->s_fstype, strlen(freeing->s_fstype) + 1);
+		for (i = 0; i < PKP_HASH_SIZE; i++) {
+			sharefs_hash_head_t *bucket;
+
+			bucket = &(freeing->s_buckets[i]);
+			while (bucket->ssh_sh != NULL) {
+				share_t *share = bucket->ssh_sh;
+
+				bucket->ssh_sh = share->sh_next;
+				sharefree(share, NULL);
+			}
+		}
+		kmem_free(freeing, sizeof (*freeing));
+	}
+
 	kmem_free(sg, sizeof (*sg));
 }
 
--- old/usr/src/uts/common/klm/nlm_impl.c	Thu Oct 17 20:20:05 2019
+++ new/usr/src/uts/common/klm/nlm_impl.c	Thu Oct 17 20:20:05 2019
@@ -843,7 +843,7 @@
 	 * statd using the status monitor protocol.
 	 */
 	error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, SM_PROG, SM_VERS,
-	    0, NLM_RPC_RETRIES, kcred, &nsm->ns_handle);
+	    0, NLM_RPC_RETRIES, zone_kcred(), &nsm->ns_handle);
 	if (error != 0)
 		goto error;
 
@@ -852,7 +852,8 @@
 	 * local statd using the address registration protocol.
 	 */
 	error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, NSM_ADDR_PROGRAM,
-	    NSM_ADDR_V1, 0, NLM_RPC_RETRIES, kcred, &nsm->ns_addr_handle);
+	    NSM_ADDR_V1, 0, NLM_RPC_RETRIES, zone_kcred(),
+	    &nsm->ns_addr_handle);
 	if (error != 0)
 		goto error;
 
@@ -861,8 +862,11 @@
 
 error:
 	kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
-	if (nsm->ns_handle)
+	if (nsm->ns_handle) {
+		ASSERT(nsm->ns_handle->cl_auth != NULL);
+		auth_destroy(nsm->ns_handle->cl_auth);
 		CLNT_DESTROY(nsm->ns_handle);
+	}
 
 	return (error);
 }
@@ -871,8 +875,12 @@
 nlm_nsm_fini(struct nlm_nsm *nsm)
 {
 	kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
+	if (nsm->ns_addr_handle->cl_auth != NULL)
+		auth_destroy(nsm->ns_addr_handle->cl_auth);
 	CLNT_DESTROY(nsm->ns_addr_handle);
 	nsm->ns_addr_handle = NULL;
+	if (nsm->ns_handle->cl_auth != NULL)
+		auth_destroy(nsm->ns_handle->cl_auth);
 	CLNT_DESTROY(nsm->ns_handle);
 	nsm->ns_handle = NULL;
 	sema_destroy(&nsm->ns_sem);
@@ -2627,9 +2635,17 @@
 
 	rw_enter(&lm_lck, RW_READER);
 	TAILQ_FOREACH(g, &nlm_zones_list, nlm_link) {
-		if (g->nlm_zoneid != exi->exi_zoneid)
-			continue;
-		nlm_zone_unexport(g, exi);
+		if (g->nlm_zoneid == exi->exi_zoneid) {
+			/*
+			 * NOTE: If we want to drop lm_lock before
+			 * calling nlm_zone_unexport(), we should break,
+			 * and have a post-rw_exit() snippit like:
+			 *	if (g != NULL)
+			 *		nlm_zone_unexport(g, exi);
+			 */
+			nlm_zone_unexport(g, exi);
+			break; /* Only going to match once! */
+		}
 	}
 	rw_exit(&lm_lck);
 }
@@ -2802,7 +2818,7 @@
 nlm_nsm_clnt_init(CLIENT *clnt, struct nlm_nsm *nsm)
 {
 	(void) clnt_tli_kinit(clnt, &nsm->ns_knc, &nsm->ns_addr, 0,
-	    NLM_RPC_RETRIES, kcred);
+	    NLM_RPC_RETRIES, zone_kcred());
 }
 
 static void
--- old/usr/src/uts/common/nfs/export.h	Thu Oct 17 20:20:05 2019
+++ new/usr/src/uts/common/nfs/export.h	Thu Oct 17 20:20:05 2019
@@ -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,21 @@
 } treenode_t;
 
 /*
- * TREE_ROOT checks if the node corresponds to a filesystem root
+ * Now that we have links to chase, we can get the zone rootvp just from
+ * an export.  No current-zone-context needed.
+ */
+#define	EXI_TO_ZONEROOTVP(exi) ((exi)->exi_ne->exi_root->exi_vp)
+
+/*
+ * 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_CMP(EXI_TO_ZONEROOTVP((t)->tree_exi), (t)->tree_exi->exi_vp)))
 
 #define	TREE_EXPORTED(t) \
 	((t)->tree_exi && !PSEUDO((t)->tree_exi))
@@ -519,6 +529,7 @@
 	krwlock_t		exi_cache_lock;
 	kmutex_t		exi_lock;
 	uint_t			exi_count;
+	zoneid_t		exi_zoneid;
 	vnode_t			*exi_vp;
 	vnode_t			*exi_dvp;
 	avl_tree_t		*exi_cache[AUTH_TABLESIZE];
@@ -529,7 +540,13 @@
 	unsigned		exi_moved:1;
 	int			exi_id;
 	avl_node_t		exi_id_link;
-	zoneid_t		exi_zoneid;
+	/*
+	 * Soft-reference/backpointer to zone's nfs_export_t.
+	 * This allows us access to the zone's rootvp (stored in
+	 * exi_ne->exi_root->exi_vp) even if the current thread isn't in
+	 * same-zone context.
+	 */
+	struct nfs_export	*exi_ne;
 #ifdef VOLATILE_FH_TEST
 	uint32_t		exi_volatile_id;
 	struct ex_vol_rename	*exi_vol_rename;
@@ -657,7 +674,8 @@
 	krwlock_t exported_lock;
 
 	/* "public" and default (root) location for public filehandle */
-	struct exportinfo *exi_public, *exi_root;
+	struct exportinfo *exi_public;
+	struct exportinfo *exi_root;
 	/* For checking default public file handle */
 	fid_t exi_rootfid;
 	/* For comparing V2 filehandles */
--- old/usr/src/uts/common/os/zone.c	Thu Oct 17 20:20:06 2019
+++ new/usr/src/uts/common/os/zone.c	Thu Oct 17 20:20:06 2019
@@ -2394,8 +2394,15 @@
 
 	cpu_uarray_free(zone->zone_ustate);
 
-	if (zone->zone_rootvp != NULL)
-		VN_RELE(zone->zone_rootvp);
+	if (zone->zone_rootvp != NULL) {
+		vnode_t *vp = zone->zone_rootvp;
+
+		mutex_enter(&vp->v_lock);
+		vp->v_flag &= ~VZONEROOT;
+		mutex_exit(&vp->v_lock);
+		VN_RELE(vp);
+		/* No need to worry about NULL-ing out zone_rootvp. */
+	}
 	if (zone->zone_rootpath)
 		kmem_free(zone->zone_rootpath, zone->zone_rootpathlen);
 	if (zone->zone_name != NULL)
@@ -3478,6 +3485,16 @@
 	}
 
 	ASSERT(error == 0);
+	mutex_enter(&vp->v_lock);
+	if (vp->v_flag & VZONEROOT) {
+		/* Wow, someone's already using this zone root! */
+		error = EEXIST;	/* XXX KEBE ASKS, better errno? */
+		mutex_exit(&vp->v_lock);
+		VN_RELE(vp);
+		goto out;
+	}
+	vp->v_flag |= VZONEROOT;
+	mutex_exit(&vp->v_lock);
 	zone->zone_rootvp = vp;		/* we hold a reference to vp */
 	zone->zone_rootpath = path;
 	zone->zone_rootpathlen = pathlen;
@@ -5369,7 +5386,12 @@
 	 * other thread that might access it exist.
 	 */
 	if (zone->zone_rootvp != NULL) {
-		VN_RELE(zone->zone_rootvp);
+		vnode_t *vp = zone->zone_rootvp;
+
+		mutex_enter(&vp->v_lock);
+		vp->v_flag &= ~VZONEROOT;
+		mutex_exit(&vp->v_lock);
+		VN_RELE(vp);
 		zone->zone_rootvp = NULL;
 	}
 
--- old/usr/src/uts/common/sys/vnode.h	Thu Oct 17 20:20:06 2019
+++ new/usr/src/uts/common/sys/vnode.h	Thu Oct 17 20:20:06 2019
@@ -415,6 +415,13 @@
 #define	VTRAVERSE	0x80000
 
 /*
+ * Indicates the vnode is a zone's root ("/" in-zone). A zone's root does NOT
+ * have to be a filesystem root, even if many zone brands guaranteed that.
+ */
+#define	VZONEROOT	0x100000
+
+
+/*
  * Vnode attributes.  A bit-mask is supplied as part of the
  * structure to indicate the attributes the caller wants to
  * set (setattr) or extract (getattr).
--- old/usr/src/uts/common/sys/zone.h	Thu Oct 17 20:20:07 2019
+++ new/usr/src/uts/common/sys/zone.h	Thu Oct 17 20:20:07 2019
@@ -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.
  */
 
@@ -782,10 +782,25 @@
 
 /*
  * A root vnode of the current zone.
+ *
+ * NOTE: It may be necessary (initialization time for file sharing where an
+ * NGZ loads a file-sharing kernel module that does zsd initialization) to NOT
+ * use this macro. One should ASSERT() that curzone == active ZSD (an
+ * ASSERTion that's not always true at ZSD initialization time) during regular
+ * use of this macro.
  */
-#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.
+ * NOTE2: See above warning about ZONE_ROOTVP().
+ */
+#define	VN_IS_CURZONEROOT(vp)   (VN_CMP(vp, ZONE_ROOTVP()))
+
+/*
  * Zone-safe version of thread_create() to be used when the caller wants to
  * create a kernel thread to run within the current zone's context.
  */
