nfs_clrpcops.c revision 321031
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: stable/10/sys/fs/nfsclient/nfs_clrpcops.c 321031 2017-07-15 19:24:54Z rmacklem $");
36
37/*
38 * Rpc op calls, generally called from the vnode op calls or through the
39 * buffer cache, for NFS v2, 3 and 4.
40 * These do not normally make any changes to vnode arguments or use
41 * structures that might change between the VFS variants. The returned
42 * arguments are all at the end, after the NFSPROC_T *p one.
43 */
44
45#ifndef APPLEKEXT
46#include "opt_inet6.h"
47
48#include <fs/nfs/nfsport.h>
49#include <sys/sysctl.h>
50
51SYSCTL_DECL(_vfs_nfs);
52
53static int	nfsignore_eexist = 0;
54SYSCTL_INT(_vfs_nfs, OID_AUTO, ignore_eexist, CTLFLAG_RW,
55    &nfsignore_eexist, 0, "NFS ignore EEXIST replies for mkdir/symlink");
56
57/*
58 * Global variables
59 */
60extern int nfs_numnfscbd;
61extern struct timeval nfsboottime;
62extern u_int32_t newnfs_false, newnfs_true;
63extern nfstype nfsv34_type[9];
64extern int nfsrv_useacl;
65extern char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
66extern int nfscl_debuglevel;
67NFSCLSTATEMUTEX;
68int nfstest_outofseq = 0;
69int nfscl_assumeposixlocks = 1;
70int nfscl_enablecallb = 0;
71short nfsv4_cbport = NFSV4_CBPORT;
72int nfstest_openallsetattr = 0;
73#endif	/* !APPLEKEXT */
74
75#define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
76
77/*
78 * nfscl_getsameserver() can return one of three values:
79 * NFSDSP_USETHISSESSION - Use this session for the DS.
80 * NFSDSP_SEQTHISSESSION - Use the nfsclds_sequence field of this dsp for new
81 *     session.
82 * NFSDSP_NOTFOUND - No matching server was found.
83 */
84enum nfsclds_state {
85	NFSDSP_USETHISSESSION = 0,
86	NFSDSP_SEQTHISSESSION = 1,
87	NFSDSP_NOTFOUND = 2,
88};
89
90static int nfsrpc_setattrrpc(vnode_t , struct vattr *, nfsv4stateid_t *,
91    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
92static int nfsrpc_readrpc(vnode_t , struct uio *, struct ucred *,
93    nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *, void *);
94static int nfsrpc_writerpc(vnode_t , struct uio *, int *, int *,
95    struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *,
96    void *);
97static int nfsrpc_createv23(vnode_t , char *, int, struct vattr *,
98    nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *,
99    struct nfsvattr *, struct nfsfh **, int *, int *, void *);
100static int nfsrpc_createv4(vnode_t , char *, int, struct vattr *,
101    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **, struct ucred *,
102    NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *,
103    int *, void *, int *);
104static int nfsrpc_locku(struct nfsrv_descript *, struct nfsmount *,
105    struct nfscllockowner *, u_int64_t, u_int64_t,
106    u_int32_t, struct ucred *, NFSPROC_T *, int);
107static int nfsrpc_setaclrpc(vnode_t, struct ucred *, NFSPROC_T *,
108    struct acl *, nfsv4stateid_t *, void *);
109static int nfsrpc_getlayout(struct nfsmount *, vnode_t, struct nfsfh *, int,
110    uint32_t *, nfsv4stateid_t *, uint64_t, struct nfscllayout **,
111    struct ucred *, NFSPROC_T *);
112static int nfsrpc_fillsa(struct nfsmount *, struct sockaddr_storage *,
113    struct nfsclds **, NFSPROC_T *);
114static void nfscl_initsessionslots(struct nfsclsession *);
115static int nfscl_doflayoutio(vnode_t, struct uio *, int *, int *, int *,
116    nfsv4stateid_t *, int, struct nfscldevinfo *, struct nfscllayout *,
117    struct nfsclflayout *, uint64_t, uint64_t, int, struct ucred *,
118    NFSPROC_T *);
119static int nfsrpc_readds(vnode_t, struct uio *, nfsv4stateid_t *, int *,
120    struct nfsclds *, uint64_t, int, struct nfsfh *, struct ucred *,
121    NFSPROC_T *);
122static int nfsrpc_writeds(vnode_t, struct uio *, int *, int *,
123    nfsv4stateid_t *, struct nfsclds *, uint64_t, int,
124    struct nfsfh *, int, struct ucred *, NFSPROC_T *);
125static enum nfsclds_state nfscl_getsameserver(struct nfsmount *,
126    struct nfsclds *, struct nfsclds **);
127static int nfsrpc_commitds(vnode_t, uint64_t, int, struct nfsclds *,
128    struct nfsfh *, struct ucred *, NFSPROC_T *);
129
130/*
131 * nfs null call from vfs.
132 */
133APPLESTATIC int
134nfsrpc_null(vnode_t vp, struct ucred *cred, NFSPROC_T *p)
135{
136	int error;
137	struct nfsrv_descript nfsd, *nd = &nfsd;
138
139	NFSCL_REQSTART(nd, NFSPROC_NULL, vp);
140	error = nfscl_request(nd, vp, p, cred, NULL);
141	if (nd->nd_repstat && !error)
142		error = nd->nd_repstat;
143	mbuf_freem(nd->nd_mrep);
144	return (error);
145}
146
147/*
148 * nfs access rpc op.
149 * For nfs version 3 and 4, use the access rpc to check accessibility. If file
150 * modes are changed on the server, accesses might still fail later.
151 */
152APPLESTATIC int
153nfsrpc_access(vnode_t vp, int acmode, struct ucred *cred,
154    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
155{
156	int error;
157	u_int32_t mode, rmode;
158
159	if (acmode & VREAD)
160		mode = NFSACCESS_READ;
161	else
162		mode = 0;
163	if (vnode_vtype(vp) == VDIR) {
164		if (acmode & VWRITE)
165			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND |
166				 NFSACCESS_DELETE);
167		if (acmode & VEXEC)
168			mode |= NFSACCESS_LOOKUP;
169	} else {
170		if (acmode & VWRITE)
171			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
172		if (acmode & VEXEC)
173			mode |= NFSACCESS_EXECUTE;
174	}
175
176	/*
177	 * Now, just call nfsrpc_accessrpc() to do the actual RPC.
178	 */
179	error = nfsrpc_accessrpc(vp, mode, cred, p, nap, attrflagp, &rmode,
180	    NULL);
181
182	/*
183	 * The NFS V3 spec does not clarify whether or not
184	 * the returned access bits can be a superset of
185	 * the ones requested, so...
186	 */
187	if (!error && (rmode & mode) != mode)
188		error = EACCES;
189	return (error);
190}
191
192/*
193 * The actual rpc, separated out for Darwin.
194 */
195APPLESTATIC int
196nfsrpc_accessrpc(vnode_t vp, u_int32_t mode, struct ucred *cred,
197    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, u_int32_t *rmodep,
198    void *stuff)
199{
200	u_int32_t *tl;
201	u_int32_t supported, rmode;
202	int error;
203	struct nfsrv_descript nfsd, *nd = &nfsd;
204	nfsattrbit_t attrbits;
205
206	*attrflagp = 0;
207	supported = mode;
208	NFSCL_REQSTART(nd, NFSPROC_ACCESS, vp);
209	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
210	*tl = txdr_unsigned(mode);
211	if (nd->nd_flag & ND_NFSV4) {
212		/*
213		 * And do a Getattr op.
214		 */
215		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
216		*tl = txdr_unsigned(NFSV4OP_GETATTR);
217		NFSGETATTR_ATTRBIT(&attrbits);
218		(void) nfsrv_putattrbit(nd, &attrbits);
219	}
220	error = nfscl_request(nd, vp, p, cred, stuff);
221	if (error)
222		return (error);
223	if (nd->nd_flag & ND_NFSV3) {
224		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
225		if (error)
226			goto nfsmout;
227	}
228	if (!nd->nd_repstat) {
229		if (nd->nd_flag & ND_NFSV4) {
230			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
231			supported = fxdr_unsigned(u_int32_t, *tl++);
232		} else {
233			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
234		}
235		rmode = fxdr_unsigned(u_int32_t, *tl);
236		if (nd->nd_flag & ND_NFSV4)
237			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
238
239		/*
240		 * It's not obvious what should be done about
241		 * unsupported access modes. For now, be paranoid
242		 * and clear the unsupported ones.
243		 */
244		rmode &= supported;
245		*rmodep = rmode;
246	} else
247		error = nd->nd_repstat;
248nfsmout:
249	mbuf_freem(nd->nd_mrep);
250	return (error);
251}
252
253/*
254 * nfs open rpc
255 */
256APPLESTATIC int
257nfsrpc_open(vnode_t vp, int amode, struct ucred *cred, NFSPROC_T *p)
258{
259	struct nfsclopen *op;
260	struct nfscldeleg *dp;
261	struct nfsfh *nfhp;
262	struct nfsnode *np = VTONFS(vp);
263	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
264	u_int32_t mode, clidrev;
265	int ret, newone, error, expireret = 0, retrycnt;
266
267	/*
268	 * For NFSv4, Open Ops are only done on Regular Files.
269	 */
270	if (vnode_vtype(vp) != VREG)
271		return (0);
272	mode = 0;
273	if (amode & FREAD)
274		mode |= NFSV4OPEN_ACCESSREAD;
275	if (amode & FWRITE)
276		mode |= NFSV4OPEN_ACCESSWRITE;
277	nfhp = np->n_fhp;
278
279	retrycnt = 0;
280#ifdef notdef
281{ char name[100]; int namel;
282namel = (np->n_v4->n4_namelen < 100) ? np->n_v4->n4_namelen : 99;
283bcopy(NFS4NODENAME(np->n_v4), name, namel);
284name[namel] = '\0';
285printf("rpcopen p=0x%x name=%s",p->p_pid,name);
286if (nfhp->nfh_len > 0) printf(" fh=0x%x\n",nfhp->nfh_fh[12]);
287else printf(" fhl=0\n");
288}
289#endif
290	do {
291	    dp = NULL;
292	    error = nfscl_open(vp, nfhp->nfh_fh, nfhp->nfh_len, mode, 1,
293		cred, p, NULL, &op, &newone, &ret, 1);
294	    if (error) {
295		return (error);
296	    }
297	    if (nmp->nm_clp != NULL)
298		clidrev = nmp->nm_clp->nfsc_clientidrev;
299	    else
300		clidrev = 0;
301	    if (ret == NFSCLOPEN_DOOPEN) {
302		if (np->n_v4 != NULL) {
303			error = nfsrpc_openrpc(nmp, vp, np->n_v4->n4_data,
304			   np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
305			   np->n_fhp->nfh_len, mode, op,
306			   NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &dp,
307			   0, 0x0, cred, p, 0, 0);
308			if (dp != NULL) {
309#ifdef APPLE
310				OSBitAndAtomic((int32_t)~NDELEGMOD, (UInt32 *)&np->n_flag);
311#else
312				NFSLOCKNODE(np);
313				np->n_flag &= ~NDELEGMOD;
314				/*
315				 * Invalidate the attribute cache, so that
316				 * attributes that pre-date the issue of a
317				 * delegation are not cached, since the
318				 * cached attributes will remain valid while
319				 * the delegation is held.
320				 */
321				NFSINVALATTRCACHE(np);
322				NFSUNLOCKNODE(np);
323#endif
324				(void) nfscl_deleg(nmp->nm_mountp,
325				    op->nfso_own->nfsow_clp,
326				    nfhp->nfh_fh, nfhp->nfh_len, cred, p, &dp);
327			}
328		} else {
329			error = EIO;
330		}
331		newnfs_copyincred(cred, &op->nfso_cred);
332	    } else if (ret == NFSCLOPEN_SETCRED)
333		/*
334		 * This is a new local open on a delegation. It needs
335		 * to have credentials so that an open can be done
336		 * against the server during recovery.
337		 */
338		newnfs_copyincred(cred, &op->nfso_cred);
339
340	    /*
341	     * nfso_opencnt is the count of how many VOP_OPEN()s have
342	     * been done on this Open successfully and a VOP_CLOSE()
343	     * is expected for each of these.
344	     * If error is non-zero, don't increment it, since the Open
345	     * hasn't succeeded yet.
346	     */
347	    if (!error)
348		op->nfso_opencnt++;
349	    nfscl_openrelease(nmp, op, error, newone);
350	    if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
351		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
352		error == NFSERR_BADSESSION) {
353		(void) nfs_catnap(PZERO, error, "nfs_open");
354	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
355		&& clidrev != 0) {
356		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
357		retrycnt++;
358	    }
359	} while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
360	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
361	    error == NFSERR_BADSESSION ||
362	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
363	     expireret == 0 && clidrev != 0 && retrycnt < 4));
364	if (error && retrycnt >= 4)
365		error = EIO;
366	return (error);
367}
368
369/*
370 * the actual open rpc
371 */
372APPLESTATIC int
373nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
374    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
375    u_int8_t *name, int namelen, struct nfscldeleg **dpp,
376    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p,
377    int syscred, int recursed)
378{
379	u_int32_t *tl;
380	struct nfsrv_descript nfsd, *nd = &nfsd;
381	struct nfscldeleg *dp, *ndp = NULL;
382	struct nfsvattr nfsva;
383	u_int32_t rflags, deleg;
384	nfsattrbit_t attrbits;
385	int error, ret, acesize, limitby;
386	struct nfsclsession *tsep;
387
388	dp = *dpp;
389	*dpp = NULL;
390	nfscl_reqstart(nd, NFSPROC_OPEN, nmp, nfhp, fhlen, NULL, NULL);
391	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
392	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
393	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
394	*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
395	tsep = nfsmnt_mdssession(nmp);
396	*tl++ = tsep->nfsess_clientid.lval[0];
397	*tl = tsep->nfsess_clientid.lval[1];
398	(void) nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
399	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
400	*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
401	if (reclaim) {
402		*tl = txdr_unsigned(NFSV4OPEN_CLAIMPREVIOUS);
403		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
404		*tl = txdr_unsigned(delegtype);
405	} else {
406		if (dp != NULL) {
407			*tl = txdr_unsigned(NFSV4OPEN_CLAIMDELEGATECUR);
408			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
409			if (NFSHASNFSV4N(nmp))
410				*tl++ = 0;
411			else
412				*tl++ = dp->nfsdl_stateid.seqid;
413			*tl++ = dp->nfsdl_stateid.other[0];
414			*tl++ = dp->nfsdl_stateid.other[1];
415			*tl = dp->nfsdl_stateid.other[2];
416		} else {
417			*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
418		}
419		(void) nfsm_strtom(nd, name, namelen);
420	}
421	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
422	*tl = txdr_unsigned(NFSV4OP_GETATTR);
423	NFSZERO_ATTRBIT(&attrbits);
424	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
425	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY);
426	(void) nfsrv_putattrbit(nd, &attrbits);
427	if (syscred)
428		nd->nd_flag |= ND_USEGSSNAME;
429	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
430	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
431	if (error)
432		return (error);
433	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
434	if (!nd->nd_repstat) {
435		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
436		    6 * NFSX_UNSIGNED);
437		op->nfso_stateid.seqid = *tl++;
438		op->nfso_stateid.other[0] = *tl++;
439		op->nfso_stateid.other[1] = *tl++;
440		op->nfso_stateid.other[2] = *tl;
441		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
442		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
443		if (error)
444			goto nfsmout;
445		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
446		deleg = fxdr_unsigned(u_int32_t, *tl);
447		if (deleg == NFSV4OPEN_DELEGATEREAD ||
448		    deleg == NFSV4OPEN_DELEGATEWRITE) {
449			if (!(op->nfso_own->nfsow_clp->nfsc_flags &
450			      NFSCLFLAGS_FIRSTDELEG))
451				op->nfso_own->nfsow_clp->nfsc_flags |=
452				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
453			MALLOC(ndp, struct nfscldeleg *,
454			    sizeof (struct nfscldeleg) + newfhlen,
455			    M_NFSCLDELEG, M_WAITOK);
456			LIST_INIT(&ndp->nfsdl_owner);
457			LIST_INIT(&ndp->nfsdl_lock);
458			ndp->nfsdl_clp = op->nfso_own->nfsow_clp;
459			ndp->nfsdl_fhlen = newfhlen;
460			NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen);
461			newnfs_copyincred(cred, &ndp->nfsdl_cred);
462			nfscl_lockinit(&ndp->nfsdl_rwlock);
463			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
464			    NFSX_UNSIGNED);
465			ndp->nfsdl_stateid.seqid = *tl++;
466			ndp->nfsdl_stateid.other[0] = *tl++;
467			ndp->nfsdl_stateid.other[1] = *tl++;
468			ndp->nfsdl_stateid.other[2] = *tl++;
469			ret = fxdr_unsigned(int, *tl);
470			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
471				ndp->nfsdl_flags = NFSCLDL_WRITE;
472				/*
473				 * Indicates how much the file can grow.
474				 */
475				NFSM_DISSECT(tl, u_int32_t *,
476				    3 * NFSX_UNSIGNED);
477				limitby = fxdr_unsigned(int, *tl++);
478				switch (limitby) {
479				case NFSV4OPEN_LIMITSIZE:
480					ndp->nfsdl_sizelimit = fxdr_hyper(tl);
481					break;
482				case NFSV4OPEN_LIMITBLOCKS:
483					ndp->nfsdl_sizelimit =
484					    fxdr_unsigned(u_int64_t, *tl++);
485					ndp->nfsdl_sizelimit *=
486					    fxdr_unsigned(u_int64_t, *tl);
487					break;
488				default:
489					error = NFSERR_BADXDR;
490					goto nfsmout;
491				};
492			} else {
493				ndp->nfsdl_flags = NFSCLDL_READ;
494			}
495			if (ret)
496				ndp->nfsdl_flags |= NFSCLDL_RECALL;
497			error = nfsrv_dissectace(nd, &ndp->nfsdl_ace, &ret,
498			    &acesize, p);
499			if (error)
500				goto nfsmout;
501		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
502			error = NFSERR_BADXDR;
503			goto nfsmout;
504		}
505		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
506		error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
507		    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
508		    NULL, NULL, NULL, p, cred);
509		if (error)
510			goto nfsmout;
511		if (ndp != NULL) {
512			ndp->nfsdl_change = nfsva.na_filerev;
513			ndp->nfsdl_modtime = nfsva.na_mtime;
514			ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
515		}
516		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM)) {
517		    do {
518			ret = nfsrpc_openconfirm(vp, newfhp, newfhlen, op,
519			    cred, p);
520			if (ret == NFSERR_DELAY)
521			    (void) nfs_catnap(PZERO, ret, "nfs_open");
522		    } while (ret == NFSERR_DELAY);
523		    error = ret;
524		}
525		if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) ||
526		    nfscl_assumeposixlocks)
527		    op->nfso_posixlock = 1;
528		else
529		    op->nfso_posixlock = 0;
530
531		/*
532		 * If the server is handing out delegations, but we didn't
533		 * get one because an OpenConfirm was required, try the
534		 * Open again, to get a delegation. This is a harmless no-op,
535		 * from a server's point of view.
536		 */
537		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM) &&
538		    (op->nfso_own->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG)
539		    && !error && dp == NULL && ndp == NULL && !recursed) {
540		    do {
541			ret = nfsrpc_openrpc(nmp, vp, nfhp, fhlen, newfhp,
542			    newfhlen, mode, op, name, namelen, &ndp, 0, 0x0,
543			    cred, p, syscred, 1);
544			if (ret == NFSERR_DELAY)
545			    (void) nfs_catnap(PZERO, ret, "nfs_open2");
546		    } while (ret == NFSERR_DELAY);
547		    if (ret) {
548			if (ndp != NULL)
549				FREE((caddr_t)ndp, M_NFSCLDELEG);
550			if (ret == NFSERR_STALECLIENTID ||
551			    ret == NFSERR_STALEDONTRECOVER ||
552			    ret == NFSERR_BADSESSION)
553				error = ret;
554		    }
555		}
556	}
557	if (nd->nd_repstat != 0 && error == 0)
558		error = nd->nd_repstat;
559	if (error == NFSERR_STALECLIENTID)
560		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
561nfsmout:
562	if (!error)
563		*dpp = ndp;
564	else if (ndp != NULL)
565		FREE((caddr_t)ndp, M_NFSCLDELEG);
566	mbuf_freem(nd->nd_mrep);
567	return (error);
568}
569
570/*
571 * open downgrade rpc
572 */
573APPLESTATIC int
574nfsrpc_opendowngrade(vnode_t vp, u_int32_t mode, struct nfsclopen *op,
575    struct ucred *cred, NFSPROC_T *p)
576{
577	u_int32_t *tl;
578	struct nfsrv_descript nfsd, *nd = &nfsd;
579	int error;
580
581	NFSCL_REQSTART(nd, NFSPROC_OPENDOWNGRADE, vp);
582	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
583	if (NFSHASNFSV4N(VFSTONFS(vnode_mount(vp))))
584		*tl++ = 0;
585	else
586		*tl++ = op->nfso_stateid.seqid;
587	*tl++ = op->nfso_stateid.other[0];
588	*tl++ = op->nfso_stateid.other[1];
589	*tl++ = op->nfso_stateid.other[2];
590	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
591	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
592	*tl = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
593	error = nfscl_request(nd, vp, p, cred, NULL);
594	if (error)
595		return (error);
596	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
597	if (!nd->nd_repstat) {
598		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
599		op->nfso_stateid.seqid = *tl++;
600		op->nfso_stateid.other[0] = *tl++;
601		op->nfso_stateid.other[1] = *tl++;
602		op->nfso_stateid.other[2] = *tl;
603	}
604	if (nd->nd_repstat && error == 0)
605		error = nd->nd_repstat;
606	if (error == NFSERR_STALESTATEID)
607		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
608nfsmout:
609	mbuf_freem(nd->nd_mrep);
610	return (error);
611}
612
613/*
614 * V4 Close operation.
615 */
616APPLESTATIC int
617nfsrpc_close(vnode_t vp, int doclose, NFSPROC_T *p)
618{
619	struct nfsclclient *clp;
620	int error;
621
622	if (vnode_vtype(vp) != VREG)
623		return (0);
624	if (doclose)
625		error = nfscl_doclose(vp, &clp, p);
626	else
627		error = nfscl_getclose(vp, &clp);
628	if (error)
629		return (error);
630
631	nfscl_clientrelease(clp);
632	return (0);
633}
634
635/*
636 * Close the open.
637 */
638APPLESTATIC void
639nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p)
640{
641	struct nfsrv_descript nfsd, *nd = &nfsd;
642	struct nfscllockowner *lp, *nlp;
643	struct nfscllock *lop, *nlop;
644	struct ucred *tcred;
645	u_int64_t off = 0, len = 0;
646	u_int32_t type = NFSV4LOCKT_READ;
647	int error, do_unlock, trycnt;
648
649	tcred = newnfs_getcred();
650	newnfs_copycred(&op->nfso_cred, tcred);
651	/*
652	 * (Theoretically this could be done in the same
653	 *  compound as the close, but having multiple
654	 *  sequenced Ops in the same compound might be
655	 *  too scary for some servers.)
656	 */
657	if (op->nfso_posixlock) {
658		off = 0;
659		len = NFS64BITSSET;
660		type = NFSV4LOCKT_READ;
661	}
662
663	/*
664	 * Since this function is only called from VOP_INACTIVE(), no
665	 * other thread will be manipulating this Open. As such, the
666	 * lock lists are not being changed by other threads, so it should
667	 * be safe to do this without locking.
668	 */
669	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
670		do_unlock = 1;
671		LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
672			if (op->nfso_posixlock == 0) {
673				off = lop->nfslo_first;
674				len = lop->nfslo_end - lop->nfslo_first;
675				if (lop->nfslo_type == F_WRLCK)
676					type = NFSV4LOCKT_WRITE;
677				else
678					type = NFSV4LOCKT_READ;
679			}
680			if (do_unlock) {
681				trycnt = 0;
682				do {
683					error = nfsrpc_locku(nd, nmp, lp, off,
684					    len, type, tcred, p, 0);
685					if ((nd->nd_repstat == NFSERR_GRACE ||
686					    nd->nd_repstat == NFSERR_DELAY) &&
687					    error == 0)
688						(void) nfs_catnap(PZERO,
689						    (int)nd->nd_repstat,
690						    "nfs_close");
691				} while ((nd->nd_repstat == NFSERR_GRACE ||
692				    nd->nd_repstat == NFSERR_DELAY) &&
693				    error == 0 && trycnt++ < 5);
694				if (op->nfso_posixlock)
695					do_unlock = 0;
696			}
697			nfscl_freelock(lop, 0);
698		}
699		/*
700		 * Do a ReleaseLockOwner.
701		 * The lock owner name nfsl_owner may be used by other opens for
702		 * other files but the lock_owner4 name that nfsrpc_rellockown()
703		 * puts on the wire has the file handle for this file appended
704		 * to it, so it can be done now.
705		 */
706		(void)nfsrpc_rellockown(nmp, lp, lp->nfsl_open->nfso_fh,
707		    lp->nfsl_open->nfso_fhlen, tcred, p);
708	}
709
710	/*
711	 * There could be other Opens for different files on the same
712	 * OpenOwner, so locking is required.
713	 */
714	NFSLOCKCLSTATE();
715	nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
716	NFSUNLOCKCLSTATE();
717	do {
718		error = nfscl_tryclose(op, tcred, nmp, p);
719		if (error == NFSERR_GRACE)
720			(void) nfs_catnap(PZERO, error, "nfs_close");
721	} while (error == NFSERR_GRACE);
722	NFSLOCKCLSTATE();
723	nfscl_lockunlock(&op->nfso_own->nfsow_rwlock);
724
725	LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp)
726		nfscl_freelockowner(lp, 0);
727	nfscl_freeopen(op, 0);
728	NFSUNLOCKCLSTATE();
729	NFSFREECRED(tcred);
730}
731
732/*
733 * The actual Close RPC.
734 */
735APPLESTATIC int
736nfsrpc_closerpc(struct nfsrv_descript *nd, struct nfsmount *nmp,
737    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p,
738    int syscred)
739{
740	u_int32_t *tl;
741	int error;
742
743	nfscl_reqstart(nd, NFSPROC_CLOSE, nmp, op->nfso_fh,
744	    op->nfso_fhlen, NULL, NULL);
745	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
746	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
747	if (NFSHASNFSV4N(nmp))
748		*tl++ = 0;
749	else
750		*tl++ = op->nfso_stateid.seqid;
751	*tl++ = op->nfso_stateid.other[0];
752	*tl++ = op->nfso_stateid.other[1];
753	*tl = op->nfso_stateid.other[2];
754	if (syscred)
755		nd->nd_flag |= ND_USEGSSNAME;
756	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
757	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
758	if (error)
759		return (error);
760	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
761	if (nd->nd_repstat == 0)
762		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
763	error = nd->nd_repstat;
764	if (error == NFSERR_STALESTATEID)
765		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
766nfsmout:
767	mbuf_freem(nd->nd_mrep);
768	return (error);
769}
770
771/*
772 * V4 Open Confirm RPC.
773 */
774APPLESTATIC int
775nfsrpc_openconfirm(vnode_t vp, u_int8_t *nfhp, int fhlen,
776    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p)
777{
778	u_int32_t *tl;
779	struct nfsrv_descript nfsd, *nd = &nfsd;
780	struct nfsmount *nmp;
781	int error;
782
783	nmp = VFSTONFS(vnode_mount(vp));
784	if (NFSHASNFSV4N(nmp))
785		return (0);		/* No confirmation for NFSv4.1. */
786	nfscl_reqstart(nd, NFSPROC_OPENCONFIRM, nmp, nfhp, fhlen, NULL, NULL);
787	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
788	*tl++ = op->nfso_stateid.seqid;
789	*tl++ = op->nfso_stateid.other[0];
790	*tl++ = op->nfso_stateid.other[1];
791	*tl++ = op->nfso_stateid.other[2];
792	*tl = txdr_unsigned(op->nfso_own->nfsow_seqid);
793	error = nfscl_request(nd, vp, p, cred, NULL);
794	if (error)
795		return (error);
796	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
797	if (!nd->nd_repstat) {
798		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
799		op->nfso_stateid.seqid = *tl++;
800		op->nfso_stateid.other[0] = *tl++;
801		op->nfso_stateid.other[1] = *tl++;
802		op->nfso_stateid.other[2] = *tl;
803	}
804	error = nd->nd_repstat;
805	if (error == NFSERR_STALESTATEID)
806		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
807nfsmout:
808	mbuf_freem(nd->nd_mrep);
809	return (error);
810}
811
812/*
813 * Do the setclientid and setclientid confirm RPCs. Called from nfs_statfs()
814 * when a mount has just occurred and when the server replies NFSERR_EXPIRED.
815 */
816APPLESTATIC int
817nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
818    struct ucred *cred, NFSPROC_T *p)
819{
820	u_int32_t *tl;
821	struct nfsrv_descript nfsd;
822	struct nfsrv_descript *nd = &nfsd;
823	nfsattrbit_t attrbits;
824	u_int8_t *cp = NULL, *cp2, addr[INET6_ADDRSTRLEN + 9];
825	u_short port;
826	int error, isinet6 = 0, callblen;
827	nfsquad_t confirm;
828	u_int32_t lease;
829	static u_int32_t rev = 0;
830	struct nfsclds *dsp;
831	struct nfsclsession *tsep;
832
833	if (nfsboottime.tv_sec == 0)
834		NFSSETBOOTTIME(nfsboottime);
835	clp->nfsc_rev = rev++;
836	if (NFSHASNFSV4N(nmp)) {
837		/*
838		 * Either there was no previous session or the
839		 * previous session has failed, so...
840		 * do an ExchangeID followed by the CreateSession.
841		 */
842		error = nfsrpc_exchangeid(nmp, clp, &nmp->nm_sockreq,
843		    NFSV4EXCH_USEPNFSMDS | NFSV4EXCH_USENONPNFS, &dsp, cred, p);
844		NFSCL_DEBUG(1, "aft exch=%d\n", error);
845		if (error == 0)
846			error = nfsrpc_createsession(nmp, &dsp->nfsclds_sess,
847			    &nmp->nm_sockreq,
848			    dsp->nfsclds_sess.nfsess_sequenceid, 1, cred, p);
849		if (error == 0) {
850			NFSLOCKMNT(nmp);
851			/*
852			 * The old sessions cannot be safely free'd
853			 * here, since they may still be used by
854			 * in-progress RPCs.
855			 */
856			tsep = NULL;
857			if (TAILQ_FIRST(&nmp->nm_sess) != NULL)
858				tsep = NFSMNT_MDSSESSION(nmp);
859			TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp,
860			    nfsclds_list);
861			/*
862			 * Wake up RPCs waiting for a slot on the
863			 * old session. These will then fail with
864			 * NFSERR_BADSESSION and be retried with the
865			 * new session by nfsv4_setsequence().
866			 * Also wakeup() processes waiting for the
867			 * new session.
868			 */
869			if (tsep != NULL)
870				wakeup(&tsep->nfsess_slots);
871			wakeup(&nmp->nm_sess);
872			NFSUNLOCKMNT(nmp);
873		} else
874			nfscl_freenfsclds(dsp);
875		NFSCL_DEBUG(1, "aft createsess=%d\n", error);
876		if (error == 0 && reclaim == 0) {
877			error = nfsrpc_reclaimcomplete(nmp, cred, p);
878			NFSCL_DEBUG(1, "aft reclaimcomp=%d\n", error);
879			if (error == NFSERR_COMPLETEALREADY ||
880			    error == NFSERR_NOTSUPP)
881				/* Ignore this error. */
882				error = 0;
883		}
884		return (error);
885	}
886
887	/*
888	 * Allocate a single session structure for NFSv4.0, because some of
889	 * the fields are used by NFSv4.0 although it doesn't do a session.
890	 */
891	dsp = malloc(sizeof(struct nfsclds), M_NFSCLDS, M_WAITOK | M_ZERO);
892	mtx_init(&dsp->nfsclds_mtx, "nfsds", NULL, MTX_DEF);
893	mtx_init(&dsp->nfsclds_sess.nfsess_mtx, "nfssession", NULL, MTX_DEF);
894	NFSLOCKMNT(nmp);
895	TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp, nfsclds_list);
896	tsep = NFSMNT_MDSSESSION(nmp);
897	NFSUNLOCKMNT(nmp);
898
899	nfscl_reqstart(nd, NFSPROC_SETCLIENTID, nmp, NULL, 0, NULL, NULL);
900	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
901	*tl++ = txdr_unsigned(nfsboottime.tv_sec);
902	*tl = txdr_unsigned(clp->nfsc_rev);
903	(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
904
905	/*
906	 * set up the callback address
907	 */
908	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
909	*tl = txdr_unsigned(NFS_CALLBCKPROG);
910	callblen = strlen(nfsv4_callbackaddr);
911	if (callblen == 0)
912		cp = nfscl_getmyip(nmp, &isinet6);
913	if (nfscl_enablecallb && nfs_numnfscbd > 0 &&
914	    (callblen > 0 || cp != NULL)) {
915		port = htons(nfsv4_cbport);
916		cp2 = (u_int8_t *)&port;
917#ifdef INET6
918		if ((callblen > 0 &&
919		     strchr(nfsv4_callbackaddr, ':')) || isinet6) {
920			char ip6buf[INET6_ADDRSTRLEN], *ip6add;
921
922			(void) nfsm_strtom(nd, "tcp6", 4);
923			if (callblen == 0) {
924				ip6_sprintf(ip6buf, (struct in6_addr *)cp);
925				ip6add = ip6buf;
926			} else {
927				ip6add = nfsv4_callbackaddr;
928			}
929			snprintf(addr, INET6_ADDRSTRLEN + 9, "%s.%d.%d",
930			    ip6add, cp2[0], cp2[1]);
931		} else
932#endif
933		{
934			(void) nfsm_strtom(nd, "tcp", 3);
935			if (callblen == 0)
936				snprintf(addr, INET6_ADDRSTRLEN + 9,
937				    "%d.%d.%d.%d.%d.%d", cp[0], cp[1],
938				    cp[2], cp[3], cp2[0], cp2[1]);
939			else
940				snprintf(addr, INET6_ADDRSTRLEN + 9,
941				    "%s.%d.%d", nfsv4_callbackaddr,
942				    cp2[0], cp2[1]);
943		}
944		(void) nfsm_strtom(nd, addr, strlen(addr));
945	} else {
946		(void) nfsm_strtom(nd, "tcp", 3);
947		(void) nfsm_strtom(nd, "0.0.0.0.0.0", 11);
948	}
949	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
950	*tl = txdr_unsigned(clp->nfsc_cbident);
951	nd->nd_flag |= ND_USEGSSNAME;
952	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
953		NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
954	if (error)
955		return (error);
956	if (nd->nd_repstat == 0) {
957	    NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
958	    tsep->nfsess_clientid.lval[0] = *tl++;
959	    tsep->nfsess_clientid.lval[1] = *tl++;
960	    confirm.lval[0] = *tl++;
961	    confirm.lval[1] = *tl;
962	    mbuf_freem(nd->nd_mrep);
963	    nd->nd_mrep = NULL;
964
965	    /*
966	     * and confirm it.
967	     */
968	    nfscl_reqstart(nd, NFSPROC_SETCLIENTIDCFRM, nmp, NULL, 0, NULL,
969		NULL);
970	    NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
971	    *tl++ = tsep->nfsess_clientid.lval[0];
972	    *tl++ = tsep->nfsess_clientid.lval[1];
973	    *tl++ = confirm.lval[0];
974	    *tl = confirm.lval[1];
975	    nd->nd_flag |= ND_USEGSSNAME;
976	    error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
977		cred, NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
978	    if (error)
979		return (error);
980	    mbuf_freem(nd->nd_mrep);
981	    nd->nd_mrep = NULL;
982	    if (nd->nd_repstat == 0) {
983		nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, nmp->nm_fh,
984		    nmp->nm_fhsize, NULL, NULL);
985		NFSZERO_ATTRBIT(&attrbits);
986		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_LEASETIME);
987		(void) nfsrv_putattrbit(nd, &attrbits);
988		nd->nd_flag |= ND_USEGSSNAME;
989		error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
990		    cred, NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
991		if (error)
992		    return (error);
993		if (nd->nd_repstat == 0) {
994		    error = nfsv4_loadattr(nd, NULL, NULL, NULL, NULL, 0, NULL,
995			NULL, NULL, NULL, NULL, 0, NULL, &lease, NULL, p, cred);
996		    if (error)
997			goto nfsmout;
998		    clp->nfsc_renew = NFSCL_RENEW(lease);
999		    clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
1000		    clp->nfsc_clientidrev++;
1001		    if (clp->nfsc_clientidrev == 0)
1002			clp->nfsc_clientidrev++;
1003		}
1004	    }
1005	}
1006	error = nd->nd_repstat;
1007nfsmout:
1008	mbuf_freem(nd->nd_mrep);
1009	return (error);
1010}
1011
1012/*
1013 * nfs getattr call.
1014 */
1015APPLESTATIC int
1016nfsrpc_getattr(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
1017    struct nfsvattr *nap, void *stuff)
1018{
1019	struct nfsrv_descript nfsd, *nd = &nfsd;
1020	int error;
1021	nfsattrbit_t attrbits;
1022
1023	NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
1024	if (nd->nd_flag & ND_NFSV4) {
1025		NFSGETATTR_ATTRBIT(&attrbits);
1026		(void) nfsrv_putattrbit(nd, &attrbits);
1027	}
1028	error = nfscl_request(nd, vp, p, cred, stuff);
1029	if (error)
1030		return (error);
1031	if (!nd->nd_repstat)
1032		error = nfsm_loadattr(nd, nap);
1033	else
1034		error = nd->nd_repstat;
1035	mbuf_freem(nd->nd_mrep);
1036	return (error);
1037}
1038
1039/*
1040 * nfs getattr call with non-vnode arguemnts.
1041 */
1042APPLESTATIC int
1043nfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred,
1044    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, u_int64_t *xidp,
1045    uint32_t *leasep)
1046{
1047	struct nfsrv_descript nfsd, *nd = &nfsd;
1048	int error, vers = NFS_VER2;
1049	nfsattrbit_t attrbits;
1050
1051	nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, fhp, fhlen, NULL, NULL);
1052	if (nd->nd_flag & ND_NFSV4) {
1053		vers = NFS_VER4;
1054		NFSGETATTR_ATTRBIT(&attrbits);
1055		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_LEASETIME);
1056		(void) nfsrv_putattrbit(nd, &attrbits);
1057	} else if (nd->nd_flag & ND_NFSV3) {
1058		vers = NFS_VER3;
1059	}
1060	if (syscred)
1061		nd->nd_flag |= ND_USEGSSNAME;
1062	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
1063	    NFS_PROG, vers, NULL, 1, xidp, NULL);
1064	if (error)
1065		return (error);
1066	if (nd->nd_repstat == 0) {
1067		if ((nd->nd_flag & ND_NFSV4) != 0)
1068			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
1069			    NULL, NULL, NULL, NULL, NULL, 0, NULL, leasep, NULL,
1070			    NULL, NULL);
1071		else
1072			error = nfsm_loadattr(nd, nap);
1073	} else
1074		error = nd->nd_repstat;
1075	mbuf_freem(nd->nd_mrep);
1076	return (error);
1077}
1078
1079/*
1080 * Do an nfs setattr operation.
1081 */
1082APPLESTATIC int
1083nfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
1084    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *rnap, int *attrflagp,
1085    void *stuff)
1086{
1087	int error, expireret = 0, openerr, retrycnt;
1088	u_int32_t clidrev = 0, mode;
1089	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1090	struct nfsfh *nfhp;
1091	nfsv4stateid_t stateid;
1092	void *lckp;
1093
1094	if (nmp->nm_clp != NULL)
1095		clidrev = nmp->nm_clp->nfsc_clientidrev;
1096	if (vap != NULL && NFSATTRISSET(u_quad_t, vap, va_size))
1097		mode = NFSV4OPEN_ACCESSWRITE;
1098	else
1099		mode = NFSV4OPEN_ACCESSREAD;
1100	retrycnt = 0;
1101	do {
1102		lckp = NULL;
1103		openerr = 1;
1104		if (NFSHASNFSV4(nmp)) {
1105			nfhp = VTONFS(vp)->n_fhp;
1106			error = nfscl_getstateid(vp, nfhp->nfh_fh,
1107			    nfhp->nfh_len, mode, 0, cred, p, &stateid, &lckp);
1108			if (error && vnode_vtype(vp) == VREG &&
1109			    (mode == NFSV4OPEN_ACCESSWRITE ||
1110			     nfstest_openallsetattr)) {
1111				/*
1112				 * No Open stateid, so try and open the file
1113				 * now.
1114				 */
1115				if (mode == NFSV4OPEN_ACCESSWRITE)
1116					openerr = nfsrpc_open(vp, FWRITE, cred,
1117					    p);
1118				else
1119					openerr = nfsrpc_open(vp, FREAD, cred,
1120					    p);
1121				if (!openerr)
1122					(void) nfscl_getstateid(vp,
1123					    nfhp->nfh_fh, nfhp->nfh_len,
1124					    mode, 0, cred, p, &stateid, &lckp);
1125			}
1126		}
1127		if (vap != NULL)
1128			error = nfsrpc_setattrrpc(vp, vap, &stateid, cred, p,
1129			    rnap, attrflagp, stuff);
1130		else
1131			error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
1132			    stuff);
1133		if (error == NFSERR_OPENMODE && mode == NFSV4OPEN_ACCESSREAD) {
1134			NFSLOCKMNT(nmp);
1135			nmp->nm_state |= NFSSTA_OPENMODE;
1136			NFSUNLOCKMNT(nmp);
1137		}
1138		if (error == NFSERR_STALESTATEID)
1139			nfscl_initiate_recovery(nmp->nm_clp);
1140		if (lckp != NULL)
1141			nfscl_lockderef(lckp);
1142		if (!openerr)
1143			(void) nfsrpc_close(vp, 0, p);
1144		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1145		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1146		    error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
1147			(void) nfs_catnap(PZERO, error, "nfs_setattr");
1148		} else if ((error == NFSERR_EXPIRED ||
1149		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1150			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1151		}
1152		retrycnt++;
1153	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1154	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1155	    error == NFSERR_BADSESSION ||
1156	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1157	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1158	     expireret == 0 && clidrev != 0 && retrycnt < 4) ||
1159	    (error == NFSERR_OPENMODE && mode == NFSV4OPEN_ACCESSREAD &&
1160	     retrycnt < 4));
1161	if (error && retrycnt >= 4)
1162		error = EIO;
1163	return (error);
1164}
1165
1166static int
1167nfsrpc_setattrrpc(vnode_t vp, struct vattr *vap,
1168    nfsv4stateid_t *stateidp, struct ucred *cred, NFSPROC_T *p,
1169    struct nfsvattr *rnap, int *attrflagp, void *stuff)
1170{
1171	u_int32_t *tl;
1172	struct nfsrv_descript nfsd, *nd = &nfsd;
1173	int error;
1174	nfsattrbit_t attrbits;
1175
1176	*attrflagp = 0;
1177	NFSCL_REQSTART(nd, NFSPROC_SETATTR, vp);
1178	if (nd->nd_flag & ND_NFSV4)
1179		nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1180	vap->va_type = vnode_vtype(vp);
1181	nfscl_fillsattr(nd, vap, vp, NFSSATTR_FULL, 0);
1182	if (nd->nd_flag & ND_NFSV3) {
1183		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1184		*tl = newnfs_false;
1185	} else if (nd->nd_flag & ND_NFSV4) {
1186		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1187		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1188		NFSGETATTR_ATTRBIT(&attrbits);
1189		(void) nfsrv_putattrbit(nd, &attrbits);
1190	}
1191	error = nfscl_request(nd, vp, p, cred, stuff);
1192	if (error)
1193		return (error);
1194	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1195		error = nfscl_wcc_data(nd, vp, rnap, attrflagp, NULL, stuff);
1196	if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 && !error)
1197		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1198	if (!(nd->nd_flag & ND_NFSV3) && !nd->nd_repstat && !error)
1199		error = nfscl_postop_attr(nd, rnap, attrflagp, stuff);
1200	mbuf_freem(nd->nd_mrep);
1201	if (nd->nd_repstat && !error)
1202		error = nd->nd_repstat;
1203	return (error);
1204}
1205
1206/*
1207 * nfs lookup rpc
1208 */
1209APPLESTATIC int
1210nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
1211    NFSPROC_T *p, struct nfsvattr *dnap, struct nfsvattr *nap,
1212    struct nfsfh **nfhpp, int *attrflagp, int *dattrflagp, void *stuff)
1213{
1214	u_int32_t *tl;
1215	struct nfsrv_descript nfsd, *nd = &nfsd;
1216	struct nfsmount *nmp;
1217	struct nfsnode *np;
1218	struct nfsfh *nfhp;
1219	nfsattrbit_t attrbits;
1220	int error = 0, lookupp = 0;
1221
1222	*attrflagp = 0;
1223	*dattrflagp = 0;
1224	if (vnode_vtype(dvp) != VDIR)
1225		return (ENOTDIR);
1226	nmp = VFSTONFS(vnode_mount(dvp));
1227	if (len > NFS_MAXNAMLEN)
1228		return (ENAMETOOLONG);
1229	if (NFSHASNFSV4(nmp) && len == 1 &&
1230		name[0] == '.') {
1231		/*
1232		 * Just return the current dir's fh.
1233		 */
1234		np = VTONFS(dvp);
1235		MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1236			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1237		nfhp->nfh_len = np->n_fhp->nfh_len;
1238		NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1239		*nfhpp = nfhp;
1240		return (0);
1241	}
1242	if (NFSHASNFSV4(nmp) && len == 2 &&
1243		name[0] == '.' && name[1] == '.') {
1244		lookupp = 1;
1245		NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, dvp);
1246	} else {
1247		NFSCL_REQSTART(nd, NFSPROC_LOOKUP, dvp);
1248		(void) nfsm_strtom(nd, name, len);
1249	}
1250	if (nd->nd_flag & ND_NFSV4) {
1251		NFSGETATTR_ATTRBIT(&attrbits);
1252		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1253		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1254		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1255		(void) nfsrv_putattrbit(nd, &attrbits);
1256	}
1257	error = nfscl_request(nd, dvp, p, cred, stuff);
1258	if (error)
1259		return (error);
1260	if (nd->nd_repstat) {
1261		/*
1262		 * When an NFSv4 Lookupp returns ENOENT, it means that
1263		 * the lookup is at the root of an fs, so return this dir.
1264		 */
1265		if (nd->nd_repstat == NFSERR_NOENT && lookupp) {
1266		    np = VTONFS(dvp);
1267		    MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1268			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1269		    nfhp->nfh_len = np->n_fhp->nfh_len;
1270		    NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1271		    *nfhpp = nfhp;
1272		    mbuf_freem(nd->nd_mrep);
1273		    return (0);
1274		}
1275		if (nd->nd_flag & ND_NFSV3)
1276		    error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1277		else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
1278		    ND_NFSV4) {
1279			/* Load the directory attributes. */
1280			error = nfsm_loadattr(nd, dnap);
1281			if (error == 0)
1282				*dattrflagp = 1;
1283		}
1284		goto nfsmout;
1285	}
1286	if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
1287		/* Load the directory attributes. */
1288		error = nfsm_loadattr(nd, dnap);
1289		if (error != 0)
1290			goto nfsmout;
1291		*dattrflagp = 1;
1292		/* Skip over the Lookup and GetFH operation status values. */
1293		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1294	}
1295	error = nfsm_getfh(nd, nfhpp);
1296	if (error)
1297		goto nfsmout;
1298
1299	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1300	if ((nd->nd_flag & ND_NFSV3) && !error)
1301		error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1302nfsmout:
1303	mbuf_freem(nd->nd_mrep);
1304	if (!error && nd->nd_repstat)
1305		error = nd->nd_repstat;
1306	return (error);
1307}
1308
1309/*
1310 * Do a readlink rpc.
1311 */
1312APPLESTATIC int
1313nfsrpc_readlink(vnode_t vp, struct uio *uiop, struct ucred *cred,
1314    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1315{
1316	u_int32_t *tl;
1317	struct nfsrv_descript nfsd, *nd = &nfsd;
1318	struct nfsnode *np = VTONFS(vp);
1319	nfsattrbit_t attrbits;
1320	int error, len, cangetattr = 1;
1321
1322	*attrflagp = 0;
1323	NFSCL_REQSTART(nd, NFSPROC_READLINK, vp);
1324	if (nd->nd_flag & ND_NFSV4) {
1325		/*
1326		 * And do a Getattr op.
1327		 */
1328		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1329		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1330		NFSGETATTR_ATTRBIT(&attrbits);
1331		(void) nfsrv_putattrbit(nd, &attrbits);
1332	}
1333	error = nfscl_request(nd, vp, p, cred, stuff);
1334	if (error)
1335		return (error);
1336	if (nd->nd_flag & ND_NFSV3)
1337		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1338	if (!nd->nd_repstat && !error) {
1339		NFSM_STRSIZ(len, NFS_MAXPATHLEN);
1340		/*
1341		 * This seems weird to me, but must have been added to
1342		 * FreeBSD for some reason. The only thing I can think of
1343		 * is that there was/is some server that replies with
1344		 * more link data than it should?
1345		 */
1346		if (len == NFS_MAXPATHLEN) {
1347			NFSLOCKNODE(np);
1348			if (np->n_size > 0 && np->n_size < NFS_MAXPATHLEN) {
1349				len = np->n_size;
1350				cangetattr = 0;
1351			}
1352			NFSUNLOCKNODE(np);
1353		}
1354		error = nfsm_mbufuio(nd, uiop, len);
1355		if ((nd->nd_flag & ND_NFSV4) && !error && cangetattr)
1356			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1357	}
1358	if (nd->nd_repstat && !error)
1359		error = nd->nd_repstat;
1360nfsmout:
1361	mbuf_freem(nd->nd_mrep);
1362	return (error);
1363}
1364
1365/*
1366 * Read operation.
1367 */
1368APPLESTATIC int
1369nfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
1370    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1371{
1372	int error, expireret = 0, retrycnt;
1373	u_int32_t clidrev = 0;
1374	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1375	struct nfsnode *np = VTONFS(vp);
1376	struct ucred *newcred;
1377	struct nfsfh *nfhp = NULL;
1378	nfsv4stateid_t stateid;
1379	void *lckp;
1380
1381	if (nmp->nm_clp != NULL)
1382		clidrev = nmp->nm_clp->nfsc_clientidrev;
1383	newcred = cred;
1384	if (NFSHASNFSV4(nmp)) {
1385		nfhp = np->n_fhp;
1386		newcred = NFSNEWCRED(cred);
1387	}
1388	retrycnt = 0;
1389	do {
1390		lckp = NULL;
1391		if (NFSHASNFSV4(nmp))
1392			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1393			    NFSV4OPEN_ACCESSREAD, 0, newcred, p, &stateid,
1394			    &lckp);
1395		error = nfsrpc_readrpc(vp, uiop, newcred, &stateid, p, nap,
1396		    attrflagp, stuff);
1397		if (error == NFSERR_OPENMODE) {
1398			NFSLOCKMNT(nmp);
1399			nmp->nm_state |= NFSSTA_OPENMODE;
1400			NFSUNLOCKMNT(nmp);
1401		}
1402		if (error == NFSERR_STALESTATEID)
1403			nfscl_initiate_recovery(nmp->nm_clp);
1404		if (lckp != NULL)
1405			nfscl_lockderef(lckp);
1406		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1407		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1408		    error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
1409			(void) nfs_catnap(PZERO, error, "nfs_read");
1410		} else if ((error == NFSERR_EXPIRED ||
1411		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1412			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1413		}
1414		retrycnt++;
1415	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1416	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1417	    error == NFSERR_BADSESSION ||
1418	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1419	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1420	     expireret == 0 && clidrev != 0 && retrycnt < 4) ||
1421	    (error == NFSERR_OPENMODE && retrycnt < 4));
1422	if (error && retrycnt >= 4)
1423		error = EIO;
1424	if (NFSHASNFSV4(nmp))
1425		NFSFREECRED(newcred);
1426	return (error);
1427}
1428
1429/*
1430 * The actual read RPC.
1431 */
1432static int
1433nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
1434    nfsv4stateid_t *stateidp, NFSPROC_T *p, struct nfsvattr *nap,
1435    int *attrflagp, void *stuff)
1436{
1437	u_int32_t *tl;
1438	int error = 0, len, retlen, tsiz, eof = 0;
1439	struct nfsrv_descript nfsd;
1440	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1441	struct nfsrv_descript *nd = &nfsd;
1442	int rsize;
1443	off_t tmp_off;
1444
1445	*attrflagp = 0;
1446	tsiz = uio_uio_resid(uiop);
1447	tmp_off = uiop->uio_offset + tsiz;
1448	NFSLOCKMNT(nmp);
1449	if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset) {
1450		NFSUNLOCKMNT(nmp);
1451		return (EFBIG);
1452	}
1453	rsize = nmp->nm_rsize;
1454	NFSUNLOCKMNT(nmp);
1455	nd->nd_mrep = NULL;
1456	while (tsiz > 0) {
1457		*attrflagp = 0;
1458		len = (tsiz > rsize) ? rsize : tsiz;
1459		NFSCL_REQSTART(nd, NFSPROC_READ, vp);
1460		if (nd->nd_flag & ND_NFSV4)
1461			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1462		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
1463		if (nd->nd_flag & ND_NFSV2) {
1464			*tl++ = txdr_unsigned(uiop->uio_offset);
1465			*tl++ = txdr_unsigned(len);
1466			*tl = 0;
1467		} else {
1468			txdr_hyper(uiop->uio_offset, tl);
1469			*(tl + 2) = txdr_unsigned(len);
1470		}
1471		/*
1472		 * Since I can't do a Getattr for NFSv4 for Write, there
1473		 * doesn't seem any point in doing one here, either.
1474		 * (See the comment in nfsrpc_writerpc() for more info.)
1475		 */
1476		error = nfscl_request(nd, vp, p, cred, stuff);
1477		if (error)
1478			return (error);
1479		if (nd->nd_flag & ND_NFSV3) {
1480			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1481		} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
1482			error = nfsm_loadattr(nd, nap);
1483			if (!error)
1484				*attrflagp = 1;
1485		}
1486		if (nd->nd_repstat || error) {
1487			if (!error)
1488				error = nd->nd_repstat;
1489			goto nfsmout;
1490		}
1491		if (nd->nd_flag & ND_NFSV3) {
1492			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1493			eof = fxdr_unsigned(int, *(tl + 1));
1494		} else if (nd->nd_flag & ND_NFSV4) {
1495			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1496			eof = fxdr_unsigned(int, *tl);
1497		}
1498		NFSM_STRSIZ(retlen, len);
1499		error = nfsm_mbufuio(nd, uiop, retlen);
1500		if (error)
1501			goto nfsmout;
1502		mbuf_freem(nd->nd_mrep);
1503		nd->nd_mrep = NULL;
1504		tsiz -= retlen;
1505		if (!(nd->nd_flag & ND_NFSV2)) {
1506			if (eof || retlen == 0)
1507				tsiz = 0;
1508		} else if (retlen < len)
1509			tsiz = 0;
1510	}
1511	return (0);
1512nfsmout:
1513	if (nd->nd_mrep != NULL)
1514		mbuf_freem(nd->nd_mrep);
1515	return (error);
1516}
1517
1518/*
1519 * nfs write operation
1520 * When called_from_strategy != 0, it should return EIO for an error that
1521 * indicates recovery is in progress, so that the buffer will be left
1522 * dirty and be written back to the server later. If it loops around,
1523 * the recovery thread could get stuck waiting for the buffer and recovery
1524 * will then deadlock.
1525 */
1526APPLESTATIC int
1527nfsrpc_write(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
1528    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
1529    void *stuff, int called_from_strategy)
1530{
1531	int error, expireret = 0, retrycnt, nostateid;
1532	u_int32_t clidrev = 0;
1533	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1534	struct nfsnode *np = VTONFS(vp);
1535	struct ucred *newcred;
1536	struct nfsfh *nfhp = NULL;
1537	nfsv4stateid_t stateid;
1538	void *lckp;
1539
1540	*must_commit = 0;
1541	if (nmp->nm_clp != NULL)
1542		clidrev = nmp->nm_clp->nfsc_clientidrev;
1543	newcred = cred;
1544	if (NFSHASNFSV4(nmp)) {
1545		newcred = NFSNEWCRED(cred);
1546		nfhp = np->n_fhp;
1547	}
1548	retrycnt = 0;
1549	do {
1550		lckp = NULL;
1551		nostateid = 0;
1552		if (NFSHASNFSV4(nmp)) {
1553			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1554			    NFSV4OPEN_ACCESSWRITE, 0, newcred, p, &stateid,
1555			    &lckp);
1556			if (stateid.other[0] == 0 && stateid.other[1] == 0 &&
1557			    stateid.other[2] == 0) {
1558				nostateid = 1;
1559				NFSCL_DEBUG(1, "stateid0 in write\n");
1560			}
1561		}
1562
1563		/*
1564		 * If there is no stateid for NFSv4, it means this is an
1565		 * extraneous write after close. Basically a poorly
1566		 * implemented buffer cache. Just don't do the write.
1567		 */
1568		if (nostateid)
1569			error = 0;
1570		else
1571			error = nfsrpc_writerpc(vp, uiop, iomode, must_commit,
1572			    newcred, &stateid, p, nap, attrflagp, stuff);
1573		if (error == NFSERR_STALESTATEID)
1574			nfscl_initiate_recovery(nmp->nm_clp);
1575		if (lckp != NULL)
1576			nfscl_lockderef(lckp);
1577		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1578		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1579		    error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
1580			(void) nfs_catnap(PZERO, error, "nfs_write");
1581		} else if ((error == NFSERR_EXPIRED ||
1582		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1583			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1584		}
1585		retrycnt++;
1586	} while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
1587	    ((error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
1588	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy == 0) ||
1589	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1590	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1591	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1592	if (error != 0 && (retrycnt >= 4 ||
1593	    ((error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
1594	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy != 0)))
1595		error = EIO;
1596	if (NFSHASNFSV4(nmp))
1597		NFSFREECRED(newcred);
1598	return (error);
1599}
1600
1601/*
1602 * The actual write RPC.
1603 */
1604static int
1605nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
1606    int *must_commit, struct ucred *cred, nfsv4stateid_t *stateidp,
1607    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1608{
1609	u_int32_t *tl;
1610	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1611	struct nfsnode *np = VTONFS(vp);
1612	int error = 0, len, tsiz, rlen, commit, committed = NFSWRITE_FILESYNC;
1613	int wccflag = 0, wsize;
1614	int32_t backup;
1615	struct nfsrv_descript nfsd;
1616	struct nfsrv_descript *nd = &nfsd;
1617	nfsattrbit_t attrbits;
1618	off_t tmp_off;
1619
1620	KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
1621	*attrflagp = 0;
1622	tsiz = uio_uio_resid(uiop);
1623	tmp_off = uiop->uio_offset + tsiz;
1624	NFSLOCKMNT(nmp);
1625	if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset) {
1626		NFSUNLOCKMNT(nmp);
1627		return (EFBIG);
1628	}
1629	wsize = nmp->nm_wsize;
1630	NFSUNLOCKMNT(nmp);
1631	nd->nd_mrep = NULL;	/* NFSv2 sometimes does a write with */
1632	nd->nd_repstat = 0;	/* uio_resid == 0, so the while is not done */
1633	while (tsiz > 0) {
1634		*attrflagp = 0;
1635		len = (tsiz > wsize) ? wsize : tsiz;
1636		NFSCL_REQSTART(nd, NFSPROC_WRITE, vp);
1637		if (nd->nd_flag & ND_NFSV4) {
1638			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1639			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+2*NFSX_UNSIGNED);
1640			txdr_hyper(uiop->uio_offset, tl);
1641			tl += 2;
1642			*tl++ = txdr_unsigned(*iomode);
1643			*tl = txdr_unsigned(len);
1644		} else if (nd->nd_flag & ND_NFSV3) {
1645			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+3*NFSX_UNSIGNED);
1646			txdr_hyper(uiop->uio_offset, tl);
1647			tl += 2;
1648			*tl++ = txdr_unsigned(len);
1649			*tl++ = txdr_unsigned(*iomode);
1650			*tl = txdr_unsigned(len);
1651		} else {
1652			u_int32_t x;
1653
1654			NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1655			/*
1656			 * Not sure why someone changed this, since the
1657			 * RFC clearly states that "beginoffset" and
1658			 * "totalcount" are ignored, but it wouldn't
1659			 * surprise me if there's a busted server out there.
1660			 */
1661			/* Set both "begin" and "current" to non-garbage. */
1662			x = txdr_unsigned((u_int32_t)uiop->uio_offset);
1663			*tl++ = x;      /* "begin offset" */
1664			*tl++ = x;      /* "current offset" */
1665			x = txdr_unsigned(len);
1666			*tl++ = x;      /* total to this offset */
1667			*tl = x;        /* size of this write */
1668
1669		}
1670		nfsm_uiombuf(nd, uiop, len);
1671		/*
1672		 * Although it is tempting to do a normal Getattr Op in the
1673		 * NFSv4 compound, the result can be a nearly hung client
1674		 * system if the Getattr asks for Owner and/or OwnerGroup.
1675		 * It occurs when the client can't map either the Owner or
1676		 * Owner_group name in the Getattr reply to a uid/gid. When
1677		 * there is a cache miss, the kernel does an upcall to the
1678		 * nfsuserd. Then, it can try and read the local /etc/passwd
1679		 * or /etc/group file. It can then block in getnewbuf(),
1680		 * waiting for dirty writes to be pushed to the NFS server.
1681		 * The only reason this doesn't result in a complete
1682		 * deadlock, is that the upcall times out and allows
1683		 * the write to complete. However, progress is so slow
1684		 * that it might just as well be deadlocked.
1685		 * As such, we get the rest of the attributes, but not
1686		 * Owner or Owner_group.
1687		 * nb: nfscl_loadattrcache() needs to be told that these
1688		 *     partial attributes from a write rpc are being
1689		 *     passed in, via a argument flag.
1690		 */
1691		if (nd->nd_flag & ND_NFSV4) {
1692			NFSWRITEGETATTR_ATTRBIT(&attrbits);
1693			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1694			*tl = txdr_unsigned(NFSV4OP_GETATTR);
1695			(void) nfsrv_putattrbit(nd, &attrbits);
1696		}
1697		error = nfscl_request(nd, vp, p, cred, stuff);
1698		if (error)
1699			return (error);
1700		if (nd->nd_repstat) {
1701			/*
1702			 * In case the rpc gets retried, roll
1703			 * the uio fileds changed by nfsm_uiombuf()
1704			 * back.
1705			 */
1706			uiop->uio_offset -= len;
1707			uio_uio_resid_add(uiop, len);
1708			uio_iov_base_add(uiop, -len);
1709			uio_iov_len_add(uiop, len);
1710		}
1711		if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1712			error = nfscl_wcc_data(nd, vp, nap, attrflagp,
1713			    &wccflag, stuff);
1714			if (error)
1715				goto nfsmout;
1716		}
1717		if (!nd->nd_repstat) {
1718			if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1719				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED
1720					+ NFSX_VERF);
1721				rlen = fxdr_unsigned(int, *tl++);
1722				if (rlen == 0) {
1723					error = NFSERR_IO;
1724					goto nfsmout;
1725				} else if (rlen < len) {
1726					backup = len - rlen;
1727					uio_iov_base_add(uiop, -(backup));
1728					uio_iov_len_add(uiop, backup);
1729					uiop->uio_offset -= backup;
1730					uio_uio_resid_add(uiop, backup);
1731					len = rlen;
1732				}
1733				commit = fxdr_unsigned(int, *tl++);
1734
1735				/*
1736				 * Return the lowest committment level
1737				 * obtained by any of the RPCs.
1738				 */
1739				if (committed == NFSWRITE_FILESYNC)
1740					committed = commit;
1741				else if (committed == NFSWRITE_DATASYNC &&
1742					commit == NFSWRITE_UNSTABLE)
1743					committed = commit;
1744				NFSLOCKMNT(nmp);
1745				if (!NFSHASWRITEVERF(nmp)) {
1746					NFSBCOPY((caddr_t)tl,
1747					    (caddr_t)&nmp->nm_verf[0],
1748					    NFSX_VERF);
1749					NFSSETWRITEVERF(nmp);
1750	    			} else if (NFSBCMP(tl, nmp->nm_verf,
1751				    NFSX_VERF)) {
1752					*must_commit = 1;
1753					NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
1754				}
1755				NFSUNLOCKMNT(nmp);
1756			}
1757			if (nd->nd_flag & ND_NFSV4)
1758				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1759			if (nd->nd_flag & (ND_NFSV2 | ND_NFSV4)) {
1760				error = nfsm_loadattr(nd, nap);
1761				if (!error)
1762					*attrflagp = NFS_LATTR_NOSHRINK;
1763			}
1764		} else {
1765			error = nd->nd_repstat;
1766		}
1767		if (error)
1768			goto nfsmout;
1769		NFSWRITERPC_SETTIME(wccflag, np, nap, (nd->nd_flag & ND_NFSV4));
1770		mbuf_freem(nd->nd_mrep);
1771		nd->nd_mrep = NULL;
1772		tsiz -= len;
1773	}
1774nfsmout:
1775	if (nd->nd_mrep != NULL)
1776		mbuf_freem(nd->nd_mrep);
1777	*iomode = committed;
1778	if (nd->nd_repstat && !error)
1779		error = nd->nd_repstat;
1780	return (error);
1781}
1782
1783/*
1784 * nfs mknod rpc
1785 * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1786 * mode set to specify the file type and the size field for rdev.
1787 */
1788APPLESTATIC int
1789nfsrpc_mknod(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1790    u_int32_t rdev, enum vtype vtyp, struct ucred *cred, NFSPROC_T *p,
1791    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1792    int *attrflagp, int *dattrflagp, void *dstuff)
1793{
1794	u_int32_t *tl;
1795	int error = 0;
1796	struct nfsrv_descript nfsd, *nd = &nfsd;
1797	nfsattrbit_t attrbits;
1798
1799	*nfhpp = NULL;
1800	*attrflagp = 0;
1801	*dattrflagp = 0;
1802	if (namelen > NFS_MAXNAMLEN)
1803		return (ENAMETOOLONG);
1804	NFSCL_REQSTART(nd, NFSPROC_MKNOD, dvp);
1805	if (nd->nd_flag & ND_NFSV4) {
1806		if (vtyp == VBLK || vtyp == VCHR) {
1807			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1808			*tl++ = vtonfsv34_type(vtyp);
1809			*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1810			*tl = txdr_unsigned(NFSMINOR(rdev));
1811		} else {
1812			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1813			*tl = vtonfsv34_type(vtyp);
1814		}
1815	}
1816	(void) nfsm_strtom(nd, name, namelen);
1817	if (nd->nd_flag & ND_NFSV3) {
1818		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1819		*tl = vtonfsv34_type(vtyp);
1820	}
1821	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1822		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1823	if ((nd->nd_flag & ND_NFSV3) &&
1824	    (vtyp == VCHR || vtyp == VBLK)) {
1825		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1826		*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1827		*tl = txdr_unsigned(NFSMINOR(rdev));
1828	}
1829	if (nd->nd_flag & ND_NFSV4) {
1830		NFSGETATTR_ATTRBIT(&attrbits);
1831		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1832		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1833		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1834		(void) nfsrv_putattrbit(nd, &attrbits);
1835	}
1836	if (nd->nd_flag & ND_NFSV2)
1837		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZERDEV, rdev);
1838	error = nfscl_request(nd, dvp, p, cred, dstuff);
1839	if (error)
1840		return (error);
1841	if (nd->nd_flag & ND_NFSV4)
1842		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1843	if (!nd->nd_repstat) {
1844		if (nd->nd_flag & ND_NFSV4) {
1845			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1846			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1847			if (error)
1848				goto nfsmout;
1849		}
1850		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1851		if (error)
1852			goto nfsmout;
1853	}
1854	if (nd->nd_flag & ND_NFSV3)
1855		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1856	if (!error && nd->nd_repstat)
1857		error = nd->nd_repstat;
1858nfsmout:
1859	mbuf_freem(nd->nd_mrep);
1860	return (error);
1861}
1862
1863/*
1864 * nfs file create call
1865 * Mostly just call the approriate routine. (I separated out v4, so that
1866 * error recovery wouldn't be as difficult.)
1867 */
1868APPLESTATIC int
1869nfsrpc_create(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1870    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1871    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1872    int *attrflagp, int *dattrflagp, void *dstuff)
1873{
1874	int error = 0, newone, expireret = 0, retrycnt, unlocked;
1875	struct nfsclowner *owp;
1876	struct nfscldeleg *dp;
1877	struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
1878	u_int32_t clidrev;
1879
1880	if (NFSHASNFSV4(nmp)) {
1881	    retrycnt = 0;
1882	    do {
1883		dp = NULL;
1884		error = nfscl_open(dvp, NULL, 0, (NFSV4OPEN_ACCESSWRITE |
1885		    NFSV4OPEN_ACCESSREAD), 0, cred, p, &owp, NULL, &newone,
1886		    NULL, 1);
1887		if (error)
1888			return (error);
1889		if (nmp->nm_clp != NULL)
1890			clidrev = nmp->nm_clp->nfsc_clientidrev;
1891		else
1892			clidrev = 0;
1893		error = nfsrpc_createv4(dvp, name, namelen, vap, cverf, fmode,
1894		  owp, &dp, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1895		  dstuff, &unlocked);
1896		/*
1897		 * There is no need to invalidate cached attributes here,
1898		 * since new post-delegation issue attributes are always
1899		 * returned by nfsrpc_createv4() and these will update the
1900		 * attribute cache.
1901		 */
1902		if (dp != NULL)
1903			(void) nfscl_deleg(nmp->nm_mountp, owp->nfsow_clp,
1904			    (*nfhpp)->nfh_fh, (*nfhpp)->nfh_len, cred, p, &dp);
1905		nfscl_ownerrelease(nmp, owp, error, newone, unlocked);
1906		if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1907		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1908		    error == NFSERR_BADSESSION) {
1909			(void) nfs_catnap(PZERO, error, "nfs_open");
1910		} else if ((error == NFSERR_EXPIRED ||
1911		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1912			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1913			retrycnt++;
1914		}
1915	    } while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1916		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1917		error == NFSERR_BADSESSION ||
1918		((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1919		 expireret == 0 && clidrev != 0 && retrycnt < 4));
1920	    if (error && retrycnt >= 4)
1921		    error = EIO;
1922	} else {
1923		error = nfsrpc_createv23(dvp, name, namelen, vap, cverf,
1924		    fmode, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1925		    dstuff);
1926	}
1927	return (error);
1928}
1929
1930/*
1931 * The create rpc for v2 and 3.
1932 */
1933static int
1934nfsrpc_createv23(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1935    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1936    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1937    int *attrflagp, int *dattrflagp, void *dstuff)
1938{
1939	u_int32_t *tl;
1940	int error = 0;
1941	struct nfsrv_descript nfsd, *nd = &nfsd;
1942
1943	*nfhpp = NULL;
1944	*attrflagp = 0;
1945	*dattrflagp = 0;
1946	if (namelen > NFS_MAXNAMLEN)
1947		return (ENAMETOOLONG);
1948	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1949	(void) nfsm_strtom(nd, name, namelen);
1950	if (nd->nd_flag & ND_NFSV3) {
1951		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1952		if (fmode & O_EXCL) {
1953			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
1954			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1955			*tl++ = cverf.lval[0];
1956			*tl = cverf.lval[1];
1957		} else {
1958			*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
1959			nfscl_fillsattr(nd, vap, dvp, 0, 0);
1960		}
1961	} else {
1962		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZE0, 0);
1963	}
1964	error = nfscl_request(nd, dvp, p, cred, dstuff);
1965	if (error)
1966		return (error);
1967	if (nd->nd_repstat == 0) {
1968		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1969		if (error)
1970			goto nfsmout;
1971	}
1972	if (nd->nd_flag & ND_NFSV3)
1973		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1974	if (nd->nd_repstat != 0 && error == 0)
1975		error = nd->nd_repstat;
1976nfsmout:
1977	mbuf_freem(nd->nd_mrep);
1978	return (error);
1979}
1980
1981static int
1982nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1983    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
1984    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
1985    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
1986    int *dattrflagp, void *dstuff, int *unlockedp)
1987{
1988	u_int32_t *tl;
1989	int error = 0, deleg, newone, ret, acesize, limitby;
1990	struct nfsrv_descript nfsd, *nd = &nfsd;
1991	struct nfsclopen *op;
1992	struct nfscldeleg *dp = NULL;
1993	struct nfsnode *np;
1994	struct nfsfh *nfhp;
1995	nfsattrbit_t attrbits;
1996	nfsv4stateid_t stateid;
1997	u_int32_t rflags;
1998	struct nfsmount *nmp;
1999	struct nfsclsession *tsep;
2000
2001	nmp = VFSTONFS(dvp->v_mount);
2002	np = VTONFS(dvp);
2003	*unlockedp = 0;
2004	*nfhpp = NULL;
2005	*dpp = NULL;
2006	*attrflagp = 0;
2007	*dattrflagp = 0;
2008	if (namelen > NFS_MAXNAMLEN)
2009		return (ENAMETOOLONG);
2010	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
2011	/*
2012	 * For V4, this is actually an Open op.
2013	 */
2014	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2015	*tl++ = txdr_unsigned(owp->nfsow_seqid);
2016	*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
2017	    NFSV4OPEN_ACCESSREAD);
2018	*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
2019	tsep = nfsmnt_mdssession(nmp);
2020	*tl++ = tsep->nfsess_clientid.lval[0];
2021	*tl = tsep->nfsess_clientid.lval[1];
2022	(void) nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
2023	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2024	*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
2025	if (fmode & O_EXCL) {
2026		if (NFSHASNFSV4N(nmp)) {
2027			if (NFSHASSESSPERSIST(nmp)) {
2028				/* Use GUARDED for persistent sessions. */
2029				*tl = txdr_unsigned(NFSCREATE_GUARDED);
2030				nfscl_fillsattr(nd, vap, dvp, 0, 0);
2031			} else {
2032				/* Otherwise, use EXCLUSIVE4_1. */
2033				*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE41);
2034				NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
2035				*tl++ = cverf.lval[0];
2036				*tl = cverf.lval[1];
2037				nfscl_fillsattr(nd, vap, dvp, 0, 0);
2038			}
2039		} else {
2040			/* NFSv4.0 */
2041			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
2042			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
2043			*tl++ = cverf.lval[0];
2044			*tl = cverf.lval[1];
2045		}
2046	} else {
2047		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
2048		nfscl_fillsattr(nd, vap, dvp, 0, 0);
2049	}
2050	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2051	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
2052	(void) nfsm_strtom(nd, name, namelen);
2053	/* Get the new file's handle and attributes. */
2054	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2055	*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2056	*tl = txdr_unsigned(NFSV4OP_GETATTR);
2057	NFSGETATTR_ATTRBIT(&attrbits);
2058	(void) nfsrv_putattrbit(nd, &attrbits);
2059	/* Get the directory's post-op attributes. */
2060	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2061	*tl = txdr_unsigned(NFSV4OP_PUTFH);
2062	(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 0);
2063	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2064	*tl = txdr_unsigned(NFSV4OP_GETATTR);
2065	(void) nfsrv_putattrbit(nd, &attrbits);
2066	error = nfscl_request(nd, dvp, p, cred, dstuff);
2067	if (error)
2068		return (error);
2069	NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
2070	if (nd->nd_repstat == 0) {
2071		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
2072		    6 * NFSX_UNSIGNED);
2073		stateid.seqid = *tl++;
2074		stateid.other[0] = *tl++;
2075		stateid.other[1] = *tl++;
2076		stateid.other[2] = *tl;
2077		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
2078		(void) nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2079		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2080		deleg = fxdr_unsigned(int, *tl);
2081		if (deleg == NFSV4OPEN_DELEGATEREAD ||
2082		    deleg == NFSV4OPEN_DELEGATEWRITE) {
2083			if (!(owp->nfsow_clp->nfsc_flags &
2084			      NFSCLFLAGS_FIRSTDELEG))
2085				owp->nfsow_clp->nfsc_flags |=
2086				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
2087			MALLOC(dp, struct nfscldeleg *,
2088			    sizeof (struct nfscldeleg) + NFSX_V4FHMAX,
2089			    M_NFSCLDELEG, M_WAITOK);
2090			LIST_INIT(&dp->nfsdl_owner);
2091			LIST_INIT(&dp->nfsdl_lock);
2092			dp->nfsdl_clp = owp->nfsow_clp;
2093			newnfs_copyincred(cred, &dp->nfsdl_cred);
2094			nfscl_lockinit(&dp->nfsdl_rwlock);
2095			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
2096			    NFSX_UNSIGNED);
2097			dp->nfsdl_stateid.seqid = *tl++;
2098			dp->nfsdl_stateid.other[0] = *tl++;
2099			dp->nfsdl_stateid.other[1] = *tl++;
2100			dp->nfsdl_stateid.other[2] = *tl++;
2101			ret = fxdr_unsigned(int, *tl);
2102			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
2103				dp->nfsdl_flags = NFSCLDL_WRITE;
2104				/*
2105				 * Indicates how much the file can grow.
2106				 */
2107				NFSM_DISSECT(tl, u_int32_t *,
2108				    3 * NFSX_UNSIGNED);
2109				limitby = fxdr_unsigned(int, *tl++);
2110				switch (limitby) {
2111				case NFSV4OPEN_LIMITSIZE:
2112					dp->nfsdl_sizelimit = fxdr_hyper(tl);
2113					break;
2114				case NFSV4OPEN_LIMITBLOCKS:
2115					dp->nfsdl_sizelimit =
2116					    fxdr_unsigned(u_int64_t, *tl++);
2117					dp->nfsdl_sizelimit *=
2118					    fxdr_unsigned(u_int64_t, *tl);
2119					break;
2120				default:
2121					error = NFSERR_BADXDR;
2122					goto nfsmout;
2123				};
2124			} else {
2125				dp->nfsdl_flags = NFSCLDL_READ;
2126			}
2127			if (ret)
2128				dp->nfsdl_flags |= NFSCLDL_RECALL;
2129			error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
2130			    &acesize, p);
2131			if (error)
2132				goto nfsmout;
2133		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
2134			error = NFSERR_BADXDR;
2135			goto nfsmout;
2136		}
2137		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2138		if (error)
2139			goto nfsmout;
2140		/* Get rid of the PutFH and Getattr status values. */
2141		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
2142		/* Load the directory attributes. */
2143		error = nfsm_loadattr(nd, dnap);
2144		if (error)
2145			goto nfsmout;
2146		*dattrflagp = 1;
2147		if (dp != NULL && *attrflagp) {
2148			dp->nfsdl_change = nnap->na_filerev;
2149			dp->nfsdl_modtime = nnap->na_mtime;
2150			dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
2151		}
2152		/*
2153		 * We can now complete the Open state.
2154		 */
2155		nfhp = *nfhpp;
2156		if (dp != NULL) {
2157			dp->nfsdl_fhlen = nfhp->nfh_len;
2158			NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh, nfhp->nfh_len);
2159		}
2160		/*
2161		 * Get an Open structure that will be
2162		 * attached to the OpenOwner, acquired already.
2163		 */
2164		error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len,
2165		    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
2166		    cred, p, NULL, &op, &newone, NULL, 0);
2167		if (error)
2168			goto nfsmout;
2169		op->nfso_stateid = stateid;
2170		newnfs_copyincred(cred, &op->nfso_cred);
2171		if ((rflags & NFSV4OPEN_RESULTCONFIRM)) {
2172		    do {
2173			ret = nfsrpc_openconfirm(dvp, nfhp->nfh_fh,
2174			    nfhp->nfh_len, op, cred, p);
2175			if (ret == NFSERR_DELAY)
2176			    (void) nfs_catnap(PZERO, ret, "nfs_create");
2177		    } while (ret == NFSERR_DELAY);
2178		    error = ret;
2179		}
2180
2181		/*
2182		 * If the server is handing out delegations, but we didn't
2183		 * get one because an OpenConfirm was required, try the
2184		 * Open again, to get a delegation. This is a harmless no-op,
2185		 * from a server's point of view.
2186		 */
2187		if ((rflags & NFSV4OPEN_RESULTCONFIRM) &&
2188		    (owp->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG) &&
2189		    !error && dp == NULL) {
2190		    do {
2191			ret = nfsrpc_openrpc(VFSTONFS(vnode_mount(dvp)), dvp,
2192			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
2193			    nfhp->nfh_fh, nfhp->nfh_len,
2194			    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), op,
2195			    name, namelen, &dp, 0, 0x0, cred, p, 0, 1);
2196			if (ret == NFSERR_DELAY)
2197			    (void) nfs_catnap(PZERO, ret, "nfs_crt2");
2198		    } while (ret == NFSERR_DELAY);
2199		    if (ret) {
2200			if (dp != NULL) {
2201				FREE((caddr_t)dp, M_NFSCLDELEG);
2202				dp = NULL;
2203			}
2204			if (ret == NFSERR_STALECLIENTID ||
2205			    ret == NFSERR_STALEDONTRECOVER ||
2206			    ret == NFSERR_BADSESSION)
2207				error = ret;
2208		    }
2209		}
2210		nfscl_openrelease(nmp, op, error, newone);
2211		*unlockedp = 1;
2212	}
2213	if (nd->nd_repstat != 0 && error == 0)
2214		error = nd->nd_repstat;
2215	if (error == NFSERR_STALECLIENTID)
2216		nfscl_initiate_recovery(owp->nfsow_clp);
2217nfsmout:
2218	if (!error)
2219		*dpp = dp;
2220	else if (dp != NULL)
2221		FREE((caddr_t)dp, M_NFSCLDELEG);
2222	mbuf_freem(nd->nd_mrep);
2223	return (error);
2224}
2225
2226/*
2227 * Nfs remove rpc
2228 */
2229APPLESTATIC int
2230nfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp,
2231    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp,
2232    void *dstuff)
2233{
2234	u_int32_t *tl;
2235	struct nfsrv_descript nfsd, *nd = &nfsd;
2236	struct nfsnode *np;
2237	struct nfsmount *nmp;
2238	nfsv4stateid_t dstateid;
2239	int error, ret = 0, i;
2240
2241	*dattrflagp = 0;
2242	if (namelen > NFS_MAXNAMLEN)
2243		return (ENAMETOOLONG);
2244	nmp = VFSTONFS(vnode_mount(dvp));
2245tryagain:
2246	if (NFSHASNFSV4(nmp) && ret == 0) {
2247		ret = nfscl_removedeleg(vp, p, &dstateid);
2248		if (ret == 1) {
2249			NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp);
2250			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
2251			    NFSX_UNSIGNED);
2252			if (NFSHASNFSV4N(nmp))
2253				*tl++ = 0;
2254			else
2255				*tl++ = dstateid.seqid;
2256			*tl++ = dstateid.other[0];
2257			*tl++ = dstateid.other[1];
2258			*tl++ = dstateid.other[2];
2259			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2260			np = VTONFS(dvp);
2261			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2262			    np->n_fhp->nfh_len, 0);
2263			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2264			*tl = txdr_unsigned(NFSV4OP_REMOVE);
2265		}
2266	} else {
2267		ret = 0;
2268	}
2269	if (ret == 0)
2270		NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp);
2271	(void) nfsm_strtom(nd, name, namelen);
2272	error = nfscl_request(nd, dvp, p, cred, dstuff);
2273	if (error)
2274		return (error);
2275	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2276		/* For NFSv4, parse out any Delereturn replies. */
2277		if (ret > 0 && nd->nd_repstat != 0 &&
2278		    (nd->nd_flag & ND_NOMOREDATA)) {
2279			/*
2280			 * If the Delegreturn failed, try again without
2281			 * it. The server will Recall, as required.
2282			 */
2283			mbuf_freem(nd->nd_mrep);
2284			goto tryagain;
2285		}
2286		for (i = 0; i < (ret * 2); i++) {
2287			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2288			    ND_NFSV4) {
2289			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2290			    if (*(tl + 1))
2291				nd->nd_flag |= ND_NOMOREDATA;
2292			}
2293		}
2294		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2295	}
2296	if (nd->nd_repstat && !error)
2297		error = nd->nd_repstat;
2298nfsmout:
2299	mbuf_freem(nd->nd_mrep);
2300	return (error);
2301}
2302
2303/*
2304 * Do an nfs rename rpc.
2305 */
2306APPLESTATIC int
2307nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen,
2308    vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred *cred,
2309    NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap,
2310    int *fattrflagp, int *tattrflagp, void *fstuff, void *tstuff)
2311{
2312	u_int32_t *tl;
2313	struct nfsrv_descript nfsd, *nd = &nfsd;
2314	struct nfsmount *nmp;
2315	struct nfsnode *np;
2316	nfsattrbit_t attrbits;
2317	nfsv4stateid_t fdstateid, tdstateid;
2318	int error = 0, ret = 0, gottd = 0, gotfd = 0, i;
2319
2320	*fattrflagp = 0;
2321	*tattrflagp = 0;
2322	nmp = VFSTONFS(vnode_mount(fdvp));
2323	if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN)
2324		return (ENAMETOOLONG);
2325tryagain:
2326	if (NFSHASNFSV4(nmp) && ret == 0) {
2327		ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp,
2328		    &tdstateid, &gottd, p);
2329		if (gotfd && gottd) {
2330			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME2, fvp);
2331		} else if (gotfd) {
2332			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, fvp);
2333		} else if (gottd) {
2334			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, tvp);
2335		}
2336		if (gotfd) {
2337			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2338			if (NFSHASNFSV4N(nmp))
2339				*tl++ = 0;
2340			else
2341				*tl++ = fdstateid.seqid;
2342			*tl++ = fdstateid.other[0];
2343			*tl++ = fdstateid.other[1];
2344			*tl = fdstateid.other[2];
2345			if (gottd) {
2346				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2347				*tl = txdr_unsigned(NFSV4OP_PUTFH);
2348				np = VTONFS(tvp);
2349				(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2350				    np->n_fhp->nfh_len, 0);
2351				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2352				*tl = txdr_unsigned(NFSV4OP_DELEGRETURN);
2353			}
2354		}
2355		if (gottd) {
2356			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2357			if (NFSHASNFSV4N(nmp))
2358				*tl++ = 0;
2359			else
2360				*tl++ = tdstateid.seqid;
2361			*tl++ = tdstateid.other[0];
2362			*tl++ = tdstateid.other[1];
2363			*tl = tdstateid.other[2];
2364		}
2365		if (ret > 0) {
2366			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2367			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2368			np = VTONFS(fdvp);
2369			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2370			    np->n_fhp->nfh_len, 0);
2371			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2372			*tl = txdr_unsigned(NFSV4OP_SAVEFH);
2373		}
2374	} else {
2375		ret = 0;
2376	}
2377	if (ret == 0)
2378		NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp);
2379	if (nd->nd_flag & ND_NFSV4) {
2380		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2381		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2382		NFSWCCATTR_ATTRBIT(&attrbits);
2383		(void) nfsrv_putattrbit(nd, &attrbits);
2384		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2385		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2386		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2387		    VTONFS(tdvp)->n_fhp->nfh_len, 0);
2388		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2389		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2390		(void) nfsrv_putattrbit(nd, &attrbits);
2391		nd->nd_flag |= ND_V4WCCATTR;
2392		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2393		*tl = txdr_unsigned(NFSV4OP_RENAME);
2394	}
2395	(void) nfsm_strtom(nd, fnameptr, fnamelen);
2396	if (!(nd->nd_flag & ND_NFSV4))
2397		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2398			VTONFS(tdvp)->n_fhp->nfh_len, 0);
2399	(void) nfsm_strtom(nd, tnameptr, tnamelen);
2400	error = nfscl_request(nd, fdvp, p, cred, fstuff);
2401	if (error)
2402		return (error);
2403	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2404		/* For NFSv4, parse out any Delereturn replies. */
2405		if (ret > 0 && nd->nd_repstat != 0 &&
2406		    (nd->nd_flag & ND_NOMOREDATA)) {
2407			/*
2408			 * If the Delegreturn failed, try again without
2409			 * it. The server will Recall, as required.
2410			 */
2411			mbuf_freem(nd->nd_mrep);
2412			goto tryagain;
2413		}
2414		for (i = 0; i < (ret * 2); i++) {
2415			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2416			    ND_NFSV4) {
2417			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2418			    if (*(tl + 1)) {
2419				if (i == 0 && ret > 1) {
2420				    /*
2421				     * If the Delegreturn failed, try again
2422				     * without it. The server will Recall, as
2423				     * required.
2424				     * If ret > 1, the first iteration of this
2425				     * loop is the second DelegReturn result.
2426				     */
2427				    mbuf_freem(nd->nd_mrep);
2428				    goto tryagain;
2429				} else {
2430				    nd->nd_flag |= ND_NOMOREDATA;
2431				}
2432			    }
2433			}
2434		}
2435		/* Now, the first wcc attribute reply. */
2436		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2437			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2438			if (*(tl + 1))
2439				nd->nd_flag |= ND_NOMOREDATA;
2440		}
2441		error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL,
2442		    fstuff);
2443		/* and the second wcc attribute reply. */
2444		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
2445		    !error) {
2446			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2447			if (*(tl + 1))
2448				nd->nd_flag |= ND_NOMOREDATA;
2449		}
2450		if (!error)
2451			error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp,
2452			    NULL, tstuff);
2453	}
2454	if (nd->nd_repstat && !error)
2455		error = nd->nd_repstat;
2456nfsmout:
2457	mbuf_freem(nd->nd_mrep);
2458	return (error);
2459}
2460
2461/*
2462 * nfs hard link create rpc
2463 */
2464APPLESTATIC int
2465nfsrpc_link(vnode_t dvp, vnode_t vp, char *name, int namelen,
2466    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2467    struct nfsvattr *nap, int *attrflagp, int *dattrflagp, void *dstuff)
2468{
2469	u_int32_t *tl;
2470	struct nfsrv_descript nfsd, *nd = &nfsd;
2471	nfsattrbit_t attrbits;
2472	int error = 0;
2473
2474	*attrflagp = 0;
2475	*dattrflagp = 0;
2476	if (namelen > NFS_MAXNAMLEN)
2477		return (ENAMETOOLONG);
2478	NFSCL_REQSTART(nd, NFSPROC_LINK, vp);
2479	if (nd->nd_flag & ND_NFSV4) {
2480		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2481		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2482	}
2483	(void) nfsm_fhtom(nd, VTONFS(dvp)->n_fhp->nfh_fh,
2484		VTONFS(dvp)->n_fhp->nfh_len, 0);
2485	if (nd->nd_flag & ND_NFSV4) {
2486		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2487		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2488		NFSWCCATTR_ATTRBIT(&attrbits);
2489		(void) nfsrv_putattrbit(nd, &attrbits);
2490		nd->nd_flag |= ND_V4WCCATTR;
2491		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2492		*tl = txdr_unsigned(NFSV4OP_LINK);
2493	}
2494	(void) nfsm_strtom(nd, name, namelen);
2495	error = nfscl_request(nd, vp, p, cred, dstuff);
2496	if (error)
2497		return (error);
2498	if (nd->nd_flag & ND_NFSV3) {
2499		error = nfscl_postop_attr(nd, nap, attrflagp, dstuff);
2500		if (!error)
2501			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2502			    NULL, dstuff);
2503	} else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2504		/*
2505		 * First, parse out the PutFH and Getattr result.
2506		 */
2507		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2508		if (!(*(tl + 1)))
2509			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2510		if (*(tl + 1))
2511			nd->nd_flag |= ND_NOMOREDATA;
2512		/*
2513		 * Get the pre-op attributes.
2514		 */
2515		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2516	}
2517	if (nd->nd_repstat && !error)
2518		error = nd->nd_repstat;
2519nfsmout:
2520	mbuf_freem(nd->nd_mrep);
2521	return (error);
2522}
2523
2524/*
2525 * nfs symbolic link create rpc
2526 */
2527APPLESTATIC int
2528nfsrpc_symlink(vnode_t dvp, char *name, int namelen, char *target,
2529    struct vattr *vap, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2530    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2531    int *dattrflagp, void *dstuff)
2532{
2533	u_int32_t *tl;
2534	struct nfsrv_descript nfsd, *nd = &nfsd;
2535	struct nfsmount *nmp;
2536	int slen, error = 0;
2537
2538	*nfhpp = NULL;
2539	*attrflagp = 0;
2540	*dattrflagp = 0;
2541	nmp = VFSTONFS(vnode_mount(dvp));
2542	slen = strlen(target);
2543	if (slen > NFS_MAXPATHLEN || namelen > NFS_MAXNAMLEN)
2544		return (ENAMETOOLONG);
2545	NFSCL_REQSTART(nd, NFSPROC_SYMLINK, dvp);
2546	if (nd->nd_flag & ND_NFSV4) {
2547		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2548		*tl = txdr_unsigned(NFLNK);
2549		(void) nfsm_strtom(nd, target, slen);
2550	}
2551	(void) nfsm_strtom(nd, name, namelen);
2552	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2553		nfscl_fillsattr(nd, vap, dvp, 0, 0);
2554	if (!(nd->nd_flag & ND_NFSV4))
2555		(void) nfsm_strtom(nd, target, slen);
2556	if (nd->nd_flag & ND_NFSV2)
2557		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2558	error = nfscl_request(nd, dvp, p, cred, dstuff);
2559	if (error)
2560		return (error);
2561	if (nd->nd_flag & ND_NFSV4)
2562		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2563	if ((nd->nd_flag & ND_NFSV3) && !error) {
2564		if (!nd->nd_repstat)
2565			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2566		if (!error)
2567			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2568			    NULL, dstuff);
2569	}
2570	if (nd->nd_repstat && !error)
2571		error = nd->nd_repstat;
2572	mbuf_freem(nd->nd_mrep);
2573	/*
2574	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2575	 * Only do this if vfs.nfs.ignore_eexist is set.
2576	 * Never do this for NFSv4.1 or later minor versions, since sessions
2577	 * should guarantee "exactly once" RPC semantics.
2578	 */
2579	if (error == EEXIST && nfsignore_eexist != 0 && (!NFSHASNFSV4(nmp) ||
2580	    nmp->nm_minorvers == 0))
2581		error = 0;
2582	return (error);
2583}
2584
2585/*
2586 * nfs make dir rpc
2587 */
2588APPLESTATIC int
2589nfsrpc_mkdir(vnode_t dvp, char *name, int namelen, struct vattr *vap,
2590    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2591    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2592    int *dattrflagp, void *dstuff)
2593{
2594	u_int32_t *tl;
2595	struct nfsrv_descript nfsd, *nd = &nfsd;
2596	nfsattrbit_t attrbits;
2597	int error = 0;
2598	struct nfsfh *fhp;
2599	struct nfsmount *nmp;
2600
2601	*nfhpp = NULL;
2602	*attrflagp = 0;
2603	*dattrflagp = 0;
2604	nmp = VFSTONFS(vnode_mount(dvp));
2605	fhp = VTONFS(dvp)->n_fhp;
2606	if (namelen > NFS_MAXNAMLEN)
2607		return (ENAMETOOLONG);
2608	NFSCL_REQSTART(nd, NFSPROC_MKDIR, dvp);
2609	if (nd->nd_flag & ND_NFSV4) {
2610		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2611		*tl = txdr_unsigned(NFDIR);
2612	}
2613	(void) nfsm_strtom(nd, name, namelen);
2614	nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2615	if (nd->nd_flag & ND_NFSV4) {
2616		NFSGETATTR_ATTRBIT(&attrbits);
2617		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2618		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2619		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2620		(void) nfsrv_putattrbit(nd, &attrbits);
2621		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2622		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2623		(void) nfsm_fhtom(nd, fhp->nfh_fh, fhp->nfh_len, 0);
2624		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2625		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2626		(void) nfsrv_putattrbit(nd, &attrbits);
2627	}
2628	error = nfscl_request(nd, dvp, p, cred, dstuff);
2629	if (error)
2630		return (error);
2631	if (nd->nd_flag & ND_NFSV4)
2632		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2633	if (!nd->nd_repstat && !error) {
2634		if (nd->nd_flag & ND_NFSV4) {
2635			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2636			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2637		}
2638		if (!error)
2639			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2640		if (error == 0 && (nd->nd_flag & ND_NFSV4) != 0) {
2641			/* Get rid of the PutFH and Getattr status values. */
2642			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
2643			/* Load the directory attributes. */
2644			error = nfsm_loadattr(nd, dnap);
2645			if (error == 0)
2646				*dattrflagp = 1;
2647		}
2648	}
2649	if ((nd->nd_flag & ND_NFSV3) && !error)
2650		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2651	if (nd->nd_repstat && !error)
2652		error = nd->nd_repstat;
2653nfsmout:
2654	mbuf_freem(nd->nd_mrep);
2655	/*
2656	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2657	 * Only do this if vfs.nfs.ignore_eexist is set.
2658	 * Never do this for NFSv4.1 or later minor versions, since sessions
2659	 * should guarantee "exactly once" RPC semantics.
2660	 */
2661	if (error == EEXIST && nfsignore_eexist != 0 && (!NFSHASNFSV4(nmp) ||
2662	    nmp->nm_minorvers == 0))
2663		error = 0;
2664	return (error);
2665}
2666
2667/*
2668 * nfs remove directory call
2669 */
2670APPLESTATIC int
2671nfsrpc_rmdir(vnode_t dvp, char *name, int namelen, struct ucred *cred,
2672    NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp, void *dstuff)
2673{
2674	struct nfsrv_descript nfsd, *nd = &nfsd;
2675	int error = 0;
2676
2677	*dattrflagp = 0;
2678	if (namelen > NFS_MAXNAMLEN)
2679		return (ENAMETOOLONG);
2680	NFSCL_REQSTART(nd, NFSPROC_RMDIR, dvp);
2681	(void) nfsm_strtom(nd, name, namelen);
2682	error = nfscl_request(nd, dvp, p, cred, dstuff);
2683	if (error)
2684		return (error);
2685	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2686		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2687	if (nd->nd_repstat && !error)
2688		error = nd->nd_repstat;
2689	mbuf_freem(nd->nd_mrep);
2690	/*
2691	 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2692	 */
2693	if (error == ENOENT)
2694		error = 0;
2695	return (error);
2696}
2697
2698/*
2699 * Readdir rpc.
2700 * Always returns with either uio_resid unchanged, if you are at the
2701 * end of the directory, or uio_resid == 0, with all DIRBLKSIZ chunks
2702 * filled in.
2703 * I felt this would allow caching of directory blocks more easily
2704 * than returning a pertially filled block.
2705 * Directory offset cookies:
2706 * Oh my, what to do with them...
2707 * I can think of three ways to deal with them:
2708 * 1 - have the layer above these RPCs maintain a map between logical
2709 *     directory byte offsets and the NFS directory offset cookies
2710 * 2 - pass the opaque directory offset cookies up into userland
2711 *     and let the libc functions deal with them, via the system call
2712 * 3 - return them to userland in the "struct dirent", so future versions
2713 *     of libc can use them and do whatever is necessary to amke things work
2714 *     above these rpc calls, in the meantime
2715 * For now, I do #3 by "hiding" the directory offset cookies after the
2716 * d_name field in struct dirent. This is space inside d_reclen that
2717 * will be ignored by anything that doesn't know about them.
2718 * The directory offset cookies are filled in as the last 8 bytes of
2719 * each directory entry, after d_name. Someday, the userland libc
2720 * functions may be able to use these. In the meantime, it satisfies
2721 * OpenBSD's requirements for cookies being returned.
2722 * If expects the directory offset cookie for the read to be in uio_offset
2723 * and returns the one for the next entry after this directory block in
2724 * there, as well.
2725 */
2726APPLESTATIC int
2727nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2728    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2729    int *eofp, void *stuff)
2730{
2731	int len, left;
2732	struct dirent *dp = NULL;
2733	u_int32_t *tl;
2734	nfsquad_t cookie, ncookie;
2735	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2736	struct nfsnode *dnp = VTONFS(vp);
2737	struct nfsvattr nfsva;
2738	struct nfsrv_descript nfsd, *nd = &nfsd;
2739	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2740	int reqsize, tryformoredirs = 1, readsize, eof = 0, gotmnton = 0;
2741	long dotfileid, dotdotfileid = 0;
2742	u_int32_t fakefileno = 0xffffffff, rderr;
2743	char *cp;
2744	nfsattrbit_t attrbits, dattrbits;
2745	u_int32_t *tl2 = NULL;
2746	size_t tresid;
2747
2748	KASSERT(uiop->uio_iovcnt == 1 &&
2749	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
2750	    ("nfs readdirrpc bad uio"));
2751
2752	/*
2753	 * There is no point in reading a lot more than uio_resid, however
2754	 * adding one additional DIRBLKSIZ makes sense. Since uio_resid
2755	 * and nm_readdirsize are both exact multiples of DIRBLKSIZ, this
2756	 * will never make readsize > nm_readdirsize.
2757	 */
2758	readsize = nmp->nm_readdirsize;
2759	if (readsize > uio_uio_resid(uiop))
2760		readsize = uio_uio_resid(uiop) + DIRBLKSIZ;
2761
2762	*attrflagp = 0;
2763	if (eofp)
2764		*eofp = 0;
2765	tresid = uio_uio_resid(uiop);
2766	cookie.lval[0] = cookiep->nfsuquad[0];
2767	cookie.lval[1] = cookiep->nfsuquad[1];
2768	nd->nd_mrep = NULL;
2769
2770	/*
2771	 * For NFSv4, first create the "." and ".." entries.
2772	 */
2773	if (NFSHASNFSV4(nmp)) {
2774		reqsize = 6 * NFSX_UNSIGNED;
2775		NFSGETATTR_ATTRBIT(&dattrbits);
2776		NFSZERO_ATTRBIT(&attrbits);
2777		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
2778		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TYPE);
2779		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
2780		    NFSATTRBIT_MOUNTEDONFILEID)) {
2781			NFSSETBIT_ATTRBIT(&attrbits,
2782			    NFSATTRBIT_MOUNTEDONFILEID);
2783			gotmnton = 1;
2784		} else {
2785			/*
2786			 * Must fake it. Use the fileno, except when the
2787			 * fsid is != to that of the directory. For that
2788			 * case, generate a fake fileno that is not the same.
2789			 */
2790			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
2791			gotmnton = 0;
2792		}
2793
2794		/*
2795		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
2796		 */
2797		if (uiop->uio_offset == 0) {
2798			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
2799			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2800			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2801			*tl = txdr_unsigned(NFSV4OP_GETATTR);
2802			(void) nfsrv_putattrbit(nd, &attrbits);
2803			error = nfscl_request(nd, vp, p, cred, stuff);
2804			if (error)
2805			    return (error);
2806			dotfileid = 0;	/* Fake out the compiler. */
2807			if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
2808			    error = nfsm_loadattr(nd, &nfsva);
2809			    if (error != 0)
2810				goto nfsmout;
2811			    dotfileid = nfsva.na_fileid;
2812			}
2813			if (nd->nd_repstat == 0) {
2814			    NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2815			    len = fxdr_unsigned(int, *(tl + 4));
2816			    if (len > 0 && len <= NFSX_V4FHMAX)
2817				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2818			    else
2819				error = EPERM;
2820			    if (!error) {
2821				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2822				nfsva.na_mntonfileno = 0xffffffff;
2823				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2824				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2825				    NULL, NULL, NULL, p, cred);
2826				if (error) {
2827				    dotdotfileid = dotfileid;
2828				} else if (gotmnton) {
2829				    if (nfsva.na_mntonfileno != 0xffffffff)
2830					dotdotfileid = nfsva.na_mntonfileno;
2831				    else
2832					dotdotfileid = nfsva.na_fileid;
2833				} else if (nfsva.na_filesid[0] ==
2834				    dnp->n_vattr.na_filesid[0] &&
2835				    nfsva.na_filesid[1] ==
2836				    dnp->n_vattr.na_filesid[1]) {
2837				    dotdotfileid = nfsva.na_fileid;
2838				} else {
2839				    do {
2840					fakefileno--;
2841				    } while (fakefileno ==
2842					nfsva.na_fileid);
2843				    dotdotfileid = fakefileno;
2844				}
2845			    }
2846			} else if (nd->nd_repstat == NFSERR_NOENT) {
2847			    /*
2848			     * Lookupp returns NFSERR_NOENT when we are
2849			     * at the root, so just use the current dir.
2850			     */
2851			    nd->nd_repstat = 0;
2852			    dotdotfileid = dotfileid;
2853			} else {
2854			    error = nd->nd_repstat;
2855			}
2856			mbuf_freem(nd->nd_mrep);
2857			if (error)
2858			    return (error);
2859			nd->nd_mrep = NULL;
2860			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2861			dp->d_type = DT_DIR;
2862			dp->d_fileno = dotfileid;
2863			dp->d_namlen = 1;
2864			dp->d_name[0] = '.';
2865			dp->d_name[1] = '\0';
2866			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2867			/*
2868			 * Just make these offset cookie 0.
2869			 */
2870			tl = (u_int32_t *)&dp->d_name[4];
2871			*tl++ = 0;
2872			*tl = 0;
2873			blksiz += dp->d_reclen;
2874			uio_uio_resid_add(uiop, -(dp->d_reclen));
2875			uiop->uio_offset += dp->d_reclen;
2876			uio_iov_base_add(uiop, dp->d_reclen);
2877			uio_iov_len_add(uiop, -(dp->d_reclen));
2878			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2879			dp->d_type = DT_DIR;
2880			dp->d_fileno = dotdotfileid;
2881			dp->d_namlen = 2;
2882			dp->d_name[0] = '.';
2883			dp->d_name[1] = '.';
2884			dp->d_name[2] = '\0';
2885			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2886			/*
2887			 * Just make these offset cookie 0.
2888			 */
2889			tl = (u_int32_t *)&dp->d_name[4];
2890			*tl++ = 0;
2891			*tl = 0;
2892			blksiz += dp->d_reclen;
2893			uio_uio_resid_add(uiop, -(dp->d_reclen));
2894			uiop->uio_offset += dp->d_reclen;
2895			uio_iov_base_add(uiop, dp->d_reclen);
2896			uio_iov_len_add(uiop, -(dp->d_reclen));
2897		}
2898		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_RDATTRERROR);
2899	} else {
2900		reqsize = 5 * NFSX_UNSIGNED;
2901	}
2902
2903
2904	/*
2905	 * Loop around doing readdir rpc's of size readsize.
2906	 * The stopping criteria is EOF or buffer full.
2907	 */
2908	while (more_dirs && bigenough) {
2909		*attrflagp = 0;
2910		NFSCL_REQSTART(nd, NFSPROC_READDIR, vp);
2911		if (nd->nd_flag & ND_NFSV2) {
2912			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2913			*tl++ = cookie.lval[1];
2914			*tl = txdr_unsigned(readsize);
2915		} else {
2916			NFSM_BUILD(tl, u_int32_t *, reqsize);
2917			*tl++ = cookie.lval[0];
2918			*tl++ = cookie.lval[1];
2919			if (cookie.qval == 0) {
2920				*tl++ = 0;
2921				*tl++ = 0;
2922			} else {
2923				NFSLOCKNODE(dnp);
2924				*tl++ = dnp->n_cookieverf.nfsuquad[0];
2925				*tl++ = dnp->n_cookieverf.nfsuquad[1];
2926				NFSUNLOCKNODE(dnp);
2927			}
2928			if (nd->nd_flag & ND_NFSV4) {
2929				*tl++ = txdr_unsigned(readsize);
2930				*tl = txdr_unsigned(readsize);
2931				(void) nfsrv_putattrbit(nd, &attrbits);
2932				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2933				*tl = txdr_unsigned(NFSV4OP_GETATTR);
2934				(void) nfsrv_putattrbit(nd, &dattrbits);
2935			} else {
2936				*tl = txdr_unsigned(readsize);
2937			}
2938		}
2939		error = nfscl_request(nd, vp, p, cred, stuff);
2940		if (error)
2941			return (error);
2942		if (!(nd->nd_flag & ND_NFSV2)) {
2943			if (nd->nd_flag & ND_NFSV3)
2944				error = nfscl_postop_attr(nd, nap, attrflagp,
2945				    stuff);
2946			if (!nd->nd_repstat && !error) {
2947				NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
2948				NFSLOCKNODE(dnp);
2949				dnp->n_cookieverf.nfsuquad[0] = *tl++;
2950				dnp->n_cookieverf.nfsuquad[1] = *tl;
2951				NFSUNLOCKNODE(dnp);
2952			}
2953		}
2954		if (nd->nd_repstat || error) {
2955			if (!error)
2956				error = nd->nd_repstat;
2957			goto nfsmout;
2958		}
2959		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2960		more_dirs = fxdr_unsigned(int, *tl);
2961		if (!more_dirs)
2962			tryformoredirs = 0;
2963
2964		/* loop thru the dir entries, doctoring them to 4bsd form */
2965		while (more_dirs && bigenough) {
2966			if (nd->nd_flag & ND_NFSV4) {
2967				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2968				ncookie.lval[0] = *tl++;
2969				ncookie.lval[1] = *tl++;
2970				len = fxdr_unsigned(int, *tl);
2971			} else if (nd->nd_flag & ND_NFSV3) {
2972				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2973				nfsva.na_fileid = fxdr_hyper(tl);
2974				tl += 2;
2975				len = fxdr_unsigned(int, *tl);
2976			} else {
2977				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2978				nfsva.na_fileid =
2979				    fxdr_unsigned(long, *tl++);
2980				len = fxdr_unsigned(int, *tl);
2981			}
2982			if (len <= 0 || len > NFS_MAXNAMLEN) {
2983				error = EBADRPC;
2984				goto nfsmout;
2985			}
2986			tlen = NFSM_RNDUP(len);
2987			if (tlen == len)
2988				tlen += 4;  /* To ensure null termination */
2989			left = DIRBLKSIZ - blksiz;
2990			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > left) {
2991				dp->d_reclen += left;
2992				uio_iov_base_add(uiop, left);
2993				uio_iov_len_add(uiop, -(left));
2994				uio_uio_resid_add(uiop, -(left));
2995				uiop->uio_offset += left;
2996				blksiz = 0;
2997			}
2998			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
2999				bigenough = 0;
3000			if (bigenough) {
3001				dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3002				dp->d_namlen = len;
3003				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
3004				dp->d_type = DT_UNKNOWN;
3005				blksiz += dp->d_reclen;
3006				if (blksiz == DIRBLKSIZ)
3007					blksiz = 0;
3008				uio_uio_resid_add(uiop, -(DIRHDSIZ));
3009				uiop->uio_offset += DIRHDSIZ;
3010				uio_iov_base_add(uiop, DIRHDSIZ);
3011				uio_iov_len_add(uiop, -(DIRHDSIZ));
3012				error = nfsm_mbufuio(nd, uiop, len);
3013				if (error)
3014					goto nfsmout;
3015				cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3016				tlen -= len;
3017				*cp = '\0';	/* null terminate */
3018				cp += tlen;	/* points to cookie storage */
3019				tl2 = (u_int32_t *)cp;
3020				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
3021				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
3022				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
3023				uiop->uio_offset += (tlen + NFSX_HYPER);
3024			} else {
3025				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3026				if (error)
3027					goto nfsmout;
3028			}
3029			if (nd->nd_flag & ND_NFSV4) {
3030				rderr = 0;
3031				nfsva.na_mntonfileno = 0xffffffff;
3032				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
3033				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3034				    NULL, NULL, &rderr, p, cred);
3035				if (error)
3036					goto nfsmout;
3037				NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3038			} else if (nd->nd_flag & ND_NFSV3) {
3039				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3040				ncookie.lval[0] = *tl++;
3041				ncookie.lval[1] = *tl++;
3042			} else {
3043				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3044				ncookie.lval[0] = 0;
3045				ncookie.lval[1] = *tl++;
3046			}
3047			if (bigenough) {
3048			    if (nd->nd_flag & ND_NFSV4) {
3049				if (rderr) {
3050				    dp->d_fileno = 0;
3051				} else {
3052				    if (gotmnton) {
3053					if (nfsva.na_mntonfileno != 0xffffffff)
3054					    dp->d_fileno = nfsva.na_mntonfileno;
3055					else
3056					    dp->d_fileno = nfsva.na_fileid;
3057				    } else if (nfsva.na_filesid[0] ==
3058					dnp->n_vattr.na_filesid[0] &&
3059					nfsva.na_filesid[1] ==
3060					dnp->n_vattr.na_filesid[1]) {
3061					dp->d_fileno = nfsva.na_fileid;
3062				    } else {
3063					do {
3064					    fakefileno--;
3065					} while (fakefileno ==
3066					    nfsva.na_fileid);
3067					dp->d_fileno = fakefileno;
3068				    }
3069				    dp->d_type = vtonfs_dtype(nfsva.na_type);
3070				}
3071			    } else {
3072				dp->d_fileno = nfsva.na_fileid;
3073			    }
3074			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
3075				ncookie.lval[0];
3076			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
3077				ncookie.lval[1];
3078			}
3079			more_dirs = fxdr_unsigned(int, *tl);
3080		}
3081		/*
3082		 * If at end of rpc data, get the eof boolean
3083		 */
3084		if (!more_dirs) {
3085			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3086			eof = fxdr_unsigned(int, *tl);
3087			if (tryformoredirs)
3088				more_dirs = !eof;
3089			if (nd->nd_flag & ND_NFSV4) {
3090				error = nfscl_postop_attr(nd, nap, attrflagp,
3091				    stuff);
3092				if (error)
3093					goto nfsmout;
3094			}
3095		}
3096		mbuf_freem(nd->nd_mrep);
3097		nd->nd_mrep = NULL;
3098	}
3099	/*
3100	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3101	 * by increasing d_reclen for the last record.
3102	 */
3103	if (blksiz > 0) {
3104		left = DIRBLKSIZ - blksiz;
3105		dp->d_reclen += left;
3106		uio_iov_base_add(uiop, left);
3107		uio_iov_len_add(uiop, -(left));
3108		uio_uio_resid_add(uiop, -(left));
3109		uiop->uio_offset += left;
3110	}
3111
3112	/*
3113	 * If returning no data, assume end of file.
3114	 * If not bigenough, return not end of file, since you aren't
3115	 *    returning all the data
3116	 * Otherwise, return the eof flag from the server.
3117	 */
3118	if (eofp) {
3119		if (tresid == ((size_t)(uio_uio_resid(uiop))))
3120			*eofp = 1;
3121		else if (!bigenough)
3122			*eofp = 0;
3123		else
3124			*eofp = eof;
3125	}
3126
3127	/*
3128	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
3129	 */
3130	while (uio_uio_resid(uiop) > 0 && ((size_t)(uio_uio_resid(uiop))) != tresid) {
3131		dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3132		dp->d_type = DT_UNKNOWN;
3133		dp->d_fileno = 0;
3134		dp->d_namlen = 0;
3135		dp->d_name[0] = '\0';
3136		tl = (u_int32_t *)&dp->d_name[4];
3137		*tl++ = cookie.lval[0];
3138		*tl = cookie.lval[1];
3139		dp->d_reclen = DIRBLKSIZ;
3140		uio_iov_base_add(uiop, DIRBLKSIZ);
3141		uio_iov_len_add(uiop, -(DIRBLKSIZ));
3142		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
3143		uiop->uio_offset += DIRBLKSIZ;
3144	}
3145
3146nfsmout:
3147	if (nd->nd_mrep != NULL)
3148		mbuf_freem(nd->nd_mrep);
3149	return (error);
3150}
3151
3152#ifndef APPLE
3153/*
3154 * NFS V3 readdir plus RPC. Used in place of nfsrpc_readdir().
3155 * (Also used for NFS V4 when mount flag set.)
3156 * (ditto above w.r.t. multiple of DIRBLKSIZ, etc.)
3157 */
3158APPLESTATIC int
3159nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
3160    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3161    int *eofp, void *stuff)
3162{
3163	int len, left;
3164	struct dirent *dp = NULL;
3165	u_int32_t *tl;
3166	vnode_t newvp = NULLVP;
3167	struct nfsrv_descript nfsd, *nd = &nfsd;
3168	struct nameidata nami, *ndp = &nami;
3169	struct componentname *cnp = &ndp->ni_cnd;
3170	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3171	struct nfsnode *dnp = VTONFS(vp), *np;
3172	struct nfsvattr nfsva;
3173	struct nfsfh *nfhp;
3174	nfsquad_t cookie, ncookie;
3175	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
3176	int attrflag, tryformoredirs = 1, eof = 0, gotmnton = 0;
3177	int isdotdot = 0, unlocknewvp = 0;
3178	long dotfileid, dotdotfileid = 0, fileno = 0;
3179	char *cp;
3180	nfsattrbit_t attrbits, dattrbits;
3181	size_t tresid;
3182	u_int32_t *tl2 = NULL, fakefileno = 0xffffffff, rderr;
3183	struct timespec dctime;
3184
3185	KASSERT(uiop->uio_iovcnt == 1 &&
3186	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
3187	    ("nfs readdirplusrpc bad uio"));
3188	timespecclear(&dctime);
3189	*attrflagp = 0;
3190	if (eofp != NULL)
3191		*eofp = 0;
3192	ndp->ni_dvp = vp;
3193	nd->nd_mrep = NULL;
3194	cookie.lval[0] = cookiep->nfsuquad[0];
3195	cookie.lval[1] = cookiep->nfsuquad[1];
3196	tresid = uio_uio_resid(uiop);
3197
3198	/*
3199	 * For NFSv4, first create the "." and ".." entries.
3200	 */
3201	if (NFSHASNFSV4(nmp)) {
3202		NFSGETATTR_ATTRBIT(&dattrbits);
3203		NFSZERO_ATTRBIT(&attrbits);
3204		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
3205		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
3206		    NFSATTRBIT_MOUNTEDONFILEID)) {
3207			NFSSETBIT_ATTRBIT(&attrbits,
3208			    NFSATTRBIT_MOUNTEDONFILEID);
3209			gotmnton = 1;
3210		} else {
3211			/*
3212			 * Must fake it. Use the fileno, except when the
3213			 * fsid is != to that of the directory. For that
3214			 * case, generate a fake fileno that is not the same.
3215			 */
3216			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
3217			gotmnton = 0;
3218		}
3219
3220		/*
3221		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
3222		 */
3223		if (uiop->uio_offset == 0) {
3224			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
3225			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3226			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
3227			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3228			(void) nfsrv_putattrbit(nd, &attrbits);
3229			error = nfscl_request(nd, vp, p, cred, stuff);
3230			if (error)
3231			    return (error);
3232			dotfileid = 0;	/* Fake out the compiler. */
3233			if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
3234			    error = nfsm_loadattr(nd, &nfsva);
3235			    if (error != 0)
3236				goto nfsmout;
3237			    dctime = nfsva.na_ctime;
3238			    dotfileid = nfsva.na_fileid;
3239			}
3240			if (nd->nd_repstat == 0) {
3241			    NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
3242			    len = fxdr_unsigned(int, *(tl + 4));
3243			    if (len > 0 && len <= NFSX_V4FHMAX)
3244				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3245			    else
3246				error = EPERM;
3247			    if (!error) {
3248				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3249				nfsva.na_mntonfileno = 0xffffffff;
3250				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
3251				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3252				    NULL, NULL, NULL, p, cred);
3253				if (error) {
3254				    dotdotfileid = dotfileid;
3255				} else if (gotmnton) {
3256				    if (nfsva.na_mntonfileno != 0xffffffff)
3257					dotdotfileid = nfsva.na_mntonfileno;
3258				    else
3259					dotdotfileid = nfsva.na_fileid;
3260				} else if (nfsva.na_filesid[0] ==
3261				    dnp->n_vattr.na_filesid[0] &&
3262				    nfsva.na_filesid[1] ==
3263				    dnp->n_vattr.na_filesid[1]) {
3264				    dotdotfileid = nfsva.na_fileid;
3265				} else {
3266				    do {
3267					fakefileno--;
3268				    } while (fakefileno ==
3269					nfsva.na_fileid);
3270				    dotdotfileid = fakefileno;
3271				}
3272			    }
3273			} else if (nd->nd_repstat == NFSERR_NOENT) {
3274			    /*
3275			     * Lookupp returns NFSERR_NOENT when we are
3276			     * at the root, so just use the current dir.
3277			     */
3278			    nd->nd_repstat = 0;
3279			    dotdotfileid = dotfileid;
3280			} else {
3281			    error = nd->nd_repstat;
3282			}
3283			mbuf_freem(nd->nd_mrep);
3284			if (error)
3285			    return (error);
3286			nd->nd_mrep = NULL;
3287			dp = (struct dirent *)uio_iov_base(uiop);
3288			dp->d_type = DT_DIR;
3289			dp->d_fileno = dotfileid;
3290			dp->d_namlen = 1;
3291			dp->d_name[0] = '.';
3292			dp->d_name[1] = '\0';
3293			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3294			/*
3295			 * Just make these offset cookie 0.
3296			 */
3297			tl = (u_int32_t *)&dp->d_name[4];
3298			*tl++ = 0;
3299			*tl = 0;
3300			blksiz += dp->d_reclen;
3301			uio_uio_resid_add(uiop, -(dp->d_reclen));
3302			uiop->uio_offset += dp->d_reclen;
3303			uio_iov_base_add(uiop, dp->d_reclen);
3304			uio_iov_len_add(uiop, -(dp->d_reclen));
3305			dp = (struct dirent *)uio_iov_base(uiop);
3306			dp->d_type = DT_DIR;
3307			dp->d_fileno = dotdotfileid;
3308			dp->d_namlen = 2;
3309			dp->d_name[0] = '.';
3310			dp->d_name[1] = '.';
3311			dp->d_name[2] = '\0';
3312			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3313			/*
3314			 * Just make these offset cookie 0.
3315			 */
3316			tl = (u_int32_t *)&dp->d_name[4];
3317			*tl++ = 0;
3318			*tl = 0;
3319			blksiz += dp->d_reclen;
3320			uio_uio_resid_add(uiop, -(dp->d_reclen));
3321			uiop->uio_offset += dp->d_reclen;
3322			uio_iov_base_add(uiop, dp->d_reclen);
3323			uio_iov_len_add(uiop, -(dp->d_reclen));
3324		}
3325		NFSREADDIRPLUS_ATTRBIT(&attrbits);
3326		if (gotmnton)
3327			NFSSETBIT_ATTRBIT(&attrbits,
3328			    NFSATTRBIT_MOUNTEDONFILEID);
3329	}
3330
3331	/*
3332	 * Loop around doing readdir rpc's of size nm_readdirsize.
3333	 * The stopping criteria is EOF or buffer full.
3334	 */
3335	while (more_dirs && bigenough) {
3336		*attrflagp = 0;
3337		NFSCL_REQSTART(nd, NFSPROC_READDIRPLUS, vp);
3338 		NFSM_BUILD(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
3339		*tl++ = cookie.lval[0];
3340		*tl++ = cookie.lval[1];
3341		if (cookie.qval == 0) {
3342			*tl++ = 0;
3343			*tl++ = 0;
3344		} else {
3345			NFSLOCKNODE(dnp);
3346			*tl++ = dnp->n_cookieverf.nfsuquad[0];
3347			*tl++ = dnp->n_cookieverf.nfsuquad[1];
3348			NFSUNLOCKNODE(dnp);
3349		}
3350		*tl++ = txdr_unsigned(nmp->nm_readdirsize);
3351		*tl = txdr_unsigned(nmp->nm_readdirsize);
3352		if (nd->nd_flag & ND_NFSV4) {
3353			(void) nfsrv_putattrbit(nd, &attrbits);
3354			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3355			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3356			(void) nfsrv_putattrbit(nd, &dattrbits);
3357		}
3358		error = nfscl_request(nd, vp, p, cred, stuff);
3359		if (error)
3360			return (error);
3361		if (nd->nd_flag & ND_NFSV3)
3362			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3363		if (nd->nd_repstat || error) {
3364			if (!error)
3365				error = nd->nd_repstat;
3366			goto nfsmout;
3367		}
3368		if ((nd->nd_flag & ND_NFSV3) != 0 && *attrflagp != 0)
3369			dctime = nap->na_ctime;
3370		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3371		NFSLOCKNODE(dnp);
3372		dnp->n_cookieverf.nfsuquad[0] = *tl++;
3373		dnp->n_cookieverf.nfsuquad[1] = *tl++;
3374		NFSUNLOCKNODE(dnp);
3375		more_dirs = fxdr_unsigned(int, *tl);
3376		if (!more_dirs)
3377			tryformoredirs = 0;
3378
3379		/* loop thru the dir entries, doctoring them to 4bsd form */
3380		while (more_dirs && bigenough) {
3381			NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3382			if (nd->nd_flag & ND_NFSV4) {
3383				ncookie.lval[0] = *tl++;
3384				ncookie.lval[1] = *tl++;
3385			} else {
3386				fileno = fxdr_unsigned(long, *++tl);
3387				tl++;
3388			}
3389			len = fxdr_unsigned(int, *tl);
3390			if (len <= 0 || len > NFS_MAXNAMLEN) {
3391				error = EBADRPC;
3392				goto nfsmout;
3393			}
3394			tlen = NFSM_RNDUP(len);
3395			if (tlen == len)
3396				tlen += 4;  /* To ensure null termination */
3397			left = DIRBLKSIZ - blksiz;
3398			if ((tlen + DIRHDSIZ + NFSX_HYPER) > left) {
3399				dp->d_reclen += left;
3400				uio_iov_base_add(uiop, left);
3401				uio_iov_len_add(uiop, -(left));
3402				uio_uio_resid_add(uiop, -(left));
3403				uiop->uio_offset += left;
3404				blksiz = 0;
3405			}
3406			if ((tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
3407				bigenough = 0;
3408			if (bigenough) {
3409				dp = (struct dirent *)uio_iov_base(uiop);
3410				dp->d_namlen = len;
3411				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
3412				dp->d_type = DT_UNKNOWN;
3413				blksiz += dp->d_reclen;
3414				if (blksiz == DIRBLKSIZ)
3415					blksiz = 0;
3416				uio_uio_resid_add(uiop, -(DIRHDSIZ));
3417				uiop->uio_offset += DIRHDSIZ;
3418				uio_iov_base_add(uiop, DIRHDSIZ);
3419				uio_iov_len_add(uiop, -(DIRHDSIZ));
3420				cnp->cn_nameptr = uio_iov_base(uiop);
3421				cnp->cn_namelen = len;
3422				NFSCNHASHZERO(cnp);
3423				error = nfsm_mbufuio(nd, uiop, len);
3424				if (error)
3425					goto nfsmout;
3426				cp = uio_iov_base(uiop);
3427				tlen -= len;
3428				*cp = '\0';
3429				cp += tlen;	/* points to cookie storage */
3430				tl2 = (u_int32_t *)cp;
3431				if (len == 2 && cnp->cn_nameptr[0] == '.' &&
3432				    cnp->cn_nameptr[1] == '.')
3433					isdotdot = 1;
3434				else
3435					isdotdot = 0;
3436				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
3437				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
3438				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
3439				uiop->uio_offset += (tlen + NFSX_HYPER);
3440			} else {
3441				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3442				if (error)
3443					goto nfsmout;
3444			}
3445			nfhp = NULL;
3446			if (nd->nd_flag & ND_NFSV3) {
3447				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3448				ncookie.lval[0] = *tl++;
3449				ncookie.lval[1] = *tl++;
3450				attrflag = fxdr_unsigned(int, *tl);
3451				if (attrflag) {
3452				  error = nfsm_loadattr(nd, &nfsva);
3453				  if (error)
3454					goto nfsmout;
3455				}
3456				NFSM_DISSECT(tl,u_int32_t *,NFSX_UNSIGNED);
3457				if (*tl) {
3458					error = nfsm_getfh(nd, &nfhp);
3459					if (error)
3460					    goto nfsmout;
3461				}
3462				if (!attrflag && nfhp != NULL) {
3463					FREE((caddr_t)nfhp, M_NFSFH);
3464					nfhp = NULL;
3465				}
3466			} else {
3467				rderr = 0;
3468				nfsva.na_mntonfileno = 0xffffffff;
3469				error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
3470				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3471				    NULL, NULL, &rderr, p, cred);
3472				if (error)
3473					goto nfsmout;
3474			}
3475
3476			if (bigenough) {
3477			    if (nd->nd_flag & ND_NFSV4) {
3478				if (rderr) {
3479				    dp->d_fileno = 0;
3480				} else if (gotmnton) {
3481				    if (nfsva.na_mntonfileno != 0xffffffff)
3482					dp->d_fileno = nfsva.na_mntonfileno;
3483				    else
3484					dp->d_fileno = nfsva.na_fileid;
3485				} else if (nfsva.na_filesid[0] ==
3486				    dnp->n_vattr.na_filesid[0] &&
3487				    nfsva.na_filesid[1] ==
3488				    dnp->n_vattr.na_filesid[1]) {
3489				    dp->d_fileno = nfsva.na_fileid;
3490				} else {
3491				    do {
3492					fakefileno--;
3493				    } while (fakefileno ==
3494					nfsva.na_fileid);
3495				    dp->d_fileno = fakefileno;
3496				}
3497			    } else {
3498				dp->d_fileno = fileno;
3499			    }
3500			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
3501				ncookie.lval[0];
3502			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
3503				ncookie.lval[1];
3504
3505			    if (nfhp != NULL) {
3506				if (NFSRV_CMPFH(nfhp->nfh_fh, nfhp->nfh_len,
3507				    dnp->n_fhp->nfh_fh, dnp->n_fhp->nfh_len)) {
3508				    VREF(vp);
3509				    newvp = vp;
3510				    unlocknewvp = 0;
3511				    FREE((caddr_t)nfhp, M_NFSFH);
3512				    np = dnp;
3513				} else if (isdotdot != 0) {
3514				    /*
3515				     * Skip doing a nfscl_nget() call for "..".
3516				     * There's a race between acquiring the nfs
3517				     * node here and lookups that look for the
3518				     * directory being read (in the parent).
3519				     * It would try to get a lock on ".." here,
3520				     * owning the lock on the directory being
3521				     * read. Lookup will hold the lock on ".."
3522				     * and try to acquire the lock on the
3523				     * directory being read.
3524				     * If the directory is unlocked/relocked,
3525				     * then there is a LOR with the buflock
3526				     * vp is relocked.
3527				     */
3528				    free(nfhp, M_NFSFH);
3529				} else {
3530				    error = nfscl_nget(vnode_mount(vp), vp,
3531				      nfhp, cnp, p, &np, NULL, LK_EXCLUSIVE);
3532				    if (!error) {
3533					newvp = NFSTOV(np);
3534					unlocknewvp = 1;
3535				    }
3536				}
3537				nfhp = NULL;
3538				if (newvp != NULLVP) {
3539				    error = nfscl_loadattrcache(&newvp,
3540					&nfsva, NULL, NULL, 0, 0);
3541				    if (error) {
3542					if (unlocknewvp)
3543					    vput(newvp);
3544					else
3545					    vrele(newvp);
3546					goto nfsmout;
3547				    }
3548				    dp->d_type =
3549					vtonfs_dtype(np->n_vattr.na_type);
3550				    ndp->ni_vp = newvp;
3551				    NFSCNHASH(cnp, HASHINIT);
3552				    if (cnp->cn_namelen <= NCHNAMLEN &&
3553					(newvp->v_type != VDIR ||
3554					 dctime.tv_sec != 0)) {
3555					cache_enter_time(ndp->ni_dvp,
3556					    ndp->ni_vp, cnp,
3557					    &nfsva.na_ctime,
3558					    newvp->v_type != VDIR ? NULL :
3559					    &dctime);
3560				    }
3561				    if (unlocknewvp)
3562					vput(newvp);
3563				    else
3564					vrele(newvp);
3565				    newvp = NULLVP;
3566				}
3567			    }
3568			} else if (nfhp != NULL) {
3569			    FREE((caddr_t)nfhp, M_NFSFH);
3570			}
3571			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3572			more_dirs = fxdr_unsigned(int, *tl);
3573		}
3574		/*
3575		 * If at end of rpc data, get the eof boolean
3576		 */
3577		if (!more_dirs) {
3578			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3579			eof = fxdr_unsigned(int, *tl);
3580			if (tryformoredirs)
3581				more_dirs = !eof;
3582			if (nd->nd_flag & ND_NFSV4) {
3583				error = nfscl_postop_attr(nd, nap, attrflagp,
3584				    stuff);
3585				if (error)
3586					goto nfsmout;
3587			}
3588		}
3589		mbuf_freem(nd->nd_mrep);
3590		nd->nd_mrep = NULL;
3591	}
3592	/*
3593	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3594	 * by increasing d_reclen for the last record.
3595	 */
3596	if (blksiz > 0) {
3597		left = DIRBLKSIZ - blksiz;
3598		dp->d_reclen += left;
3599		uio_iov_base_add(uiop, left);
3600		uio_iov_len_add(uiop, -(left));
3601		uio_uio_resid_add(uiop, -(left));
3602		uiop->uio_offset += left;
3603	}
3604
3605	/*
3606	 * If returning no data, assume end of file.
3607	 * If not bigenough, return not end of file, since you aren't
3608	 *    returning all the data
3609	 * Otherwise, return the eof flag from the server.
3610	 */
3611	if (eofp != NULL) {
3612		if (tresid == uio_uio_resid(uiop))
3613			*eofp = 1;
3614		else if (!bigenough)
3615			*eofp = 0;
3616		else
3617			*eofp = eof;
3618	}
3619
3620	/*
3621	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
3622	 */
3623	while (uio_uio_resid(uiop) > 0 && uio_uio_resid(uiop) != tresid) {
3624		dp = (struct dirent *)uio_iov_base(uiop);
3625		dp->d_type = DT_UNKNOWN;
3626		dp->d_fileno = 0;
3627		dp->d_namlen = 0;
3628		dp->d_name[0] = '\0';
3629		tl = (u_int32_t *)&dp->d_name[4];
3630		*tl++ = cookie.lval[0];
3631		*tl = cookie.lval[1];
3632		dp->d_reclen = DIRBLKSIZ;
3633		uio_iov_base_add(uiop, DIRBLKSIZ);
3634		uio_iov_len_add(uiop, -(DIRBLKSIZ));
3635		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
3636		uiop->uio_offset += DIRBLKSIZ;
3637	}
3638
3639nfsmout:
3640	if (nd->nd_mrep != NULL)
3641		mbuf_freem(nd->nd_mrep);
3642	return (error);
3643}
3644#endif	/* !APPLE */
3645
3646/*
3647 * Nfs commit rpc
3648 */
3649APPLESTATIC int
3650nfsrpc_commit(vnode_t vp, u_quad_t offset, int cnt, struct ucred *cred,
3651    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
3652{
3653	u_int32_t *tl;
3654	struct nfsrv_descript nfsd, *nd = &nfsd;
3655	nfsattrbit_t attrbits;
3656	int error;
3657	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3658
3659	*attrflagp = 0;
3660	NFSCL_REQSTART(nd, NFSPROC_COMMIT, vp);
3661	NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3662	txdr_hyper(offset, tl);
3663	tl += 2;
3664	*tl = txdr_unsigned(cnt);
3665	if (nd->nd_flag & ND_NFSV4) {
3666		/*
3667		 * And do a Getattr op.
3668		 */
3669		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3670		*tl = txdr_unsigned(NFSV4OP_GETATTR);
3671		NFSGETATTR_ATTRBIT(&attrbits);
3672		(void) nfsrv_putattrbit(nd, &attrbits);
3673	}
3674	error = nfscl_request(nd, vp, p, cred, stuff);
3675	if (error)
3676		return (error);
3677	error = nfscl_wcc_data(nd, vp, nap, attrflagp, NULL, stuff);
3678	if (!error && !nd->nd_repstat) {
3679		NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
3680		NFSLOCKMNT(nmp);
3681		if (NFSBCMP(nmp->nm_verf, tl, NFSX_VERF)) {
3682			NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
3683			nd->nd_repstat = NFSERR_STALEWRITEVERF;
3684		}
3685		NFSUNLOCKMNT(nmp);
3686		if (nd->nd_flag & ND_NFSV4)
3687			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3688	}
3689nfsmout:
3690	if (!error && nd->nd_repstat)
3691		error = nd->nd_repstat;
3692	mbuf_freem(nd->nd_mrep);
3693	return (error);
3694}
3695
3696/*
3697 * NFS byte range lock rpc.
3698 * (Mostly just calls one of the three lower level RPC routines.)
3699 */
3700APPLESTATIC int
3701nfsrpc_advlock(vnode_t vp, off_t size, int op, struct flock *fl,
3702    int reclaim, struct ucred *cred, NFSPROC_T *p, void *id, int flags)
3703{
3704	struct nfscllockowner *lp;
3705	struct nfsclclient *clp;
3706	struct nfsfh *nfhp;
3707	struct nfsrv_descript nfsd, *nd = &nfsd;
3708	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3709	u_int64_t off, len;
3710	off_t start, end;
3711	u_int32_t clidrev = 0;
3712	int error = 0, newone = 0, expireret = 0, retrycnt, donelocally;
3713	int callcnt, dorpc;
3714
3715	/*
3716	 * Convert the flock structure into a start and end and do POSIX
3717	 * bounds checking.
3718	 */
3719	switch (fl->l_whence) {
3720	case SEEK_SET:
3721	case SEEK_CUR:
3722		/*
3723		 * Caller is responsible for adding any necessary offset
3724		 * when SEEK_CUR is used.
3725		 */
3726		start = fl->l_start;
3727		off = fl->l_start;
3728		break;
3729	case SEEK_END:
3730		start = size + fl->l_start;
3731		off = size + fl->l_start;
3732		break;
3733	default:
3734		return (EINVAL);
3735	};
3736	if (start < 0)
3737		return (EINVAL);
3738	if (fl->l_len != 0) {
3739		end = start + fl->l_len - 1;
3740		if (end < start)
3741			return (EINVAL);
3742	}
3743
3744	len = fl->l_len;
3745	if (len == 0)
3746		len = NFS64BITSSET;
3747	retrycnt = 0;
3748	do {
3749	    nd->nd_repstat = 0;
3750	    if (op == F_GETLK) {
3751		error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
3752		if (error)
3753			return (error);
3754		error = nfscl_lockt(vp, clp, off, len, fl, p, id, flags);
3755		if (!error) {
3756			clidrev = clp->nfsc_clientidrev;
3757			error = nfsrpc_lockt(nd, vp, clp, off, len, fl, cred,
3758			    p, id, flags);
3759		} else if (error == -1) {
3760			error = 0;
3761		}
3762		nfscl_clientrelease(clp);
3763	    } else if (op == F_UNLCK && fl->l_type == F_UNLCK) {
3764		/*
3765		 * We must loop around for all lockowner cases.
3766		 */
3767		callcnt = 0;
3768		error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
3769		if (error)
3770			return (error);
3771		do {
3772		    error = nfscl_relbytelock(vp, off, len, cred, p, callcnt,
3773			clp, id, flags, &lp, &dorpc);
3774		    /*
3775		     * If it returns a NULL lp, we're done.
3776		     */
3777		    if (lp == NULL) {
3778			if (callcnt == 0)
3779			    nfscl_clientrelease(clp);
3780			else
3781			    nfscl_releasealllocks(clp, vp, p, id, flags);
3782			return (error);
3783		    }
3784		    if (nmp->nm_clp != NULL)
3785			clidrev = nmp->nm_clp->nfsc_clientidrev;
3786		    else
3787			clidrev = 0;
3788		    /*
3789		     * If the server doesn't support Posix lock semantics,
3790		     * only allow locks on the entire file, since it won't
3791		     * handle overlapping byte ranges.
3792		     * There might still be a problem when a lock
3793		     * upgrade/downgrade (read<->write) occurs, since the
3794		     * server "might" expect an unlock first?
3795		     */
3796		    if (dorpc && (lp->nfsl_open->nfso_posixlock ||
3797			(off == 0 && len == NFS64BITSSET))) {
3798			/*
3799			 * Since the lock records will go away, we must
3800			 * wait for grace and delay here.
3801			 */
3802			do {
3803			    error = nfsrpc_locku(nd, nmp, lp, off, len,
3804				NFSV4LOCKT_READ, cred, p, 0);
3805			    if ((nd->nd_repstat == NFSERR_GRACE ||
3806				 nd->nd_repstat == NFSERR_DELAY) &&
3807				error == 0)
3808				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3809				    "nfs_advlock");
3810			} while ((nd->nd_repstat == NFSERR_GRACE ||
3811			    nd->nd_repstat == NFSERR_DELAY) && error == 0);
3812		    }
3813		    callcnt++;
3814		} while (error == 0 && nd->nd_repstat == 0);
3815		nfscl_releasealllocks(clp, vp, p, id, flags);
3816	    } else if (op == F_SETLK) {
3817		error = nfscl_getbytelock(vp, off, len, fl->l_type, cred, p,
3818		    NULL, 0, id, flags, NULL, NULL, &lp, &newone, &donelocally);
3819		if (error || donelocally) {
3820			return (error);
3821		}
3822		if (nmp->nm_clp != NULL)
3823			clidrev = nmp->nm_clp->nfsc_clientidrev;
3824		else
3825			clidrev = 0;
3826		nfhp = VTONFS(vp)->n_fhp;
3827		if (!lp->nfsl_open->nfso_posixlock &&
3828		    (off != 0 || len != NFS64BITSSET)) {
3829			error = EINVAL;
3830		} else {
3831			error = nfsrpc_lock(nd, nmp, vp, nfhp->nfh_fh,
3832			    nfhp->nfh_len, lp, newone, reclaim, off,
3833			    len, fl->l_type, cred, p, 0);
3834		}
3835		if (!error)
3836			error = nd->nd_repstat;
3837		nfscl_lockrelease(lp, error, newone);
3838	    } else {
3839		error = EINVAL;
3840	    }
3841	    if (!error)
3842	        error = nd->nd_repstat;
3843	    if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
3844		error == NFSERR_STALEDONTRECOVER ||
3845		error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
3846		error == NFSERR_BADSESSION) {
3847		(void) nfs_catnap(PZERO, error, "nfs_advlock");
3848	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
3849		&& clidrev != 0) {
3850		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
3851		retrycnt++;
3852	    }
3853	} while (error == NFSERR_GRACE ||
3854	    error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
3855	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_STALESTATEID ||
3856	    error == NFSERR_BADSESSION ||
3857	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
3858	     expireret == 0 && clidrev != 0 && retrycnt < 4));
3859	if (error && retrycnt >= 4)
3860		error = EIO;
3861	return (error);
3862}
3863
3864/*
3865 * The lower level routine for the LockT case.
3866 */
3867APPLESTATIC int
3868nfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
3869    struct nfsclclient *clp, u_int64_t off, u_int64_t len, struct flock *fl,
3870    struct ucred *cred, NFSPROC_T *p, void *id, int flags)
3871{
3872	u_int32_t *tl;
3873	int error, type, size;
3874	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
3875	struct nfsnode *np;
3876	struct nfsmount *nmp;
3877	struct nfsclsession *tsep;
3878
3879	nmp = VFSTONFS(vp->v_mount);
3880	NFSCL_REQSTART(nd, NFSPROC_LOCKT, vp);
3881	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3882	if (fl->l_type == F_RDLCK)
3883		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3884	else
3885		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3886	txdr_hyper(off, tl);
3887	tl += 2;
3888	txdr_hyper(len, tl);
3889	tl += 2;
3890	tsep = nfsmnt_mdssession(nmp);
3891	*tl++ = tsep->nfsess_clientid.lval[0];
3892	*tl = tsep->nfsess_clientid.lval[1];
3893	nfscl_filllockowner(id, own, flags);
3894	np = VTONFS(vp);
3895	NFSBCOPY(np->n_fhp->nfh_fh, &own[NFSV4CL_LOCKNAMELEN],
3896	    np->n_fhp->nfh_len);
3897	(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + np->n_fhp->nfh_len);
3898	error = nfscl_request(nd, vp, p, cred, NULL);
3899	if (error)
3900		return (error);
3901	if (nd->nd_repstat == 0) {
3902		fl->l_type = F_UNLCK;
3903	} else if (nd->nd_repstat == NFSERR_DENIED) {
3904		nd->nd_repstat = 0;
3905		fl->l_whence = SEEK_SET;
3906		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3907		fl->l_start = fxdr_hyper(tl);
3908		tl += 2;
3909		len = fxdr_hyper(tl);
3910		tl += 2;
3911		if (len == NFS64BITSSET)
3912			fl->l_len = 0;
3913		else
3914			fl->l_len = len;
3915		type = fxdr_unsigned(int, *tl++);
3916		if (type == NFSV4LOCKT_WRITE)
3917			fl->l_type = F_WRLCK;
3918		else
3919			fl->l_type = F_RDLCK;
3920		/*
3921		 * XXX For now, I have no idea what to do with the
3922		 * conflicting lock_owner, so I'll just set the pid == 0
3923		 * and skip over the lock_owner.
3924		 */
3925		fl->l_pid = (pid_t)0;
3926		tl += 2;
3927		size = fxdr_unsigned(int, *tl);
3928		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3929			error = EBADRPC;
3930		if (!error)
3931			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3932	} else if (nd->nd_repstat == NFSERR_STALECLIENTID)
3933		nfscl_initiate_recovery(clp);
3934nfsmout:
3935	mbuf_freem(nd->nd_mrep);
3936	return (error);
3937}
3938
3939/*
3940 * Lower level function that performs the LockU RPC.
3941 */
3942static int
3943nfsrpc_locku(struct nfsrv_descript *nd, struct nfsmount *nmp,
3944    struct nfscllockowner *lp, u_int64_t off, u_int64_t len,
3945    u_int32_t type, struct ucred *cred, NFSPROC_T *p, int syscred)
3946{
3947	u_int32_t *tl;
3948	int error;
3949
3950	nfscl_reqstart(nd, NFSPROC_LOCKU, nmp, lp->nfsl_open->nfso_fh,
3951	    lp->nfsl_open->nfso_fhlen, NULL, NULL);
3952	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
3953	*tl++ = txdr_unsigned(type);
3954	*tl = txdr_unsigned(lp->nfsl_seqid);
3955	if (nfstest_outofseq &&
3956	    (arc4random() % nfstest_outofseq) == 0)
3957		*tl = txdr_unsigned(lp->nfsl_seqid + 1);
3958	tl++;
3959	if (NFSHASNFSV4N(nmp))
3960		*tl++ = 0;
3961	else
3962		*tl++ = lp->nfsl_stateid.seqid;
3963	*tl++ = lp->nfsl_stateid.other[0];
3964	*tl++ = lp->nfsl_stateid.other[1];
3965	*tl++ = lp->nfsl_stateid.other[2];
3966	txdr_hyper(off, tl);
3967	tl += 2;
3968	txdr_hyper(len, tl);
3969	if (syscred)
3970		nd->nd_flag |= ND_USEGSSNAME;
3971	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
3972	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
3973	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
3974	if (error)
3975		return (error);
3976	if (nd->nd_repstat == 0) {
3977		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3978		lp->nfsl_stateid.seqid = *tl++;
3979		lp->nfsl_stateid.other[0] = *tl++;
3980		lp->nfsl_stateid.other[1] = *tl++;
3981		lp->nfsl_stateid.other[2] = *tl;
3982	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
3983		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
3984nfsmout:
3985	mbuf_freem(nd->nd_mrep);
3986	return (error);
3987}
3988
3989/*
3990 * The actual Lock RPC.
3991 */
3992APPLESTATIC int
3993nfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
3994    u_int8_t *nfhp, int fhlen, struct nfscllockowner *lp, int newone,
3995    int reclaim, u_int64_t off, u_int64_t len, short type, struct ucred *cred,
3996    NFSPROC_T *p, int syscred)
3997{
3998	u_int32_t *tl;
3999	int error, size;
4000	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
4001	struct nfsclsession *tsep;
4002
4003	nfscl_reqstart(nd, NFSPROC_LOCK, nmp, nfhp, fhlen, NULL, NULL);
4004	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
4005	if (type == F_RDLCK)
4006		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
4007	else
4008		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
4009	*tl++ = txdr_unsigned(reclaim);
4010	txdr_hyper(off, tl);
4011	tl += 2;
4012	txdr_hyper(len, tl);
4013	tl += 2;
4014	if (newone) {
4015	    *tl = newnfs_true;
4016	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
4017		2 * NFSX_UNSIGNED + NFSX_HYPER);
4018	    *tl++ = txdr_unsigned(lp->nfsl_open->nfso_own->nfsow_seqid);
4019	    if (NFSHASNFSV4N(nmp))
4020		*tl++ = 0;
4021	    else
4022		*tl++ = lp->nfsl_open->nfso_stateid.seqid;
4023	    *tl++ = lp->nfsl_open->nfso_stateid.other[0];
4024	    *tl++ = lp->nfsl_open->nfso_stateid.other[1];
4025	    *tl++ = lp->nfsl_open->nfso_stateid.other[2];
4026	    *tl++ = txdr_unsigned(lp->nfsl_seqid);
4027	    tsep = nfsmnt_mdssession(nmp);
4028	    *tl++ = tsep->nfsess_clientid.lval[0];
4029	    *tl = tsep->nfsess_clientid.lval[1];
4030	    NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
4031	    NFSBCOPY(nfhp, &own[NFSV4CL_LOCKNAMELEN], fhlen);
4032	    (void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
4033	} else {
4034	    *tl = newnfs_false;
4035	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
4036	    if (NFSHASNFSV4N(nmp))
4037		*tl++ = 0;
4038	    else
4039		*tl++ = lp->nfsl_stateid.seqid;
4040	    *tl++ = lp->nfsl_stateid.other[0];
4041	    *tl++ = lp->nfsl_stateid.other[1];
4042	    *tl++ = lp->nfsl_stateid.other[2];
4043	    *tl = txdr_unsigned(lp->nfsl_seqid);
4044	    if (nfstest_outofseq &&
4045		(arc4random() % nfstest_outofseq) == 0)
4046		    *tl = txdr_unsigned(lp->nfsl_seqid + 1);
4047	}
4048	if (syscred)
4049		nd->nd_flag |= ND_USEGSSNAME;
4050	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
4051	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4052	if (error)
4053		return (error);
4054	if (newone)
4055	    NFSCL_INCRSEQID(lp->nfsl_open->nfso_own->nfsow_seqid, nd);
4056	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
4057	if (nd->nd_repstat == 0) {
4058		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
4059		lp->nfsl_stateid.seqid = *tl++;
4060		lp->nfsl_stateid.other[0] = *tl++;
4061		lp->nfsl_stateid.other[1] = *tl++;
4062		lp->nfsl_stateid.other[2] = *tl;
4063	} else if (nd->nd_repstat == NFSERR_DENIED) {
4064		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
4065		size = fxdr_unsigned(int, *(tl + 7));
4066		if (size < 0 || size > NFSV4_OPAQUELIMIT)
4067			error = EBADRPC;
4068		if (!error)
4069			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
4070	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
4071		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
4072nfsmout:
4073	mbuf_freem(nd->nd_mrep);
4074	return (error);
4075}
4076
4077/*
4078 * nfs statfs rpc
4079 * (always called with the vp for the mount point)
4080 */
4081APPLESTATIC int
4082nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
4083    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
4084    void *stuff)
4085{
4086	u_int32_t *tl = NULL;
4087	struct nfsrv_descript nfsd, *nd = &nfsd;
4088	struct nfsmount *nmp;
4089	nfsattrbit_t attrbits;
4090	int error;
4091
4092	*attrflagp = 0;
4093	nmp = VFSTONFS(vnode_mount(vp));
4094	if (NFSHASNFSV4(nmp)) {
4095		/*
4096		 * For V4, you actually do a getattr.
4097		 */
4098		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
4099		NFSSTATFS_GETATTRBIT(&attrbits);
4100		(void) nfsrv_putattrbit(nd, &attrbits);
4101		nd->nd_flag |= ND_USEGSSNAME;
4102		error = nfscl_request(nd, vp, p, cred, stuff);
4103		if (error)
4104			return (error);
4105		if (nd->nd_repstat == 0) {
4106			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
4107			    NULL, NULL, sbp, fsp, NULL, 0, NULL, NULL, NULL, p,
4108			    cred);
4109			if (!error) {
4110				nmp->nm_fsid[0] = nap->na_filesid[0];
4111				nmp->nm_fsid[1] = nap->na_filesid[1];
4112				NFSSETHASSETFSID(nmp);
4113				*attrflagp = 1;
4114			}
4115		} else {
4116			error = nd->nd_repstat;
4117		}
4118		if (error)
4119			goto nfsmout;
4120	} else {
4121		NFSCL_REQSTART(nd, NFSPROC_FSSTAT, vp);
4122		error = nfscl_request(nd, vp, p, cred, stuff);
4123		if (error)
4124			return (error);
4125		if (nd->nd_flag & ND_NFSV3) {
4126			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
4127			if (error)
4128				goto nfsmout;
4129		}
4130		if (nd->nd_repstat) {
4131			error = nd->nd_repstat;
4132			goto nfsmout;
4133		}
4134		NFSM_DISSECT(tl, u_int32_t *,
4135		    NFSX_STATFS(nd->nd_flag & ND_NFSV3));
4136	}
4137	if (NFSHASNFSV3(nmp)) {
4138		sbp->sf_tbytes = fxdr_hyper(tl); tl += 2;
4139		sbp->sf_fbytes = fxdr_hyper(tl); tl += 2;
4140		sbp->sf_abytes = fxdr_hyper(tl); tl += 2;
4141		sbp->sf_tfiles = fxdr_hyper(tl); tl += 2;
4142		sbp->sf_ffiles = fxdr_hyper(tl); tl += 2;
4143		sbp->sf_afiles = fxdr_hyper(tl); tl += 2;
4144		sbp->sf_invarsec = fxdr_unsigned(u_int32_t, *tl);
4145	} else if (NFSHASNFSV4(nmp) == 0) {
4146		sbp->sf_tsize = fxdr_unsigned(u_int32_t, *tl++);
4147		sbp->sf_bsize = fxdr_unsigned(u_int32_t, *tl++);
4148		sbp->sf_blocks = fxdr_unsigned(u_int32_t, *tl++);
4149		sbp->sf_bfree = fxdr_unsigned(u_int32_t, *tl++);
4150		sbp->sf_bavail = fxdr_unsigned(u_int32_t, *tl);
4151	}
4152nfsmout:
4153	mbuf_freem(nd->nd_mrep);
4154	return (error);
4155}
4156
4157/*
4158 * nfs pathconf rpc
4159 */
4160APPLESTATIC int
4161nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc,
4162    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
4163    void *stuff)
4164{
4165	struct nfsrv_descript nfsd, *nd = &nfsd;
4166	struct nfsmount *nmp;
4167	u_int32_t *tl;
4168	nfsattrbit_t attrbits;
4169	int error;
4170
4171	*attrflagp = 0;
4172	nmp = VFSTONFS(vnode_mount(vp));
4173	if (NFSHASNFSV4(nmp)) {
4174		/*
4175		 * For V4, you actually do a getattr.
4176		 */
4177		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
4178		NFSPATHCONF_GETATTRBIT(&attrbits);
4179		(void) nfsrv_putattrbit(nd, &attrbits);
4180		nd->nd_flag |= ND_USEGSSNAME;
4181		error = nfscl_request(nd, vp, p, cred, stuff);
4182		if (error)
4183			return (error);
4184		if (nd->nd_repstat == 0) {
4185			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
4186			    pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p,
4187			    cred);
4188			if (!error)
4189				*attrflagp = 1;
4190		} else {
4191			error = nd->nd_repstat;
4192		}
4193	} else {
4194		NFSCL_REQSTART(nd, NFSPROC_PATHCONF, vp);
4195		error = nfscl_request(nd, vp, p, cred, stuff);
4196		if (error)
4197			return (error);
4198		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
4199		if (nd->nd_repstat && !error)
4200			error = nd->nd_repstat;
4201		if (!error) {
4202			NFSM_DISSECT(tl, u_int32_t *, NFSX_V3PATHCONF);
4203			pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl++);
4204			pc->pc_namemax = fxdr_unsigned(u_int32_t, *tl++);
4205			pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl++);
4206			pc->pc_chownrestricted =
4207			    fxdr_unsigned(u_int32_t, *tl++);
4208			pc->pc_caseinsensitive =
4209			    fxdr_unsigned(u_int32_t, *tl++);
4210			pc->pc_casepreserving = fxdr_unsigned(u_int32_t, *tl);
4211		}
4212	}
4213nfsmout:
4214	mbuf_freem(nd->nd_mrep);
4215	return (error);
4216}
4217
4218/*
4219 * nfs version 3 fsinfo rpc call
4220 */
4221APPLESTATIC int
4222nfsrpc_fsinfo(vnode_t vp, struct nfsfsinfo *fsp, struct ucred *cred,
4223    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
4224{
4225	u_int32_t *tl;
4226	struct nfsrv_descript nfsd, *nd = &nfsd;
4227	int error;
4228
4229	*attrflagp = 0;
4230	NFSCL_REQSTART(nd, NFSPROC_FSINFO, vp);
4231	error = nfscl_request(nd, vp, p, cred, stuff);
4232	if (error)
4233		return (error);
4234	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
4235	if (nd->nd_repstat && !error)
4236		error = nd->nd_repstat;
4237	if (!error) {
4238		NFSM_DISSECT(tl, u_int32_t *, NFSX_V3FSINFO);
4239		fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *tl++);
4240		fsp->fs_rtpref = fxdr_unsigned(u_int32_t, *tl++);
4241		fsp->fs_rtmult = fxdr_unsigned(u_int32_t, *tl++);
4242		fsp->fs_wtmax = fxdr_unsigned(u_int32_t, *tl++);
4243		fsp->fs_wtpref = fxdr_unsigned(u_int32_t, *tl++);
4244		fsp->fs_wtmult = fxdr_unsigned(u_int32_t, *tl++);
4245		fsp->fs_dtpref = fxdr_unsigned(u_int32_t, *tl++);
4246		fsp->fs_maxfilesize = fxdr_hyper(tl);
4247		tl += 2;
4248		fxdr_nfsv3time(tl, &fsp->fs_timedelta);
4249		tl += 2;
4250		fsp->fs_properties = fxdr_unsigned(u_int32_t, *tl);
4251	}
4252nfsmout:
4253	mbuf_freem(nd->nd_mrep);
4254	return (error);
4255}
4256
4257/*
4258 * This function performs the Renew RPC.
4259 */
4260APPLESTATIC int
4261nfsrpc_renew(struct nfsclclient *clp, struct nfsclds *dsp, struct ucred *cred,
4262    NFSPROC_T *p)
4263{
4264	u_int32_t *tl;
4265	struct nfsrv_descript nfsd;
4266	struct nfsrv_descript *nd = &nfsd;
4267	struct nfsmount *nmp;
4268	int error;
4269	struct nfssockreq *nrp;
4270	struct nfsclsession *tsep;
4271
4272	nmp = clp->nfsc_nmp;
4273	if (nmp == NULL)
4274		return (0);
4275	if (dsp == NULL)
4276		nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL, NULL);
4277	else
4278		nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL,
4279		    &dsp->nfsclds_sess);
4280	if (!NFSHASNFSV4N(nmp)) {
4281		/* NFSv4.1 just uses a Sequence Op and not a Renew. */
4282		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4283		tsep = nfsmnt_mdssession(nmp);
4284		*tl++ = tsep->nfsess_clientid.lval[0];
4285		*tl = tsep->nfsess_clientid.lval[1];
4286	}
4287	nrp = NULL;
4288	if (dsp != NULL)
4289		nrp = dsp->nfsclds_sockp;
4290	if (nrp == NULL)
4291		/* If NULL, use the MDS socket. */
4292		nrp = &nmp->nm_sockreq;
4293	nd->nd_flag |= ND_USEGSSNAME;
4294	if (dsp == NULL)
4295		error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
4296		    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4297	else
4298		error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
4299		    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
4300	if (error)
4301		return (error);
4302	error = nd->nd_repstat;
4303	mbuf_freem(nd->nd_mrep);
4304	return (error);
4305}
4306
4307/*
4308 * This function performs the Releaselockowner RPC.
4309 */
4310APPLESTATIC int
4311nfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
4312    uint8_t *fh, int fhlen, struct ucred *cred, NFSPROC_T *p)
4313{
4314	struct nfsrv_descript nfsd, *nd = &nfsd;
4315	u_int32_t *tl;
4316	int error;
4317	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
4318	struct nfsclsession *tsep;
4319
4320	if (NFSHASNFSV4N(nmp)) {
4321		/* For NFSv4.1, do a FreeStateID. */
4322		nfscl_reqstart(nd, NFSPROC_FREESTATEID, nmp, NULL, 0, NULL,
4323		    NULL);
4324		nfsm_stateidtom(nd, &lp->nfsl_stateid, NFSSTATEID_PUTSTATEID);
4325	} else {
4326		nfscl_reqstart(nd, NFSPROC_RELEASELCKOWN, nmp, NULL, 0, NULL,
4327		    NULL);
4328		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4329		tsep = nfsmnt_mdssession(nmp);
4330		*tl++ = tsep->nfsess_clientid.lval[0];
4331		*tl = tsep->nfsess_clientid.lval[1];
4332		NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
4333		NFSBCOPY(fh, &own[NFSV4CL_LOCKNAMELEN], fhlen);
4334		(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
4335	}
4336	nd->nd_flag |= ND_USEGSSNAME;
4337	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4338	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4339	if (error)
4340		return (error);
4341	error = nd->nd_repstat;
4342	mbuf_freem(nd->nd_mrep);
4343	return (error);
4344}
4345
4346/*
4347 * This function performs the Compound to get the mount pt FH.
4348 */
4349APPLESTATIC int
4350nfsrpc_getdirpath(struct nfsmount *nmp, u_char *dirpath, struct ucred *cred,
4351    NFSPROC_T *p)
4352{
4353	u_int32_t *tl;
4354	struct nfsrv_descript nfsd;
4355	struct nfsrv_descript *nd = &nfsd;
4356	u_char *cp, *cp2;
4357	int error, cnt, len, setnil;
4358	u_int32_t *opcntp;
4359
4360	nfscl_reqstart(nd, NFSPROC_PUTROOTFH, nmp, NULL, 0, &opcntp, NULL);
4361	cp = dirpath;
4362	cnt = 0;
4363	do {
4364		setnil = 0;
4365		while (*cp == '/')
4366			cp++;
4367		cp2 = cp;
4368		while (*cp2 != '\0' && *cp2 != '/')
4369			cp2++;
4370		if (*cp2 == '/') {
4371			setnil = 1;
4372			*cp2 = '\0';
4373		}
4374		if (cp2 != cp) {
4375			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4376			*tl = txdr_unsigned(NFSV4OP_LOOKUP);
4377			nfsm_strtom(nd, cp, strlen(cp));
4378			cnt++;
4379		}
4380		if (setnil)
4381			*cp2++ = '/';
4382		cp = cp2;
4383	} while (*cp != '\0');
4384	if (NFSHASNFSV4N(nmp))
4385		/* Has a Sequence Op done by nfscl_reqstart(). */
4386		*opcntp = txdr_unsigned(3 + cnt);
4387	else
4388		*opcntp = txdr_unsigned(2 + cnt);
4389	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4390	*tl = txdr_unsigned(NFSV4OP_GETFH);
4391	nd->nd_flag |= ND_USEGSSNAME;
4392	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4393		NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4394	if (error)
4395		return (error);
4396	if (nd->nd_repstat == 0) {
4397		NFSM_DISSECT(tl, u_int32_t *, (3 + 2 * cnt) * NFSX_UNSIGNED);
4398		tl += (2 + 2 * cnt);
4399		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
4400			len > NFSX_FHMAX) {
4401			nd->nd_repstat = NFSERR_BADXDR;
4402		} else {
4403			nd->nd_repstat = nfsrv_mtostr(nd, nmp->nm_fh, len);
4404			if (nd->nd_repstat == 0)
4405				nmp->nm_fhsize = len;
4406		}
4407	}
4408	error = nd->nd_repstat;
4409nfsmout:
4410	mbuf_freem(nd->nd_mrep);
4411	return (error);
4412}
4413
4414/*
4415 * This function performs the Delegreturn RPC.
4416 */
4417APPLESTATIC int
4418nfsrpc_delegreturn(struct nfscldeleg *dp, struct ucred *cred,
4419    struct nfsmount *nmp, NFSPROC_T *p, int syscred)
4420{
4421	u_int32_t *tl;
4422	struct nfsrv_descript nfsd;
4423	struct nfsrv_descript *nd = &nfsd;
4424	int error;
4425
4426	nfscl_reqstart(nd, NFSPROC_DELEGRETURN, nmp, dp->nfsdl_fh,
4427	    dp->nfsdl_fhlen, NULL, NULL);
4428	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
4429	if (NFSHASNFSV4N(nmp))
4430		*tl++ = 0;
4431	else
4432		*tl++ = dp->nfsdl_stateid.seqid;
4433	*tl++ = dp->nfsdl_stateid.other[0];
4434	*tl++ = dp->nfsdl_stateid.other[1];
4435	*tl = dp->nfsdl_stateid.other[2];
4436	if (syscred)
4437		nd->nd_flag |= ND_USEGSSNAME;
4438	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4439	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4440	if (error)
4441		return (error);
4442	error = nd->nd_repstat;
4443	mbuf_freem(nd->nd_mrep);
4444	return (error);
4445}
4446
4447/*
4448 * nfs getacl call.
4449 */
4450APPLESTATIC int
4451nfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4452    struct acl *aclp, void *stuff)
4453{
4454	struct nfsrv_descript nfsd, *nd = &nfsd;
4455	int error;
4456	nfsattrbit_t attrbits;
4457	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4458
4459	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4460		return (EOPNOTSUPP);
4461	NFSCL_REQSTART(nd, NFSPROC_GETACL, vp);
4462	NFSZERO_ATTRBIT(&attrbits);
4463	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4464	(void) nfsrv_putattrbit(nd, &attrbits);
4465	error = nfscl_request(nd, vp, p, cred, stuff);
4466	if (error)
4467		return (error);
4468	if (!nd->nd_repstat)
4469		error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
4470		    NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, p, cred);
4471	else
4472		error = nd->nd_repstat;
4473	mbuf_freem(nd->nd_mrep);
4474	return (error);
4475}
4476
4477/*
4478 * nfs setacl call.
4479 */
4480APPLESTATIC int
4481nfsrpc_setacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4482    struct acl *aclp, void *stuff)
4483{
4484	int error;
4485	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4486
4487	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4488		return (EOPNOTSUPP);
4489	error = nfsrpc_setattr(vp, NULL, aclp, cred, p, NULL, NULL, stuff);
4490	return (error);
4491}
4492
4493/*
4494 * nfs setacl call.
4495 */
4496static int
4497nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4498    struct acl *aclp, nfsv4stateid_t *stateidp, void *stuff)
4499{
4500	struct nfsrv_descript nfsd, *nd = &nfsd;
4501	int error;
4502	nfsattrbit_t attrbits;
4503	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4504
4505	if (!NFSHASNFSV4(nmp))
4506		return (EOPNOTSUPP);
4507	NFSCL_REQSTART(nd, NFSPROC_SETACL, vp);
4508	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
4509	NFSZERO_ATTRBIT(&attrbits);
4510	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4511	(void) nfsv4_fillattr(nd, vnode_mount(vp), vp, aclp, NULL, NULL, 0,
4512	    &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0);
4513	error = nfscl_request(nd, vp, p, cred, stuff);
4514	if (error)
4515		return (error);
4516	/* Don't care about the pre/postop attributes */
4517	mbuf_freem(nd->nd_mrep);
4518	return (nd->nd_repstat);
4519}
4520
4521/*
4522 * Do the NFSv4.1 Exchange ID.
4523 */
4524int
4525nfsrpc_exchangeid(struct nfsmount *nmp, struct nfsclclient *clp,
4526    struct nfssockreq *nrp, uint32_t exchflags, struct nfsclds **dspp,
4527    struct ucred *cred, NFSPROC_T *p)
4528{
4529	uint32_t *tl, v41flags;
4530	struct nfsrv_descript nfsd;
4531	struct nfsrv_descript *nd = &nfsd;
4532	struct nfsclds *dsp;
4533	struct timespec verstime;
4534	int error, len;
4535
4536	*dspp = NULL;
4537	nfscl_reqstart(nd, NFSPROC_EXCHANGEID, nmp, NULL, 0, NULL, NULL);
4538	NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4539	*tl++ = txdr_unsigned(nfsboottime.tv_sec);	/* Client owner */
4540	*tl = txdr_unsigned(clp->nfsc_rev);
4541	(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
4542
4543	NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
4544	*tl++ = txdr_unsigned(exchflags);
4545	*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);
4546
4547	/* Set the implementation id4 */
4548	*tl = txdr_unsigned(1);
4549	(void) nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
4550	(void) nfsm_strtom(nd, version, strlen(version));
4551	NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
4552	verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
4553	verstime.tv_nsec = 0;
4554	txdr_nfsv4time(&verstime, tl);
4555	nd->nd_flag |= ND_USEGSSNAME;
4556	error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
4557	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4558	NFSCL_DEBUG(1, "exchangeid err=%d reps=%d\n", error,
4559	    (int)nd->nd_repstat);
4560	if (error != 0)
4561		return (error);
4562	if (nd->nd_repstat == 0) {
4563		NFSM_DISSECT(tl, uint32_t *, 6 * NFSX_UNSIGNED + NFSX_HYPER);
4564		len = fxdr_unsigned(int, *(tl + 7));
4565		if (len < 0 || len > NFSV4_OPAQUELIMIT) {
4566			error = NFSERR_BADXDR;
4567			goto nfsmout;
4568		}
4569		dsp = malloc(sizeof(struct nfsclds) + len + 1, M_NFSCLDS,
4570		    M_WAITOK | M_ZERO);
4571		dsp->nfsclds_expire = NFSD_MONOSEC + clp->nfsc_renew;
4572		dsp->nfsclds_servownlen = len;
4573		dsp->nfsclds_sess.nfsess_clientid.lval[0] = *tl++;
4574		dsp->nfsclds_sess.nfsess_clientid.lval[1] = *tl++;
4575		dsp->nfsclds_sess.nfsess_sequenceid =
4576		    fxdr_unsigned(uint32_t, *tl++);
4577		v41flags = fxdr_unsigned(uint32_t, *tl);
4578		if ((v41flags & NFSV4EXCH_USEPNFSMDS) != 0 &&
4579		    NFSHASPNFSOPT(nmp)) {
4580			NFSCL_DEBUG(1, "set PNFS\n");
4581			NFSLOCKMNT(nmp);
4582			nmp->nm_state |= NFSSTA_PNFS;
4583			NFSUNLOCKMNT(nmp);
4584			dsp->nfsclds_flags |= NFSCLDS_MDS;
4585		}
4586		if ((v41flags & NFSV4EXCH_USEPNFSDS) != 0)
4587			dsp->nfsclds_flags |= NFSCLDS_DS;
4588		if (len > 0)
4589			nd->nd_repstat = nfsrv_mtostr(nd,
4590			    dsp->nfsclds_serverown, len);
4591		if (nd->nd_repstat == 0) {
4592			mtx_init(&dsp->nfsclds_mtx, "nfsds", NULL, MTX_DEF);
4593			mtx_init(&dsp->nfsclds_sess.nfsess_mtx, "nfssession",
4594			    NULL, MTX_DEF);
4595			nfscl_initsessionslots(&dsp->nfsclds_sess);
4596			*dspp = dsp;
4597		} else
4598			free(dsp, M_NFSCLDS);
4599	}
4600	error = nd->nd_repstat;
4601nfsmout:
4602	mbuf_freem(nd->nd_mrep);
4603	return (error);
4604}
4605
4606/*
4607 * Do the NFSv4.1 Create Session.
4608 */
4609int
4610nfsrpc_createsession(struct nfsmount *nmp, struct nfsclsession *sep,
4611    struct nfssockreq *nrp, uint32_t sequenceid, int mds, struct ucred *cred,
4612    NFSPROC_T *p)
4613{
4614	uint32_t crflags, maxval, *tl;
4615	struct nfsrv_descript nfsd;
4616	struct nfsrv_descript *nd = &nfsd;
4617	int error, irdcnt;
4618
4619	nfscl_reqstart(nd, NFSPROC_CREATESESSION, nmp, NULL, 0, NULL, NULL);
4620	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED);
4621	*tl++ = sep->nfsess_clientid.lval[0];
4622	*tl++ = sep->nfsess_clientid.lval[1];
4623	*tl++ = txdr_unsigned(sequenceid);
4624	crflags = (NFSMNT_RDONLY(nmp->nm_mountp) ? 0 : NFSV4CRSESS_PERSIST);
4625	if (nfscl_enablecallb != 0 && nfs_numnfscbd > 0 && mds != 0)
4626		crflags |= NFSV4CRSESS_CONNBACKCHAN;
4627	*tl = txdr_unsigned(crflags);
4628
4629	/* Fill in fore channel attributes. */
4630	NFSM_BUILD(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4631	*tl++ = 0;				/* Header pad size */
4632	*tl++ = txdr_unsigned(nmp->nm_wsize + NFS_MAXXDR);/* Max request size */
4633	*tl++ = txdr_unsigned(nmp->nm_rsize + NFS_MAXXDR);/* Max reply size */
4634	*tl++ = txdr_unsigned(4096);		/* Max response size cached */
4635	*tl++ = txdr_unsigned(20);		/* Max operations */
4636	*tl++ = txdr_unsigned(64);		/* Max slots */
4637	*tl = 0;				/* No rdma ird */
4638
4639	/* Fill in back channel attributes. */
4640	NFSM_BUILD(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4641	*tl++ = 0;				/* Header pad size */
4642	*tl++ = txdr_unsigned(10000);		/* Max request size */
4643	*tl++ = txdr_unsigned(10000);		/* Max response size */
4644	*tl++ = txdr_unsigned(4096);		/* Max response size cached */
4645	*tl++ = txdr_unsigned(4);		/* Max operations */
4646	*tl++ = txdr_unsigned(NFSV4_CBSLOTS);	/* Max slots */
4647	*tl = 0;				/* No rdma ird */
4648
4649	NFSM_BUILD(tl, uint32_t *, 8 * NFSX_UNSIGNED);
4650	*tl++ = txdr_unsigned(NFS_CALLBCKPROG);	/* Call back prog # */
4651
4652	/* Allow AUTH_SYS callbacks as uid, gid == 0. */
4653	*tl++ = txdr_unsigned(1);		/* Auth_sys only */
4654	*tl++ = txdr_unsigned(AUTH_SYS);	/* AUTH_SYS type */
4655	*tl++ = txdr_unsigned(nfsboottime.tv_sec); /* time stamp */
4656	*tl++ = 0;				/* Null machine name */
4657	*tl++ = 0;				/* Uid == 0 */
4658	*tl++ = 0;				/* Gid == 0 */
4659	*tl = 0;				/* No additional gids */
4660	nd->nd_flag |= ND_USEGSSNAME;
4661	error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred, NFS_PROG,
4662	    NFS_VER4, NULL, 1, NULL, NULL);
4663	if (error != 0)
4664		return (error);
4665	if (nd->nd_repstat == 0) {
4666		NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
4667		    2 * NFSX_UNSIGNED);
4668		bcopy(tl, sep->nfsess_sessionid, NFSX_V4SESSIONID);
4669		tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
4670		sep->nfsess_sequenceid = fxdr_unsigned(uint32_t, *tl++);
4671		crflags = fxdr_unsigned(uint32_t, *tl);
4672		if ((crflags & NFSV4CRSESS_PERSIST) != 0 && mds != 0) {
4673			NFSLOCKMNT(nmp);
4674			nmp->nm_state |= NFSSTA_SESSPERSIST;
4675			NFSUNLOCKMNT(nmp);
4676		}
4677
4678		/* Get the fore channel slot count. */
4679		NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4680		tl++;			/* Skip the header pad size. */
4681
4682		/* Make sure nm_wsize is small enough. */
4683		maxval = fxdr_unsigned(uint32_t, *tl++);
4684		while (maxval < nmp->nm_wsize + NFS_MAXXDR) {
4685			if (nmp->nm_wsize > 8096)
4686				nmp->nm_wsize /= 2;
4687			else
4688				break;
4689		}
4690
4691		/* Make sure nm_rsize is small enough. */
4692		maxval = fxdr_unsigned(uint32_t, *tl++);
4693		while (maxval < nmp->nm_rsize + NFS_MAXXDR) {
4694			if (nmp->nm_rsize > 8096)
4695				nmp->nm_rsize /= 2;
4696			else
4697				break;
4698		}
4699
4700		sep->nfsess_maxcache = fxdr_unsigned(int, *tl++);
4701		tl++;
4702		sep->nfsess_foreslots = fxdr_unsigned(uint16_t, *tl++);
4703		NFSCL_DEBUG(4, "fore slots=%d\n", (int)sep->nfsess_foreslots);
4704		irdcnt = fxdr_unsigned(int, *tl);
4705		if (irdcnt > 0)
4706			NFSM_DISSECT(tl, uint32_t *, irdcnt * NFSX_UNSIGNED);
4707
4708		/* and the back channel slot count. */
4709		NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4710		tl += 5;
4711		sep->nfsess_backslots = fxdr_unsigned(uint16_t, *tl);
4712		NFSCL_DEBUG(4, "back slots=%d\n", (int)sep->nfsess_backslots);
4713	}
4714	error = nd->nd_repstat;
4715nfsmout:
4716	mbuf_freem(nd->nd_mrep);
4717	return (error);
4718}
4719
4720/*
4721 * Do the NFSv4.1 Destroy Session.
4722 */
4723int
4724nfsrpc_destroysession(struct nfsmount *nmp, struct nfsclclient *clp,
4725    struct ucred *cred, NFSPROC_T *p)
4726{
4727	uint32_t *tl;
4728	struct nfsrv_descript nfsd;
4729	struct nfsrv_descript *nd = &nfsd;
4730	int error;
4731	struct nfsclsession *tsep;
4732
4733	nfscl_reqstart(nd, NFSPROC_DESTROYSESSION, nmp, NULL, 0, NULL, NULL);
4734	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4735	tsep = nfsmnt_mdssession(nmp);
4736	bcopy(tsep->nfsess_sessionid, tl, NFSX_V4SESSIONID);
4737	nd->nd_flag |= ND_USEGSSNAME;
4738	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4739	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4740	if (error != 0)
4741		return (error);
4742	error = nd->nd_repstat;
4743	mbuf_freem(nd->nd_mrep);
4744	return (error);
4745}
4746
4747/*
4748 * Do the NFSv4.1 Destroy Client.
4749 */
4750int
4751nfsrpc_destroyclient(struct nfsmount *nmp, struct nfsclclient *clp,
4752    struct ucred *cred, NFSPROC_T *p)
4753{
4754	uint32_t *tl;
4755	struct nfsrv_descript nfsd;
4756	struct nfsrv_descript *nd = &nfsd;
4757	int error;
4758	struct nfsclsession *tsep;
4759
4760	nfscl_reqstart(nd, NFSPROC_DESTROYCLIENT, nmp, NULL, 0, NULL, NULL);
4761	NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4762	tsep = nfsmnt_mdssession(nmp);
4763	*tl++ = tsep->nfsess_clientid.lval[0];
4764	*tl = tsep->nfsess_clientid.lval[1];
4765	nd->nd_flag |= ND_USEGSSNAME;
4766	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4767	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4768	if (error != 0)
4769		return (error);
4770	error = nd->nd_repstat;
4771	mbuf_freem(nd->nd_mrep);
4772	return (error);
4773}
4774
4775/*
4776 * Do the NFSv4.1 LayoutGet.
4777 */
4778int
4779nfsrpc_layoutget(struct nfsmount *nmp, uint8_t *fhp, int fhlen, int iomode,
4780    uint64_t offset, uint64_t len, uint64_t minlen, int layoutlen,
4781    nfsv4stateid_t *stateidp, int *retonclosep, struct nfsclflayouthead *flhp,
4782    struct ucred *cred, NFSPROC_T *p, void *stuff)
4783{
4784	uint32_t *tl;
4785	struct nfsrv_descript nfsd, *nd = &nfsd;
4786	struct nfsfh *nfhp;
4787	struct nfsclflayout *flp, *prevflp, *tflp;
4788	int cnt, error, gotiomode, fhcnt, nfhlen, i, j;
4789	uint8_t *cp;
4790	uint64_t retlen;
4791
4792	flp = NULL;
4793	gotiomode = -1;
4794	nfscl_reqstart(nd, NFSPROC_LAYOUTGET, nmp, fhp, fhlen, NULL, NULL);
4795	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
4796	    NFSX_STATEID);
4797	*tl++ = newnfs_false;		/* Don't signal availability. */
4798	*tl++ = txdr_unsigned(NFSLAYOUT_NFSV4_1_FILES);
4799	*tl++ = txdr_unsigned(iomode);
4800	txdr_hyper(offset, tl);
4801	tl += 2;
4802	txdr_hyper(len, tl);
4803	tl += 2;
4804	txdr_hyper(minlen, tl);
4805	tl += 2;
4806	*tl++ = txdr_unsigned(stateidp->seqid);
4807	NFSCL_DEBUG(4, "layget seq=%d\n", (int)stateidp->seqid);
4808	*tl++ = stateidp->other[0];
4809	*tl++ = stateidp->other[1];
4810	*tl++ = stateidp->other[2];
4811	*tl = txdr_unsigned(layoutlen);
4812	nd->nd_flag |= ND_USEGSSNAME;
4813	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4814	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4815	if (error != 0)
4816		return (error);
4817	if (nd->nd_repstat == 0) {
4818		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + NFSX_STATEID);
4819		if (*tl++ != 0)
4820			*retonclosep = 1;
4821		else
4822			*retonclosep = 0;
4823		stateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
4824		NFSCL_DEBUG(4, "retoncls=%d stseq=%d\n", *retonclosep,
4825		    (int)stateidp->seqid);
4826		stateidp->other[0] = *tl++;
4827		stateidp->other[1] = *tl++;
4828		stateidp->other[2] = *tl++;
4829		cnt = fxdr_unsigned(int, *tl);
4830		NFSCL_DEBUG(4, "layg cnt=%d\n", cnt);
4831		if (cnt <= 0 || cnt > 10000) {
4832			/* Don't accept more than 10000 layouts in reply. */
4833			error = NFSERR_BADXDR;
4834			goto nfsmout;
4835		}
4836		for (i = 0; i < cnt; i++) {
4837			/* Dissect all the way to the file handle cnt. */
4838			NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_HYPER +
4839			    6 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
4840			fhcnt = fxdr_unsigned(int, *(tl + 11 +
4841			    NFSX_V4DEVICEID / NFSX_UNSIGNED));
4842			NFSCL_DEBUG(4, "fhcnt=%d\n", fhcnt);
4843			if (fhcnt < 0 || fhcnt > 100) {
4844				/* Don't accept more than 100 file handles. */
4845				error = NFSERR_BADXDR;
4846				goto nfsmout;
4847			}
4848			if (fhcnt > 1)
4849				flp = malloc(sizeof(*flp) + (fhcnt - 1) *
4850				    sizeof(struct nfsfh *),
4851				    M_NFSFLAYOUT, M_WAITOK);
4852			else
4853				flp = malloc(sizeof(*flp),
4854				    M_NFSFLAYOUT, M_WAITOK);
4855			flp->nfsfl_flags = 0;
4856			flp->nfsfl_fhcnt = 0;
4857			flp->nfsfl_devp = NULL;
4858			flp->nfsfl_off = fxdr_hyper(tl); tl += 2;
4859			retlen = fxdr_hyper(tl); tl += 2;
4860			if (flp->nfsfl_off + retlen < flp->nfsfl_off)
4861				flp->nfsfl_end = UINT64_MAX - flp->nfsfl_off;
4862			else
4863				flp->nfsfl_end = flp->nfsfl_off + retlen;
4864			flp->nfsfl_iomode = fxdr_unsigned(int, *tl++);
4865			if (gotiomode == -1)
4866				gotiomode = flp->nfsfl_iomode;
4867			NFSCL_DEBUG(4, "layg reqiom=%d retiom=%d\n", iomode,
4868			    (int)flp->nfsfl_iomode);
4869			if (fxdr_unsigned(int, *tl++) !=
4870			    NFSLAYOUT_NFSV4_1_FILES) {
4871				printf("NFSv4.1: got non-files layout\n");
4872				error = NFSERR_BADXDR;
4873				goto nfsmout;
4874			}
4875			NFSBCOPY(++tl, flp->nfsfl_dev, NFSX_V4DEVICEID);
4876			tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
4877			flp->nfsfl_util = fxdr_unsigned(uint32_t, *tl++);
4878			NFSCL_DEBUG(4, "flutil=0x%x\n", flp->nfsfl_util);
4879			flp->nfsfl_stripe1 = fxdr_unsigned(uint32_t, *tl++);
4880			flp->nfsfl_patoff = fxdr_hyper(tl); tl += 2;
4881			if (fxdr_unsigned(int, *tl) != fhcnt) {
4882				printf("EEK! bad fhcnt\n");
4883				error = NFSERR_BADXDR;
4884				goto nfsmout;
4885			}
4886			for (j = 0; j < fhcnt; j++) {
4887				NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4888				nfhlen = fxdr_unsigned(int, *tl);
4889				if (nfhlen <= 0 || nfhlen > NFSX_V4FHMAX) {
4890					error = NFSERR_BADXDR;
4891					goto nfsmout;
4892				}
4893				nfhp = malloc(sizeof(*nfhp) + nfhlen - 1,
4894				    M_NFSFH, M_WAITOK);
4895				flp->nfsfl_fh[j] = nfhp;
4896				flp->nfsfl_fhcnt++;
4897				nfhp->nfh_len = nfhlen;
4898				NFSM_DISSECT(cp, uint8_t *, NFSM_RNDUP(nfhlen));
4899				NFSBCOPY(cp, nfhp->nfh_fh, nfhlen);
4900			}
4901			if (flp->nfsfl_iomode == gotiomode) {
4902				/* Keep the list in increasing offset order. */
4903				tflp = LIST_FIRST(flhp);
4904				prevflp = NULL;
4905				while (tflp != NULL &&
4906				    tflp->nfsfl_off < flp->nfsfl_off) {
4907					prevflp = tflp;
4908					tflp = LIST_NEXT(tflp, nfsfl_list);
4909				}
4910				if (prevflp == NULL)
4911					LIST_INSERT_HEAD(flhp, flp, nfsfl_list);
4912				else
4913					LIST_INSERT_AFTER(prevflp, flp,
4914					    nfsfl_list);
4915			} else {
4916				printf("nfscl_layoutget(): got wrong iomode\n");
4917				nfscl_freeflayout(flp);
4918			}
4919			flp = NULL;
4920		}
4921	}
4922	if (nd->nd_repstat != 0 && error == 0)
4923		error = nd->nd_repstat;
4924nfsmout:
4925	if (error != 0 && flp != NULL)
4926		nfscl_freeflayout(flp);
4927	mbuf_freem(nd->nd_mrep);
4928	return (error);
4929}
4930
4931/*
4932 * Do the NFSv4.1 Get Device Info.
4933 */
4934int
4935nfsrpc_getdeviceinfo(struct nfsmount *nmp, uint8_t *deviceid, int layouttype,
4936    uint32_t *notifybitsp, struct nfscldevinfo **ndip, struct ucred *cred,
4937    NFSPROC_T *p)
4938{
4939	uint32_t cnt, *tl;
4940	struct nfsrv_descript nfsd;
4941	struct nfsrv_descript *nd = &nfsd;
4942	struct sockaddr_storage ss;
4943	struct nfsclds *dsp = NULL, **dspp;
4944	struct nfscldevinfo *ndi;
4945	int addrcnt, bitcnt, error, i, isudp, j, pos, safilled, stripecnt;
4946	uint8_t stripeindex;
4947
4948	*ndip = NULL;
4949	ndi = NULL;
4950	nfscl_reqstart(nd, NFSPROC_GETDEVICEINFO, nmp, NULL, 0, NULL, NULL);
4951	NFSM_BUILD(tl, uint32_t *, NFSX_V4DEVICEID + 3 * NFSX_UNSIGNED);
4952	NFSBCOPY(deviceid, tl, NFSX_V4DEVICEID);
4953	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
4954	*tl++ = txdr_unsigned(layouttype);
4955	*tl++ = txdr_unsigned(100000);
4956	if (notifybitsp != NULL && *notifybitsp != 0) {
4957		*tl = txdr_unsigned(1);		/* One word of bits. */
4958		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4959		*tl = txdr_unsigned(*notifybitsp);
4960	} else
4961		*tl = txdr_unsigned(0);
4962	nd->nd_flag |= ND_USEGSSNAME;
4963	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4964	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4965	if (error != 0)
4966		return (error);
4967	if (nd->nd_repstat == 0) {
4968		NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_UNSIGNED);
4969		if (layouttype != fxdr_unsigned(int, *tl++))
4970			printf("EEK! devinfo layout type not same!\n");
4971		stripecnt = fxdr_unsigned(int, *++tl);
4972		NFSCL_DEBUG(4, "stripecnt=%d\n", stripecnt);
4973		if (stripecnt < 1 || stripecnt > 4096) {
4974			printf("NFS devinfo stripecnt %d: out of range\n",
4975			    stripecnt);
4976			error = NFSERR_BADXDR;
4977			goto nfsmout;
4978		}
4979		NFSM_DISSECT(tl, uint32_t *, (stripecnt + 1) * NFSX_UNSIGNED);
4980		addrcnt = fxdr_unsigned(int, *(tl + stripecnt));
4981		NFSCL_DEBUG(4, "addrcnt=%d\n", addrcnt);
4982		if (addrcnt < 1 || addrcnt > 128) {
4983			printf("NFS devinfo addrcnt %d: out of range\n",
4984			    addrcnt);
4985			error = NFSERR_BADXDR;
4986			goto nfsmout;
4987		}
4988
4989		/*
4990		 * Now we know how many stripe indices and addresses, so
4991		 * we can allocate the structure the correct size.
4992		 */
4993		i = (stripecnt * sizeof(uint8_t)) / sizeof(struct nfsclds *)
4994		    + 1;
4995		NFSCL_DEBUG(4, "stripeindices=%d\n", i);
4996		ndi = malloc(sizeof(*ndi) + (addrcnt + i) *
4997		    sizeof(struct nfsclds *), M_NFSDEVINFO, M_WAITOK | M_ZERO);
4998		NFSBCOPY(deviceid, ndi->nfsdi_deviceid, NFSX_V4DEVICEID);
4999		ndi->nfsdi_refcnt = 0;
5000		ndi->nfsdi_stripecnt = stripecnt;
5001		ndi->nfsdi_addrcnt = addrcnt;
5002		/* Fill in the stripe indices. */
5003		for (i = 0; i < stripecnt; i++) {
5004			stripeindex = fxdr_unsigned(uint8_t, *tl++);
5005			NFSCL_DEBUG(4, "stripeind=%d\n", stripeindex);
5006			if (stripeindex >= addrcnt) {
5007				printf("NFS devinfo stripeindex %d: too big\n",
5008				    (int)stripeindex);
5009				error = NFSERR_BADXDR;
5010				goto nfsmout;
5011			}
5012			nfsfldi_setstripeindex(ndi, i, stripeindex);
5013		}
5014
5015		/* Now, dissect the server address(es). */
5016		safilled = 0;
5017		for (i = 0; i < addrcnt; i++) {
5018			NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5019			cnt = fxdr_unsigned(uint32_t, *tl);
5020			if (cnt == 0) {
5021				printf("NFS devinfo 0 len addrlist\n");
5022				error = NFSERR_BADXDR;
5023				goto nfsmout;
5024			}
5025			dspp = nfsfldi_addr(ndi, i);
5026			pos = arc4random() % cnt;	/* Choose one. */
5027			safilled = 0;
5028			for (j = 0; j < cnt; j++) {
5029				error = nfsv4_getipaddr(nd, &ss, &isudp);
5030				if (error != 0 && error != EPERM) {
5031					error = NFSERR_BADXDR;
5032					goto nfsmout;
5033				}
5034				if (error == 0 && isudp == 0) {
5035					/*
5036					 * The algorithm is:
5037					 * - use "pos" entry if it is of the
5038					 *   same af_family or none of them
5039					 *   is of the same af_family
5040					 * else
5041					 * - use the first one of the same
5042					 *   af_family.
5043					 */
5044					if ((safilled == 0 && ss.ss_family ==
5045					     nmp->nm_nam->sa_family) ||
5046					    (j == pos &&
5047					     (safilled == 0 || ss.ss_family ==
5048					      nmp->nm_nam->sa_family)) ||
5049					    (safilled == 1 && ss.ss_family ==
5050					     nmp->nm_nam->sa_family)) {
5051						error = nfsrpc_fillsa(nmp, &ss,
5052						    &dsp, p);
5053						if (error == 0) {
5054							*dspp = dsp;
5055							if (ss.ss_family ==
5056							 nmp->nm_nam->sa_family)
5057								safilled = 2;
5058							else
5059								safilled = 1;
5060						}
5061					}
5062				}
5063			}
5064			if (safilled == 0)
5065				break;
5066		}
5067
5068		/* And the notify bits. */
5069		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5070		if (safilled != 0) {
5071			bitcnt = fxdr_unsigned(int, *tl);
5072			if (bitcnt > 0) {
5073				NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5074				if (notifybitsp != NULL)
5075					*notifybitsp =
5076					    fxdr_unsigned(uint32_t, *tl);
5077			}
5078			*ndip = ndi;
5079		} else
5080			error = EPERM;
5081	}
5082	if (nd->nd_repstat != 0)
5083		error = nd->nd_repstat;
5084nfsmout:
5085	if (error != 0 && ndi != NULL)
5086		nfscl_freedevinfo(ndi);
5087	mbuf_freem(nd->nd_mrep);
5088	return (error);
5089}
5090
5091/*
5092 * Do the NFSv4.1 LayoutCommit.
5093 */
5094int
5095nfsrpc_layoutcommit(struct nfsmount *nmp, uint8_t *fh, int fhlen, int reclaim,
5096    uint64_t off, uint64_t len, uint64_t lastbyte, nfsv4stateid_t *stateidp,
5097    int layouttype, int layoutupdatecnt, uint8_t *layp, struct ucred *cred,
5098    NFSPROC_T *p, void *stuff)
5099{
5100	uint32_t *tl;
5101	struct nfsrv_descript nfsd, *nd = &nfsd;
5102	int error, outcnt, i;
5103	uint8_t *cp;
5104
5105	nfscl_reqstart(nd, NFSPROC_LAYOUTCOMMIT, nmp, fh, fhlen, NULL, NULL);
5106	NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
5107	    NFSX_STATEID);
5108	txdr_hyper(off, tl);
5109	tl += 2;
5110	txdr_hyper(len, tl);
5111	tl += 2;
5112	if (reclaim != 0)
5113		*tl++ = newnfs_true;
5114	else
5115		*tl++ = newnfs_false;
5116	*tl++ = txdr_unsigned(stateidp->seqid);
5117	*tl++ = stateidp->other[0];
5118	*tl++ = stateidp->other[1];
5119	*tl++ = stateidp->other[2];
5120	*tl++ = newnfs_true;
5121	if (lastbyte < off)
5122		lastbyte = off;
5123	else if (lastbyte >= (off + len))
5124		lastbyte = off + len - 1;
5125	txdr_hyper(lastbyte, tl);
5126	tl += 2;
5127	*tl++ = newnfs_false;
5128	*tl++ = txdr_unsigned(layouttype);
5129	*tl = txdr_unsigned(layoutupdatecnt);
5130	if (layoutupdatecnt > 0) {
5131		KASSERT(layouttype != NFSLAYOUT_NFSV4_1_FILES,
5132		    ("Must be nil for Files Layout"));
5133		outcnt = NFSM_RNDUP(layoutupdatecnt);
5134		NFSM_BUILD(cp, uint8_t *, outcnt);
5135		NFSBCOPY(layp, cp, layoutupdatecnt);
5136		cp += layoutupdatecnt;
5137		for (i = 0; i < (outcnt - layoutupdatecnt); i++)
5138			*cp++ = 0x0;
5139	}
5140	nd->nd_flag |= ND_USEGSSNAME;
5141	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
5142	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
5143	if (error != 0)
5144		return (error);
5145	error = nd->nd_repstat;
5146	mbuf_freem(nd->nd_mrep);
5147	return (error);
5148}
5149
5150/*
5151 * Do the NFSv4.1 LayoutReturn.
5152 */
5153int
5154nfsrpc_layoutreturn(struct nfsmount *nmp, uint8_t *fh, int fhlen, int reclaim,
5155    int layouttype, uint32_t iomode, int layoutreturn, uint64_t offset,
5156    uint64_t len, nfsv4stateid_t *stateidp, int layoutcnt, uint32_t *layp,
5157    struct ucred *cred, NFSPROC_T *p, void *stuff)
5158{
5159	uint32_t *tl;
5160	struct nfsrv_descript nfsd, *nd = &nfsd;
5161	int error, outcnt, i;
5162	uint8_t *cp;
5163
5164	nfscl_reqstart(nd, NFSPROC_LAYOUTRETURN, nmp, fh, fhlen, NULL, NULL);
5165	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED);
5166	if (reclaim != 0)
5167		*tl++ = newnfs_true;
5168	else
5169		*tl++ = newnfs_false;
5170	*tl++ = txdr_unsigned(layouttype);
5171	*tl++ = txdr_unsigned(iomode);
5172	*tl = txdr_unsigned(layoutreturn);
5173	if (layoutreturn == NFSLAYOUTRETURN_FILE) {
5174		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
5175		    NFSX_UNSIGNED);
5176		txdr_hyper(offset, tl);
5177		tl += 2;
5178		txdr_hyper(len, tl);
5179		tl += 2;
5180		NFSCL_DEBUG(4, "layoutret stseq=%d\n", (int)stateidp->seqid);
5181		*tl++ = txdr_unsigned(stateidp->seqid);
5182		*tl++ = stateidp->other[0];
5183		*tl++ = stateidp->other[1];
5184		*tl++ = stateidp->other[2];
5185		*tl = txdr_unsigned(layoutcnt);
5186		if (layoutcnt > 0) {
5187			outcnt = NFSM_RNDUP(layoutcnt);
5188			NFSM_BUILD(cp, uint8_t *, outcnt);
5189			NFSBCOPY(layp, cp, layoutcnt);
5190			cp += layoutcnt;
5191			for (i = 0; i < (outcnt - layoutcnt); i++)
5192				*cp++ = 0x0;
5193		}
5194	}
5195	nd->nd_flag |= ND_USEGSSNAME;
5196	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
5197	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
5198	if (error != 0)
5199		return (error);
5200	if (nd->nd_repstat == 0) {
5201		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5202		if (*tl != 0) {
5203			NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
5204			stateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
5205			stateidp->other[0] = *tl++;
5206			stateidp->other[1] = *tl++;
5207			stateidp->other[2] = *tl;
5208		}
5209	} else
5210		error = nd->nd_repstat;
5211nfsmout:
5212	mbuf_freem(nd->nd_mrep);
5213	return (error);
5214}
5215
5216/*
5217 * Acquire a layout and devinfo, if possible. The caller must have acquired
5218 * a reference count on the nfsclclient structure before calling this.
5219 * Return the layout in lypp with a reference count on it, if successful.
5220 */
5221static int
5222nfsrpc_getlayout(struct nfsmount *nmp, vnode_t vp, struct nfsfh *nfhp,
5223    int iomode, uint32_t *notifybitsp, nfsv4stateid_t *stateidp, uint64_t off,
5224    struct nfscllayout **lypp, struct ucred *cred, NFSPROC_T *p)
5225{
5226	struct nfscllayout *lyp;
5227	struct nfsclflayout *flp, *tflp;
5228	struct nfscldevinfo *dip;
5229	struct nfsclflayouthead flh;
5230	int error = 0, islocked, layoutlen, recalled, retonclose;
5231	nfsv4stateid_t stateid;
5232	struct nfsclsession *tsep;
5233
5234	*lypp = NULL;
5235	/*
5236	 * If lyp is returned non-NULL, there will be a refcnt (shared lock)
5237	 * on it, iff flp != NULL or a lock (exclusive lock) on it iff
5238	 * flp == NULL.
5239	 */
5240	lyp = nfscl_getlayout(nmp->nm_clp, nfhp->nfh_fh, nfhp->nfh_len,
5241	    off, &flp, &recalled);
5242	islocked = 0;
5243	if (lyp == NULL || flp == NULL) {
5244		if (recalled != 0)
5245			return (EIO);
5246		LIST_INIT(&flh);
5247		tsep = nfsmnt_mdssession(nmp);
5248		layoutlen = tsep->nfsess_maxcache -
5249		    (NFSX_STATEID + 3 * NFSX_UNSIGNED);
5250		if (lyp == NULL) {
5251			stateid.seqid = 0;
5252			stateid.other[0] = stateidp->other[0];
5253			stateid.other[1] = stateidp->other[1];
5254			stateid.other[2] = stateidp->other[2];
5255			error = nfsrpc_layoutget(nmp, nfhp->nfh_fh,
5256			    nfhp->nfh_len, iomode, (uint64_t)0, UINT64_MAX,
5257			    (uint64_t)0, layoutlen, &stateid, &retonclose,
5258			    &flh, cred, p, NULL);
5259		} else {
5260			islocked = 1;
5261			stateid.seqid = lyp->nfsly_stateid.seqid;
5262			stateid.other[0] = lyp->nfsly_stateid.other[0];
5263			stateid.other[1] = lyp->nfsly_stateid.other[1];
5264			stateid.other[2] = lyp->nfsly_stateid.other[2];
5265			error = nfsrpc_layoutget(nmp, nfhp->nfh_fh,
5266			    nfhp->nfh_len, iomode, off, UINT64_MAX,
5267			    (uint64_t)0, layoutlen, &stateid, &retonclose,
5268			    &flh, cred, p, NULL);
5269		}
5270		if (error == 0)
5271			LIST_FOREACH(tflp, &flh, nfsfl_list) {
5272				error = nfscl_adddevinfo(nmp, NULL, tflp);
5273				if (error != 0) {
5274					error = nfsrpc_getdeviceinfo(nmp,
5275					    tflp->nfsfl_dev,
5276					    NFSLAYOUT_NFSV4_1_FILES,
5277					    notifybitsp, &dip, cred, p);
5278					if (error != 0)
5279						break;
5280					error = nfscl_adddevinfo(nmp, dip,
5281					    tflp);
5282					if (error != 0)
5283						printf(
5284						    "getlayout: cannot add\n");
5285				}
5286			}
5287		if (error == 0) {
5288			/*
5289			 * nfscl_layout() always returns with the nfsly_lock
5290			 * set to a refcnt (shared lock).
5291			 */
5292			error = nfscl_layout(nmp, vp, nfhp->nfh_fh,
5293			    nfhp->nfh_len, &stateid, retonclose, &flh, &lyp,
5294			    cred, p);
5295			if (error == 0)
5296				*lypp = lyp;
5297		} else if (islocked != 0)
5298			nfsv4_unlock(&lyp->nfsly_lock, 0);
5299	} else
5300		*lypp = lyp;
5301	return (error);
5302}
5303
5304/*
5305 * Do a TCP connection plus exchange id and create session.
5306 * If successful, a "struct nfsclds" is linked into the list for the
5307 * mount point and a pointer to it is returned.
5308 */
5309static int
5310nfsrpc_fillsa(struct nfsmount *nmp, struct sockaddr_storage *ssp,
5311    struct nfsclds **dspp, NFSPROC_T *p)
5312{
5313	struct sockaddr_in *msad, *sad, *ssd;
5314	struct sockaddr_in6 *msad6, *sad6, *ssd6;
5315	struct nfsclclient *clp;
5316	struct nfssockreq *nrp;
5317	struct nfsclds *dsp, *tdsp;
5318	int error;
5319	enum nfsclds_state retv;
5320	uint32_t sequenceid;
5321
5322	KASSERT(nmp->nm_sockreq.nr_cred != NULL,
5323	    ("nfsrpc_fillsa: NULL nr_cred"));
5324	NFSLOCKCLSTATE();
5325	clp = nmp->nm_clp;
5326	NFSUNLOCKCLSTATE();
5327	if (clp == NULL)
5328		return (EPERM);
5329	if (ssp->ss_family == AF_INET) {
5330		ssd = (struct sockaddr_in *)ssp;
5331		NFSLOCKMNT(nmp);
5332
5333		/*
5334		 * Check to see if we already have a session for this
5335		 * address that is usable for a DS.
5336		 * Note that the MDS's address is in a different place
5337		 * than the sessions already acquired for DS's.
5338		 */
5339		msad = (struct sockaddr_in *)nmp->nm_sockreq.nr_nam;
5340		tdsp = TAILQ_FIRST(&nmp->nm_sess);
5341		while (tdsp != NULL) {
5342			if (msad != NULL && msad->sin_family == AF_INET &&
5343			    ssd->sin_addr.s_addr == msad->sin_addr.s_addr &&
5344			    ssd->sin_port == msad->sin_port &&
5345			    (tdsp->nfsclds_flags & NFSCLDS_DS) != 0 &&
5346			    tdsp->nfsclds_sess.nfsess_defunct == 0) {
5347				*dspp = tdsp;
5348				NFSUNLOCKMNT(nmp);
5349				NFSCL_DEBUG(4, "fnd same addr\n");
5350				return (0);
5351			}
5352			tdsp = TAILQ_NEXT(tdsp, nfsclds_list);
5353			if (tdsp != NULL && tdsp->nfsclds_sockp != NULL)
5354				msad = (struct sockaddr_in *)
5355				    tdsp->nfsclds_sockp->nr_nam;
5356			else
5357				msad = NULL;
5358		}
5359		NFSUNLOCKMNT(nmp);
5360
5361		/* No IP address match, so look for new/trunked one. */
5362		sad = malloc(sizeof(*sad), M_SONAME, M_WAITOK | M_ZERO);
5363		sad->sin_len = sizeof(*sad);
5364		sad->sin_family = AF_INET;
5365		sad->sin_port = ssd->sin_port;
5366		sad->sin_addr.s_addr = ssd->sin_addr.s_addr;
5367		nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ, M_WAITOK | M_ZERO);
5368		nrp->nr_nam = (struct sockaddr *)sad;
5369	} else if (ssp->ss_family == AF_INET6) {
5370		ssd6 = (struct sockaddr_in6 *)ssp;
5371		NFSLOCKMNT(nmp);
5372
5373		/*
5374		 * Check to see if we already have a session for this
5375		 * address that is usable for a DS.
5376		 * Note that the MDS's address is in a different place
5377		 * than the sessions already acquired for DS's.
5378		 */
5379		msad6 = (struct sockaddr_in6 *)nmp->nm_sockreq.nr_nam;
5380		tdsp = TAILQ_FIRST(&nmp->nm_sess);
5381		while (tdsp != NULL) {
5382			if (msad6 != NULL && msad6->sin6_family == AF_INET6 &&
5383			    IN6_ARE_ADDR_EQUAL(&ssd6->sin6_addr,
5384			    &msad6->sin6_addr) &&
5385			    ssd6->sin6_port == msad6->sin6_port &&
5386			    (tdsp->nfsclds_flags & NFSCLDS_DS) != 0 &&
5387			    tdsp->nfsclds_sess.nfsess_defunct == 0) {
5388				*dspp = tdsp;
5389				NFSUNLOCKMNT(nmp);
5390				return (0);
5391			}
5392			tdsp = TAILQ_NEXT(tdsp, nfsclds_list);
5393			if (tdsp != NULL && tdsp->nfsclds_sockp != NULL)
5394				msad6 = (struct sockaddr_in6 *)
5395				    tdsp->nfsclds_sockp->nr_nam;
5396			else
5397				msad6 = NULL;
5398		}
5399		NFSUNLOCKMNT(nmp);
5400
5401		/* No IP address match, so look for new/trunked one. */
5402		sad6 = malloc(sizeof(*sad6), M_SONAME, M_WAITOK | M_ZERO);
5403		sad6->sin6_len = sizeof(*sad6);
5404		sad6->sin6_family = AF_INET6;
5405		sad6->sin6_port = ssd6->sin6_port;
5406		NFSBCOPY(&ssd6->sin6_addr, &sad6->sin6_addr,
5407		    sizeof(struct in6_addr));
5408		nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ, M_WAITOK | M_ZERO);
5409		nrp->nr_nam = (struct sockaddr *)sad6;
5410	} else
5411		return (EPERM);
5412
5413	nrp->nr_sotype = SOCK_STREAM;
5414	mtx_init(&nrp->nr_mtx, "nfssock", NULL, MTX_DEF);
5415	nrp->nr_prog = NFS_PROG;
5416	nrp->nr_vers = NFS_VER4;
5417
5418	/*
5419	 * Use the credentials that were used for the mount, which are
5420	 * in nmp->nm_sockreq.nr_cred for newnfs_connect() etc.
5421	 * Ref. counting the credentials with crhold() is probably not
5422	 * necessary, since nm_sockreq.nr_cred won't be crfree()'d until
5423	 * unmount, but I did it anyhow.
5424	 */
5425	nrp->nr_cred = crhold(nmp->nm_sockreq.nr_cred);
5426	error = newnfs_connect(nmp, nrp, NULL, p, 0);
5427	NFSCL_DEBUG(3, "DS connect=%d\n", error);
5428
5429	/* Now, do the exchangeid and create session. */
5430	if (error == 0) {
5431		error = nfsrpc_exchangeid(nmp, clp, nrp, NFSV4EXCH_USEPNFSDS,
5432		    &dsp, nrp->nr_cred, p);
5433		NFSCL_DEBUG(3, "DS exchangeid=%d\n", error);
5434		if (error != 0)
5435			newnfs_disconnect(nrp);
5436	}
5437	if (error == 0) {
5438		dsp->nfsclds_sockp = nrp;
5439		NFSLOCKMNT(nmp);
5440		retv = nfscl_getsameserver(nmp, dsp, &tdsp);
5441		NFSCL_DEBUG(3, "getsame ret=%d\n", retv);
5442		if (retv == NFSDSP_USETHISSESSION) {
5443			NFSUNLOCKMNT(nmp);
5444			/*
5445			 * If there is already a session for this server,
5446			 * use it.
5447			 */
5448			(void)newnfs_disconnect(nrp);
5449			nfscl_freenfsclds(dsp);
5450			*dspp = tdsp;
5451			return (0);
5452		}
5453		if (retv == NFSDSP_SEQTHISSESSION)
5454			sequenceid = tdsp->nfsclds_sess.nfsess_sequenceid;
5455		else
5456			sequenceid = dsp->nfsclds_sess.nfsess_sequenceid;
5457		NFSUNLOCKMNT(nmp);
5458		error = nfsrpc_createsession(nmp, &dsp->nfsclds_sess,
5459		    nrp, sequenceid, 0, nrp->nr_cred, p);
5460		NFSCL_DEBUG(3, "DS createsess=%d\n", error);
5461	} else {
5462		NFSFREECRED(nrp->nr_cred);
5463		NFSFREEMUTEX(&nrp->nr_mtx);
5464		free(nrp->nr_nam, M_SONAME);
5465		free(nrp, M_NFSSOCKREQ);
5466	}
5467	if (error == 0) {
5468		NFSCL_DEBUG(3, "add DS session\n");
5469		/*
5470		 * Put it at the end of the list. That way the list
5471		 * is ordered by when the entry was added. This matters
5472		 * since the one done first is the one that should be
5473		 * used for sequencid'ing any subsequent create sessions.
5474		 */
5475		NFSLOCKMNT(nmp);
5476		TAILQ_INSERT_TAIL(&nmp->nm_sess, dsp, nfsclds_list);
5477		NFSUNLOCKMNT(nmp);
5478		*dspp = dsp;
5479	} else if (dsp != NULL) {
5480		newnfs_disconnect(nrp);
5481		nfscl_freenfsclds(dsp);
5482	}
5483	return (error);
5484}
5485
5486/*
5487 * Do the NFSv4.1 Reclaim Complete.
5488 */
5489int
5490nfsrpc_reclaimcomplete(struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
5491{
5492	uint32_t *tl;
5493	struct nfsrv_descript nfsd;
5494	struct nfsrv_descript *nd = &nfsd;
5495	int error;
5496
5497	nfscl_reqstart(nd, NFSPROC_RECLAIMCOMPL, nmp, NULL, 0, NULL, NULL);
5498	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
5499	*tl = newnfs_false;
5500	nd->nd_flag |= ND_USEGSSNAME;
5501	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
5502	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
5503	if (error != 0)
5504		return (error);
5505	error = nd->nd_repstat;
5506	mbuf_freem(nd->nd_mrep);
5507	return (error);
5508}
5509
5510/*
5511 * Initialize the slot tables for a session.
5512 */
5513static void
5514nfscl_initsessionslots(struct nfsclsession *sep)
5515{
5516	int i;
5517
5518	for (i = 0; i < NFSV4_CBSLOTS; i++) {
5519		if (sep->nfsess_cbslots[i].nfssl_reply != NULL)
5520			m_freem(sep->nfsess_cbslots[i].nfssl_reply);
5521		NFSBZERO(&sep->nfsess_cbslots[i], sizeof(struct nfsslot));
5522	}
5523	for (i = 0; i < 64; i++)
5524		sep->nfsess_slotseq[i] = 0;
5525	sep->nfsess_slots = 0;
5526}
5527
5528/*
5529 * Called to try and do an I/O operation via an NFSv4.1 Data Server (DS).
5530 */
5531int
5532nfscl_doiods(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
5533    uint32_t rwaccess, int docommit, struct ucred *cred, NFSPROC_T *p)
5534{
5535	struct nfsnode *np = VTONFS(vp);
5536	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5537	struct nfscllayout *layp;
5538	struct nfscldevinfo *dip;
5539	struct nfsclflayout *rflp;
5540	nfsv4stateid_t stateid;
5541	struct ucred *newcred;
5542	uint64_t lastbyte, len, off, oresid, xfer;
5543	int eof, error, iolaymode, recalled;
5544	void *lckp;
5545
5546	if (!NFSHASPNFS(nmp) || nfscl_enablecallb == 0 || nfs_numnfscbd == 0 ||
5547	    (np->n_flag & NNOLAYOUT) != 0)
5548		return (EIO);
5549	/* Now, get a reference cnt on the clientid for this mount. */
5550	if (nfscl_getref(nmp) == 0)
5551		return (EIO);
5552
5553	/* Find an appropriate stateid. */
5554	newcred = NFSNEWCRED(cred);
5555	error = nfscl_getstateid(vp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
5556	    rwaccess, 1, newcred, p, &stateid, &lckp);
5557	if (error != 0) {
5558		NFSFREECRED(newcred);
5559		nfscl_relref(nmp);
5560		return (error);
5561	}
5562	/* Search for a layout for this file. */
5563	off = uiop->uio_offset;
5564	layp = nfscl_getlayout(nmp->nm_clp, np->n_fhp->nfh_fh,
5565	    np->n_fhp->nfh_len, off, &rflp, &recalled);
5566	if (layp == NULL || rflp == NULL) {
5567		if (recalled != 0) {
5568			NFSFREECRED(newcred);
5569			nfscl_relref(nmp);
5570			return (EIO);
5571		}
5572		if (layp != NULL) {
5573			nfscl_rellayout(layp, (rflp == NULL) ? 1 : 0);
5574			layp = NULL;
5575		}
5576		/* Try and get a Layout, if it is supported. */
5577		if (rwaccess == NFSV4OPEN_ACCESSWRITE ||
5578		    (np->n_flag & NWRITEOPENED) != 0)
5579			iolaymode = NFSLAYOUTIOMODE_RW;
5580		else
5581			iolaymode = NFSLAYOUTIOMODE_READ;
5582		error = nfsrpc_getlayout(nmp, vp, np->n_fhp, iolaymode,
5583		    NULL, &stateid, off, &layp, newcred, p);
5584		if (error != 0) {
5585			NFSLOCKNODE(np);
5586			np->n_flag |= NNOLAYOUT;
5587			NFSUNLOCKNODE(np);
5588			if (lckp != NULL)
5589				nfscl_lockderef(lckp);
5590			NFSFREECRED(newcred);
5591			if (layp != NULL)
5592				nfscl_rellayout(layp, 0);
5593			nfscl_relref(nmp);
5594			return (error);
5595		}
5596	}
5597
5598	/*
5599	 * Loop around finding a layout that works for the first part of
5600	 * this I/O operation, and then call the function that actually
5601	 * does the RPC.
5602	 */
5603	eof = 0;
5604	len = (uint64_t)uiop->uio_resid;
5605	while (len > 0 && error == 0 && eof == 0) {
5606		off = uiop->uio_offset;
5607		error = nfscl_findlayoutforio(layp, off, rwaccess, &rflp);
5608		if (error == 0) {
5609			oresid = xfer = (uint64_t)uiop->uio_resid;
5610			if (xfer > (rflp->nfsfl_end - rflp->nfsfl_off))
5611				xfer = rflp->nfsfl_end - rflp->nfsfl_off;
5612			dip = nfscl_getdevinfo(nmp->nm_clp, rflp->nfsfl_dev,
5613			    rflp->nfsfl_devp);
5614			if (dip != NULL) {
5615				error = nfscl_doflayoutio(vp, uiop, iomode,
5616				    must_commit, &eof, &stateid, rwaccess, dip,
5617				    layp, rflp, off, xfer, docommit, newcred,
5618				    p);
5619				nfscl_reldevinfo(dip);
5620				lastbyte = off + xfer - 1;
5621				if (error == 0) {
5622					NFSLOCKCLSTATE();
5623					if (lastbyte > layp->nfsly_lastbyte)
5624						layp->nfsly_lastbyte = lastbyte;
5625					NFSUNLOCKCLSTATE();
5626				} else if (error == NFSERR_OPENMODE &&
5627				    rwaccess == NFSV4OPEN_ACCESSREAD) {
5628					NFSLOCKMNT(nmp);
5629					nmp->nm_state |= NFSSTA_OPENMODE;
5630					NFSUNLOCKMNT(nmp);
5631				}
5632			} else
5633				error = EIO;
5634			if (error == 0)
5635				len -= (oresid - (uint64_t)uiop->uio_resid);
5636		}
5637	}
5638	if (lckp != NULL)
5639		nfscl_lockderef(lckp);
5640	NFSFREECRED(newcred);
5641	nfscl_rellayout(layp, 0);
5642	nfscl_relref(nmp);
5643	return (error);
5644}
5645
5646/*
5647 * Find a file layout that will handle the first bytes of the requested
5648 * range and return the information from it needed to to the I/O operation.
5649 */
5650int
5651nfscl_findlayoutforio(struct nfscllayout *lyp, uint64_t off, uint32_t rwaccess,
5652    struct nfsclflayout **retflpp)
5653{
5654	struct nfsclflayout *flp, *nflp, *rflp;
5655	uint32_t rw;
5656
5657	rflp = NULL;
5658	rw = rwaccess;
5659	/* For reading, do the Read list first and then the Write list. */
5660	do {
5661		if (rw == NFSV4OPEN_ACCESSREAD)
5662			flp = LIST_FIRST(&lyp->nfsly_flayread);
5663		else
5664			flp = LIST_FIRST(&lyp->nfsly_flayrw);
5665		while (flp != NULL) {
5666			nflp = LIST_NEXT(flp, nfsfl_list);
5667			if (flp->nfsfl_off > off)
5668				break;
5669			if (flp->nfsfl_end > off &&
5670			    (rflp == NULL || rflp->nfsfl_end < flp->nfsfl_end))
5671				rflp = flp;
5672			flp = nflp;
5673		}
5674		if (rw == NFSV4OPEN_ACCESSREAD)
5675			rw = NFSV4OPEN_ACCESSWRITE;
5676		else
5677			rw = 0;
5678	} while (rw != 0);
5679	if (rflp != NULL) {
5680		/* This one covers the most bytes starting at off. */
5681		*retflpp = rflp;
5682		return (0);
5683	}
5684	return (EIO);
5685}
5686
5687/*
5688 * Do I/O using an NFSv4.1 file layout.
5689 */
5690static int
5691nfscl_doflayoutio(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
5692    int *eofp, nfsv4stateid_t *stateidp, int rwflag, struct nfscldevinfo *dp,
5693    struct nfscllayout *lyp, struct nfsclflayout *flp, uint64_t off,
5694    uint64_t len, int docommit, struct ucred *cred, NFSPROC_T *p)
5695{
5696	uint64_t io_off, rel_off, stripe_unit_size, transfer, xfer;
5697	int commit_thru_mds, error, stripe_index, stripe_pos;
5698	struct nfsnode *np;
5699	struct nfsfh *fhp;
5700	struct nfsclds **dspp;
5701
5702	np = VTONFS(vp);
5703	rel_off = off - flp->nfsfl_patoff;
5704	stripe_unit_size = (flp->nfsfl_util >> 6) & 0x3ffffff;
5705	stripe_pos = (rel_off / stripe_unit_size + flp->nfsfl_stripe1) %
5706	    dp->nfsdi_stripecnt;
5707	transfer = stripe_unit_size - (rel_off % stripe_unit_size);
5708	error = 0;
5709
5710	/* Loop around, doing I/O for each stripe unit. */
5711	while (len > 0 && error == 0) {
5712		stripe_index = nfsfldi_stripeindex(dp, stripe_pos);
5713		dspp = nfsfldi_addr(dp, stripe_index);
5714		if (len > transfer && docommit == 0)
5715			xfer = transfer;
5716		else
5717			xfer = len;
5718		if ((flp->nfsfl_util & NFSFLAYUTIL_DENSE) != 0) {
5719			/* Dense layout. */
5720			if (stripe_pos >= flp->nfsfl_fhcnt)
5721				return (EIO);
5722			fhp = flp->nfsfl_fh[stripe_pos];
5723			io_off = (rel_off / (stripe_unit_size *
5724			    dp->nfsdi_stripecnt)) * stripe_unit_size +
5725			    rel_off % stripe_unit_size;
5726		} else {
5727			/* Sparse layout. */
5728			if (flp->nfsfl_fhcnt > 1) {
5729				if (stripe_index >= flp->nfsfl_fhcnt)
5730					return (EIO);
5731				fhp = flp->nfsfl_fh[stripe_index];
5732			} else if (flp->nfsfl_fhcnt == 1)
5733				fhp = flp->nfsfl_fh[0];
5734			else
5735				fhp = np->n_fhp;
5736			io_off = off;
5737		}
5738		if ((flp->nfsfl_util & NFSFLAYUTIL_COMMIT_THRU_MDS) != 0) {
5739			commit_thru_mds = 1;
5740			if (docommit != 0)
5741				error = EIO;
5742		} else {
5743			commit_thru_mds = 0;
5744			mtx_lock(&np->n_mtx);
5745			np->n_flag |= NDSCOMMIT;
5746			mtx_unlock(&np->n_mtx);
5747		}
5748		if (docommit != 0) {
5749			if (error == 0)
5750				error = nfsrpc_commitds(vp, io_off, xfer,
5751				    *dspp, fhp, cred, p);
5752			if (error == 0) {
5753				/*
5754				 * Set both eof and uio_resid = 0 to end any
5755				 * loops.
5756				 */
5757				*eofp = 1;
5758				uiop->uio_resid = 0;
5759			} else {
5760				mtx_lock(&np->n_mtx);
5761				np->n_flag &= ~NDSCOMMIT;
5762				mtx_unlock(&np->n_mtx);
5763			}
5764		} else if (rwflag == FREAD)
5765			error = nfsrpc_readds(vp, uiop, stateidp, eofp, *dspp,
5766			    io_off, xfer, fhp, cred, p);
5767		else {
5768			error = nfsrpc_writeds(vp, uiop, iomode, must_commit,
5769			    stateidp, *dspp, io_off, xfer, fhp, commit_thru_mds,
5770			    cred, p);
5771			if (error == 0) {
5772				NFSLOCKCLSTATE();
5773				lyp->nfsly_flags |= NFSLY_WRITTEN;
5774				NFSUNLOCKCLSTATE();
5775			}
5776		}
5777		if (error == 0) {
5778			transfer = stripe_unit_size;
5779			stripe_pos = (stripe_pos + 1) % dp->nfsdi_stripecnt;
5780			len -= xfer;
5781			off += xfer;
5782		}
5783	}
5784	return (error);
5785}
5786
5787/*
5788 * The actual read RPC done to a DS.
5789 */
5790static int
5791nfsrpc_readds(vnode_t vp, struct uio *uiop, nfsv4stateid_t *stateidp, int *eofp,
5792    struct nfsclds *dsp, uint64_t io_off, int len, struct nfsfh *fhp,
5793    struct ucred *cred, NFSPROC_T *p)
5794{
5795	uint32_t *tl;
5796	int error, retlen;
5797	struct nfsrv_descript nfsd;
5798	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5799	struct nfsrv_descript *nd = &nfsd;
5800	struct nfssockreq *nrp;
5801
5802	nd->nd_mrep = NULL;
5803	nfscl_reqstart(nd, NFSPROC_READDS, nmp, fhp->nfh_fh, fhp->nfh_len,
5804	    NULL, &dsp->nfsclds_sess);
5805	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSEQIDZERO);
5806	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED * 3);
5807	txdr_hyper(io_off, tl);
5808	*(tl + 2) = txdr_unsigned(len);
5809	nrp = dsp->nfsclds_sockp;
5810	if (nrp == NULL)
5811		/* If NULL, use the MDS socket. */
5812		nrp = &nmp->nm_sockreq;
5813	error = newnfs_request(nd, nmp, NULL, nrp, vp, p, cred,
5814	    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
5815	if (error != 0)
5816		return (error);
5817	if (nd->nd_repstat != 0) {
5818		error = nd->nd_repstat;
5819		goto nfsmout;
5820	}
5821	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5822	*eofp = fxdr_unsigned(int, *tl);
5823	NFSM_STRSIZ(retlen, len);
5824	error = nfsm_mbufuio(nd, uiop, retlen);
5825nfsmout:
5826	if (nd->nd_mrep != NULL)
5827		mbuf_freem(nd->nd_mrep);
5828	return (error);
5829}
5830
5831/*
5832 * The actual write RPC done to a DS.
5833 */
5834static int
5835nfsrpc_writeds(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
5836    nfsv4stateid_t *stateidp, struct nfsclds *dsp, uint64_t io_off, int len,
5837    struct nfsfh *fhp, int commit_thru_mds, struct ucred *cred, NFSPROC_T *p)
5838{
5839	uint32_t *tl;
5840	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5841	int error, rlen, commit, committed = NFSWRITE_FILESYNC;
5842	int32_t backup;
5843	struct nfsrv_descript nfsd;
5844	struct nfsrv_descript *nd = &nfsd;
5845	struct nfssockreq *nrp;
5846
5847	KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
5848	nd->nd_mrep = NULL;
5849	nfscl_reqstart(nd, NFSPROC_WRITEDS, nmp, fhp->nfh_fh, fhp->nfh_len,
5850	    NULL, &dsp->nfsclds_sess);
5851	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSEQIDZERO);
5852	NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
5853	txdr_hyper(io_off, tl);
5854	tl += 2;
5855	*tl++ = txdr_unsigned(*iomode);
5856	*tl = txdr_unsigned(len);
5857	nfsm_uiombuf(nd, uiop, len);
5858	nrp = dsp->nfsclds_sockp;
5859	if (nrp == NULL)
5860		/* If NULL, use the MDS socket. */
5861		nrp = &nmp->nm_sockreq;
5862	error = newnfs_request(nd, nmp, NULL, nrp, vp, p, cred,
5863	    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
5864	if (error != 0)
5865		return (error);
5866	if (nd->nd_repstat != 0) {
5867		/*
5868		 * In case the rpc gets retried, roll
5869		 * the uio fileds changed by nfsm_uiombuf()
5870		 * back.
5871		 */
5872		uiop->uio_offset -= len;
5873		uio_uio_resid_add(uiop, len);
5874		uio_iov_base_add(uiop, -len);
5875		uio_iov_len_add(uiop, len);
5876		error = nd->nd_repstat;
5877	} else {
5878		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + NFSX_VERF);
5879		rlen = fxdr_unsigned(int, *tl++);
5880		if (rlen == 0) {
5881			error = NFSERR_IO;
5882			goto nfsmout;
5883		} else if (rlen < len) {
5884			backup = len - rlen;
5885			uio_iov_base_add(uiop, -(backup));
5886			uio_iov_len_add(uiop, backup);
5887			uiop->uio_offset -= backup;
5888			uio_uio_resid_add(uiop, backup);
5889			len = rlen;
5890		}
5891		commit = fxdr_unsigned(int, *tl++);
5892
5893		/*
5894		 * Return the lowest committment level
5895		 * obtained by any of the RPCs.
5896		 */
5897		if (committed == NFSWRITE_FILESYNC)
5898			committed = commit;
5899		else if (committed == NFSWRITE_DATASYNC &&
5900		    commit == NFSWRITE_UNSTABLE)
5901			committed = commit;
5902		if (commit_thru_mds != 0) {
5903			NFSLOCKMNT(nmp);
5904			if (!NFSHASWRITEVERF(nmp)) {
5905				NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
5906				NFSSETWRITEVERF(nmp);
5907	    		} else if (NFSBCMP(tl, nmp->nm_verf, NFSX_VERF)) {
5908				*must_commit = 1;
5909				NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
5910			}
5911			NFSUNLOCKMNT(nmp);
5912		} else {
5913			NFSLOCKDS(dsp);
5914			if ((dsp->nfsclds_flags & NFSCLDS_HASWRITEVERF) == 0) {
5915				NFSBCOPY(tl, dsp->nfsclds_verf, NFSX_VERF);
5916				dsp->nfsclds_flags |= NFSCLDS_HASWRITEVERF;
5917			} else if (NFSBCMP(tl, dsp->nfsclds_verf, NFSX_VERF)) {
5918				*must_commit = 1;
5919				NFSBCOPY(tl, dsp->nfsclds_verf, NFSX_VERF);
5920			}
5921			NFSUNLOCKDS(dsp);
5922		}
5923	}
5924nfsmout:
5925	if (nd->nd_mrep != NULL)
5926		mbuf_freem(nd->nd_mrep);
5927	*iomode = committed;
5928	if (nd->nd_repstat != 0 && error == 0)
5929		error = nd->nd_repstat;
5930	return (error);
5931}
5932
5933/*
5934 * Free up the nfsclds structure.
5935 */
5936void
5937nfscl_freenfsclds(struct nfsclds *dsp)
5938{
5939	int i;
5940
5941	if (dsp == NULL)
5942		return;
5943	if (dsp->nfsclds_sockp != NULL) {
5944		NFSFREECRED(dsp->nfsclds_sockp->nr_cred);
5945		NFSFREEMUTEX(&dsp->nfsclds_sockp->nr_mtx);
5946		free(dsp->nfsclds_sockp->nr_nam, M_SONAME);
5947		free(dsp->nfsclds_sockp, M_NFSSOCKREQ);
5948	}
5949	NFSFREEMUTEX(&dsp->nfsclds_mtx);
5950	NFSFREEMUTEX(&dsp->nfsclds_sess.nfsess_mtx);
5951	for (i = 0; i < NFSV4_CBSLOTS; i++) {
5952		if (dsp->nfsclds_sess.nfsess_cbslots[i].nfssl_reply != NULL)
5953			m_freem(
5954			    dsp->nfsclds_sess.nfsess_cbslots[i].nfssl_reply);
5955	}
5956	free(dsp, M_NFSCLDS);
5957}
5958
5959static enum nfsclds_state
5960nfscl_getsameserver(struct nfsmount *nmp, struct nfsclds *newdsp,
5961    struct nfsclds **retdspp)
5962{
5963	struct nfsclds *dsp, *cur_dsp;
5964
5965	/*
5966	 * Search the list of nfsclds structures for one with the same
5967	 * server.
5968	 */
5969	cur_dsp = NULL;
5970	TAILQ_FOREACH(dsp, &nmp->nm_sess, nfsclds_list) {
5971		if (dsp->nfsclds_servownlen == newdsp->nfsclds_servownlen &&
5972		    dsp->nfsclds_servownlen != 0 &&
5973		    !NFSBCMP(dsp->nfsclds_serverown, newdsp->nfsclds_serverown,
5974		    dsp->nfsclds_servownlen) &&
5975		    dsp->nfsclds_sess.nfsess_defunct == 0) {
5976			NFSCL_DEBUG(4, "fnd same fdsp=%p dsp=%p flg=0x%x\n",
5977			    TAILQ_FIRST(&nmp->nm_sess), dsp,
5978			    dsp->nfsclds_flags);
5979			/* Server major id matches. */
5980			if ((dsp->nfsclds_flags & NFSCLDS_DS) != 0) {
5981				*retdspp = dsp;
5982				return (NFSDSP_USETHISSESSION);
5983			}
5984
5985			/*
5986			 * Note the first match, so it can be used for
5987			 * sequence'ing new sessions.
5988			 */
5989			if (cur_dsp == NULL)
5990				cur_dsp = dsp;
5991		}
5992	}
5993	if (cur_dsp != NULL) {
5994		*retdspp = cur_dsp;
5995		return (NFSDSP_SEQTHISSESSION);
5996	}
5997	return (NFSDSP_NOTFOUND);
5998}
5999
6000/*
6001 * NFS commit rpc to a NFSv4.1 DS.
6002 */
6003static int
6004nfsrpc_commitds(vnode_t vp, uint64_t offset, int cnt, struct nfsclds *dsp,
6005    struct nfsfh *fhp, struct ucred *cred, NFSPROC_T *p)
6006{
6007	uint32_t *tl;
6008	struct nfsrv_descript nfsd, *nd = &nfsd;
6009	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
6010	struct nfssockreq *nrp;
6011	int error;
6012
6013	nd->nd_mrep = NULL;
6014	nfscl_reqstart(nd, NFSPROC_COMMITDS, nmp, fhp->nfh_fh, fhp->nfh_len,
6015	    NULL, &dsp->nfsclds_sess);
6016	NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
6017	txdr_hyper(offset, tl);
6018	tl += 2;
6019	*tl = txdr_unsigned(cnt);
6020	nrp = dsp->nfsclds_sockp;
6021	if (nrp == NULL)
6022		/* If NULL, use the MDS socket. */
6023		nrp = &nmp->nm_sockreq;
6024	error = newnfs_request(nd, nmp, NULL, nrp, vp, p, cred,
6025	    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
6026	if (error != 0)
6027		return (error);
6028	if (nd->nd_repstat == 0) {
6029		NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
6030		NFSLOCKDS(dsp);
6031		if (NFSBCMP(tl, dsp->nfsclds_verf, NFSX_VERF)) {
6032			NFSBCOPY(tl, dsp->nfsclds_verf, NFSX_VERF);
6033			error = NFSERR_STALEWRITEVERF;
6034		}
6035		NFSUNLOCKDS(dsp);
6036	}
6037nfsmout:
6038	if (error == 0 && nd->nd_repstat != 0)
6039		error = nd->nd_repstat;
6040	mbuf_freem(nd->nd_mrep);
6041	return (error);
6042}
6043
6044