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