Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 14 Jul 2017 20:47:42 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r320998 - in stable/11/sys/fs: nfs nfsclient
Message-ID:  <201707142047.v6EKlgWq070971@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rmacklem
Date: Fri Jul 14 20:47:41 2017
New Revision: 320998
URL: https://svnweb.freebsd.org/changeset/base/320998

Log:
  MFC: r320322
  Add two new compound RPCs to the NFSv4.1/pNFS client.
  
  When the NFSv4.1 client is doing pNFS, it needs to get an Open and
  a Layout for every file it will be doing I/O on. The current code
  does two separate RPCs to get these. This patch adds two new compounds
  that do the both the Open and LayoutGet in the same RPC, reducing the
  RPC count.
  It also factors out the code that sets up and parses the LayoutGet operation
  into separate functions, so that the code doesn't get duplicated for
  these new RPCs.
  This patch is fairly large, but should only affect the NFSv4.1 client
  when the "pnfs" option is specified.

Modified:
  stable/11/sys/fs/nfs/nfs_commonsubs.c
  stable/11/sys/fs/nfs/nfsport.h
  stable/11/sys/fs/nfs/nfsproto.h
  stable/11/sys/fs/nfsclient/nfs_clcomsubs.c
  stable/11/sys/fs/nfsclient/nfs_clrpcops.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/fs/nfs/nfs_commonsubs.c
==============================================================================
--- stable/11/sys/fs/nfs/nfs_commonsubs.c	Fri Jul 14 18:36:15 2017	(r320997)
+++ stable/11/sys/fs/nfs/nfs_commonsubs.c	Fri Jul 14 20:47:41 2017	(r320998)
@@ -175,7 +175,7 @@ static struct nfsrv_lughash	*nfsgroupnamehash;
  */
 int nfs_bigreply[NFSV41_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 };
 
 /* local functions */
 static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);

Modified: stable/11/sys/fs/nfs/nfsport.h
==============================================================================
--- stable/11/sys/fs/nfs/nfsport.h	Fri Jul 14 18:36:15 2017	(r320997)
+++ stable/11/sys/fs/nfs/nfsport.h	Fri Jul 14 20:47:41 2017	(r320998)
@@ -357,11 +357,13 @@
 #define	NFSPROC_WRITEDS		51
 #define	NFSPROC_READDS		52
 #define	NFSPROC_COMMITDS	53
+#define	NFSPROC_OPENLAYGET	54
+#define	NFSPROC_CREATELAYGET	55
 
 /*
  * Must be defined as one higher than the last NFSv4.1 Proc# above.
  */
-#define	NFSV41_NPROCS		54
+#define	NFSV41_NPROCS		56
 
 #endif	/* NFS_V3NPROCS */
 
@@ -390,7 +392,7 @@ struct nfsstatsv1 {
 	uint64_t	readlink_bios;
 	uint64_t	biocache_readdirs;
 	uint64_t	readdir_bios;
-	uint64_t	rpccnt[NFSV41_NPROCS + 15];
+	uint64_t	rpccnt[NFSV41_NPROCS + 13];
 	uint64_t	rpcretries;
 	uint64_t	srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS];
 	uint64_t	srvrpc_errs;

Modified: stable/11/sys/fs/nfs/nfsproto.h
==============================================================================
--- stable/11/sys/fs/nfs/nfsproto.h	Fri Jul 14 18:36:15 2017	(r320997)
+++ stable/11/sys/fs/nfs/nfsproto.h	Fri Jul 14 20:47:41 2017	(r320998)
@@ -342,11 +342,13 @@
 #define	NFSPROC_WRITEDS		51
 #define	NFSPROC_READDS		52
 #define	NFSPROC_COMMITDS	53
+#define	NFSPROC_OPENLAYGET	54
+#define	NFSPROC_CREATELAYGET	55
 
 /*
  * Must be defined as one higher than the last NFSv4.1 Proc# above.
  */
-#define	NFSV41_NPROCS		54
+#define	NFSV41_NPROCS		56
 
 #endif	/* NFS_V3NPROCS */
 

Modified: stable/11/sys/fs/nfsclient/nfs_clcomsubs.c
==============================================================================
--- stable/11/sys/fs/nfsclient/nfs_clcomsubs.c	Fri Jul 14 18:36:15 2017	(r320997)
+++ stable/11/sys/fs/nfsclient/nfs_clcomsubs.c	Fri Jul 14 20:47:41 2017	(r320998)
@@ -112,6 +112,8 @@ static struct {
 	{ NFSV4OP_WRITE, 1, "WriteDS", 7, },
 	{ NFSV4OP_READ, 1, "ReadDS", 6, },
 	{ NFSV4OP_COMMIT, 1, "CommitDS", 8, },
+	{ NFSV4OP_OPEN, 3, "OpenLayoutGet", 13, },
+	{ NFSV4OP_OPEN, 8, "CreateLayGet", 12, },
 };
 
 /*
@@ -120,7 +122,7 @@ static struct {
 static int nfs_bigrequest[NFSV41_NPROCS] = {
 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 1, 0, 0
+	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0
 };
 
 /*

Modified: stable/11/sys/fs/nfsclient/nfs_clrpcops.c
==============================================================================
--- stable/11/sys/fs/nfsclient/nfs_clrpcops.c	Fri Jul 14 18:36:15 2017	(r320997)
+++ stable/11/sys/fs/nfsclient/nfs_clrpcops.c	Fri Jul 14 20:47:41 2017	(r320998)
@@ -127,6 +127,29 @@ static enum nfsclds_state nfscl_getsameserver(struct n
 static int nfsrpc_commitds(vnode_t, uint64_t, int, struct nfsclds *,
     struct nfsfh *, struct ucred *, NFSPROC_T *, void *);
 #endif
+static void nfsrv_setuplayoutget(struct nfsrv_descript *, int, uint64_t,
+    uint64_t, uint64_t, nfsv4stateid_t *, int, int);
+static int nfsrv_parselayoutget(struct nfsrv_descript *, nfsv4stateid_t *,
+    int *, struct nfsclflayouthead *);
+static int nfsrpc_getopenlayout(struct nfsmount *, vnode_t, u_int8_t *,
+    int, uint8_t *, int, uint32_t, struct nfsclopen *, uint8_t *, int,
+    struct nfscldeleg **, struct ucred *, NFSPROC_T *);
+static int nfsrpc_getcreatelayout(vnode_t, char *, int, struct vattr *,
+    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **,
+    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
+    struct nfsfh **, int *, int *, void *, int *);
+static int nfsrpc_openlayoutrpc(struct nfsmount *, vnode_t, u_int8_t *,
+    int, uint8_t *, int, uint32_t, struct nfsclopen *, uint8_t *, int,
+    struct nfscldeleg **, nfsv4stateid_t *, int, int, int *,
+    struct nfsclflayouthead *, int *, struct ucred *, NFSPROC_T *);
+static int nfsrpc_createlayout(vnode_t, char *, int, struct vattr *,
+    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **,
+    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
+    struct nfsfh **, int *, int *, void *, int *, nfsv4stateid_t *,
+    int, int, int *, struct nfsclflayouthead *, int *);
+static int nfsrpc_layoutgetres(struct nfsmount *, vnode_t, uint8_t *,
+    int, nfsv4stateid_t *, int, uint32_t *, struct nfscllayout **,
+    struct nfsclflayouthead *, int, int *, struct ucred *, NFSPROC_T *);
 
 /*
  * nfs null call from vfs.
@@ -301,11 +324,27 @@ else printf(" fhl=0\n");
 		clidrev = 0;
 	    if (ret == NFSCLOPEN_DOOPEN) {
 		if (np->n_v4 != NULL) {
-			error = nfsrpc_openrpc(nmp, vp, np->n_v4->n4_data,
-			   np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
-			   np->n_fhp->nfh_len, mode, op,
-			   NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &dp,
-			   0, 0x0, cred, p, 0, 0);
+			/*
+			 * For the first attempt, try and get a layout, if
+			 * pNFS is enabled for the mount.
+			 */
+			if (!NFSHASPNFS(nmp) || nfscl_enablecallb == 0 ||
+			    nfs_numnfscbd == 0 ||
+			    (np->n_flag & NNOLAYOUT) != 0 || retrycnt > 0)
+				error = nfsrpc_openrpc(nmp, vp,
+				    np->n_v4->n4_data,
+				    np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
+				    np->n_fhp->nfh_len, mode, op,
+				    NFS4NODENAME(np->n_v4),
+				    np->n_v4->n4_namelen,
+				    &dp, 0, 0x0, cred, p, 0, 0);
+			else
+				error = nfsrpc_getopenlayout(nmp, vp,
+				    np->n_v4->n4_data,
+				    np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
+				    np->n_fhp->nfh_len, mode, op,
+				    NFS4NODENAME(np->n_v4),
+				    np->n_v4->n4_namelen, &dp, cred, p);
 			if (dp != NULL) {
 #ifdef APPLE
 				OSBitAndAtomic((int32_t)~NDELEGMOD, (UInt32 *)&np->n_flag);
@@ -1894,9 +1933,15 @@ nfsrpc_create(vnode_t dvp, char *name, int namelen, st
 			clidrev = nmp->nm_clp->nfsc_clientidrev;
 		else
 			clidrev = 0;
-		error = nfsrpc_createv4(dvp, name, namelen, vap, cverf, fmode,
-		  owp, &dp, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
-		  dstuff, &unlocked);
+		if (!NFSHASPNFS(nmp) || nfscl_enablecallb == 0 ||
+		    nfs_numnfscbd == 0 || retrycnt > 0)
+			error = nfsrpc_createv4(dvp, name, namelen, vap, cverf,
+			  fmode, owp, &dp, cred, p, dnap, nnap, nfhpp,
+			  attrflagp, dattrflagp, dstuff, &unlocked);
+		else
+			error = nfsrpc_getcreatelayout(dvp, name, namelen, vap,
+			  cverf, fmode, owp, &dp, cred, p, dnap, nnap, nfhpp,
+			  attrflagp, dattrflagp, dstuff, &unlocked);
 		/*
 		 * There is no need to invalidate cached attributes here,
 		 * since new post-delegation issue attributes are always
@@ -4785,149 +4830,22 @@ nfsrpc_layoutget(struct nfsmount *nmp, uint8_t *fhp, i
     nfsv4stateid_t *stateidp, int *retonclosep, struct nfsclflayouthead *flhp,
     struct ucred *cred, NFSPROC_T *p, void *stuff)
 {
-	uint32_t *tl;
 	struct nfsrv_descript nfsd, *nd = &nfsd;
-	struct nfsfh *nfhp;
-	struct nfsclflayout *flp, *prevflp, *tflp;
-	int cnt, error, gotiomode, fhcnt, nfhlen, i, j;
-	uint8_t *cp;
-	uint64_t retlen;
+	int error;
 
-	flp = NULL;
-	gotiomode = -1;
 	nfscl_reqstart(nd, NFSPROC_LAYOUTGET, nmp, fhp, fhlen, NULL, NULL);
-	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
-	    NFSX_STATEID);
-	*tl++ = newnfs_false;		/* Don't signal availability. */
-	*tl++ = txdr_unsigned(NFSLAYOUT_NFSV4_1_FILES);
-	*tl++ = txdr_unsigned(iomode);
-	txdr_hyper(offset, tl);
-	tl += 2;
-	txdr_hyper(len, tl);
-	tl += 2;
-	txdr_hyper(minlen, tl);
-	tl += 2;
-	*tl++ = txdr_unsigned(stateidp->seqid);
-	NFSCL_DEBUG(4, "layget seq=%d\n", (int)stateidp->seqid);
-	*tl++ = stateidp->other[0];
-	*tl++ = stateidp->other[1];
-	*tl++ = stateidp->other[2];
-	*tl = txdr_unsigned(layoutlen);
+	nfsrv_setuplayoutget(nd, iomode, offset, len, minlen, stateidp,
+	    layoutlen, 0);
 	nd->nd_flag |= ND_USEGSSNAME;
 	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
 	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
+	NFSCL_DEBUG(4, "layget err=%d st=%d\n", error, nd->nd_repstat);
 	if (error != 0)
 		return (error);
-	if (nd->nd_repstat == 0) {
-		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + NFSX_STATEID);
-		if (*tl++ != 0)
-			*retonclosep = 1;
-		else
-			*retonclosep = 0;
-		stateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
-		NFSCL_DEBUG(4, "retoncls=%d stseq=%d\n", *retonclosep,
-		    (int)stateidp->seqid);
-		stateidp->other[0] = *tl++;
-		stateidp->other[1] = *tl++;
-		stateidp->other[2] = *tl++;
-		cnt = fxdr_unsigned(int, *tl);
-		NFSCL_DEBUG(4, "layg cnt=%d\n", cnt);
-		if (cnt <= 0 || cnt > 10000) {
-			/* Don't accept more than 10000 layouts in reply. */
-			error = NFSERR_BADXDR;
-			goto nfsmout;
-		}
-		for (i = 0; i < cnt; i++) {
-			/* Dissect all the way to the file handle cnt. */
-			NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_HYPER +
-			    6 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
-			fhcnt = fxdr_unsigned(int, *(tl + 11 +
-			    NFSX_V4DEVICEID / NFSX_UNSIGNED));
-			NFSCL_DEBUG(4, "fhcnt=%d\n", fhcnt);
-			if (fhcnt < 0 || fhcnt > 100) {
-				/* Don't accept more than 100 file handles. */
-				error = NFSERR_BADXDR;
-				goto nfsmout;
-			}
-			if (fhcnt > 1)
-				flp = malloc(sizeof(*flp) + (fhcnt - 1) *
-				    sizeof(struct nfsfh *),
-				    M_NFSFLAYOUT, M_WAITOK);
-			else
-				flp = malloc(sizeof(*flp),
-				    M_NFSFLAYOUT, M_WAITOK);
-			flp->nfsfl_flags = 0;
-			flp->nfsfl_fhcnt = 0;
-			flp->nfsfl_devp = NULL;
-			flp->nfsfl_off = fxdr_hyper(tl); tl += 2;
-			retlen = fxdr_hyper(tl); tl += 2;
-			if (flp->nfsfl_off + retlen < flp->nfsfl_off)
-				flp->nfsfl_end = UINT64_MAX - flp->nfsfl_off;
-			else
-				flp->nfsfl_end = flp->nfsfl_off + retlen;
-			flp->nfsfl_iomode = fxdr_unsigned(int, *tl++);
-			if (gotiomode == -1)
-				gotiomode = flp->nfsfl_iomode;
-			NFSCL_DEBUG(4, "layg reqiom=%d retiom=%d\n", iomode,
-			    (int)flp->nfsfl_iomode);
-			if (fxdr_unsigned(int, *tl++) !=
-			    NFSLAYOUT_NFSV4_1_FILES) {
-				printf("NFSv4.1: got non-files layout\n");
-				error = NFSERR_BADXDR;
-				goto nfsmout;
-			}
-			NFSBCOPY(++tl, flp->nfsfl_dev, NFSX_V4DEVICEID);
-			tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
-			flp->nfsfl_util = fxdr_unsigned(uint32_t, *tl++);
-			NFSCL_DEBUG(4, "flutil=0x%x\n", flp->nfsfl_util);
-			flp->nfsfl_stripe1 = fxdr_unsigned(uint32_t, *tl++);
-			flp->nfsfl_patoff = fxdr_hyper(tl); tl += 2;
-			if (fxdr_unsigned(int, *tl) != fhcnt) {
-				printf("EEK! bad fhcnt\n");
-				error = NFSERR_BADXDR;
-				goto nfsmout;
-			}
-			for (j = 0; j < fhcnt; j++) {
-				NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
-				nfhlen = fxdr_unsigned(int, *tl);
-				if (nfhlen <= 0 || nfhlen > NFSX_V4FHMAX) {
-					error = NFSERR_BADXDR;
-					goto nfsmout;
-				}
-				nfhp = malloc(sizeof(*nfhp) + nfhlen - 1,
-				    M_NFSFH, M_WAITOK);
-				flp->nfsfl_fh[j] = nfhp;
-				flp->nfsfl_fhcnt++;
-				nfhp->nfh_len = nfhlen;
-				NFSM_DISSECT(cp, uint8_t *, NFSM_RNDUP(nfhlen));
-				NFSBCOPY(cp, nfhp->nfh_fh, nfhlen);
-			}
-			if (flp->nfsfl_iomode == gotiomode) {
-				/* Keep the list in increasing offset order. */
-				tflp = LIST_FIRST(flhp);
-				prevflp = NULL;
-				while (tflp != NULL &&
-				    tflp->nfsfl_off < flp->nfsfl_off) {
-					prevflp = tflp;
-					tflp = LIST_NEXT(tflp, nfsfl_list);
-				}
-				if (prevflp == NULL)
-					LIST_INSERT_HEAD(flhp, flp, nfsfl_list);
-				else
-					LIST_INSERT_AFTER(prevflp, flp,
-					    nfsfl_list);
-			} else {
-				printf("nfscl_layoutget(): got wrong iomode\n");
-				nfscl_freeflayout(flp);
-			}
-			flp = NULL;
-		}
-	}
-	if (nd->nd_repstat != 0 && error == 0)
+	if (nd->nd_repstat == 0)
+		error = nfsrv_parselayoutget(nd, stateidp, retonclosep, flhp);
+	if (error == 0 && nd->nd_repstat != 0)
 		error = nd->nd_repstat;
-nfsmout:
-	if (error != 0 && flp != NULL)
-		nfscl_freeflayout(flp);
 	mbuf_freem(nd->nd_mrep);
 	return (error);
 }
@@ -5228,8 +5146,7 @@ nfsrpc_getlayout(struct nfsmount *nmp, vnode_t vp, str
     struct nfscllayout **lypp, struct ucred *cred, NFSPROC_T *p)
 {
 	struct nfscllayout *lyp;
-	struct nfsclflayout *flp, *tflp;
-	struct nfscldevinfo *dip;
+	struct nfsclflayout *flp;
 	struct nfsclflayouthead flh;
 	int error = 0, islocked, layoutlen, recalled, retonclose;
 	nfsv4stateid_t stateid;
@@ -5271,35 +5188,13 @@ nfsrpc_getlayout(struct nfsmount *nmp, vnode_t vp, str
 			    (uint64_t)0, layoutlen, &stateid, &retonclose,
 			    &flh, cred, p, NULL);
 		}
+		error = nfsrpc_layoutgetres(nmp, vp, nfhp->nfh_fh,
+		    nfhp->nfh_len, &stateid, retonclose, notifybitsp, &lyp,
+		    &flh, error, NULL, cred, p);
 		if (error == 0)
-			LIST_FOREACH(tflp, &flh, nfsfl_list) {
-				error = nfscl_adddevinfo(nmp, NULL, tflp);
-				if (error != 0) {
-					error = nfsrpc_getdeviceinfo(nmp,
-					    tflp->nfsfl_dev,
-					    NFSLAYOUT_NFSV4_1_FILES,
-					    notifybitsp, &dip, cred, p);
-					if (error != 0)
-						break;
-					error = nfscl_adddevinfo(nmp, dip,
-					    tflp);
-					if (error != 0)
-						printf(
-						    "getlayout: cannot add\n");
-				}
-			}
-		if (error == 0) {
-			/*
-			 * nfscl_layout() always returns with the nfsly_lock
-			 * set to a refcnt (shared lock).
-			 */
-			error = nfscl_layout(nmp, vp, nfhp->nfh_fh,
-			    nfhp->nfh_len, &stateid, retonclose, &flh, &lyp,
-			    cred, p);
-			if (error == 0)
-				*lypp = lyp;
-		} else if (islocked != 0)
-			nfsv4_unlock(&lyp->nfsly_lock, 0);
+			*lypp = lyp;
+		else if (islocked != 0)
+			nfscl_rellayout(lyp, 1);
 	} else
 		*lypp = lyp;
 	return (error);
@@ -6021,4 +5916,756 @@ nfsmout:
 	return (error);
 }
 #endif
+
+/*
+ * Set up the XDR arguments for the LayoutGet operation.
+ */
+static void
+nfsrv_setuplayoutget(struct nfsrv_descript *nd, int iomode, uint64_t offset,
+    uint64_t len, uint64_t minlen, nfsv4stateid_t *stateidp, int layoutlen,
+    int usecurstateid)
+{
+	uint32_t *tl;
+
+	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
+	    NFSX_STATEID);
+	*tl++ = newnfs_false;		/* Don't signal availability. */
+	*tl++ = txdr_unsigned(NFSLAYOUT_NFSV4_1_FILES);
+	*tl++ = txdr_unsigned(iomode);
+	txdr_hyper(offset, tl);
+	tl += 2;
+	txdr_hyper(len, tl);
+	tl += 2;
+	txdr_hyper(minlen, tl);
+	tl += 2;
+	if (usecurstateid != 0) {
+		/* Special stateid for Current stateid. */
+		*tl++ = txdr_unsigned(1);
+		*tl++ = 0;
+		*tl++ = 0;
+		*tl++ = 0;
+	} else {
+		*tl++ = txdr_unsigned(stateidp->seqid);
+		NFSCL_DEBUG(4, "layget seq=%d\n", (int)stateidp->seqid);
+		*tl++ = stateidp->other[0];
+		*tl++ = stateidp->other[1];
+		*tl++ = stateidp->other[2];
+	}
+	*tl = txdr_unsigned(layoutlen);
+}
+
+/*
+ * Parse the reply for a successful LayoutGet operation.
+ */
+static int
+nfsrv_parselayoutget(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp,
+    int *retonclosep, struct nfsclflayouthead *flhp)
+{
+	uint32_t *tl;
+	struct nfsclflayout *flp, *prevflp, *tflp;
+	int cnt, error, gotiomode, fhcnt, nfhlen, i, j;
+	uint64_t retlen;
+	struct nfsfh *nfhp;
+	uint8_t *cp;
+
+	error = 0;
+	flp = NULL;
+	gotiomode = -1;
+	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + NFSX_STATEID);
+	if (*tl++ != 0)
+		*retonclosep = 1;
+	else
+		*retonclosep = 0;
+	stateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
+	NFSCL_DEBUG(4, "retoncls=%d stseq=%d\n", *retonclosep,
+	    (int)stateidp->seqid);
+	stateidp->other[0] = *tl++;
+	stateidp->other[1] = *tl++;
+	stateidp->other[2] = *tl++;
+	cnt = fxdr_unsigned(int, *tl);
+	NFSCL_DEBUG(4, "layg cnt=%d\n", cnt);
+	if (cnt <= 0 || cnt > 10000) {
+		/* Don't accept more than 10000 layouts in reply. */
+		error = NFSERR_BADXDR;
+		goto nfsmout;
+	}
+	for (i = 0; i < cnt; i++) {
+		/* Dissect all the way to the file handle cnt. */
+		NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_HYPER +
+		    6 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
+		fhcnt = fxdr_unsigned(int, *(tl + 11 +
+		    NFSX_V4DEVICEID / NFSX_UNSIGNED));
+		NFSCL_DEBUG(4, "fhcnt=%d\n", fhcnt);
+		if (fhcnt < 0 || fhcnt > 100) {
+			/* Don't accept more than 100 file handles. */
+			error = NFSERR_BADXDR;
+			goto nfsmout;
+		}
+		if (fhcnt > 1)
+			flp = malloc(sizeof(*flp) + (fhcnt - 1) *
+			    sizeof(struct nfsfh *), M_NFSFLAYOUT, M_WAITOK);
+		else
+			flp = malloc(sizeof(*flp), M_NFSFLAYOUT, M_WAITOK);
+		flp->nfsfl_flags = 0;
+		flp->nfsfl_fhcnt = 0;
+		flp->nfsfl_devp = NULL;
+		flp->nfsfl_off = fxdr_hyper(tl); tl += 2;
+		retlen = fxdr_hyper(tl); tl += 2;
+		if (flp->nfsfl_off + retlen < flp->nfsfl_off)
+			flp->nfsfl_end = UINT64_MAX - flp->nfsfl_off;
+		else
+			flp->nfsfl_end = flp->nfsfl_off + retlen;
+		flp->nfsfl_iomode = fxdr_unsigned(int, *tl++);
+		if (gotiomode == -1)
+			gotiomode = flp->nfsfl_iomode;
+		if (fxdr_unsigned(int, *tl++) != NFSLAYOUT_NFSV4_1_FILES) {
+			printf("NFSv4.1: got non-files layout\n");
+			error = NFSERR_BADXDR;
+			goto nfsmout;
+		}
+		NFSBCOPY(++tl, flp->nfsfl_dev, NFSX_V4DEVICEID);
+		tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
+		flp->nfsfl_util = fxdr_unsigned(uint32_t, *tl++);
+		NFSCL_DEBUG(4, "flutil=0x%x\n", flp->nfsfl_util);
+		flp->nfsfl_stripe1 = fxdr_unsigned(uint32_t, *tl++);
+		flp->nfsfl_patoff = fxdr_hyper(tl); tl += 2;
+		if (fxdr_unsigned(int, *tl) != fhcnt) {
+			printf("EEK! bad fhcnt\n");
+			error = NFSERR_BADXDR;
+			goto nfsmout;
+		}
+		for (j = 0; j < fhcnt; j++) {
+			NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+			nfhlen = fxdr_unsigned(int, *tl);
+			if (nfhlen <= 0 || nfhlen > NFSX_V4FHMAX) {
+				error = NFSERR_BADXDR;
+				goto nfsmout;
+			}
+			nfhp = malloc(sizeof(*nfhp) + nfhlen - 1, M_NFSFH,
+			    M_WAITOK);
+			flp->nfsfl_fh[j] = nfhp;
+			flp->nfsfl_fhcnt++;
+			nfhp->nfh_len = nfhlen;
+			NFSM_DISSECT(cp, uint8_t *, NFSM_RNDUP(nfhlen));
+			NFSBCOPY(cp, nfhp->nfh_fh, nfhlen);
+		}
+		if (flp->nfsfl_iomode == gotiomode) {
+			/* Keep the list in increasing offset order. */
+			tflp = LIST_FIRST(flhp);
+			prevflp = NULL;
+			while (tflp != NULL &&
+			    tflp->nfsfl_off < flp->nfsfl_off) {
+				prevflp = tflp;
+				tflp = LIST_NEXT(tflp, nfsfl_list);
+			}
+			if (prevflp == NULL)
+				LIST_INSERT_HEAD(flhp, flp, nfsfl_list);
+			else
+				LIST_INSERT_AFTER(prevflp, flp,
+				    nfsfl_list);
+		} else {
+			printf("nfscl_layoutget(): got wrong iomode\n");
+			nfscl_freeflayout(flp);
+		}
+		flp = NULL;
+	}
+nfsmout:
+	if (error != 0 && flp != NULL)
+		nfscl_freeflayout(flp);
+	return (error);
+}
+
+/*
+ * Similar to nfsrpc_getlayout(), except that it uses nfsrpc_openlayget(),
+ * so that it does both an Open and a Layoutget.
+ */
+static int
+nfsrpc_getopenlayout(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp,
+    int fhlen, uint8_t *newfhp, int newfhlen, uint32_t mode,
+    struct nfsclopen *op, uint8_t *name, int namelen, struct nfscldeleg **dpp,
+    struct ucred *cred, NFSPROC_T *p)
+{
+	struct nfscllayout *lyp;
+	struct nfsclflayout *flp;
+	struct nfsclflayouthead flh;
+	int error, islocked, layoutlen, recalled, retonclose, usecurstateid;
+	int laystat;
+	nfsv4stateid_t stateid;
+	struct nfsclsession *tsep;
+
+	error = 0;
+	/*
+	 * If lyp is returned non-NULL, there will be a refcnt (shared lock)
+	 * on it, iff flp != NULL or a lock (exclusive lock) on it iff
+	 * flp == NULL.
+	 */
+	lyp = nfscl_getlayout(nmp->nm_clp, newfhp, newfhlen, 0, &flp,
+	    &recalled);
+	NFSCL_DEBUG(4, "nfsrpc_getopenlayout nfscl_getlayout lyp=%p\n", lyp);
+	if (lyp == NULL)
+		islocked = 0;
+	else if (flp != NULL)
+		islocked = 1;
+	else
+		islocked = 2;
+	if ((lyp == NULL || flp == NULL) && recalled == 0) {
+		LIST_INIT(&flh);
+		tsep = nfsmnt_mdssession(nmp);
+		layoutlen = tsep->nfsess_maxcache - (NFSX_STATEID +
+		    3 * NFSX_UNSIGNED);
+		if (lyp == NULL)
+			usecurstateid = 1;
+		else {
+			usecurstateid = 0;
+			stateid.seqid = lyp->nfsly_stateid.seqid;
+			stateid.other[0] = lyp->nfsly_stateid.other[0];
+			stateid.other[1] = lyp->nfsly_stateid.other[1];
+			stateid.other[2] = lyp->nfsly_stateid.other[2];
+		}
+		error = nfsrpc_openlayoutrpc(nmp, vp, nfhp, fhlen,
+		    newfhp, newfhlen, mode, op, name, namelen,
+		    dpp, &stateid, usecurstateid, layoutlen,
+		    &retonclose, &flh, &laystat, cred, p);
+		NFSCL_DEBUG(4, "aft nfsrpc_openlayoutrpc laystat=%d err=%d\n",
+		    laystat, error);
+		laystat = nfsrpc_layoutgetres(nmp, vp, newfhp, newfhlen,
+		    &stateid, retonclose, NULL, &lyp, &flh, laystat, &islocked,
+		    cred, p);
+	} else
+		error = nfsrpc_openrpc(nmp, vp, nfhp, fhlen, newfhp, newfhlen,
+		    mode, op, name, namelen, dpp, 0, 0, cred, p, 0, 0);
+	if (islocked == 2)
+		nfscl_rellayout(lyp, 1);
+	else if (islocked == 1)
+		nfscl_rellayout(lyp, 0);
+	return (error);
+}
+
+/*
+ * This function does an Open+LayoutGet for an NFSv4.1 mount with pNFS
+ * enabled, only for the CLAIM_NULL case.  All other NFSv4 Opens are
+ * handled by nfsrpc_openrpc().
+ * For the case where op == NULL, dvp is the directory.  When op != NULL, it
+ * can be NULL.
+ */
+static int
+nfsrpc_openlayoutrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp,
+    int fhlen, uint8_t *newfhp, int newfhlen, uint32_t mode,
+    struct nfsclopen *op, uint8_t *name, int namelen, struct nfscldeleg **dpp,
+    nfsv4stateid_t *stateidp, int usecurstateid,
+    int layoutlen, int *retonclosep, struct nfsclflayouthead *flhp,
+    int *laystatp, struct ucred *cred, NFSPROC_T *p)
+{
+	uint32_t *tl;
+	struct nfsrv_descript nfsd, *nd = &nfsd;
+	struct nfscldeleg *ndp = NULL;
+	struct nfsvattr nfsva;
+	struct nfsclsession *tsep;
+	uint32_t rflags, deleg;
+	nfsattrbit_t attrbits;
+	int error, ret, acesize, limitby, iomode;
+
+	*dpp = NULL;
+	*laystatp = ENXIO;
+	nfscl_reqstart(nd, NFSPROC_OPENLAYGET, nmp, nfhp, fhlen, NULL, NULL);
+	NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
+	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
+	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
+	*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
+	tsep = nfsmnt_mdssession(nmp);
+	*tl++ = tsep->nfsess_clientid.lval[0];
+	*tl = tsep->nfsess_clientid.lval[1];
+	nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
+	NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+	*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
+	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
+	nfsm_strtom(nd, name, namelen);
+	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OP_GETATTR);
+	NFSZERO_ATTRBIT(&attrbits);
+	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
+	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY);
+	nfsrv_putattrbit(nd, &attrbits);
+	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OP_LAYOUTGET);
+	if ((mode & NFSV4OPEN_ACCESSWRITE) != 0)
+		iomode = NFSLAYOUTIOMODE_RW;
+	else
+		iomode = NFSLAYOUTIOMODE_READ;
+	nfsrv_setuplayoutget(nd, iomode, 0, UINT64_MAX, 0, stateidp,
+	    layoutlen, usecurstateid);
+	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
+	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
+	if (error != 0)
+		return (error);
+	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
+	if (nd->nd_repstat != 0)
+		*laystatp = nd->nd_repstat;
+	if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+		/* ND_NOMOREDATA will be set if the Open operation failed. */
+		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
+		    6 * NFSX_UNSIGNED);
+		op->nfso_stateid.seqid = *tl++;
+		op->nfso_stateid.other[0] = *tl++;
+		op->nfso_stateid.other[1] = *tl++;
+		op->nfso_stateid.other[2] = *tl;
+		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
+		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
+		if (error != 0)
+			goto nfsmout;
+		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+		deleg = fxdr_unsigned(u_int32_t, *tl);
+		if (deleg == NFSV4OPEN_DELEGATEREAD ||
+		    deleg == NFSV4OPEN_DELEGATEWRITE) {
+			if (!(op->nfso_own->nfsow_clp->nfsc_flags &
+			      NFSCLFLAGS_FIRSTDELEG))
+				op->nfso_own->nfsow_clp->nfsc_flags |=
+				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
+			ndp = malloc(sizeof(struct nfscldeleg) + newfhlen,
+			    M_NFSCLDELEG, M_WAITOK);
+			LIST_INIT(&ndp->nfsdl_owner);
+			LIST_INIT(&ndp->nfsdl_lock);
+			ndp->nfsdl_clp = op->nfso_own->nfsow_clp;
+			ndp->nfsdl_fhlen = newfhlen;
+			NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen);
+			newnfs_copyincred(cred, &ndp->nfsdl_cred);
+			nfscl_lockinit(&ndp->nfsdl_rwlock);
+			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
+			    NFSX_UNSIGNED);
+			ndp->nfsdl_stateid.seqid = *tl++;
+			ndp->nfsdl_stateid.other[0] = *tl++;
+			ndp->nfsdl_stateid.other[1] = *tl++;
+			ndp->nfsdl_stateid.other[2] = *tl++;
+			ret = fxdr_unsigned(int, *tl);
+			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
+				ndp->nfsdl_flags = NFSCLDL_WRITE;
+				/*
+				 * Indicates how much the file can grow.
+				 */
+				NFSM_DISSECT(tl, u_int32_t *,
+				    3 * NFSX_UNSIGNED);
+				limitby = fxdr_unsigned(int, *tl++);
+				switch (limitby) {
+				case NFSV4OPEN_LIMITSIZE:
+					ndp->nfsdl_sizelimit = fxdr_hyper(tl);
+					break;
+				case NFSV4OPEN_LIMITBLOCKS:
+					ndp->nfsdl_sizelimit =
+					    fxdr_unsigned(u_int64_t, *tl++);
+					ndp->nfsdl_sizelimit *=
+					    fxdr_unsigned(u_int64_t, *tl);
+					break;
+				default:
+					error = NFSERR_BADXDR;
+					goto nfsmout;
+				};
+			} else
+				ndp->nfsdl_flags = NFSCLDL_READ;
+			if (ret != 0)
+				ndp->nfsdl_flags |= NFSCLDL_RECALL;
+			error = nfsrv_dissectace(nd, &ndp->nfsdl_ace, &ret,
+			    &acesize, p);
+			if (error != 0)
+				goto nfsmout;
+		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
+			error = NFSERR_BADXDR;
+			goto nfsmout;
+		}
+		if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) != 0 ||
+		    nfscl_assumeposixlocks)
+			op->nfso_posixlock = 1;
+		else
+			op->nfso_posixlock = 0;
+		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+		/* If the 2nd element == NFS_OK, the Getattr succeeded. */
+		if (*++tl == 0) {
+			error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
+			    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
+			    NULL, NULL, NULL, p, cred);
+			if (error != 0)
+				goto nfsmout;
+			if (ndp != NULL) {
+				ndp->nfsdl_change = nfsva.na_filerev;
+				ndp->nfsdl_modtime = nfsva.na_mtime;
+				ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
+				*dpp = ndp;
+				ndp = NULL;
+			}
+			/*
+			 * At this point, the Open has succeeded, so set
+			 * nd_repstat = NFS_OK.  If the Layoutget failed,
+			 * this function just won't return a layout.
+			 */
+			if (nd->nd_repstat == 0) {
+				NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+				*laystatp = fxdr_unsigned(int, *++tl);
+				if (*laystatp == 0) {
+					error = nfsrv_parselayoutget(nd,
+					    stateidp, retonclosep, flhp);
+					if (error != 0)
+						*laystatp = error;
+				}
+			} else
+				nd->nd_repstat = 0;	/* Return 0 for Open. */
+		}
+	}
+	if (nd->nd_repstat != 0 && error == 0)
+		error = nd->nd_repstat;
+nfsmout:
+	free(ndp, M_NFSCLDELEG);
+	mbuf_freem(nd->nd_mrep);
+	return (error);
+}
+
+/*
+ * Similar nfsrpc_createv4(), but also does the LayoutGet operation.
+ * Used only for mounts with pNFS enabled.
+ */
+static int
+nfsrpc_createlayout(vnode_t dvp, char *name, int namelen, struct vattr *vap,
+    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
+    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
+    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
+    int *dattrflagp, void *dstuff, int *unlockedp, nfsv4stateid_t *stateidp,
+    int usecurstateid, int layoutlen, int *retonclosep,
+    struct nfsclflayouthead *flhp, int *laystatp)
+{
+	uint32_t *tl;
+	int error = 0, deleg, newone, ret, acesize, limitby;
+	struct nfsrv_descript nfsd, *nd = &nfsd;
+	struct nfsclopen *op;
+	struct nfscldeleg *dp = NULL;
+	struct nfsnode *np;
+	struct nfsfh *nfhp;
+	struct nfsclsession *tsep;
+	nfsattrbit_t attrbits;
+	nfsv4stateid_t stateid;
+	uint32_t rflags;
+	struct nfsmount *nmp;
+
+	nmp = VFSTONFS(dvp->v_mount);
+	np = VTONFS(dvp);
+	*laystatp = ENXIO;
+	*unlockedp = 0;
+	*nfhpp = NULL;
+	*dpp = NULL;
+	*attrflagp = 0;
+	*dattrflagp = 0;
+	if (namelen > NFS_MAXNAMLEN)
+		return (ENAMETOOLONG);
+	NFSCL_REQSTART(nd, NFSPROC_CREATELAYGET, dvp);
+	/*
+	 * For V4, this is actually an Open op.
+	 */
+	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+	*tl++ = txdr_unsigned(owp->nfsow_seqid);
+	*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
+	    NFSV4OPEN_ACCESSREAD);
+	*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
+	tsep = nfsmnt_mdssession(nmp);
+	*tl++ = tsep->nfsess_clientid.lval[0];
+	*tl = tsep->nfsess_clientid.lval[1];
+	nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
+	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+	*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
+	if ((fmode & O_EXCL) != 0) {
+		if (NFSHASSESSPERSIST(nmp)) {
+			/* Use GUARDED for persistent sessions. */
+			*tl = txdr_unsigned(NFSCREATE_GUARDED);
+			nfscl_fillsattr(nd, vap, dvp, 0, 0);
+		} else {
+			/* Otherwise, use EXCLUSIVE4_1. */
+			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE41);
+			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
+			*tl++ = cverf.lval[0];
+			*tl = cverf.lval[1];
+			nfscl_fillsattr(nd, vap, dvp, 0, 0);
+		}
+	} else {
+		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
+		nfscl_fillsattr(nd, vap, dvp, 0, 0);
+	}
+	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
+	nfsm_strtom(nd, name, namelen);
+	/* Get the new file's handle and attributes, plus save the FH. */
+	NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+	*tl++ = txdr_unsigned(NFSV4OP_SAVEFH);
+	*tl++ = txdr_unsigned(NFSV4OP_GETFH);
+	*tl = txdr_unsigned(NFSV4OP_GETATTR);
+	NFSGETATTR_ATTRBIT(&attrbits);
+	nfsrv_putattrbit(nd, &attrbits);
+	/* Get the directory's post-op attributes. */
+	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OP_PUTFH);
+	nfsm_fhtom(nd, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 0);
+	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OP_GETATTR);
+	nfsrv_putattrbit(nd, &attrbits);
+	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+	*tl++ = txdr_unsigned(NFSV4OP_RESTOREFH);
+	*tl = txdr_unsigned(NFSV4OP_LAYOUTGET);
+	nfsrv_setuplayoutget(nd, NFSLAYOUTIOMODE_RW, 0, UINT64_MAX, 0, stateidp,
+	    layoutlen, usecurstateid);
+	error = nfscl_request(nd, dvp, p, cred, dstuff);
+	if (error != 0)
+		return (error);
+	NFSCL_DEBUG(4, "nfsrpc_createlayout stat=%d err=%d\n", nd->nd_repstat,
+	    error);
+	if (nd->nd_repstat != 0)
+		*laystatp = nd->nd_repstat;
+	NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
+	if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+		NFSCL_DEBUG(4, "nfsrpc_createlayout open succeeded\n");
+		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
+		    6 * NFSX_UNSIGNED);
+		stateid.seqid = *tl++;
+		stateid.other[0] = *tl++;
+		stateid.other[1] = *tl++;
+		stateid.other[2] = *tl;
+		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
+		nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
+		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+		deleg = fxdr_unsigned(int, *tl);
+		if (deleg == NFSV4OPEN_DELEGATEREAD ||
+		    deleg == NFSV4OPEN_DELEGATEWRITE) {
+			if (!(owp->nfsow_clp->nfsc_flags &
+			      NFSCLFLAGS_FIRSTDELEG))
+				owp->nfsow_clp->nfsc_flags |=
+				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
+			dp = malloc(sizeof(struct nfscldeleg) + NFSX_V4FHMAX,
+			    M_NFSCLDELEG, M_WAITOK);
+			LIST_INIT(&dp->nfsdl_owner);
+			LIST_INIT(&dp->nfsdl_lock);
+			dp->nfsdl_clp = owp->nfsow_clp;
+			newnfs_copyincred(cred, &dp->nfsdl_cred);
+			nfscl_lockinit(&dp->nfsdl_rwlock);
+			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
+			    NFSX_UNSIGNED);
+			dp->nfsdl_stateid.seqid = *tl++;
+			dp->nfsdl_stateid.other[0] = *tl++;
+			dp->nfsdl_stateid.other[1] = *tl++;
+			dp->nfsdl_stateid.other[2] = *tl++;
+			ret = fxdr_unsigned(int, *tl);
+			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
+				dp->nfsdl_flags = NFSCLDL_WRITE;
+				/*
+				 * Indicates how much the file can grow.
+				 */
+				NFSM_DISSECT(tl, u_int32_t *,
+				    3 * NFSX_UNSIGNED);
+				limitby = fxdr_unsigned(int, *tl++);
+				switch (limitby) {
+				case NFSV4OPEN_LIMITSIZE:
+					dp->nfsdl_sizelimit = fxdr_hyper(tl);
+					break;
+				case NFSV4OPEN_LIMITBLOCKS:
+					dp->nfsdl_sizelimit =
+					    fxdr_unsigned(u_int64_t, *tl++);
+					dp->nfsdl_sizelimit *=
+					    fxdr_unsigned(u_int64_t, *tl);
+					break;
+				default:
+					error = NFSERR_BADXDR;
+					goto nfsmout;
+				};
+			} else {
+				dp->nfsdl_flags = NFSCLDL_READ;
+			}
+			if (ret != 0)
+				dp->nfsdl_flags |= NFSCLDL_RECALL;
+			error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
+			    &acesize, p);
+			if (error != 0)
+				goto nfsmout;
+		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
+			error = NFSERR_BADXDR;
+			goto nfsmout;
+		}
+
+		/* Now, we should have the status for the SaveFH. */
+		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+		if (*++tl == 0) {
+			NFSCL_DEBUG(4, "nfsrpc_createlayout SaveFH ok\n");
+			/*
+			 * Now, process the GetFH and Getattr for the newly
+			 * created file. nfscl_mtofh() will set
+			 * ND_NOMOREDATA if these weren't successful.
+			 */
+			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
+			NFSCL_DEBUG(4, "aft nfscl_mtofh err=%d\n", error);
+			if (error != 0)
+				goto nfsmout;
+		} else
+			nd->nd_flag |= ND_NOMOREDATA;
+		/* Now we have the PutFH and Getattr for the directory. */
+		if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+			NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+			if (*++tl != 0)
+				nd->nd_flag |= ND_NOMOREDATA;
+			else {
+				NFSM_DISSECT(tl, uint32_t *, 2 *
+				    NFSX_UNSIGNED);
+				if (*++tl != 0)
+					nd->nd_flag |= ND_NOMOREDATA;
+			}
+		}
+		if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+			/* Load the directory attributes. */
+			error = nfsm_loadattr(nd, dnap);
+			NFSCL_DEBUG(4, "aft nfsm_loadattr err=%d\n", error);
+			if (error != 0)
+				goto nfsmout;
+			*dattrflagp = 1;
+			if (dp != NULL && *attrflagp != 0) {
+				dp->nfsdl_change = nnap->na_filerev;
+				dp->nfsdl_modtime = nnap->na_mtime;
+				dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
+			}
+			/*
+			 * We can now complete the Open state.
+			 */
+			nfhp = *nfhpp;
+			if (dp != NULL) {
+				dp->nfsdl_fhlen = nfhp->nfh_len;
+				NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh,
+				    nfhp->nfh_len);
+			}
+			/*
+			 * Get an Open structure that will be
+			 * attached to the OpenOwner, acquired already.
+			 */
+			error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len, 
+			    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
+			    cred, p, NULL, &op, &newone, NULL, 0);
+			if (error != 0)
+				goto nfsmout;
+			op->nfso_stateid = stateid;
+			newnfs_copyincred(cred, &op->nfso_cred);
+	
+			nfscl_openrelease(nmp, op, error, newone);
+			*unlockedp = 1;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201707142047.v6EKlgWq070971>