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