nfs_clrpcops.c revision 207170
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clrpcops.c 207170 2010-04-24 22:52:14Z rmacklem $");
36
37/*
38 * Rpc op calls, generally called from the vnode op calls or through the
39 * buffer cache, for NFS v2, 3 and 4.
40 * These do not normally make any changes to vnode arguments or use
41 * structures that might change between the VFS variants. The returned
42 * arguments are all at the end, after the NFSPROC_T *p one.
43 */
44
45#ifndef APPLEKEXT
46#include <fs/nfs/nfsport.h>
47
48/*
49 * Global variables
50 */
51extern int nfs_numnfscbd;
52extern struct timeval nfsboottime;
53extern u_int32_t newnfs_false, newnfs_true;
54extern nfstype nfsv34_type[9];
55extern int nfsrv_useacl;
56extern char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
57NFSCLSTATEMUTEX;
58int nfstest_outofseq = 0;
59int nfscl_assumeposixlocks = 1;
60int nfscl_enablecallb = 0;
61short nfsv4_cbport = NFSV4_CBPORT;
62int nfstest_openallsetattr = 0;
63#endif	/* !APPLEKEXT */
64
65#define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
66
67static int nfsrpc_setattrrpc(vnode_t , struct vattr *, nfsv4stateid_t *,
68    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
69static int nfsrpc_readrpc(vnode_t , struct uio *, struct ucred *,
70    nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *, void *);
71static int nfsrpc_writerpc(vnode_t , struct uio *, int *, u_char *,
72    struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *,
73    void *);
74static int nfsrpc_createv23(vnode_t , char *, int, struct vattr *,
75    nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *,
76    struct nfsvattr *, struct nfsfh **, int *, int *, void *);
77static int nfsrpc_createv4(vnode_t , char *, int, struct vattr *,
78    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **, struct ucred *,
79    NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *,
80    int *, void *, int *);
81static int nfsrpc_locku(struct nfsrv_descript *, struct nfsmount *,
82    struct nfscllockowner *, u_int64_t, u_int64_t,
83    u_int32_t, struct ucred *, NFSPROC_T *, int);
84static int nfsrpc_setaclrpc(vnode_t, struct ucred *, NFSPROC_T *,
85    struct acl *, nfsv4stateid_t *, void *);
86
87/*
88 * nfs null call from vfs.
89 */
90APPLESTATIC int
91nfsrpc_null(vnode_t vp, struct ucred *cred, NFSPROC_T *p)
92{
93	int error;
94	struct nfsrv_descript nfsd, *nd = &nfsd;
95
96	NFSCL_REQSTART(nd, NFSPROC_NULL, vp);
97	error = nfscl_request(nd, vp, p, cred, NULL);
98	if (nd->nd_repstat && !error)
99		error = nd->nd_repstat;
100	mbuf_freem(nd->nd_mrep);
101	return (error);
102}
103
104/*
105 * nfs access rpc op.
106 * For nfs version 3 and 4, use the access rpc to check accessibility. If file
107 * modes are changed on the server, accesses might still fail later.
108 */
109APPLESTATIC int
110nfsrpc_access(vnode_t vp, int acmode, struct ucred *cred,
111    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
112{
113	int error;
114	u_int32_t mode, rmode;
115
116	if (acmode & VREAD)
117		mode = NFSACCESS_READ;
118	else
119		mode = 0;
120	if (vnode_vtype(vp) == VDIR) {
121		if (acmode & VWRITE)
122			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND |
123				 NFSACCESS_DELETE);
124		if (acmode & VEXEC)
125			mode |= NFSACCESS_LOOKUP;
126	} else {
127		if (acmode & VWRITE)
128			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
129		if (acmode & VEXEC)
130			mode |= NFSACCESS_EXECUTE;
131	}
132
133	/*
134	 * Now, just call nfsrpc_accessrpc() to do the actual RPC.
135	 */
136	error = nfsrpc_accessrpc(vp, mode, cred, p, nap, attrflagp, &rmode,
137	    NULL);
138
139	/*
140	 * The NFS V3 spec does not clarify whether or not
141	 * the returned access bits can be a superset of
142	 * the ones requested, so...
143	 */
144	if (!error && (rmode & mode) != mode)
145		error = EACCES;
146	return (error);
147}
148
149/*
150 * The actual rpc, separated out for Darwin.
151 */
152APPLESTATIC int
153nfsrpc_accessrpc(vnode_t vp, u_int32_t mode, struct ucred *cred,
154    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, u_int32_t *rmodep,
155    void *stuff)
156{
157	u_int32_t *tl;
158	u_int32_t supported, rmode;
159	int error;
160	struct nfsrv_descript nfsd, *nd = &nfsd;
161	nfsattrbit_t attrbits;
162
163	*attrflagp = 0;
164	supported = mode;
165	NFSCL_REQSTART(nd, NFSPROC_ACCESS, vp);
166	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
167	*tl = txdr_unsigned(mode);
168	if (nd->nd_flag & ND_NFSV4) {
169		/*
170		 * And do a Getattr op.
171		 */
172		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
173		*tl = txdr_unsigned(NFSV4OP_GETATTR);
174		NFSGETATTR_ATTRBIT(&attrbits);
175		(void) nfsrv_putattrbit(nd, &attrbits);
176	}
177	error = nfscl_request(nd, vp, p, cred, stuff);
178	if (error)
179		return (error);
180	if (nd->nd_flag & ND_NFSV3) {
181		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
182		if (error)
183			goto nfsmout;
184	}
185	if (!nd->nd_repstat) {
186		if (nd->nd_flag & ND_NFSV4) {
187			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
188			supported = fxdr_unsigned(u_int32_t, *tl++);
189		} else {
190			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
191		}
192		rmode = fxdr_unsigned(u_int32_t, *tl);
193		if (nd->nd_flag & ND_NFSV4)
194			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
195
196		/*
197		 * It's not obvious what should be done about
198		 * unsupported access modes. For now, be paranoid
199		 * and clear the unsupported ones.
200		 */
201		rmode &= supported;
202		*rmodep = rmode;
203	} else
204		error = nd->nd_repstat;
205nfsmout:
206	mbuf_freem(nd->nd_mrep);
207	return (error);
208}
209
210/*
211 * nfs open rpc
212 */
213APPLESTATIC int
214nfsrpc_open(vnode_t vp, int amode, struct ucred *cred, NFSPROC_T *p)
215{
216	struct nfsclopen *op;
217	struct nfscldeleg *dp;
218	struct nfsfh *nfhp;
219	struct nfsnode *np = VTONFS(vp);
220	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
221	u_int32_t mode, clidrev;
222	int ret, newone, error, expireret = 0, retrycnt;
223
224	/*
225	 * For NFSv4, Open Ops are only done on Regular Files.
226	 */
227	if (vnode_vtype(vp) != VREG)
228		return (0);
229	mode = 0;
230	if (amode & FREAD)
231		mode |= NFSV4OPEN_ACCESSREAD;
232	if (amode & FWRITE)
233		mode |= NFSV4OPEN_ACCESSWRITE;
234	nfhp = np->n_fhp;
235
236	retrycnt = 0;
237#ifdef notdef
238{ char name[100]; int namel;
239namel = (np->n_v4->n4_namelen < 100) ? np->n_v4->n4_namelen : 99;
240bcopy(NFS4NODENAME(np->n_v4), name, namel);
241name[namel] = '\0';
242printf("rpcopen p=0x%x name=%s",p->p_pid,name);
243if (nfhp->nfh_len > 0) printf(" fh=0x%x\n",nfhp->nfh_fh[12]);
244else printf(" fhl=0\n");
245}
246#endif
247	do {
248	    dp = NULL;
249	    error = nfscl_open(vp, nfhp->nfh_fh, nfhp->nfh_len, mode, 1,
250		cred, p, NULL, &op, &newone, &ret, 1);
251	    if (error) {
252		return (error);
253	    }
254	    if (nmp->nm_clp != NULL)
255		clidrev = nmp->nm_clp->nfsc_clientidrev;
256	    else
257		clidrev = 0;
258	    if (ret == NFSCLOPEN_DOOPEN) {
259		if (np->n_v4 != NULL) {
260			error = nfsrpc_openrpc(nmp, vp, np->n_v4->n4_data,
261			   np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
262			   np->n_fhp->nfh_len, mode, op,
263			   NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &dp,
264			   0, 0x0, cred, p, 0, 0);
265			if (dp != NULL) {
266#ifdef APPLE
267				OSBitAndAtomic((int32_t)~NDELEGMOD, (UInt32 *)&np->n_flag);
268#else
269				NFSLOCKNODE(np);
270				np->n_flag &= ~NDELEGMOD;
271				NFSUNLOCKNODE(np);
272#endif
273				(void) nfscl_deleg(nmp->nm_mountp,
274				    op->nfso_own->nfsow_clp,
275				    nfhp->nfh_fh, nfhp->nfh_len, cred, p, &dp);
276			}
277		} else {
278			error = EIO;
279		}
280		newnfs_copyincred(cred, &op->nfso_cred);
281	    } else if (ret == NFSCLOPEN_SETCRED)
282		/*
283		 * This is a new local open on a delegation. It needs
284		 * to have credentials so that an open can be done
285		 * against the server during recovery.
286		 */
287		newnfs_copyincred(cred, &op->nfso_cred);
288
289	    /*
290	     * nfso_opencnt is the count of how many VOP_OPEN()s have
291	     * been done on this Open successfully and a VOP_CLOSE()
292	     * is expected for each of these.
293	     * If error is non-zero, don't increment it, since the Open
294	     * hasn't succeeded yet.
295	     */
296	    if (!error)
297		op->nfso_opencnt++;
298	    nfscl_openrelease(op, error, newone);
299	    if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
300		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY) {
301		(void) nfs_catnap(PZERO, error, "nfs_open");
302	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
303		&& clidrev != 0) {
304		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
305		retrycnt++;
306	    }
307	} while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
308	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
309	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
310	     expireret == 0 && clidrev != 0 && retrycnt < 4));
311	if (error && retrycnt >= 4)
312		error = EIO;
313	return (error);
314}
315
316/*
317 * the actual open rpc
318 */
319APPLESTATIC int
320nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
321    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
322    u_int8_t *name, int namelen, struct nfscldeleg **dpp,
323    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p,
324    int syscred, int recursed)
325{
326	u_int32_t *tl;
327	struct nfsrv_descript nfsd, *nd = &nfsd;
328	struct nfscldeleg *dp, *ndp = NULL;
329	struct nfsvattr nfsva;
330	u_int32_t rflags, deleg;
331	nfsattrbit_t attrbits;
332	int error, ret, acesize, limitby;
333
334	dp = *dpp;
335	*dpp = NULL;
336	nfscl_reqstart(nd, NFSPROC_OPEN, nmp, nfhp, fhlen, NULL);
337	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
338	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
339	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
340	*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
341	*tl++ = op->nfso_own->nfsow_clp->nfsc_clientid.lval[0];
342	*tl = op->nfso_own->nfsow_clp->nfsc_clientid.lval[1];
343	(void) nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
344	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
345	*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
346	if (reclaim) {
347		*tl = txdr_unsigned(NFSV4OPEN_CLAIMPREVIOUS);
348		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
349		*tl = txdr_unsigned(delegtype);
350	} else {
351		if (dp != NULL) {
352			*tl = txdr_unsigned(NFSV4OPEN_CLAIMDELEGATECUR);
353			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
354			*tl++ = dp->nfsdl_stateid.seqid;
355			*tl++ = dp->nfsdl_stateid.other[0];
356			*tl++ = dp->nfsdl_stateid.other[1];
357			*tl = dp->nfsdl_stateid.other[2];
358		} else {
359			*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
360		}
361		(void) nfsm_strtom(nd, name, namelen);
362	}
363	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
364	*tl = txdr_unsigned(NFSV4OP_GETATTR);
365	NFSZERO_ATTRBIT(&attrbits);
366	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
367	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY);
368	(void) nfsrv_putattrbit(nd, &attrbits);
369	if (syscred)
370		nd->nd_flag |= ND_USEGSSNAME;
371	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
372	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
373	if (error)
374		return (error);
375	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
376	if (!nd->nd_repstat) {
377		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
378		    6 * NFSX_UNSIGNED);
379		op->nfso_stateid.seqid = *tl++;
380		op->nfso_stateid.other[0] = *tl++;
381		op->nfso_stateid.other[1] = *tl++;
382		op->nfso_stateid.other[2] = *tl;
383		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
384		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
385		if (error)
386			goto nfsmout;
387		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
388		deleg = fxdr_unsigned(u_int32_t, *tl);
389		if (deleg == NFSV4OPEN_DELEGATEREAD ||
390		    deleg == NFSV4OPEN_DELEGATEWRITE) {
391			if (!(op->nfso_own->nfsow_clp->nfsc_flags &
392			      NFSCLFLAGS_FIRSTDELEG))
393				op->nfso_own->nfsow_clp->nfsc_flags |=
394				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
395			MALLOC(ndp, struct nfscldeleg *,
396			    sizeof (struct nfscldeleg) + newfhlen,
397			    M_NFSCLDELEG, M_WAITOK);
398			LIST_INIT(&ndp->nfsdl_owner);
399			LIST_INIT(&ndp->nfsdl_lock);
400			ndp->nfsdl_clp = op->nfso_own->nfsow_clp;
401			ndp->nfsdl_fhlen = newfhlen;
402			NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen);
403			newnfs_copyincred(cred, &ndp->nfsdl_cred);
404			nfscl_lockinit(&ndp->nfsdl_rwlock);
405			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
406			    NFSX_UNSIGNED);
407			ndp->nfsdl_stateid.seqid = *tl++;
408			ndp->nfsdl_stateid.other[0] = *tl++;
409			ndp->nfsdl_stateid.other[1] = *tl++;
410			ndp->nfsdl_stateid.other[2] = *tl++;
411			ret = fxdr_unsigned(int, *tl);
412			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
413				ndp->nfsdl_flags = NFSCLDL_WRITE;
414				/*
415				 * Indicates how much the file can grow.
416				 */
417				NFSM_DISSECT(tl, u_int32_t *,
418				    3 * NFSX_UNSIGNED);
419				limitby = fxdr_unsigned(int, *tl++);
420				switch (limitby) {
421				case NFSV4OPEN_LIMITSIZE:
422					ndp->nfsdl_sizelimit = fxdr_hyper(tl);
423					break;
424				case NFSV4OPEN_LIMITBLOCKS:
425					ndp->nfsdl_sizelimit =
426					    fxdr_unsigned(u_int64_t, *tl++);
427					ndp->nfsdl_sizelimit *=
428					    fxdr_unsigned(u_int64_t, *tl);
429					break;
430				default:
431					error = NFSERR_BADXDR;
432					goto nfsmout;
433				};
434			} else {
435				ndp->nfsdl_flags = NFSCLDL_READ;
436			}
437			if (ret)
438				ndp->nfsdl_flags |= NFSCLDL_RECALL;
439			error = nfsrv_dissectace(nd, &ndp->nfsdl_ace, &ret,
440			    &acesize, p);
441			if (error)
442				goto nfsmout;
443		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
444			error = NFSERR_BADXDR;
445			goto nfsmout;
446		}
447		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
448		error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
449		    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
450		    NULL, NULL, NULL, p, cred);
451		if (error)
452			goto nfsmout;
453		if (ndp != NULL) {
454			ndp->nfsdl_change = nfsva.na_filerev;
455			ndp->nfsdl_modtime = nfsva.na_mtime;
456			ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
457		}
458		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM)) {
459		    do {
460			ret = nfsrpc_openconfirm(vp, newfhp, newfhlen, op,
461			    cred, p);
462			if (ret == NFSERR_DELAY)
463			    (void) nfs_catnap(PZERO, ret, "nfs_open");
464		    } while (ret == NFSERR_DELAY);
465		    error = ret;
466		}
467		if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) ||
468		    nfscl_assumeposixlocks)
469		    op->nfso_posixlock = 1;
470		else
471		    op->nfso_posixlock = 0;
472
473		/*
474		 * If the server is handing out delegations, but we didn't
475		 * get one because an OpenConfirm was required, try the
476		 * Open again, to get a delegation. This is a harmless no-op,
477		 * from a server's point of view.
478		 */
479		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM) &&
480		    (op->nfso_own->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG)
481		    && !error && dp == NULL && ndp == NULL && !recursed) {
482		    do {
483			ret = nfsrpc_openrpc(nmp, vp, nfhp, fhlen, newfhp,
484			    newfhlen, mode, op, name, namelen, &ndp, 0, 0x0,
485			    cred, p, syscred, 1);
486			if (ret == NFSERR_DELAY)
487			    (void) nfs_catnap(PZERO, ret, "nfs_open2");
488		    } while (ret == NFSERR_DELAY);
489		    if (ret) {
490			if (ndp != NULL)
491				FREE((caddr_t)ndp, M_NFSCLDELEG);
492			if (ret == NFSERR_STALECLIENTID ||
493			    ret == NFSERR_STALEDONTRECOVER)
494				error = ret;
495		    }
496		}
497	}
498	if (nd->nd_repstat != 0 && error == 0)
499		error = nd->nd_repstat;
500	if (error == NFSERR_STALECLIENTID)
501		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
502nfsmout:
503	if (!error)
504		*dpp = ndp;
505	else if (ndp != NULL)
506		FREE((caddr_t)ndp, M_NFSCLDELEG);
507	mbuf_freem(nd->nd_mrep);
508	return (error);
509}
510
511/*
512 * open downgrade rpc
513 */
514APPLESTATIC int
515nfsrpc_opendowngrade(vnode_t vp, u_int32_t mode, struct nfsclopen *op,
516    struct ucred *cred, NFSPROC_T *p)
517{
518	u_int32_t *tl;
519	struct nfsrv_descript nfsd, *nd = &nfsd;
520	int error;
521
522	NFSCL_REQSTART(nd, NFSPROC_OPENDOWNGRADE, vp);
523	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
524	*tl++ = op->nfso_stateid.seqid;
525	*tl++ = op->nfso_stateid.other[0];
526	*tl++ = op->nfso_stateid.other[1];
527	*tl++ = op->nfso_stateid.other[2];
528	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
529	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
530	*tl = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
531	error = nfscl_request(nd, vp, p, cred, NULL);
532	if (error)
533		return (error);
534	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
535	if (!nd->nd_repstat) {
536		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
537		op->nfso_stateid.seqid = *tl++;
538		op->nfso_stateid.other[0] = *tl++;
539		op->nfso_stateid.other[1] = *tl++;
540		op->nfso_stateid.other[2] = *tl;
541	}
542	if (nd->nd_repstat && error == 0)
543		error = nd->nd_repstat;
544	if (error == NFSERR_STALESTATEID)
545		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
546nfsmout:
547	mbuf_freem(nd->nd_mrep);
548	return (error);
549}
550
551/*
552 * V4 Close operation.
553 */
554APPLESTATIC int
555nfsrpc_close(vnode_t vp, int doclose, NFSPROC_T *p)
556{
557	struct nfsclclient *clp;
558	int error;
559
560	if (vnode_vtype(vp) != VREG)
561		return (0);
562	if (doclose)
563		error = nfscl_doclose(vp, &clp, p);
564	else
565		error = nfscl_getclose(vp, &clp);
566	if (error)
567		return (error);
568
569	nfscl_clientrelease(clp);
570	return (0);
571}
572
573/*
574 * Close the open.
575 */
576APPLESTATIC void
577nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p)
578{
579	struct nfsrv_descript nfsd, *nd = &nfsd;
580	struct nfscllockowner *lp;
581	struct nfscllock *lop, *nlop;
582	struct ucred *tcred;
583	u_int64_t off = 0, len = 0;
584	u_int32_t type = NFSV4LOCKT_READ;
585	int error, do_unlock, trycnt;
586
587	tcred = newnfs_getcred();
588	newnfs_copycred(&op->nfso_cred, tcred);
589	/*
590	 * (Theoretically this could be done in the same
591	 *  compound as the close, but having multiple
592	 *  sequenced Ops in the same compound might be
593	 *  too scary for some servers.)
594	 */
595	if (op->nfso_posixlock) {
596		off = 0;
597		len = NFS64BITSSET;
598		type = NFSV4LOCKT_READ;
599	}
600
601	/*
602	 * Since this function is only called from VOP_INACTIVE(), no
603	 * other thread will be manipulating this Open. As such, the
604	 * lock lists are not being changed by other threads, so it should
605	 * be safe to do this without locking.
606	 */
607	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
608		do_unlock = 1;
609		LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
610			if (op->nfso_posixlock == 0) {
611				off = lop->nfslo_first;
612				len = lop->nfslo_end - lop->nfslo_first;
613				if (lop->nfslo_type == F_WRLCK)
614					type = NFSV4LOCKT_WRITE;
615				else
616					type = NFSV4LOCKT_READ;
617			}
618			if (do_unlock) {
619				trycnt = 0;
620				do {
621					error = nfsrpc_locku(nd, nmp, lp, off,
622					    len, type, tcred, p, 0);
623					if ((nd->nd_repstat == NFSERR_GRACE ||
624					    nd->nd_repstat == NFSERR_DELAY) &&
625					    error == 0)
626						(void) nfs_catnap(PZERO,
627						    (int)nd->nd_repstat,
628						    "nfs_close");
629				} while ((nd->nd_repstat == NFSERR_GRACE ||
630				    nd->nd_repstat == NFSERR_DELAY) &&
631				    error == 0 && trycnt++ < 5);
632				if (op->nfso_posixlock)
633					do_unlock = 0;
634			}
635			nfscl_freelock(lop, 0);
636		}
637	}
638
639	/*
640	 * There could be other Opens for different files on the same
641	 * OpenOwner, so locking is required.
642	 */
643	NFSLOCKCLSTATE();
644	nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
645	NFSUNLOCKCLSTATE();
646	do {
647		error = nfscl_tryclose(op, tcred, nmp, p);
648		if (error == NFSERR_GRACE)
649			(void) nfs_catnap(PZERO, error, "nfs_close");
650	} while (error == NFSERR_GRACE);
651	NFSLOCKCLSTATE();
652	nfscl_lockunlock(&op->nfso_own->nfsow_rwlock);
653
654	/*
655	 * Move the lockowner to nfsc_defunctlockowner,
656	 * so the Renew thread will do the ReleaseLockOwner
657	 * Op on it later. There might still be other
658	 * opens using the same lockowner name.
659	 */
660	lp = LIST_FIRST(&op->nfso_lock);
661	if (lp != NULL) {
662		while (LIST_NEXT(lp, nfsl_list) != NULL)
663			lp = LIST_NEXT(lp, nfsl_list);
664		LIST_PREPEND(&nmp->nm_clp->nfsc_defunctlockowner,
665		    &op->nfso_lock, lp, nfsl_list);
666		LIST_INIT(&op->nfso_lock);
667	}
668	nfscl_freeopen(op, 0);
669	NFSUNLOCKCLSTATE();
670	NFSFREECRED(tcred);
671}
672
673/*
674 * The actual Close RPC.
675 */
676APPLESTATIC int
677nfsrpc_closerpc(struct nfsrv_descript *nd, struct nfsmount *nmp,
678    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p,
679    int syscred)
680{
681	u_int32_t *tl;
682	int error;
683
684	nfscl_reqstart(nd, NFSPROC_CLOSE, nmp, op->nfso_fh,
685	    op->nfso_fhlen, NULL);
686	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
687	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
688	*tl++ = op->nfso_stateid.seqid;
689	*tl++ = op->nfso_stateid.other[0];
690	*tl++ = op->nfso_stateid.other[1];
691	*tl = op->nfso_stateid.other[2];
692	if (syscred)
693		nd->nd_flag |= ND_USEGSSNAME;
694	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
695	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
696	if (error)
697		return (error);
698	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
699	if (nd->nd_repstat == 0)
700		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
701	error = nd->nd_repstat;
702	if (error == NFSERR_STALESTATEID)
703		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
704nfsmout:
705	mbuf_freem(nd->nd_mrep);
706	return (error);
707}
708
709/*
710 * V4 Open Confirm RPC.
711 */
712APPLESTATIC int
713nfsrpc_openconfirm(vnode_t vp, u_int8_t *nfhp, int fhlen,
714    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p)
715{
716	u_int32_t *tl;
717	struct nfsrv_descript nfsd, *nd = &nfsd;
718	int error;
719
720	nfscl_reqstart(nd, NFSPROC_OPENCONFIRM, VFSTONFS(vnode_mount(vp)),
721	    nfhp, fhlen, NULL);
722	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
723	*tl++ = op->nfso_stateid.seqid;
724	*tl++ = op->nfso_stateid.other[0];
725	*tl++ = op->nfso_stateid.other[1];
726	*tl++ = op->nfso_stateid.other[2];
727	*tl = txdr_unsigned(op->nfso_own->nfsow_seqid);
728	error = nfscl_request(nd, vp, p, cred, NULL);
729	if (error)
730		return (error);
731	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
732	if (!nd->nd_repstat) {
733		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
734		op->nfso_stateid.seqid = *tl++;
735		op->nfso_stateid.other[0] = *tl++;
736		op->nfso_stateid.other[1] = *tl++;
737		op->nfso_stateid.other[2] = *tl;
738	}
739	error = nd->nd_repstat;
740	if (error == NFSERR_STALESTATEID)
741		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
742nfsmout:
743	mbuf_freem(nd->nd_mrep);
744	return (error);
745}
746
747/*
748 * Do the setclientid and setclientid confirm RPCs. Called from nfs_statfs()
749 * when a mount has just occurred and when the server replies NFSERR_EXPIRED.
750 */
751APPLESTATIC int
752nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp,
753    struct ucred *cred, NFSPROC_T *p)
754{
755	u_int32_t *tl;
756	struct nfsrv_descript nfsd;
757	struct nfsrv_descript *nd = &nfsd;
758	nfsattrbit_t attrbits;
759	u_int8_t *cp = NULL, *cp2, addr[INET6_ADDRSTRLEN + 9];
760	u_short port;
761	int error, isinet6 = 0, callblen;
762	nfsquad_t confirm;
763	u_int32_t lease;
764	static u_int32_t rev = 0;
765
766	if (nfsboottime.tv_sec == 0)
767		NFSSETBOOTTIME(nfsboottime);
768	nfscl_reqstart(nd, NFSPROC_SETCLIENTID, nmp, NULL, 0, NULL);
769	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
770	*tl++ = txdr_unsigned(nfsboottime.tv_sec);
771	*tl = txdr_unsigned(rev++);
772	(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
773
774	/*
775	 * set up the callback address
776	 */
777	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
778	*tl = txdr_unsigned(NFS_CALLBCKPROG);
779	callblen = strlen(nfsv4_callbackaddr);
780	if (callblen == 0)
781		cp = nfscl_getmyip(nmp, &isinet6);
782	if (nfscl_enablecallb && nfs_numnfscbd > 0 &&
783	    (callblen > 0 || cp != NULL)) {
784		port = htons(nfsv4_cbport);
785		cp2 = (u_int8_t *)&port;
786#ifdef INET6
787		if ((callblen > 0 &&
788		     strchr(nfsv4_callbackaddr, ':')) || isinet6) {
789			char ip6buf[INET6_ADDRSTRLEN], *ip6add;
790
791			(void) nfsm_strtom(nd, "tcp6", 4);
792			if (callblen == 0) {
793				ip6_sprintf(ip6buf, (struct in6_addr *)cp);
794				ip6add = ip6buf;
795			} else {
796				ip6add = nfsv4_callbackaddr;
797			}
798			snprintf(addr, INET6_ADDRSTRLEN + 9, "%s.%d.%d",
799			    ip6add, cp2[0], cp2[1]);
800		} else
801#endif
802		{
803			(void) nfsm_strtom(nd, "tcp", 3);
804			if (callblen == 0)
805				snprintf(addr, INET6_ADDRSTRLEN + 9,
806				    "%d.%d.%d.%d.%d.%d", cp[0], cp[1],
807				    cp[2], cp[3], cp2[0], cp2[1]);
808			else
809				snprintf(addr, INET6_ADDRSTRLEN + 9,
810				    "%s.%d.%d", nfsv4_callbackaddr,
811				    cp2[0], cp2[1]);
812		}
813		(void) nfsm_strtom(nd, addr, strlen(addr));
814	} else {
815		(void) nfsm_strtom(nd, "tcp", 3);
816		(void) nfsm_strtom(nd, "0.0.0.0.0.0", 11);
817	}
818	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
819	*tl = txdr_unsigned(clp->nfsc_cbident);
820	nd->nd_flag |= ND_USEGSSNAME;
821	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
822		NFS_PROG, NFS_VER4, NULL, 1, NULL);
823	if (error)
824		return (error);
825	if (nd->nd_repstat == 0) {
826	    NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
827	    clp->nfsc_clientid.lval[0] = *tl++;
828	    clp->nfsc_clientid.lval[1] = *tl++;
829	    confirm.lval[0] = *tl++;
830	    confirm.lval[1] = *tl;
831	    mbuf_freem(nd->nd_mrep);
832	    nd->nd_mrep = NULL;
833
834	    /*
835	     * and confirm it.
836	     */
837	    nfscl_reqstart(nd, NFSPROC_SETCLIENTIDCFRM, nmp, NULL, 0, NULL);
838	    NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
839	    *tl++ = clp->nfsc_clientid.lval[0];
840	    *tl++ = clp->nfsc_clientid.lval[1];
841	    *tl++ = confirm.lval[0];
842	    *tl = confirm.lval[1];
843	    nd->nd_flag |= ND_USEGSSNAME;
844	    error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
845		cred, NFS_PROG, NFS_VER4, NULL, 1, NULL);
846	    if (error)
847		return (error);
848	    mbuf_freem(nd->nd_mrep);
849	    nd->nd_mrep = NULL;
850	    if (nd->nd_repstat == 0) {
851		nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, nmp->nm_fh,
852		    nmp->nm_fhsize, NULL);
853		NFSZERO_ATTRBIT(&attrbits);
854		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_LEASETIME);
855		(void) nfsrv_putattrbit(nd, &attrbits);
856		nd->nd_flag |= ND_USEGSSNAME;
857		error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
858		    cred, NFS_PROG, NFS_VER4, NULL, 1, NULL);
859		if (error)
860		    return (error);
861		if (nd->nd_repstat == 0) {
862		    error = nfsv4_loadattr(nd, NULL, NULL, NULL, NULL, 0, NULL,
863			NULL, NULL, NULL, NULL, 0, NULL, &lease, NULL, p, cred);
864		    if (error)
865			goto nfsmout;
866		    clp->nfsc_renew = NFSCL_RENEW(lease);
867		    clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
868		    clp->nfsc_clientidrev++;
869		    if (clp->nfsc_clientidrev == 0)
870			clp->nfsc_clientidrev++;
871		}
872	    }
873	}
874	error = nd->nd_repstat;
875nfsmout:
876	mbuf_freem(nd->nd_mrep);
877	return (error);
878}
879
880/*
881 * nfs getattr call.
882 */
883APPLESTATIC int
884nfsrpc_getattr(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
885    struct nfsvattr *nap, void *stuff)
886{
887	struct nfsrv_descript nfsd, *nd = &nfsd;
888	int error;
889	nfsattrbit_t attrbits;
890
891	NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
892	if (nd->nd_flag & ND_NFSV4) {
893		NFSGETATTR_ATTRBIT(&attrbits);
894		(void) nfsrv_putattrbit(nd, &attrbits);
895	}
896	error = nfscl_request(nd, vp, p, cred, stuff);
897	if (error)
898		return (error);
899	if (!nd->nd_repstat)
900		error = nfsm_loadattr(nd, nap);
901	else
902		error = nd->nd_repstat;
903	mbuf_freem(nd->nd_mrep);
904	return (error);
905}
906
907/*
908 * nfs getattr call with non-vnode arguemnts.
909 */
910APPLESTATIC int
911nfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred,
912    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, u_int64_t *xidp)
913{
914	struct nfsrv_descript nfsd, *nd = &nfsd;
915	int error, vers = NFS_VER2;
916	nfsattrbit_t attrbits;
917
918	nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, fhp, fhlen, NULL);
919	if (nd->nd_flag & ND_NFSV4) {
920		vers = NFS_VER4;
921		NFSGETATTR_ATTRBIT(&attrbits);
922		(void) nfsrv_putattrbit(nd, &attrbits);
923	} else if (nd->nd_flag & ND_NFSV3) {
924		vers = NFS_VER3;
925	}
926	if (syscred)
927		nd->nd_flag |= ND_USEGSSNAME;
928	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
929	    NFS_PROG, vers, NULL, 1, xidp);
930	if (error)
931		return (error);
932	if (!nd->nd_repstat)
933		error = nfsm_loadattr(nd, nap);
934	else
935		error = nd->nd_repstat;
936	mbuf_freem(nd->nd_mrep);
937	return (error);
938}
939
940/*
941 * Do an nfs setattr operation.
942 */
943APPLESTATIC int
944nfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
945    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *rnap, int *attrflagp,
946    void *stuff)
947{
948	int error, expireret = 0, openerr, retrycnt;
949	u_int32_t clidrev = 0, mode;
950	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
951	struct nfsfh *nfhp;
952	nfsv4stateid_t stateid;
953	void *lckp;
954
955	if (nmp->nm_clp != NULL)
956		clidrev = nmp->nm_clp->nfsc_clientidrev;
957	if (vap != NULL && NFSATTRISSET(u_quad_t, vap, va_size))
958		mode = NFSV4OPEN_ACCESSWRITE;
959	else
960		mode = NFSV4OPEN_ACCESSREAD;
961	retrycnt = 0;
962	do {
963		lckp = NULL;
964		openerr = 1;
965		if (NFSHASNFSV4(nmp)) {
966			nfhp = VTONFS(vp)->n_fhp;
967			error = nfscl_getstateid(vp, nfhp->nfh_fh,
968			    nfhp->nfh_len, mode, cred, p, &stateid, &lckp);
969			if (error && vnode_vtype(vp) == VREG &&
970			    (mode == NFSV4OPEN_ACCESSWRITE ||
971			     nfstest_openallsetattr)) {
972				/*
973				 * No Open stateid, so try and open the file
974				 * now.
975				 */
976				if (mode == NFSV4OPEN_ACCESSWRITE)
977					openerr = nfsrpc_open(vp, FWRITE, cred,
978					    p);
979				else
980					openerr = nfsrpc_open(vp, FREAD, cred,
981					    p);
982				if (!openerr)
983					(void) nfscl_getstateid(vp,
984					    nfhp->nfh_fh, nfhp->nfh_len,
985					    mode, cred, p, &stateid, &lckp);
986			}
987		}
988		if (vap != NULL)
989			error = nfsrpc_setattrrpc(vp, vap, &stateid, cred, p,
990			    rnap, attrflagp, stuff);
991		else
992			error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
993			    stuff);
994		if (error == NFSERR_STALESTATEID)
995			nfscl_initiate_recovery(nmp->nm_clp);
996		if (lckp != NULL)
997			nfscl_lockderef(lckp);
998		if (!openerr)
999			(void) nfsrpc_close(vp, 0, p);
1000		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1001		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1002		    error == NFSERR_OLDSTATEID) {
1003			(void) nfs_catnap(PZERO, error, "nfs_setattr");
1004		} else if ((error == NFSERR_EXPIRED ||
1005		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1006			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1007		}
1008		retrycnt++;
1009	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1010	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1011	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1012	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1013	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1014	if (error && retrycnt >= 4)
1015		error = EIO;
1016	return (error);
1017}
1018
1019static int
1020nfsrpc_setattrrpc(vnode_t vp, struct vattr *vap,
1021    nfsv4stateid_t *stateidp, struct ucred *cred, NFSPROC_T *p,
1022    struct nfsvattr *rnap, int *attrflagp, void *stuff)
1023{
1024	u_int32_t *tl;
1025	struct nfsrv_descript nfsd, *nd = &nfsd;
1026	int error;
1027	nfsattrbit_t attrbits;
1028
1029	*attrflagp = 0;
1030	NFSCL_REQSTART(nd, NFSPROC_SETATTR, vp);
1031	if (nd->nd_flag & ND_NFSV4)
1032		nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1033	vap->va_type = vnode_vtype(vp);
1034	nfscl_fillsattr(nd, vap, vp, NFSSATTR_FULL, 0);
1035	if (nd->nd_flag & ND_NFSV3) {
1036		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1037		*tl = newnfs_false;
1038	} else if (nd->nd_flag & ND_NFSV4) {
1039		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1040		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1041		NFSGETATTR_ATTRBIT(&attrbits);
1042		(void) nfsrv_putattrbit(nd, &attrbits);
1043	}
1044	error = nfscl_request(nd, vp, p, cred, stuff);
1045	if (error)
1046		return (error);
1047	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1048		error = nfscl_wcc_data(nd, vp, rnap, attrflagp, NULL, stuff);
1049	if ((nd->nd_flag & ND_NFSV4) && !error)
1050		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1051	if (!(nd->nd_flag & ND_NFSV3) && !nd->nd_repstat && !error)
1052		error = nfscl_postop_attr(nd, rnap, attrflagp, stuff);
1053	mbuf_freem(nd->nd_mrep);
1054	if (nd->nd_repstat && !error)
1055		error = nd->nd_repstat;
1056	return (error);
1057}
1058
1059/*
1060 * nfs lookup rpc
1061 */
1062APPLESTATIC int
1063nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
1064    NFSPROC_T *p, struct nfsvattr *dnap, struct nfsvattr *nap,
1065    struct nfsfh **nfhpp, int *attrflagp, int *dattrflagp, void *stuff)
1066{
1067	u_int32_t *tl;
1068	struct nfsrv_descript nfsd, *nd = &nfsd;
1069	struct nfsmount *nmp;
1070	struct nfsnode *np;
1071	struct nfsfh *nfhp;
1072	nfsattrbit_t attrbits;
1073	int error = 0, lookupp = 0;
1074
1075	*attrflagp = 0;
1076	*dattrflagp = 0;
1077	if (vnode_vtype(dvp) != VDIR)
1078		return (ENOTDIR);
1079	nmp = VFSTONFS(vnode_mount(dvp));
1080	if (len > NFS_MAXNAMLEN)
1081		return (ENAMETOOLONG);
1082	if (NFSHASNFSV4(nmp) && len == 1 &&
1083		name[0] == '.') {
1084		/*
1085		 * Just return the current dir's fh.
1086		 */
1087		np = VTONFS(dvp);
1088		MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1089			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1090		nfhp->nfh_len = np->n_fhp->nfh_len;
1091		NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1092		*nfhpp = nfhp;
1093		return (0);
1094	}
1095	if (NFSHASNFSV4(nmp) && len == 2 &&
1096		name[0] == '.' && name[1] == '.') {
1097		lookupp = 1;
1098		NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, dvp);
1099	} else {
1100		NFSCL_REQSTART(nd, NFSPROC_LOOKUP, dvp);
1101		(void) nfsm_strtom(nd, name, len);
1102	}
1103	if (nd->nd_flag & ND_NFSV4) {
1104		NFSGETATTR_ATTRBIT(&attrbits);
1105		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1106		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1107		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1108		(void) nfsrv_putattrbit(nd, &attrbits);
1109	}
1110	error = nfscl_request(nd, dvp, p, cred, stuff);
1111	if (error)
1112		return (error);
1113	if (nd->nd_repstat) {
1114		/*
1115		 * When an NFSv4 Lookupp returns ENOENT, it means that
1116		 * the lookup is at the root of an fs, so return this dir.
1117		 */
1118		if (nd->nd_repstat == NFSERR_NOENT && lookupp) {
1119		    np = VTONFS(dvp);
1120		    MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1121			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1122		    nfhp->nfh_len = np->n_fhp->nfh_len;
1123		    NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1124		    *nfhpp = nfhp;
1125		    mbuf_freem(nd->nd_mrep);
1126		    return (0);
1127		}
1128		if (nd->nd_flag & ND_NFSV3)
1129		    error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1130		goto nfsmout;
1131	}
1132	if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
1133		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1134		if (*(tl + 1)) {
1135			nd->nd_flag |= ND_NOMOREDATA;
1136			goto nfsmout;
1137		}
1138	}
1139	error = nfsm_getfh(nd, nfhpp);
1140	if (error)
1141		goto nfsmout;
1142
1143	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1144	if ((nd->nd_flag & ND_NFSV3) && !error)
1145		error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1146nfsmout:
1147	mbuf_freem(nd->nd_mrep);
1148	if (!error && nd->nd_repstat)
1149		error = nd->nd_repstat;
1150	return (error);
1151}
1152
1153/*
1154 * Do a readlink rpc.
1155 */
1156APPLESTATIC int
1157nfsrpc_readlink(vnode_t vp, struct uio *uiop, struct ucred *cred,
1158    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1159{
1160	u_int32_t *tl;
1161	struct nfsrv_descript nfsd, *nd = &nfsd;
1162	struct nfsnode *np = VTONFS(vp);
1163	nfsattrbit_t attrbits;
1164	int error, len, cangetattr = 1;
1165
1166	*attrflagp = 0;
1167	NFSCL_REQSTART(nd, NFSPROC_READLINK, vp);
1168	if (nd->nd_flag & ND_NFSV4) {
1169		/*
1170		 * And do a Getattr op.
1171		 */
1172		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1173		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1174		NFSGETATTR_ATTRBIT(&attrbits);
1175		(void) nfsrv_putattrbit(nd, &attrbits);
1176	}
1177	error = nfscl_request(nd, vp, p, cred, stuff);
1178	if (error)
1179		return (error);
1180	if (nd->nd_flag & ND_NFSV3)
1181		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1182	if (!nd->nd_repstat && !error) {
1183		NFSM_STRSIZ(len, NFS_MAXPATHLEN);
1184		/*
1185		 * This seems weird to me, but must have been added to
1186		 * FreeBSD for some reason. The only thing I can think of
1187		 * is that there was/is some server that replies with
1188		 * more link data than it should?
1189		 */
1190		if (len == NFS_MAXPATHLEN) {
1191			NFSLOCKNODE(np);
1192			if (np->n_size > 0 && np->n_size < NFS_MAXPATHLEN) {
1193				len = np->n_size;
1194				cangetattr = 0;
1195			}
1196			NFSUNLOCKNODE(np);
1197		}
1198		error = nfsm_mbufuio(nd, uiop, len);
1199		if ((nd->nd_flag & ND_NFSV4) && !error && cangetattr)
1200			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1201	}
1202	if (nd->nd_repstat && !error)
1203		error = nd->nd_repstat;
1204nfsmout:
1205	mbuf_freem(nd->nd_mrep);
1206	return (error);
1207}
1208
1209/*
1210 * Read operation.
1211 */
1212APPLESTATIC int
1213nfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
1214    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1215{
1216	int error, expireret = 0, retrycnt;
1217	u_int32_t clidrev = 0;
1218	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1219	struct nfsnode *np = VTONFS(vp);
1220	struct ucred *newcred;
1221	struct nfsfh *nfhp = NULL;
1222	nfsv4stateid_t stateid;
1223	void *lckp;
1224
1225	if (nmp->nm_clp != NULL)
1226		clidrev = nmp->nm_clp->nfsc_clientidrev;
1227	newcred = cred;
1228	if (NFSHASNFSV4(nmp)) {
1229		nfhp = np->n_fhp;
1230		if (p == NULL)
1231			newcred = NFSNEWCRED(cred);
1232	}
1233	retrycnt = 0;
1234	do {
1235		lckp = NULL;
1236		if (NFSHASNFSV4(nmp))
1237			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1238			    NFSV4OPEN_ACCESSREAD, newcred, p, &stateid, &lckp);
1239		error = nfsrpc_readrpc(vp, uiop, newcred, &stateid, p, nap,
1240		    attrflagp, stuff);
1241		if (error == NFSERR_STALESTATEID)
1242			nfscl_initiate_recovery(nmp->nm_clp);
1243		if (lckp != NULL)
1244			nfscl_lockderef(lckp);
1245		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1246		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1247		    error == NFSERR_OLDSTATEID) {
1248			(void) nfs_catnap(PZERO, error, "nfs_read");
1249		} else if ((error == NFSERR_EXPIRED ||
1250		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1251			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1252		}
1253		retrycnt++;
1254	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1255	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1256	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1257	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1258	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1259	if (error && retrycnt >= 4)
1260		error = EIO;
1261	if (NFSHASNFSV4(nmp) && p == NULL)
1262		NFSFREECRED(newcred);
1263	return (error);
1264}
1265
1266/*
1267 * The actual read RPC.
1268 */
1269static int
1270nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
1271    nfsv4stateid_t *stateidp, NFSPROC_T *p, struct nfsvattr *nap,
1272    int *attrflagp, void *stuff)
1273{
1274	u_int32_t *tl;
1275	int error = 0, len, retlen, tsiz, eof = 0;
1276	struct nfsrv_descript nfsd;
1277	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1278	struct nfsrv_descript *nd = &nfsd;
1279
1280	*attrflagp = 0;
1281	tsiz = uio_uio_resid(uiop);
1282	if (uiop->uio_offset + tsiz > 0xffffffff &&
1283	    !NFSHASNFSV3OR4(nmp))
1284		return (EFBIG);
1285	nd->nd_mrep = NULL;
1286	while (tsiz > 0) {
1287		*attrflagp = 0;
1288		len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
1289		NFSCL_REQSTART(nd, NFSPROC_READ, vp);
1290		if (nd->nd_flag & ND_NFSV4)
1291			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1292		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
1293		if (nd->nd_flag & ND_NFSV2) {
1294			*tl++ = txdr_unsigned(uiop->uio_offset);
1295			*tl++ = txdr_unsigned(len);
1296			*tl = 0;
1297		} else {
1298			txdr_hyper(uiop->uio_offset, tl);
1299			*(tl + 2) = txdr_unsigned(len);
1300		}
1301		/*
1302		 * Since I can't do a Getattr for NFSv4 for Write, there
1303		 * doesn't seem any point in doing one here, either.
1304		 * (See the comment in nfsrpc_writerpc() for more info.)
1305		 */
1306		error = nfscl_request(nd, vp, p, cred, stuff);
1307		if (error)
1308			return (error);
1309		if (nd->nd_flag & ND_NFSV3) {
1310			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1311		} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
1312			error = nfsm_loadattr(nd, nap);
1313			if (!error)
1314				*attrflagp = 1;
1315		}
1316		if (nd->nd_repstat || error) {
1317			if (!error)
1318				error = nd->nd_repstat;
1319			goto nfsmout;
1320		}
1321		if (nd->nd_flag & ND_NFSV3) {
1322			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1323			eof = fxdr_unsigned(int, *(tl + 1));
1324		} else if (nd->nd_flag & ND_NFSV4) {
1325			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1326			eof = fxdr_unsigned(int, *tl);
1327		}
1328		NFSM_STRSIZ(retlen, nmp->nm_rsize);
1329		error = nfsm_mbufuio(nd, uiop, retlen);
1330		if (error)
1331			goto nfsmout;
1332		mbuf_freem(nd->nd_mrep);
1333		nd->nd_mrep = NULL;
1334		tsiz -= retlen;
1335		if (!(nd->nd_flag & ND_NFSV2)) {
1336			if (eof || retlen == 0)
1337				tsiz = 0;
1338		} else if (retlen < len)
1339			tsiz = 0;
1340	}
1341	return (0);
1342nfsmout:
1343	if (nd->nd_mrep != NULL)
1344		mbuf_freem(nd->nd_mrep);
1345	return (error);
1346}
1347
1348/*
1349 * nfs write operation
1350 * When called_from_strategy != 0, it should return EIO for an error that
1351 * indicates recovery is in progress, so that the buffer will be left
1352 * dirty and be written back to the server later. If it loops around,
1353 * the recovery thread could get stuck waiting for the buffer and recovery
1354 * will then deadlock.
1355 */
1356APPLESTATIC int
1357nfsrpc_write(vnode_t vp, struct uio *uiop, int *iomode, u_char *verfp,
1358    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
1359    void *stuff, int called_from_strategy)
1360{
1361	int error, expireret = 0, retrycnt, nostateid;
1362	u_int32_t clidrev = 0;
1363	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1364	struct nfsnode *np = VTONFS(vp);
1365	struct ucred *newcred;
1366	struct nfsfh *nfhp = NULL;
1367	nfsv4stateid_t stateid;
1368	void *lckp;
1369
1370	if (nmp->nm_clp != NULL)
1371		clidrev = nmp->nm_clp->nfsc_clientidrev;
1372	newcred = cred;
1373	if (NFSHASNFSV4(nmp)) {
1374		if (p == NULL)
1375			newcred = NFSNEWCRED(cred);
1376		nfhp = np->n_fhp;
1377	}
1378	retrycnt = 0;
1379	do {
1380		lckp = NULL;
1381		nostateid = 0;
1382		if (NFSHASNFSV4(nmp)) {
1383			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1384			    NFSV4OPEN_ACCESSWRITE, newcred, p, &stateid, &lckp);
1385			if (stateid.other[0] == 0 && stateid.other[1] == 0 &&
1386			    stateid.other[2] == 0) {
1387				nostateid = 1;
1388				printf("stateid0 in write\n");
1389			}
1390		}
1391
1392		/*
1393		 * If there is no stateid for NFSv4, it means this is an
1394		 * extraneous write after close. Basically a poorly
1395		 * implemented buffer cache. Just don't do the write.
1396		 */
1397		if (nostateid)
1398			error = 0;
1399		else
1400			error = nfsrpc_writerpc(vp, uiop, iomode, verfp,
1401			    newcred, &stateid, p, nap, attrflagp, stuff);
1402if (error == NFSERR_BADSTATEID) {
1403printf("st=0x%x 0x%x 0x%x\n",stateid.other[0],stateid.other[1],stateid.other[2]);
1404nfscl_dumpstate(nmp, 1, 1, 0, 0);
1405}
1406		if (error == NFSERR_STALESTATEID)
1407			nfscl_initiate_recovery(nmp->nm_clp);
1408		if (lckp != NULL)
1409			nfscl_lockderef(lckp);
1410		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1411		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1412		    error == NFSERR_OLDSTATEID) {
1413			(void) nfs_catnap(PZERO, error, "nfs_write");
1414		} else if ((error == NFSERR_EXPIRED ||
1415		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1416			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1417		}
1418		retrycnt++;
1419	} while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
1420	    ((error == NFSERR_STALESTATEID ||
1421	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy == 0) ||
1422	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1423	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1424	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1425	if (error != 0 && (retrycnt >= 4 ||
1426	    ((error == NFSERR_STALESTATEID ||
1427	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy != 0)))
1428		error = EIO;
1429	if (NFSHASNFSV4(nmp) && p == NULL)
1430		NFSFREECRED(newcred);
1431	return (error);
1432}
1433
1434/*
1435 * The actual write RPC.
1436 */
1437static int
1438nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
1439    u_char *verfp, struct ucred *cred, nfsv4stateid_t *stateidp,
1440    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1441{
1442	u_int32_t *tl;
1443	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1444	struct nfsnode *np = VTONFS(vp);
1445	int error = 0, len, tsiz, rlen, commit, committed = NFSWRITE_FILESYNC;
1446	int wccflag = 0, wsize;
1447	int32_t backup;
1448	struct nfsrv_descript nfsd;
1449	struct nfsrv_descript *nd = &nfsd;
1450	nfsattrbit_t attrbits;
1451
1452#ifdef DIAGNOSTIC
1453	if (uiop->uio_iovcnt != 1)
1454		panic("nfs: writerpc iovcnt > 1");
1455#endif
1456	*attrflagp = 0;
1457	tsiz = uio_uio_resid(uiop);
1458	NFSLOCKMNT(nmp);
1459	if (uiop->uio_offset + tsiz > 0xffffffff &&
1460	    !NFSHASNFSV3OR4(nmp)) {
1461		NFSUNLOCKMNT(nmp);
1462		return (EFBIG);
1463	}
1464	wsize = nmp->nm_wsize;
1465	NFSUNLOCKMNT(nmp);
1466	nd->nd_mrep = NULL;	/* NFSv2 sometimes does a write with */
1467	nd->nd_repstat = 0;	/* uio_resid == 0, so the while is not done */
1468	while (tsiz > 0) {
1469		nmp = VFSTONFS(vnode_mount(vp));
1470		if (nmp == NULL) {
1471			error = ENXIO;
1472			goto nfsmout;
1473		}
1474		*attrflagp = 0;
1475		len = (tsiz > wsize) ? wsize : tsiz;
1476		NFSCL_REQSTART(nd, NFSPROC_WRITE, vp);
1477		if (nd->nd_flag & ND_NFSV4) {
1478			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1479			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+2*NFSX_UNSIGNED);
1480			txdr_hyper(uiop->uio_offset, tl);
1481			tl += 2;
1482			*tl++ = txdr_unsigned(*iomode);
1483			*tl = txdr_unsigned(len);
1484		} else if (nd->nd_flag & ND_NFSV3) {
1485			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+3*NFSX_UNSIGNED);
1486			txdr_hyper(uiop->uio_offset, tl);
1487			tl += 2;
1488			*tl++ = txdr_unsigned(len);
1489			*tl++ = txdr_unsigned(*iomode);
1490			*tl = txdr_unsigned(len);
1491		} else {
1492			u_int32_t x;
1493
1494			NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1495			/*
1496			 * Not sure why someone changed this, since the
1497			 * RFC clearly states that "beginoffset" and
1498			 * "totalcount" are ignored, but it wouldn't
1499			 * surprise me if there's a busted server out there.
1500			 */
1501			/* Set both "begin" and "current" to non-garbage. */
1502			x = txdr_unsigned((u_int32_t)uiop->uio_offset);
1503			*tl++ = x;      /* "begin offset" */
1504			*tl++ = x;      /* "current offset" */
1505			x = txdr_unsigned(len);
1506			*tl++ = x;      /* total to this offset */
1507			*tl = x;        /* size of this write */
1508
1509		}
1510		nfsm_uiombuf(nd, uiop, len);
1511		/*
1512		 * Although it is tempting to do a normal Getattr Op in the
1513		 * NFSv4 compound, the result can be a nearly hung client
1514		 * system if the Getattr asks for Owner and/or OwnerGroup.
1515		 * It occurs when the client can't map either the Owner or
1516		 * Owner_group name in the Getattr reply to a uid/gid. When
1517		 * there is a cache miss, the kernel does an upcall to the
1518		 * nfsuserd. Then, it can try and read the local /etc/passwd
1519		 * or /etc/group file. It can then block in getnewbuf(),
1520		 * waiting for dirty writes to be pushed to the NFS server.
1521		 * The only reason this doesn't result in a complete
1522		 * deadlock, is that the upcall times out and allows
1523		 * the write to complete. However, progress is so slow
1524		 * that it might just as well be deadlocked.
1525		 * So, we just get the attributes that change with each
1526		 * write Op.
1527		 * nb: nfscl_loadattrcache() needs to be told that these
1528		 *     partial attributes from a write rpc are being
1529		 *     passed in, via a argument flag.
1530		 */
1531		if (nd->nd_flag & ND_NFSV4) {
1532			NFSWRITEGETATTR_ATTRBIT(&attrbits);
1533			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1534			*tl = txdr_unsigned(NFSV4OP_GETATTR);
1535			(void) nfsrv_putattrbit(nd, &attrbits);
1536		}
1537		error = nfscl_request(nd, vp, p, cred, stuff);
1538		if (error)
1539			return (error);
1540		if (nd->nd_repstat) {
1541			/*
1542			 * In case the rpc gets retried, roll
1543			 * the uio fileds changed by nfsm_uiombuf()
1544			 * back.
1545			 */
1546			uiop->uio_offset -= len;
1547			uio_uio_resid_add(uiop, len);
1548			uio_iov_base_add(uiop, -len);
1549			uio_iov_len_add(uiop, len);
1550		}
1551		if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1552			error = nfscl_wcc_data(nd, vp, nap, attrflagp,
1553			    &wccflag, stuff);
1554			if (error)
1555				goto nfsmout;
1556		}
1557		if (!nd->nd_repstat) {
1558			if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1559				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED
1560					+ NFSX_VERF);
1561				rlen = fxdr_unsigned(int, *tl++);
1562				if (rlen == 0) {
1563					error = NFSERR_IO;
1564					goto nfsmout;
1565				} else if (rlen < len) {
1566					backup = len - rlen;
1567					uio_iov_base_add(uiop, -(backup));
1568					uio_iov_len_add(uiop, backup);
1569					uiop->uio_offset -= backup;
1570					uio_uio_resid_add(uiop, backup);
1571					len = rlen;
1572				}
1573				commit = fxdr_unsigned(int, *tl++);
1574
1575				/*
1576				 * Return the lowest committment level
1577				 * obtained by any of the RPCs.
1578				 */
1579				if (committed == NFSWRITE_FILESYNC)
1580					committed = commit;
1581				else if (committed == NFSWRITE_DATASYNC &&
1582					commit == NFSWRITE_UNSTABLE)
1583					committed = commit;
1584				if (verfp != NULL)
1585					NFSBCOPY((caddr_t)tl, verfp, NFSX_VERF);
1586				NFSLOCKMNT(nmp);
1587				if (!NFSHASWRITEVERF(nmp)) {
1588					NFSBCOPY((caddr_t)tl,
1589					    (caddr_t)&nmp->nm_verf[0],
1590					    NFSX_VERF);
1591					NFSSETWRITEVERF(nmp);
1592				}
1593				NFSUNLOCKMNT(nmp);
1594			}
1595			if (nd->nd_flag & ND_NFSV4)
1596				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1597			if (nd->nd_flag & (ND_NFSV2 | ND_NFSV4)) {
1598				error = nfsm_loadattr(nd, nap);
1599				if (!error)
1600					*attrflagp = NFS_LATTR_NOSHRINK;
1601			}
1602		} else {
1603			error = nd->nd_repstat;
1604		}
1605		if (error)
1606			goto nfsmout;
1607		NFSWRITERPC_SETTIME(wccflag, np, (nd->nd_flag & ND_NFSV4));
1608		mbuf_freem(nd->nd_mrep);
1609		nd->nd_mrep = NULL;
1610		tsiz -= len;
1611	}
1612nfsmout:
1613	if (nd->nd_mrep != NULL)
1614		mbuf_freem(nd->nd_mrep);
1615	*iomode = committed;
1616	if (nd->nd_repstat && !error)
1617		error = nd->nd_repstat;
1618	return (error);
1619}
1620
1621/*
1622 * nfs mknod rpc
1623 * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1624 * mode set to specify the file type and the size field for rdev.
1625 */
1626APPLESTATIC int
1627nfsrpc_mknod(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1628    u_int32_t rdev, enum vtype vtyp, struct ucred *cred, NFSPROC_T *p,
1629    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1630    int *attrflagp, int *dattrflagp, void *dstuff)
1631{
1632	u_int32_t *tl;
1633	int error = 0;
1634	struct nfsrv_descript nfsd, *nd = &nfsd;
1635	nfsattrbit_t attrbits;
1636
1637	*nfhpp = NULL;
1638	*attrflagp = 0;
1639	*dattrflagp = 0;
1640	if (namelen > NFS_MAXNAMLEN)
1641		return (ENAMETOOLONG);
1642	NFSCL_REQSTART(nd, NFSPROC_MKNOD, dvp);
1643	if (nd->nd_flag & ND_NFSV4) {
1644		if (vtyp == VBLK || vtyp == VCHR) {
1645			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1646			*tl++ = vtonfsv34_type(vtyp);
1647			*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1648			*tl = txdr_unsigned(NFSMINOR(rdev));
1649		} else {
1650			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1651			*tl = vtonfsv34_type(vtyp);
1652		}
1653	}
1654	(void) nfsm_strtom(nd, name, namelen);
1655	if (nd->nd_flag & ND_NFSV3) {
1656		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1657		*tl = vtonfsv34_type(vtyp);
1658	}
1659	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1660		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1661	if ((nd->nd_flag & ND_NFSV3) &&
1662	    (vtyp == VCHR || vtyp == VBLK)) {
1663		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1664		*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1665		*tl = txdr_unsigned(NFSMINOR(rdev));
1666	}
1667	if (nd->nd_flag & ND_NFSV4) {
1668		NFSGETATTR_ATTRBIT(&attrbits);
1669		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1670		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1671		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1672		(void) nfsrv_putattrbit(nd, &attrbits);
1673	}
1674	if (nd->nd_flag & ND_NFSV2)
1675		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZERDEV, rdev);
1676	error = nfscl_request(nd, dvp, p, cred, dstuff);
1677	if (error)
1678		return (error);
1679	if (nd->nd_flag & ND_NFSV4)
1680		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1681	if (!nd->nd_repstat) {
1682		if (nd->nd_flag & ND_NFSV4) {
1683			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1684			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1685			if (error)
1686				goto nfsmout;
1687		}
1688		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1689		if (error)
1690			goto nfsmout;
1691	}
1692	if (nd->nd_flag & ND_NFSV3)
1693		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1694	if (!error && nd->nd_repstat)
1695		error = nd->nd_repstat;
1696nfsmout:
1697	mbuf_freem(nd->nd_mrep);
1698	return (error);
1699}
1700
1701/*
1702 * nfs file create call
1703 * Mostly just call the approriate routine. (I separated out v4, so that
1704 * error recovery wouldn't be as difficult.)
1705 */
1706APPLESTATIC int
1707nfsrpc_create(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1708    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1709    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1710    int *attrflagp, int *dattrflagp, void *dstuff)
1711{
1712	int error = 0, newone, expireret = 0, retrycnt, unlocked;
1713	struct nfsclowner *owp;
1714	struct nfscldeleg *dp;
1715	struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
1716	u_int32_t clidrev;
1717
1718	if (NFSHASNFSV4(nmp)) {
1719	    retrycnt = 0;
1720	    do {
1721		dp = NULL;
1722		error = nfscl_open(dvp, NULL, 0, (NFSV4OPEN_ACCESSWRITE |
1723		    NFSV4OPEN_ACCESSREAD), 0, cred, p, &owp, NULL, &newone,
1724		    NULL, 1);
1725		if (error)
1726			return (error);
1727		if (nmp->nm_clp != NULL)
1728			clidrev = nmp->nm_clp->nfsc_clientidrev;
1729		else
1730			clidrev = 0;
1731		error = nfsrpc_createv4(dvp, name, namelen, vap, cverf, fmode,
1732		  owp, &dp, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1733		  dstuff, &unlocked);
1734		if (dp != NULL)
1735			(void) nfscl_deleg(nmp->nm_mountp, owp->nfsow_clp,
1736			    (*nfhpp)->nfh_fh, (*nfhpp)->nfh_len, cred, p, &dp);
1737		nfscl_ownerrelease(owp, error, newone, unlocked);
1738		if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1739		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY) {
1740			(void) nfs_catnap(PZERO, error, "nfs_open");
1741		} else if ((error == NFSERR_EXPIRED ||
1742		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1743			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1744			retrycnt++;
1745		}
1746	    } while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1747		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1748		((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1749		 expireret == 0 && clidrev != 0 && retrycnt < 4));
1750	    if (error && retrycnt >= 4)
1751		    error = EIO;
1752	} else {
1753		error = nfsrpc_createv23(dvp, name, namelen, vap, cverf,
1754		    fmode, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1755		    dstuff);
1756	}
1757	return (error);
1758}
1759
1760/*
1761 * The create rpc for v2 and 3.
1762 */
1763static int
1764nfsrpc_createv23(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1765    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1766    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1767    int *attrflagp, int *dattrflagp, void *dstuff)
1768{
1769	u_int32_t *tl;
1770	int error = 0;
1771	struct nfsrv_descript nfsd, *nd = &nfsd;
1772
1773	*nfhpp = NULL;
1774	*attrflagp = 0;
1775	*dattrflagp = 0;
1776	if (namelen > NFS_MAXNAMLEN)
1777		return (ENAMETOOLONG);
1778	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1779	(void) nfsm_strtom(nd, name, namelen);
1780	if (nd->nd_flag & ND_NFSV3) {
1781		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1782		if (fmode & O_EXCL) {
1783			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
1784			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1785			*tl++ = cverf.lval[0];
1786			*tl = cverf.lval[1];
1787		} else {
1788			*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
1789			nfscl_fillsattr(nd, vap, dvp, 0, 0);
1790		}
1791	} else {
1792		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZE0, 0);
1793	}
1794	error = nfscl_request(nd, dvp, p, cred, dstuff);
1795	if (error)
1796		return (error);
1797	if (nd->nd_repstat == 0) {
1798		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1799		if (error)
1800			goto nfsmout;
1801	}
1802	if (nd->nd_flag & ND_NFSV3)
1803		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1804	if (nd->nd_repstat != 0 && error == 0)
1805		error = nd->nd_repstat;
1806nfsmout:
1807	mbuf_freem(nd->nd_mrep);
1808	return (error);
1809}
1810
1811static int
1812nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1813    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
1814    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
1815    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
1816    int *dattrflagp, void *dstuff, int *unlockedp)
1817{
1818	u_int32_t *tl;
1819	int error = 0, deleg, newone, ret, acesize, limitby;
1820	struct nfsrv_descript nfsd, *nd = &nfsd;
1821	struct nfsclopen *op;
1822	struct nfscldeleg *dp = NULL;
1823	struct nfsnode *np;
1824	struct nfsfh *nfhp;
1825	nfsattrbit_t attrbits;
1826	nfsv4stateid_t stateid;
1827	u_int32_t rflags;
1828
1829	*unlockedp = 0;
1830	*nfhpp = NULL;
1831	*dpp = NULL;
1832	*attrflagp = 0;
1833	*dattrflagp = 0;
1834	if (namelen > NFS_MAXNAMLEN)
1835		return (ENAMETOOLONG);
1836	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1837	/*
1838	 * For V4, this is actually an Open op.
1839	 */
1840	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1841	*tl++ = txdr_unsigned(owp->nfsow_seqid);
1842	*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
1843	    NFSV4OPEN_ACCESSREAD);
1844	*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
1845	*tl++ = owp->nfsow_clp->nfsc_clientid.lval[0];
1846	*tl = owp->nfsow_clp->nfsc_clientid.lval[1];
1847	(void) nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
1848	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1849	*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
1850	if (fmode & O_EXCL) {
1851		*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
1852		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1853		*tl++ = cverf.lval[0];
1854		*tl = cverf.lval[1];
1855	} else {
1856		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
1857		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1858	}
1859	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1860	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
1861	(void) nfsm_strtom(nd, name, namelen);
1862	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1863	*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1864	*tl = txdr_unsigned(NFSV4OP_GETATTR);
1865	NFSGETATTR_ATTRBIT(&attrbits);
1866	(void) nfsrv_putattrbit(nd, &attrbits);
1867	error = nfscl_request(nd, dvp, p, cred, dstuff);
1868	if (error)
1869		return (error);
1870	error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1871	if (error)
1872		goto nfsmout;
1873	NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
1874	if (nd->nd_repstat == 0) {
1875		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
1876		    6 * NFSX_UNSIGNED);
1877		stateid.seqid = *tl++;
1878		stateid.other[0] = *tl++;
1879		stateid.other[1] = *tl++;
1880		stateid.other[2] = *tl;
1881		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
1882		(void) nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1883		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1884		deleg = fxdr_unsigned(int, *tl);
1885		if (deleg == NFSV4OPEN_DELEGATEREAD ||
1886		    deleg == NFSV4OPEN_DELEGATEWRITE) {
1887			if (!(owp->nfsow_clp->nfsc_flags &
1888			      NFSCLFLAGS_FIRSTDELEG))
1889				owp->nfsow_clp->nfsc_flags |=
1890				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
1891			MALLOC(dp, struct nfscldeleg *,
1892			    sizeof (struct nfscldeleg) + NFSX_V4FHMAX,
1893			    M_NFSCLDELEG, M_WAITOK);
1894			LIST_INIT(&dp->nfsdl_owner);
1895			LIST_INIT(&dp->nfsdl_lock);
1896			dp->nfsdl_clp = owp->nfsow_clp;
1897			newnfs_copyincred(cred, &dp->nfsdl_cred);
1898			nfscl_lockinit(&dp->nfsdl_rwlock);
1899			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
1900			    NFSX_UNSIGNED);
1901			dp->nfsdl_stateid.seqid = *tl++;
1902			dp->nfsdl_stateid.other[0] = *tl++;
1903			dp->nfsdl_stateid.other[1] = *tl++;
1904			dp->nfsdl_stateid.other[2] = *tl++;
1905			ret = fxdr_unsigned(int, *tl);
1906			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
1907				dp->nfsdl_flags = NFSCLDL_WRITE;
1908				/*
1909				 * Indicates how much the file can grow.
1910				 */
1911				NFSM_DISSECT(tl, u_int32_t *,
1912				    3 * NFSX_UNSIGNED);
1913				limitby = fxdr_unsigned(int, *tl++);
1914				switch (limitby) {
1915				case NFSV4OPEN_LIMITSIZE:
1916					dp->nfsdl_sizelimit = fxdr_hyper(tl);
1917					break;
1918				case NFSV4OPEN_LIMITBLOCKS:
1919					dp->nfsdl_sizelimit =
1920					    fxdr_unsigned(u_int64_t, *tl++);
1921					dp->nfsdl_sizelimit *=
1922					    fxdr_unsigned(u_int64_t, *tl);
1923					break;
1924				default:
1925					error = NFSERR_BADXDR;
1926					goto nfsmout;
1927				};
1928			} else {
1929				dp->nfsdl_flags = NFSCLDL_READ;
1930			}
1931			if (ret)
1932				dp->nfsdl_flags |= NFSCLDL_RECALL;
1933			error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
1934			    &acesize, p);
1935			if (error)
1936				goto nfsmout;
1937		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
1938			error = NFSERR_BADXDR;
1939			goto nfsmout;
1940		}
1941		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1942		if (error)
1943			goto nfsmout;
1944		if (dp != NULL && *attrflagp) {
1945			dp->nfsdl_change = nnap->na_filerev;
1946			dp->nfsdl_modtime = nnap->na_mtime;
1947			dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
1948		}
1949		/*
1950		 * We can now complete the Open state.
1951		 */
1952		nfhp = *nfhpp;
1953		if (dp != NULL) {
1954			dp->nfsdl_fhlen = nfhp->nfh_len;
1955			NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh, nfhp->nfh_len);
1956		}
1957		/*
1958		 * Get an Open structure that will be
1959		 * attached to the OpenOwner, acquired already.
1960		 */
1961		error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len,
1962		    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
1963		    cred, p, NULL, &op, &newone, NULL, 0);
1964		if (error)
1965			goto nfsmout;
1966		op->nfso_stateid = stateid;
1967		newnfs_copyincred(cred, &op->nfso_cred);
1968		if ((rflags & NFSV4OPEN_RESULTCONFIRM)) {
1969		    do {
1970			ret = nfsrpc_openconfirm(dvp, nfhp->nfh_fh,
1971			    nfhp->nfh_len, op, cred, p);
1972			if (ret == NFSERR_DELAY)
1973			    (void) nfs_catnap(PZERO, ret, "nfs_create");
1974		    } while (ret == NFSERR_DELAY);
1975		    error = ret;
1976		}
1977
1978		/*
1979		 * If the server is handing out delegations, but we didn't
1980		 * get one because an OpenConfirm was required, try the
1981		 * Open again, to get a delegation. This is a harmless no-op,
1982		 * from a server's point of view.
1983		 */
1984		if ((rflags & NFSV4OPEN_RESULTCONFIRM) &&
1985		    (owp->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG) &&
1986		    !error && dp == NULL) {
1987		    np = VTONFS(dvp);
1988		    do {
1989			ret = nfsrpc_openrpc(VFSTONFS(vnode_mount(dvp)), dvp,
1990			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
1991			    nfhp->nfh_fh, nfhp->nfh_len,
1992			    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), op,
1993			    name, namelen, &dp, 0, 0x0, cred, p, 0, 1);
1994			if (ret == NFSERR_DELAY)
1995			    (void) nfs_catnap(PZERO, ret, "nfs_crt2");
1996		    } while (ret == NFSERR_DELAY);
1997		    if (ret) {
1998			if (dp != NULL)
1999				FREE((caddr_t)dp, M_NFSCLDELEG);
2000			if (ret == NFSERR_STALECLIENTID ||
2001			    ret == NFSERR_STALEDONTRECOVER)
2002				error = ret;
2003		    }
2004		}
2005		nfscl_openrelease(op, error, newone);
2006		*unlockedp = 1;
2007	}
2008	if (nd->nd_repstat != 0 && error == 0)
2009		error = nd->nd_repstat;
2010	if (error == NFSERR_STALECLIENTID)
2011		nfscl_initiate_recovery(owp->nfsow_clp);
2012nfsmout:
2013	if (!error)
2014		*dpp = dp;
2015	else if (dp != NULL)
2016		FREE((caddr_t)dp, M_NFSCLDELEG);
2017	mbuf_freem(nd->nd_mrep);
2018	return (error);
2019}
2020
2021/*
2022 * Nfs remove rpc
2023 */
2024APPLESTATIC int
2025nfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp,
2026    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp,
2027    void *dstuff)
2028{
2029	u_int32_t *tl;
2030	struct nfsrv_descript nfsd, *nd = &nfsd;
2031	struct nfsnode *np;
2032	struct nfsmount *nmp;
2033	nfsv4stateid_t dstateid;
2034	int error, ret = 0, i;
2035
2036	*dattrflagp = 0;
2037	if (namelen > NFS_MAXNAMLEN)
2038		return (ENAMETOOLONG);
2039	nmp = VFSTONFS(vnode_mount(dvp));
2040tryagain:
2041	if (NFSHASNFSV4(nmp) && ret == 0) {
2042		ret = nfscl_removedeleg(vp, p, &dstateid);
2043		if (ret == 1) {
2044			NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp);
2045			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
2046			    NFSX_UNSIGNED);
2047			*tl++ = dstateid.seqid;
2048			*tl++ = dstateid.other[0];
2049			*tl++ = dstateid.other[1];
2050			*tl++ = dstateid.other[2];
2051			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2052			np = VTONFS(dvp);
2053			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2054			    np->n_fhp->nfh_len, 0);
2055			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2056			*tl = txdr_unsigned(NFSV4OP_REMOVE);
2057		}
2058	} else {
2059		ret = 0;
2060	}
2061	if (ret == 0)
2062		NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp);
2063	(void) nfsm_strtom(nd, name, namelen);
2064	error = nfscl_request(nd, dvp, p, cred, dstuff);
2065	if (error)
2066		return (error);
2067	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2068		/* For NFSv4, parse out any Delereturn replies. */
2069		if (ret > 0 && nd->nd_repstat != 0 &&
2070		    (nd->nd_flag & ND_NOMOREDATA)) {
2071			/*
2072			 * If the Delegreturn failed, try again without
2073			 * it. The server will Recall, as required.
2074			 */
2075			mbuf_freem(nd->nd_mrep);
2076			goto tryagain;
2077		}
2078		for (i = 0; i < (ret * 2); i++) {
2079			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2080			    ND_NFSV4) {
2081			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2082			    if (*(tl + 1))
2083				nd->nd_flag |= ND_NOMOREDATA;
2084			}
2085		}
2086		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2087	}
2088	if (nd->nd_repstat && !error)
2089		error = nd->nd_repstat;
2090nfsmout:
2091	mbuf_freem(nd->nd_mrep);
2092	return (error);
2093}
2094
2095/*
2096 * Do an nfs rename rpc.
2097 */
2098APPLESTATIC int
2099nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen,
2100    vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred *cred,
2101    NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap,
2102    int *fattrflagp, int *tattrflagp, void *fstuff, void *tstuff)
2103{
2104	u_int32_t *tl;
2105	struct nfsrv_descript nfsd, *nd = &nfsd;
2106	struct nfsmount *nmp;
2107	struct nfsnode *np;
2108	nfsattrbit_t attrbits;
2109	nfsv4stateid_t fdstateid, tdstateid;
2110	int error = 0, ret = 0, gottd = 0, gotfd = 0, i;
2111
2112	*fattrflagp = 0;
2113	*tattrflagp = 0;
2114	nmp = VFSTONFS(vnode_mount(fdvp));
2115	if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN)
2116		return (ENAMETOOLONG);
2117tryagain:
2118	if (NFSHASNFSV4(nmp) && ret == 0) {
2119		ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp,
2120		    &tdstateid, &gottd, p);
2121		if (gotfd && gottd) {
2122			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME2, fvp);
2123		} else if (gotfd) {
2124			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, fvp);
2125		} else if (gottd) {
2126			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, tvp);
2127		}
2128		if (gotfd) {
2129			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2130			*tl++ = fdstateid.seqid;
2131			*tl++ = fdstateid.other[0];
2132			*tl++ = fdstateid.other[1];
2133			*tl = fdstateid.other[2];
2134			if (gottd) {
2135				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2136				*tl = txdr_unsigned(NFSV4OP_PUTFH);
2137				np = VTONFS(tvp);
2138				(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2139				    np->n_fhp->nfh_len, 0);
2140				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2141				*tl = txdr_unsigned(NFSV4OP_DELEGRETURN);
2142			}
2143		}
2144		if (gottd) {
2145			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2146			*tl++ = tdstateid.seqid;
2147			*tl++ = tdstateid.other[0];
2148			*tl++ = tdstateid.other[1];
2149			*tl = tdstateid.other[2];
2150		}
2151		if (ret > 0) {
2152			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2153			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2154			np = VTONFS(fdvp);
2155			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2156			    np->n_fhp->nfh_len, 0);
2157			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2158			*tl = txdr_unsigned(NFSV4OP_SAVEFH);
2159		}
2160	} else {
2161		ret = 0;
2162	}
2163	if (ret == 0)
2164		NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp);
2165	if (nd->nd_flag & ND_NFSV4) {
2166		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2167		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2168		NFSWCCATTR_ATTRBIT(&attrbits);
2169		(void) nfsrv_putattrbit(nd, &attrbits);
2170		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2171		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2172		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2173		    VTONFS(tdvp)->n_fhp->nfh_len, 0);
2174		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2175		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2176		(void) nfsrv_putattrbit(nd, &attrbits);
2177		nd->nd_flag |= ND_V4WCCATTR;
2178		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2179		*tl = txdr_unsigned(NFSV4OP_RENAME);
2180	}
2181	(void) nfsm_strtom(nd, fnameptr, fnamelen);
2182	if (!(nd->nd_flag & ND_NFSV4))
2183		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2184			VTONFS(tdvp)->n_fhp->nfh_len, 0);
2185	(void) nfsm_strtom(nd, tnameptr, tnamelen);
2186	error = nfscl_request(nd, fdvp, p, cred, fstuff);
2187	if (error)
2188		return (error);
2189	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2190		/* For NFSv4, parse out any Delereturn replies. */
2191		if (ret > 0 && nd->nd_repstat != 0 &&
2192		    (nd->nd_flag & ND_NOMOREDATA)) {
2193			/*
2194			 * If the Delegreturn failed, try again without
2195			 * it. The server will Recall, as required.
2196			 */
2197			mbuf_freem(nd->nd_mrep);
2198			goto tryagain;
2199		}
2200		for (i = 0; i < (ret * 2); i++) {
2201			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2202			    ND_NFSV4) {
2203			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2204			    if (*(tl + 1)) {
2205				if (i == 0 && ret > 1) {
2206				    /*
2207				     * If the Delegreturn failed, try again
2208				     * without it. The server will Recall, as
2209				     * required.
2210				     * If ret > 1, the first iteration of this
2211				     * loop is the second DelegReturn result.
2212				     */
2213				    mbuf_freem(nd->nd_mrep);
2214				    goto tryagain;
2215				} else {
2216				    nd->nd_flag |= ND_NOMOREDATA;
2217				}
2218			    }
2219			}
2220		}
2221		/* Now, the first wcc attribute reply. */
2222		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2223			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2224			if (*(tl + 1))
2225				nd->nd_flag |= ND_NOMOREDATA;
2226		}
2227		error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL,
2228		    fstuff);
2229		/* and the second wcc attribute reply. */
2230		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
2231		    !error) {
2232			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2233			if (*(tl + 1))
2234				nd->nd_flag |= ND_NOMOREDATA;
2235		}
2236		if (!error)
2237			error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp,
2238			    NULL, tstuff);
2239	}
2240	if (nd->nd_repstat && !error)
2241		error = nd->nd_repstat;
2242nfsmout:
2243	mbuf_freem(nd->nd_mrep);
2244	return (error);
2245}
2246
2247/*
2248 * nfs hard link create rpc
2249 */
2250APPLESTATIC int
2251nfsrpc_link(vnode_t dvp, vnode_t vp, char *name, int namelen,
2252    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2253    struct nfsvattr *nap, int *attrflagp, int *dattrflagp, void *dstuff)
2254{
2255	u_int32_t *tl;
2256	struct nfsrv_descript nfsd, *nd = &nfsd;
2257	nfsattrbit_t attrbits;
2258	int error = 0;
2259
2260	*attrflagp = 0;
2261	*dattrflagp = 0;
2262	if (namelen > NFS_MAXNAMLEN)
2263		return (ENAMETOOLONG);
2264	NFSCL_REQSTART(nd, NFSPROC_LINK, vp);
2265	if (nd->nd_flag & ND_NFSV4) {
2266		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2267		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2268	}
2269	(void) nfsm_fhtom(nd, VTONFS(dvp)->n_fhp->nfh_fh,
2270		VTONFS(dvp)->n_fhp->nfh_len, 0);
2271	if (nd->nd_flag & ND_NFSV4) {
2272		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2273		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2274		NFSWCCATTR_ATTRBIT(&attrbits);
2275		(void) nfsrv_putattrbit(nd, &attrbits);
2276		nd->nd_flag |= ND_V4WCCATTR;
2277		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2278		*tl = txdr_unsigned(NFSV4OP_LINK);
2279	}
2280	(void) nfsm_strtom(nd, name, namelen);
2281	error = nfscl_request(nd, vp, p, cred, dstuff);
2282	if (error)
2283		return (error);
2284	if (nd->nd_flag & ND_NFSV3) {
2285		error = nfscl_postop_attr(nd, nap, attrflagp, dstuff);
2286		if (!error)
2287			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2288			    NULL, dstuff);
2289	} else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2290		/*
2291		 * First, parse out the PutFH and Getattr result.
2292		 */
2293		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2294		if (!(*(tl + 1)))
2295			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2296		if (*(tl + 1))
2297			nd->nd_flag |= ND_NOMOREDATA;
2298		/*
2299		 * Get the pre-op attributes.
2300		 */
2301		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2302	}
2303	if (nd->nd_repstat && !error)
2304		error = nd->nd_repstat;
2305nfsmout:
2306	mbuf_freem(nd->nd_mrep);
2307	return (error);
2308}
2309
2310/*
2311 * nfs symbolic link create rpc
2312 */
2313APPLESTATIC int
2314nfsrpc_symlink(vnode_t dvp, char *name, int namelen, char *target,
2315    struct vattr *vap, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2316    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2317    int *dattrflagp, void *dstuff)
2318{
2319	u_int32_t *tl;
2320	struct nfsrv_descript nfsd, *nd = &nfsd;
2321	struct nfsmount *nmp;
2322	int slen, error = 0;
2323
2324	*nfhpp = NULL;
2325	*attrflagp = 0;
2326	*dattrflagp = 0;
2327	nmp = VFSTONFS(vnode_mount(dvp));
2328	slen = strlen(target);
2329	if (slen > NFS_MAXPATHLEN || namelen > NFS_MAXNAMLEN)
2330		return (ENAMETOOLONG);
2331	NFSCL_REQSTART(nd, NFSPROC_SYMLINK, dvp);
2332	if (nd->nd_flag & ND_NFSV4) {
2333		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2334		*tl = txdr_unsigned(NFLNK);
2335		(void) nfsm_strtom(nd, target, slen);
2336	}
2337	(void) nfsm_strtom(nd, name, namelen);
2338	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2339		nfscl_fillsattr(nd, vap, dvp, 0, 0);
2340	if (!(nd->nd_flag & ND_NFSV4))
2341		(void) nfsm_strtom(nd, target, slen);
2342	if (nd->nd_flag & ND_NFSV2)
2343		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2344	error = nfscl_request(nd, dvp, p, cred, dstuff);
2345	if (error)
2346		return (error);
2347	if (nd->nd_flag & ND_NFSV4)
2348		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2349	if ((nd->nd_flag & ND_NFSV3) && !error) {
2350		if (!nd->nd_repstat)
2351			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2352		if (!error)
2353			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2354			    NULL, dstuff);
2355	}
2356	if (nd->nd_repstat && !error)
2357		error = nd->nd_repstat;
2358	mbuf_freem(nd->nd_mrep);
2359	/*
2360	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2361	 */
2362	if (error == EEXIST)
2363		error = 0;
2364	return (error);
2365}
2366
2367/*
2368 * nfs make dir rpc
2369 */
2370APPLESTATIC int
2371nfsrpc_mkdir(vnode_t dvp, char *name, int namelen, struct vattr *vap,
2372    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2373    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2374    int *dattrflagp, void *dstuff)
2375{
2376	u_int32_t *tl;
2377	struct nfsrv_descript nfsd, *nd = &nfsd;
2378	nfsattrbit_t attrbits;
2379	int error = 0;
2380
2381	*nfhpp = NULL;
2382	*attrflagp = 0;
2383	*dattrflagp = 0;
2384	if (namelen > NFS_MAXNAMLEN)
2385		return (ENAMETOOLONG);
2386	NFSCL_REQSTART(nd, NFSPROC_MKDIR, dvp);
2387	if (nd->nd_flag & ND_NFSV4) {
2388		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2389		*tl = txdr_unsigned(NFDIR);
2390	}
2391	(void) nfsm_strtom(nd, name, namelen);
2392	nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2393	if (nd->nd_flag & ND_NFSV4) {
2394		NFSGETATTR_ATTRBIT(&attrbits);
2395		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2396		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2397		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2398		(void) nfsrv_putattrbit(nd, &attrbits);
2399	}
2400	error = nfscl_request(nd, dvp, p, cred, dstuff);
2401	if (error)
2402		return (error);
2403	if (nd->nd_flag & ND_NFSV4)
2404		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2405	if (!nd->nd_repstat && !error) {
2406		if (nd->nd_flag & ND_NFSV4) {
2407			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2408			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2409		}
2410		if (!error)
2411			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2412	}
2413	if ((nd->nd_flag & ND_NFSV3) && !error)
2414		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2415	if (nd->nd_repstat && !error)
2416		error = nd->nd_repstat;
2417nfsmout:
2418	mbuf_freem(nd->nd_mrep);
2419	/*
2420	 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry.
2421	 */
2422	if (error == EEXIST)
2423		error = 0;
2424	return (error);
2425}
2426
2427/*
2428 * nfs remove directory call
2429 */
2430APPLESTATIC int
2431nfsrpc_rmdir(vnode_t dvp, char *name, int namelen, struct ucred *cred,
2432    NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp, void *dstuff)
2433{
2434	struct nfsrv_descript nfsd, *nd = &nfsd;
2435	int error = 0;
2436
2437	*dattrflagp = 0;
2438	if (namelen > NFS_MAXNAMLEN)
2439		return (ENAMETOOLONG);
2440	NFSCL_REQSTART(nd, NFSPROC_RMDIR, dvp);
2441	(void) nfsm_strtom(nd, name, namelen);
2442	error = nfscl_request(nd, dvp, p, cred, dstuff);
2443	if (error)
2444		return (error);
2445	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2446		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2447	if (nd->nd_repstat && !error)
2448		error = nd->nd_repstat;
2449	mbuf_freem(nd->nd_mrep);
2450	/*
2451	 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2452	 */
2453	if (error == ENOENT)
2454		error = 0;
2455	return (error);
2456}
2457
2458/*
2459 * Readdir rpc.
2460 * Always returns with either uio_resid unchanged, if you are at the
2461 * end of the directory, or uio_resid == 0, with all DIRBLKSIZ chunks
2462 * filled in.
2463 * I felt this would allow caching of directory blocks more easily
2464 * than returning a pertially filled block.
2465 * Directory offset cookies:
2466 * Oh my, what to do with them...
2467 * I can think of three ways to deal with them:
2468 * 1 - have the layer above these RPCs maintain a map between logical
2469 *     directory byte offsets and the NFS directory offset cookies
2470 * 2 - pass the opaque directory offset cookies up into userland
2471 *     and let the libc functions deal with them, via the system call
2472 * 3 - return them to userland in the "struct dirent", so future versions
2473 *     of libc can use them and do whatever is necessary to amke things work
2474 *     above these rpc calls, in the meantime
2475 * For now, I do #3 by "hiding" the directory offset cookies after the
2476 * d_name field in struct dirent. This is space inside d_reclen that
2477 * will be ignored by anything that doesn't know about them.
2478 * The directory offset cookies are filled in as the last 8 bytes of
2479 * each directory entry, after d_name. Someday, the userland libc
2480 * functions may be able to use these. In the meantime, it satisfies
2481 * OpenBSD's requirements for cookies being returned.
2482 * If expects the directory offset cookie for the read to be in uio_offset
2483 * and returns the one for the next entry after this directory block in
2484 * there, as well.
2485 */
2486APPLESTATIC int
2487nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2488    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2489    int *eofp, void *stuff)
2490{
2491	int len, left;
2492	struct dirent *dp = NULL;
2493	u_int32_t *tl;
2494	nfsquad_t cookie, ncookie;
2495	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2496	struct nfsnode *dnp = VTONFS(vp);
2497	struct nfsvattr nfsva;
2498	struct nfsrv_descript nfsd, *nd = &nfsd;
2499	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2500	int reqsize, tryformoredirs = 1, readsize, eof = 0, gotmnton = 0;
2501	long dotfileid, dotdotfileid = 0;
2502	u_int32_t fakefileno = 0xffffffff, rderr;
2503	char *cp;
2504	nfsattrbit_t attrbits, dattrbits;
2505	u_int32_t *tl2 = NULL;
2506	size_t tresid;
2507
2508#ifdef DIAGNOSTIC
2509	if (uiop->uio_iovcnt != 1 || (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)))
2510		panic("nfs readdirrpc bad uio");
2511#endif
2512
2513	/*
2514	 * There is no point in reading a lot more than uio_resid, however
2515	 * adding one additional DIRBLKSIZ makes sense. Since uio_resid
2516	 * and nm_readdirsize are both exact multiples of DIRBLKSIZ, this
2517	 * will never make readsize > nm_readdirsize.
2518	 */
2519	readsize = nmp->nm_readdirsize;
2520	if (readsize > uio_uio_resid(uiop))
2521		readsize = uio_uio_resid(uiop) + DIRBLKSIZ;
2522
2523	*attrflagp = 0;
2524	if (eofp)
2525		*eofp = 0;
2526	tresid = uio_uio_resid(uiop);
2527	cookie.lval[0] = cookiep->nfsuquad[0];
2528	cookie.lval[1] = cookiep->nfsuquad[1];
2529	nd->nd_mrep = NULL;
2530
2531	/*
2532	 * For NFSv4, first create the "." and ".." entries.
2533	 */
2534	if (NFSHASNFSV4(nmp)) {
2535		reqsize = 6 * NFSX_UNSIGNED;
2536		NFSGETATTR_ATTRBIT(&dattrbits);
2537		NFSZERO_ATTRBIT(&attrbits);
2538		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
2539		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TYPE);
2540		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
2541		    NFSATTRBIT_MOUNTEDONFILEID)) {
2542			NFSSETBIT_ATTRBIT(&attrbits,
2543			    NFSATTRBIT_MOUNTEDONFILEID);
2544			gotmnton = 1;
2545		} else {
2546			/*
2547			 * Must fake it. Use the fileno, except when the
2548			 * fsid is != to that of the directory. For that
2549			 * case, generate a fake fileno that is not the same.
2550			 */
2551			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
2552			gotmnton = 0;
2553		}
2554
2555		/*
2556		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
2557		 */
2558		if (uiop->uio_offset == 0) {
2559#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
2560			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred);
2561#else
2562			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred, p);
2563#endif
2564			if (error)
2565			    return (error);
2566			dotfileid = nfsva.na_fileid;
2567			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
2568			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2569			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2570			*tl = txdr_unsigned(NFSV4OP_GETATTR);
2571			(void) nfsrv_putattrbit(nd, &attrbits);
2572			error = nfscl_request(nd, vp, p, cred, stuff);
2573			if (error)
2574			    return (error);
2575			if (nd->nd_repstat == 0) {
2576			    NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2577			    len = fxdr_unsigned(int, *(tl + 2));
2578			    if (len > 0 && len <= NFSX_V4FHMAX)
2579				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2580			    else
2581				error = EPERM;
2582			    if (!error) {
2583				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2584				nfsva.na_mntonfileno = 0xffffffff;
2585				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2586				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2587				    NULL, NULL, NULL, p, cred);
2588				if (error) {
2589				    dotdotfileid = dotfileid;
2590				} else if (gotmnton) {
2591				    if (nfsva.na_mntonfileno != 0xffffffff)
2592					dotdotfileid = nfsva.na_mntonfileno;
2593				    else
2594					dotdotfileid = nfsva.na_fileid;
2595				} else if (nfsva.na_filesid[0] ==
2596				    dnp->n_vattr.na_filesid[0] &&
2597				    nfsva.na_filesid[1] ==
2598				    dnp->n_vattr.na_filesid[1]) {
2599				    dotdotfileid = nfsva.na_fileid;
2600				} else {
2601				    do {
2602					fakefileno--;
2603				    } while (fakefileno ==
2604					nfsva.na_fileid);
2605				    dotdotfileid = fakefileno;
2606				}
2607			    }
2608			} else if (nd->nd_repstat == NFSERR_NOENT) {
2609			    /*
2610			     * Lookupp returns NFSERR_NOENT when we are
2611			     * at the root, so just use the current dir.
2612			     */
2613			    nd->nd_repstat = 0;
2614			    dotdotfileid = dotfileid;
2615			} else {
2616			    error = nd->nd_repstat;
2617			}
2618			mbuf_freem(nd->nd_mrep);
2619			if (error)
2620			    return (error);
2621			nd->nd_mrep = NULL;
2622			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2623			dp->d_type = DT_DIR;
2624			dp->d_fileno = dotfileid;
2625			dp->d_namlen = 1;
2626			dp->d_name[0] = '.';
2627			dp->d_name[1] = '\0';
2628			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2629			/*
2630			 * Just make these offset cookie 0.
2631			 */
2632			tl = (u_int32_t *)&dp->d_name[4];
2633			*tl++ = 0;
2634			*tl = 0;
2635			blksiz += dp->d_reclen;
2636			uio_uio_resid_add(uiop, -(dp->d_reclen));
2637			uiop->uio_offset += dp->d_reclen;
2638			uio_iov_base_add(uiop, dp->d_reclen);
2639			uio_iov_len_add(uiop, -(dp->d_reclen));
2640			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2641			dp->d_type = DT_DIR;
2642			dp->d_fileno = dotdotfileid;
2643			dp->d_namlen = 2;
2644			dp->d_name[0] = '.';
2645			dp->d_name[1] = '.';
2646			dp->d_name[2] = '\0';
2647			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2648			/*
2649			 * Just make these offset cookie 0.
2650			 */
2651			tl = (u_int32_t *)&dp->d_name[4];
2652			*tl++ = 0;
2653			*tl = 0;
2654			blksiz += dp->d_reclen;
2655			uio_uio_resid_add(uiop, -(dp->d_reclen));
2656			uiop->uio_offset += dp->d_reclen;
2657			uio_iov_base_add(uiop, dp->d_reclen);
2658			uio_iov_len_add(uiop, -(dp->d_reclen));
2659		}
2660		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_RDATTRERROR);
2661	} else {
2662		reqsize = 5 * NFSX_UNSIGNED;
2663	}
2664
2665
2666	/*
2667	 * Loop around doing readdir rpc's of size readsize.
2668	 * The stopping criteria is EOF or buffer full.
2669	 */
2670	while (more_dirs && bigenough) {
2671		*attrflagp = 0;
2672		NFSCL_REQSTART(nd, NFSPROC_READDIR, vp);
2673		if (nd->nd_flag & ND_NFSV2) {
2674			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2675			*tl++ = cookie.lval[1];
2676			*tl = txdr_unsigned(readsize);
2677		} else {
2678			NFSM_BUILD(tl, u_int32_t *, reqsize);
2679			*tl++ = cookie.lval[0];
2680			*tl++ = cookie.lval[1];
2681			if (cookie.qval == 0) {
2682				*tl++ = 0;
2683				*tl++ = 0;
2684			} else {
2685				NFSLOCKNODE(dnp);
2686				*tl++ = dnp->n_cookieverf.nfsuquad[0];
2687				*tl++ = dnp->n_cookieverf.nfsuquad[1];
2688				NFSUNLOCKNODE(dnp);
2689			}
2690			if (nd->nd_flag & ND_NFSV4) {
2691				*tl++ = txdr_unsigned(readsize);
2692				*tl = txdr_unsigned(readsize);
2693				(void) nfsrv_putattrbit(nd, &attrbits);
2694				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2695				*tl = txdr_unsigned(NFSV4OP_GETATTR);
2696				(void) nfsrv_putattrbit(nd, &dattrbits);
2697			} else {
2698				*tl = txdr_unsigned(readsize);
2699			}
2700		}
2701		error = nfscl_request(nd, vp, p, cred, stuff);
2702		if (error)
2703			return (error);
2704		if (!(nd->nd_flag & ND_NFSV2)) {
2705			if (nd->nd_flag & ND_NFSV3)
2706				error = nfscl_postop_attr(nd, nap, attrflagp,
2707				    stuff);
2708			if (!nd->nd_repstat && !error) {
2709				NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
2710				NFSLOCKNODE(dnp);
2711				dnp->n_cookieverf.nfsuquad[0] = *tl++;
2712				dnp->n_cookieverf.nfsuquad[1] = *tl;
2713				NFSUNLOCKNODE(dnp);
2714			}
2715		}
2716		if (nd->nd_repstat || error) {
2717			if (!error)
2718				error = nd->nd_repstat;
2719			goto nfsmout;
2720		}
2721		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2722		more_dirs = fxdr_unsigned(int, *tl);
2723		if (!more_dirs)
2724			tryformoredirs = 0;
2725
2726		/* loop thru the dir entries, doctoring them to 4bsd form */
2727		while (more_dirs && bigenough) {
2728			if (nd->nd_flag & ND_NFSV4) {
2729				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2730				ncookie.lval[0] = *tl++;
2731				ncookie.lval[1] = *tl++;
2732				len = fxdr_unsigned(int, *tl);
2733			} else if (nd->nd_flag & ND_NFSV3) {
2734				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2735				nfsva.na_fileid =
2736				    fxdr_unsigned(long, *++tl);
2737				len = fxdr_unsigned(int, *++tl);
2738			} else {
2739				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2740				nfsva.na_fileid =
2741				    fxdr_unsigned(long, *tl++);
2742				len = fxdr_unsigned(int, *tl);
2743			}
2744			if (len <= 0 || len > NFS_MAXNAMLEN) {
2745				error = EBADRPC;
2746				goto nfsmout;
2747			}
2748			tlen = NFSM_RNDUP(len);
2749			if (tlen == len)
2750				tlen += 4;  /* To ensure null termination */
2751			left = DIRBLKSIZ - blksiz;
2752			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > left) {
2753				dp->d_reclen += left;
2754				uio_iov_base_add(uiop, left);
2755				uio_iov_len_add(uiop, -(left));
2756				uio_uio_resid_add(uiop, -(left));
2757				uiop->uio_offset += left;
2758				blksiz = 0;
2759			}
2760			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
2761				bigenough = 0;
2762			if (bigenough) {
2763				dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2764				dp->d_namlen = len;
2765				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
2766				dp->d_type = DT_UNKNOWN;
2767				blksiz += dp->d_reclen;
2768				if (blksiz == DIRBLKSIZ)
2769					blksiz = 0;
2770				uio_uio_resid_add(uiop, -(DIRHDSIZ));
2771				uiop->uio_offset += DIRHDSIZ;
2772				uio_iov_base_add(uiop, DIRHDSIZ);
2773				uio_iov_len_add(uiop, -(DIRHDSIZ));
2774				error = nfsm_mbufuio(nd, uiop, len);
2775				if (error)
2776					goto nfsmout;
2777				cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
2778				tlen -= len;
2779				*cp = '\0';	/* null terminate */
2780				cp += tlen;	/* points to cookie storage */
2781				tl2 = (u_int32_t *)cp;
2782				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
2783				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
2784				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
2785				uiop->uio_offset += (tlen + NFSX_HYPER);
2786			} else {
2787				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2788				if (error)
2789					goto nfsmout;
2790			}
2791			if (nd->nd_flag & ND_NFSV4) {
2792				rderr = 0;
2793				nfsva.na_mntonfileno = 0xffffffff;
2794				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2795				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2796				    NULL, NULL, &rderr, p, cred);
2797				if (error)
2798					goto nfsmout;
2799				NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2800			} else if (nd->nd_flag & ND_NFSV3) {
2801				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2802				ncookie.lval[0] = *tl++;
2803				ncookie.lval[1] = *tl++;
2804			} else {
2805				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2806				ncookie.lval[0] = 0;
2807				ncookie.lval[1] = *tl++;
2808			}
2809			if (bigenough) {
2810			    if (nd->nd_flag & ND_NFSV4) {
2811				if (rderr) {
2812				    dp->d_fileno = 0;
2813				} else {
2814				    if (gotmnton) {
2815					if (nfsva.na_mntonfileno != 0xffffffff)
2816					    dp->d_fileno = nfsva.na_mntonfileno;
2817					else
2818					    dp->d_fileno = nfsva.na_fileid;
2819				    } else if (nfsva.na_filesid[0] ==
2820					dnp->n_vattr.na_filesid[0] &&
2821					nfsva.na_filesid[1] ==
2822					dnp->n_vattr.na_filesid[1]) {
2823					dp->d_fileno = nfsva.na_fileid;
2824				    } else {
2825					do {
2826					    fakefileno--;
2827					} while (fakefileno ==
2828					    nfsva.na_fileid);
2829					dp->d_fileno = fakefileno;
2830				    }
2831				    dp->d_type = vtonfs_dtype(nfsva.na_type);
2832				}
2833			    } else {
2834				dp->d_fileno = nfsva.na_fileid;
2835			    }
2836			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
2837				ncookie.lval[0];
2838			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
2839				ncookie.lval[1];
2840			}
2841			more_dirs = fxdr_unsigned(int, *tl);
2842		}
2843		/*
2844		 * If at end of rpc data, get the eof boolean
2845		 */
2846		if (!more_dirs) {
2847			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2848			eof = fxdr_unsigned(int, *tl);
2849			if (tryformoredirs)
2850				more_dirs = !eof;
2851			if (nd->nd_flag & ND_NFSV4) {
2852				error = nfscl_postop_attr(nd, nap, attrflagp,
2853				    stuff);
2854				if (error)
2855					goto nfsmout;
2856			}
2857		}
2858		mbuf_freem(nd->nd_mrep);
2859		nd->nd_mrep = NULL;
2860	}
2861	/*
2862	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
2863	 * by increasing d_reclen for the last record.
2864	 */
2865	if (blksiz > 0) {
2866		left = DIRBLKSIZ - blksiz;
2867		dp->d_reclen += left;
2868		uio_iov_base_add(uiop, left);
2869		uio_iov_len_add(uiop, -(left));
2870		uio_uio_resid_add(uiop, -(left));
2871		uiop->uio_offset += left;
2872	}
2873
2874	/*
2875	 * If returning no data, assume end of file.
2876	 * If not bigenough, return not end of file, since you aren't
2877	 *    returning all the data
2878	 * Otherwise, return the eof flag from the server.
2879	 */
2880	if (eofp) {
2881		if (tresid == ((size_t)(uio_uio_resid(uiop))))
2882			*eofp = 1;
2883		else if (!bigenough)
2884			*eofp = 0;
2885		else
2886			*eofp = eof;
2887	}
2888
2889	/*
2890	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
2891	 */
2892	while (uio_uio_resid(uiop) > 0 && ((size_t)(uio_uio_resid(uiop))) != tresid) {
2893		dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2894		dp->d_type = DT_UNKNOWN;
2895		dp->d_fileno = 0;
2896		dp->d_namlen = 0;
2897		dp->d_name[0] = '\0';
2898		tl = (u_int32_t *)&dp->d_name[4];
2899		*tl++ = cookie.lval[0];
2900		*tl = cookie.lval[1];
2901		dp->d_reclen = DIRBLKSIZ;
2902		uio_iov_base_add(uiop, DIRBLKSIZ);
2903		uio_iov_len_add(uiop, -(DIRBLKSIZ));
2904		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
2905		uiop->uio_offset += DIRBLKSIZ;
2906	}
2907
2908nfsmout:
2909	if (nd->nd_mrep != NULL)
2910		mbuf_freem(nd->nd_mrep);
2911	return (error);
2912}
2913
2914#ifndef APPLE
2915/*
2916 * NFS V3 readdir plus RPC. Used in place of nfsrpc_readdir().
2917 * (Also used for NFS V4 when mount flag set.)
2918 * (ditto above w.r.t. multiple of DIRBLKSIZ, etc.)
2919 */
2920APPLESTATIC int
2921nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2922    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2923    int *eofp, void *stuff)
2924{
2925	int len, left;
2926	struct dirent *dp = NULL;
2927	u_int32_t *tl;
2928	vnode_t newvp = NULLVP;
2929	struct nfsrv_descript nfsd, *nd = &nfsd;
2930	struct nameidata nami, *ndp = &nami;
2931	struct componentname *cnp = &ndp->ni_cnd;
2932	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2933	struct nfsnode *dnp = VTONFS(vp), *np;
2934	struct nfsvattr nfsva;
2935	struct nfsfh *nfhp;
2936	nfsquad_t cookie, ncookie;
2937	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2938	int attrflag, tryformoredirs = 1, eof = 0, gotmnton = 0;
2939	int unlocknewvp = 0;
2940	long dotfileid, dotdotfileid = 0, fileno = 0;
2941	char *cp;
2942	nfsattrbit_t attrbits, dattrbits;
2943	size_t tresid;
2944	u_int32_t *tl2 = NULL, fakefileno = 0xffffffff, rderr;
2945
2946#ifdef DIAGNOSTIC
2947	if (uiop->uio_iovcnt != 1 || (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)))
2948		panic("nfs readdirplusrpc bad uio");
2949#endif
2950	*attrflagp = 0;
2951	if (eofp != NULL)
2952		*eofp = 0;
2953	ndp->ni_dvp = vp;
2954	nd->nd_mrep = NULL;
2955	cookie.lval[0] = cookiep->nfsuquad[0];
2956	cookie.lval[1] = cookiep->nfsuquad[1];
2957	tresid = uio_uio_resid(uiop);
2958
2959	/*
2960	 * For NFSv4, first create the "." and ".." entries.
2961	 */
2962	if (NFSHASNFSV4(nmp)) {
2963		NFSGETATTR_ATTRBIT(&dattrbits);
2964		NFSZERO_ATTRBIT(&attrbits);
2965		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
2966		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
2967		    NFSATTRBIT_MOUNTEDONFILEID)) {
2968			NFSSETBIT_ATTRBIT(&attrbits,
2969			    NFSATTRBIT_MOUNTEDONFILEID);
2970			gotmnton = 1;
2971		} else {
2972			/*
2973			 * Must fake it. Use the fileno, except when the
2974			 * fsid is != to that of the directory. For that
2975			 * case, generate a fake fileno that is not the same.
2976			 */
2977			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
2978			gotmnton = 0;
2979		}
2980
2981		/*
2982		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
2983		 */
2984		if (uiop->uio_offset == 0) {
2985#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
2986			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred);
2987#else
2988			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred, p);
2989#endif
2990			if (error)
2991			    return (error);
2992			dotfileid = nfsva.na_fileid;
2993			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
2994			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2995			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2996			*tl = txdr_unsigned(NFSV4OP_GETATTR);
2997			(void) nfsrv_putattrbit(nd, &attrbits);
2998			error = nfscl_request(nd, vp, p, cred, stuff);
2999			if (error)
3000			    return (error);
3001			if (nd->nd_repstat == 0) {
3002			    NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3003			    len = fxdr_unsigned(int, *(tl + 2));
3004			    if (len > 0 && len <= NFSX_V4FHMAX)
3005				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3006			    else
3007				error = EPERM;
3008			    if (!error) {
3009				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3010				nfsva.na_mntonfileno = 0xffffffff;
3011				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
3012				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3013				    NULL, NULL, NULL, p, cred);
3014				if (error) {
3015				    dotdotfileid = dotfileid;
3016				} else if (gotmnton) {
3017				    if (nfsva.na_mntonfileno != 0xffffffff)
3018					dotdotfileid = nfsva.na_mntonfileno;
3019				    else
3020					dotdotfileid = nfsva.na_fileid;
3021				} else if (nfsva.na_filesid[0] ==
3022				    dnp->n_vattr.na_filesid[0] &&
3023				    nfsva.na_filesid[1] ==
3024				    dnp->n_vattr.na_filesid[1]) {
3025				    dotdotfileid = nfsva.na_fileid;
3026				} else {
3027				    do {
3028					fakefileno--;
3029				    } while (fakefileno ==
3030					nfsva.na_fileid);
3031				    dotdotfileid = fakefileno;
3032				}
3033			    }
3034			} else if (nd->nd_repstat == NFSERR_NOENT) {
3035			    /*
3036			     * Lookupp returns NFSERR_NOENT when we are
3037			     * at the root, so just use the current dir.
3038			     */
3039			    nd->nd_repstat = 0;
3040			    dotdotfileid = dotfileid;
3041			} else {
3042			    error = nd->nd_repstat;
3043			}
3044			mbuf_freem(nd->nd_mrep);
3045			if (error)
3046			    return (error);
3047			nd->nd_mrep = NULL;
3048			dp = (struct dirent *)uio_iov_base(uiop);
3049			dp->d_type = DT_DIR;
3050			dp->d_fileno = dotfileid;
3051			dp->d_namlen = 1;
3052			dp->d_name[0] = '.';
3053			dp->d_name[1] = '\0';
3054			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3055			/*
3056			 * Just make these offset cookie 0.
3057			 */
3058			tl = (u_int32_t *)&dp->d_name[4];
3059			*tl++ = 0;
3060			*tl = 0;
3061			blksiz += dp->d_reclen;
3062			uio_uio_resid_add(uiop, -(dp->d_reclen));
3063			uiop->uio_offset += dp->d_reclen;
3064			uio_iov_base_add(uiop, dp->d_reclen);
3065			uio_iov_len_add(uiop, -(dp->d_reclen));
3066			dp = (struct dirent *)uio_iov_base(uiop);
3067			dp->d_type = DT_DIR;
3068			dp->d_fileno = dotdotfileid;
3069			dp->d_namlen = 2;
3070			dp->d_name[0] = '.';
3071			dp->d_name[1] = '.';
3072			dp->d_name[2] = '\0';
3073			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3074			/*
3075			 * Just make these offset cookie 0.
3076			 */
3077			tl = (u_int32_t *)&dp->d_name[4];
3078			*tl++ = 0;
3079			*tl = 0;
3080			blksiz += dp->d_reclen;
3081			uio_uio_resid_add(uiop, -(dp->d_reclen));
3082			uiop->uio_offset += dp->d_reclen;
3083			uio_iov_base_add(uiop, dp->d_reclen);
3084			uio_iov_len_add(uiop, -(dp->d_reclen));
3085		}
3086		NFSREADDIRPLUS_ATTRBIT(&attrbits);
3087		if (gotmnton)
3088			NFSSETBIT_ATTRBIT(&attrbits,
3089			    NFSATTRBIT_MOUNTEDONFILEID);
3090	}
3091
3092	/*
3093	 * Loop around doing readdir rpc's of size nm_readdirsize.
3094	 * The stopping criteria is EOF or buffer full.
3095	 */
3096	while (more_dirs && bigenough) {
3097		*attrflagp = 0;
3098		NFSCL_REQSTART(nd, NFSPROC_READDIRPLUS, vp);
3099 		NFSM_BUILD(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
3100		*tl++ = cookie.lval[0];
3101		*tl++ = cookie.lval[1];
3102		if (cookie.qval == 0) {
3103			*tl++ = 0;
3104			*tl++ = 0;
3105		} else {
3106			NFSLOCKNODE(dnp);
3107			*tl++ = dnp->n_cookieverf.nfsuquad[0];
3108			*tl++ = dnp->n_cookieverf.nfsuquad[1];
3109			NFSUNLOCKNODE(dnp);
3110		}
3111		*tl++ = txdr_unsigned(nmp->nm_readdirsize);
3112		*tl = txdr_unsigned(nmp->nm_readdirsize);
3113		if (nd->nd_flag & ND_NFSV4) {
3114			(void) nfsrv_putattrbit(nd, &attrbits);
3115			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3116			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3117			(void) nfsrv_putattrbit(nd, &dattrbits);
3118		}
3119		error = nfscl_request(nd, vp, p, cred, stuff);
3120		if (error)
3121			return (error);
3122		if (nd->nd_flag & ND_NFSV3)
3123			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3124		if (nd->nd_repstat || error) {
3125			if (!error)
3126				error = nd->nd_repstat;
3127			goto nfsmout;
3128		}
3129		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3130		NFSLOCKNODE(dnp);
3131		dnp->n_cookieverf.nfsuquad[0] = *tl++;
3132		dnp->n_cookieverf.nfsuquad[1] = *tl++;
3133		NFSUNLOCKNODE(dnp);
3134		more_dirs = fxdr_unsigned(int, *tl);
3135		if (!more_dirs)
3136			tryformoredirs = 0;
3137
3138		/* loop thru the dir entries, doctoring them to 4bsd form */
3139		while (more_dirs && bigenough) {
3140			NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3141			if (nd->nd_flag & ND_NFSV4) {
3142				ncookie.lval[0] = *tl++;
3143				ncookie.lval[1] = *tl++;
3144			} else {
3145				fileno = fxdr_unsigned(long, *++tl);
3146				tl++;
3147			}
3148			len = fxdr_unsigned(int, *tl);
3149			if (len <= 0 || len > NFS_MAXNAMLEN) {
3150				error = EBADRPC;
3151				goto nfsmout;
3152			}
3153			tlen = NFSM_RNDUP(len);
3154			if (tlen == len)
3155				tlen += 4;  /* To ensure null termination */
3156			left = DIRBLKSIZ - blksiz;
3157			if ((tlen + DIRHDSIZ + NFSX_HYPER) > left) {
3158				dp->d_reclen += left;
3159				uio_iov_base_add(uiop, left);
3160				uio_iov_len_add(uiop, -(left));
3161				uio_uio_resid_add(uiop, -(left));
3162				uiop->uio_offset += left;
3163				blksiz = 0;
3164			}
3165			if ((tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
3166				bigenough = 0;
3167			if (bigenough) {
3168				dp = (struct dirent *)uio_iov_base(uiop);
3169				dp->d_namlen = len;
3170				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
3171				dp->d_type = DT_UNKNOWN;
3172				blksiz += dp->d_reclen;
3173				if (blksiz == DIRBLKSIZ)
3174					blksiz = 0;
3175				uio_uio_resid_add(uiop, -(DIRHDSIZ));
3176				uiop->uio_offset += DIRHDSIZ;
3177				uio_iov_base_add(uiop, DIRHDSIZ);
3178				uio_iov_len_add(uiop, -(DIRHDSIZ));
3179				cnp->cn_nameptr = uio_iov_base(uiop);
3180				cnp->cn_namelen = len;
3181				NFSCNHASHZERO(cnp);
3182				error = nfsm_mbufuio(nd, uiop, len);
3183				if (error)
3184					goto nfsmout;
3185				cp = uio_iov_base(uiop);
3186				tlen -= len;
3187				*cp = '\0';
3188				cp += tlen;	/* points to cookie storage */
3189				tl2 = (u_int32_t *)cp;
3190				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
3191				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
3192				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
3193				uiop->uio_offset += (tlen + NFSX_HYPER);
3194			} else {
3195				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3196				if (error)
3197					goto nfsmout;
3198			}
3199			nfhp = NULL;
3200			if (nd->nd_flag & ND_NFSV3) {
3201				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3202				ncookie.lval[0] = *tl++;
3203				ncookie.lval[1] = *tl++;
3204				attrflag = fxdr_unsigned(int, *tl);
3205				if (attrflag) {
3206				  error = nfsm_loadattr(nd, &nfsva);
3207				  if (error)
3208					goto nfsmout;
3209				}
3210				NFSM_DISSECT(tl,u_int32_t *,NFSX_UNSIGNED);
3211				if (*tl) {
3212					error = nfsm_getfh(nd, &nfhp);
3213					if (error)
3214					    goto nfsmout;
3215				}
3216				if (!attrflag && nfhp != NULL) {
3217					FREE((caddr_t)nfhp, M_NFSFH);
3218					nfhp = NULL;
3219				}
3220			} else {
3221				rderr = 0;
3222				nfsva.na_mntonfileno = 0xffffffff;
3223				error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
3224				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3225				    NULL, NULL, &rderr, p, cred);
3226				if (error)
3227					goto nfsmout;
3228			}
3229
3230			if (bigenough) {
3231			    if (nd->nd_flag & ND_NFSV4) {
3232				if (rderr) {
3233				    dp->d_fileno = 0;
3234				} else if (gotmnton) {
3235				    if (nfsva.na_mntonfileno != 0xffffffff)
3236					dp->d_fileno = nfsva.na_mntonfileno;
3237				    else
3238					dp->d_fileno = nfsva.na_fileid;
3239				} else if (nfsva.na_filesid[0] ==
3240				    dnp->n_vattr.na_filesid[0] &&
3241				    nfsva.na_filesid[1] ==
3242				    dnp->n_vattr.na_filesid[1]) {
3243				    dp->d_fileno = nfsva.na_fileid;
3244				} else {
3245				    do {
3246					fakefileno--;
3247				    } while (fakefileno ==
3248					nfsva.na_fileid);
3249				    dp->d_fileno = fakefileno;
3250				}
3251			    } else {
3252				dp->d_fileno = fileno;
3253			    }
3254			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
3255				ncookie.lval[0];
3256			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
3257				ncookie.lval[1];
3258
3259			    if (nfhp != NULL) {
3260				if (NFSRV_CMPFH(nfhp->nfh_fh, nfhp->nfh_len,
3261				    dnp->n_fhp->nfh_fh, dnp->n_fhp->nfh_len)) {
3262				    VREF(vp);
3263				    newvp = vp;
3264				    unlocknewvp = 0;
3265				    FREE((caddr_t)nfhp, M_NFSFH);
3266				    np = dnp;
3267				} else {
3268				    error = nfscl_nget(vnode_mount(vp), vp,
3269				      nfhp, cnp, p, &np, NULL);
3270				    if (!error) {
3271					newvp = NFSTOV(np);
3272					unlocknewvp = 1;
3273				    }
3274				}
3275				nfhp = NULL;
3276				if (newvp != NULLVP) {
3277				    error = nfscl_loadattrcache(&newvp,
3278					&nfsva, NULL, NULL, 0, 0);
3279				    if (error) {
3280					if (unlocknewvp)
3281					    vput(newvp);
3282					else
3283					    vrele(newvp);
3284					goto nfsmout;
3285				    }
3286				    dp->d_type =
3287					vtonfs_dtype(np->n_vattr.na_type);
3288				    ndp->ni_vp = newvp;
3289				    NFSCNHASH(cnp, HASHINIT);
3290				    if (cnp->cn_namelen <= NCHNAMLEN) {
3291					np->n_ctime =
3292					  np->n_vattr.na_ctime.tv_sec;
3293					cache_enter(ndp->ni_dvp,ndp->ni_vp,cnp);
3294				    }
3295				    if (unlocknewvp)
3296					vput(newvp);
3297				    else
3298					vrele(newvp);
3299				    newvp = NULLVP;
3300				}
3301			    }
3302			} else if (nfhp != NULL) {
3303			    FREE((caddr_t)nfhp, M_NFSFH);
3304			}
3305			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3306			more_dirs = fxdr_unsigned(int, *tl);
3307		}
3308		/*
3309		 * If at end of rpc data, get the eof boolean
3310		 */
3311		if (!more_dirs) {
3312			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3313			eof = fxdr_unsigned(int, *tl);
3314			if (tryformoredirs)
3315				more_dirs = !eof;
3316			if (nd->nd_flag & ND_NFSV4) {
3317				error = nfscl_postop_attr(nd, nap, attrflagp,
3318				    stuff);
3319				if (error)
3320					goto nfsmout;
3321			}
3322		}
3323		mbuf_freem(nd->nd_mrep);
3324		nd->nd_mrep = NULL;
3325	}
3326	/*
3327	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3328	 * by increasing d_reclen for the last record.
3329	 */
3330	if (blksiz > 0) {
3331		left = DIRBLKSIZ - blksiz;
3332		dp->d_reclen += left;
3333		uio_iov_base_add(uiop, left);
3334		uio_iov_len_add(uiop, -(left));
3335		uio_uio_resid_add(uiop, -(left));
3336		uiop->uio_offset += left;
3337	}
3338
3339	/*
3340	 * If returning no data, assume end of file.
3341	 * If not bigenough, return not end of file, since you aren't
3342	 *    returning all the data
3343	 * Otherwise, return the eof flag from the server.
3344	 */
3345	if (eofp != NULL) {
3346		if (tresid == uio_uio_resid(uiop))
3347			*eofp = 1;
3348		else if (!bigenough)
3349			*eofp = 0;
3350		else
3351			*eofp = eof;
3352	}
3353
3354	/*
3355	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
3356	 */
3357	while (uio_uio_resid(uiop) > 0 && uio_uio_resid(uiop) != tresid) {
3358		dp = (struct dirent *)uio_iov_base(uiop);
3359		dp->d_type = DT_UNKNOWN;
3360		dp->d_fileno = 0;
3361		dp->d_namlen = 0;
3362		dp->d_name[0] = '\0';
3363		tl = (u_int32_t *)&dp->d_name[4];
3364		*tl++ = cookie.lval[0];
3365		*tl = cookie.lval[1];
3366		dp->d_reclen = DIRBLKSIZ;
3367		uio_iov_base_add(uiop, DIRBLKSIZ);
3368		uio_iov_len_add(uiop, -(DIRBLKSIZ));
3369		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
3370		uiop->uio_offset += DIRBLKSIZ;
3371	}
3372
3373nfsmout:
3374	if (nd->nd_mrep != NULL)
3375		mbuf_freem(nd->nd_mrep);
3376	return (error);
3377}
3378#endif	/* !APPLE */
3379
3380/*
3381 * Nfs commit rpc
3382 */
3383APPLESTATIC int
3384nfsrpc_commit(vnode_t vp, u_quad_t offset, int cnt, struct ucred *cred,
3385    NFSPROC_T *p, u_char *verfp, struct nfsvattr *nap, int *attrflagp,
3386    void *stuff)
3387{
3388	u_int32_t *tl;
3389	struct nfsrv_descript nfsd, *nd = &nfsd;
3390	nfsattrbit_t attrbits;
3391	int error;
3392
3393	*attrflagp = 0;
3394	NFSCL_REQSTART(nd, NFSPROC_COMMIT, vp);
3395	NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3396	txdr_hyper(offset, tl);
3397	tl += 2;
3398	*tl = txdr_unsigned(cnt);
3399	if (nd->nd_flag & ND_NFSV4) {
3400		/*
3401		 * And do a Getattr op.
3402		 */
3403		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3404		*tl = txdr_unsigned(NFSV4OP_GETATTR);
3405		NFSGETATTR_ATTRBIT(&attrbits);
3406		(void) nfsrv_putattrbit(nd, &attrbits);
3407	}
3408	error = nfscl_request(nd, vp, p, cred, stuff);
3409	if (error)
3410		return (error);
3411	error = nfscl_wcc_data(nd, vp, nap, attrflagp, NULL, stuff);
3412	if (!error && !nd->nd_repstat) {
3413		NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
3414		NFSBCOPY((caddr_t)tl, verfp, NFSX_VERF);
3415		if (nd->nd_flag & ND_NFSV4)
3416			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3417	}
3418nfsmout:
3419	if (!error && nd->nd_repstat)
3420		error = nd->nd_repstat;
3421	mbuf_freem(nd->nd_mrep);
3422	return (error);
3423}
3424
3425/*
3426 * NFS byte range lock rpc.
3427 * (Mostly just calls one of the three lower level RPC routines.)
3428 */
3429APPLESTATIC int
3430nfsrpc_advlock(vnode_t vp, off_t size, int op, struct flock *fl,
3431    int reclaim, struct ucred *cred, NFSPROC_T *p)
3432{
3433	struct nfscllockowner *lp;
3434	struct nfsclclient *clp;
3435	struct nfsfh *nfhp;
3436	struct nfsrv_descript nfsd, *nd = &nfsd;
3437	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3438	u_int64_t off, len;
3439	off_t start, end;
3440	u_int32_t clidrev = 0;
3441	int error = 0, newone = 0, expireret = 0, retrycnt, donelocally;
3442	int callcnt, dorpc;
3443
3444	/*
3445	 * Convert the flock structure into a start and end and do POSIX
3446	 * bounds checking.
3447	 */
3448	switch (fl->l_whence) {
3449	case SEEK_SET:
3450	case SEEK_CUR:
3451		/*
3452		 * Caller is responsible for adding any necessary offset
3453		 * when SEEK_CUR is used.
3454		 */
3455		start = fl->l_start;
3456		off = fl->l_start;
3457		break;
3458	case SEEK_END:
3459		start = size + fl->l_start;
3460		off = size + fl->l_start;
3461		break;
3462	default:
3463		return (EINVAL);
3464	};
3465	if (start < 0)
3466		return (EINVAL);
3467	if (fl->l_len != 0) {
3468		end = start + fl->l_len - 1;
3469		if (end < start)
3470			return (EINVAL);
3471	}
3472
3473	len = fl->l_len;
3474	if (len == 0)
3475		len = NFS64BITSSET;
3476	retrycnt = 0;
3477	do {
3478	    nd->nd_repstat = 0;
3479	    if (op == F_GETLK) {
3480		error = nfscl_getcl(vp, cred, p, &clp);
3481		if (error)
3482			return (error);
3483		error = nfscl_lockt(vp, clp, off, len, fl, p);
3484		if (!error) {
3485			clidrev = clp->nfsc_clientidrev;
3486			error = nfsrpc_lockt(nd, vp, clp, off, len, fl, cred,
3487			    p);
3488		} else if (error == -1) {
3489			error = 0;
3490		}
3491		nfscl_clientrelease(clp);
3492	    } else if (op == F_UNLCK && fl->l_type == F_UNLCK) {
3493		/*
3494		 * We must loop around for all lockowner cases.
3495		 */
3496		callcnt = 0;
3497		error = nfscl_getcl(vp, cred, p, &clp);
3498		if (error)
3499			return (error);
3500		do {
3501		    error = nfscl_relbytelock(vp, off, len, cred, p, callcnt,
3502			clp, &lp, &dorpc);
3503		    /*
3504		     * If it returns a NULL lp, we're done.
3505		     */
3506		    if (lp == NULL) {
3507			if (callcnt == 0)
3508			    nfscl_clientrelease(clp);
3509			else
3510			    nfscl_releasealllocks(clp, vp, p);
3511			return (error);
3512		    }
3513		    if (nmp->nm_clp != NULL)
3514			clidrev = nmp->nm_clp->nfsc_clientidrev;
3515		    else
3516			clidrev = 0;
3517		    /*
3518		     * If the server doesn't support Posix lock semantics,
3519		     * only allow locks on the entire file, since it won't
3520		     * handle overlapping byte ranges.
3521		     * There might still be a problem when a lock
3522		     * upgrade/downgrade (read<->write) occurs, since the
3523		     * server "might" expect an unlock first?
3524		     */
3525		    if (dorpc && (lp->nfsl_open->nfso_posixlock ||
3526			(off == 0 && len == NFS64BITSSET))) {
3527			/*
3528			 * Since the lock records will go away, we must
3529			 * wait for grace and delay here.
3530			 */
3531			do {
3532			    error = nfsrpc_locku(nd, nmp, lp, off, len,
3533				NFSV4LOCKT_READ, cred, p, 0);
3534			    if ((nd->nd_repstat == NFSERR_GRACE ||
3535				 nd->nd_repstat == NFSERR_DELAY) &&
3536				error == 0)
3537				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3538				    "nfs_advlock");
3539			} while ((nd->nd_repstat == NFSERR_GRACE ||
3540			    nd->nd_repstat == NFSERR_DELAY) && error == 0);
3541		    }
3542		    callcnt++;
3543		} while (error == 0 && nd->nd_repstat == 0);
3544		nfscl_releasealllocks(clp, vp, p);
3545	    } else if (op == F_SETLK) {
3546		error = nfscl_getbytelock(vp, off, len, fl->l_type, cred, p,
3547		    NULL, 0, NULL, NULL, &lp, &newone, &donelocally);
3548		if (error || donelocally) {
3549			return (error);
3550		}
3551		if (nmp->nm_clp != NULL)
3552			clidrev = nmp->nm_clp->nfsc_clientidrev;
3553		else
3554			clidrev = 0;
3555		nfhp = VTONFS(vp)->n_fhp;
3556		if (!lp->nfsl_open->nfso_posixlock &&
3557		    (off != 0 || len != NFS64BITSSET)) {
3558			error = EINVAL;
3559		} else {
3560			error = nfsrpc_lock(nd, nmp, vp, nfhp->nfh_fh,
3561			    nfhp->nfh_len, lp, newone, reclaim, off,
3562			    len, fl->l_type, cred, p, 0);
3563		}
3564		if (!error)
3565			error = nd->nd_repstat;
3566		nfscl_lockrelease(lp, error, newone);
3567	    } else {
3568		error = EINVAL;
3569	    }
3570	    if (!error)
3571	        error = nd->nd_repstat;
3572	    if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
3573		error == NFSERR_STALEDONTRECOVER ||
3574		error == NFSERR_STALECLIENTID || error == NFSERR_DELAY) {
3575		(void) nfs_catnap(PZERO, error, "nfs_advlock");
3576	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
3577		&& clidrev != 0) {
3578		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
3579		retrycnt++;
3580	    }
3581	} while (error == NFSERR_GRACE ||
3582	    error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
3583	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_STALESTATEID ||
3584	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
3585	     expireret == 0 && clidrev != 0 && retrycnt < 4));
3586	if (error && retrycnt >= 4)
3587		error = EIO;
3588	return (error);
3589}
3590
3591/*
3592 * The lower level routine for the LockT case.
3593 */
3594APPLESTATIC int
3595nfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
3596    struct nfsclclient *clp, u_int64_t off, u_int64_t len, struct flock *fl,
3597    struct ucred *cred, NFSPROC_T *p)
3598{
3599	u_int32_t *tl;
3600	int error, type, size;
3601	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3602
3603	NFSCL_REQSTART(nd, NFSPROC_LOCKT, vp);
3604	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3605	if (fl->l_type == F_RDLCK)
3606		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3607	else
3608		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3609	txdr_hyper(off, tl);
3610	tl += 2;
3611	txdr_hyper(len, tl);
3612	tl += 2;
3613	*tl++ = clp->nfsc_clientid.lval[0];
3614	*tl = clp->nfsc_clientid.lval[1];
3615	nfscl_filllockowner(p, own);
3616	(void) nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN);
3617	error = nfscl_request(nd, vp, p, cred, NULL);
3618	if (error)
3619		return (error);
3620	if (nd->nd_repstat == 0) {
3621		fl->l_type = F_UNLCK;
3622	} else if (nd->nd_repstat == NFSERR_DENIED) {
3623		nd->nd_repstat = 0;
3624		fl->l_whence = SEEK_SET;
3625		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3626		fl->l_start = fxdr_hyper(tl);
3627		tl += 2;
3628		len = fxdr_hyper(tl);
3629		tl += 2;
3630		if (len == NFS64BITSSET)
3631			fl->l_len = 0;
3632		else
3633			fl->l_len = len;
3634		type = fxdr_unsigned(int, *tl++);
3635		if (type == NFSV4LOCKT_WRITE)
3636			fl->l_type = F_WRLCK;
3637		else
3638			fl->l_type = F_RDLCK;
3639		/*
3640		 * XXX For now, I have no idea what to do with the
3641		 * conflicting lock_owner, so I'll just set the pid == 0
3642		 * and skip over the lock_owner.
3643		 */
3644		fl->l_pid = (pid_t)0;
3645		tl += 2;
3646		size = fxdr_unsigned(int, *tl);
3647		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3648			error = EBADRPC;
3649		if (!error)
3650			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3651	} else if (nd->nd_repstat == NFSERR_STALECLIENTID)
3652		nfscl_initiate_recovery(clp);
3653nfsmout:
3654	mbuf_freem(nd->nd_mrep);
3655	return (error);
3656}
3657
3658/*
3659 * Lower level function that performs the LockU RPC.
3660 */
3661static int
3662nfsrpc_locku(struct nfsrv_descript *nd, struct nfsmount *nmp,
3663    struct nfscllockowner *lp, u_int64_t off, u_int64_t len,
3664    u_int32_t type, struct ucred *cred, NFSPROC_T *p, int syscred)
3665{
3666	u_int32_t *tl;
3667	int error;
3668
3669	nfscl_reqstart(nd, NFSPROC_LOCKU, nmp, lp->nfsl_open->nfso_fh,
3670	    lp->nfsl_open->nfso_fhlen, NULL);
3671	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
3672	*tl++ = txdr_unsigned(type);
3673	*tl = txdr_unsigned(lp->nfsl_seqid);
3674	if (nfstest_outofseq &&
3675	    (arc4random() % nfstest_outofseq) == 0)
3676		*tl = txdr_unsigned(lp->nfsl_seqid + 1);
3677	tl++;
3678	*tl++ = lp->nfsl_stateid.seqid;
3679	*tl++ = lp->nfsl_stateid.other[0];
3680	*tl++ = lp->nfsl_stateid.other[1];
3681	*tl++ = lp->nfsl_stateid.other[2];
3682	txdr_hyper(off, tl);
3683	tl += 2;
3684	txdr_hyper(len, tl);
3685	if (syscred)
3686		nd->nd_flag |= ND_USEGSSNAME;
3687	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
3688	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
3689	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
3690	if (error)
3691		return (error);
3692	if (nd->nd_repstat == 0) {
3693		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3694		lp->nfsl_stateid.seqid = *tl++;
3695		lp->nfsl_stateid.other[0] = *tl++;
3696		lp->nfsl_stateid.other[1] = *tl++;
3697		lp->nfsl_stateid.other[2] = *tl;
3698	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
3699		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
3700nfsmout:
3701	mbuf_freem(nd->nd_mrep);
3702	return (error);
3703}
3704
3705/*
3706 * The actual Lock RPC.
3707 */
3708APPLESTATIC int
3709nfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
3710    u_int8_t *nfhp, int fhlen, struct nfscllockowner *lp, int newone,
3711    int reclaim, u_int64_t off, u_int64_t len, short type, struct ucred *cred,
3712    NFSPROC_T *p, int syscred)
3713{
3714	u_int32_t *tl;
3715	int error, size;
3716
3717	nfscl_reqstart(nd, NFSPROC_LOCK, nmp, nfhp, fhlen, NULL);
3718	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3719	if (type == F_RDLCK)
3720		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3721	else
3722		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3723	*tl++ = txdr_unsigned(reclaim);
3724	txdr_hyper(off, tl);
3725	tl += 2;
3726	txdr_hyper(len, tl);
3727	tl += 2;
3728	if (newone) {
3729	    *tl = newnfs_true;
3730	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
3731		2 * NFSX_UNSIGNED + NFSX_HYPER);
3732	    *tl++ = txdr_unsigned(lp->nfsl_open->nfso_own->nfsow_seqid);
3733	    *tl++ = lp->nfsl_open->nfso_stateid.seqid;
3734	    *tl++ = lp->nfsl_open->nfso_stateid.other[0];
3735	    *tl++ = lp->nfsl_open->nfso_stateid.other[1];
3736	    *tl++ = lp->nfsl_open->nfso_stateid.other[2];
3737	    *tl++ = txdr_unsigned(lp->nfsl_seqid);
3738	    *tl++ = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[0];
3739	    *tl = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[1];
3740	    (void) nfsm_strtom(nd, lp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
3741	} else {
3742	    *tl = newnfs_false;
3743	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3744	    *tl++ = lp->nfsl_stateid.seqid;
3745	    *tl++ = lp->nfsl_stateid.other[0];
3746	    *tl++ = lp->nfsl_stateid.other[1];
3747	    *tl++ = lp->nfsl_stateid.other[2];
3748	    *tl = txdr_unsigned(lp->nfsl_seqid);
3749	    if (nfstest_outofseq &&
3750		(arc4random() % nfstest_outofseq) == 0)
3751		    *tl = txdr_unsigned(lp->nfsl_seqid + 1);
3752	}
3753	if (syscred)
3754		nd->nd_flag |= ND_USEGSSNAME;
3755	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
3756	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
3757	if (error)
3758		return (error);
3759	if (newone)
3760	    NFSCL_INCRSEQID(lp->nfsl_open->nfso_own->nfsow_seqid, nd);
3761	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
3762	if (nd->nd_repstat == 0) {
3763		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3764		lp->nfsl_stateid.seqid = *tl++;
3765		lp->nfsl_stateid.other[0] = *tl++;
3766		lp->nfsl_stateid.other[1] = *tl++;
3767		lp->nfsl_stateid.other[2] = *tl;
3768	} else if (nd->nd_repstat == NFSERR_DENIED) {
3769		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3770		size = fxdr_unsigned(int, *(tl + 7));
3771		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3772			error = EBADRPC;
3773		if (!error)
3774			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3775	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
3776		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
3777nfsmout:
3778	mbuf_freem(nd->nd_mrep);
3779	return (error);
3780}
3781
3782/*
3783 * nfs statfs rpc
3784 * (always called with the vp for the mount point)
3785 */
3786APPLESTATIC int
3787nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
3788    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3789    void *stuff)
3790{
3791	u_int32_t *tl = NULL;
3792	struct nfsrv_descript nfsd, *nd = &nfsd;
3793	struct nfsmount *nmp;
3794	nfsattrbit_t attrbits;
3795	int error;
3796
3797	*attrflagp = 0;
3798	nmp = VFSTONFS(vnode_mount(vp));
3799	if (NFSHASNFSV4(nmp)) {
3800		/*
3801		 * For V4, you actually do a getattr.
3802		 */
3803		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
3804		NFSSTATFS_GETATTRBIT(&attrbits);
3805		(void) nfsrv_putattrbit(nd, &attrbits);
3806		nd->nd_flag |= ND_USEGSSNAME;
3807		error = nfscl_request(nd, vp, p, cred, stuff);
3808		if (error)
3809			return (error);
3810		if (nd->nd_repstat == 0) {
3811			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
3812			    NULL, NULL, sbp, fsp, NULL, 0, NULL, NULL, NULL, p,
3813			    cred);
3814			if (!error) {
3815				nmp->nm_fsid[0] = nap->na_filesid[0];
3816				nmp->nm_fsid[1] = nap->na_filesid[1];
3817				NFSSETHASSETFSID(nmp);
3818				*attrflagp = 1;
3819			}
3820		} else {
3821			error = nd->nd_repstat;
3822		}
3823		if (error)
3824			goto nfsmout;
3825	} else {
3826		NFSCL_REQSTART(nd, NFSPROC_FSSTAT, vp);
3827		error = nfscl_request(nd, vp, p, cred, stuff);
3828		if (error)
3829			return (error);
3830		if (nd->nd_flag & ND_NFSV3) {
3831			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3832			if (error)
3833				goto nfsmout;
3834		}
3835		if (nd->nd_repstat) {
3836			error = nd->nd_repstat;
3837			goto nfsmout;
3838		}
3839		NFSM_DISSECT(tl, u_int32_t *,
3840		    NFSX_STATFS(nd->nd_flag & ND_NFSV3));
3841	}
3842	if (NFSHASNFSV3(nmp)) {
3843		sbp->sf_tbytes = fxdr_hyper(tl); tl += 2;
3844		sbp->sf_fbytes = fxdr_hyper(tl); tl += 2;
3845		sbp->sf_abytes = fxdr_hyper(tl); tl += 2;
3846		sbp->sf_tfiles = fxdr_hyper(tl); tl += 2;
3847		sbp->sf_ffiles = fxdr_hyper(tl); tl += 2;
3848		sbp->sf_afiles = fxdr_hyper(tl); tl += 2;
3849		sbp->sf_invarsec = fxdr_unsigned(u_int32_t, *tl);
3850	} else if (NFSHASNFSV4(nmp) == 0) {
3851		sbp->sf_tsize = fxdr_unsigned(u_int32_t, *tl++);
3852		sbp->sf_bsize = fxdr_unsigned(u_int32_t, *tl++);
3853		sbp->sf_blocks = fxdr_unsigned(u_int32_t, *tl++);
3854		sbp->sf_bfree = fxdr_unsigned(u_int32_t, *tl++);
3855		sbp->sf_bavail = fxdr_unsigned(u_int32_t, *tl);
3856	}
3857nfsmout:
3858	mbuf_freem(nd->nd_mrep);
3859	return (error);
3860}
3861
3862/*
3863 * nfs pathconf rpc
3864 */
3865APPLESTATIC int
3866nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc,
3867    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3868    void *stuff)
3869{
3870	struct nfsrv_descript nfsd, *nd = &nfsd;
3871	struct nfsmount *nmp;
3872	u_int32_t *tl;
3873	nfsattrbit_t attrbits;
3874	int error;
3875
3876	*attrflagp = 0;
3877	nmp = VFSTONFS(vnode_mount(vp));
3878	if (NFSHASNFSV4(nmp)) {
3879		/*
3880		 * For V4, you actually do a getattr.
3881		 */
3882		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
3883		NFSPATHCONF_GETATTRBIT(&attrbits);
3884		(void) nfsrv_putattrbit(nd, &attrbits);
3885		nd->nd_flag |= ND_USEGSSNAME;
3886		error = nfscl_request(nd, vp, p, cred, stuff);
3887		if (error)
3888			return (error);
3889		if (nd->nd_repstat == 0) {
3890			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
3891			    pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p,
3892			    cred);
3893			if (!error)
3894				*attrflagp = 1;
3895		} else {
3896			error = nd->nd_repstat;
3897		}
3898	} else {
3899		NFSCL_REQSTART(nd, NFSPROC_PATHCONF, vp);
3900		error = nfscl_request(nd, vp, p, cred, stuff);
3901		if (error)
3902			return (error);
3903		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3904		if (nd->nd_repstat && !error)
3905			error = nd->nd_repstat;
3906		if (!error) {
3907			NFSM_DISSECT(tl, u_int32_t *, NFSX_V3PATHCONF);
3908			pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl++);
3909			pc->pc_namemax = fxdr_unsigned(u_int32_t, *tl++);
3910			pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl++);
3911			pc->pc_chownrestricted =
3912			    fxdr_unsigned(u_int32_t, *tl++);
3913			pc->pc_caseinsensitive =
3914			    fxdr_unsigned(u_int32_t, *tl++);
3915			pc->pc_casepreserving = fxdr_unsigned(u_int32_t, *tl);
3916		}
3917	}
3918nfsmout:
3919	mbuf_freem(nd->nd_mrep);
3920	return (error);
3921}
3922
3923/*
3924 * nfs version 3 fsinfo rpc call
3925 */
3926APPLESTATIC int
3927nfsrpc_fsinfo(vnode_t vp, struct nfsfsinfo *fsp, struct ucred *cred,
3928    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
3929{
3930	u_int32_t *tl;
3931	struct nfsrv_descript nfsd, *nd = &nfsd;
3932	int error;
3933
3934	*attrflagp = 0;
3935	NFSCL_REQSTART(nd, NFSPROC_FSINFO, vp);
3936	error = nfscl_request(nd, vp, p, cred, stuff);
3937	if (error)
3938		return (error);
3939	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3940	if (nd->nd_repstat && !error)
3941		error = nd->nd_repstat;
3942	if (!error) {
3943		NFSM_DISSECT(tl, u_int32_t *, NFSX_V3FSINFO);
3944		fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *tl++);
3945		fsp->fs_rtpref = fxdr_unsigned(u_int32_t, *tl++);
3946		fsp->fs_rtmult = fxdr_unsigned(u_int32_t, *tl++);
3947		fsp->fs_wtmax = fxdr_unsigned(u_int32_t, *tl++);
3948		fsp->fs_wtpref = fxdr_unsigned(u_int32_t, *tl++);
3949		fsp->fs_wtmult = fxdr_unsigned(u_int32_t, *tl++);
3950		fsp->fs_dtpref = fxdr_unsigned(u_int32_t, *tl++);
3951		fsp->fs_maxfilesize = fxdr_hyper(tl);
3952		tl += 2;
3953		fxdr_nfsv3time(tl, &fsp->fs_timedelta);
3954		tl += 2;
3955		fsp->fs_properties = fxdr_unsigned(u_int32_t, *tl);
3956	}
3957nfsmout:
3958	mbuf_freem(nd->nd_mrep);
3959	return (error);
3960}
3961
3962/*
3963 * This function performs the Renew RPC.
3964 */
3965APPLESTATIC int
3966nfsrpc_renew(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
3967{
3968	u_int32_t *tl;
3969	struct nfsrv_descript nfsd;
3970	struct nfsrv_descript *nd = &nfsd;
3971	struct nfsmount *nmp;
3972	int error;
3973
3974	nmp = clp->nfsc_nmp;
3975	if (nmp == NULL)
3976		return (0);
3977	nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL);
3978	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3979	*tl++ = clp->nfsc_clientid.lval[0];
3980	*tl = clp->nfsc_clientid.lval[1];
3981	nd->nd_flag |= ND_USEGSSNAME;
3982	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
3983		NFS_PROG, NFS_VER4, NULL, 1, NULL);
3984	if (error)
3985		return (error);
3986	error = nd->nd_repstat;
3987	mbuf_freem(nd->nd_mrep);
3988	return (error);
3989}
3990
3991/*
3992 * This function performs the Releaselockowner RPC.
3993 */
3994APPLESTATIC int
3995nfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
3996    struct ucred *cred, NFSPROC_T *p)
3997{
3998	struct nfsrv_descript nfsd, *nd = &nfsd;
3999	u_int32_t *tl;
4000	int error;
4001
4002	nfscl_reqstart(nd, NFSPROC_RELEASELCKOWN, nmp, NULL, 0, NULL);
4003	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4004	*tl++ = nmp->nm_clp->nfsc_clientid.lval[0];
4005	*tl = nmp->nm_clp->nfsc_clientid.lval[1];
4006	(void) nfsm_strtom(nd, lp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
4007	nd->nd_flag |= ND_USEGSSNAME;
4008	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4009	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
4010	if (error)
4011		return (error);
4012	error = nd->nd_repstat;
4013	mbuf_freem(nd->nd_mrep);
4014	return (error);
4015}
4016
4017/*
4018 * This function performs the Compound to get the mount pt FH.
4019 */
4020APPLESTATIC int
4021nfsrpc_getdirpath(struct nfsmount *nmp, u_char *dirpath, struct ucred *cred,
4022    NFSPROC_T *p)
4023{
4024	u_int32_t *tl;
4025	struct nfsrv_descript nfsd;
4026	struct nfsrv_descript *nd = &nfsd;
4027	u_char *cp, *cp2;
4028	int error, cnt, len, setnil;
4029	u_int32_t *opcntp;
4030
4031	nfscl_reqstart(nd, NFSPROC_PUTROOTFH, nmp, NULL, 0, &opcntp);
4032	cp = dirpath;
4033	cnt = 0;
4034	do {
4035		setnil = 0;
4036		while (*cp == '/')
4037			cp++;
4038		cp2 = cp;
4039		while (*cp2 != '\0' && *cp2 != '/')
4040			cp2++;
4041		if (*cp2 == '/') {
4042			setnil = 1;
4043			*cp2 = '\0';
4044		}
4045		if (cp2 != cp) {
4046			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4047			*tl = txdr_unsigned(NFSV4OP_LOOKUP);
4048			nfsm_strtom(nd, cp, strlen(cp));
4049			cnt++;
4050		}
4051		if (setnil)
4052			*cp2++ = '/';
4053		cp = cp2;
4054	} while (*cp != '\0');
4055	*opcntp = txdr_unsigned(2 + cnt);
4056	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4057	*tl = txdr_unsigned(NFSV4OP_GETFH);
4058	nd->nd_flag |= ND_USEGSSNAME;
4059	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4060		NFS_PROG, NFS_VER4, NULL, 1, NULL);
4061	if (error)
4062		return (error);
4063	if (nd->nd_repstat == 0) {
4064		NFSM_DISSECT(tl, u_int32_t *, (3 + 2 * cnt) * NFSX_UNSIGNED);
4065		tl += (2 + 2 * cnt);
4066		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
4067			len > NFSX_FHMAX) {
4068			nd->nd_repstat = NFSERR_BADXDR;
4069		} else {
4070			nd->nd_repstat = nfsrv_mtostr(nd, nmp->nm_fh, len);
4071			if (nd->nd_repstat == 0)
4072				nmp->nm_fhsize = len;
4073		}
4074	}
4075	error = nd->nd_repstat;
4076nfsmout:
4077	mbuf_freem(nd->nd_mrep);
4078	return (error);
4079}
4080
4081/*
4082 * This function performs the Delegreturn RPC.
4083 */
4084APPLESTATIC int
4085nfsrpc_delegreturn(struct nfscldeleg *dp, struct ucred *cred,
4086    struct nfsmount *nmp, NFSPROC_T *p, int syscred)
4087{
4088	u_int32_t *tl;
4089	struct nfsrv_descript nfsd;
4090	struct nfsrv_descript *nd = &nfsd;
4091	int error;
4092
4093	nfscl_reqstart(nd, NFSPROC_DELEGRETURN, nmp, dp->nfsdl_fh,
4094	    dp->nfsdl_fhlen, NULL);
4095	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
4096	*tl++ = dp->nfsdl_stateid.seqid;
4097	*tl++ = dp->nfsdl_stateid.other[0];
4098	*tl++ = dp->nfsdl_stateid.other[1];
4099	*tl = dp->nfsdl_stateid.other[2];
4100	if (syscred)
4101		nd->nd_flag |= ND_USEGSSNAME;
4102	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4103	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
4104	if (error)
4105		return (error);
4106	error = nd->nd_repstat;
4107	mbuf_freem(nd->nd_mrep);
4108	return (error);
4109}
4110
4111/*
4112 * nfs getacl call.
4113 */
4114APPLESTATIC int
4115nfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4116    struct acl *aclp, void *stuff)
4117{
4118	struct nfsrv_descript nfsd, *nd = &nfsd;
4119	int error;
4120	nfsattrbit_t attrbits;
4121	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4122
4123	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4124		return (EOPNOTSUPP);
4125	NFSCL_REQSTART(nd, NFSPROC_GETACL, vp);
4126	NFSZERO_ATTRBIT(&attrbits);
4127	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4128	(void) nfsrv_putattrbit(nd, &attrbits);
4129	error = nfscl_request(nd, vp, p, cred, stuff);
4130	if (error)
4131		return (error);
4132	if (!nd->nd_repstat)
4133		error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
4134		    NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, p, cred);
4135	else
4136		error = nd->nd_repstat;
4137	mbuf_freem(nd->nd_mrep);
4138	return (error);
4139}
4140
4141/*
4142 * nfs setacl call.
4143 */
4144APPLESTATIC int
4145nfsrpc_setacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4146    struct acl *aclp, void *stuff)
4147{
4148	int error;
4149	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4150
4151	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4152		return (EOPNOTSUPP);
4153	error = nfsrpc_setattr(vp, NULL, aclp, cred, p, NULL, NULL, stuff);
4154	return (error);
4155}
4156
4157/*
4158 * nfs setacl call.
4159 */
4160static int
4161nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4162    struct acl *aclp, nfsv4stateid_t *stateidp, void *stuff)
4163{
4164	struct nfsrv_descript nfsd, *nd = &nfsd;
4165	int error;
4166	nfsattrbit_t attrbits;
4167	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4168
4169	if (!NFSHASNFSV4(nmp))
4170		return (EOPNOTSUPP);
4171	NFSCL_REQSTART(nd, NFSPROC_SETACL, vp);
4172	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
4173	NFSZERO_ATTRBIT(&attrbits);
4174	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4175	(void) nfsv4_fillattr(nd, vp, aclp, NULL, NULL, 0, &attrbits,
4176	    NULL, NULL, 0, 0);
4177	error = nfscl_request(nd, vp, p, cred, stuff);
4178	if (error)
4179		return (error);
4180	/* Don't care about the pre/postop attributes */
4181	mbuf_freem(nd->nd_mrep);
4182	return (nd->nd_repstat);
4183}
4184