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