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