--- old/usr/src/uts/common/fs/nfs/nfs_export.c	Mon Feb  3 11:23:29 2020
+++ new/usr/src/uts/common/fs/nfs/nfs_export.c	Mon Feb  3 11:23:29 2020
@@ -20,15 +20,17 @@
  */
 
 /*
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
- *  	Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.
+ *	Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.
  *		All rights reserved.
  */
 
+/*
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -65,14 +67,25 @@
 #include <nfs/nfs_log.h>
 #include <nfs/lm.h>
 #include <sys/sunddi.h>
-#include <sys/pkp_hash.h>
 
-treenode_t *ns_root;
+/*
+ * exi_id support
+ *
+ * exi_id_next		The next exi_id available.
+ * exi_id_overflow	The exi_id_next already overflowed, so we should
+ *			thoroughly check for duplicates.
+ * exi_id_tree		AVL tree indexed by exi_id.
+ * nfs_exi_id_lock	Lock to protect the export ID list
+ *
+ * All exi_id_next, exi_id_overflow, and exi_id_tree are protected by
+ * nfs_exi_id_lock.
+ */
+static int exi_id_next;
+static bool_t exi_id_overflow;
+avl_tree_t exi_id_tree;
+kmutex_t nfs_exi_id_lock;
 
-struct exportinfo *exptable_path_hash[PKP_HASH_SIZE];
-struct exportinfo *exptable[EXPTABLESIZE];
-
-static int	unexport(exportinfo_t *);
+static int	unexport(nfs_export_t *, exportinfo_t *);
 static void	exportfree(exportinfo_t *);
 static int	loadindex(exportdata_t *);
 
@@ -80,31 +93,18 @@
 extern int	sec_svc_loadrootnames(int, int, caddr_t **, model_t);
 extern void	sec_svc_freerootnames(int, int, caddr_t *);
 
-static int build_seclist_nodups(exportdata_t *, secinfo_t *, int);
-static void srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int);
-static void srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int);
-static void srv_secinfo_treeclimb(exportinfo_t *, secinfo_t *, int, bool_t);
+static int	build_seclist_nodups(exportdata_t *, secinfo_t *, int);
+static void	srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int);
+static void	srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int);
+static void	srv_secinfo_treeclimb(nfs_export_t *, exportinfo_t *,
+		    secinfo_t *, int, bool_t);
 
 #ifdef VOLATILE_FH_TEST
 static struct ex_vol_rename *find_volrnm_fh(exportinfo_t *, nfs_fh4 *);
 static uint32_t find_volrnm_fh_id(exportinfo_t *, nfs_fh4 *);
-static void free_volrnm_list(exportinfo_t *);
+static void	free_volrnm_list(exportinfo_t *);
 #endif /* VOLATILE_FH_TEST */
 
-/*
- * exported_lock	Read/Write lock that protects the exportinfo list.
- *			This lock must be held when searching or modifiying
- *			the exportinfo list.
- */
-krwlock_t exported_lock;
-
-/*
- * "public" and default (root) location for public filehandle
- */
-struct exportinfo *exi_public, *exi_root;
-
-fid_t exi_rootfid;	/* for checking the default public file handle */
-
 fhandle_t nullfh2;	/* for comparing V2 filehandles */
 
 /*
@@ -117,6 +117,15 @@
 
 #define	exptablehash(fsid, fid) (nfs_fhhash((fsid), (fid)) & (EXPTABLESIZE - 1))
 
+extern nfs_export_t *
+nfs_get_export(void)
+{
+	nfs_globals_t *ng = nfs_srv_getzg();
+	nfs_export_t *ne = ng->nfs_export;
+	ASSERT(ne != NULL);
+	return (ne);
+}
+
 static uint8_t
 xor_hash(uint8_t *data, int len)
 {
@@ -693,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);
 }
 
@@ -702,15 +712,26 @@
  * Add or remove the newly exported or unexported security flavors of the
  * given exportinfo from its ancestors upto the system root.
  */
-void
-srv_secinfo_treeclimb(exportinfo_t *exip, secinfo_t *sec, int seccnt,
-    bool_t isadd)
+static void
+srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec,
+    int seccnt, bool_t isadd)
 {
-	treenode_t *tnode = exip->exi_tree;
+	treenode_t *tnode;
 
-	ASSERT(RW_WRITE_HELD(&exported_lock));
-	ASSERT(tnode != NULL);
+	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 
+	/*
+	 * exi_tree can be null for the zone root
+	 * which means we're already at the "top"
+	 * and there's nothing more to "climb".
+	 */
+	tnode = exip->exi_tree;
+	if (tnode == NULL) {
+		/* Should only happen for... */
+		ASSERT(exip == ne->exi_root);
+		return;
+	}
+
 	if (seccnt == 0)
 		return;
 
@@ -722,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,
@@ -782,111 +804,305 @@
 	*(bucket) = (exi);
 
 void
-export_link(exportinfo_t *exi)
+export_link(nfs_export_t *ne, exportinfo_t *exi)
 {
 	exportinfo_t **bckt;
 
-	bckt = &exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
+	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
+
+	bckt = &ne->exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
 	exp_hash_link(exi, fid_hash, bckt);
 
-	bckt = &exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path,
+	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;
 }
 
 /*
- * Initialization routine for export routines. Should only be called once.
+ * Helper functions for exi_id handling
  */
+static int
+exi_id_compar(const void *v1, const void *v2)
+{
+	const struct exportinfo *e1 = v1;
+	const struct exportinfo *e2 = v2;
+
+	if (e1->exi_id < e2->exi_id)
+		return (-1);
+	if (e1->exi_id > e2->exi_id)
+		return (1);
+
+	return (0);
+}
+
 int
-nfs_exportinit(void)
+exi_id_get_next()
 {
-	int error;
+	struct exportinfo e;
+	int ret = exi_id_next;
+
+	ASSERT(MUTEX_HELD(&nfs_exi_id_lock));
+
+	do {
+		exi_id_next++;
+		if (exi_id_next == 0)
+			exi_id_overflow = TRUE;
+
+		if (!exi_id_overflow)
+			break;
+
+		if (exi_id_next == ret)
+			cmn_err(CE_PANIC, "exi_id exhausted");
+
+		e.exi_id = exi_id_next;
+	} while (avl_find(&exi_id_tree, &e, NULL) != NULL);
+
+	return (ret);
+}
+
+/*
+ * Get the root file handle for this zone.
+ * Called when nfs_svc() starts
+ */
+int
+nfs_export_get_rootfh(nfs_globals_t *g)
+{
+	nfs_export_t *ne = g->nfs_export;
+	int err;
+
+	ne->exi_rootfid.fid_len = MAXFIDSZ;
+	err = vop_fid_pseudo(ne->exi_root->exi_vp, &ne->exi_rootfid);
+	if (err != 0) {
+		ne->exi_rootfid.fid_len = 0;
+		return (err);
+	}
+
+	/* Setup the fhandle template exi_fh */
+	ne->exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid;
+	ne->exi_root->exi_fh.fh_xlen = ne->exi_rootfid.fid_len;
+	bcopy(ne->exi_rootfid.fid_data, ne->exi_root->exi_fh.fh_xdata,
+	    ne->exi_rootfid.fid_len);
+	ne->exi_root->exi_fh.fh_len = sizeof (ne->exi_root->exi_fh.fh_data);
+
+	return (0);
+}
+
+void
+nfs_export_zone_init(nfs_globals_t *ng)
+{
 	int i;
+	nfs_export_t *ne;
+	zone_t *zone;
 
-	rw_init(&exported_lock, NULL, RW_DEFAULT, NULL);
+	ne = kmem_zalloc(sizeof (*ne), KM_SLEEP);
 
+	rw_init(&ne->exported_lock, NULL, RW_DEFAULT, NULL);
+
+	ne->ne_globals = ng; /* "up" pointer */
+
 	/*
 	 * Allocate the place holder for the public file handle, which
 	 * is all zeroes. It is initially set to the root filesystem.
 	 */
-	exi_root = kmem_zalloc(sizeof (*exi_root), KM_SLEEP);
-	exi_public = exi_root;
+	ne->exi_root = kmem_zalloc(sizeof (*ne->exi_root), KM_SLEEP);
+	ne->exi_public = ne->exi_root;
 
-	exi_root->exi_export.ex_flags = EX_PUBLIC;
-	exi_root->exi_export.ex_pathlen = 1;	/* length of "/" */
-	exi_root->exi_export.ex_path =
-	    kmem_alloc(exi_root->exi_export.ex_pathlen + 1, KM_SLEEP);
-	exi_root->exi_export.ex_path[0] = '/';
-	exi_root->exi_export.ex_path[1] = '\0';
+	ne->exi_root->exi_export.ex_flags = EX_PUBLIC;
+	ne->exi_root->exi_export.ex_pathlen = 1;	/* length of "/" */
+	ne->exi_root->exi_export.ex_path =
+	    kmem_alloc(ne->exi_root->exi_export.ex_pathlen + 1, KM_SLEEP);
+	ne->exi_root->exi_export.ex_path[0] = '/';
+	ne->exi_root->exi_export.ex_path[1] = '\0';
 
-	exi_root->exi_count = 1;
-	mutex_init(&exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
+	ne->exi_root->exi_count = 1;
+	mutex_init(&ne->exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
 
-	exi_root->exi_vp = rootdir;
-	exi_rootfid.fid_len = MAXFIDSZ;
-	error = vop_fid_pseudo(exi_root->exi_vp, &exi_rootfid);
-	if (error) {
-		mutex_destroy(&exi_root->exi_lock);
-		kmem_free(exi_root, sizeof (*exi_root));
-		return (error);
-	}
+	/*
+	 * 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;
 
 	/*
-	 * Initialize auth cache and auth cache lock
+	 * Fill in ne->exi_rootfid later, in nfs_export_get_rootfid
+	 * because we can't correctly return errors here.
 	 */
+
+	/* Initialize auth cache and auth cache lock */
 	for (i = 0; i < AUTH_TABLESIZE; i++) {
-		exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t),
+		ne->exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t),
 		    KM_SLEEP);
-		avl_create(exi_root->exi_cache[i], nfsauth_cache_clnt_compar,
-		    sizeof (struct auth_cache_clnt),
+		avl_create(ne->exi_root->exi_cache[i],
+		    nfsauth_cache_clnt_compar, sizeof (struct auth_cache_clnt),
 		    offsetof(struct auth_cache_clnt, authc_link));
 	}
-	rw_init(&exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL);
+	rw_init(&ne->exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL);
 
-	/* setup the fhandle template */
-	exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid;
-	exi_root->exi_fh.fh_xlen = exi_rootfid.fid_len;
-	bcopy(exi_rootfid.fid_data, exi_root->exi_fh.fh_xdata,
-	    exi_rootfid.fid_len);
-	exi_root->exi_fh.fh_len = sizeof (exi_root->exi_fh.fh_data);
+	/* setup exi_fh later, in nfs_export_get_rootfid */
 
-	/*
-	 * Publish the exportinfo in the hash table
-	 */
-	export_link(exi_root);
+	rw_enter(&ne->exported_lock, RW_WRITER);
 
-	nfslog_init();
-	ns_root = NULL;
+	/* Publish the exportinfo in the hash table */
+	export_link(ne, ne->exi_root);
 
-	return (0);
+	/* Initialize exi_id and exi_kstats */
+	mutex_enter(&nfs_exi_id_lock);
+	ne->exi_root->exi_id = exi_id_get_next();
+	avl_add(&exi_id_tree, ne->exi_root);
+	mutex_exit(&nfs_exi_id_lock);
+
+	rw_exit(&ne->exported_lock);
+	ne->ns_root = NULL;
+
+	ng->nfs_export = ne;
 }
 
 /*
- * Finalization routine for export routines. Called to cleanup previously
- * initialization work when the NFS server module could not be loaded correctly.
+ * During zone shutdown, remove exports
  */
 void
-nfs_exportfini(void)
+nfs_export_zone_shutdown(nfs_globals_t *ng)
 {
+	nfs_export_t *ne = ng->nfs_export;
+	struct exportinfo *exi, *nexi;
+	int i, errors;
+
+	rw_enter(&ne->exported_lock, RW_READER);
+
+	errors = 0;
+	for (i = 0; i < EXPTABLESIZE; i++) {
+
+		exi = ne->exptable[i];
+		if (exi != NULL)
+			exi_hold(exi);
+
+		while (exi != NULL) {
+
+			/*
+			 * Get and hold next export before
+			 * dropping the rwlock and unexport
+			 */
+			nexi = exi->fid_hash.next;
+			if (nexi != NULL)
+				exi_hold(nexi);
+
+			rw_exit(&ne->exported_lock);
+
+			/*
+			 * Skip ne->exi_root which gets special
+			 * create/destroy handling.
+			 */
+			if (exi != ne->exi_root &&
+			    unexport(ne, exi) != 0)
+				errors++;
+			exi_rele(exi);
+
+			rw_enter(&ne->exported_lock, RW_READER);
+			exi = nexi;
+		}
+	}
+	if (errors > 0) {
+		cmn_err(CE_NOTE, "NFS: failed un-exports in zone %d",
+		    (int)ng->nfs_zoneid);
+	}
+
+	rw_exit(&ne->exported_lock);
+}
+
+void
+nfs_export_zone_fini(nfs_globals_t *ng)
+{
 	int i;
+	nfs_export_t *ne = ng->nfs_export;
+	struct exportinfo *exi;
 
+	ng->nfs_export = NULL;
+
+	rw_enter(&ne->exported_lock, RW_WRITER);
+
+	mutex_enter(&nfs_exi_id_lock);
+	avl_remove(&exi_id_tree, ne->exi_root);
+	mutex_exit(&nfs_exi_id_lock);
+
+	export_unlink(ne, ne->exi_root);
+
+	rw_exit(&ne->exported_lock);
+
+	/* Deallocate the place holder for the public file handle */
+	srv_secinfo_list_free(ne->exi_root->exi_export.ex_secinfo,
+	    ne->exi_root->exi_export.ex_seccnt);
+	mutex_destroy(&ne->exi_root->exi_lock);
+
+	rw_destroy(&ne->exi_root->exi_cache_lock);
+	for (i = 0; i < AUTH_TABLESIZE; i++) {
+		avl_destroy(ne->exi_root->exi_cache[i]);
+		kmem_free(ne->exi_root->exi_cache[i], sizeof (avl_tree_t));
+	}
+
+	kmem_free(ne->exi_root->exi_export.ex_path,
+	    ne->exi_root->exi_export.ex_pathlen + 1);
+	kmem_free(ne->exi_root, sizeof (*ne->exi_root));
+
 	/*
-	 * Deallocate the place holder for the public file handle.
+	 * The shutdown hook should have left the exi_id_tree
+	 * with nothing belonging to this zone.
 	 */
-	srv_secinfo_list_free(exi_root->exi_export.ex_secinfo,
-	    exi_root->exi_export.ex_seccnt);
-	mutex_destroy(&exi_root->exi_lock);
-	rw_destroy(&exi_root->exi_cache_lock);
-	for (i = 0; i < AUTH_TABLESIZE; i++) {
-		avl_destroy(exi_root->exi_cache[i]);
-		kmem_free(exi_root->exi_cache[i], sizeof (avl_tree_t));
+	mutex_enter(&nfs_exi_id_lock);
+	i = 0;
+	exi = avl_first(&exi_id_tree);
+	while (exi != NULL) {
+		if (exi->exi_zoneid == ng->nfs_zoneid)
+			i++;
+		exi = AVL_NEXT(&exi_id_tree, exi);
 	}
-	kmem_free(exi_root, sizeof (*exi_root));
+	mutex_exit(&nfs_exi_id_lock);
+	if (i > 0) {
+		cmn_err(CE_NOTE,
+		    "NFS: zone %d has %d export IDs left after shutdown",
+		    (int)ng->nfs_zoneid, i);
+	}
+	rw_destroy(&ne->exported_lock);
+	kmem_free(ne, sizeof (*ne));
+}
 
-	rw_destroy(&exported_lock);
+/*
+ * Initialization routine for export routines.
+ * Should only be called once.
+ */
+void
+nfs_exportinit(void)
+{
+	mutex_init(&nfs_exi_id_lock, NULL, MUTEX_DEFAULT, NULL);
+
+	/* exi_id handling initialization */
+	exi_id_next = 0;
+	exi_id_overflow = FALSE;
+	avl_create(&exi_id_tree, exi_id_compar, sizeof (struct exportinfo),
+	    offsetof(struct exportinfo, exi_id_link));
+
+	nfslog_init();
 }
 
 /*
+ * Finalization routine for export routines.
+ */
+void
+nfs_exportfini(void)
+{
+	avl_destroy(&exi_id_tree);
+	mutex_destroy(&nfs_exi_id_lock);
+}
+
+/*
  *  Check if 2 gss mechanism identifiers are the same.
  *
  *  return FALSE if not the same.
@@ -922,6 +1138,7 @@
 	int i, j;
 	rpc_gss_rawcred_t *raw_cred;
 	struct exportinfo *exi;
+	nfs_export_t *ne = nfs_get_export();
 
 	/*
 	 * We don't deal with delegated credentials.
@@ -932,9 +1149,10 @@
 	raw_cred = lock->raw_cred;
 	*cookie = NULL;
 
-	rw_enter(&exported_lock, RW_READER);
+	rw_enter(&ne->exported_lock, RW_READER);
+
 	for (i = 0; i < EXPTABLESIZE; i++) {
-		exi = exptable[i];
+		exi = ne->exptable[i];
 		while (exi) {
 			if (exi->exi_export.ex_seccnt > 0) {
 				struct secinfo *secp;
@@ -974,7 +1192,7 @@
 		}
 	}
 done:
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 
 	/*
 	 * If no nfs pseudo number mapping can be found in the export
@@ -1041,6 +1259,7 @@
 	int oldcnt;
 	int i;
 	struct pathname lookpn;
+	nfs_export_t *ne = nfs_get_export();
 
 	STRUCT_SET_HANDLE(uap, model, args);
 
@@ -1049,11 +1268,11 @@
 		return (error);
 
 	/* Walk the export list looking for that pathname */
-	rw_enter(&exported_lock, RW_READER);
+	rw_enter(&ne->exported_lock, RW_READER);
 	DTRACE_PROBE(nfss__i__exported_lock1_start);
-	for (ex1 = exptable_path_hash[pkp_tab_hash(lookpn.pn_path,
+	for (ex1 = ne->exptable_path_hash[pkp_tab_hash(lookpn.pn_path,
 	    strlen(lookpn.pn_path))]; ex1; ex1 = ex1->path_hash.next) {
-		if (ex1 != exi_root && 0 ==
+		if (ex1 != ne->exi_root && 0 ==
 		    strcmp(ex1->exi_export.ex_path, lookpn.pn_path)) {
 			exi_hold(ex1);
 			break;
@@ -1060,7 +1279,7 @@
 		}
 	}
 	DTRACE_PROBE(nfss__i__exported_lock1_stop);
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 
 	/* Is this an unshare? */
 	if (STRUCT_FGETP(uap, uex) == NULL) {
@@ -1067,7 +1286,7 @@
 		pn_free(&lookpn);
 		if (ex1 == NULL)
 			return (EINVAL);
-		error = unexport(ex1);
+		error = unexport(ne, ex1);
 		exi_rele(ex1);
 		return (error);
 	}
@@ -1163,15 +1382,15 @@
 	 * Do not allow re-sharing a shared vnode under a different path
 	 * PSEUDO export has ex_path fabricated, e.g. "/tmp (pseudo)", skip it.
 	 */
-	rw_enter(&exported_lock, RW_READER);
+	rw_enter(&ne->exported_lock, RW_READER);
 	DTRACE_PROBE(nfss__i__exported_lock2_start);
-	for (ex2 = exptable[exptablehash(&fsid, &fid)]; ex2;
+	for (ex2 = ne->exptable[exptablehash(&fsid, &fid)]; ex2;
 	    ex2 = ex2->fid_hash.next) {
-		if (ex2 != exi_root && !PSEUDO(ex2) &&
+		if (ex2 != ne->exi_root && !PSEUDO(ex2) &&
 		    VN_CMP(ex2->exi_vp, vp) &&
 		    strcmp(ex2->exi_export.ex_path, lookpn.pn_path) != 0) {
 			DTRACE_PROBE(nfss__i__exported_lock2_stop);
-			rw_exit(&exported_lock);
+			rw_exit(&ne->exported_lock);
 			VN_RELE(vp);
 			if (dvp != NULL)
 				VN_RELE(dvp);
@@ -1180,7 +1399,7 @@
 		}
 	}
 	DTRACE_PROBE(nfss__i__exported_lock2_stop);
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 	pn_free(&lookpn);
 
 	exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
@@ -1188,6 +1407,8 @@
 	exi->exi_fid = fid;
 	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);
@@ -1461,10 +1682,10 @@
 	/*
 	 * Insert the new entry at the front of the export list
 	 */
-	rw_enter(&exported_lock, RW_WRITER);
+	rw_enter(&ne->exported_lock, RW_WRITER);
 	DTRACE_PROBE(nfss__i__exported_lock3_start);
 
-	export_link(exi);
+	export_link(ne, exi);
 
 	/*
 	 * Check the rest of the list for an old entry for the fs.
@@ -1472,8 +1693,11 @@
 	 * only reference and then free it.
 	 */
 	for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
-		if (ex != exi_root && VN_CMP(ex->exi_vp, vp)) {
-			export_unlink(ex);
+		if (ex != ne->exi_root && VN_CMP(ex->exi_vp, vp)) {
+			mutex_enter(&nfs_exi_id_lock);
+			avl_remove(&exi_id_tree, ex);
+			mutex_exit(&nfs_exi_id_lock);
+			export_unlink(ne, ex);
 			break;
 		}
 	}
@@ -1482,8 +1706,8 @@
 	 * If the public filehandle is pointing at the
 	 * old entry, then point it back at the root.
 	 */
-	if (ex != NULL && ex == exi_public)
-		exi_public = exi_root;
+	if (ex != NULL && ex == ne->exi_public)
+		ne->exi_public = ne->exi_root;
 
 	/*
 	 * If the public flag is on, make the global exi_public
@@ -1491,7 +1715,7 @@
 	 * we can distinguish it from the place holder export.
 	 */
 	if (kex->ex_flags & EX_PUBLIC) {
-		exi_public = exi;
+		ne->exi_public = exi;
 		kex->ex_flags &= ~EX_PUBLIC;
 	}
 
@@ -1523,7 +1747,7 @@
 		exi->exi_tree->tree_exi = exi;
 
 		/* Update the change timestamp */
-		tree_update_change(exi->exi_tree, NULL);
+		tree_update_change(ne, exi->exi_tree, NULL);
 	}
 
 	/*
@@ -1533,7 +1757,7 @@
 	 */
 	newcnt = build_seclist_nodups(&exi->exi_export, newsec, FALSE);
 
-	srv_secinfo_treeclimb(exi, newsec, newcnt, TRUE);
+	srv_secinfo_treeclimb(ne, exi, newsec, newcnt, TRUE);
 
 	/*
 	 * If re-sharing an old export entry, update the secinfo data
@@ -1558,7 +1782,7 @@
 			 * Remove old flavor refs last.
 			 */
 			srv_secinfo_exp2exp(&exi->exi_export, oldsec, oldcnt);
-			srv_secinfo_treeclimb(ex, oldsec, oldcnt, FALSE);
+			srv_secinfo_treeclimb(ne, ex, oldsec, oldcnt, FALSE);
 		}
 	}
 
@@ -1571,10 +1795,24 @@
 		ex->exi_visible = NULL;
 	}
 
+	/*
+	 * Initialize exi_id and exi_kstats
+	 */
+	if (ex != NULL) {
+		exi->exi_id = ex->exi_id;
+	} else {
+		mutex_enter(&nfs_exi_id_lock);
+		exi->exi_id = exi_id_get_next();
+		mutex_exit(&nfs_exi_id_lock);
+	}
+	mutex_enter(&nfs_exi_id_lock);
+	avl_add(&exi_id_tree, exi);
+	mutex_exit(&nfs_exi_id_lock);
+
 	DTRACE_PROBE(nfss__i__exported_lock3_stop);
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 
-	if (exi_public == exi || kex->ex_flags & EX_LOG) {
+	if (ne->exi_public == exi || kex->ex_flags & EX_LOG) {
 		/*
 		 * Log share operation to this buffer only.
 		 */
@@ -1588,9 +1826,9 @@
 
 out7:
 	/* Unlink the new export in exptable. */
-	export_unlink(exi);
+	export_unlink(ne, exi);
 	DTRACE_PROBE(nfss__i__exported_lock3_stop);
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 out6:
 	if (kex->ex_flags & EX_INDEX)
 		kmem_free(kex->ex_index, strlen(kex->ex_index) + 1);
@@ -1634,12 +1872,14 @@
  * Remove the exportinfo from the export list
  */
 void
-export_unlink(struct exportinfo *exi)
+export_unlink(nfs_export_t *ne, struct exportinfo *exi)
 {
-	ASSERT(RW_WRITE_HELD(&exported_lock));
+	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 
 	exp_hash_unlink(exi, fid_hash);
 	exp_hash_unlink(exi, path_hash);
+	ASSERT3P(exi->exi_ne, ==, ne);
+	exi->exi_ne = NULL;
 }
 
 /*
@@ -1646,20 +1886,23 @@
  * Unexport an exported filesystem
  */
 static int
-unexport(struct exportinfo *exi)
+unexport(nfs_export_t *ne, struct exportinfo *exi)
 {
 	struct secinfo cursec[MAX_FLAVORS];
 	int curcnt;
 
-	rw_enter(&exported_lock, RW_WRITER);
+	rw_enter(&ne->exported_lock, RW_WRITER);
 
 	/* Check if exi is still linked in the export table */
 	if (!EXP_LINKED(exi) || PSEUDO(exi)) {
-		rw_exit(&exported_lock);
+		rw_exit(&ne->exported_lock);
 		return (EINVAL);
 	}
 
-	export_unlink(exi);
+	mutex_enter(&nfs_exi_id_lock);
+	avl_remove(&exi_id_tree, exi);
+	mutex_exit(&nfs_exi_id_lock);
+	export_unlink(ne, exi);
 
 	/*
 	 * Remove security flavors before treeclimb_unexport() is called
@@ -1666,9 +1909,8 @@
 	 * because srv_secinfo_treeclimb needs the namespace tree
 	 */
 	curcnt = build_seclist_nodups(&exi->exi_export, cursec, TRUE);
+	srv_secinfo_treeclimb(ne, exi, cursec, curcnt, FALSE);
 
-	srv_secinfo_treeclimb(exi, cursec, curcnt, FALSE);
-
 	/*
 	 * If there's a visible list, then need to leave
 	 * a pseudo export here to retain the visible list
@@ -1677,7 +1919,7 @@
 	if (exi->exi_visible != NULL) {
 		struct exportinfo *newexi;
 
-		newexi = pseudo_exportfs(exi->exi_vp, &exi->exi_fid,
+		newexi = pseudo_exportfs(ne, exi->exi_vp, &exi->exi_fid,
 		    exi->exi_visible, &exi->exi_export);
 		exi->exi_visible = NULL;
 
@@ -1686,12 +1928,12 @@
 		newexi->exi_tree->tree_exi = newexi;
 
 		/* Update the change timestamp */
-		tree_update_change(exi->exi_tree, NULL);
+		tree_update_change(ne, exi->exi_tree, NULL);
 	} else {
-		treeclimb_unexport(exi);
+		treeclimb_unexport(ne, exi);
 	}
 
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 
 	/*
 	 * Need to call into the NFSv4 server and release all data
@@ -1699,7 +1941,7 @@
 	 * the v4 server may be holding file locks or vnodes under
 	 * this export.
 	 */
-	rfs4_clean_state_exi(exi);
+	rfs4_clean_state_exi(ne, exi);
 
 	/*
 	 * Notify the lock manager that the filesystem is being
@@ -1711,15 +1953,19 @@
 	 * If this was a public export, restore
 	 * the public filehandle to the root.
 	 */
-	if (exi == exi_public) {
-		exi_public = exi_root;
 
-		nfslog_share_record(exi_public, CRED());
+	/*
+	 * XXX KEBE ASKS --> Should CRED() instead be
+	 * exi->exi_zone->zone_kcred?
+	 */
+	if (exi == ne->exi_public) {
+		ne->exi_public = ne->exi_root;
+
+		nfslog_share_record(ne->exi_public, CRED());
 	}
 
-	if (exi->exi_export.ex_flags & EX_LOG) {
+	if (exi->exi_export.ex_flags & EX_LOG)
 		nfslog_unshare_record(exi, CRED());
-	}
 
 	exi_rele(exi);
 	return (0);
@@ -1946,7 +2192,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;
 		}
@@ -2446,9 +2693,10 @@
 checkexport(fsid_t *fsid, fid_t *fid)
 {
 	struct exportinfo *exi;
+	nfs_export_t *ne = nfs_get_export();
 
-	rw_enter(&exported_lock, RW_READER);
-	for (exi = exptable[exptablehash(fsid, fid)];
+	rw_enter(&ne->exported_lock, RW_READER);
+	for (exi = ne->exptable[exptablehash(fsid, fid)];
 	    exi != NULL;
 	    exi = exi->fid_hash.next) {
 		if (exportmatch(exi, fsid, fid)) {
@@ -2459,15 +2707,15 @@
 			 * handle.
 			 */
 			if (exi->exi_export.ex_flags & EX_PUBLIC) {
-				exi = exi_public;
+				exi = ne->exi_public;
 			}
 
 			exi_hold(exi);
-			rw_exit(&exported_lock);
+			rw_exit(&ne->exported_lock);
 			return (exi);
 		}
 	}
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 	return (NULL);
 }
 
@@ -2483,10 +2731,11 @@
 checkexport4(fsid_t *fsid, fid_t *fid, vnode_t *vp)
 {
 	struct exportinfo *exi;
+	nfs_export_t *ne = nfs_get_export();
 
-	ASSERT(RW_LOCK_HELD(&exported_lock));
+	ASSERT(RW_LOCK_HELD(&ne->exported_lock));
 
-	for (exi = exptable[exptablehash(fsid, fid)];
+	for (exi = ne->exptable[exptablehash(fsid, fid)];
 	    exi != NULL;
 	    exi = exi->fid_hash.next) {
 		if (exportmatch(exi, fsid, fid)) {
@@ -2497,7 +2746,7 @@
 			 * handle.
 			 */
 			if (exi->exi_export.ex_flags & EX_PUBLIC) {
-				exi = exi_public;
+				exi = ne->exi_public;
 			}
 
 			/*
