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