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