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