nfs_clrpcops.c revision 220807
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 220807 2011-04-18 23:35:16Z rmacklem $");
36
37/*
38 * Rpc op calls, generally called from the vnode op calls or through the
39 * buffer cache, for NFS v2, 3 and 4.
40 * These do not normally make any changes to vnode arguments or use
41 * structures that might change between the VFS variants. The returned
42 * arguments are all at the end, after the NFSPROC_T *p one.
43 */
44
45#ifndef APPLEKEXT
46#include <fs/nfs/nfsport.h>
47
48/*
49 * Global variables
50 */
51extern int nfs_numnfscbd;
52extern struct timeval nfsboottime;
53extern u_int32_t newnfs_false, newnfs_true;
54extern nfstype nfsv34_type[9];
55extern int nfsrv_useacl;
56extern char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
57NFSCLSTATEMUTEX;
58int nfstest_outofseq = 0;
59int nfscl_assumeposixlocks = 1;
60int nfscl_enablecallb = 0;
61short nfsv4_cbport = NFSV4_CBPORT;
62int nfstest_openallsetattr = 0;
63#endif	/* !APPLEKEXT */
64
65#define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
66
67static int nfsrpc_setattrrpc(vnode_t , struct vattr *, nfsv4stateid_t *,
68    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
69static int nfsrpc_readrpc(vnode_t , struct uio *, struct ucred *,
70    nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *, void *);
71static int nfsrpc_writerpc(vnode_t , struct uio *, int *, u_char *,
72    struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *,
73    void *);
74static int nfsrpc_createv23(vnode_t , char *, int, struct vattr *,
75    nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *,
76    struct nfsvattr *, struct nfsfh **, int *, int *, void *);
77static int nfsrpc_createv4(vnode_t , char *, int, struct vattr *,
78    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **, struct ucred *,
79    NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *,
80    int *, void *, int *);
81static int nfsrpc_locku(struct nfsrv_descript *, struct nfsmount *,
82    struct nfscllockowner *, u_int64_t, u_int64_t,
83    u_int32_t, struct ucred *, NFSPROC_T *, int);
84static int nfsrpc_setaclrpc(vnode_t, struct ucred *, NFSPROC_T *,
85    struct acl *, nfsv4stateid_t *, void *);
86
87/*
88 * nfs null call from vfs.
89 */
90APPLESTATIC int
91nfsrpc_null(vnode_t vp, struct ucred *cred, NFSPROC_T *p)
92{
93	int error;
94	struct nfsrv_descript nfsd, *nd = &nfsd;
95
96	NFSCL_REQSTART(nd, NFSPROC_NULL, vp);
97	error = nfscl_request(nd, vp, p, cred, NULL);
98	if (nd->nd_repstat && !error)
99		error = nd->nd_repstat;
100	mbuf_freem(nd->nd_mrep);
101	return (error);
102}
103
104/*
105 * nfs access rpc op.
106 * For nfs version 3 and 4, use the access rpc to check accessibility. If file
107 * modes are changed on the server, accesses might still fail later.
108 */
109APPLESTATIC int
110nfsrpc_access(vnode_t vp, int acmode, struct ucred *cred,
111    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
112{
113	int error;
114	u_int32_t mode, rmode;
115
116	if (acmode & VREAD)
117		mode = NFSACCESS_READ;
118	else
119		mode = 0;
120	if (vnode_vtype(vp) == VDIR) {
121		if (acmode & VWRITE)
122			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND |
123				 NFSACCESS_DELETE);
124		if (acmode & VEXEC)
125			mode |= NFSACCESS_LOOKUP;
126	} else {
127		if (acmode & VWRITE)
128			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
129		if (acmode & VEXEC)
130			mode |= NFSACCESS_EXECUTE;
131	}
132
133	/*
134	 * Now, just call nfsrpc_accessrpc() to do the actual RPC.
135	 */
136	error = nfsrpc_accessrpc(vp, mode, cred, p, nap, attrflagp, &rmode,
137	    NULL);
138
139	/*
140	 * The NFS V3 spec does not clarify whether or not
141	 * the returned access bits can be a superset of
142	 * the ones requested, so...
143	 */
144	if (!error && (rmode & mode) != mode)
145		error = EACCES;
146	return (error);
147}
148
149/*
150 * The actual rpc, separated out for Darwin.
151 */
152APPLESTATIC int
153nfsrpc_accessrpc(vnode_t vp, u_int32_t mode, struct ucred *cred,
154    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, u_int32_t *rmodep,
155    void *stuff)
156{
157	u_int32_t *tl;
158	u_int32_t supported, rmode;
159	int error;
160	struct nfsrv_descript nfsd, *nd = &nfsd;
161	nfsattrbit_t attrbits;
162
163	*attrflagp = 0;
164	supported = mode;
165	NFSCL_REQSTART(nd, NFSPROC_ACCESS, vp);
166	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
167	*tl = txdr_unsigned(mode);
168	if (nd->nd_flag & ND_NFSV4) {
169		/*
170		 * And do a Getattr op.
171		 */
172		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
173		*tl = txdr_unsigned(NFSV4OP_GETATTR);
174		NFSGETATTR_ATTRBIT(&attrbits);
175		(void) nfsrv_putattrbit(nd, &attrbits);
176	}
177	error = nfscl_request(nd, vp, p, cred, stuff);
178	if (error)
179		return (error);
180	if (nd->nd_flag & ND_NFSV3) {
181		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
182		if (error)
183			goto nfsmout;
184	}
185	if (!nd->nd_repstat) {
186		if (nd->nd_flag & ND_NFSV4) {
187			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
188			supported = fxdr_unsigned(u_int32_t, *tl++);
189		} else {
190			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
191		}
192		rmode = fxdr_unsigned(u_int32_t, *tl);
193		if (nd->nd_flag & ND_NFSV4)
194			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
195
196		/*
197		 * It's not obvious what should be done about
198		 * unsupported access modes. For now, be paranoid
199		 * and clear the unsupported ones.
200		 */
201		rmode &= supported;
202		*rmodep = rmode;
203	} else
204		error = nd->nd_repstat;
205nfsmout:
206	mbuf_freem(nd->nd_mrep);
207	return (error);
208}
209
210/*
211 * nfs open rpc
212 */
213APPLESTATIC int
214nfsrpc_open(vnode_t vp, int amode, struct ucred *cred, NFSPROC_T *p)
215{
216	struct nfsclopen *op;
217	struct nfscldeleg *dp;
218	struct nfsfh *nfhp;
219	struct nfsnode *np = VTONFS(vp);
220	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
221	u_int32_t mode, clidrev;
222	int ret, newone, error, expireret = 0, retrycnt;
223
224	/*
225	 * For NFSv4, Open Ops are only done on Regular Files.
226	 */
227	if (vnode_vtype(vp) != VREG)
228		return (0);
229	mode = 0;
230	if (amode & FREAD)
231		mode |= NFSV4OPEN_ACCESSREAD;
232	if (amode & FWRITE)
233		mode |= NFSV4OPEN_ACCESSWRITE;
234	nfhp = np->n_fhp;
235
236	retrycnt = 0;
237#ifdef notdef
238{ char name[100]; int namel;
239namel = (np->n_v4->n4_namelen < 100) ? np->n_v4->n4_namelen : 99;
240bcopy(NFS4NODENAME(np->n_v4), name, namel);
241name[namel] = '\0';
242printf("rpcopen p=0x%x name=%s",p->p_pid,name);
243if (nfhp->nfh_len > 0) printf(" fh=0x%x\n",nfhp->nfh_fh[12]);
244else printf(" fhl=0\n");
245}
246#endif
247	do {
248	    dp = NULL;
249	    error = nfscl_open(vp, nfhp->nfh_fh, nfhp->nfh_len, mode, 1,
250		cred, p, NULL, &op, &newone, &ret, 1);
251	    if (error) {
252		return (error);
253	    }
254	    if (nmp->nm_clp != NULL)
255		clidrev = nmp->nm_clp->nfsc_clientidrev;
256	    else
257		clidrev = 0;
258	    if (ret == NFSCLOPEN_DOOPEN) {
259		if (np->n_v4 != NULL) {
260			error = nfsrpc_openrpc(nmp, vp, np->n_v4->n4_data,
261			   np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
262			   np->n_fhp->nfh_len, mode, op,
263			   NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &dp,
264			   0, 0x0, cred, p, 0, 0);
265			if (dp != NULL) {
266#ifdef APPLE
267				OSBitAndAtomic((int32_t)~NDELEGMOD, (UInt32 *)&np->n_flag);
268#else
269				NFSLOCKNODE(np);
270				np->n_flag &= ~NDELEGMOD;
271				/*
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
1288	*attrflagp = 0;
1289	tsiz = uio_uio_resid(uiop);
1290	if (uiop->uio_offset + tsiz > 0xffffffff &&
1291	    !NFSHASNFSV3OR4(nmp))
1292		return (EFBIG);
1293	nd->nd_mrep = NULL;
1294	while (tsiz > 0) {
1295		*attrflagp = 0;
1296		len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
1297		NFSCL_REQSTART(nd, NFSPROC_READ, vp);
1298		if (nd->nd_flag & ND_NFSV4)
1299			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1300		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
1301		if (nd->nd_flag & ND_NFSV2) {
1302			*tl++ = txdr_unsigned(uiop->uio_offset);
1303			*tl++ = txdr_unsigned(len);
1304			*tl = 0;
1305		} else {
1306			txdr_hyper(uiop->uio_offset, tl);
1307			*(tl + 2) = txdr_unsigned(len);
1308		}
1309		/*
1310		 * Since I can't do a Getattr for NFSv4 for Write, there
1311		 * doesn't seem any point in doing one here, either.
1312		 * (See the comment in nfsrpc_writerpc() for more info.)
1313		 */
1314		error = nfscl_request(nd, vp, p, cred, stuff);
1315		if (error)
1316			return (error);
1317		if (nd->nd_flag & ND_NFSV3) {
1318			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1319		} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
1320			error = nfsm_loadattr(nd, nap);
1321			if (!error)
1322				*attrflagp = 1;
1323		}
1324		if (nd->nd_repstat || error) {
1325			if (!error)
1326				error = nd->nd_repstat;
1327			goto nfsmout;
1328		}
1329		if (nd->nd_flag & ND_NFSV3) {
1330			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1331			eof = fxdr_unsigned(int, *(tl + 1));
1332		} else if (nd->nd_flag & ND_NFSV4) {
1333			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1334			eof = fxdr_unsigned(int, *tl);
1335		}
1336		NFSM_STRSIZ(retlen, nmp->nm_rsize);
1337		error = nfsm_mbufuio(nd, uiop, retlen);
1338		if (error)
1339			goto nfsmout;
1340		mbuf_freem(nd->nd_mrep);
1341		nd->nd_mrep = NULL;
1342		tsiz -= retlen;
1343		if (!(nd->nd_flag & ND_NFSV2)) {
1344			if (eof || retlen == 0)
1345				tsiz = 0;
1346		} else if (retlen < len)
1347			tsiz = 0;
1348	}
1349	return (0);
1350nfsmout:
1351	if (nd->nd_mrep != NULL)
1352		mbuf_freem(nd->nd_mrep);
1353	return (error);
1354}
1355
1356/*
1357 * nfs write operation
1358 * When called_from_strategy != 0, it should return EIO for an error that
1359 * indicates recovery is in progress, so that the buffer will be left
1360 * dirty and be written back to the server later. If it loops around,
1361 * the recovery thread could get stuck waiting for the buffer and recovery
1362 * will then deadlock.
1363 */
1364APPLESTATIC int
1365nfsrpc_write(vnode_t vp, struct uio *uiop, int *iomode, u_char *verfp,
1366    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
1367    void *stuff, int called_from_strategy)
1368{
1369	int error, expireret = 0, retrycnt, nostateid;
1370	u_int32_t clidrev = 0;
1371	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1372	struct nfsnode *np = VTONFS(vp);
1373	struct ucred *newcred;
1374	struct nfsfh *nfhp = NULL;
1375	nfsv4stateid_t stateid;
1376	void *lckp;
1377
1378	if (nmp->nm_clp != NULL)
1379		clidrev = nmp->nm_clp->nfsc_clientidrev;
1380	newcred = cred;
1381	if (NFSHASNFSV4(nmp)) {
1382		if (p == NULL)
1383			newcred = NFSNEWCRED(cred);
1384		nfhp = np->n_fhp;
1385	}
1386	retrycnt = 0;
1387	do {
1388		lckp = NULL;
1389		nostateid = 0;
1390		if (NFSHASNFSV4(nmp)) {
1391			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1392			    NFSV4OPEN_ACCESSWRITE, newcred, p, &stateid, &lckp);
1393			if (stateid.other[0] == 0 && stateid.other[1] == 0 &&
1394			    stateid.other[2] == 0) {
1395				nostateid = 1;
1396				printf("stateid0 in write\n");
1397			}
1398		}
1399
1400		/*
1401		 * If there is no stateid for NFSv4, it means this is an
1402		 * extraneous write after close. Basically a poorly
1403		 * implemented buffer cache. Just don't do the write.
1404		 */
1405		if (nostateid)
1406			error = 0;
1407		else
1408			error = nfsrpc_writerpc(vp, uiop, iomode, verfp,
1409			    newcred, &stateid, p, nap, attrflagp, stuff);
1410		if (error == NFSERR_STALESTATEID)
1411			nfscl_initiate_recovery(nmp->nm_clp);
1412		if (lckp != NULL)
1413			nfscl_lockderef(lckp);
1414		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1415		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1416		    error == NFSERR_OLDSTATEID) {
1417			(void) nfs_catnap(PZERO, error, "nfs_write");
1418		} else if ((error == NFSERR_EXPIRED ||
1419		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1420			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1421		}
1422		retrycnt++;
1423	} while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
1424	    ((error == NFSERR_STALESTATEID ||
1425	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy == 0) ||
1426	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1427	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1428	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1429	if (error != 0 && (retrycnt >= 4 ||
1430	    ((error == NFSERR_STALESTATEID ||
1431	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy != 0)))
1432		error = EIO;
1433	if (NFSHASNFSV4(nmp) && p == NULL)
1434		NFSFREECRED(newcred);
1435	return (error);
1436}
1437
1438/*
1439 * The actual write RPC.
1440 */
1441static int
1442nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
1443    u_char *verfp, struct ucred *cred, nfsv4stateid_t *stateidp,
1444    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1445{
1446	u_int32_t *tl;
1447	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1448	struct nfsnode *np = VTONFS(vp);
1449	int error = 0, len, tsiz, rlen, commit, committed = NFSWRITE_FILESYNC;
1450	int wccflag = 0, wsize;
1451	int32_t backup;
1452	struct nfsrv_descript nfsd;
1453	struct nfsrv_descript *nd = &nfsd;
1454	nfsattrbit_t attrbits;
1455
1456	KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
1457	*attrflagp = 0;
1458	tsiz = uio_uio_resid(uiop);
1459	NFSLOCKMNT(nmp);
1460	if (uiop->uio_offset + tsiz > 0xffffffff &&
1461	    !NFSHASNFSV3OR4(nmp)) {
1462		NFSUNLOCKMNT(nmp);
1463		return (EFBIG);
1464	}
1465	wsize = nmp->nm_wsize;
1466	NFSUNLOCKMNT(nmp);
1467	nd->nd_mrep = NULL;	/* NFSv2 sometimes does a write with */
1468	nd->nd_repstat = 0;	/* uio_resid == 0, so the while is not done */
1469	while (tsiz > 0) {
1470		nmp = VFSTONFS(vnode_mount(vp));
1471		if (nmp == NULL) {
1472			error = ENXIO;
1473			goto nfsmout;
1474		}
1475		*attrflagp = 0;
1476		len = (tsiz > wsize) ? wsize : tsiz;
1477		NFSCL_REQSTART(nd, NFSPROC_WRITE, vp);
1478		if (nd->nd_flag & ND_NFSV4) {
1479			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1480			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+2*NFSX_UNSIGNED);
1481			txdr_hyper(uiop->uio_offset, tl);
1482			tl += 2;
1483			*tl++ = txdr_unsigned(*iomode);
1484			*tl = txdr_unsigned(len);
1485		} else if (nd->nd_flag & ND_NFSV3) {
1486			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+3*NFSX_UNSIGNED);
1487			txdr_hyper(uiop->uio_offset, tl);
1488			tl += 2;
1489			*tl++ = txdr_unsigned(len);
1490			*tl++ = txdr_unsigned(*iomode);
1491			*tl = txdr_unsigned(len);
1492		} else {
1493			u_int32_t x;
1494
1495			NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1496			/*
1497			 * Not sure why someone changed this, since the
1498			 * RFC clearly states that "beginoffset" and
1499			 * "totalcount" are ignored, but it wouldn't
1500			 * surprise me if there's a busted server out there.
1501			 */
1502			/* Set both "begin" and "current" to non-garbage. */
1503			x = txdr_unsigned((u_int32_t)uiop->uio_offset);
1504			*tl++ = x;      /* "begin offset" */
1505			*tl++ = x;      /* "current offset" */
1506			x = txdr_unsigned(len);
1507			*tl++ = x;      /* total to this offset */
1508			*tl = x;        /* size of this write */
1509
1510		}
1511		nfsm_uiombuf(nd, uiop, len);
1512		/*
1513		 * Although it is tempting to do a normal Getattr Op in the
1514		 * NFSv4 compound, the result can be a nearly hung client
1515		 * system if the Getattr asks for Owner and/or OwnerGroup.
1516		 * It occurs when the client can't map either the Owner or
1517		 * Owner_group name in the Getattr reply to a uid/gid. When
1518		 * there is a cache miss, the kernel does an upcall to the
1519		 * nfsuserd. Then, it can try and read the local /etc/passwd
1520		 * or /etc/group file. It can then block in getnewbuf(),
1521		 * waiting for dirty writes to be pushed to the NFS server.
1522		 * The only reason this doesn't result in a complete
1523		 * deadlock, is that the upcall times out and allows
1524		 * the write to complete. However, progress is so slow
1525		 * that it might just as well be deadlocked.
1526		 * So, we just get the attributes that change with each
1527		 * write Op.
1528		 * nb: nfscl_loadattrcache() needs to be told that these
1529		 *     partial attributes from a write rpc are being
1530		 *     passed in, via a argument flag.
1531		 */
1532		if (nd->nd_flag & ND_NFSV4) {
1533			NFSWRITEGETATTR_ATTRBIT(&attrbits);
1534			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1535			*tl = txdr_unsigned(NFSV4OP_GETATTR);
1536			(void) nfsrv_putattrbit(nd, &attrbits);
1537		}
1538		error = nfscl_request(nd, vp, p, cred, stuff);
1539		if (error)
1540			return (error);
1541		if (nd->nd_repstat) {
1542			/*
1543			 * In case the rpc gets retried, roll
1544			 * the uio fileds changed by nfsm_uiombuf()
1545			 * back.
1546			 */
1547			uiop->uio_offset -= len;
1548			uio_uio_resid_add(uiop, len);
1549			uio_iov_base_add(uiop, -len);
1550			uio_iov_len_add(uiop, len);
1551		}
1552		if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1553			error = nfscl_wcc_data(nd, vp, nap, attrflagp,
1554			    &wccflag, stuff);
1555			if (error)
1556				goto nfsmout;
1557		}
1558		if (!nd->nd_repstat) {
1559			if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1560				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED
1561					+ NFSX_VERF);
1562				rlen = fxdr_unsigned(int, *tl++);
1563				if (rlen == 0) {
1564					error = NFSERR_IO;
1565					goto nfsmout;
1566				} else if (rlen < len) {
1567					backup = len - rlen;
1568					uio_iov_base_add(uiop, -(backup));
1569					uio_iov_len_add(uiop, backup);
1570					uiop->uio_offset -= backup;
1571					uio_uio_resid_add(uiop, backup);
1572					len = rlen;
1573				}
1574				commit = fxdr_unsigned(int, *tl++);
1575
1576				/*
1577				 * Return the lowest committment level
1578				 * obtained by any of the RPCs.
1579				 */
1580				if (committed == NFSWRITE_FILESYNC)
1581					committed = commit;
1582				else if (committed == NFSWRITE_DATASYNC &&
1583					commit == NFSWRITE_UNSTABLE)
1584					committed = commit;
1585				if (verfp != NULL)
1586					NFSBCOPY((caddr_t)tl, verfp, NFSX_VERF);
1587				NFSLOCKMNT(nmp);
1588				if (!NFSHASWRITEVERF(nmp)) {
1589					NFSBCOPY((caddr_t)tl,
1590					    (caddr_t)&nmp->nm_verf[0],
1591					    NFSX_VERF);
1592					NFSSETWRITEVERF(nmp);
1593				}
1594				NFSUNLOCKMNT(nmp);
1595			}
1596			if (nd->nd_flag & ND_NFSV4)
1597				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1598			if (nd->nd_flag & (ND_NFSV2 | ND_NFSV4)) {
1599				error = nfsm_loadattr(nd, nap);
1600				if (!error)
1601					*attrflagp = NFS_LATTR_NOSHRINK;
1602			}
1603		} else {
1604			error = nd->nd_repstat;
1605		}
1606		if (error)
1607			goto nfsmout;
1608		NFSWRITERPC_SETTIME(wccflag, np, (nd->nd_flag & ND_NFSV4));
1609		mbuf_freem(nd->nd_mrep);
1610		nd->nd_mrep = NULL;
1611		tsiz -= len;
1612	}
1613nfsmout:
1614	if (nd->nd_mrep != NULL)
1615		mbuf_freem(nd->nd_mrep);
1616	*iomode = committed;
1617	if (nd->nd_repstat && !error)
1618		error = nd->nd_repstat;
1619	return (error);
1620}
1621
1622/*
1623 * nfs mknod rpc
1624 * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1625 * mode set to specify the file type and the size field for rdev.
1626 */
1627APPLESTATIC int
1628nfsrpc_mknod(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1629    u_int32_t rdev, enum vtype vtyp, struct ucred *cred, NFSPROC_T *p,
1630    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1631    int *attrflagp, int *dattrflagp, void *dstuff)
1632{
1633	u_int32_t *tl;
1634	int error = 0;
1635	struct nfsrv_descript nfsd, *nd = &nfsd;
1636	nfsattrbit_t attrbits;
1637
1638	*nfhpp = NULL;
1639	*attrflagp = 0;
1640	*dattrflagp = 0;
1641	if (namelen > NFS_MAXNAMLEN)
1642		return (ENAMETOOLONG);
1643	NFSCL_REQSTART(nd, NFSPROC_MKNOD, dvp);
1644	if (nd->nd_flag & ND_NFSV4) {
1645		if (vtyp == VBLK || vtyp == VCHR) {
1646			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1647			*tl++ = vtonfsv34_type(vtyp);
1648			*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1649			*tl = txdr_unsigned(NFSMINOR(rdev));
1650		} else {
1651			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1652			*tl = vtonfsv34_type(vtyp);
1653		}
1654	}
1655	(void) nfsm_strtom(nd, name, namelen);
1656	if (nd->nd_flag & ND_NFSV3) {
1657		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1658		*tl = vtonfsv34_type(vtyp);
1659	}
1660	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1661		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1662	if ((nd->nd_flag & ND_NFSV3) &&
1663	    (vtyp == VCHR || vtyp == VBLK)) {
1664		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1665		*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1666		*tl = txdr_unsigned(NFSMINOR(rdev));
1667	}
1668	if (nd->nd_flag & ND_NFSV4) {
1669		NFSGETATTR_ATTRBIT(&attrbits);
1670		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1671		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1672		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1673		(void) nfsrv_putattrbit(nd, &attrbits);
1674	}
1675	if (nd->nd_flag & ND_NFSV2)
1676		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZERDEV, rdev);
1677	error = nfscl_request(nd, dvp, p, cred, dstuff);
1678	if (error)
1679		return (error);
1680	if (nd->nd_flag & ND_NFSV4)
1681		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1682	if (!nd->nd_repstat) {
1683		if (nd->nd_flag & ND_NFSV4) {
1684			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1685			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1686			if (error)
1687				goto nfsmout;
1688		}
1689		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1690		if (error)
1691			goto nfsmout;
1692	}
1693	if (nd->nd_flag & ND_NFSV3)
1694		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1695	if (!error && nd->nd_repstat)
1696		error = nd->nd_repstat;
1697nfsmout:
1698	mbuf_freem(nd->nd_mrep);
1699	return (error);
1700}
1701
1702/*
1703 * nfs file create call
1704 * Mostly just call the approriate routine. (I separated out v4, so that
1705 * error recovery wouldn't be as difficult.)
1706 */
1707APPLESTATIC int
1708nfsrpc_create(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1709    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1710    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1711    int *attrflagp, int *dattrflagp, void *dstuff)
1712{
1713	int error = 0, newone, expireret = 0, retrycnt, unlocked;
1714	struct nfsclowner *owp;
1715	struct nfscldeleg *dp;
1716	struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
1717	u_int32_t clidrev;
1718
1719	if (NFSHASNFSV4(nmp)) {
1720	    retrycnt = 0;
1721	    do {
1722		dp = NULL;
1723		error = nfscl_open(dvp, NULL, 0, (NFSV4OPEN_ACCESSWRITE |
1724		    NFSV4OPEN_ACCESSREAD), 0, cred, p, &owp, NULL, &newone,
1725		    NULL, 1);
1726		if (error)
1727			return (error);
1728		if (nmp->nm_clp != NULL)
1729			clidrev = nmp->nm_clp->nfsc_clientidrev;
1730		else
1731			clidrev = 0;
1732		error = nfsrpc_createv4(dvp, name, namelen, vap, cverf, fmode,
1733		  owp, &dp, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1734		  dstuff, &unlocked);
1735		/*
1736		 * There is no need to invalidate cached attributes here,
1737		 * since new post-delegation issue attributes are always
1738		 * returned by nfsrpc_createv4() and these will update the
1739		 * attribute cache.
1740		 */
1741		if (dp != NULL)
1742			(void) nfscl_deleg(nmp->nm_mountp, owp->nfsow_clp,
1743			    (*nfhpp)->nfh_fh, (*nfhpp)->nfh_len, cred, p, &dp);
1744		nfscl_ownerrelease(owp, error, newone, unlocked);
1745		if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1746		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY) {
1747			(void) nfs_catnap(PZERO, error, "nfs_open");
1748		} else if ((error == NFSERR_EXPIRED ||
1749		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1750			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1751			retrycnt++;
1752		}
1753	    } while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1754		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1755		((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1756		 expireret == 0 && clidrev != 0 && retrycnt < 4));
1757	    if (error && retrycnt >= 4)
1758		    error = EIO;
1759	} else {
1760		error = nfsrpc_createv23(dvp, name, namelen, vap, cverf,
1761		    fmode, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1762		    dstuff);
1763	}
1764	return (error);
1765}
1766
1767/*
1768 * The create rpc for v2 and 3.
1769 */
1770static int
1771nfsrpc_createv23(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1772    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1773    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1774    int *attrflagp, int *dattrflagp, void *dstuff)
1775{
1776	u_int32_t *tl;
1777	int error = 0;
1778	struct nfsrv_descript nfsd, *nd = &nfsd;
1779
1780	*nfhpp = NULL;
1781	*attrflagp = 0;
1782	*dattrflagp = 0;
1783	if (namelen > NFS_MAXNAMLEN)
1784		return (ENAMETOOLONG);
1785	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1786	(void) nfsm_strtom(nd, name, namelen);
1787	if (nd->nd_flag & ND_NFSV3) {
1788		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1789		if (fmode & O_EXCL) {
1790			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
1791			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1792			*tl++ = cverf.lval[0];
1793			*tl = cverf.lval[1];
1794		} else {
1795			*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
1796			nfscl_fillsattr(nd, vap, dvp, 0, 0);
1797		}
1798	} else {
1799		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZE0, 0);
1800	}
1801	error = nfscl_request(nd, dvp, p, cred, dstuff);
1802	if (error)
1803		return (error);
1804	if (nd->nd_repstat == 0) {
1805		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1806		if (error)
1807			goto nfsmout;
1808	}
1809	if (nd->nd_flag & ND_NFSV3)
1810		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1811	if (nd->nd_repstat != 0 && error == 0)
1812		error = nd->nd_repstat;
1813nfsmout:
1814	mbuf_freem(nd->nd_mrep);
1815	return (error);
1816}
1817
1818static int
1819nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1820    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
1821    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
1822    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
1823    int *dattrflagp, void *dstuff, int *unlockedp)
1824{
1825	u_int32_t *tl;
1826	int error = 0, deleg, newone, ret, acesize, limitby;
1827	struct nfsrv_descript nfsd, *nd = &nfsd;
1828	struct nfsclopen *op;
1829	struct nfscldeleg *dp = NULL;
1830	struct nfsnode *np;
1831	struct nfsfh *nfhp;
1832	nfsattrbit_t attrbits;
1833	nfsv4stateid_t stateid;
1834	u_int32_t rflags;
1835
1836	*unlockedp = 0;
1837	*nfhpp = NULL;
1838	*dpp = NULL;
1839	*attrflagp = 0;
1840	*dattrflagp = 0;
1841	if (namelen > NFS_MAXNAMLEN)
1842		return (ENAMETOOLONG);
1843	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1844	/*
1845	 * For V4, this is actually an Open op.
1846	 */
1847	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1848	*tl++ = txdr_unsigned(owp->nfsow_seqid);
1849	*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
1850	    NFSV4OPEN_ACCESSREAD);
1851	*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
1852	*tl++ = owp->nfsow_clp->nfsc_clientid.lval[0];
1853	*tl = owp->nfsow_clp->nfsc_clientid.lval[1];
1854	(void) nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
1855	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1856	*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
1857	if (fmode & O_EXCL) {
1858		*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
1859		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1860		*tl++ = cverf.lval[0];
1861		*tl = cverf.lval[1];
1862	} else {
1863		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
1864		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1865	}
1866	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1867	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
1868	(void) nfsm_strtom(nd, name, namelen);
1869	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1870	*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1871	*tl = txdr_unsigned(NFSV4OP_GETATTR);
1872	NFSGETATTR_ATTRBIT(&attrbits);
1873	(void) nfsrv_putattrbit(nd, &attrbits);
1874	error = nfscl_request(nd, dvp, p, cred, dstuff);
1875	if (error)
1876		return (error);
1877	error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1878	if (error)
1879		goto nfsmout;
1880	NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
1881	if (nd->nd_repstat == 0) {
1882		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
1883		    6 * NFSX_UNSIGNED);
1884		stateid.seqid = *tl++;
1885		stateid.other[0] = *tl++;
1886		stateid.other[1] = *tl++;
1887		stateid.other[2] = *tl;
1888		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
1889		(void) nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1890		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1891		deleg = fxdr_unsigned(int, *tl);
1892		if (deleg == NFSV4OPEN_DELEGATEREAD ||
1893		    deleg == NFSV4OPEN_DELEGATEWRITE) {
1894			if (!(owp->nfsow_clp->nfsc_flags &
1895			      NFSCLFLAGS_FIRSTDELEG))
1896				owp->nfsow_clp->nfsc_flags |=
1897				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
1898			MALLOC(dp, struct nfscldeleg *,
1899			    sizeof (struct nfscldeleg) + NFSX_V4FHMAX,
1900			    M_NFSCLDELEG, M_WAITOK);
1901			LIST_INIT(&dp->nfsdl_owner);
1902			LIST_INIT(&dp->nfsdl_lock);
1903			dp->nfsdl_clp = owp->nfsow_clp;
1904			newnfs_copyincred(cred, &dp->nfsdl_cred);
1905			nfscl_lockinit(&dp->nfsdl_rwlock);
1906			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
1907			    NFSX_UNSIGNED);
1908			dp->nfsdl_stateid.seqid = *tl++;
1909			dp->nfsdl_stateid.other[0] = *tl++;
1910			dp->nfsdl_stateid.other[1] = *tl++;
1911			dp->nfsdl_stateid.other[2] = *tl++;
1912			ret = fxdr_unsigned(int, *tl);
1913			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
1914				dp->nfsdl_flags = NFSCLDL_WRITE;
1915				/*
1916				 * Indicates how much the file can grow.
1917				 */
1918				NFSM_DISSECT(tl, u_int32_t *,
1919				    3 * NFSX_UNSIGNED);
1920				limitby = fxdr_unsigned(int, *tl++);
1921				switch (limitby) {
1922				case NFSV4OPEN_LIMITSIZE:
1923					dp->nfsdl_sizelimit = fxdr_hyper(tl);
1924					break;
1925				case NFSV4OPEN_LIMITBLOCKS:
1926					dp->nfsdl_sizelimit =
1927					    fxdr_unsigned(u_int64_t, *tl++);
1928					dp->nfsdl_sizelimit *=
1929					    fxdr_unsigned(u_int64_t, *tl);
1930					break;
1931				default:
1932					error = NFSERR_BADXDR;
1933					goto nfsmout;
1934				};
1935			} else {
1936				dp->nfsdl_flags = NFSCLDL_READ;
1937			}
1938			if (ret)
1939				dp->nfsdl_flags |= NFSCLDL_RECALL;
1940			error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
1941			    &acesize, p);
1942			if (error)
1943				goto nfsmout;
1944		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
1945			error = NFSERR_BADXDR;
1946			goto nfsmout;
1947		}
1948		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1949		if (error)
1950			goto nfsmout;
1951		if (dp != NULL && *attrflagp) {
1952			dp->nfsdl_change = nnap->na_filerev;
1953			dp->nfsdl_modtime = nnap->na_mtime;
1954			dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
1955		}
1956		/*
1957		 * We can now complete the Open state.
1958		 */
1959		nfhp = *nfhpp;
1960		if (dp != NULL) {
1961			dp->nfsdl_fhlen = nfhp->nfh_len;
1962			NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh, nfhp->nfh_len);
1963		}
1964		/*
1965		 * Get an Open structure that will be
1966		 * attached to the OpenOwner, acquired already.
1967		 */
1968		error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len,
1969		    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
1970		    cred, p, NULL, &op, &newone, NULL, 0);
1971		if (error)
1972			goto nfsmout;
1973		op->nfso_stateid = stateid;
1974		newnfs_copyincred(cred, &op->nfso_cred);
1975		if ((rflags & NFSV4OPEN_RESULTCONFIRM)) {
1976		    do {
1977			ret = nfsrpc_openconfirm(dvp, nfhp->nfh_fh,
1978			    nfhp->nfh_len, op, cred, p);
1979			if (ret == NFSERR_DELAY)
1980			    (void) nfs_catnap(PZERO, ret, "nfs_create");
1981		    } while (ret == NFSERR_DELAY);
1982		    error = ret;
1983		}
1984
1985		/*
1986		 * If the server is handing out delegations, but we didn't
1987		 * get one because an OpenConfirm was required, try the
1988		 * Open again, to get a delegation. This is a harmless no-op,
1989		 * from a server's point of view.
1990		 */
1991		if ((rflags & NFSV4OPEN_RESULTCONFIRM) &&
1992		    (owp->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG) &&
1993		    !error && dp == NULL) {
1994		    np = VTONFS(dvp);
1995		    do {
1996			ret = nfsrpc_openrpc(VFSTONFS(vnode_mount(dvp)), dvp,
1997			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
1998			    nfhp->nfh_fh, nfhp->nfh_len,
1999			    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), op,
2000			    name, namelen, &dp, 0, 0x0, cred, p, 0, 1);
2001			if (ret == NFSERR_DELAY)
2002			    (void) nfs_catnap(PZERO, ret, "nfs_crt2");
2003		    } while (ret == NFSERR_DELAY);
2004		    if (ret) {
2005			if (dp != NULL)
2006				FREE((caddr_t)dp, M_NFSCLDELEG);
2007			if (ret == NFSERR_STALECLIENTID ||
2008			    ret == NFSERR_STALEDONTRECOVER)
2009				error = ret;
2010		    }
2011		}
2012		nfscl_openrelease(op, error, newone);
2013		*unlockedp = 1;
2014	}
2015	if (nd->nd_repstat != 0 && error == 0)
2016		error = nd->nd_repstat;
2017	if (error == NFSERR_STALECLIENTID)
2018		nfscl_initiate_recovery(owp->nfsow_clp);
2019nfsmout:
2020	if (!error)
2021		*dpp = dp;
2022	else if (dp != NULL)
2023		FREE((caddr_t)dp, M_NFSCLDELEG);
2024	mbuf_freem(nd->nd_mrep);
2025	return (error);
2026}
2027
2028/*
2029 * Nfs remove rpc
2030 */
2031APPLESTATIC int
2032nfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp,
2033    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp,
2034    void *dstuff)
2035{
2036	u_int32_t *tl;
2037	struct nfsrv_descript nfsd, *nd = &nfsd;
2038	struct nfsnode *np;
2039	struct nfsmount *nmp;
2040	nfsv4stateid_t dstateid;
2041	int error, ret = 0, i;
2042
2043	*dattrflagp = 0;
2044	if (namelen > NFS_MAXNAMLEN)
2045		return (ENAMETOOLONG);
2046	nmp = VFSTONFS(vnode_mount(dvp));
2047tryagain:
2048	if (NFSHASNFSV4(nmp) && ret == 0) {
2049		ret = nfscl_removedeleg(vp, p, &dstateid);
2050		if (ret == 1) {
2051			NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp);
2052			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
2053			    NFSX_UNSIGNED);
2054			*tl++ = dstateid.seqid;
2055			*tl++ = dstateid.other[0];
2056			*tl++ = dstateid.other[1];
2057			*tl++ = dstateid.other[2];
2058			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2059			np = VTONFS(dvp);
2060			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2061			    np->n_fhp->nfh_len, 0);
2062			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2063			*tl = txdr_unsigned(NFSV4OP_REMOVE);
2064		}
2065	} else {
2066		ret = 0;
2067	}
2068	if (ret == 0)
2069		NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp);
2070	(void) nfsm_strtom(nd, name, namelen);
2071	error = nfscl_request(nd, dvp, p, cred, dstuff);
2072	if (error)
2073		return (error);
2074	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2075		/* For NFSv4, parse out any Delereturn replies. */
2076		if (ret > 0 && nd->nd_repstat != 0 &&
2077		    (nd->nd_flag & ND_NOMOREDATA)) {
2078			/*
2079			 * If the Delegreturn failed, try again without
2080			 * it. The server will Recall, as required.
2081			 */
2082			mbuf_freem(nd->nd_mrep);
2083			goto tryagain;
2084		}
2085		for (i = 0; i < (ret * 2); i++) {
2086			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2087			    ND_NFSV4) {
2088			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2089			    if (*(tl + 1))
2090				nd->nd_flag |= ND_NOMOREDATA;
2091			}
2092		}
2093		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2094	}
2095	if (nd->nd_repstat && !error)
2096		error = nd->nd_repstat;
2097nfsmout:
2098	mbuf_freem(nd->nd_mrep);
2099	return (error);
2100}
2101
2102/*
2103 * Do an nfs rename rpc.
2104 */
2105APPLESTATIC int
2106nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen,
2107    vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred *cred,
2108    NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap,
2109    int *fattrflagp, int *tattrflagp, void *fstuff, void *tstuff)
2110{
2111	u_int32_t *tl;
2112	struct nfsrv_descript nfsd, *nd = &nfsd;
2113	struct nfsmount *nmp;
2114	struct nfsnode *np;
2115	nfsattrbit_t attrbits;
2116	nfsv4stateid_t fdstateid, tdstateid;
2117	int error = 0, ret = 0, gottd = 0, gotfd = 0, i;
2118
2119	*fattrflagp = 0;
2120	*tattrflagp = 0;
2121	nmp = VFSTONFS(vnode_mount(fdvp));
2122	if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN)
2123		return (ENAMETOOLONG);
2124tryagain:
2125	if (NFSHASNFSV4(nmp) && ret == 0) {
2126		ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp,
2127		    &tdstateid, &gottd, p);
2128		if (gotfd && gottd) {
2129			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME2, fvp);
2130		} else if (gotfd) {
2131			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, fvp);
2132		} else if (gottd) {
2133			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, tvp);
2134		}
2135		if (gotfd) {
2136			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2137			*tl++ = fdstateid.seqid;
2138			*tl++ = fdstateid.other[0];
2139			*tl++ = fdstateid.other[1];
2140			*tl = fdstateid.other[2];
2141			if (gottd) {
2142				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2143				*tl = txdr_unsigned(NFSV4OP_PUTFH);
2144				np = VTONFS(tvp);
2145				(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2146				    np->n_fhp->nfh_len, 0);
2147				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2148				*tl = txdr_unsigned(NFSV4OP_DELEGRETURN);
2149			}
2150		}
2151		if (gottd) {
2152			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2153			*tl++ = tdstateid.seqid;
2154			*tl++ = tdstateid.other[0];
2155			*tl++ = tdstateid.other[1];
2156			*tl = tdstateid.other[2];
2157		}
2158		if (ret > 0) {
2159			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2160			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2161			np = VTONFS(fdvp);
2162			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2163			    np->n_fhp->nfh_len, 0);
2164			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2165			*tl = txdr_unsigned(NFSV4OP_SAVEFH);
2166		}
2167	} else {
2168		ret = 0;
2169	}
2170	if (ret == 0)
2171		NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp);
2172	if (nd->nd_flag & ND_NFSV4) {
2173		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2174		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2175		NFSWCCATTR_ATTRBIT(&attrbits);
2176		(void) nfsrv_putattrbit(nd, &attrbits);
2177		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2178		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2179		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2180		    VTONFS(tdvp)->n_fhp->nfh_len, 0);
2181		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2182		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2183		(void) nfsrv_putattrbit(nd, &attrbits);
2184		nd->nd_flag |= ND_V4WCCATTR;
2185		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2186		*tl = txdr_unsigned(NFSV4OP_RENAME);
2187	}
2188	(void) nfsm_strtom(nd, fnameptr, fnamelen);
2189	if (!(nd->nd_flag & ND_NFSV4))
2190		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2191			VTONFS(tdvp)->n_fhp->nfh_len, 0);
2192	(void) nfsm_strtom(nd, tnameptr, tnamelen);
2193	error = nfscl_request(nd, fdvp, p, cred, fstuff);
2194	if (error)
2195		return (error);
2196	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2197		/* For NFSv4, parse out any Delereturn replies. */
2198		if (ret > 0 && nd->nd_repstat != 0 &&
2199		    (nd->nd_flag & ND_NOMOREDATA)) {
2200			/*
2201			 * If the Delegreturn failed, try again without
2202			 * it. The server will Recall, as required.
2203			 */
2204			mbuf_freem(nd->nd_mrep);
2205			goto tryagain;
2206		}
2207		for (i = 0; i < (ret * 2); i++) {
2208			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2209			    ND_NFSV4) {
2210			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2211			    if (*(tl + 1)) {
2212				if (i == 0 && ret > 1) {
2213				    /*
2214				     * If the Delegreturn failed, try again
2215				     * without it. The server will Recall, as
2216				     * required.
2217				     * If ret > 1, the first iteration of this
2218				     * loop is the second DelegReturn result.
2219				     */
2220				    mbuf_freem(nd->nd_mrep);
2221				    goto tryagain;
2222				} else {
2223				    nd->nd_flag |= ND_NOMOREDATA;
2224				}
2225			    }
2226			}
2227		}
2228		/* Now, the first wcc attribute reply. */
2229		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2230			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2231			if (*(tl + 1))
2232				nd->nd_flag |= ND_NOMOREDATA;
2233		}
2234		error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL,
2235		    fstuff);
2236		/* and the second wcc attribute reply. */
2237		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
2238		    !error) {
2239			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2240			if (*(tl + 1))
2241				nd->nd_flag |= ND_NOMOREDATA;
2242		}
2243		if (!error)
2244			error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp,
2245			    NULL, tstuff);
2246	}
2247	if (nd->nd_repstat && !error)
2248		error = nd->nd_repstat;
2249nfsmout:
2250	mbuf_freem(nd->nd_mrep);
2251	return (error);
2252}
2253
2254/*
2255 * nfs hard link create rpc
2256 */
2257APPLESTATIC int
2258nfsrpc_link(vnode_t dvp, vnode_t vp, char *name, int namelen,
2259    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2260    struct nfsvattr *nap, int *attrflagp, int *dattrflagp, void *dstuff)
2261{
2262	u_int32_t *tl;
2263	struct nfsrv_descript nfsd, *nd = &nfsd;
2264	nfsattrbit_t attrbits;
2265	int error = 0;
2266
2267	*attrflagp = 0;
2268	*dattrflagp = 0;
2269	if (namelen > NFS_MAXNAMLEN)
2270		return (ENAMETOOLONG);
2271	NFSCL_REQSTART(nd, NFSPROC_LINK, vp);
2272	if (nd->nd_flag & ND_NFSV4) {
2273		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2274		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2275	}
2276	(void) nfsm_fhtom(nd, VTONFS(dvp)->n_fhp->nfh_fh,
2277		VTONFS(dvp)->n_fhp->nfh_len, 0);
2278	if (nd->nd_flag & ND_NFSV4) {
2279		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2280		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2281		NFSWCCATTR_ATTRBIT(&attrbits);
2282		(void) nfsrv_putattrbit(nd, &attrbits);
2283		nd->nd_flag |= ND_V4WCCATTR;
2284		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2285		*tl = txdr_unsigned(NFSV4OP_LINK);
2286	}
2287	(void) nfsm_strtom(nd, name, namelen);
2288	error = nfscl_request(nd, vp, p, cred, dstuff);
2289	if (error)
2290		return (error);
2291	if (nd->nd_flag & ND_NFSV3) {
2292		error = nfscl_postop_attr(nd, nap, attrflagp, dstuff);
2293		if (!error)
2294			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2295			    NULL, dstuff);
2296	} else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2297		/*
2298		 * First, parse out the PutFH and Getattr result.
2299		 */
2300		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2301		if (!(*(tl + 1)))
2302			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2303		if (*(tl + 1))
2304			nd->nd_flag |= ND_NOMOREDATA;
2305		/*
2306		 * Get the pre-op attributes.
2307		 */
2308		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2309	}
2310	if (nd->nd_repstat && !error)
2311		error = nd->nd_repstat;
2312nfsmout:
2313	mbuf_freem(nd->nd_mrep);
2314	return (error);
2315}
2316
2317/*
2318 * nfs symbolic link create rpc
2319 */
2320APPLESTATIC int
2321nfsrpc_symlink(vnode_t dvp, char *name, int namelen, char *target,
2322    struct vattr *vap, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2323    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2324    int *dattrflagp, void *dstuff)
2325{
2326	u_int32_t *tl;
2327	struct nfsrv_descript nfsd, *nd = &nfsd;
2328	struct nfsmount *nmp;
2329	int slen, error = 0;
2330
2331	*nfhpp = NULL;
2332	*attrflagp = 0;
2333	*dattrflagp = 0;
2334	nmp = VFSTONFS(vnode_mount(dvp));
2335	slen = strlen(target);
2336	if (slen > NFS_MAXPATHLEN || namelen > NFS_MAXNAMLEN)
2337		return (ENAMETOOLONG);
2338	NFSCL_REQSTART(nd, NFSPROC_SYMLINK, dvp);
2339	if (nd->nd_flag & ND_NFSV4) {
2340		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2341		*tl = txdr_unsigned(NFLNK);
2342		(void) nfsm_strtom(nd, target, slen);
2343	}
2344	(void) nfsm_strtom(nd, name, namelen);
2345	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2346		nfscl_fillsattr(nd, vap, dvp, 0, 0);
2347	if (!(nd->nd_flag & ND_NFSV4))
2348		(void) nfsm_strtom(nd, target, slen);
2349	if (nd->nd_flag & ND_NFSV2)
2350		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2351	error = nfscl_request(nd, dvp, p, cred, dstuff);
2352	if (error)
2353		return (error);
2354	if (nd->nd_flag & ND_NFSV4)
2355		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2356	if ((nd->nd_flag & ND_NFSV3) && !error) {
2357		if (!nd->nd_repstat)
2358			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2359		if (!error)
2360			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2361			    NULL, dstuff);
2362	}
2363	if (nd->nd_repstat && !error)
2364		error = nd->nd_repstat;
2365	mbuf_freem(nd->nd_mrep);
2366	/*
2367	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2368	 */
2369	if (error == EEXIST)
2370		error = 0;
2371	return (error);
2372}
2373
2374/*
2375 * nfs make dir rpc
2376 */
2377APPLESTATIC int
2378nfsrpc_mkdir(vnode_t dvp, char *name, int namelen, struct vattr *vap,
2379    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2380    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2381    int *dattrflagp, void *dstuff)
2382{
2383	u_int32_t *tl;
2384	struct nfsrv_descript nfsd, *nd = &nfsd;
2385	nfsattrbit_t attrbits;
2386	int error = 0;
2387
2388	*nfhpp = NULL;
2389	*attrflagp = 0;
2390	*dattrflagp = 0;
2391	if (namelen > NFS_MAXNAMLEN)
2392		return (ENAMETOOLONG);
2393	NFSCL_REQSTART(nd, NFSPROC_MKDIR, dvp);
2394	if (nd->nd_flag & ND_NFSV4) {
2395		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2396		*tl = txdr_unsigned(NFDIR);
2397	}
2398	(void) nfsm_strtom(nd, name, namelen);
2399	nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2400	if (nd->nd_flag & ND_NFSV4) {
2401		NFSGETATTR_ATTRBIT(&attrbits);
2402		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2403		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2404		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2405		(void) nfsrv_putattrbit(nd, &attrbits);
2406	}
2407	error = nfscl_request(nd, dvp, p, cred, dstuff);
2408	if (error)
2409		return (error);
2410	if (nd->nd_flag & ND_NFSV4)
2411		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2412	if (!nd->nd_repstat && !error) {
2413		if (nd->nd_flag & ND_NFSV4) {
2414			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2415			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2416		}
2417		if (!error)
2418			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2419	}
2420	if ((nd->nd_flag & ND_NFSV3) && !error)
2421		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2422	if (nd->nd_repstat && !error)
2423		error = nd->nd_repstat;
2424nfsmout:
2425	mbuf_freem(nd->nd_mrep);
2426	/*
2427	 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry.
2428	 */
2429	if (error == EEXIST)
2430		error = 0;
2431	return (error);
2432}
2433
2434/*
2435 * nfs remove directory call
2436 */
2437APPLESTATIC int
2438nfsrpc_rmdir(vnode_t dvp, char *name, int namelen, struct ucred *cred,
2439    NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp, void *dstuff)
2440{
2441	struct nfsrv_descript nfsd, *nd = &nfsd;
2442	int error = 0;
2443
2444	*dattrflagp = 0;
2445	if (namelen > NFS_MAXNAMLEN)
2446		return (ENAMETOOLONG);
2447	NFSCL_REQSTART(nd, NFSPROC_RMDIR, dvp);
2448	(void) nfsm_strtom(nd, name, namelen);
2449	error = nfscl_request(nd, dvp, p, cred, dstuff);
2450	if (error)
2451		return (error);
2452	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2453		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2454	if (nd->nd_repstat && !error)
2455		error = nd->nd_repstat;
2456	mbuf_freem(nd->nd_mrep);
2457	/*
2458	 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2459	 */
2460	if (error == ENOENT)
2461		error = 0;
2462	return (error);
2463}
2464
2465/*
2466 * Readdir rpc.
2467 * Always returns with either uio_resid unchanged, if you are at the
2468 * end of the directory, or uio_resid == 0, with all DIRBLKSIZ chunks
2469 * filled in.
2470 * I felt this would allow caching of directory blocks more easily
2471 * than returning a pertially filled block.
2472 * Directory offset cookies:
2473 * Oh my, what to do with them...
2474 * I can think of three ways to deal with them:
2475 * 1 - have the layer above these RPCs maintain a map between logical
2476 *     directory byte offsets and the NFS directory offset cookies
2477 * 2 - pass the opaque directory offset cookies up into userland
2478 *     and let the libc functions deal with them, via the system call
2479 * 3 - return them to userland in the "struct dirent", so future versions
2480 *     of libc can use them and do whatever is necessary to amke things work
2481 *     above these rpc calls, in the meantime
2482 * For now, I do #3 by "hiding" the directory offset cookies after the
2483 * d_name field in struct dirent. This is space inside d_reclen that
2484 * will be ignored by anything that doesn't know about them.
2485 * The directory offset cookies are filled in as the last 8 bytes of
2486 * each directory entry, after d_name. Someday, the userland libc
2487 * functions may be able to use these. In the meantime, it satisfies
2488 * OpenBSD's requirements for cookies being returned.
2489 * If expects the directory offset cookie for the read to be in uio_offset
2490 * and returns the one for the next entry after this directory block in
2491 * there, as well.
2492 */
2493APPLESTATIC int
2494nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2495    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2496    int *eofp, void *stuff)
2497{
2498	int len, left;
2499	struct dirent *dp = NULL;
2500	u_int32_t *tl;
2501	nfsquad_t cookie, ncookie;
2502	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2503	struct nfsnode *dnp = VTONFS(vp);
2504	struct nfsvattr nfsva;
2505	struct nfsrv_descript nfsd, *nd = &nfsd;
2506	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2507	int reqsize, tryformoredirs = 1, readsize, eof = 0, gotmnton = 0;
2508	long dotfileid, dotdotfileid = 0;
2509	u_int32_t fakefileno = 0xffffffff, rderr;
2510	char *cp;
2511	nfsattrbit_t attrbits, dattrbits;
2512	u_int32_t *tl2 = NULL;
2513	size_t tresid;
2514
2515	KASSERT(uiop->uio_iovcnt == 1 &&
2516	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
2517	    ("nfs readdirrpc bad uio"));
2518
2519	/*
2520	 * There is no point in reading a lot more than uio_resid, however
2521	 * adding one additional DIRBLKSIZ makes sense. Since uio_resid
2522	 * and nm_readdirsize are both exact multiples of DIRBLKSIZ, this
2523	 * will never make readsize > nm_readdirsize.
2524	 */
2525	readsize = nmp->nm_readdirsize;
2526	if (readsize > uio_uio_resid(uiop))
2527		readsize = uio_uio_resid(uiop) + DIRBLKSIZ;
2528
2529	*attrflagp = 0;
2530	if (eofp)
2531		*eofp = 0;
2532	tresid = uio_uio_resid(uiop);
2533	cookie.lval[0] = cookiep->nfsuquad[0];
2534	cookie.lval[1] = cookiep->nfsuquad[1];
2535	nd->nd_mrep = NULL;
2536
2537	/*
2538	 * For NFSv4, first create the "." and ".." entries.
2539	 */
2540	if (NFSHASNFSV4(nmp)) {
2541		reqsize = 6 * NFSX_UNSIGNED;
2542		NFSGETATTR_ATTRBIT(&dattrbits);
2543		NFSZERO_ATTRBIT(&attrbits);
2544		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
2545		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TYPE);
2546		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
2547		    NFSATTRBIT_MOUNTEDONFILEID)) {
2548			NFSSETBIT_ATTRBIT(&attrbits,
2549			    NFSATTRBIT_MOUNTEDONFILEID);
2550			gotmnton = 1;
2551		} else {
2552			/*
2553			 * Must fake it. Use the fileno, except when the
2554			 * fsid is != to that of the directory. For that
2555			 * case, generate a fake fileno that is not the same.
2556			 */
2557			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
2558			gotmnton = 0;
2559		}
2560
2561		/*
2562		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
2563		 */
2564		if (uiop->uio_offset == 0) {
2565#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
2566			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred);
2567#else
2568			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred, p);
2569#endif
2570			if (error)
2571			    return (error);
2572			dotfileid = nfsva.na_fileid;
2573			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
2574			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2575			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2576			*tl = txdr_unsigned(NFSV4OP_GETATTR);
2577			(void) nfsrv_putattrbit(nd, &attrbits);
2578			error = nfscl_request(nd, vp, p, cred, stuff);
2579			if (error)
2580			    return (error);
2581			if (nd->nd_repstat == 0) {
2582			    NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2583			    len = fxdr_unsigned(int, *(tl + 2));
2584			    if (len > 0 && len <= NFSX_V4FHMAX)
2585				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2586			    else
2587				error = EPERM;
2588			    if (!error) {
2589				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2590				nfsva.na_mntonfileno = 0xffffffff;
2591				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2592				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2593				    NULL, NULL, NULL, p, cred);
2594				if (error) {
2595				    dotdotfileid = dotfileid;
2596				} else if (gotmnton) {
2597				    if (nfsva.na_mntonfileno != 0xffffffff)
2598					dotdotfileid = nfsva.na_mntonfileno;
2599				    else
2600					dotdotfileid = nfsva.na_fileid;
2601				} else if (nfsva.na_filesid[0] ==
2602				    dnp->n_vattr.na_filesid[0] &&
2603				    nfsva.na_filesid[1] ==
2604				    dnp->n_vattr.na_filesid[1]) {
2605				    dotdotfileid = nfsva.na_fileid;
2606				} else {
2607				    do {
2608					fakefileno--;
2609				    } while (fakefileno ==
2610					nfsva.na_fileid);
2611				    dotdotfileid = fakefileno;
2612				}
2613			    }
2614			} else if (nd->nd_repstat == NFSERR_NOENT) {
2615			    /*
2616			     * Lookupp returns NFSERR_NOENT when we are
2617			     * at the root, so just use the current dir.
2618			     */
2619			    nd->nd_repstat = 0;
2620			    dotdotfileid = dotfileid;
2621			} else {
2622			    error = nd->nd_repstat;
2623			}
2624			mbuf_freem(nd->nd_mrep);
2625			if (error)
2626			    return (error);
2627			nd->nd_mrep = NULL;
2628			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2629			dp->d_type = DT_DIR;
2630			dp->d_fileno = dotfileid;
2631			dp->d_namlen = 1;
2632			dp->d_name[0] = '.';
2633			dp->d_name[1] = '\0';
2634			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2635			/*
2636			 * Just make these offset cookie 0.
2637			 */
2638			tl = (u_int32_t *)&dp->d_name[4];
2639			*tl++ = 0;
2640			*tl = 0;
2641			blksiz += dp->d_reclen;
2642			uio_uio_resid_add(uiop, -(dp->d_reclen));
2643			uiop->uio_offset += dp->d_reclen;
2644			uio_iov_base_add(uiop, dp->d_reclen);
2645			uio_iov_len_add(uiop, -(dp->d_reclen));
2646			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2647			dp->d_type = DT_DIR;
2648			dp->d_fileno = dotdotfileid;
2649			dp->d_namlen = 2;
2650			dp->d_name[0] = '.';
2651			dp->d_name[1] = '.';
2652			dp->d_name[2] = '\0';
2653			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2654			/*
2655			 * Just make these offset cookie 0.
2656			 */
2657			tl = (u_int32_t *)&dp->d_name[4];
2658			*tl++ = 0;
2659			*tl = 0;
2660			blksiz += dp->d_reclen;
2661			uio_uio_resid_add(uiop, -(dp->d_reclen));
2662			uiop->uio_offset += dp->d_reclen;
2663			uio_iov_base_add(uiop, dp->d_reclen);
2664			uio_iov_len_add(uiop, -(dp->d_reclen));
2665		}
2666		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_RDATTRERROR);
2667	} else {
2668		reqsize = 5 * NFSX_UNSIGNED;
2669	}
2670
2671
2672	/*
2673	 * Loop around doing readdir rpc's of size readsize.
2674	 * The stopping criteria is EOF or buffer full.
2675	 */
2676	while (more_dirs && bigenough) {
2677		*attrflagp = 0;
2678		NFSCL_REQSTART(nd, NFSPROC_READDIR, vp);
2679		if (nd->nd_flag & ND_NFSV2) {
2680			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2681			*tl++ = cookie.lval[1];
2682			*tl = txdr_unsigned(readsize);
2683		} else {
2684			NFSM_BUILD(tl, u_int32_t *, reqsize);
2685			*tl++ = cookie.lval[0];
2686			*tl++ = cookie.lval[1];
2687			if (cookie.qval == 0) {
2688				*tl++ = 0;
2689				*tl++ = 0;
2690			} else {
2691				NFSLOCKNODE(dnp);
2692				*tl++ = dnp->n_cookieverf.nfsuquad[0];
2693				*tl++ = dnp->n_cookieverf.nfsuquad[1];
2694				NFSUNLOCKNODE(dnp);
2695			}
2696			if (nd->nd_flag & ND_NFSV4) {
2697				*tl++ = txdr_unsigned(readsize);
2698				*tl = txdr_unsigned(readsize);
2699				(void) nfsrv_putattrbit(nd, &attrbits);
2700				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2701				*tl = txdr_unsigned(NFSV4OP_GETATTR);
2702				(void) nfsrv_putattrbit(nd, &dattrbits);
2703			} else {
2704				*tl = txdr_unsigned(readsize);
2705			}
2706		}
2707		error = nfscl_request(nd, vp, p, cred, stuff);
2708		if (error)
2709			return (error);
2710		if (!(nd->nd_flag & ND_NFSV2)) {
2711			if (nd->nd_flag & ND_NFSV3)
2712				error = nfscl_postop_attr(nd, nap, attrflagp,
2713				    stuff);
2714			if (!nd->nd_repstat && !error) {
2715				NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
2716				NFSLOCKNODE(dnp);
2717				dnp->n_cookieverf.nfsuquad[0] = *tl++;
2718				dnp->n_cookieverf.nfsuquad[1] = *tl;
2719				NFSUNLOCKNODE(dnp);
2720			}
2721		}
2722		if (nd->nd_repstat || error) {
2723			if (!error)
2724				error = nd->nd_repstat;
2725			goto nfsmout;
2726		}
2727		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2728		more_dirs = fxdr_unsigned(int, *tl);
2729		if (!more_dirs)
2730			tryformoredirs = 0;
2731
2732		/* loop thru the dir entries, doctoring them to 4bsd form */
2733		while (more_dirs && bigenough) {
2734			if (nd->nd_flag & ND_NFSV4) {
2735				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2736				ncookie.lval[0] = *tl++;
2737				ncookie.lval[1] = *tl++;
2738				len = fxdr_unsigned(int, *tl);
2739			} else if (nd->nd_flag & ND_NFSV3) {
2740				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2741				nfsva.na_fileid = fxdr_hyper(tl);
2742				tl += 2;
2743				len = fxdr_unsigned(int, *tl);
2744			} else {
2745				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2746				nfsva.na_fileid =
2747				    fxdr_unsigned(long, *tl++);
2748				len = fxdr_unsigned(int, *tl);
2749			}
2750			if (len <= 0 || len > NFS_MAXNAMLEN) {
2751				error = EBADRPC;
2752				goto nfsmout;
2753			}
2754			tlen = NFSM_RNDUP(len);
2755			if (tlen == len)
2756				tlen += 4;  /* To ensure null termination */
2757			left = DIRBLKSIZ - blksiz;
2758			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > left) {
2759				dp->d_reclen += left;
2760				uio_iov_base_add(uiop, left);
2761				uio_iov_len_add(uiop, -(left));
2762				uio_uio_resid_add(uiop, -(left));
2763				uiop->uio_offset += left;
2764				blksiz = 0;
2765			}
2766			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
2767				bigenough = 0;
2768			if (bigenough) {
2769				dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2770				dp->d_namlen = len;
2771				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
2772				dp->d_type = DT_UNKNOWN;
2773				blksiz += dp->d_reclen;
2774				if (blksiz == DIRBLKSIZ)
2775					blksiz = 0;
2776				uio_uio_resid_add(uiop, -(DIRHDSIZ));
2777				uiop->uio_offset += DIRHDSIZ;
2778				uio_iov_base_add(uiop, DIRHDSIZ);
2779				uio_iov_len_add(uiop, -(DIRHDSIZ));
2780				error = nfsm_mbufuio(nd, uiop, len);
2781				if (error)
2782					goto nfsmout;
2783				cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
2784				tlen -= len;
2785				*cp = '\0';	/* null terminate */
2786				cp += tlen;	/* points to cookie storage */
2787				tl2 = (u_int32_t *)cp;
2788				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
2789				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
2790				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
2791				uiop->uio_offset += (tlen + NFSX_HYPER);
2792			} else {
2793				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2794				if (error)
2795					goto nfsmout;
2796			}
2797			if (nd->nd_flag & ND_NFSV4) {
2798				rderr = 0;
2799				nfsva.na_mntonfileno = 0xffffffff;
2800				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2801				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2802				    NULL, NULL, &rderr, p, cred);
2803				if (error)
2804					goto nfsmout;
2805				NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2806			} else if (nd->nd_flag & ND_NFSV3) {
2807				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2808				ncookie.lval[0] = *tl++;
2809				ncookie.lval[1] = *tl++;
2810			} else {
2811				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2812				ncookie.lval[0] = 0;
2813				ncookie.lval[1] = *tl++;
2814			}
2815			if (bigenough) {
2816			    if (nd->nd_flag & ND_NFSV4) {
2817				if (rderr) {
2818				    dp->d_fileno = 0;
2819				} else {
2820				    if (gotmnton) {
2821					if (nfsva.na_mntonfileno != 0xffffffff)
2822					    dp->d_fileno = nfsva.na_mntonfileno;
2823					else
2824					    dp->d_fileno = nfsva.na_fileid;
2825				    } else if (nfsva.na_filesid[0] ==
2826					dnp->n_vattr.na_filesid[0] &&
2827					nfsva.na_filesid[1] ==
2828					dnp->n_vattr.na_filesid[1]) {
2829					dp->d_fileno = nfsva.na_fileid;
2830				    } else {
2831					do {
2832					    fakefileno--;
2833					} while (fakefileno ==
2834					    nfsva.na_fileid);
2835					dp->d_fileno = fakefileno;
2836				    }
2837				    dp->d_type = vtonfs_dtype(nfsva.na_type);
2838				}
2839			    } else {
2840				dp->d_fileno = nfsva.na_fileid;
2841			    }
2842			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
2843				ncookie.lval[0];
2844			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
2845				ncookie.lval[1];
2846			}
2847			more_dirs = fxdr_unsigned(int, *tl);
2848		}
2849		/*
2850		 * If at end of rpc data, get the eof boolean
2851		 */
2852		if (!more_dirs) {
2853			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2854			eof = fxdr_unsigned(int, *tl);
2855			if (tryformoredirs)
2856				more_dirs = !eof;
2857			if (nd->nd_flag & ND_NFSV4) {
2858				error = nfscl_postop_attr(nd, nap, attrflagp,
2859				    stuff);
2860				if (error)
2861					goto nfsmout;
2862			}
2863		}
2864		mbuf_freem(nd->nd_mrep);
2865		nd->nd_mrep = NULL;
2866	}
2867	/*
2868	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
2869	 * by increasing d_reclen for the last record.
2870	 */
2871	if (blksiz > 0) {
2872		left = DIRBLKSIZ - blksiz;
2873		dp->d_reclen += left;
2874		uio_iov_base_add(uiop, left);
2875		uio_iov_len_add(uiop, -(left));
2876		uio_uio_resid_add(uiop, -(left));
2877		uiop->uio_offset += left;
2878	}
2879
2880	/*
2881	 * If returning no data, assume end of file.
2882	 * If not bigenough, return not end of file, since you aren't
2883	 *    returning all the data
2884	 * Otherwise, return the eof flag from the server.
2885	 */
2886	if (eofp) {
2887		if (tresid == ((size_t)(uio_uio_resid(uiop))))
2888			*eofp = 1;
2889		else if (!bigenough)
2890			*eofp = 0;
2891		else
2892			*eofp = eof;
2893	}
2894
2895	/*
2896	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
2897	 */
2898	while (uio_uio_resid(uiop) > 0 && ((size_t)(uio_uio_resid(uiop))) != tresid) {
2899		dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2900		dp->d_type = DT_UNKNOWN;
2901		dp->d_fileno = 0;
2902		dp->d_namlen = 0;
2903		dp->d_name[0] = '\0';
2904		tl = (u_int32_t *)&dp->d_name[4];
2905		*tl++ = cookie.lval[0];
2906		*tl = cookie.lval[1];
2907		dp->d_reclen = DIRBLKSIZ;
2908		uio_iov_base_add(uiop, DIRBLKSIZ);
2909		uio_iov_len_add(uiop, -(DIRBLKSIZ));
2910		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
2911		uiop->uio_offset += DIRBLKSIZ;
2912	}
2913
2914nfsmout:
2915	if (nd->nd_mrep != NULL)
2916		mbuf_freem(nd->nd_mrep);
2917	return (error);
2918}
2919
2920#ifndef APPLE
2921/*
2922 * NFS V3 readdir plus RPC. Used in place of nfsrpc_readdir().
2923 * (Also used for NFS V4 when mount flag set.)
2924 * (ditto above w.r.t. multiple of DIRBLKSIZ, etc.)
2925 */
2926APPLESTATIC int
2927nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2928    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2929    int *eofp, void *stuff)
2930{
2931	int len, left;
2932	struct dirent *dp = NULL;
2933	u_int32_t *tl;
2934	vnode_t newvp = NULLVP;
2935	struct nfsrv_descript nfsd, *nd = &nfsd;
2936	struct nameidata nami, *ndp = &nami;
2937	struct componentname *cnp = &ndp->ni_cnd;
2938	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2939	struct nfsnode *dnp = VTONFS(vp), *np;
2940	struct nfsvattr nfsva;
2941	struct nfsfh *nfhp;
2942	nfsquad_t cookie, ncookie;
2943	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2944	int attrflag, tryformoredirs = 1, eof = 0, gotmnton = 0;
2945	int isdotdot = 0, unlocknewvp = 0;
2946	long dotfileid, dotdotfileid = 0, fileno = 0;
2947	char *cp;
2948	nfsattrbit_t attrbits, dattrbits;
2949	size_t tresid;
2950	u_int32_t *tl2 = NULL, fakefileno = 0xffffffff, rderr;
2951
2952	KASSERT(uiop->uio_iovcnt == 1 &&
2953	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
2954	    ("nfs readdirplusrpc bad uio"));
2955	*attrflagp = 0;
2956	if (eofp != NULL)
2957		*eofp = 0;
2958	ndp->ni_dvp = vp;
2959	nd->nd_mrep = NULL;
2960	cookie.lval[0] = cookiep->nfsuquad[0];
2961	cookie.lval[1] = cookiep->nfsuquad[1];
2962	tresid = uio_uio_resid(uiop);
2963
2964	/*
2965	 * For NFSv4, first create the "." and ".." entries.
2966	 */
2967	if (NFSHASNFSV4(nmp)) {
2968		NFSGETATTR_ATTRBIT(&dattrbits);
2969		NFSZERO_ATTRBIT(&attrbits);
2970		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
2971		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
2972		    NFSATTRBIT_MOUNTEDONFILEID)) {
2973			NFSSETBIT_ATTRBIT(&attrbits,
2974			    NFSATTRBIT_MOUNTEDONFILEID);
2975			gotmnton = 1;
2976		} else {
2977			/*
2978			 * Must fake it. Use the fileno, except when the
2979			 * fsid is != to that of the directory. For that
2980			 * case, generate a fake fileno that is not the same.
2981			 */
2982			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
2983			gotmnton = 0;
2984		}
2985
2986		/*
2987		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
2988		 */
2989		if (uiop->uio_offset == 0) {
2990#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
2991			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred);
2992#else
2993			error = VOP_GETATTR(vp, &nfsva.na_vattr, cred, p);
2994#endif
2995			if (error)
2996			    return (error);
2997			dotfileid = nfsva.na_fileid;
2998			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
2999			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3000			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
3001			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3002			(void) nfsrv_putattrbit(nd, &attrbits);
3003			error = nfscl_request(nd, vp, p, cred, stuff);
3004			if (error)
3005			    return (error);
3006			if (nd->nd_repstat == 0) {
3007			    NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3008			    len = fxdr_unsigned(int, *(tl + 2));
3009			    if (len > 0 && len <= NFSX_V4FHMAX)
3010				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3011			    else
3012				error = EPERM;
3013			    if (!error) {
3014				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3015				nfsva.na_mntonfileno = 0xffffffff;
3016				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
3017				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3018				    NULL, NULL, NULL, p, cred);
3019				if (error) {
3020				    dotdotfileid = dotfileid;
3021				} else if (gotmnton) {
3022				    if (nfsva.na_mntonfileno != 0xffffffff)
3023					dotdotfileid = nfsva.na_mntonfileno;
3024				    else
3025					dotdotfileid = nfsva.na_fileid;
3026				} else if (nfsva.na_filesid[0] ==
3027				    dnp->n_vattr.na_filesid[0] &&
3028				    nfsva.na_filesid[1] ==
3029				    dnp->n_vattr.na_filesid[1]) {
3030				    dotdotfileid = nfsva.na_fileid;
3031				} else {
3032				    do {
3033					fakefileno--;
3034				    } while (fakefileno ==
3035					nfsva.na_fileid);
3036				    dotdotfileid = fakefileno;
3037				}
3038			    }
3039			} else if (nd->nd_repstat == NFSERR_NOENT) {
3040			    /*
3041			     * Lookupp returns NFSERR_NOENT when we are
3042			     * at the root, so just use the current dir.
3043			     */
3044			    nd->nd_repstat = 0;
3045			    dotdotfileid = dotfileid;
3046			} else {
3047			    error = nd->nd_repstat;
3048			}
3049			mbuf_freem(nd->nd_mrep);
3050			if (error)
3051			    return (error);
3052			nd->nd_mrep = NULL;
3053			dp = (struct dirent *)uio_iov_base(uiop);
3054			dp->d_type = DT_DIR;
3055			dp->d_fileno = dotfileid;
3056			dp->d_namlen = 1;
3057			dp->d_name[0] = '.';
3058			dp->d_name[1] = '\0';
3059			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3060			/*
3061			 * Just make these offset cookie 0.
3062			 */
3063			tl = (u_int32_t *)&dp->d_name[4];
3064			*tl++ = 0;
3065			*tl = 0;
3066			blksiz += dp->d_reclen;
3067			uio_uio_resid_add(uiop, -(dp->d_reclen));
3068			uiop->uio_offset += dp->d_reclen;
3069			uio_iov_base_add(uiop, dp->d_reclen);
3070			uio_iov_len_add(uiop, -(dp->d_reclen));
3071			dp = (struct dirent *)uio_iov_base(uiop);
3072			dp->d_type = DT_DIR;
3073			dp->d_fileno = dotdotfileid;
3074			dp->d_namlen = 2;
3075			dp->d_name[0] = '.';
3076			dp->d_name[1] = '.';
3077			dp->d_name[2] = '\0';
3078			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3079			/*
3080			 * Just make these offset cookie 0.
3081			 */
3082			tl = (u_int32_t *)&dp->d_name[4];
3083			*tl++ = 0;
3084			*tl = 0;
3085			blksiz += dp->d_reclen;
3086			uio_uio_resid_add(uiop, -(dp->d_reclen));
3087			uiop->uio_offset += dp->d_reclen;
3088			uio_iov_base_add(uiop, dp->d_reclen);
3089			uio_iov_len_add(uiop, -(dp->d_reclen));
3090		}
3091		NFSREADDIRPLUS_ATTRBIT(&attrbits);
3092		if (gotmnton)
3093			NFSSETBIT_ATTRBIT(&attrbits,
3094			    NFSATTRBIT_MOUNTEDONFILEID);
3095	}
3096
3097	/*
3098	 * Loop around doing readdir rpc's of size nm_readdirsize.
3099	 * The stopping criteria is EOF or buffer full.
3100	 */
3101	while (more_dirs && bigenough) {
3102		*attrflagp = 0;
3103		NFSCL_REQSTART(nd, NFSPROC_READDIRPLUS, vp);
3104 		NFSM_BUILD(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
3105		*tl++ = cookie.lval[0];
3106		*tl++ = cookie.lval[1];
3107		if (cookie.qval == 0) {
3108			*tl++ = 0;
3109			*tl++ = 0;
3110		} else {
3111			NFSLOCKNODE(dnp);
3112			*tl++ = dnp->n_cookieverf.nfsuquad[0];
3113			*tl++ = dnp->n_cookieverf.nfsuquad[1];
3114			NFSUNLOCKNODE(dnp);
3115		}
3116		*tl++ = txdr_unsigned(nmp->nm_readdirsize);
3117		*tl = txdr_unsigned(nmp->nm_readdirsize);
3118		if (nd->nd_flag & ND_NFSV4) {
3119			(void) nfsrv_putattrbit(nd, &attrbits);
3120			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3121			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3122			(void) nfsrv_putattrbit(nd, &dattrbits);
3123		}
3124		error = nfscl_request(nd, vp, p, cred, stuff);
3125		if (error)
3126			return (error);
3127		if (nd->nd_flag & ND_NFSV3)
3128			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3129		if (nd->nd_repstat || error) {
3130			if (!error)
3131				error = nd->nd_repstat;
3132			goto nfsmout;
3133		}
3134		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3135		NFSLOCKNODE(dnp);
3136		dnp->n_cookieverf.nfsuquad[0] = *tl++;
3137		dnp->n_cookieverf.nfsuquad[1] = *tl++;
3138		NFSUNLOCKNODE(dnp);
3139		more_dirs = fxdr_unsigned(int, *tl);
3140		if (!more_dirs)
3141			tryformoredirs = 0;
3142
3143		/* loop thru the dir entries, doctoring them to 4bsd form */
3144		while (more_dirs && bigenough) {
3145			NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3146			if (nd->nd_flag & ND_NFSV4) {
3147				ncookie.lval[0] = *tl++;
3148				ncookie.lval[1] = *tl++;
3149			} else {
3150				fileno = fxdr_unsigned(long, *++tl);
3151				tl++;
3152			}
3153			len = fxdr_unsigned(int, *tl);
3154			if (len <= 0 || len > NFS_MAXNAMLEN) {
3155				error = EBADRPC;
3156				goto nfsmout;
3157			}
3158			tlen = NFSM_RNDUP(len);
3159			if (tlen == len)
3160				tlen += 4;  /* To ensure null termination */
3161			left = DIRBLKSIZ - blksiz;
3162			if ((tlen + DIRHDSIZ + NFSX_HYPER) > left) {
3163				dp->d_reclen += left;
3164				uio_iov_base_add(uiop, left);
3165				uio_iov_len_add(uiop, -(left));
3166				uio_uio_resid_add(uiop, -(left));
3167				uiop->uio_offset += left;
3168				blksiz = 0;
3169			}
3170			if ((tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
3171				bigenough = 0;
3172			if (bigenough) {
3173				dp = (struct dirent *)uio_iov_base(uiop);
3174				dp->d_namlen = len;
3175				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
3176				dp->d_type = DT_UNKNOWN;
3177				blksiz += dp->d_reclen;
3178				if (blksiz == DIRBLKSIZ)
3179					blksiz = 0;
3180				uio_uio_resid_add(uiop, -(DIRHDSIZ));
3181				uiop->uio_offset += DIRHDSIZ;
3182				uio_iov_base_add(uiop, DIRHDSIZ);
3183				uio_iov_len_add(uiop, -(DIRHDSIZ));
3184				cnp->cn_nameptr = uio_iov_base(uiop);
3185				cnp->cn_namelen = len;
3186				NFSCNHASHZERO(cnp);
3187				error = nfsm_mbufuio(nd, uiop, len);
3188				if (error)
3189					goto nfsmout;
3190				cp = uio_iov_base(uiop);
3191				tlen -= len;
3192				*cp = '\0';
3193				cp += tlen;	/* points to cookie storage */
3194				tl2 = (u_int32_t *)cp;
3195				if (len == 2 && cnp->cn_nameptr[0] == '.' &&
3196				    cnp->cn_nameptr[1] == '.')
3197					isdotdot = 1;
3198				else
3199					isdotdot = 0;
3200				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
3201				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
3202				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
3203				uiop->uio_offset += (tlen + NFSX_HYPER);
3204			} else {
3205				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3206				if (error)
3207					goto nfsmout;
3208			}
3209			nfhp = NULL;
3210			if (nd->nd_flag & ND_NFSV3) {
3211				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3212				ncookie.lval[0] = *tl++;
3213				ncookie.lval[1] = *tl++;
3214				attrflag = fxdr_unsigned(int, *tl);
3215				if (attrflag) {
3216				  error = nfsm_loadattr(nd, &nfsva);
3217				  if (error)
3218					goto nfsmout;
3219				}
3220				NFSM_DISSECT(tl,u_int32_t *,NFSX_UNSIGNED);
3221				if (*tl) {
3222					error = nfsm_getfh(nd, &nfhp);
3223					if (error)
3224					    goto nfsmout;
3225				}
3226				if (!attrflag && nfhp != NULL) {
3227					FREE((caddr_t)nfhp, M_NFSFH);
3228					nfhp = NULL;
3229				}
3230			} else {
3231				rderr = 0;
3232				nfsva.na_mntonfileno = 0xffffffff;
3233				error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
3234				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3235				    NULL, NULL, &rderr, p, cred);
3236				if (error)
3237					goto nfsmout;
3238			}
3239
3240			if (bigenough) {
3241			    if (nd->nd_flag & ND_NFSV4) {
3242				if (rderr) {
3243				    dp->d_fileno = 0;
3244				} else if (gotmnton) {
3245				    if (nfsva.na_mntonfileno != 0xffffffff)
3246					dp->d_fileno = nfsva.na_mntonfileno;
3247				    else
3248					dp->d_fileno = nfsva.na_fileid;
3249				} else if (nfsva.na_filesid[0] ==
3250				    dnp->n_vattr.na_filesid[0] &&
3251				    nfsva.na_filesid[1] ==
3252				    dnp->n_vattr.na_filesid[1]) {
3253				    dp->d_fileno = nfsva.na_fileid;
3254				} else {
3255				    do {
3256					fakefileno--;
3257				    } while (fakefileno ==
3258					nfsva.na_fileid);
3259				    dp->d_fileno = fakefileno;
3260				}
3261			    } else {
3262				dp->d_fileno = fileno;
3263			    }
3264			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
3265				ncookie.lval[0];
3266			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
3267				ncookie.lval[1];
3268
3269			    if (nfhp != NULL) {
3270				if (NFSRV_CMPFH(nfhp->nfh_fh, nfhp->nfh_len,
3271				    dnp->n_fhp->nfh_fh, dnp->n_fhp->nfh_len)) {
3272				    VREF(vp);
3273				    newvp = vp;
3274				    unlocknewvp = 0;
3275				    FREE((caddr_t)nfhp, M_NFSFH);
3276				    np = dnp;
3277				} else if (isdotdot != 0) {
3278				    /*
3279				     * Skip doing a nfscl_nget() call for "..".
3280				     * There's a race between acquiring the nfs
3281				     * node here and lookups that look for the
3282				     * directory being read (in the parent).
3283				     * It would try to get a lock on ".." here,
3284				     * owning the lock on the directory being
3285				     * read. Lookup will hold the lock on ".."
3286				     * and try to acquire the lock on the
3287				     * directory being read.
3288				     * If the directory is unlocked/relocked,
3289				     * then there is a LOR with the buflock
3290				     * vp is relocked.
3291				     */
3292				    free(nfhp, M_NFSFH);
3293				} else {
3294				    error = nfscl_nget(vnode_mount(vp), vp,
3295				      nfhp, cnp, p, &np, NULL, LK_EXCLUSIVE);
3296				    if (!error) {
3297					newvp = NFSTOV(np);
3298					unlocknewvp = 1;
3299				    }
3300				}
3301				nfhp = NULL;
3302				if (newvp != NULLVP) {
3303				    error = nfscl_loadattrcache(&newvp,
3304					&nfsva, NULL, NULL, 0, 0);
3305				    if (error) {
3306					if (unlocknewvp)
3307					    vput(newvp);
3308					else
3309					    vrele(newvp);
3310					goto nfsmout;
3311				    }
3312				    dp->d_type =
3313					vtonfs_dtype(np->n_vattr.na_type);
3314				    ndp->ni_vp = newvp;
3315				    NFSCNHASH(cnp, HASHINIT);
3316				    if (cnp->cn_namelen <= NCHNAMLEN) {
3317					np->n_ctime = np->n_vattr.na_ctime;
3318					cache_enter(ndp->ni_dvp,ndp->ni_vp,cnp);
3319				    }
3320				    if (unlocknewvp)
3321					vput(newvp);
3322				    else
3323					vrele(newvp);
3324				    newvp = NULLVP;
3325				}
3326			    }
3327			} else if (nfhp != NULL) {
3328			    FREE((caddr_t)nfhp, M_NFSFH);
3329			}
3330			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3331			more_dirs = fxdr_unsigned(int, *tl);
3332		}
3333		/*
3334		 * If at end of rpc data, get the eof boolean
3335		 */
3336		if (!more_dirs) {
3337			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3338			eof = fxdr_unsigned(int, *tl);
3339			if (tryformoredirs)
3340				more_dirs = !eof;
3341			if (nd->nd_flag & ND_NFSV4) {
3342				error = nfscl_postop_attr(nd, nap, attrflagp,
3343				    stuff);
3344				if (error)
3345					goto nfsmout;
3346			}
3347		}
3348		mbuf_freem(nd->nd_mrep);
3349		nd->nd_mrep = NULL;
3350	}
3351	/*
3352	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3353	 * by increasing d_reclen for the last record.
3354	 */
3355	if (blksiz > 0) {
3356		left = DIRBLKSIZ - blksiz;
3357		dp->d_reclen += left;
3358		uio_iov_base_add(uiop, left);
3359		uio_iov_len_add(uiop, -(left));
3360		uio_uio_resid_add(uiop, -(left));
3361		uiop->uio_offset += left;
3362	}
3363
3364	/*
3365	 * If returning no data, assume end of file.
3366	 * If not bigenough, return not end of file, since you aren't
3367	 *    returning all the data
3368	 * Otherwise, return the eof flag from the server.
3369	 */
3370	if (eofp != NULL) {
3371		if (tresid == uio_uio_resid(uiop))
3372			*eofp = 1;
3373		else if (!bigenough)
3374			*eofp = 0;
3375		else
3376			*eofp = eof;
3377	}
3378
3379	/*
3380	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
3381	 */
3382	while (uio_uio_resid(uiop) > 0 && uio_uio_resid(uiop) != tresid) {
3383		dp = (struct dirent *)uio_iov_base(uiop);
3384		dp->d_type = DT_UNKNOWN;
3385		dp->d_fileno = 0;
3386		dp->d_namlen = 0;
3387		dp->d_name[0] = '\0';
3388		tl = (u_int32_t *)&dp->d_name[4];
3389		*tl++ = cookie.lval[0];
3390		*tl = cookie.lval[1];
3391		dp->d_reclen = DIRBLKSIZ;
3392		uio_iov_base_add(uiop, DIRBLKSIZ);
3393		uio_iov_len_add(uiop, -(DIRBLKSIZ));
3394		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
3395		uiop->uio_offset += DIRBLKSIZ;
3396	}
3397
3398nfsmout:
3399	if (nd->nd_mrep != NULL)
3400		mbuf_freem(nd->nd_mrep);
3401	return (error);
3402}
3403#endif	/* !APPLE */
3404
3405/*
3406 * Nfs commit rpc
3407 */
3408APPLESTATIC int
3409nfsrpc_commit(vnode_t vp, u_quad_t offset, int cnt, struct ucred *cred,
3410    NFSPROC_T *p, u_char *verfp, struct nfsvattr *nap, int *attrflagp,
3411    void *stuff)
3412{
3413	u_int32_t *tl;
3414	struct nfsrv_descript nfsd, *nd = &nfsd;
3415	nfsattrbit_t attrbits;
3416	int error;
3417
3418	*attrflagp = 0;
3419	NFSCL_REQSTART(nd, NFSPROC_COMMIT, vp);
3420	NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3421	txdr_hyper(offset, tl);
3422	tl += 2;
3423	*tl = txdr_unsigned(cnt);
3424	if (nd->nd_flag & ND_NFSV4) {
3425		/*
3426		 * And do a Getattr op.
3427		 */
3428		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3429		*tl = txdr_unsigned(NFSV4OP_GETATTR);
3430		NFSGETATTR_ATTRBIT(&attrbits);
3431		(void) nfsrv_putattrbit(nd, &attrbits);
3432	}
3433	error = nfscl_request(nd, vp, p, cred, stuff);
3434	if (error)
3435		return (error);
3436	error = nfscl_wcc_data(nd, vp, nap, attrflagp, NULL, stuff);
3437	if (!error && !nd->nd_repstat) {
3438		NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
3439		NFSBCOPY((caddr_t)tl, verfp, NFSX_VERF);
3440		if (nd->nd_flag & ND_NFSV4)
3441			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3442	}
3443nfsmout:
3444	if (!error && nd->nd_repstat)
3445		error = nd->nd_repstat;
3446	mbuf_freem(nd->nd_mrep);
3447	return (error);
3448}
3449
3450/*
3451 * NFS byte range lock rpc.
3452 * (Mostly just calls one of the three lower level RPC routines.)
3453 */
3454APPLESTATIC int
3455nfsrpc_advlock(vnode_t vp, off_t size, int op, struct flock *fl,
3456    int reclaim, struct ucred *cred, NFSPROC_T *p)
3457{
3458	struct nfscllockowner *lp;
3459	struct nfsclclient *clp;
3460	struct nfsfh *nfhp;
3461	struct nfsrv_descript nfsd, *nd = &nfsd;
3462	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3463	u_int64_t off, len;
3464	off_t start, end;
3465	u_int32_t clidrev = 0;
3466	int error = 0, newone = 0, expireret = 0, retrycnt, donelocally;
3467	int callcnt, dorpc;
3468
3469	/*
3470	 * Convert the flock structure into a start and end and do POSIX
3471	 * bounds checking.
3472	 */
3473	switch (fl->l_whence) {
3474	case SEEK_SET:
3475	case SEEK_CUR:
3476		/*
3477		 * Caller is responsible for adding any necessary offset
3478		 * when SEEK_CUR is used.
3479		 */
3480		start = fl->l_start;
3481		off = fl->l_start;
3482		break;
3483	case SEEK_END:
3484		start = size + fl->l_start;
3485		off = size + fl->l_start;
3486		break;
3487	default:
3488		return (EINVAL);
3489	};
3490	if (start < 0)
3491		return (EINVAL);
3492	if (fl->l_len != 0) {
3493		end = start + fl->l_len - 1;
3494		if (end < start)
3495			return (EINVAL);
3496	}
3497
3498	len = fl->l_len;
3499	if (len == 0)
3500		len = NFS64BITSSET;
3501	retrycnt = 0;
3502	do {
3503	    nd->nd_repstat = 0;
3504	    if (op == F_GETLK) {
3505		error = nfscl_getcl(vp, cred, p, &clp);
3506		if (error)
3507			return (error);
3508		error = nfscl_lockt(vp, clp, off, len, fl, p);
3509		if (!error) {
3510			clidrev = clp->nfsc_clientidrev;
3511			error = nfsrpc_lockt(nd, vp, clp, off, len, fl, cred,
3512			    p);
3513		} else if (error == -1) {
3514			error = 0;
3515		}
3516		nfscl_clientrelease(clp);
3517	    } else if (op == F_UNLCK && fl->l_type == F_UNLCK) {
3518		/*
3519		 * We must loop around for all lockowner cases.
3520		 */
3521		callcnt = 0;
3522		error = nfscl_getcl(vp, cred, p, &clp);
3523		if (error)
3524			return (error);
3525		do {
3526		    error = nfscl_relbytelock(vp, off, len, cred, p, callcnt,
3527			clp, &lp, &dorpc);
3528		    /*
3529		     * If it returns a NULL lp, we're done.
3530		     */
3531		    if (lp == NULL) {
3532			if (callcnt == 0)
3533			    nfscl_clientrelease(clp);
3534			else
3535			    nfscl_releasealllocks(clp, vp, p);
3536			return (error);
3537		    }
3538		    if (nmp->nm_clp != NULL)
3539			clidrev = nmp->nm_clp->nfsc_clientidrev;
3540		    else
3541			clidrev = 0;
3542		    /*
3543		     * If the server doesn't support Posix lock semantics,
3544		     * only allow locks on the entire file, since it won't
3545		     * handle overlapping byte ranges.
3546		     * There might still be a problem when a lock
3547		     * upgrade/downgrade (read<->write) occurs, since the
3548		     * server "might" expect an unlock first?
3549		     */
3550		    if (dorpc && (lp->nfsl_open->nfso_posixlock ||
3551			(off == 0 && len == NFS64BITSSET))) {
3552			/*
3553			 * Since the lock records will go away, we must
3554			 * wait for grace and delay here.
3555			 */
3556			do {
3557			    error = nfsrpc_locku(nd, nmp, lp, off, len,
3558				NFSV4LOCKT_READ, cred, p, 0);
3559			    if ((nd->nd_repstat == NFSERR_GRACE ||
3560				 nd->nd_repstat == NFSERR_DELAY) &&
3561				error == 0)
3562				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3563				    "nfs_advlock");
3564			} while ((nd->nd_repstat == NFSERR_GRACE ||
3565			    nd->nd_repstat == NFSERR_DELAY) && error == 0);
3566		    }
3567		    callcnt++;
3568		} while (error == 0 && nd->nd_repstat == 0);
3569		nfscl_releasealllocks(clp, vp, p);
3570	    } else if (op == F_SETLK) {
3571		error = nfscl_getbytelock(vp, off, len, fl->l_type, cred, p,
3572		    NULL, 0, NULL, NULL, &lp, &newone, &donelocally);
3573		if (error || donelocally) {
3574			return (error);
3575		}
3576		if (nmp->nm_clp != NULL)
3577			clidrev = nmp->nm_clp->nfsc_clientidrev;
3578		else
3579			clidrev = 0;
3580		nfhp = VTONFS(vp)->n_fhp;
3581		if (!lp->nfsl_open->nfso_posixlock &&
3582		    (off != 0 || len != NFS64BITSSET)) {
3583			error = EINVAL;
3584		} else {
3585			error = nfsrpc_lock(nd, nmp, vp, nfhp->nfh_fh,
3586			    nfhp->nfh_len, lp, newone, reclaim, off,
3587			    len, fl->l_type, cred, p, 0);
3588		}
3589		if (!error)
3590			error = nd->nd_repstat;
3591		nfscl_lockrelease(lp, error, newone);
3592	    } else {
3593		error = EINVAL;
3594	    }
3595	    if (!error)
3596	        error = nd->nd_repstat;
3597	    if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
3598		error == NFSERR_STALEDONTRECOVER ||
3599		error == NFSERR_STALECLIENTID || error == NFSERR_DELAY) {
3600		(void) nfs_catnap(PZERO, error, "nfs_advlock");
3601	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
3602		&& clidrev != 0) {
3603		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
3604		retrycnt++;
3605	    }
3606	} while (error == NFSERR_GRACE ||
3607	    error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
3608	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_STALESTATEID ||
3609	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
3610	     expireret == 0 && clidrev != 0 && retrycnt < 4));
3611	if (error && retrycnt >= 4)
3612		error = EIO;
3613	return (error);
3614}
3615
3616/*
3617 * The lower level routine for the LockT case.
3618 */
3619APPLESTATIC int
3620nfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
3621    struct nfsclclient *clp, u_int64_t off, u_int64_t len, struct flock *fl,
3622    struct ucred *cred, NFSPROC_T *p)
3623{
3624	u_int32_t *tl;
3625	int error, type, size;
3626	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3627
3628	NFSCL_REQSTART(nd, NFSPROC_LOCKT, vp);
3629	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3630	if (fl->l_type == F_RDLCK)
3631		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3632	else
3633		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3634	txdr_hyper(off, tl);
3635	tl += 2;
3636	txdr_hyper(len, tl);
3637	tl += 2;
3638	*tl++ = clp->nfsc_clientid.lval[0];
3639	*tl = clp->nfsc_clientid.lval[1];
3640	nfscl_filllockowner(p, own);
3641	(void) nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN);
3642	error = nfscl_request(nd, vp, p, cred, NULL);
3643	if (error)
3644		return (error);
3645	if (nd->nd_repstat == 0) {
3646		fl->l_type = F_UNLCK;
3647	} else if (nd->nd_repstat == NFSERR_DENIED) {
3648		nd->nd_repstat = 0;
3649		fl->l_whence = SEEK_SET;
3650		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3651		fl->l_start = fxdr_hyper(tl);
3652		tl += 2;
3653		len = fxdr_hyper(tl);
3654		tl += 2;
3655		if (len == NFS64BITSSET)
3656			fl->l_len = 0;
3657		else
3658			fl->l_len = len;
3659		type = fxdr_unsigned(int, *tl++);
3660		if (type == NFSV4LOCKT_WRITE)
3661			fl->l_type = F_WRLCK;
3662		else
3663			fl->l_type = F_RDLCK;
3664		/*
3665		 * XXX For now, I have no idea what to do with the
3666		 * conflicting lock_owner, so I'll just set the pid == 0
3667		 * and skip over the lock_owner.
3668		 */
3669		fl->l_pid = (pid_t)0;
3670		tl += 2;
3671		size = fxdr_unsigned(int, *tl);
3672		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3673			error = EBADRPC;
3674		if (!error)
3675			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3676	} else if (nd->nd_repstat == NFSERR_STALECLIENTID)
3677		nfscl_initiate_recovery(clp);
3678nfsmout:
3679	mbuf_freem(nd->nd_mrep);
3680	return (error);
3681}
3682
3683/*
3684 * Lower level function that performs the LockU RPC.
3685 */
3686static int
3687nfsrpc_locku(struct nfsrv_descript *nd, struct nfsmount *nmp,
3688    struct nfscllockowner *lp, u_int64_t off, u_int64_t len,
3689    u_int32_t type, struct ucred *cred, NFSPROC_T *p, int syscred)
3690{
3691	u_int32_t *tl;
3692	int error;
3693
3694	nfscl_reqstart(nd, NFSPROC_LOCKU, nmp, lp->nfsl_open->nfso_fh,
3695	    lp->nfsl_open->nfso_fhlen, NULL);
3696	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
3697	*tl++ = txdr_unsigned(type);
3698	*tl = txdr_unsigned(lp->nfsl_seqid);
3699	if (nfstest_outofseq &&
3700	    (arc4random() % nfstest_outofseq) == 0)
3701		*tl = txdr_unsigned(lp->nfsl_seqid + 1);
3702	tl++;
3703	*tl++ = lp->nfsl_stateid.seqid;
3704	*tl++ = lp->nfsl_stateid.other[0];
3705	*tl++ = lp->nfsl_stateid.other[1];
3706	*tl++ = lp->nfsl_stateid.other[2];
3707	txdr_hyper(off, tl);
3708	tl += 2;
3709	txdr_hyper(len, tl);
3710	if (syscred)
3711		nd->nd_flag |= ND_USEGSSNAME;
3712	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
3713	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
3714	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
3715	if (error)
3716		return (error);
3717	if (nd->nd_repstat == 0) {
3718		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3719		lp->nfsl_stateid.seqid = *tl++;
3720		lp->nfsl_stateid.other[0] = *tl++;
3721		lp->nfsl_stateid.other[1] = *tl++;
3722		lp->nfsl_stateid.other[2] = *tl;
3723	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
3724		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
3725nfsmout:
3726	mbuf_freem(nd->nd_mrep);
3727	return (error);
3728}
3729
3730/*
3731 * The actual Lock RPC.
3732 */
3733APPLESTATIC int
3734nfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
3735    u_int8_t *nfhp, int fhlen, struct nfscllockowner *lp, int newone,
3736    int reclaim, u_int64_t off, u_int64_t len, short type, struct ucred *cred,
3737    NFSPROC_T *p, int syscred)
3738{
3739	u_int32_t *tl;
3740	int error, size;
3741
3742	nfscl_reqstart(nd, NFSPROC_LOCK, nmp, nfhp, fhlen, NULL);
3743	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3744	if (type == F_RDLCK)
3745		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3746	else
3747		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3748	*tl++ = txdr_unsigned(reclaim);
3749	txdr_hyper(off, tl);
3750	tl += 2;
3751	txdr_hyper(len, tl);
3752	tl += 2;
3753	if (newone) {
3754	    *tl = newnfs_true;
3755	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
3756		2 * NFSX_UNSIGNED + NFSX_HYPER);
3757	    *tl++ = txdr_unsigned(lp->nfsl_open->nfso_own->nfsow_seqid);
3758	    *tl++ = lp->nfsl_open->nfso_stateid.seqid;
3759	    *tl++ = lp->nfsl_open->nfso_stateid.other[0];
3760	    *tl++ = lp->nfsl_open->nfso_stateid.other[1];
3761	    *tl++ = lp->nfsl_open->nfso_stateid.other[2];
3762	    *tl++ = txdr_unsigned(lp->nfsl_seqid);
3763	    *tl++ = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[0];
3764	    *tl = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[1];
3765	    (void) nfsm_strtom(nd, lp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
3766	} else {
3767	    *tl = newnfs_false;
3768	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3769	    *tl++ = lp->nfsl_stateid.seqid;
3770	    *tl++ = lp->nfsl_stateid.other[0];
3771	    *tl++ = lp->nfsl_stateid.other[1];
3772	    *tl++ = lp->nfsl_stateid.other[2];
3773	    *tl = txdr_unsigned(lp->nfsl_seqid);
3774	    if (nfstest_outofseq &&
3775		(arc4random() % nfstest_outofseq) == 0)
3776		    *tl = txdr_unsigned(lp->nfsl_seqid + 1);
3777	}
3778	if (syscred)
3779		nd->nd_flag |= ND_USEGSSNAME;
3780	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
3781	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
3782	if (error)
3783		return (error);
3784	if (newone)
3785	    NFSCL_INCRSEQID(lp->nfsl_open->nfso_own->nfsow_seqid, nd);
3786	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
3787	if (nd->nd_repstat == 0) {
3788		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3789		lp->nfsl_stateid.seqid = *tl++;
3790		lp->nfsl_stateid.other[0] = *tl++;
3791		lp->nfsl_stateid.other[1] = *tl++;
3792		lp->nfsl_stateid.other[2] = *tl;
3793	} else if (nd->nd_repstat == NFSERR_DENIED) {
3794		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3795		size = fxdr_unsigned(int, *(tl + 7));
3796		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3797			error = EBADRPC;
3798		if (!error)
3799			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3800	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
3801		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
3802nfsmout:
3803	mbuf_freem(nd->nd_mrep);
3804	return (error);
3805}
3806
3807/*
3808 * nfs statfs rpc
3809 * (always called with the vp for the mount point)
3810 */
3811APPLESTATIC int
3812nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
3813    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3814    void *stuff)
3815{
3816	u_int32_t *tl = NULL;
3817	struct nfsrv_descript nfsd, *nd = &nfsd;
3818	struct nfsmount *nmp;
3819	nfsattrbit_t attrbits;
3820	int error;
3821
3822	*attrflagp = 0;
3823	nmp = VFSTONFS(vnode_mount(vp));
3824	if (NFSHASNFSV4(nmp)) {
3825		/*
3826		 * For V4, you actually do a getattr.
3827		 */
3828		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
3829		NFSSTATFS_GETATTRBIT(&attrbits);
3830		(void) nfsrv_putattrbit(nd, &attrbits);
3831		nd->nd_flag |= ND_USEGSSNAME;
3832		error = nfscl_request(nd, vp, p, cred, stuff);
3833		if (error)
3834			return (error);
3835		if (nd->nd_repstat == 0) {
3836			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
3837			    NULL, NULL, sbp, fsp, NULL, 0, NULL, NULL, NULL, p,
3838			    cred);
3839			if (!error) {
3840				nmp->nm_fsid[0] = nap->na_filesid[0];
3841				nmp->nm_fsid[1] = nap->na_filesid[1];
3842				NFSSETHASSETFSID(nmp);
3843				*attrflagp = 1;
3844			}
3845		} else {
3846			error = nd->nd_repstat;
3847		}
3848		if (error)
3849			goto nfsmout;
3850	} else {
3851		NFSCL_REQSTART(nd, NFSPROC_FSSTAT, vp);
3852		error = nfscl_request(nd, vp, p, cred, stuff);
3853		if (error)
3854			return (error);
3855		if (nd->nd_flag & ND_NFSV3) {
3856			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3857			if (error)
3858				goto nfsmout;
3859		}
3860		if (nd->nd_repstat) {
3861			error = nd->nd_repstat;
3862			goto nfsmout;
3863		}
3864		NFSM_DISSECT(tl, u_int32_t *,
3865		    NFSX_STATFS(nd->nd_flag & ND_NFSV3));
3866	}
3867	if (NFSHASNFSV3(nmp)) {
3868		sbp->sf_tbytes = fxdr_hyper(tl); tl += 2;
3869		sbp->sf_fbytes = fxdr_hyper(tl); tl += 2;
3870		sbp->sf_abytes = fxdr_hyper(tl); tl += 2;
3871		sbp->sf_tfiles = fxdr_hyper(tl); tl += 2;
3872		sbp->sf_ffiles = fxdr_hyper(tl); tl += 2;
3873		sbp->sf_afiles = fxdr_hyper(tl); tl += 2;
3874		sbp->sf_invarsec = fxdr_unsigned(u_int32_t, *tl);
3875	} else if (NFSHASNFSV4(nmp) == 0) {
3876		sbp->sf_tsize = fxdr_unsigned(u_int32_t, *tl++);
3877		sbp->sf_bsize = fxdr_unsigned(u_int32_t, *tl++);
3878		sbp->sf_blocks = fxdr_unsigned(u_int32_t, *tl++);
3879		sbp->sf_bfree = fxdr_unsigned(u_int32_t, *tl++);
3880		sbp->sf_bavail = fxdr_unsigned(u_int32_t, *tl);
3881	}
3882nfsmout:
3883	mbuf_freem(nd->nd_mrep);
3884	return (error);
3885}
3886
3887/*
3888 * nfs pathconf rpc
3889 */
3890APPLESTATIC int
3891nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc,
3892    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3893    void *stuff)
3894{
3895	struct nfsrv_descript nfsd, *nd = &nfsd;
3896	struct nfsmount *nmp;
3897	u_int32_t *tl;
3898	nfsattrbit_t attrbits;
3899	int error;
3900
3901	*attrflagp = 0;
3902	nmp = VFSTONFS(vnode_mount(vp));
3903	if (NFSHASNFSV4(nmp)) {
3904		/*
3905		 * For V4, you actually do a getattr.
3906		 */
3907		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
3908		NFSPATHCONF_GETATTRBIT(&attrbits);
3909		(void) nfsrv_putattrbit(nd, &attrbits);
3910		nd->nd_flag |= ND_USEGSSNAME;
3911		error = nfscl_request(nd, vp, p, cred, stuff);
3912		if (error)
3913			return (error);
3914		if (nd->nd_repstat == 0) {
3915			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
3916			    pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p,
3917			    cred);
3918			if (!error)
3919				*attrflagp = 1;
3920		} else {
3921			error = nd->nd_repstat;
3922		}
3923	} else {
3924		NFSCL_REQSTART(nd, NFSPROC_PATHCONF, vp);
3925		error = nfscl_request(nd, vp, p, cred, stuff);
3926		if (error)
3927			return (error);
3928		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3929		if (nd->nd_repstat && !error)
3930			error = nd->nd_repstat;
3931		if (!error) {
3932			NFSM_DISSECT(tl, u_int32_t *, NFSX_V3PATHCONF);
3933			pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl++);
3934			pc->pc_namemax = fxdr_unsigned(u_int32_t, *tl++);
3935			pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl++);
3936			pc->pc_chownrestricted =
3937			    fxdr_unsigned(u_int32_t, *tl++);
3938			pc->pc_caseinsensitive =
3939			    fxdr_unsigned(u_int32_t, *tl++);
3940			pc->pc_casepreserving = fxdr_unsigned(u_int32_t, *tl);
3941		}
3942	}
3943nfsmout:
3944	mbuf_freem(nd->nd_mrep);
3945	return (error);
3946}
3947
3948/*
3949 * nfs version 3 fsinfo rpc call
3950 */
3951APPLESTATIC int
3952nfsrpc_fsinfo(vnode_t vp, struct nfsfsinfo *fsp, struct ucred *cred,
3953    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
3954{
3955	u_int32_t *tl;
3956	struct nfsrv_descript nfsd, *nd = &nfsd;
3957	int error;
3958
3959	*attrflagp = 0;
3960	NFSCL_REQSTART(nd, NFSPROC_FSINFO, vp);
3961	error = nfscl_request(nd, vp, p, cred, stuff);
3962	if (error)
3963		return (error);
3964	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3965	if (nd->nd_repstat && !error)
3966		error = nd->nd_repstat;
3967	if (!error) {
3968		NFSM_DISSECT(tl, u_int32_t *, NFSX_V3FSINFO);
3969		fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *tl++);
3970		fsp->fs_rtpref = fxdr_unsigned(u_int32_t, *tl++);
3971		fsp->fs_rtmult = fxdr_unsigned(u_int32_t, *tl++);
3972		fsp->fs_wtmax = fxdr_unsigned(u_int32_t, *tl++);
3973		fsp->fs_wtpref = fxdr_unsigned(u_int32_t, *tl++);
3974		fsp->fs_wtmult = fxdr_unsigned(u_int32_t, *tl++);
3975		fsp->fs_dtpref = fxdr_unsigned(u_int32_t, *tl++);
3976		fsp->fs_maxfilesize = fxdr_hyper(tl);
3977		tl += 2;
3978		fxdr_nfsv3time(tl, &fsp->fs_timedelta);
3979		tl += 2;
3980		fsp->fs_properties = fxdr_unsigned(u_int32_t, *tl);
3981	}
3982nfsmout:
3983	mbuf_freem(nd->nd_mrep);
3984	return (error);
3985}
3986
3987/*
3988 * This function performs the Renew RPC.
3989 */
3990APPLESTATIC int
3991nfsrpc_renew(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
3992{
3993	u_int32_t *tl;
3994	struct nfsrv_descript nfsd;
3995	struct nfsrv_descript *nd = &nfsd;
3996	struct nfsmount *nmp;
3997	int error;
3998
3999	nmp = clp->nfsc_nmp;
4000	if (nmp == NULL)
4001		return (0);
4002	nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL);
4003	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4004	*tl++ = clp->nfsc_clientid.lval[0];
4005	*tl = clp->nfsc_clientid.lval[1];
4006	nd->nd_flag |= ND_USEGSSNAME;
4007	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4008		NFS_PROG, NFS_VER4, NULL, 1, NULL);
4009	if (error)
4010		return (error);
4011	error = nd->nd_repstat;
4012	mbuf_freem(nd->nd_mrep);
4013	return (error);
4014}
4015
4016/*
4017 * This function performs the Releaselockowner RPC.
4018 */
4019APPLESTATIC int
4020nfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
4021    struct ucred *cred, NFSPROC_T *p)
4022{
4023	struct nfsrv_descript nfsd, *nd = &nfsd;
4024	u_int32_t *tl;
4025	int error;
4026
4027	nfscl_reqstart(nd, NFSPROC_RELEASELCKOWN, nmp, NULL, 0, NULL);
4028	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4029	*tl++ = nmp->nm_clp->nfsc_clientid.lval[0];
4030	*tl = nmp->nm_clp->nfsc_clientid.lval[1];
4031	(void) nfsm_strtom(nd, lp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
4032	nd->nd_flag |= ND_USEGSSNAME;
4033	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4034	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
4035	if (error)
4036		return (error);
4037	error = nd->nd_repstat;
4038	mbuf_freem(nd->nd_mrep);
4039	return (error);
4040}
4041
4042/*
4043 * This function performs the Compound to get the mount pt FH.
4044 */
4045APPLESTATIC int
4046nfsrpc_getdirpath(struct nfsmount *nmp, u_char *dirpath, struct ucred *cred,
4047    NFSPROC_T *p)
4048{
4049	u_int32_t *tl;
4050	struct nfsrv_descript nfsd;
4051	struct nfsrv_descript *nd = &nfsd;
4052	u_char *cp, *cp2;
4053	int error, cnt, len, setnil;
4054	u_int32_t *opcntp;
4055
4056	nfscl_reqstart(nd, NFSPROC_PUTROOTFH, nmp, NULL, 0, &opcntp);
4057	cp = dirpath;
4058	cnt = 0;
4059	do {
4060		setnil = 0;
4061		while (*cp == '/')
4062			cp++;
4063		cp2 = cp;
4064		while (*cp2 != '\0' && *cp2 != '/')
4065			cp2++;
4066		if (*cp2 == '/') {
4067			setnil = 1;
4068			*cp2 = '\0';
4069		}
4070		if (cp2 != cp) {
4071			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4072			*tl = txdr_unsigned(NFSV4OP_LOOKUP);
4073			nfsm_strtom(nd, cp, strlen(cp));
4074			cnt++;
4075		}
4076		if (setnil)
4077			*cp2++ = '/';
4078		cp = cp2;
4079	} while (*cp != '\0');
4080	*opcntp = txdr_unsigned(2 + cnt);
4081	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4082	*tl = txdr_unsigned(NFSV4OP_GETFH);
4083	nd->nd_flag |= ND_USEGSSNAME;
4084	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4085		NFS_PROG, NFS_VER4, NULL, 1, NULL);
4086	if (error)
4087		return (error);
4088	if (nd->nd_repstat == 0) {
4089		NFSM_DISSECT(tl, u_int32_t *, (3 + 2 * cnt) * NFSX_UNSIGNED);
4090		tl += (2 + 2 * cnt);
4091		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
4092			len > NFSX_FHMAX) {
4093			nd->nd_repstat = NFSERR_BADXDR;
4094		} else {
4095			nd->nd_repstat = nfsrv_mtostr(nd, nmp->nm_fh, len);
4096			if (nd->nd_repstat == 0)
4097				nmp->nm_fhsize = len;
4098		}
4099	}
4100	error = nd->nd_repstat;
4101nfsmout:
4102	mbuf_freem(nd->nd_mrep);
4103	return (error);
4104}
4105
4106/*
4107 * This function performs the Delegreturn RPC.
4108 */
4109APPLESTATIC int
4110nfsrpc_delegreturn(struct nfscldeleg *dp, struct ucred *cred,
4111    struct nfsmount *nmp, NFSPROC_T *p, int syscred)
4112{
4113	u_int32_t *tl;
4114	struct nfsrv_descript nfsd;
4115	struct nfsrv_descript *nd = &nfsd;
4116	int error;
4117
4118	nfscl_reqstart(nd, NFSPROC_DELEGRETURN, nmp, dp->nfsdl_fh,
4119	    dp->nfsdl_fhlen, NULL);
4120	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
4121	*tl++ = dp->nfsdl_stateid.seqid;
4122	*tl++ = dp->nfsdl_stateid.other[0];
4123	*tl++ = dp->nfsdl_stateid.other[1];
4124	*tl = dp->nfsdl_stateid.other[2];
4125	if (syscred)
4126		nd->nd_flag |= ND_USEGSSNAME;
4127	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4128	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
4129	if (error)
4130		return (error);
4131	error = nd->nd_repstat;
4132	mbuf_freem(nd->nd_mrep);
4133	return (error);
4134}
4135
4136/*
4137 * nfs getacl call.
4138 */
4139APPLESTATIC int
4140nfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4141    struct acl *aclp, void *stuff)
4142{
4143	struct nfsrv_descript nfsd, *nd = &nfsd;
4144	int error;
4145	nfsattrbit_t attrbits;
4146	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4147
4148	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4149		return (EOPNOTSUPP);
4150	NFSCL_REQSTART(nd, NFSPROC_GETACL, vp);
4151	NFSZERO_ATTRBIT(&attrbits);
4152	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4153	(void) nfsrv_putattrbit(nd, &attrbits);
4154	error = nfscl_request(nd, vp, p, cred, stuff);
4155	if (error)
4156		return (error);
4157	if (!nd->nd_repstat)
4158		error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
4159		    NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, p, cred);
4160	else
4161		error = nd->nd_repstat;
4162	mbuf_freem(nd->nd_mrep);
4163	return (error);
4164}
4165
4166/*
4167 * nfs setacl call.
4168 */
4169APPLESTATIC int
4170nfsrpc_setacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4171    struct acl *aclp, void *stuff)
4172{
4173	int error;
4174	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4175
4176	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4177		return (EOPNOTSUPP);
4178	error = nfsrpc_setattr(vp, NULL, aclp, cred, p, NULL, NULL, stuff);
4179	return (error);
4180}
4181
4182/*
4183 * nfs setacl call.
4184 */
4185static int
4186nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4187    struct acl *aclp, nfsv4stateid_t *stateidp, void *stuff)
4188{
4189	struct nfsrv_descript nfsd, *nd = &nfsd;
4190	int error;
4191	nfsattrbit_t attrbits;
4192	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4193
4194	if (!NFSHASNFSV4(nmp))
4195		return (EOPNOTSUPP);
4196	NFSCL_REQSTART(nd, NFSPROC_SETACL, vp);
4197	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
4198	NFSZERO_ATTRBIT(&attrbits);
4199	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4200	(void) nfsv4_fillattr(nd, vnode_mount(vp), vp, aclp, NULL, NULL, 0,
4201	    &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0);
4202	error = nfscl_request(nd, vp, p, cred, stuff);
4203	if (error)
4204		return (error);
4205	/* Don't care about the pre/postop attributes */
4206	mbuf_freem(nd->nd_mrep);
4207	return (nd->nd_repstat);
4208}
4209