--- old/usr/src/uts/common/fs/nfs/nfs3_srv.c	Mon Aug  5 17:25:57 2019
+++ new/usr/src/uts/common/fs/nfs/nfs3_srv.c	Mon Aug  5 17:25:57 2019
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.
  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  */
@@ -28,6 +28,7 @@
 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
 /* All Rights Reserved */
 
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -68,13 +69,18 @@
 #include <inet/ip6.h>
 
 /*
+ * Zone global variables of NFSv3 server
+ */
+typedef struct nfs3_srv {
+	writeverf3	write3verf;
+} nfs3_srv_t;
+
+/*
  * These are the interface routines for the server side of the
  * Network File System.  See the NFS version 3 protocol specification
  * for a description of this interface.
  */
 
-static writeverf3 write3verf;
-
 static int	sattr3_to_vattr(sattr3 *, struct vattr *);
 static int	vattr_to_fattr3(struct vattr *, fattr3 *);
 static int	vattr_to_wcc_attr(struct vattr *, wcc_attr *);
@@ -85,6 +91,7 @@
 extern int nfs_loaned_buffers;
 
 u_longlong_t nfs3_srv_caller_id;
+static zone_key_t rfs3_zone_key;
 
 /* ARGSUSED */
 void
@@ -97,8 +104,9 @@
 
 	vp = nfs3_fhtovp(&args->object, exi);
 
-	DTRACE_NFSV3_4(op__getattr__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, GETATTR3args *, args);
+	DTRACE_NFSV3_5(op__getattr__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    GETATTR3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -119,8 +127,9 @@
 			goto out;
 		resp->status = NFS3_OK;
 
-		DTRACE_NFSV3_4(op__getattr__done, struct svc_req *, req,
-		    cred_t *, cr, vnode_t *, vp, GETATTR3res *, resp);
+		DTRACE_NFSV3_5(op__getattr__done, struct svc_req *, req,
+		    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+		    GETATTR3res *, resp);
 
 		VN_RELE(vp);
 
@@ -134,8 +143,9 @@
 	} else
 		resp->status = puterrno3(error);
 
-	DTRACE_NFSV3_4(op__getattr__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, GETATTR3res *, resp);
+	DTRACE_NFSV3_5(op__getattr__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    GETATTR3res *, resp);
 
 	if (vp != NULL)
 		VN_RELE(vp);
@@ -144,7 +154,6 @@
 void *
 rfs3_getattr_getfh(GETATTR3args *args)
 {
-
 	return (&args->object);
 }
 
@@ -168,8 +177,9 @@
 
 	vp = nfs3_fhtovp(&args->object, exi);
 
-	DTRACE_NFSV3_4(op__setattr__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, SETATTR3args *, args);
+	DTRACE_NFSV3_5(op__setattr__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    SETATTR3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -330,8 +340,9 @@
 	resp->status = NFS3_OK;
 	vattr_to_wcc_data(bvap, avap, &resp->resok.obj_wcc);
 
-	DTRACE_NFSV3_4(op__setattr__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, SETATTR3res *, resp);
+	DTRACE_NFSV3_5(op__setattr__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    SETATTR3res *, resp);
 
 	VN_RELE(vp);
 
@@ -344,8 +355,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__setattr__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, SETATTR3res *, resp);
+	DTRACE_NFSV3_5(op__setattr__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    SETATTR3res *, resp);
 
 	if (vp != NULL) {
 		if (in_crit)
@@ -358,7 +370,6 @@
 void *
 rfs3_setattr_getfh(SETATTR3args *args)
 {
-
 	return (&args->object);
 }
 
@@ -382,21 +393,26 @@
 
 	dvap = NULL;
 
+	if (exi != NULL)
+		exi_hold(exi);
+
 	/*
 	 * Allow lookups from the root - the default
 	 * location of the public filehandle.
 	 */
 	if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
-		dvp = rootdir;
+		dvp = ZONE_ROOTVP();
 		VN_HOLD(dvp);
 
-		DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
-		    cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
+		DTRACE_NFSV3_5(op__lookup__start, struct svc_req *, req,
+		    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+		    LOOKUP3args *, args);
 	} else {
 		dvp = nfs3_fhtovp(&args->what.dir, exi);
 
-		DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
-		    cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
+		DTRACE_NFSV3_5(op__lookup__start, struct svc_req *, req,
+		    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+		    LOOKUP3args *, args);
 
 		if (dvp == NULL) {
 			error = ESTALE;
@@ -420,8 +436,19 @@
 	fhp = &args->what.dir;
 	if (strcmp(args->what.name, "..") == 0 &&
 	    EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {
-		resp->status = NFS3ERR_NOENT;
-		goto out1;
+		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) {
+				resp->status = NFS3ERR_ACCES;
+				goto out1;
+			}
+		} else {
+			resp->status = NFS3ERR_NOENT;
+			goto out1;
+		}
 	}
 
 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
@@ -439,10 +466,12 @@
 	 */
 	if (PUBLIC_FH3(&args->what.dir)) {
 		publicfh_flag = TRUE;
+
+		exi_rele(&exi);
+
 		error = rfs_publicfh_mclookup(name, dvp, cr, &vp,
 		    &exi, &sec);
-		if (error && exi != NULL)
-			exi_rele(exi); /* See comment below Re: publicfh_flag */
+
 		/*
 		 * Since WebNFS may bypass MOUNT, we need to ensure this
 		 * request didn't come from an unlabeled admin_low client.
@@ -464,8 +493,6 @@
 			if (tp == NULL || tp->tpc_tp.tp_doi !=
 			    l_admin_low->tsl_doi || tp->tpc_tp.host_type !=
 			    SUN_CIPSO) {
-				if (exi != NULL)
-					exi_rele(exi);
 				VN_RELE(vp);
 				error = EACCES;
 			}
@@ -480,6 +507,12 @@
 	if (name != args->what.name)
 		kmem_free(name, MAXPATHLEN + 1);
 
+	if (error == 0 && vn_ismntpt(vp)) {
+		error = rfs_cross_mnt(&vp, &exi);
+		if (error)
+			VN_RELE(vp);
+	}
+
 	if (is_system_labeled() && error == 0) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -490,8 +523,6 @@
 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
 			if (!do_rfs_label_check(clabel, dvp,
 			    DOMINANCE_CHECK, exi)) {
-				if (publicfh_flag && exi != NULL)
-					exi_rele(exi);
 				VN_RELE(vp);
 				error = EACCES;
 			}
@@ -512,15 +543,6 @@
 			auth_weak = TRUE;
 	}
 
-	/*
-	 * 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 that the original exportinfo pointed to by exi
-	 * will be released by the caller, common_dispatch.
-	 */
-	if (publicfh_flag)
-		exi_rele(exi);
-
 	if (error) {
 		VN_RELE(vp);
 		goto out;
@@ -529,6 +551,7 @@
 	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;
@@ -543,8 +566,9 @@
 	if (auth_weak)
 		resp->status = (enum nfsstat3)WNFSERR_CLNT_FLAVOR;
 
-	DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
+	DTRACE_NFSV3_5(op__lookup__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    LOOKUP3res *, resp);
 	VN_RELE(dvp);
 
 	return;
@@ -556,9 +580,13 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
+	if (exi != NULL)
+		exi_rele(&exi);
 
+	DTRACE_NFSV3_5(op__lookup__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    LOOKUP3res *, resp);
+
 	if (dvp != NULL)
 		VN_RELE(dvp);
 	vattr_to_post_op_attr(dvap, &resp->resfail.dir_attributes);
@@ -568,11 +596,9 @@
 void *
 rfs3_lookup_getfh(LOOKUP3args *args)
 {
-
 	return (&args->what.dir);
 }
 
-/* ARGSUSED */
 void
 rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi,
     struct svc_req *req, cred_t *cr, bool_t ro)
@@ -590,8 +616,9 @@
 
 	vp = nfs3_fhtovp(&args->object, exi);
 
-	DTRACE_NFSV3_4(op__access__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, ACCESS3args *, args);
+	DTRACE_NFSV3_5(op__access__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    ACCESS3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -701,8 +728,9 @@
 	resp->status = NFS3_OK;
 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
 
-	DTRACE_NFSV3_4(op__access__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, ACCESS3res *, resp);
+	DTRACE_NFSV3_5(op__access__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    ACCESS3res *, resp);
 
 	VN_RELE(vp);
 
@@ -714,8 +742,9 @@
 		resp->status = NFS3ERR_JUKEBOX;
 	} else
 		resp->status = puterrno3(error);
-	DTRACE_NFSV3_4(op__access__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, ACCESS3res *, resp);
+	DTRACE_NFSV3_5(op__access__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    ACCESS3res *, resp);
 	if (vp != NULL)
 		VN_RELE(vp);
 	vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
@@ -724,7 +753,6 @@
 void *
 rfs3_access_getfh(ACCESS3args *args)
 {
-
 	return (&args->object);
 }
 
@@ -748,8 +776,9 @@
 
 	vp = nfs3_fhtovp(&args->symlink, exi);
 
-	DTRACE_NFSV3_4(op__readlink__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READLINK3args *, args);
+	DTRACE_NFSV3_5(op__readlink__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READLINK3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -869,8 +898,9 @@
 	vattr_to_post_op_attr(vap, &resp->resok.symlink_attributes);
 	resp->resok.data = name;
 
-	DTRACE_NFSV3_4(op__readlink__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READLINK3res *, resp);
+	DTRACE_NFSV3_5(op__readlink__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READLINK3res *, resp);
 	VN_RELE(vp);
 
 	if (name != data)
@@ -885,8 +915,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__readlink__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READLINK3res *, resp);
+	DTRACE_NFSV3_5(op__readlink__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READLINK3res *, resp);
 	if (vp != NULL)
 		VN_RELE(vp);
 	vattr_to_post_op_attr(vap, &resp->resfail.symlink_attributes);
@@ -895,7 +926,6 @@
 void *
 rfs3_readlink_getfh(READLINK3args *args)
 {
-
 	return (&args->symlink);
 }
 
@@ -902,7 +932,6 @@
 void
 rfs3_readlink_free(READLINK3res *resp)
 {
-
 	if (resp->status == NFS3_OK)
 		kmem_free(resp->resok.data, MAXPATHLEN + 1);
 }
@@ -936,9 +965,11 @@
 
 	vp = nfs3_fhtovp(&args->file, exi);
 
-	DTRACE_NFSV3_4(op__read__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READ3args *, args);
+	DTRACE_NFSV3_5(op__read__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READ3args *, args);
 
+
 	if (vp == NULL) {
 		error = ESTALE;
 		goto out;
@@ -1192,8 +1223,9 @@
 	}
 
 done:
-	DTRACE_NFSV3_4(op__read__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READ3res *, resp);
+	DTRACE_NFSV3_5(op__read__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READ3res *, resp);
 
 	VN_RELE(vp);
 
@@ -1209,8 +1241,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__read__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READ3res *, resp);
+	DTRACE_NFSV3_5(op__read__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READ3res *, resp);
 
 	if (vp != NULL) {
 		if (need_rwunlock)
@@ -1240,7 +1273,6 @@
 void *
 rfs3_read_getfh(READ3args *args)
 {
-
 	return (&args->file);
 }
 
@@ -1255,6 +1287,7 @@
 rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi,
     struct svc_req *req, cred_t *cr, bool_t ro)
 {
+	nfs3_srv_t *ns;
 	int error;
 	vnode_t *vp;
 	struct vattr *bvap = NULL;
@@ -1275,8 +1308,9 @@
 
 	vp = nfs3_fhtovp(&args->file, exi);
 
-	DTRACE_NFSV3_4(op__write__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, WRITE3args *, args);
+	DTRACE_NFSV3_5(op__write__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    WRITE3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -1283,6 +1317,7 @@
 		goto err;
 	}
 
+	ns = zone_getspecific(rfs3_zone_key, curzone);
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -1370,7 +1405,7 @@
 		vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
 		resp->resok.count = 0;
 		resp->resok.committed = args->stable;
-		resp->resok.verf = write3verf;
+		resp->resok.verf = ns->write3verf;
 		goto out;
 	}
 
@@ -1472,7 +1507,7 @@
 	vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
 	resp->resok.count = args->count - uio.uio_resid;
 	resp->resok.committed = args->stable;
-	resp->resok.verf = write3verf;
+	resp->resok.verf = ns->write3verf;
 	goto out;
 
 err:
@@ -1484,8 +1519,9 @@
 err1:
 	vattr_to_wcc_data(bvap, avap, &resp->resfail.file_wcc);
 out:
-	DTRACE_NFSV3_4(op__write__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, WRITE3res *, resp);
+	DTRACE_NFSV3_5(op__write__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    WRITE3res *, resp);
 
 	if (vp != NULL) {
 		if (rwlock_ret != -1)
@@ -1499,7 +1535,6 @@
 void *
 rfs3_write_getfh(WRITE3args *args)
 {
-
 	return (&args->file);
 }
 
@@ -1530,8 +1565,9 @@
 
 	dvp = nfs3_fhtovp(&args->where.dir, exi);
 
-	DTRACE_NFSV3_4(op__create__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, CREATE3args *, args);
+	DTRACE_NFSV3_5(op__create__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    CREATE3args *, args);
 
 	if (dvp == NULL) {
 		error = ESTALE;
@@ -1557,6 +1593,11 @@
 		goto out1;
 	}
 
+	if (protect_zfs_mntpt(dvp) != 0) {
+		resp->status = NFS3ERR_ACCES;
+		goto out1;
+	}
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -1830,8 +1871,9 @@
 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
 	vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
 
-	DTRACE_NFSV3_4(op__create__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, CREATE3res *, resp);
+	DTRACE_NFSV3_5(op__create__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    CREATE3res *, resp);
 
 	VN_RELE(dvp);
 	return;
@@ -1843,8 +1885,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__create__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, CREATE3res *, resp);
+	DTRACE_NFSV3_5(op__create__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    CREATE3res *, resp);
 
 	if (name != NULL && name != args->where.name)
 		kmem_free(name, MAXPATHLEN + 1);
@@ -1862,7 +1905,6 @@
 void *
 rfs3_create_getfh(CREATE3args *args)
 {
-
 	return (&args->where.dir);
 }
 
@@ -1887,8 +1929,9 @@
 
 	dvp = nfs3_fhtovp(&args->where.dir, exi);
 
-	DTRACE_NFSV3_4(op__mkdir__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, MKDIR3args *, args);
+	DTRACE_NFSV3_5(op__mkdir__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    MKDIR3args *, args);
 
 	if (dvp == NULL) {
 		error = ESTALE;
@@ -1914,6 +1957,11 @@
 		goto out1;
 	}
 
+	if (protect_zfs_mntpt(dvp) != 0) {
+		resp->status = NFS3ERR_ACCES;
+		goto out1;
+	}
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -1987,8 +2035,9 @@
 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
 	vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
 
-	DTRACE_NFSV3_4(op__mkdir__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, MKDIR3res *, resp);
+	DTRACE_NFSV3_5(op__mkdir__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    MKDIR3res *, resp);
 	VN_RELE(dvp);
 
 	return;
@@ -2000,8 +2049,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__mkdir__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, MKDIR3res *, resp);
+	DTRACE_NFSV3_5(op__mkdir__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    MKDIR3res *, resp);
 	if (dvp != NULL)
 		VN_RELE(dvp);
 	vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
@@ -2010,7 +2060,6 @@
 void *
 rfs3_mkdir_getfh(MKDIR3args *args)
 {
-
 	return (&args->where.dir);
 }
 
@@ -2036,8 +2085,9 @@
 
 	dvp = nfs3_fhtovp(&args->where.dir, exi);
 
-	DTRACE_NFSV3_4(op__symlink__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, SYMLINK3args *, args);
+	DTRACE_NFSV3_5(op__symlink__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    SYMLINK3args *, args);
 
 	if (dvp == NULL) {
 		error = ESTALE;
@@ -2063,6 +2113,11 @@
 		goto err1;
 	}
 
+	if (protect_zfs_mntpt(dvp) != 0) {
+		resp->status = NFS3ERR_ACCES;
+		goto err1;
+	}
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -2174,8 +2229,9 @@
 	if (symdata != NULL && symdata != args->symlink.symlink_data)
 		kmem_free(symdata, MAXPATHLEN + 1);
 
-	DTRACE_NFSV3_4(op__symlink__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, SYMLINK3res *, resp);
+	DTRACE_NFSV3_5(op__symlink__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    SYMLINK3res *, resp);
 
 	if (dvp != NULL)
 		VN_RELE(dvp);
@@ -2184,7 +2240,6 @@
 void *
 rfs3_symlink_getfh(SYMLINK3args *args)
 {
-
 	return (&args->where.dir);
 }
 
@@ -2212,8 +2267,9 @@
 
 	dvp = nfs3_fhtovp(&args->where.dir, exi);
 
-	DTRACE_NFSV3_4(op__mknod__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, MKNOD3args *, args);
+	DTRACE_NFSV3_5(op__mknod__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    MKNOD3args *, args);
 
 	if (dvp == NULL) {
 		error = ESTALE;
@@ -2239,6 +2295,11 @@
 		goto out1;
 	}
 
+	if (protect_zfs_mntpt(dvp) != 0) {
+		resp->status = NFS3ERR_ACCES;
+		goto out1;
+	}
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -2359,8 +2420,9 @@
 
 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
 	vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
-	DTRACE_NFSV3_4(op__mknod__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, MKNOD3res *, resp);
+	DTRACE_NFSV3_5(op__mknod__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    MKNOD3res *, resp);
 	VN_RELE(dvp);
 	return;
 
@@ -2371,8 +2433,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__mknod__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, dvp, MKNOD3res *, resp);
+	DTRACE_NFSV3_5(op__mknod__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
+	    MKNOD3res *, resp);
 	if (dvp != NULL)
 		VN_RELE(dvp);
 	vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
@@ -2381,7 +2444,6 @@
 void *
 rfs3_mknod_getfh(MKNOD3args *args)
 {
-
 	return (&args->where.dir);
 }
 
@@ -2404,8 +2466,9 @@
 
 	vp = nfs3_fhtovp(&args->object.dir, exi);
 
-	DTRACE_NFSV3_4(op__remove__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, REMOVE3args *, args);
+	DTRACE_NFSV3_5(op__remove__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    REMOVE3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -2513,8 +2576,9 @@
 err1:
 	vattr_to_wcc_data(bvap, avap, &resp->resfail.dir_wcc);
 out:
-	DTRACE_NFSV3_4(op__remove__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, REMOVE3res *, resp);
+	DTRACE_NFSV3_5(op__remove__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    REMOVE3res *, resp);
 
 	if (name != NULL && name != args->object.name)
 		kmem_free(name, MAXPATHLEN + 1);
@@ -2526,7 +2590,6 @@
 void *
 rfs3_remove_getfh(REMOVE3args *args)
 {
-
 	return (&args->object.dir);
 }
 
@@ -2548,8 +2611,9 @@
 
 	vp = nfs3_fhtovp(&args->object.dir, exi);
 
-	DTRACE_NFSV3_4(op__rmdir__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, RMDIR3args *, args);
+	DTRACE_NFSV3_5(op__rmdir__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    RMDIR3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -2605,7 +2669,7 @@
 		goto err1;
 	}
 
-	error = VOP_RMDIR(vp, name, rootdir, cr, NULL, 0);
+	error = VOP_RMDIR(vp, name, ZONE_ROOTVP(), cr, NULL, 0);
 
 	if (name != args->object.name)
 		kmem_free(name, MAXPATHLEN + 1);
@@ -2643,8 +2707,9 @@
 err1:
 	vattr_to_wcc_data(bvap, avap, &resp->resfail.dir_wcc);
 out:
-	DTRACE_NFSV3_4(op__rmdir__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, RMDIR3res *, resp);
+	DTRACE_NFSV3_5(op__rmdir__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    RMDIR3res *, resp);
 	if (vp != NULL)
 		VN_RELE(vp);
 
@@ -2653,7 +2718,6 @@
 void *
 rfs3_rmdir_getfh(RMDIR3args *args)
 {
-
 	return (&args->object.dir);
 }
 
@@ -2689,8 +2753,9 @@
 
 	fvp = nfs3_fhtovp(&args->from.dir, exi);
 
-	DTRACE_NFSV3_4(op__rename__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, fvp, RENAME3args *, args);
+	DTRACE_NFSV3_5(op__rename__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, fvp, struct exportinfo *, exi,
+	    RENAME3args *, args);
 
 	if (fvp == NULL) {
 		error = ESTALE;
@@ -2722,7 +2787,7 @@
 		resp->status = NFS3ERR_ACCES;
 		goto err1;
 	}
-	exi_rele(to_exi);
+	exi_rele(&to_exi);
 
 	if (to_exi != exi) {
 		resp->status = NFS3ERR_XDEV;
@@ -2760,6 +2825,11 @@
 		goto err1;
 	}
 
+	if (protect_zfs_mntpt(tvp) != 0) {
+		resp->status = NFS3ERR_ACCES;
+		goto err1;
+	}
+
 	if (is_system_labeled()) {
 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
 			if (!do_rfs_label_check(clabel, tvp, EQUALITY_CHECK,
@@ -2807,10 +2877,10 @@
 	}
 
 	/*
-	 * Check for renaming over a delegated file.  Check rfs4_deleg_policy
+	 * Check for renaming over a delegated file.  Check nfs4_deleg_policy
 	 * first to avoid VOP_LOOKUP if possible.
 	 */
-	if (rfs4_deleg_policy != SRV_NEVER_DELEGATE &&
+	if (nfs4_get_deleg_policy() != SRV_NEVER_DELEGATE &&
 	    VOP_LOOKUP(tvp, toname, &targvp, NULL, 0, NULL, cr,
 	    NULL, NULL, NULL) == 0) {
 
@@ -2874,8 +2944,9 @@
 	if (toname != NULL && toname != args->to.name)
 		kmem_free(toname, MAXPATHLEN + 1);
 
-	DTRACE_NFSV3_4(op__rename__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, fvp, RENAME3res *, resp);
+	DTRACE_NFSV3_5(op__rename__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, fvp, struct exportinfo *, exi,
+	    RENAME3res *, resp);
 	if (fvp != NULL)
 		VN_RELE(fvp);
 	if (tvp != NULL)
@@ -2885,7 +2956,6 @@
 void *
 rfs3_rename_getfh(RENAME3args *args)
 {
-
 	return (&args->from.dir);
 }
 
@@ -2915,8 +2985,9 @@
 
 	vp = nfs3_fhtovp(&args->file, exi);
 
-	DTRACE_NFSV3_4(op__link__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, LINK3args *, args);
+	DTRACE_NFSV3_5(op__link__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    LINK3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -2932,7 +3003,7 @@
 		resp->status = NFS3ERR_ACCES;
 		goto out1;
 	}
-	exi_rele(to_exi);
+	exi_rele(&to_exi);
 
 	if (to_exi != exi) {
 		resp->status = NFS3ERR_XDEV;
@@ -2984,6 +3055,11 @@
 		goto out1;
 	}
 
+	if (protect_zfs_mntpt(dvp) != 0) {
+		resp->status = NFS3ERR_ACCES;
+		goto out1;
+	}
+
 	if (is_system_labeled()) {
 		DTRACE_PROBE2(tx__rfs3__log__info__oplinkdir__clabel, char *,
 		    "got client label from request(1)", struct svc_req *, req);
@@ -3028,8 +3104,9 @@
 	vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
 	vattr_to_wcc_data(bvap, avap, &resp->resok.linkdir_wcc);
 
-	DTRACE_NFSV3_4(op__link__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, LINK3res *, resp);
+	DTRACE_NFSV3_5(op__link__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    LINK3res *, resp);
 
 	VN_RELE(vp);
 
@@ -3045,8 +3122,9 @@
 	if (name != NULL && name != args->link.name)
 		kmem_free(name, MAXPATHLEN + 1);
 
-	DTRACE_NFSV3_4(op__link__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, LINK3res *, resp);
+	DTRACE_NFSV3_5(op__link__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    LINK3res *, resp);
 
 	if (vp != NULL)
 		VN_RELE(vp);
@@ -3059,39 +3137,14 @@
 void *
 rfs3_link_getfh(LINK3args *args)
 {
-
 	return (&args->file);
 }
 
-/*
- * This macro defines the size of a response which contains attribute
- * information and one directory entry (whose length is specified by
- * the macro parameter).  If the incoming request is larger than this,
- * then we are guaranteed to be able to return at one directory entry
- * if one exists.  Therefore, we do not need to check for
- * NFS3ERR_TOOSMALL if the requested size is larger then this.  If it
- * is not, then we need to check to make sure that this error does not
- * need to be returned.
- *
- * NFS3_READDIR_MIN_COUNT is comprised of following :
- *
- * status - 1 * BYTES_PER_XDR_UNIT
- * attr. flag - 1 * BYTES_PER_XDR_UNIT
- * cookie verifier - 2 * BYTES_PER_XDR_UNIT
- * attributes  - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT
- * boolean - 1 * BYTES_PER_XDR_UNIT
- * file id - 2 * BYTES_PER_XDR_UNIT
- * directory name length - 1 * BYTES_PER_XDR_UNIT
- * cookie - 2 * BYTES_PER_XDR_UNIT
- * end of list - 1 * BYTES_PER_XDR_UNIT
- * end of file - 1 * BYTES_PER_XDR_UNIT
- * Name length of directory to the nearest byte
- */
+#ifdef nextdp
+#undef nextdp
+#endif
+#define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
 
-#define	NFS3_READDIR_MIN_COUNT(length)	\
-	((1 + 1 + 2 + NFS3_SIZEOF_FATTR3 + 1 + 2 + 1 + 2 + 1 + 1) * \
-		BYTES_PER_XDR_UNIT + roundup((length), BYTES_PER_XDR_UNIT))
-
 /* ARGSUSED */
 void
 rfs3_readdir(READDIR3args *args, READDIR3res *resp, struct exportinfo *exi,
@@ -3103,25 +3156,37 @@
 	struct vattr va;
 	struct iovec iov;
 	struct uio uio;
-	char *data;
 	int iseof;
-	int bufsize;
-	int namlen;
-	uint_t count;
-	struct sockaddr *ca;
 
-	vap = NULL;
+	count3 count = args->count;
+	count3 size;		/* size of the READDIR3resok structure */
 
+	size_t datasz;
+	char *data = NULL;
+	dirent64_t *dp;
+
+	struct sockaddr *ca;
+	entry3 **eptr;
+	entry3 *entry;
+
 	vp = nfs3_fhtovp(&args->dir, exi);
 
-	DTRACE_NFSV3_4(op__readdir__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READDIR3args *, args);
+	DTRACE_NFSV3_5(op__readdir__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READDIR3args *, args);
 
 	if (vp == NULL) {
-		error = ESTALE;
-		goto out;
+		resp->status = NFS3ERR_STALE;
+		vap = NULL;
+		goto out1;
 	}
 
+	if (vp->v_type != VDIR) {
+		resp->status = NFS3ERR_NOTDIR;
+		vap = NULL;
+		goto out1;
+	}
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -3133,6 +3198,7 @@
 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
 			    exi)) {
 				resp->status = NFS3ERR_ACCES;
+				vap = NULL;
 				goto out1;
 			}
 		}
@@ -3143,119 +3209,148 @@
 	va.va_mask = AT_ALL;
 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
 
-	if (vp->v_type != VDIR) {
-		resp->status = NFS3ERR_NOTDIR;
-		goto out1;
-	}
-
 	error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
 	if (error)
 		goto out;
 
 	/*
-	 * Now don't allow arbitrary count to alloc;
-	 * allow the maximum not to exceed rfs3_tsize()
+	 * Don't allow arbitrary counts for allocation
 	 */
-	if (args->count > rfs3_tsize(req))
-		args->count = rfs3_tsize(req);
+	if (count > rfs3_tsize(req))
+		count = rfs3_tsize(req);
 
 	/*
+	 * struct READDIR3resok:
+	 *   dir_attributes:	1 + NFS3_SIZEOF_FATTR3
+	 *   cookieverf:	2
+	 *   entries (bool):	1
+	 *   eof:		1
+	 */
+	size = (1 + NFS3_SIZEOF_FATTR3 + 2 + 1 + 1) * BYTES_PER_XDR_UNIT;
+
+	if (size > count) {
+		resp->status = NFS3ERR_TOOSMALL;
+		goto out1;
+	}
+
+	/*
+	 * This is simplification.  The dirent64_t size is not the same as the
+	 * size of XDR representation of entry3, but the sizes are similar so
+	 * we'll assume they are same.  This assumption should not cause any
+	 * harm.  In worst case we will need to issue VOP_READDIR() once more.
+	 */
+	datasz = count;
+
+	/*
 	 * Make sure that there is room to read at least one entry
 	 * if any are available.
 	 */
-	if (args->count < DIRENT64_RECLEN(MAXNAMELEN))
-		count = DIRENT64_RECLEN(MAXNAMELEN);
-	else
-		count = args->count;
+	if (datasz < DIRENT64_RECLEN(MAXNAMELEN))
+		datasz = DIRENT64_RECLEN(MAXNAMELEN);
 
-	data = kmem_alloc(count, KM_SLEEP);
+	data = kmem_alloc(datasz, KM_NOSLEEP);
+	if (data == NULL) {
+		/* The allocation failed; downsize and wait for it this time */
+		if (datasz > MAXBSIZE)
+			datasz = MAXBSIZE;
+		data = kmem_alloc(datasz, KM_SLEEP);
+	}
 
-	iov.iov_base = data;
-	iov.iov_len = count;
 	uio.uio_iov = &iov;
 	uio.uio_iovcnt = 1;
 	uio.uio_segflg = UIO_SYSSPACE;
 	uio.uio_extflg = UIO_COPY_CACHED;
 	uio.uio_loffset = (offset_t)args->cookie;
-	uio.uio_resid = count;
+	uio.uio_resid = datasz;
 
-	error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
+	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
+	eptr = &resp->resok.reply.entries;
+	entry = NULL;
 
-	va.va_mask = AT_ALL;
-	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
+getmoredents:
+	iov.iov_base = data;
+	iov.iov_len = datasz;
 
+	error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
 	if (error) {
-		kmem_free(data, count);
-		goto out;
+		iseof = 0;
+		goto done;
 	}
 
-	/*
-	 * If the count was not large enough to be able to guarantee
-	 * to be able to return at least one entry, then need to
-	 * check to see if NFS3ERR_TOOSMALL should be returned.
-	 */
-	if (args->count < NFS3_READDIR_MIN_COUNT(MAXNAMELEN)) {
-		/*
-		 * bufsize is used to keep track of the size of the response.
-		 * It is primed with:
-		 *	1 for the status +
-		 *	1 for the dir_attributes.attributes boolean +
-		 *	2 for the cookie verifier
-		 * all times BYTES_PER_XDR_UNIT to convert from XDR units
-		 * to bytes.  If there are directory attributes to be
-		 * returned, then:
-		 *	NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3
-		 * time BYTES_PER_XDR_UNIT is added to account for them.
-		 */
-		bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT;
-		if (vap != NULL)
-			bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
-		/*
-		 * An entry is composed of:
-		 *	1 for the true/false list indicator +
-		 *	2 for the fileid +
-		 *	1 for the length of the name +
-		 *	2 for the cookie +
-		 * all times BYTES_PER_XDR_UNIT to convert from
-		 * XDR units to bytes, plus the length of the name
-		 * rounded up to the nearest BYTES_PER_XDR_UNIT.
-		 */
-		if (count != uio.uio_resid) {
-			namlen = strlen(((struct dirent64 *)data)->d_name);
-			bufsize += (1 + 2 + 1 + 2) * BYTES_PER_XDR_UNIT +
-			    roundup(namlen, BYTES_PER_XDR_UNIT);
+	if (iov.iov_len == datasz)
+		goto done;
+
+	for (dp = (dirent64_t *)data; (char *)dp - data < datasz - iov.iov_len;
+	    dp = nextdp(dp)) {
+		char *name;
+		count3 esize;
+
+		if (dp->d_ino == 0) {
+			if (entry != NULL)
+				entry->cookie = (cookie3)dp->d_off;
+			continue;
 		}
+
+		name = nfscmd_convname(ca, exi, dp->d_name,
+		    NFSCMD_CONV_OUTBOUND, MAXPATHLEN + 1);
+		if (name == NULL) {
+			if (entry != NULL)
+				entry->cookie = (cookie3)dp->d_off;
+			continue;
+		}
+
 		/*
-		 * We need to check to see if the number of bytes left
-		 * to go into the buffer will actually fit into the
-		 * buffer.  This is calculated as the size of this
-		 * entry plus:
-		 *	1 for the true/false list indicator +
-		 *	1 for the eof indicator
-		 * times BYTES_PER_XDR_UNIT to convert from from
-		 * XDR units to bytes.
+		 * struct entry3:
+		 *   fileid:		2
+		 *   name (length):	1
+		 *   name (data):	length (rounded up)
+		 *   cookie:		2
+		 *   nextentry (bool):	1
 		 */
-		bufsize += (1 + 1) * BYTES_PER_XDR_UNIT;
-		if (bufsize > args->count) {
-			kmem_free(data, count);
-			resp->status = NFS3ERR_TOOSMALL;
-			goto out1;
+		esize = (2 + 1 + 2 + 1) * BYTES_PER_XDR_UNIT +
+		    RNDUP(strlen(name));
+
+		/* If the new entry does not fit, discard it */
+		if (esize > count - size) {
+			if (name != dp->d_name)
+				kmem_free(name, MAXPATHLEN + 1);
+			iseof = 0;
+			goto done;
 		}
+
+		entry = kmem_alloc(sizeof (entry3), KM_SLEEP);
+
+		entry->fileid = (fileid3)dp->d_ino;
+		entry->name = strdup(name);
+		if (name != dp->d_name)
+			kmem_free(name, MAXPATHLEN + 1);
+		entry->cookie = (cookie3)dp->d_off;
+
+		size += esize;
+
+		/* Add the entry to the linked list */
+		*eptr = entry;
+		eptr = &entry->nextentry;
 	}
 
-	/*
-	 * Have a valid readir buffer for the native character
-	 * set. Need to check if a conversion is necessary and
-	 * potentially rewrite the whole buffer. Note that if the
-	 * conversion expands names enough, the structure may not
-	 * fit. In this case, we need to drop entries until if fits
-	 * and patch the counts in order that the next readdir will
-	 * get the correct entries.
-	 */
-	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
-	data = nfscmd_convdirent(ca, exi, data, count, &resp->status);
+	if (!iseof && size < count) {
+		uio.uio_resid = MIN(datasz, MAXBSIZE);
+		goto getmoredents;
+	}
 
+done:
+	*eptr = NULL;
 
+	va.va_mask = AT_ALL;
+	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
+
+	if (!iseof && resp->resok.reply.entries == NULL) {
+		if (error)
+			goto out;
+		resp->status = NFS3ERR_TOOSMALL;
+		goto out1;
+	}
+
 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
 
 #if 0 /* notyet */
@@ -3271,19 +3366,20 @@
 #endif
 
 	resp->status = NFS3_OK;
-	vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
 	resp->resok.cookieverf = 0;
-	resp->resok.reply.entries = (entry3 *)data;
-	resp->resok.reply.eof = iseof;
-	resp->resok.size = count - uio.uio_resid;
-	resp->resok.count = args->count;
-	resp->resok.freecount = count;
+	resp->resok.reply.eof = iseof ? TRUE : FALSE;
 
-	DTRACE_NFSV3_4(op__readdir__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READDIR3res *, resp);
+	vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
 
+	DTRACE_NFSV3_5(op__readdir__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READDIR3res *, resp);
+
 	VN_RELE(vp);
 
+	if (data != NULL)
+		kmem_free(data, datasz);
+
 	return;
 
 out:
@@ -3293,20 +3389,24 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__readdir__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READDIR3res *, resp);
+	vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
 
+	DTRACE_NFSV3_5(op__readdir__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READDIR3res *, resp);
+
 	if (vp != NULL) {
 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
 		VN_RELE(vp);
 	}
-	vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
+
+	if (data != NULL)
+		kmem_free(data, datasz);
 }
 
 void *
 rfs3_readdir_getfh(READDIR3args *args)
 {
-
 	return (&args->dir);
 }
 
@@ -3313,9 +3413,16 @@
 void
 rfs3_readdir_free(READDIR3res *resp)
 {
+	if (resp->status == NFS3_OK) {
+		entry3 *entry, *nentry;
 
-	if (resp->status == NFS3_OK)
-		kmem_free(resp->resok.reply.entries, resp->resok.freecount);
+		for (entry = resp->resok.reply.entries; entry != NULL;
+		    entry = nentry) {
+			nentry = entry->nextentry;
+			strfree(entry->name);
+			kmem_free(entry, sizeof (entry3));
+		}
+	}
 }
 
 #ifdef nextdp
@@ -3323,32 +3430,6 @@
 #endif
 #define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
 
-/*
- * This macro computes the size of a response which contains
- * one directory entry including the attributes as well as file handle.
- * If the incoming request is larger than this, then we are guaranteed to be
- * able to return at least one more directory entry if one exists.
- *
- * NFS3_READDIRPLUS_ENTRY is made up of the following:
- *
- * boolean - 1 * BYTES_PER_XDR_UNIT
- * file id - 2 * BYTES_PER_XDR_UNIT
- * directory name length - 1 * BYTES_PER_XDR_UNIT
- * cookie - 2 * BYTES_PER_XDR_UNIT
- * attribute flag - 1 * BYTES_PER_XDR_UNIT
- * attributes - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT
- * status byte for file handle - 1 *  BYTES_PER_XDR_UNIT
- * length of a file handle - 1 * BYTES_PER_XDR_UNIT
- * Maximum length of a file handle (NFS3_MAXFHSIZE)
- * name length of the entry to the nearest bytes
- */
-#define	NFS3_READDIRPLUS_ENTRY(namelen)	\
-	((1 + 2 + 1 + 2 + 1 + NFS3_SIZEOF_FATTR3 + 1 + 1) * \
-		BYTES_PER_XDR_UNIT + \
-	NFS3_MAXFHSIZE + roundup(namelen, BYTES_PER_XDR_UNIT))
-
-static int rfs3_readdir_unit = MAXBSIZE;
-
 /* ARGSUSED */
 void
 rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp,
@@ -3360,39 +3441,39 @@
 	struct vattr va;
 	struct iovec iov;
 	struct uio uio;
-	char *data;
 	int iseof;
-	struct dirent64 *dp;
-	vnode_t *nvp;
-	struct vattr *nvap;
-	struct vattr nva;
-	entryplus3_info *infop = NULL;
-	int size = 0;
-	int nents = 0;
-	int bufsize = 0;
-	int entrysize = 0;
-	int tofit = 0;
-	int rd_unit = rfs3_readdir_unit;
-	int prev_len;
-	int space_left;
-	int i;
-	uint_t *namlen = NULL;
-	char *ndata = NULL;
-	struct sockaddr *ca;
-	size_t ret;
 
-	vap = NULL;
+	count3 dircount = args->dircount;
+	count3 maxcount = args->maxcount;
+	count3 dirsize = 0;
+	count3 size;		/* size of the READDIRPLUS3resok structure */
 
+	size_t datasz;
+	char *data = NULL;
+	dirent64_t *dp;
+
+	struct sockaddr *ca;
+	entryplus3 **eptr;
+	entryplus3 *entry;
+
 	vp = nfs3_fhtovp(&args->dir, exi);
 
-	DTRACE_NFSV3_4(op__readdirplus__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READDIRPLUS3args *, args);
+	DTRACE_NFSV3_5(op__readdirplus__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READDIRPLUS3args *, args);
 
 	if (vp == NULL) {
-		error = ESTALE;
-		goto out;
+		resp->status = NFS3ERR_STALE;
+		vap = NULL;
+		goto out1;
 	}
 
+	if (vp->v_type != VDIR) {
+		resp->status = NFS3ERR_NOTDIR;
+		vap = NULL;
+		goto out1;
+	}
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -3405,6 +3486,7 @@
 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
 			    exi)) {
 				resp->status = NFS3ERR_ACCES;
+				vap = NULL;
 				goto out1;
 			}
 		}
@@ -3415,11 +3497,6 @@
 	va.va_mask = AT_ALL;
 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
 
-	if (vp->v_type != VDIR) {
-		error = ENOTDIR;
-		goto out;
-	}
-
 	error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
 	if (error)
 		goto out;
@@ -3427,218 +3504,213 @@
 	/*
 	 * Don't allow arbitrary counts for allocation
 	 */
-	if (args->maxcount > rfs3_tsize(req))
-		args->maxcount = rfs3_tsize(req);
+	if (maxcount > rfs3_tsize(req))
+		maxcount = rfs3_tsize(req);
 
 	/*
-	 * Make sure that there is room to read at least one entry
-	 * if any are available
+	 * struct READDIRPLUS3resok:
+	 *   dir_attributes:	1 + NFS3_SIZEOF_FATTR3
+	 *   cookieverf:	2
+	 *   entries (bool):	1
+	 *   eof:		1
 	 */
-	args->dircount = MIN(args->dircount, args->maxcount);
+	size = (1 + NFS3_SIZEOF_FATTR3 + 2 + 1 + 1) * BYTES_PER_XDR_UNIT;
 
-	if (args->dircount < DIRENT64_RECLEN(MAXNAMELEN))
-		args->dircount = DIRENT64_RECLEN(MAXNAMELEN);
+	if (size > maxcount) {
+		resp->status = NFS3ERR_TOOSMALL;
+		goto out1;
+	}
 
 	/*
-	 * This allocation relies on a minimum directory entry
-	 * being roughly 24 bytes.  Therefore, the namlen array
-	 * will have enough space based on the maximum number of
-	 * entries to read.
+	 * This is simplification.  The dirent64_t size is not the same as the
+	 * size of XDR representation of entryplus3 (excluding attributes and
+	 * handle), but the sizes are similar so we'll assume they are same.
+	 * This assumption should not cause any harm.  In worst case we will
+	 * need to issue VOP_READDIR() once more.
 	 */
-	namlen = kmem_alloc(args->dircount, KM_SLEEP);
 
-	space_left = args->dircount;
-	data = kmem_alloc(args->dircount, KM_SLEEP);
-	dp = (struct dirent64 *)data;
+	datasz = MIN(dircount, maxcount);
+
+	/*
+	 * Make sure that there is room to read at least one entry
+	 * if any are available.
+	 */
+	if (datasz < DIRENT64_RECLEN(MAXNAMELEN))
+		datasz = DIRENT64_RECLEN(MAXNAMELEN);
+
+	data = kmem_alloc(datasz, KM_NOSLEEP);
+	if (data == NULL) {
+		/* The allocation failed; downsize and wait for it this time */
+		if (datasz > MAXBSIZE)
+			datasz = MAXBSIZE;
+		data = kmem_alloc(datasz, KM_SLEEP);
+	}
+
 	uio.uio_iov = &iov;
 	uio.uio_iovcnt = 1;
 	uio.uio_segflg = UIO_SYSSPACE;
 	uio.uio_extflg = UIO_COPY_CACHED;
 	uio.uio_loffset = (offset_t)args->cookie;
+	uio.uio_resid = datasz;
 
-	/*
-	 * bufsize is used to keep track of the size of the response as we
-	 * get post op attributes and filehandles for each entry.  This is
-	 * an optimization as the server may have read more entries than will
-	 * fit in the buffer specified by maxcount.  We stop calculating
-	 * post op attributes and filehandles once we have exceeded maxcount.
-	 * This will minimize the effect of truncation.
-	 *
-	 * It is primed with:
-	 *	1 for the status +
-	 *	1 for the dir_attributes.attributes boolean +
-	 *	2 for the cookie verifier
-	 * all times BYTES_PER_XDR_UNIT to convert from XDR units
-	 * to bytes.  If there are directory attributes to be
-	 * returned, then:
-	 *	NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3
-	 * time BYTES_PER_XDR_UNIT is added to account for them.
-	 */
-	bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT;
-	if (vap != NULL)
-		bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
+	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
+	eptr = &resp->resok.reply.entries;
+	entry = NULL;
 
 getmoredents:
-	/*
-	 * Here we make a check so that our read unit is not larger than
-	 * the space left in the buffer.
-	 */
-	rd_unit = MIN(rd_unit, space_left);
-	iov.iov_base = (char *)dp;
-	iov.iov_len = rd_unit;
-	uio.uio_resid = rd_unit;
-	prev_len = rd_unit;
+	iov.iov_base = data;
+	iov.iov_len = datasz;
 
 	error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
-
 	if (error) {
-		kmem_free(data, args->dircount);
-		goto out;
+		iseof = 0;
+		goto done;
 	}
 
-	if (uio.uio_resid == prev_len && !iseof) {
-		if (nents == 0) {
-			kmem_free(data, args->dircount);
-			resp->status = NFS3ERR_TOOSMALL;
-			goto out1;
-		}
+	if (iov.iov_len == datasz)
+		goto done;
 
-		/*
-		 * We could not get any more entries, so get the attributes
-		 * and filehandle for the entries already obtained.
-		 */
-		goto good;
-	}
+	for (dp = (dirent64_t *)data; (char *)dp - data < datasz - iov.iov_len;
+	    dp = nextdp(dp)) {
+		char *name;
+		vnode_t *nvp;
+		count3 edirsize;
+		count3 esize;
 
-	/*
-	 * We estimate the size of the response by assuming the
-	 * entry exists and attributes and filehandle are also valid
-	 */
-	for (size = prev_len - uio.uio_resid;
-	    size > 0;
-	    size -= dp->d_reclen, dp = nextdp(dp)) {
-
 		if (dp->d_ino == 0) {
-			nents++;
+			if (entry != NULL)
+				entry->cookie = (cookie3)dp->d_off;
 			continue;
 		}
 
-		namlen[nents] = strlen(dp->d_name);
-		entrysize = NFS3_READDIRPLUS_ENTRY(namlen[nents]);
+		name = nfscmd_convname(ca, exi, dp->d_name,
+		    NFSCMD_CONV_OUTBOUND, MAXPATHLEN + 1);
+		if (name == NULL) {
+			if (entry != NULL)
+				entry->cookie = (cookie3)dp->d_off;
+			continue;
+		}
 
 		/*
-		 * We need to check to see if the number of bytes left
-		 * to go into the buffer will actually fit into the
-		 * buffer.  This is calculated as the size of this
-		 * entry plus:
-		 *	1 for the true/false list indicator +
-		 *	1 for the eof indicator
-		 * times BYTES_PER_XDR_UNIT to convert from XDR units
-		 * to bytes.
-		 *
-		 * Also check the dircount limit against the first entry read
-		 *
+		 * struct entryplus3:
+		 *   fileid:		2
+		 *   name (length):	1
+		 *   name (data):	length (rounded up)
+		 *   cookie:		2
 		 */
-		tofit = entrysize + (1 + 1) * BYTES_PER_XDR_UNIT;
-		if (bufsize + tofit > args->maxcount) {
-			/*
-			 * We make a check here to see if this was the
-			 * first entry being measured.  If so, then maxcount
-			 * was too small to begin with and so we need to
-			 * return with NFS3ERR_TOOSMALL.
-			 */
-			if (nents == 0) {
-				kmem_free(data, args->dircount);
-				resp->status = NFS3ERR_TOOSMALL;
-				goto out1;
-			}
-			iseof = FALSE;
-			goto good;
+		edirsize = (2 + 1 + 2) * BYTES_PER_XDR_UNIT +
+		    RNDUP(strlen(name));
+
+		/*
+		 * struct entryplus3:
+		 *   attributes_follow:	1
+		 *   handle_follows:	1
+		 *   nextentry (bool):	1
+		 */
+		esize = edirsize + (1 + 1 + 1) * BYTES_PER_XDR_UNIT;
+
+		/* If the new entry does not fit, we are done */
+		if (edirsize > dircount - dirsize || esize > maxcount - size) {
+			if (name != dp->d_name)
+				kmem_free(name, MAXPATHLEN + 1);
+			iseof = 0;
+			error = 0;
+			goto done;
 		}
-		bufsize += entrysize;
-		nents++;
-	}
 
-	/*
-	 * If there is enough room to fit at least 1 more entry including
-	 * post op attributes and filehandle in the buffer AND that we haven't
-	 * exceeded dircount then go back and get some more.
-	 */
-	if (!iseof &&
-	    (args->maxcount - bufsize) >= NFS3_READDIRPLUS_ENTRY(MAXNAMELEN)) {
-		space_left -= (prev_len - uio.uio_resid);
-		if (space_left >= DIRENT64_RECLEN(MAXNAMELEN))
-			goto getmoredents;
+		entry = kmem_alloc(sizeof (entryplus3), KM_SLEEP);
 
-		/* else, fall through */
-	}
-good:
-	va.va_mask = AT_ALL;
-	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
+		entry->fileid = (fileid3)dp->d_ino;
+		entry->name = strdup(name);
+		if (name != dp->d_name)
+			kmem_free(name, MAXPATHLEN + 1);
+		entry->cookie = (cookie3)dp->d_off;
 
-	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
+		error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr,
+		    NULL, NULL, NULL);
+		if (error) {
+			entry->name_attributes.attributes = FALSE;
+			entry->name_handle.handle_follows = FALSE;
+		} else {
+			struct vattr nva;
+			struct vattr *nvap;
 
-	infop = kmem_alloc(nents * sizeof (struct entryplus3_info), KM_SLEEP);
-	resp->resok.infop = infop;
+			nva.va_mask = AT_ALL;
+			nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL :
+			    &nva;
 
-	dp = (struct dirent64 *)data;
-	for (i = 0; i < nents; i++) {
+			/* Lie about the object type for a referral */
+			if (nvap != NULL && vn_is_nfs_reparse(nvp, cr))
+				nvap->va_type = VLNK;
 
-		if (dp->d_ino == 0) {
-			infop[i].attr.attributes = FALSE;
-			infop[i].fh.handle_follows = FALSE;
-			dp = nextdp(dp);
-			continue;
-		}
+			if (vn_ismntpt(nvp)) {
+				entry->name_attributes.attributes = FALSE;
+				entry->name_handle.handle_follows = FALSE;
+			} else {
+				vattr_to_post_op_attr(nvap,
+				    &entry->name_attributes);
 
-		infop[i].namelen = namlen[i];
+				error = makefh3(&entry->name_handle.handle, nvp,
+				    exi);
+				if (!error)
+					entry->name_handle.handle_follows =
+					    TRUE;
+				else
+					entry->name_handle.handle_follows =
+					    FALSE;
+			}
 
-		error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr,
-		    NULL, NULL, NULL);
-		if (error) {
-			infop[i].attr.attributes = FALSE;
-			infop[i].fh.handle_follows = FALSE;
-			dp = nextdp(dp);
-			continue;
+			VN_RELE(nvp);
 		}
 
-		nva.va_mask = AT_ALL;
-		nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL : &nva;
+		/*
+		 * struct entryplus3 (optionally):
+		 *   attributes:	NFS3_SIZEOF_FATTR3
+		 *   handle length:	1
+		 *   handle data:	length (rounded up)
+		 */
+		if (entry->name_attributes.attributes == TRUE)
+			esize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
+		if (entry->name_handle.handle_follows == TRUE)
+			esize += 1 * BYTES_PER_XDR_UNIT +
+			    RNDUP(entry->name_handle.handle.fh3_length);
 
-		/* Lie about the object type for a referral */
-		if (vn_is_nfs_reparse(nvp, cr))
-			nvap->va_type = VLNK;
+		/* If the new entry does not fit, discard it */
+		if (esize > maxcount - size) {
+			strfree(entry->name);
+			kmem_free(entry, sizeof (entryplus3));
+			iseof = 0;
+			error = 0;
+			goto done;
+		}
 
-		vattr_to_post_op_attr(nvap, &infop[i].attr);
+		dirsize += edirsize;
+		size += esize;
 
-		error = makefh3(&infop[i].fh.handle, nvp, exi);
-		if (!error)
-			infop[i].fh.handle_follows = TRUE;
-		else
-			infop[i].fh.handle_follows = FALSE;
+		/* Add the entry to the linked list */
+		*eptr = entry;
+		eptr = &entry->nextentry;
+	}
 
-		VN_RELE(nvp);
-		dp = nextdp(dp);
+	if (!iseof && dirsize < dircount && size < maxcount) {
+		uio.uio_resid = MIN(datasz, MAXBSIZE);
+		goto getmoredents;
 	}
 
-	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
-	ret = nfscmd_convdirplus(ca, exi, data, nents, args->dircount, &ndata);
-	if (ndata == NULL)
-		ndata = data;
+done:
+	*eptr = NULL;
 
-	if (ret > 0) {
-		/*
-		 * We had to drop one or more entries in order to fit
-		 * during the character conversion.  We need to patch
-		 * up the size and eof info.
-		 */
-		if (iseof)
-			iseof = FALSE;
+	va.va_mask = AT_ALL;
+	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
 
-		ret = nfscmd_dropped_entrysize((struct dirent64 *)data,
-		    nents, ret);
+	if (!iseof && resp->resok.reply.entries == NULL) {
+		if (error)
+			goto out;
+		resp->status = NFS3ERR_TOOSMALL;
+		goto out1;
 	}
 
+	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
 
 #if 0 /* notyet */
 	/*
@@ -3652,25 +3724,21 @@
 	(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
 #endif
 
-	kmem_free(namlen, args->dircount);
-
 	resp->status = NFS3_OK;
-	vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
 	resp->resok.cookieverf = 0;
-	resp->resok.reply.entries = (entryplus3 *)ndata;
-	resp->resok.reply.eof = iseof;
-	resp->resok.size = nents;
-	resp->resok.count = args->dircount - ret;
-	resp->resok.maxcount = args->maxcount;
+	resp->resok.reply.eof = iseof ? TRUE : FALSE;
 
-	DTRACE_NFSV3_4(op__readdirplus__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READDIRPLUS3res *, resp);
-	if (ndata != data)
-		kmem_free(data, args->dircount);
+	vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
 
+	DTRACE_NFSV3_5(op__readdirplus__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READDIRPLUS3res *, resp);
 
 	VN_RELE(vp);
 
+	if (data != NULL)
+		kmem_free(data, datasz);
+
 	return;
 
 out:
@@ -3681,24 +3749,24 @@
 		resp->status = puterrno3(error);
 	}
 out1:
-	DTRACE_NFSV3_4(op__readdirplus__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, READDIRPLUS3res *, resp);
+	vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
 
+	DTRACE_NFSV3_5(op__readdirplus__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    READDIRPLUS3res *, resp);
+
 	if (vp != NULL) {
 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
 		VN_RELE(vp);
 	}
 
-	if (namlen != NULL)
-		kmem_free(namlen, args->dircount);
-
-	vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
+	if (data != NULL)
+		kmem_free(data, datasz);
 }
 
 void *
 rfs3_readdirplus_getfh(READDIRPLUS3args *args)
 {
-
 	return (&args->dir);
 }
 
@@ -3705,11 +3773,15 @@
 void
 rfs3_readdirplus_free(READDIRPLUS3res *resp)
 {
-
 	if (resp->status == NFS3_OK) {
-		kmem_free(resp->resok.reply.entries, resp->resok.count);
-		kmem_free(resp->resok.infop,
-		    resp->resok.size * sizeof (struct entryplus3_info));
+		entryplus3 *entry, *nentry;
+
+		for (entry = resp->resok.reply.entries; entry != NULL;
+		    entry = nentry) {
+			nentry = entry->nextentry;
+			strfree(entry->name);
+			kmem_free(entry, sizeof (entryplus3));
+		}
 	}
 }
 
@@ -3728,8 +3800,9 @@
 
 	vp = nfs3_fhtovp(&args->fsroot, exi);
 
-	DTRACE_NFSV3_4(op__fsstat__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, FSSTAT3args *, args);
+	DTRACE_NFSV3_5(op__fsstat__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    FSSTAT3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -3779,8 +3852,9 @@
 	resp->resok.afiles = (size3)sb.f_favail;
 	resp->resok.invarsec = 0;
 
-	DTRACE_NFSV3_4(op__fsstat__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, FSSTAT3res *, resp);
+	DTRACE_NFSV3_5(op__fsstat__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    FSSTAT3res *, resp);
 	VN_RELE(vp);
 
 	return;
@@ -3792,8 +3866,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__fsstat__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, FSSTAT3res *, resp);
+	DTRACE_NFSV3_5(op__fsstat__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    FSSTAT3res *, resp);
 
 	if (vp != NULL)
 		VN_RELE(vp);
@@ -3803,7 +3878,6 @@
 void *
 rfs3_fsstat_getfh(FSSTAT3args *args)
 {
-
 	return (&args->fsroot);
 }
 
@@ -3821,8 +3895,9 @@
 
 	vp = nfs3_fhtovp(&args->fsroot, exi);
 
-	DTRACE_NFSV3_4(op__fsinfo__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, FSINFO3args *, args);
+	DTRACE_NFSV3_5(op__fsinfo__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    FSINFO3args *, args);
 
 	if (vp == NULL) {
 		if (curthread->t_flag & T_WOULDBLOCK) {
@@ -3896,8 +3971,9 @@
 	resp->resok.properties = FSF3_LINK | FSF3_SYMLINK |
 	    FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
 
-	DTRACE_NFSV3_4(op__fsinfo__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, FSINFO3res *, resp);
+	DTRACE_NFSV3_5(op__fsinfo__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    FSINFO3res *, resp);
 
 	VN_RELE(vp);
 
@@ -3904,8 +3980,9 @@
 	return;
 
 out:
-	DTRACE_NFSV3_4(op__fsinfo__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, NULL, FSINFO3res *, resp);
+	DTRACE_NFSV3_5(op__fsinfo__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, NULL, struct exportinfo *, exi,
+	    FSINFO3res *, resp);
 	if (vp != NULL)
 		VN_RELE(vp);
 }
@@ -3931,8 +4008,9 @@
 
 	vp = nfs3_fhtovp(&args->object, exi);
 
-	DTRACE_NFSV3_4(op__pathconf__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, PATHCONF3args *, args);
+	DTRACE_NFSV3_5(op__pathconf__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    PATHCONF3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -3988,8 +4066,9 @@
 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
 	resp->resok.info.case_insensitive = FALSE;
 	resp->resok.info.case_preserving = TRUE;
-	DTRACE_NFSV3_4(op__pathconf__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, PATHCONF3res *, resp);
+	DTRACE_NFSV3_5(op__pathconf__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    PATHCONF3res *, resp);
 	VN_RELE(vp);
 	return;
 
@@ -4000,8 +4079,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__pathconf__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, PATHCONF3res *, resp);
+	DTRACE_NFSV3_5(op__pathconf__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    PATHCONF3res *, resp);
 	if (vp != NULL)
 		VN_RELE(vp);
 	vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
@@ -4010,7 +4090,6 @@
 void *
 rfs3_pathconf_getfh(PATHCONF3args *args)
 {
-
 	return (&args->object);
 }
 
@@ -4018,6 +4097,7 @@
 rfs3_commit(COMMIT3args *args, COMMIT3res *resp, struct exportinfo *exi,
     struct svc_req *req, cred_t *cr, bool_t ro)
 {
+	nfs3_srv_t *ns;
 	int error;
 	vnode_t *vp;
 	struct vattr *bvap;
@@ -4030,8 +4110,9 @@
 
 	vp = nfs3_fhtovp(&args->file, exi);
 
-	DTRACE_NFSV3_4(op__commit__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, COMMIT3args *, args);
+	DTRACE_NFSV3_5(op__commit__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    COMMIT3args *, args);
 
 	if (vp == NULL) {
 		error = ESTALE;
@@ -4038,6 +4119,7 @@
 		goto out;
 	}
 
+	ns = zone_getspecific(rfs3_zone_key, curzone);
 	bva.va_mask = AT_ALL;
 	error = VOP_GETATTR(vp, &bva, 0, cr, NULL);
 
@@ -4090,10 +4172,11 @@
 
 	resp->status = NFS3_OK;
 	vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
-	resp->resok.verf = write3verf;
+	resp->resok.verf = ns->write3verf;
 
-	DTRACE_NFSV3_4(op__commit__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, COMMIT3res *, resp);
+	DTRACE_NFSV3_5(op__commit__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    COMMIT3res *, resp);
 
 	VN_RELE(vp);
 
@@ -4106,8 +4189,9 @@
 	} else
 		resp->status = puterrno3(error);
 out1:
-	DTRACE_NFSV3_4(op__commit__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, vp, COMMIT3res *, resp);
+	DTRACE_NFSV3_5(op__commit__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
+	    COMMIT3res *, resp);
 
 	if (vp != NULL)
 		VN_RELE(vp);
@@ -4117,7 +4201,6 @@
 void *
 rfs3_commit_getfh(COMMIT3args *args)
 {
-
 	return (&args->file);
 }
 
@@ -4185,7 +4268,7 @@
 	return (0);
 }
 
-static ftype3 vt_to_nf3[] = {
+static const ftype3 vt_to_nf3[] = {
 	0, NF3REG, NF3DIR, NF3BLK, NF3CHR, NF3LNK, NF3FIFO, 0, 0, NF3SOCK, 0
 };
 
@@ -4267,14 +4350,32 @@
 static void
 vattr_to_wcc_data(struct vattr *bvap, struct vattr *avap, wcc_data *wccp)
 {
-
 	vattr_to_pre_op_attr(bvap, &wccp->before);
 	vattr_to_post_op_attr(avap, &wccp->after);
 }
 
-void
-rfs3_srvrinit(void)
+static int
+rdma_setup_read_data3(READ3args *args, READ3resok *rok)
 {
+	struct clist	*wcl;
+	int		wlist_len;
+	count3		count = rok->count;
+
+	wcl = args->wlist;
+	if (rdma_setup_read_chunks(wcl, count, &wlist_len) == FALSE)
+		return (FALSE);
+
+	wcl = args->wlist;
+	rok->wlist_len = wlist_len;
+	rok->wlist = wcl;
+	return (TRUE);
+}
+
+/* ARGSUSED */
+static void *
+rfs3_zone_init(zoneid_t zoneid)
+{
+	nfs3_srv_t *ns;
 	struct rfs3_verf_overlay {
 		uint_t id; /* a "unique" identifier */
 		int ts; /* a unique timestamp */
@@ -4281,6 +4382,8 @@
 	} *verfp;
 	timestruc_t now;
 
+	ns = kmem_zalloc(sizeof (*ns), KM_SLEEP);
+
 	/*
 	 * The following algorithm attempts to find a unique verifier
 	 * to be used as the write verifier returned from the server
@@ -4304,11 +4407,11 @@
 	 * We ASSERT that this constant logic expression is
 	 * always true because in the past, it wasn't.
 	 */
-	ASSERT(sizeof (*verfp) <= sizeof (write3verf));
+	ASSERT(sizeof (*verfp) <= sizeof (ns->write3verf));
 #endif
 
 	gethrestime(&now);
-	verfp = (struct rfs3_verf_overlay *)&write3verf;
+	verfp = (struct rfs3_verf_overlay *)&ns->write3verf;
 	verfp->ts = (int)now.tv_sec;
 	verfp->id = zone_get_hostid(NULL);
 
@@ -4315,26 +4418,23 @@
 	if (verfp->id == 0)
 		verfp->id = (uint_t)now.tv_nsec;
 
-	nfs3_srv_caller_id = fs_new_caller_id();
-
+	return (ns);
 }
 
-static int
-rdma_setup_read_data3(READ3args *args, READ3resok *rok)
+/* ARGSUSED */
+static void
+rfs3_zone_fini(zoneid_t zoneid, void *data)
 {
-	struct clist	*wcl;
-	int		wlist_len;
-	count3		count = rok->count;
+	nfs3_srv_t *ns = data;
 
-	wcl = args->wlist;
-	if (rdma_setup_read_chunks(wcl, count, &wlist_len) == FALSE) {
-		return (FALSE);
-	}
+	kmem_free(ns, sizeof (*ns));
+}
 
-	wcl = args->wlist;
-	rok->wlist_len = wlist_len;
-	rok->wlist = wcl;
-	return (TRUE);
+void
+rfs3_srvrinit(void)
+{
+	nfs3_srv_caller_id = fs_new_caller_id();
+	zone_key_create(&rfs3_zone_key, rfs3_zone_init, NULL, rfs3_zone_fini);
 }
 
 void
