1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem */
33191783Srmacklem
34191783Srmacklem#include <sys/cdefs.h>
35191783Srmacklem__FBSDID("$FreeBSD$");
36191783Srmacklem
37191783Srmacklem/*
38191783Srmacklem * Rpc op calls, generally called from the vnode op calls or through the
39191783Srmacklem * buffer cache, for NFS v2, 3 and 4.
40191783Srmacklem * These do not normally make any changes to vnode arguments or use
41191783Srmacklem * structures that might change between the VFS variants. The returned
42191783Srmacklem * arguments are all at the end, after the NFSPROC_T *p one.
43191783Srmacklem */
44191783Srmacklem
45191783Srmacklem#ifndef APPLEKEXT
46230446Srmacklem#include "opt_inet6.h"
47230446Srmacklem
48191783Srmacklem#include <fs/nfs/nfsport.h>
49191783Srmacklem
50191783Srmacklem/*
51191783Srmacklem * Global variables
52191783Srmacklem */
53191783Srmacklemextern int nfs_numnfscbd;
54191783Srmacklemextern struct timeval nfsboottime;
55191783Srmacklemextern u_int32_t newnfs_false, newnfs_true;
56191783Srmacklemextern nfstype nfsv34_type[9];
57191783Srmacklemextern int nfsrv_useacl;
58191783Srmacklemextern char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
59240977Srmacklemextern int nfscl_debuglevel;
60191783SrmacklemNFSCLSTATEMUTEX;
61191783Srmacklemint nfstest_outofseq = 0;
62191783Srmacklemint nfscl_assumeposixlocks = 1;
63191783Srmacklemint nfscl_enablecallb = 0;
64191783Srmacklemshort nfsv4_cbport = NFSV4_CBPORT;
65191783Srmacklemint nfstest_openallsetattr = 0;
66191783Srmacklem#endif	/* !APPLEKEXT */
67191783Srmacklem
68191783Srmacklem#define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
69191783Srmacklem
70191783Srmacklemstatic int nfsrpc_setattrrpc(vnode_t , struct vattr *, nfsv4stateid_t *,
71191783Srmacklem    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
72191783Srmacklemstatic int nfsrpc_readrpc(vnode_t , struct uio *, struct ucred *,
73191783Srmacklem    nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *, void *);
74222289Srmacklemstatic int nfsrpc_writerpc(vnode_t , struct uio *, int *, int *,
75191783Srmacklem    struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *,
76191783Srmacklem    void *);
77191783Srmacklemstatic int nfsrpc_createv23(vnode_t , char *, int, struct vattr *,
78191783Srmacklem    nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *,
79191783Srmacklem    struct nfsvattr *, struct nfsfh **, int *, int *, void *);
80191783Srmacklemstatic int nfsrpc_createv4(vnode_t , char *, int, struct vattr *,
81191783Srmacklem    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **, struct ucred *,
82191783Srmacklem    NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *,
83191783Srmacklem    int *, void *, int *);
84191783Srmacklemstatic int nfsrpc_locku(struct nfsrv_descript *, struct nfsmount *,
85191783Srmacklem    struct nfscllockowner *, u_int64_t, u_int64_t,
86191783Srmacklem    u_int32_t, struct ucred *, NFSPROC_T *, int);
87191783Srmacklemstatic int nfsrpc_setaclrpc(vnode_t, struct ucred *, NFSPROC_T *,
88191783Srmacklem    struct acl *, nfsv4stateid_t *, void *);
89191783Srmacklem
90191783Srmacklem/*
91191783Srmacklem * nfs null call from vfs.
92191783Srmacklem */
93191783SrmacklemAPPLESTATIC int
94191783Srmacklemnfsrpc_null(vnode_t vp, struct ucred *cred, NFSPROC_T *p)
95191783Srmacklem{
96191783Srmacklem	int error;
97191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
98191783Srmacklem
99191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_NULL, vp);
100191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
101191783Srmacklem	if (nd->nd_repstat && !error)
102191783Srmacklem		error = nd->nd_repstat;
103191783Srmacklem	mbuf_freem(nd->nd_mrep);
104191783Srmacklem	return (error);
105191783Srmacklem}
106191783Srmacklem
107191783Srmacklem/*
108191783Srmacklem * nfs access rpc op.
109191783Srmacklem * For nfs version 3 and 4, use the access rpc to check accessibility. If file
110191783Srmacklem * modes are changed on the server, accesses might still fail later.
111191783Srmacklem */
112191783SrmacklemAPPLESTATIC int
113191783Srmacklemnfsrpc_access(vnode_t vp, int acmode, struct ucred *cred,
114191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
115191783Srmacklem{
116191783Srmacklem	int error;
117191783Srmacklem	u_int32_t mode, rmode;
118191783Srmacklem
119191783Srmacklem	if (acmode & VREAD)
120191783Srmacklem		mode = NFSACCESS_READ;
121191783Srmacklem	else
122191783Srmacklem		mode = 0;
123191783Srmacklem	if (vnode_vtype(vp) == VDIR) {
124191783Srmacklem		if (acmode & VWRITE)
125191783Srmacklem			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND |
126191783Srmacklem				 NFSACCESS_DELETE);
127191783Srmacklem		if (acmode & VEXEC)
128191783Srmacklem			mode |= NFSACCESS_LOOKUP;
129191783Srmacklem	} else {
130191783Srmacklem		if (acmode & VWRITE)
131191783Srmacklem			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
132191783Srmacklem		if (acmode & VEXEC)
133191783Srmacklem			mode |= NFSACCESS_EXECUTE;
134191783Srmacklem	}
135191783Srmacklem
136191783Srmacklem	/*
137191783Srmacklem	 * Now, just call nfsrpc_accessrpc() to do the actual RPC.
138191783Srmacklem	 */
139191783Srmacklem	error = nfsrpc_accessrpc(vp, mode, cred, p, nap, attrflagp, &rmode,
140191783Srmacklem	    NULL);
141191783Srmacklem
142191783Srmacklem	/*
143191783Srmacklem	 * The NFS V3 spec does not clarify whether or not
144191783Srmacklem	 * the returned access bits can be a superset of
145191783Srmacklem	 * the ones requested, so...
146191783Srmacklem	 */
147191783Srmacklem	if (!error && (rmode & mode) != mode)
148191783Srmacklem		error = EACCES;
149191783Srmacklem	return (error);
150191783Srmacklem}
151191783Srmacklem
152191783Srmacklem/*
153191783Srmacklem * The actual rpc, separated out for Darwin.
154191783Srmacklem */
155191783SrmacklemAPPLESTATIC int
156191783Srmacklemnfsrpc_accessrpc(vnode_t vp, u_int32_t mode, struct ucred *cred,
157191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, u_int32_t *rmodep,
158191783Srmacklem    void *stuff)
159191783Srmacklem{
160191783Srmacklem	u_int32_t *tl;
161191783Srmacklem	u_int32_t supported, rmode;
162191783Srmacklem	int error;
163191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
164191783Srmacklem	nfsattrbit_t attrbits;
165191783Srmacklem
166191783Srmacklem	*attrflagp = 0;
167191783Srmacklem	supported = mode;
168191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_ACCESS, vp);
169191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
170191783Srmacklem	*tl = txdr_unsigned(mode);
171191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
172191783Srmacklem		/*
173191783Srmacklem		 * And do a Getattr op.
174191783Srmacklem		 */
175191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
176191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
177191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
178191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
179191783Srmacklem	}
180191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
181191783Srmacklem	if (error)
182191783Srmacklem		return (error);
183191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
184191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
185191783Srmacklem		if (error)
186191783Srmacklem			goto nfsmout;
187191783Srmacklem	}
188191783Srmacklem	if (!nd->nd_repstat) {
189191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
190191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
191191783Srmacklem			supported = fxdr_unsigned(u_int32_t, *tl++);
192191783Srmacklem		} else {
193191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
194191783Srmacklem		}
195191783Srmacklem		rmode = fxdr_unsigned(u_int32_t, *tl);
196191783Srmacklem		if (nd->nd_flag & ND_NFSV4)
197191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
198191783Srmacklem
199191783Srmacklem		/*
200191783Srmacklem		 * It's not obvious what should be done about
201191783Srmacklem		 * unsupported access modes. For now, be paranoid
202191783Srmacklem		 * and clear the unsupported ones.
203191783Srmacklem		 */
204191783Srmacklem		rmode &= supported;
205191783Srmacklem		*rmodep = rmode;
206191783Srmacklem	} else
207191783Srmacklem		error = nd->nd_repstat;
208191783Srmacklemnfsmout:
209191783Srmacklem	mbuf_freem(nd->nd_mrep);
210191783Srmacklem	return (error);
211191783Srmacklem}
212191783Srmacklem
213191783Srmacklem/*
214191783Srmacklem * nfs open rpc
215191783Srmacklem */
216191783SrmacklemAPPLESTATIC int
217191783Srmacklemnfsrpc_open(vnode_t vp, int amode, struct ucred *cred, NFSPROC_T *p)
218191783Srmacklem{
219191783Srmacklem	struct nfsclopen *op;
220191783Srmacklem	struct nfscldeleg *dp;
221191783Srmacklem	struct nfsfh *nfhp;
222191783Srmacklem	struct nfsnode *np = VTONFS(vp);
223191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
224191783Srmacklem	u_int32_t mode, clidrev;
225191783Srmacklem	int ret, newone, error, expireret = 0, retrycnt;
226191783Srmacklem
227191783Srmacklem	/*
228191783Srmacklem	 * For NFSv4, Open Ops are only done on Regular Files.
229191783Srmacklem	 */
230191783Srmacklem	if (vnode_vtype(vp) != VREG)
231191783Srmacklem		return (0);
232191783Srmacklem	mode = 0;
233191783Srmacklem	if (amode & FREAD)
234191783Srmacklem		mode |= NFSV4OPEN_ACCESSREAD;
235191783Srmacklem	if (amode & FWRITE)
236191783Srmacklem		mode |= NFSV4OPEN_ACCESSWRITE;
237191783Srmacklem	nfhp = np->n_fhp;
238191783Srmacklem
239191783Srmacklem	retrycnt = 0;
240191783Srmacklem#ifdef notdef
241191783Srmacklem{ char name[100]; int namel;
242191783Srmacklemnamel = (np->n_v4->n4_namelen < 100) ? np->n_v4->n4_namelen : 99;
243191783Srmacklembcopy(NFS4NODENAME(np->n_v4), name, namel);
244191783Srmacklemname[namel] = '\0';
245191783Srmacklemprintf("rpcopen p=0x%x name=%s",p->p_pid,name);
246191783Srmacklemif (nfhp->nfh_len > 0) printf(" fh=0x%x\n",nfhp->nfh_fh[12]);
247191783Srmacklemelse printf(" fhl=0\n");
248191783Srmacklem}
249191783Srmacklem#endif
250191783Srmacklem	do {
251191783Srmacklem	    dp = NULL;
252191783Srmacklem	    error = nfscl_open(vp, nfhp->nfh_fh, nfhp->nfh_len, mode, 1,
253191783Srmacklem		cred, p, NULL, &op, &newone, &ret, 1);
254191783Srmacklem	    if (error) {
255191783Srmacklem		return (error);
256191783Srmacklem	    }
257191783Srmacklem	    if (nmp->nm_clp != NULL)
258191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
259191783Srmacklem	    else
260191783Srmacklem		clidrev = 0;
261191783Srmacklem	    if (ret == NFSCLOPEN_DOOPEN) {
262191783Srmacklem		if (np->n_v4 != NULL) {
263191783Srmacklem			error = nfsrpc_openrpc(nmp, vp, np->n_v4->n4_data,
264191783Srmacklem			   np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
265191783Srmacklem			   np->n_fhp->nfh_len, mode, op,
266191783Srmacklem			   NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &dp,
267191783Srmacklem			   0, 0x0, cred, p, 0, 0);
268191783Srmacklem			if (dp != NULL) {
269191783Srmacklem#ifdef APPLE
270191783Srmacklem				OSBitAndAtomic((int32_t)~NDELEGMOD, (UInt32 *)&np->n_flag);
271191783Srmacklem#else
272191783Srmacklem				NFSLOCKNODE(np);
273191783Srmacklem				np->n_flag &= ~NDELEGMOD;
274210034Srmacklem				/*
275210034Srmacklem				 * Invalidate the attribute cache, so that
276210034Srmacklem				 * attributes that pre-date the issue of a
277210034Srmacklem				 * delegation are not cached, since the
278210034Srmacklem				 * cached attributes will remain valid while
279210034Srmacklem				 * the delegation is held.
280210034Srmacklem				 */
281210034Srmacklem				NFSINVALATTRCACHE(np);
282191783Srmacklem				NFSUNLOCKNODE(np);
283191783Srmacklem#endif
284191783Srmacklem				(void) nfscl_deleg(nmp->nm_mountp,
285191783Srmacklem				    op->nfso_own->nfsow_clp,
286191783Srmacklem				    nfhp->nfh_fh, nfhp->nfh_len, cred, p, &dp);
287191783Srmacklem			}
288191783Srmacklem		} else {
289191783Srmacklem			error = EIO;
290191783Srmacklem		}
291191783Srmacklem		newnfs_copyincred(cred, &op->nfso_cred);
292206688Srmacklem	    } else if (ret == NFSCLOPEN_SETCRED)
293206688Srmacklem		/*
294206688Srmacklem		 * This is a new local open on a delegation. It needs
295206688Srmacklem		 * to have credentials so that an open can be done
296206688Srmacklem		 * against the server during recovery.
297206688Srmacklem		 */
298206688Srmacklem		newnfs_copyincred(cred, &op->nfso_cred);
299191783Srmacklem
300191783Srmacklem	    /*
301191783Srmacklem	     * nfso_opencnt is the count of how many VOP_OPEN()s have
302191783Srmacklem	     * been done on this Open successfully and a VOP_CLOSE()
303191783Srmacklem	     * is expected for each of these.
304191783Srmacklem	     * If error is non-zero, don't increment it, since the Open
305191783Srmacklem	     * hasn't succeeded yet.
306191783Srmacklem	     */
307191783Srmacklem	    if (!error)
308191783Srmacklem		op->nfso_opencnt++;
309191783Srmacklem	    nfscl_openrelease(op, error, newone);
310191783Srmacklem	    if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
311191783Srmacklem		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY) {
312207170Srmacklem		(void) nfs_catnap(PZERO, error, "nfs_open");
313191783Srmacklem	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
314191783Srmacklem		&& clidrev != 0) {
315191783Srmacklem		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
316191783Srmacklem		retrycnt++;
317191783Srmacklem	    }
318191783Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
319191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
320191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
321191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
322191783Srmacklem	if (error && retrycnt >= 4)
323191783Srmacklem		error = EIO;
324191783Srmacklem	return (error);
325191783Srmacklem}
326191783Srmacklem
327191783Srmacklem/*
328191783Srmacklem * the actual open rpc
329191783Srmacklem */
330191783SrmacklemAPPLESTATIC int
331191783Srmacklemnfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
332191783Srmacklem    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
333191783Srmacklem    u_int8_t *name, int namelen, struct nfscldeleg **dpp,
334191783Srmacklem    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p,
335191783Srmacklem    int syscred, int recursed)
336191783Srmacklem{
337191783Srmacklem	u_int32_t *tl;
338191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
339191783Srmacklem	struct nfscldeleg *dp, *ndp = NULL;
340191783Srmacklem	struct nfsvattr nfsva;
341191783Srmacklem	u_int32_t rflags, deleg;
342191783Srmacklem	nfsattrbit_t attrbits;
343191783Srmacklem	int error, ret, acesize, limitby;
344191783Srmacklem
345191783Srmacklem	dp = *dpp;
346191783Srmacklem	*dpp = NULL;
347191783Srmacklem	nfscl_reqstart(nd, NFSPROC_OPEN, nmp, nfhp, fhlen, NULL);
348191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
349191783Srmacklem	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
350191783Srmacklem	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
351191783Srmacklem	*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
352191783Srmacklem	*tl++ = op->nfso_own->nfsow_clp->nfsc_clientid.lval[0];
353191783Srmacklem	*tl = op->nfso_own->nfsow_clp->nfsc_clientid.lval[1];
354191783Srmacklem	(void) nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
355191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
356191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
357191783Srmacklem	if (reclaim) {
358191783Srmacklem		*tl = txdr_unsigned(NFSV4OPEN_CLAIMPREVIOUS);
359191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
360191783Srmacklem		*tl = txdr_unsigned(delegtype);
361191783Srmacklem	} else {
362191783Srmacklem		if (dp != NULL) {
363191783Srmacklem			*tl = txdr_unsigned(NFSV4OPEN_CLAIMDELEGATECUR);
364191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
365191783Srmacklem			*tl++ = dp->nfsdl_stateid.seqid;
366191783Srmacklem			*tl++ = dp->nfsdl_stateid.other[0];
367191783Srmacklem			*tl++ = dp->nfsdl_stateid.other[1];
368191783Srmacklem			*tl = dp->nfsdl_stateid.other[2];
369191783Srmacklem		} else {
370191783Srmacklem			*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
371191783Srmacklem		}
372191783Srmacklem		(void) nfsm_strtom(nd, name, namelen);
373191783Srmacklem	}
374191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
375191783Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
376191783Srmacklem	NFSZERO_ATTRBIT(&attrbits);
377191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
378191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY);
379191783Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
380191783Srmacklem	if (syscred)
381191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
382191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
383191783Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
384191783Srmacklem	if (error)
385191783Srmacklem		return (error);
386191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
387191783Srmacklem	if (!nd->nd_repstat) {
388191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
389191783Srmacklem		    6 * NFSX_UNSIGNED);
390191783Srmacklem		op->nfso_stateid.seqid = *tl++;
391191783Srmacklem		op->nfso_stateid.other[0] = *tl++;
392191783Srmacklem		op->nfso_stateid.other[1] = *tl++;
393191783Srmacklem		op->nfso_stateid.other[2] = *tl;
394191783Srmacklem		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
395191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
396191783Srmacklem		if (error)
397191783Srmacklem			goto nfsmout;
398191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
399191783Srmacklem		deleg = fxdr_unsigned(u_int32_t, *tl);
400191783Srmacklem		if (deleg == NFSV4OPEN_DELEGATEREAD ||
401191783Srmacklem		    deleg == NFSV4OPEN_DELEGATEWRITE) {
402191783Srmacklem			if (!(op->nfso_own->nfsow_clp->nfsc_flags &
403191783Srmacklem			      NFSCLFLAGS_FIRSTDELEG))
404191783Srmacklem				op->nfso_own->nfsow_clp->nfsc_flags |=
405191783Srmacklem				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
406191783Srmacklem			MALLOC(ndp, struct nfscldeleg *,
407191783Srmacklem			    sizeof (struct nfscldeleg) + newfhlen,
408191783Srmacklem			    M_NFSCLDELEG, M_WAITOK);
409191783Srmacklem			LIST_INIT(&ndp->nfsdl_owner);
410191783Srmacklem			LIST_INIT(&ndp->nfsdl_lock);
411191783Srmacklem			ndp->nfsdl_clp = op->nfso_own->nfsow_clp;
412191783Srmacklem			ndp->nfsdl_fhlen = newfhlen;
413191783Srmacklem			NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen);
414191783Srmacklem			newnfs_copyincred(cred, &ndp->nfsdl_cred);
415191783Srmacklem			nfscl_lockinit(&ndp->nfsdl_rwlock);
416191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
417191783Srmacklem			    NFSX_UNSIGNED);
418191783Srmacklem			ndp->nfsdl_stateid.seqid = *tl++;
419191783Srmacklem			ndp->nfsdl_stateid.other[0] = *tl++;
420191783Srmacklem			ndp->nfsdl_stateid.other[1] = *tl++;
421191783Srmacklem			ndp->nfsdl_stateid.other[2] = *tl++;
422191783Srmacklem			ret = fxdr_unsigned(int, *tl);
423191783Srmacklem			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
424191783Srmacklem				ndp->nfsdl_flags = NFSCLDL_WRITE;
425191783Srmacklem				/*
426191783Srmacklem				 * Indicates how much the file can grow.
427191783Srmacklem				 */
428191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *,
429191783Srmacklem				    3 * NFSX_UNSIGNED);
430191783Srmacklem				limitby = fxdr_unsigned(int, *tl++);
431191783Srmacklem				switch (limitby) {
432191783Srmacklem				case NFSV4OPEN_LIMITSIZE:
433191783Srmacklem					ndp->nfsdl_sizelimit = fxdr_hyper(tl);
434191783Srmacklem					break;
435191783Srmacklem				case NFSV4OPEN_LIMITBLOCKS:
436191783Srmacklem					ndp->nfsdl_sizelimit =
437191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl++);
438191783Srmacklem					ndp->nfsdl_sizelimit *=
439191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl);
440191783Srmacklem					break;
441191783Srmacklem				default:
442191783Srmacklem					error = NFSERR_BADXDR;
443191783Srmacklem					goto nfsmout;
444191783Srmacklem				};
445191783Srmacklem			} else {
446191783Srmacklem				ndp->nfsdl_flags = NFSCLDL_READ;
447191783Srmacklem			}
448191783Srmacklem			if (ret)
449191783Srmacklem				ndp->nfsdl_flags |= NFSCLDL_RECALL;
450191783Srmacklem			error = nfsrv_dissectace(nd, &ndp->nfsdl_ace, &ret,
451191783Srmacklem			    &acesize, p);
452191783Srmacklem			if (error)
453191783Srmacklem				goto nfsmout;
454191783Srmacklem		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
455191783Srmacklem			error = NFSERR_BADXDR;
456191783Srmacklem			goto nfsmout;
457191783Srmacklem		}
458191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
459191783Srmacklem		error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
460191783Srmacklem		    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
461191783Srmacklem		    NULL, NULL, NULL, p, cred);
462191783Srmacklem		if (error)
463191783Srmacklem			goto nfsmout;
464191783Srmacklem		if (ndp != NULL) {
465191783Srmacklem			ndp->nfsdl_change = nfsva.na_filerev;
466191783Srmacklem			ndp->nfsdl_modtime = nfsva.na_mtime;
467191783Srmacklem			ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
468191783Srmacklem		}
469191783Srmacklem		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM)) {
470191783Srmacklem		    do {
471191783Srmacklem			ret = nfsrpc_openconfirm(vp, newfhp, newfhlen, op,
472191783Srmacklem			    cred, p);
473191783Srmacklem			if (ret == NFSERR_DELAY)
474207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_open");
475191783Srmacklem		    } while (ret == NFSERR_DELAY);
476191783Srmacklem		    error = ret;
477191783Srmacklem		}
478191783Srmacklem		if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) ||
479191783Srmacklem		    nfscl_assumeposixlocks)
480191783Srmacklem		    op->nfso_posixlock = 1;
481191783Srmacklem		else
482191783Srmacklem		    op->nfso_posixlock = 0;
483191783Srmacklem
484191783Srmacklem		/*
485191783Srmacklem		 * If the server is handing out delegations, but we didn't
486191783Srmacklem		 * get one because an OpenConfirm was required, try the
487191783Srmacklem		 * Open again, to get a delegation. This is a harmless no-op,
488191783Srmacklem		 * from a server's point of view.
489191783Srmacklem		 */
490191783Srmacklem		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM) &&
491191783Srmacklem		    (op->nfso_own->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG)
492191783Srmacklem		    && !error && dp == NULL && ndp == NULL && !recursed) {
493191783Srmacklem		    do {
494191783Srmacklem			ret = nfsrpc_openrpc(nmp, vp, nfhp, fhlen, newfhp,
495191783Srmacklem			    newfhlen, mode, op, name, namelen, &ndp, 0, 0x0,
496191783Srmacklem			    cred, p, syscred, 1);
497191783Srmacklem			if (ret == NFSERR_DELAY)
498207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_open2");
499191783Srmacklem		    } while (ret == NFSERR_DELAY);
500191783Srmacklem		    if (ret) {
501191783Srmacklem			if (ndp != NULL)
502191783Srmacklem				FREE((caddr_t)ndp, M_NFSCLDELEG);
503191783Srmacklem			if (ret == NFSERR_STALECLIENTID ||
504191783Srmacklem			    ret == NFSERR_STALEDONTRECOVER)
505191783Srmacklem				error = ret;
506191783Srmacklem		    }
507191783Srmacklem		}
508191783Srmacklem	}
509191783Srmacklem	if (nd->nd_repstat != 0 && error == 0)
510191783Srmacklem		error = nd->nd_repstat;
511191783Srmacklem	if (error == NFSERR_STALECLIENTID)
512191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
513191783Srmacklemnfsmout:
514191783Srmacklem	if (!error)
515191783Srmacklem		*dpp = ndp;
516191783Srmacklem	else if (ndp != NULL)
517191783Srmacklem		FREE((caddr_t)ndp, M_NFSCLDELEG);
518191783Srmacklem	mbuf_freem(nd->nd_mrep);
519191783Srmacklem	return (error);
520191783Srmacklem}
521191783Srmacklem
522191783Srmacklem/*
523191783Srmacklem * open downgrade rpc
524191783Srmacklem */
525191783SrmacklemAPPLESTATIC int
526191783Srmacklemnfsrpc_opendowngrade(vnode_t vp, u_int32_t mode, struct nfsclopen *op,
527191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
528191783Srmacklem{
529191783Srmacklem	u_int32_t *tl;
530191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
531191783Srmacklem	int error;
532191783Srmacklem
533191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_OPENDOWNGRADE, vp);
534191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
535191783Srmacklem	*tl++ = op->nfso_stateid.seqid;
536191783Srmacklem	*tl++ = op->nfso_stateid.other[0];
537191783Srmacklem	*tl++ = op->nfso_stateid.other[1];
538191783Srmacklem	*tl++ = op->nfso_stateid.other[2];
539191783Srmacklem	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
540191783Srmacklem	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
541191783Srmacklem	*tl = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
542191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
543191783Srmacklem	if (error)
544191783Srmacklem		return (error);
545191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
546191783Srmacklem	if (!nd->nd_repstat) {
547191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
548191783Srmacklem		op->nfso_stateid.seqid = *tl++;
549191783Srmacklem		op->nfso_stateid.other[0] = *tl++;
550191783Srmacklem		op->nfso_stateid.other[1] = *tl++;
551191783Srmacklem		op->nfso_stateid.other[2] = *tl;
552191783Srmacklem	}
553191783Srmacklem	if (nd->nd_repstat && error == 0)
554191783Srmacklem		error = nd->nd_repstat;
555191783Srmacklem	if (error == NFSERR_STALESTATEID)
556191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
557191783Srmacklemnfsmout:
558191783Srmacklem	mbuf_freem(nd->nd_mrep);
559191783Srmacklem	return (error);
560191783Srmacklem}
561191783Srmacklem
562191783Srmacklem/*
563191783Srmacklem * V4 Close operation.
564191783Srmacklem */
565191783SrmacklemAPPLESTATIC int
566192337Srmacklemnfsrpc_close(vnode_t vp, int doclose, NFSPROC_T *p)
567191783Srmacklem{
568191783Srmacklem	struct nfsclclient *clp;
569191783Srmacklem	int error;
570191783Srmacklem
571191783Srmacklem	if (vnode_vtype(vp) != VREG)
572191783Srmacklem		return (0);
573192337Srmacklem	if (doclose)
574195510Srmacklem		error = nfscl_doclose(vp, &clp, p);
575192337Srmacklem	else
576195510Srmacklem		error = nfscl_getclose(vp, &clp);
577191783Srmacklem	if (error)
578191783Srmacklem		return (error);
579191783Srmacklem
580191783Srmacklem	nfscl_clientrelease(clp);
581191783Srmacklem	return (0);
582191783Srmacklem}
583191783Srmacklem
584191783Srmacklem/*
585195510Srmacklem * Close the open.
586191783Srmacklem */
587195510SrmacklemAPPLESTATIC void
588195510Srmacklemnfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p)
589191783Srmacklem{
590191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
591223747Srmacklem	struct nfscllockowner *lp, *nlp;
592191783Srmacklem	struct nfscllock *lop, *nlop;
593191783Srmacklem	struct ucred *tcred;
594191783Srmacklem	u_int64_t off = 0, len = 0;
595191783Srmacklem	u_int32_t type = NFSV4LOCKT_READ;
596195510Srmacklem	int error, do_unlock, trycnt;
597191783Srmacklem
598191783Srmacklem	tcred = newnfs_getcred();
599195510Srmacklem	newnfs_copycred(&op->nfso_cred, tcred);
600195510Srmacklem	/*
601195510Srmacklem	 * (Theoretically this could be done in the same
602195510Srmacklem	 *  compound as the close, but having multiple
603195510Srmacklem	 *  sequenced Ops in the same compound might be
604195510Srmacklem	 *  too scary for some servers.)
605195510Srmacklem	 */
606195510Srmacklem	if (op->nfso_posixlock) {
607195510Srmacklem		off = 0;
608195510Srmacklem		len = NFS64BITSSET;
609195510Srmacklem		type = NFSV4LOCKT_READ;
610195510Srmacklem	}
611195510Srmacklem
612195510Srmacklem	/*
613195510Srmacklem	 * Since this function is only called from VOP_INACTIVE(), no
614195510Srmacklem	 * other thread will be manipulating this Open. As such, the
615195510Srmacklem	 * lock lists are not being changed by other threads, so it should
616195510Srmacklem	 * be safe to do this without locking.
617195510Srmacklem	 */
618195510Srmacklem	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
619195510Srmacklem		do_unlock = 1;
620195510Srmacklem		LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
621191783Srmacklem			if (op->nfso_posixlock == 0) {
622195510Srmacklem				off = lop->nfslo_first;
623195510Srmacklem				len = lop->nfslo_end - lop->nfslo_first;
624195510Srmacklem				if (lop->nfslo_type == F_WRLCK)
625195510Srmacklem					type = NFSV4LOCKT_WRITE;
626195510Srmacklem				else
627195510Srmacklem					type = NFSV4LOCKT_READ;
628191783Srmacklem			}
629195510Srmacklem			if (do_unlock) {
630195510Srmacklem				trycnt = 0;
631195510Srmacklem				do {
632195510Srmacklem					error = nfsrpc_locku(nd, nmp, lp, off,
633195510Srmacklem					    len, type, tcred, p, 0);
634195510Srmacklem					if ((nd->nd_repstat == NFSERR_GRACE ||
635195510Srmacklem					    nd->nd_repstat == NFSERR_DELAY) &&
636195510Srmacklem					    error == 0)
637195510Srmacklem						(void) nfs_catnap(PZERO,
638207170Srmacklem						    (int)nd->nd_repstat,
639195510Srmacklem						    "nfs_close");
640195510Srmacklem				} while ((nd->nd_repstat == NFSERR_GRACE ||
641195510Srmacklem				    nd->nd_repstat == NFSERR_DELAY) &&
642195510Srmacklem				    error == 0 && trycnt++ < 5);
643195510Srmacklem				if (op->nfso_posixlock)
644195510Srmacklem					do_unlock = 0;
645191783Srmacklem			}
646191783Srmacklem			nfscl_freelock(lop, 0);
647191783Srmacklem		}
648223747Srmacklem		/*
649223747Srmacklem		 * Do a ReleaseLockOwner.
650223747Srmacklem		 * The lock owner name nfsl_owner may be used by other opens for
651223747Srmacklem		 * other files but the lock_owner4 name that nfsrpc_rellockown()
652223747Srmacklem		 * puts on the wire has the file handle for this file appended
653223747Srmacklem		 * to it, so it can be done now.
654223747Srmacklem		 */
655229674Srmacklem		(void)nfsrpc_rellockown(nmp, lp, lp->nfsl_open->nfso_fh,
656229674Srmacklem		    lp->nfsl_open->nfso_fhlen, tcred, p);
657195510Srmacklem	}
658191783Srmacklem
659195510Srmacklem	/*
660195510Srmacklem	 * There could be other Opens for different files on the same
661195510Srmacklem	 * OpenOwner, so locking is required.
662195510Srmacklem	 */
663195510Srmacklem	NFSLOCKCLSTATE();
664195510Srmacklem	nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
665195510Srmacklem	NFSUNLOCKCLSTATE();
666195510Srmacklem	do {
667195510Srmacklem		error = nfscl_tryclose(op, tcred, nmp, p);
668195510Srmacklem		if (error == NFSERR_GRACE)
669207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_close");
670195510Srmacklem	} while (error == NFSERR_GRACE);
671195510Srmacklem	NFSLOCKCLSTATE();
672195510Srmacklem	nfscl_lockunlock(&op->nfso_own->nfsow_rwlock);
673195510Srmacklem
674223747Srmacklem	LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp)
675223747Srmacklem		nfscl_freelockowner(lp, 0);
676195510Srmacklem	nfscl_freeopen(op, 0);
677195510Srmacklem	NFSUNLOCKCLSTATE();
678191783Srmacklem	NFSFREECRED(tcred);
679191783Srmacklem}
680191783Srmacklem
681191783Srmacklem/*
682191783Srmacklem * The actual Close RPC.
683191783Srmacklem */
684191783SrmacklemAPPLESTATIC int
685191783Srmacklemnfsrpc_closerpc(struct nfsrv_descript *nd, struct nfsmount *nmp,
686191783Srmacklem    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p,
687191783Srmacklem    int syscred)
688191783Srmacklem{
689191783Srmacklem	u_int32_t *tl;
690191783Srmacklem	int error;
691191783Srmacklem
692191783Srmacklem	nfscl_reqstart(nd, NFSPROC_CLOSE, nmp, op->nfso_fh,
693191783Srmacklem	    op->nfso_fhlen, NULL);
694191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
695191783Srmacklem	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
696191783Srmacklem	*tl++ = op->nfso_stateid.seqid;
697191783Srmacklem	*tl++ = op->nfso_stateid.other[0];
698191783Srmacklem	*tl++ = op->nfso_stateid.other[1];
699191783Srmacklem	*tl = op->nfso_stateid.other[2];
700191783Srmacklem	if (syscred)
701191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
702191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
703191783Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
704191783Srmacklem	if (error)
705191783Srmacklem		return (error);
706191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
707191783Srmacklem	if (nd->nd_repstat == 0)
708191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
709191783Srmacklem	error = nd->nd_repstat;
710191783Srmacklem	if (error == NFSERR_STALESTATEID)
711191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
712191783Srmacklemnfsmout:
713191783Srmacklem	mbuf_freem(nd->nd_mrep);
714191783Srmacklem	return (error);
715191783Srmacklem}
716191783Srmacklem
717191783Srmacklem/*
718191783Srmacklem * V4 Open Confirm RPC.
719191783Srmacklem */
720191783SrmacklemAPPLESTATIC int
721191783Srmacklemnfsrpc_openconfirm(vnode_t vp, u_int8_t *nfhp, int fhlen,
722191783Srmacklem    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p)
723191783Srmacklem{
724191783Srmacklem	u_int32_t *tl;
725191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
726191783Srmacklem	int error;
727191783Srmacklem
728191783Srmacklem	nfscl_reqstart(nd, NFSPROC_OPENCONFIRM, VFSTONFS(vnode_mount(vp)),
729191783Srmacklem	    nfhp, fhlen, NULL);
730191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
731191783Srmacklem	*tl++ = op->nfso_stateid.seqid;
732191783Srmacklem	*tl++ = op->nfso_stateid.other[0];
733191783Srmacklem	*tl++ = op->nfso_stateid.other[1];
734191783Srmacklem	*tl++ = op->nfso_stateid.other[2];
735191783Srmacklem	*tl = txdr_unsigned(op->nfso_own->nfsow_seqid);
736191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
737191783Srmacklem	if (error)
738191783Srmacklem		return (error);
739191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
740191783Srmacklem	if (!nd->nd_repstat) {
741191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
742191783Srmacklem		op->nfso_stateid.seqid = *tl++;
743191783Srmacklem		op->nfso_stateid.other[0] = *tl++;
744191783Srmacklem		op->nfso_stateid.other[1] = *tl++;
745191783Srmacklem		op->nfso_stateid.other[2] = *tl;
746191783Srmacklem	}
747191783Srmacklem	error = nd->nd_repstat;
748191783Srmacklem	if (error == NFSERR_STALESTATEID)
749191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
750191783Srmacklemnfsmout:
751191783Srmacklem	mbuf_freem(nd->nd_mrep);
752191783Srmacklem	return (error);
753191783Srmacklem}
754191783Srmacklem
755191783Srmacklem/*
756191783Srmacklem * Do the setclientid and setclientid confirm RPCs. Called from nfs_statfs()
757191783Srmacklem * when a mount has just occurred and when the server replies NFSERR_EXPIRED.
758191783Srmacklem */
759191783SrmacklemAPPLESTATIC int
760191783Srmacklemnfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp,
761191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
762191783Srmacklem{
763191783Srmacklem	u_int32_t *tl;
764191783Srmacklem	struct nfsrv_descript nfsd;
765191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
766191783Srmacklem	nfsattrbit_t attrbits;
767191783Srmacklem	u_int8_t *cp = NULL, *cp2, addr[INET6_ADDRSTRLEN + 9];
768191783Srmacklem	u_short port;
769195825Srmacklem	int error, isinet6 = 0, callblen;
770191783Srmacklem	nfsquad_t confirm;
771191783Srmacklem	u_int32_t lease;
772191783Srmacklem	static u_int32_t rev = 0;
773191783Srmacklem
774191783Srmacklem	if (nfsboottime.tv_sec == 0)
775191783Srmacklem		NFSSETBOOTTIME(nfsboottime);
776191783Srmacklem	nfscl_reqstart(nd, NFSPROC_SETCLIENTID, nmp, NULL, 0, NULL);
777191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
778191783Srmacklem	*tl++ = txdr_unsigned(nfsboottime.tv_sec);
779191783Srmacklem	*tl = txdr_unsigned(rev++);
780191783Srmacklem	(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
781191783Srmacklem
782191783Srmacklem	/*
783191783Srmacklem	 * set up the callback address
784191783Srmacklem	 */
785191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
786191783Srmacklem	*tl = txdr_unsigned(NFS_CALLBCKPROG);
787191783Srmacklem	callblen = strlen(nfsv4_callbackaddr);
788191783Srmacklem	if (callblen == 0)
789191783Srmacklem		cp = nfscl_getmyip(nmp, &isinet6);
790191783Srmacklem	if (nfscl_enablecallb && nfs_numnfscbd > 0 &&
791191783Srmacklem	    (callblen > 0 || cp != NULL)) {
792191783Srmacklem		port = htons(nfsv4_cbport);
793191783Srmacklem		cp2 = (u_int8_t *)&port;
794191783Srmacklem#ifdef INET6
795191783Srmacklem		if ((callblen > 0 &&
796191783Srmacklem		     strchr(nfsv4_callbackaddr, ':')) || isinet6) {
797191783Srmacklem			char ip6buf[INET6_ADDRSTRLEN], *ip6add;
798191783Srmacklem
799191783Srmacklem			(void) nfsm_strtom(nd, "tcp6", 4);
800191783Srmacklem			if (callblen == 0) {
801191783Srmacklem				ip6_sprintf(ip6buf, (struct in6_addr *)cp);
802191783Srmacklem				ip6add = ip6buf;
803191783Srmacklem			} else {
804191783Srmacklem				ip6add = nfsv4_callbackaddr;
805191783Srmacklem			}
806191783Srmacklem			snprintf(addr, INET6_ADDRSTRLEN + 9, "%s.%d.%d",
807191783Srmacklem			    ip6add, cp2[0], cp2[1]);
808191783Srmacklem		} else
809191783Srmacklem#endif
810191783Srmacklem		{
811191783Srmacklem			(void) nfsm_strtom(nd, "tcp", 3);
812191783Srmacklem			if (callblen == 0)
813191783Srmacklem				snprintf(addr, INET6_ADDRSTRLEN + 9,
814191783Srmacklem				    "%d.%d.%d.%d.%d.%d", cp[0], cp[1],
815191783Srmacklem				    cp[2], cp[3], cp2[0], cp2[1]);
816191783Srmacklem			else
817191783Srmacklem				snprintf(addr, INET6_ADDRSTRLEN + 9,
818191783Srmacklem				    "%s.%d.%d", nfsv4_callbackaddr,
819191783Srmacklem				    cp2[0], cp2[1]);
820191783Srmacklem		}
821191783Srmacklem		(void) nfsm_strtom(nd, addr, strlen(addr));
822191783Srmacklem	} else {
823191783Srmacklem		(void) nfsm_strtom(nd, "tcp", 3);
824191783Srmacklem		(void) nfsm_strtom(nd, "0.0.0.0.0.0", 11);
825191783Srmacklem	}
826191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
827191783Srmacklem	*tl = txdr_unsigned(clp->nfsc_cbident);
828191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
829191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
830191783Srmacklem		NFS_PROG, NFS_VER4, NULL, 1, NULL);
831191783Srmacklem	if (error)
832191783Srmacklem		return (error);
833191783Srmacklem	if (nd->nd_repstat == 0) {
834191783Srmacklem	    NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
835191783Srmacklem	    clp->nfsc_clientid.lval[0] = *tl++;
836191783Srmacklem	    clp->nfsc_clientid.lval[1] = *tl++;
837191783Srmacklem	    confirm.lval[0] = *tl++;
838191783Srmacklem	    confirm.lval[1] = *tl;
839191783Srmacklem	    mbuf_freem(nd->nd_mrep);
840191783Srmacklem	    nd->nd_mrep = NULL;
841191783Srmacklem
842191783Srmacklem	    /*
843191783Srmacklem	     * and confirm it.
844191783Srmacklem	     */
845191783Srmacklem	    nfscl_reqstart(nd, NFSPROC_SETCLIENTIDCFRM, nmp, NULL, 0, NULL);
846191783Srmacklem	    NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
847191783Srmacklem	    *tl++ = clp->nfsc_clientid.lval[0];
848191783Srmacklem	    *tl++ = clp->nfsc_clientid.lval[1];
849191783Srmacklem	    *tl++ = confirm.lval[0];
850191783Srmacklem	    *tl = confirm.lval[1];
851191783Srmacklem	    nd->nd_flag |= ND_USEGSSNAME;
852191783Srmacklem	    error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
853191783Srmacklem		cred, NFS_PROG, NFS_VER4, NULL, 1, NULL);
854191783Srmacklem	    if (error)
855191783Srmacklem		return (error);
856191783Srmacklem	    mbuf_freem(nd->nd_mrep);
857191783Srmacklem	    nd->nd_mrep = NULL;
858191783Srmacklem	    if (nd->nd_repstat == 0) {
859191783Srmacklem		nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, nmp->nm_fh,
860191783Srmacklem		    nmp->nm_fhsize, NULL);
861191783Srmacklem		NFSZERO_ATTRBIT(&attrbits);
862191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_LEASETIME);
863191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
864191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
865191783Srmacklem		error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
866191783Srmacklem		    cred, NFS_PROG, NFS_VER4, NULL, 1, NULL);
867191783Srmacklem		if (error)
868191783Srmacklem		    return (error);
869191783Srmacklem		if (nd->nd_repstat == 0) {
870191783Srmacklem		    error = nfsv4_loadattr(nd, NULL, NULL, NULL, NULL, 0, NULL,
871191783Srmacklem			NULL, NULL, NULL, NULL, 0, NULL, &lease, NULL, p, cred);
872191783Srmacklem		    if (error)
873191783Srmacklem			goto nfsmout;
874191783Srmacklem		    clp->nfsc_renew = NFSCL_RENEW(lease);
875191783Srmacklem		    clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
876191783Srmacklem		    clp->nfsc_clientidrev++;
877191783Srmacklem		    if (clp->nfsc_clientidrev == 0)
878191783Srmacklem			clp->nfsc_clientidrev++;
879191783Srmacklem		}
880191783Srmacklem	    }
881191783Srmacklem	}
882191783Srmacklem	error = nd->nd_repstat;
883191783Srmacklemnfsmout:
884191783Srmacklem	mbuf_freem(nd->nd_mrep);
885191783Srmacklem	return (error);
886191783Srmacklem}
887191783Srmacklem
888191783Srmacklem/*
889191783Srmacklem * nfs getattr call.
890191783Srmacklem */
891191783SrmacklemAPPLESTATIC int
892191783Srmacklemnfsrpc_getattr(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
893191783Srmacklem    struct nfsvattr *nap, void *stuff)
894191783Srmacklem{
895191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
896191783Srmacklem	int error;
897191783Srmacklem	nfsattrbit_t attrbits;
898191783Srmacklem
899191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
900191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
901191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
902191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
903191783Srmacklem	}
904191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
905191783Srmacklem	if (error)
906191783Srmacklem		return (error);
907191783Srmacklem	if (!nd->nd_repstat)
908191783Srmacklem		error = nfsm_loadattr(nd, nap);
909191783Srmacklem	else
910191783Srmacklem		error = nd->nd_repstat;
911191783Srmacklem	mbuf_freem(nd->nd_mrep);
912191783Srmacklem	return (error);
913191783Srmacklem}
914191783Srmacklem
915191783Srmacklem/*
916191783Srmacklem * nfs getattr call with non-vnode arguemnts.
917191783Srmacklem */
918191783SrmacklemAPPLESTATIC int
919191783Srmacklemnfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred,
920191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, u_int64_t *xidp)
921191783Srmacklem{
922191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
923191783Srmacklem	int error, vers = NFS_VER2;
924191783Srmacklem	nfsattrbit_t attrbits;
925191783Srmacklem
926191783Srmacklem	nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, fhp, fhlen, NULL);
927191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
928191783Srmacklem		vers = NFS_VER4;
929191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
930191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
931191783Srmacklem	} else if (nd->nd_flag & ND_NFSV3) {
932191783Srmacklem		vers = NFS_VER3;
933191783Srmacklem	}
934191783Srmacklem	if (syscred)
935191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
936191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
937191783Srmacklem	    NFS_PROG, vers, NULL, 1, xidp);
938191783Srmacklem	if (error)
939191783Srmacklem		return (error);
940191783Srmacklem	if (!nd->nd_repstat)
941191783Srmacklem		error = nfsm_loadattr(nd, nap);
942191783Srmacklem	else
943191783Srmacklem		error = nd->nd_repstat;
944191783Srmacklem	mbuf_freem(nd->nd_mrep);
945191783Srmacklem	return (error);
946191783Srmacklem}
947191783Srmacklem
948191783Srmacklem/*
949191783Srmacklem * Do an nfs setattr operation.
950191783Srmacklem */
951191783SrmacklemAPPLESTATIC int
952191783Srmacklemnfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
953191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *rnap, int *attrflagp,
954191783Srmacklem    void *stuff)
955191783Srmacklem{
956191783Srmacklem	int error, expireret = 0, openerr, retrycnt;
957191783Srmacklem	u_int32_t clidrev = 0, mode;
958191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
959191783Srmacklem	struct nfsfh *nfhp;
960191783Srmacklem	nfsv4stateid_t stateid;
961191783Srmacklem	void *lckp;
962191783Srmacklem
963191783Srmacklem	if (nmp->nm_clp != NULL)
964191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
965191783Srmacklem	if (vap != NULL && NFSATTRISSET(u_quad_t, vap, va_size))
966191783Srmacklem		mode = NFSV4OPEN_ACCESSWRITE;
967191783Srmacklem	else
968191783Srmacklem		mode = NFSV4OPEN_ACCESSREAD;
969191783Srmacklem	retrycnt = 0;
970191783Srmacklem	do {
971191783Srmacklem		lckp = NULL;
972191783Srmacklem		openerr = 1;
973191783Srmacklem		if (NFSHASNFSV4(nmp)) {
974191783Srmacklem			nfhp = VTONFS(vp)->n_fhp;
975191783Srmacklem			error = nfscl_getstateid(vp, nfhp->nfh_fh,
976191783Srmacklem			    nfhp->nfh_len, mode, cred, p, &stateid, &lckp);
977191783Srmacklem			if (error && vnode_vtype(vp) == VREG &&
978191783Srmacklem			    (mode == NFSV4OPEN_ACCESSWRITE ||
979191783Srmacklem			     nfstest_openallsetattr)) {
980191783Srmacklem				/*
981191783Srmacklem				 * No Open stateid, so try and open the file
982191783Srmacklem				 * now.
983191783Srmacklem				 */
984191783Srmacklem				if (mode == NFSV4OPEN_ACCESSWRITE)
985191783Srmacklem					openerr = nfsrpc_open(vp, FWRITE, cred,
986191783Srmacklem					    p);
987191783Srmacklem				else
988191783Srmacklem					openerr = nfsrpc_open(vp, FREAD, cred,
989191783Srmacklem					    p);
990191783Srmacklem				if (!openerr)
991191783Srmacklem					(void) nfscl_getstateid(vp,
992191783Srmacklem					    nfhp->nfh_fh, nfhp->nfh_len,
993191783Srmacklem					    mode, cred, p, &stateid, &lckp);
994191783Srmacklem			}
995191783Srmacklem		}
996191783Srmacklem		if (vap != NULL)
997191783Srmacklem			error = nfsrpc_setattrrpc(vp, vap, &stateid, cred, p,
998191783Srmacklem			    rnap, attrflagp, stuff);
999191783Srmacklem		else
1000191783Srmacklem			error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
1001191783Srmacklem			    stuff);
1002191783Srmacklem		if (error == NFSERR_STALESTATEID)
1003191783Srmacklem			nfscl_initiate_recovery(nmp->nm_clp);
1004191783Srmacklem		if (lckp != NULL)
1005191783Srmacklem			nfscl_lockderef(lckp);
1006191783Srmacklem		if (!openerr)
1007192337Srmacklem			(void) nfsrpc_close(vp, 0, p);
1008191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1009191783Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1010191783Srmacklem		    error == NFSERR_OLDSTATEID) {
1011207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_setattr");
1012191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1013191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1014191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1015191783Srmacklem		}
1016191783Srmacklem		retrycnt++;
1017191783Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1018191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1019191783Srmacklem	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1020191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1021191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1022191783Srmacklem	if (error && retrycnt >= 4)
1023191783Srmacklem		error = EIO;
1024191783Srmacklem	return (error);
1025191783Srmacklem}
1026191783Srmacklem
1027191783Srmacklemstatic int
1028191783Srmacklemnfsrpc_setattrrpc(vnode_t vp, struct vattr *vap,
1029191783Srmacklem    nfsv4stateid_t *stateidp, struct ucred *cred, NFSPROC_T *p,
1030191783Srmacklem    struct nfsvattr *rnap, int *attrflagp, void *stuff)
1031191783Srmacklem{
1032191783Srmacklem	u_int32_t *tl;
1033191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1034191783Srmacklem	int error;
1035191783Srmacklem	nfsattrbit_t attrbits;
1036191783Srmacklem
1037191783Srmacklem	*attrflagp = 0;
1038191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_SETATTR, vp);
1039191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
1040191783Srmacklem		nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1041191783Srmacklem	vap->va_type = vnode_vtype(vp);
1042191783Srmacklem	nfscl_fillsattr(nd, vap, vp, NFSSATTR_FULL, 0);
1043191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
1044191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1045191783Srmacklem		*tl = newnfs_false;
1046191783Srmacklem	} else if (nd->nd_flag & ND_NFSV4) {
1047191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1048191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1049191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1050191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1051191783Srmacklem	}
1052191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
1053191783Srmacklem	if (error)
1054191783Srmacklem		return (error);
1055191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1056191783Srmacklem		error = nfscl_wcc_data(nd, vp, rnap, attrflagp, NULL, stuff);
1057191783Srmacklem	if ((nd->nd_flag & ND_NFSV4) && !error)
1058191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1059191783Srmacklem	if (!(nd->nd_flag & ND_NFSV3) && !nd->nd_repstat && !error)
1060191783Srmacklem		error = nfscl_postop_attr(nd, rnap, attrflagp, stuff);
1061191783Srmacklem	mbuf_freem(nd->nd_mrep);
1062191783Srmacklem	if (nd->nd_repstat && !error)
1063191783Srmacklem		error = nd->nd_repstat;
1064191783Srmacklem	return (error);
1065191783Srmacklem}
1066191783Srmacklem
1067191783Srmacklem/*
1068191783Srmacklem * nfs lookup rpc
1069191783Srmacklem */
1070191783SrmacklemAPPLESTATIC int
1071191783Srmacklemnfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
1072191783Srmacklem    NFSPROC_T *p, struct nfsvattr *dnap, struct nfsvattr *nap,
1073191783Srmacklem    struct nfsfh **nfhpp, int *attrflagp, int *dattrflagp, void *stuff)
1074191783Srmacklem{
1075191783Srmacklem	u_int32_t *tl;
1076191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1077191783Srmacklem	struct nfsmount *nmp;
1078191783Srmacklem	struct nfsnode *np;
1079191783Srmacklem	struct nfsfh *nfhp;
1080191783Srmacklem	nfsattrbit_t attrbits;
1081191783Srmacklem	int error = 0, lookupp = 0;
1082191783Srmacklem
1083191783Srmacklem	*attrflagp = 0;
1084191783Srmacklem	*dattrflagp = 0;
1085191783Srmacklem	if (vnode_vtype(dvp) != VDIR)
1086191783Srmacklem		return (ENOTDIR);
1087191783Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
1088191783Srmacklem	if (len > NFS_MAXNAMLEN)
1089191783Srmacklem		return (ENAMETOOLONG);
1090191783Srmacklem	if (NFSHASNFSV4(nmp) && len == 1 &&
1091191783Srmacklem		name[0] == '.') {
1092191783Srmacklem		/*
1093191783Srmacklem		 * Just return the current dir's fh.
1094191783Srmacklem		 */
1095191783Srmacklem		np = VTONFS(dvp);
1096191783Srmacklem		MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1097191783Srmacklem			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1098191783Srmacklem		nfhp->nfh_len = np->n_fhp->nfh_len;
1099191783Srmacklem		NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1100191783Srmacklem		*nfhpp = nfhp;
1101191783Srmacklem		return (0);
1102191783Srmacklem	}
1103191783Srmacklem	if (NFSHASNFSV4(nmp) && len == 2 &&
1104191783Srmacklem		name[0] == '.' && name[1] == '.') {
1105191783Srmacklem		lookupp = 1;
1106191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, dvp);
1107191783Srmacklem	} else {
1108191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_LOOKUP, dvp);
1109191783Srmacklem		(void) nfsm_strtom(nd, name, len);
1110191783Srmacklem	}
1111191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1112191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1113191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1114191783Srmacklem		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1115191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1116191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1117191783Srmacklem	}
1118191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, stuff);
1119191783Srmacklem	if (error)
1120191783Srmacklem		return (error);
1121191783Srmacklem	if (nd->nd_repstat) {
1122191783Srmacklem		/*
1123191783Srmacklem		 * When an NFSv4 Lookupp returns ENOENT, it means that
1124191783Srmacklem		 * the lookup is at the root of an fs, so return this dir.
1125191783Srmacklem		 */
1126191783Srmacklem		if (nd->nd_repstat == NFSERR_NOENT && lookupp) {
1127191783Srmacklem		    np = VTONFS(dvp);
1128191783Srmacklem		    MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1129191783Srmacklem			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1130191783Srmacklem		    nfhp->nfh_len = np->n_fhp->nfh_len;
1131191783Srmacklem		    NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1132191783Srmacklem		    *nfhpp = nfhp;
1133191783Srmacklem		    mbuf_freem(nd->nd_mrep);
1134191783Srmacklem		    return (0);
1135191783Srmacklem		}
1136191783Srmacklem		if (nd->nd_flag & ND_NFSV3)
1137191783Srmacklem		    error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1138265339Srmacklem		else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
1139265339Srmacklem		    ND_NFSV4) {
1140265339Srmacklem			/* Load the directory attributes. */
1141265339Srmacklem			error = nfsm_loadattr(nd, dnap);
1142265339Srmacklem			if (error == 0)
1143265339Srmacklem				*dattrflagp = 1;
1144265339Srmacklem		}
1145191783Srmacklem		goto nfsmout;
1146191783Srmacklem	}
1147191783Srmacklem	if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
1148265339Srmacklem		/* Load the directory attributes. */
1149265339Srmacklem		error = nfsm_loadattr(nd, dnap);
1150265339Srmacklem		if (error != 0)
1151191783Srmacklem			goto nfsmout;
1152265339Srmacklem		*dattrflagp = 1;
1153265339Srmacklem		/* Skip over the Lookup and GetFH operation status values. */
1154265339Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1155191783Srmacklem	}
1156191783Srmacklem	error = nfsm_getfh(nd, nfhpp);
1157191783Srmacklem	if (error)
1158191783Srmacklem		goto nfsmout;
1159191783Srmacklem
1160191783Srmacklem	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1161191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) && !error)
1162191783Srmacklem		error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1163191783Srmacklemnfsmout:
1164191783Srmacklem	mbuf_freem(nd->nd_mrep);
1165191783Srmacklem	if (!error && nd->nd_repstat)
1166191783Srmacklem		error = nd->nd_repstat;
1167191783Srmacklem	return (error);
1168191783Srmacklem}
1169191783Srmacklem
1170191783Srmacklem/*
1171191783Srmacklem * Do a readlink rpc.
1172191783Srmacklem */
1173191783SrmacklemAPPLESTATIC int
1174191783Srmacklemnfsrpc_readlink(vnode_t vp, struct uio *uiop, struct ucred *cred,
1175191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1176191783Srmacklem{
1177191783Srmacklem	u_int32_t *tl;
1178191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1179191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1180191783Srmacklem	nfsattrbit_t attrbits;
1181191783Srmacklem	int error, len, cangetattr = 1;
1182191783Srmacklem
1183191783Srmacklem	*attrflagp = 0;
1184191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_READLINK, vp);
1185191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1186191783Srmacklem		/*
1187191783Srmacklem		 * And do a Getattr op.
1188191783Srmacklem		 */
1189191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1190191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1191191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1192191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1193191783Srmacklem	}
1194191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
1195191783Srmacklem	if (error)
1196191783Srmacklem		return (error);
1197191783Srmacklem	if (nd->nd_flag & ND_NFSV3)
1198191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1199191783Srmacklem	if (!nd->nd_repstat && !error) {
1200191783Srmacklem		NFSM_STRSIZ(len, NFS_MAXPATHLEN);
1201191783Srmacklem		/*
1202191783Srmacklem		 * This seems weird to me, but must have been added to
1203191783Srmacklem		 * FreeBSD for some reason. The only thing I can think of
1204191783Srmacklem		 * is that there was/is some server that replies with
1205191783Srmacklem		 * more link data than it should?
1206191783Srmacklem		 */
1207191783Srmacklem		if (len == NFS_MAXPATHLEN) {
1208191783Srmacklem			NFSLOCKNODE(np);
1209191783Srmacklem			if (np->n_size > 0 && np->n_size < NFS_MAXPATHLEN) {
1210191783Srmacklem				len = np->n_size;
1211191783Srmacklem				cangetattr = 0;
1212191783Srmacklem			}
1213191783Srmacklem			NFSUNLOCKNODE(np);
1214191783Srmacklem		}
1215191783Srmacklem		error = nfsm_mbufuio(nd, uiop, len);
1216191783Srmacklem		if ((nd->nd_flag & ND_NFSV4) && !error && cangetattr)
1217191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1218191783Srmacklem	}
1219191783Srmacklem	if (nd->nd_repstat && !error)
1220191783Srmacklem		error = nd->nd_repstat;
1221191783Srmacklemnfsmout:
1222191783Srmacklem	mbuf_freem(nd->nd_mrep);
1223191783Srmacklem	return (error);
1224191783Srmacklem}
1225191783Srmacklem
1226191783Srmacklem/*
1227191783Srmacklem * Read operation.
1228191783Srmacklem */
1229191783SrmacklemAPPLESTATIC int
1230191783Srmacklemnfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
1231191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1232191783Srmacklem{
1233191783Srmacklem	int error, expireret = 0, retrycnt;
1234191783Srmacklem	u_int32_t clidrev = 0;
1235191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1236191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1237191783Srmacklem	struct ucred *newcred;
1238191783Srmacklem	struct nfsfh *nfhp = NULL;
1239191783Srmacklem	nfsv4stateid_t stateid;
1240191783Srmacklem	void *lckp;
1241191783Srmacklem
1242191783Srmacklem	if (nmp->nm_clp != NULL)
1243191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
1244191783Srmacklem	newcred = cred;
1245191783Srmacklem	if (NFSHASNFSV4(nmp)) {
1246191783Srmacklem		nfhp = np->n_fhp;
1247229953Srmacklem		newcred = NFSNEWCRED(cred);
1248191783Srmacklem	}
1249191783Srmacklem	retrycnt = 0;
1250191783Srmacklem	do {
1251191783Srmacklem		lckp = NULL;
1252191783Srmacklem		if (NFSHASNFSV4(nmp))
1253191783Srmacklem			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1254191783Srmacklem			    NFSV4OPEN_ACCESSREAD, newcred, p, &stateid, &lckp);
1255191783Srmacklem		error = nfsrpc_readrpc(vp, uiop, newcred, &stateid, p, nap,
1256191783Srmacklem		    attrflagp, stuff);
1257191783Srmacklem		if (error == NFSERR_STALESTATEID)
1258191783Srmacklem			nfscl_initiate_recovery(nmp->nm_clp);
1259191783Srmacklem		if (lckp != NULL)
1260191783Srmacklem			nfscl_lockderef(lckp);
1261191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1262191783Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1263191783Srmacklem		    error == NFSERR_OLDSTATEID) {
1264207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_read");
1265191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1266191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1267191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1268191783Srmacklem		}
1269191783Srmacklem		retrycnt++;
1270191783Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1271191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1272191783Srmacklem	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1273191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1274191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1275191783Srmacklem	if (error && retrycnt >= 4)
1276191783Srmacklem		error = EIO;
1277229953Srmacklem	if (NFSHASNFSV4(nmp))
1278191783Srmacklem		NFSFREECRED(newcred);
1279191783Srmacklem	return (error);
1280191783Srmacklem}
1281191783Srmacklem
1282191783Srmacklem/*
1283191783Srmacklem * The actual read RPC.
1284191783Srmacklem */
1285191783Srmacklemstatic int
1286191783Srmacklemnfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
1287191783Srmacklem    nfsv4stateid_t *stateidp, NFSPROC_T *p, struct nfsvattr *nap,
1288191783Srmacklem    int *attrflagp, void *stuff)
1289191783Srmacklem{
1290191783Srmacklem	u_int32_t *tl;
1291191783Srmacklem	int error = 0, len, retlen, tsiz, eof = 0;
1292191783Srmacklem	struct nfsrv_descript nfsd;
1293191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1294191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
1295220810Srmacklem	int rsize;
1296220876Srmacklem	off_t tmp_off;
1297191783Srmacklem
1298191783Srmacklem	*attrflagp = 0;
1299191783Srmacklem	tsiz = uio_uio_resid(uiop);
1300220876Srmacklem	tmp_off = uiop->uio_offset + tsiz;
1301220810Srmacklem	NFSLOCKMNT(nmp);
1302220876Srmacklem	if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset) {
1303220810Srmacklem		NFSUNLOCKMNT(nmp);
1304191783Srmacklem		return (EFBIG);
1305220810Srmacklem	}
1306220810Srmacklem	rsize = nmp->nm_rsize;
1307220810Srmacklem	NFSUNLOCKMNT(nmp);
1308191783Srmacklem	nd->nd_mrep = NULL;
1309191783Srmacklem	while (tsiz > 0) {
1310191783Srmacklem		*attrflagp = 0;
1311220810Srmacklem		len = (tsiz > rsize) ? rsize : tsiz;
1312191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_READ, vp);
1313191783Srmacklem		if (nd->nd_flag & ND_NFSV4)
1314191783Srmacklem			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1315191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
1316191783Srmacklem		if (nd->nd_flag & ND_NFSV2) {
1317191783Srmacklem			*tl++ = txdr_unsigned(uiop->uio_offset);
1318191783Srmacklem			*tl++ = txdr_unsigned(len);
1319191783Srmacklem			*tl = 0;
1320191783Srmacklem		} else {
1321191783Srmacklem			txdr_hyper(uiop->uio_offset, tl);
1322191783Srmacklem			*(tl + 2) = txdr_unsigned(len);
1323191783Srmacklem		}
1324191783Srmacklem		/*
1325191783Srmacklem		 * Since I can't do a Getattr for NFSv4 for Write, there
1326191783Srmacklem		 * doesn't seem any point in doing one here, either.
1327191783Srmacklem		 * (See the comment in nfsrpc_writerpc() for more info.)
1328191783Srmacklem		 */
1329191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
1330191783Srmacklem		if (error)
1331191783Srmacklem			return (error);
1332191783Srmacklem		if (nd->nd_flag & ND_NFSV3) {
1333191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1334191783Srmacklem		} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
1335191783Srmacklem			error = nfsm_loadattr(nd, nap);
1336191783Srmacklem			if (!error)
1337191783Srmacklem				*attrflagp = 1;
1338191783Srmacklem		}
1339191783Srmacklem		if (nd->nd_repstat || error) {
1340191783Srmacklem			if (!error)
1341191783Srmacklem				error = nd->nd_repstat;
1342191783Srmacklem			goto nfsmout;
1343191783Srmacklem		}
1344191783Srmacklem		if (nd->nd_flag & ND_NFSV3) {
1345191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1346191783Srmacklem			eof = fxdr_unsigned(int, *(tl + 1));
1347191783Srmacklem		} else if (nd->nd_flag & ND_NFSV4) {
1348191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1349191783Srmacklem			eof = fxdr_unsigned(int, *tl);
1350191783Srmacklem		}
1351246285Skib		NFSM_STRSIZ(retlen, len);
1352191783Srmacklem		error = nfsm_mbufuio(nd, uiop, retlen);
1353191783Srmacklem		if (error)
1354191783Srmacklem			goto nfsmout;
1355191783Srmacklem		mbuf_freem(nd->nd_mrep);
1356191783Srmacklem		nd->nd_mrep = NULL;
1357191783Srmacklem		tsiz -= retlen;
1358191783Srmacklem		if (!(nd->nd_flag & ND_NFSV2)) {
1359191783Srmacklem			if (eof || retlen == 0)
1360191783Srmacklem				tsiz = 0;
1361191783Srmacklem		} else if (retlen < len)
1362191783Srmacklem			tsiz = 0;
1363191783Srmacklem	}
1364191783Srmacklem	return (0);
1365191783Srmacklemnfsmout:
1366191783Srmacklem	if (nd->nd_mrep != NULL)
1367191783Srmacklem		mbuf_freem(nd->nd_mrep);
1368191783Srmacklem	return (error);
1369191783Srmacklem}
1370191783Srmacklem
1371191783Srmacklem/*
1372191783Srmacklem * nfs write operation
1373207082Srmacklem * When called_from_strategy != 0, it should return EIO for an error that
1374207082Srmacklem * indicates recovery is in progress, so that the buffer will be left
1375207082Srmacklem * dirty and be written back to the server later. If it loops around,
1376207082Srmacklem * the recovery thread could get stuck waiting for the buffer and recovery
1377207082Srmacklem * will then deadlock.
1378191783Srmacklem */
1379191783SrmacklemAPPLESTATIC int
1380222289Srmacklemnfsrpc_write(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
1381191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
1382207082Srmacklem    void *stuff, int called_from_strategy)
1383191783Srmacklem{
1384191783Srmacklem	int error, expireret = 0, retrycnt, nostateid;
1385191783Srmacklem	u_int32_t clidrev = 0;
1386191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1387191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1388191783Srmacklem	struct ucred *newcred;
1389191783Srmacklem	struct nfsfh *nfhp = NULL;
1390191783Srmacklem	nfsv4stateid_t stateid;
1391191783Srmacklem	void *lckp;
1392191783Srmacklem
1393222289Srmacklem	*must_commit = 0;
1394191783Srmacklem	if (nmp->nm_clp != NULL)
1395191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
1396191783Srmacklem	newcred = cred;
1397191783Srmacklem	if (NFSHASNFSV4(nmp)) {
1398229953Srmacklem		newcred = NFSNEWCRED(cred);
1399191783Srmacklem		nfhp = np->n_fhp;
1400191783Srmacklem	}
1401191783Srmacklem	retrycnt = 0;
1402191783Srmacklem	do {
1403191783Srmacklem		lckp = NULL;
1404191783Srmacklem		nostateid = 0;
1405191783Srmacklem		if (NFSHASNFSV4(nmp)) {
1406191783Srmacklem			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1407191783Srmacklem			    NFSV4OPEN_ACCESSWRITE, newcred, p, &stateid, &lckp);
1408191783Srmacklem			if (stateid.other[0] == 0 && stateid.other[1] == 0 &&
1409191783Srmacklem			    stateid.other[2] == 0) {
1410191783Srmacklem				nostateid = 1;
1411240977Srmacklem				NFSCL_DEBUG(1, "stateid0 in write\n");
1412191783Srmacklem			}
1413191783Srmacklem		}
1414191783Srmacklem
1415191783Srmacklem		/*
1416191783Srmacklem		 * If there is no stateid for NFSv4, it means this is an
1417191783Srmacklem		 * extraneous write after close. Basically a poorly
1418191783Srmacklem		 * implemented buffer cache. Just don't do the write.
1419191783Srmacklem		 */
1420191783Srmacklem		if (nostateid)
1421191783Srmacklem			error = 0;
1422191783Srmacklem		else
1423222289Srmacklem			error = nfsrpc_writerpc(vp, uiop, iomode, must_commit,
1424191783Srmacklem			    newcred, &stateid, p, nap, attrflagp, stuff);
1425191783Srmacklem		if (error == NFSERR_STALESTATEID)
1426191783Srmacklem			nfscl_initiate_recovery(nmp->nm_clp);
1427191783Srmacklem		if (lckp != NULL)
1428191783Srmacklem			nfscl_lockderef(lckp);
1429191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1430191783Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1431191783Srmacklem		    error == NFSERR_OLDSTATEID) {
1432207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_write");
1433191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1434191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1435191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1436191783Srmacklem		}
1437191783Srmacklem		retrycnt++;
1438207082Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
1439207082Srmacklem	    ((error == NFSERR_STALESTATEID ||
1440207082Srmacklem	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy == 0) ||
1441191783Srmacklem	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1442191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1443191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1444207082Srmacklem	if (error != 0 && (retrycnt >= 4 ||
1445207082Srmacklem	    ((error == NFSERR_STALESTATEID ||
1446207082Srmacklem	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy != 0)))
1447191783Srmacklem		error = EIO;
1448229953Srmacklem	if (NFSHASNFSV4(nmp))
1449191783Srmacklem		NFSFREECRED(newcred);
1450191783Srmacklem	return (error);
1451191783Srmacklem}
1452191783Srmacklem
1453191783Srmacklem/*
1454191783Srmacklem * The actual write RPC.
1455191783Srmacklem */
1456191783Srmacklemstatic int
1457191783Srmacklemnfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
1458222289Srmacklem    int *must_commit, struct ucred *cred, nfsv4stateid_t *stateidp,
1459191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1460191783Srmacklem{
1461191783Srmacklem	u_int32_t *tl;
1462191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1463191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1464191783Srmacklem	int error = 0, len, tsiz, rlen, commit, committed = NFSWRITE_FILESYNC;
1465191783Srmacklem	int wccflag = 0, wsize;
1466191783Srmacklem	int32_t backup;
1467191783Srmacklem	struct nfsrv_descript nfsd;
1468191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
1469191783Srmacklem	nfsattrbit_t attrbits;
1470220876Srmacklem	off_t tmp_off;
1471191783Srmacklem
1472209120Skib	KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
1473191783Srmacklem	*attrflagp = 0;
1474191783Srmacklem	tsiz = uio_uio_resid(uiop);
1475220876Srmacklem	tmp_off = uiop->uio_offset + tsiz;
1476191783Srmacklem	NFSLOCKMNT(nmp);
1477220876Srmacklem	if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset) {
1478191783Srmacklem		NFSUNLOCKMNT(nmp);
1479191783Srmacklem		return (EFBIG);
1480191783Srmacklem	}
1481191783Srmacklem	wsize = nmp->nm_wsize;
1482191783Srmacklem	NFSUNLOCKMNT(nmp);
1483191783Srmacklem	nd->nd_mrep = NULL;	/* NFSv2 sometimes does a write with */
1484191783Srmacklem	nd->nd_repstat = 0;	/* uio_resid == 0, so the while is not done */
1485191783Srmacklem	while (tsiz > 0) {
1486191783Srmacklem		*attrflagp = 0;
1487191783Srmacklem		len = (tsiz > wsize) ? wsize : tsiz;
1488191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_WRITE, vp);
1489191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
1490191783Srmacklem			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1491191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+2*NFSX_UNSIGNED);
1492191783Srmacklem			txdr_hyper(uiop->uio_offset, tl);
1493191783Srmacklem			tl += 2;
1494191783Srmacklem			*tl++ = txdr_unsigned(*iomode);
1495191783Srmacklem			*tl = txdr_unsigned(len);
1496191783Srmacklem		} else if (nd->nd_flag & ND_NFSV3) {
1497191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+3*NFSX_UNSIGNED);
1498191783Srmacklem			txdr_hyper(uiop->uio_offset, tl);
1499191783Srmacklem			tl += 2;
1500191783Srmacklem			*tl++ = txdr_unsigned(len);
1501191783Srmacklem			*tl++ = txdr_unsigned(*iomode);
1502191783Srmacklem			*tl = txdr_unsigned(len);
1503191783Srmacklem		} else {
1504191783Srmacklem			u_int32_t x;
1505191783Srmacklem
1506191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1507191783Srmacklem			/*
1508191783Srmacklem			 * Not sure why someone changed this, since the
1509191783Srmacklem			 * RFC clearly states that "beginoffset" and
1510191783Srmacklem			 * "totalcount" are ignored, but it wouldn't
1511191783Srmacklem			 * surprise me if there's a busted server out there.
1512191783Srmacklem			 */
1513191783Srmacklem			/* Set both "begin" and "current" to non-garbage. */
1514191783Srmacklem			x = txdr_unsigned((u_int32_t)uiop->uio_offset);
1515191783Srmacklem			*tl++ = x;      /* "begin offset" */
1516191783Srmacklem			*tl++ = x;      /* "current offset" */
1517191783Srmacklem			x = txdr_unsigned(len);
1518191783Srmacklem			*tl++ = x;      /* total to this offset */
1519191783Srmacklem			*tl = x;        /* size of this write */
1520191783Srmacklem
1521191783Srmacklem		}
1522191783Srmacklem		nfsm_uiombuf(nd, uiop, len);
1523191783Srmacklem		/*
1524191783Srmacklem		 * Although it is tempting to do a normal Getattr Op in the
1525191783Srmacklem		 * NFSv4 compound, the result can be a nearly hung client
1526191783Srmacklem		 * system if the Getattr asks for Owner and/or OwnerGroup.
1527191783Srmacklem		 * It occurs when the client can't map either the Owner or
1528191783Srmacklem		 * Owner_group name in the Getattr reply to a uid/gid. When
1529191783Srmacklem		 * there is a cache miss, the kernel does an upcall to the
1530191783Srmacklem		 * nfsuserd. Then, it can try and read the local /etc/passwd
1531191783Srmacklem		 * or /etc/group file. It can then block in getnewbuf(),
1532191783Srmacklem		 * waiting for dirty writes to be pushed to the NFS server.
1533191783Srmacklem		 * The only reason this doesn't result in a complete
1534191783Srmacklem		 * deadlock, is that the upcall times out and allows
1535191783Srmacklem		 * the write to complete. However, progress is so slow
1536191783Srmacklem		 * that it might just as well be deadlocked.
1537223657Srmacklem		 * As such, we get the rest of the attributes, but not
1538223657Srmacklem		 * Owner or Owner_group.
1539191783Srmacklem		 * nb: nfscl_loadattrcache() needs to be told that these
1540191783Srmacklem		 *     partial attributes from a write rpc are being
1541191783Srmacklem		 *     passed in, via a argument flag.
1542191783Srmacklem		 */
1543191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
1544191783Srmacklem			NFSWRITEGETATTR_ATTRBIT(&attrbits);
1545191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1546191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
1547191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
1548191783Srmacklem		}
1549191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
1550191783Srmacklem		if (error)
1551191783Srmacklem			return (error);
1552191783Srmacklem		if (nd->nd_repstat) {
1553191783Srmacklem			/*
1554191783Srmacklem			 * In case the rpc gets retried, roll
1555191783Srmacklem			 * the uio fileds changed by nfsm_uiombuf()
1556191783Srmacklem			 * back.
1557191783Srmacklem			 */
1558191783Srmacklem			uiop->uio_offset -= len;
1559191783Srmacklem			uio_uio_resid_add(uiop, len);
1560191783Srmacklem			uio_iov_base_add(uiop, -len);
1561191783Srmacklem			uio_iov_len_add(uiop, len);
1562191783Srmacklem		}
1563191783Srmacklem		if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1564191783Srmacklem			error = nfscl_wcc_data(nd, vp, nap, attrflagp,
1565191783Srmacklem			    &wccflag, stuff);
1566191783Srmacklem			if (error)
1567191783Srmacklem				goto nfsmout;
1568191783Srmacklem		}
1569191783Srmacklem		if (!nd->nd_repstat) {
1570191783Srmacklem			if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1571191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED
1572191783Srmacklem					+ NFSX_VERF);
1573191783Srmacklem				rlen = fxdr_unsigned(int, *tl++);
1574191783Srmacklem				if (rlen == 0) {
1575191783Srmacklem					error = NFSERR_IO;
1576191783Srmacklem					goto nfsmout;
1577191783Srmacklem				} else if (rlen < len) {
1578191783Srmacklem					backup = len - rlen;
1579191783Srmacklem					uio_iov_base_add(uiop, -(backup));
1580191783Srmacklem					uio_iov_len_add(uiop, backup);
1581191783Srmacklem					uiop->uio_offset -= backup;
1582191783Srmacklem					uio_uio_resid_add(uiop, backup);
1583191783Srmacklem					len = rlen;
1584191783Srmacklem				}
1585191783Srmacklem				commit = fxdr_unsigned(int, *tl++);
1586191783Srmacklem
1587191783Srmacklem				/*
1588191783Srmacklem				 * Return the lowest committment level
1589191783Srmacklem				 * obtained by any of the RPCs.
1590191783Srmacklem				 */
1591191783Srmacklem				if (committed == NFSWRITE_FILESYNC)
1592191783Srmacklem					committed = commit;
1593191783Srmacklem				else if (committed == NFSWRITE_DATASYNC &&
1594191783Srmacklem					commit == NFSWRITE_UNSTABLE)
1595191783Srmacklem					committed = commit;
1596191783Srmacklem				NFSLOCKMNT(nmp);
1597191783Srmacklem				if (!NFSHASWRITEVERF(nmp)) {
1598191783Srmacklem					NFSBCOPY((caddr_t)tl,
1599191783Srmacklem					    (caddr_t)&nmp->nm_verf[0],
1600191783Srmacklem					    NFSX_VERF);
1601191783Srmacklem					NFSSETWRITEVERF(nmp);
1602222289Srmacklem	    			} else if (NFSBCMP(tl, nmp->nm_verf,
1603222289Srmacklem				    NFSX_VERF)) {
1604222289Srmacklem					*must_commit = 1;
1605222289Srmacklem					NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
1606191783Srmacklem				}
1607191783Srmacklem				NFSUNLOCKMNT(nmp);
1608191783Srmacklem			}
1609191783Srmacklem			if (nd->nd_flag & ND_NFSV4)
1610191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1611191783Srmacklem			if (nd->nd_flag & (ND_NFSV2 | ND_NFSV4)) {
1612191783Srmacklem				error = nfsm_loadattr(nd, nap);
1613191783Srmacklem				if (!error)
1614191783Srmacklem					*attrflagp = NFS_LATTR_NOSHRINK;
1615191783Srmacklem			}
1616191783Srmacklem		} else {
1617191783Srmacklem			error = nd->nd_repstat;
1618191783Srmacklem		}
1619191783Srmacklem		if (error)
1620191783Srmacklem			goto nfsmout;
1621191783Srmacklem		NFSWRITERPC_SETTIME(wccflag, np, (nd->nd_flag & ND_NFSV4));
1622191783Srmacklem		mbuf_freem(nd->nd_mrep);
1623191783Srmacklem		nd->nd_mrep = NULL;
1624191783Srmacklem		tsiz -= len;
1625191783Srmacklem	}
1626191783Srmacklemnfsmout:
1627191783Srmacklem	if (nd->nd_mrep != NULL)
1628191783Srmacklem		mbuf_freem(nd->nd_mrep);
1629191783Srmacklem	*iomode = committed;
1630191783Srmacklem	if (nd->nd_repstat && !error)
1631191783Srmacklem		error = nd->nd_repstat;
1632191783Srmacklem	return (error);
1633191783Srmacklem}
1634191783Srmacklem
1635191783Srmacklem/*
1636191783Srmacklem * nfs mknod rpc
1637191783Srmacklem * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1638191783Srmacklem * mode set to specify the file type and the size field for rdev.
1639191783Srmacklem */
1640191783SrmacklemAPPLESTATIC int
1641191783Srmacklemnfsrpc_mknod(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1642191783Srmacklem    u_int32_t rdev, enum vtype vtyp, struct ucred *cred, NFSPROC_T *p,
1643191783Srmacklem    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1644191783Srmacklem    int *attrflagp, int *dattrflagp, void *dstuff)
1645191783Srmacklem{
1646191783Srmacklem	u_int32_t *tl;
1647191783Srmacklem	int error = 0;
1648191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1649191783Srmacklem	nfsattrbit_t attrbits;
1650191783Srmacklem
1651191783Srmacklem	*nfhpp = NULL;
1652191783Srmacklem	*attrflagp = 0;
1653191783Srmacklem	*dattrflagp = 0;
1654191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
1655191783Srmacklem		return (ENAMETOOLONG);
1656191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_MKNOD, dvp);
1657191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1658201345Srmacklem		if (vtyp == VBLK || vtyp == VCHR) {
1659201345Srmacklem			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1660201345Srmacklem			*tl++ = vtonfsv34_type(vtyp);
1661201345Srmacklem			*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1662201345Srmacklem			*tl = txdr_unsigned(NFSMINOR(rdev));
1663201345Srmacklem		} else {
1664201345Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1665201345Srmacklem			*tl = vtonfsv34_type(vtyp);
1666201345Srmacklem		}
1667191783Srmacklem	}
1668191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
1669191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
1670191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1671191783Srmacklem		*tl = vtonfsv34_type(vtyp);
1672191783Srmacklem	}
1673191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1674191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1675191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) &&
1676191783Srmacklem	    (vtyp == VCHR || vtyp == VBLK)) {
1677191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1678191783Srmacklem		*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1679191783Srmacklem		*tl = txdr_unsigned(NFSMINOR(rdev));
1680191783Srmacklem	}
1681191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1682191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1683191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1684191783Srmacklem		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1685191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1686191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1687191783Srmacklem	}
1688191783Srmacklem	if (nd->nd_flag & ND_NFSV2)
1689191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZERDEV, rdev);
1690191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
1691191783Srmacklem	if (error)
1692191783Srmacklem		return (error);
1693191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
1694191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1695191783Srmacklem	if (!nd->nd_repstat) {
1696191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
1697191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1698191783Srmacklem			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1699191783Srmacklem			if (error)
1700191783Srmacklem				goto nfsmout;
1701191783Srmacklem		}
1702191783Srmacklem		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1703191783Srmacklem		if (error)
1704191783Srmacklem			goto nfsmout;
1705191783Srmacklem	}
1706191783Srmacklem	if (nd->nd_flag & ND_NFSV3)
1707191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1708191783Srmacklem	if (!error && nd->nd_repstat)
1709191783Srmacklem		error = nd->nd_repstat;
1710191783Srmacklemnfsmout:
1711191783Srmacklem	mbuf_freem(nd->nd_mrep);
1712191783Srmacklem	return (error);
1713191783Srmacklem}
1714191783Srmacklem
1715191783Srmacklem/*
1716191783Srmacklem * nfs file create call
1717191783Srmacklem * Mostly just call the approriate routine. (I separated out v4, so that
1718191783Srmacklem * error recovery wouldn't be as difficult.)
1719191783Srmacklem */
1720191783SrmacklemAPPLESTATIC int
1721191783Srmacklemnfsrpc_create(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1722191783Srmacklem    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1723191783Srmacklem    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1724191783Srmacklem    int *attrflagp, int *dattrflagp, void *dstuff)
1725191783Srmacklem{
1726191783Srmacklem	int error = 0, newone, expireret = 0, retrycnt, unlocked;
1727191783Srmacklem	struct nfsclowner *owp;
1728191783Srmacklem	struct nfscldeleg *dp;
1729191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
1730191783Srmacklem	u_int32_t clidrev;
1731191783Srmacklem
1732191783Srmacklem	if (NFSHASNFSV4(nmp)) {
1733191783Srmacklem	    retrycnt = 0;
1734191783Srmacklem	    do {
1735191783Srmacklem		dp = NULL;
1736191783Srmacklem		error = nfscl_open(dvp, NULL, 0, (NFSV4OPEN_ACCESSWRITE |
1737191783Srmacklem		    NFSV4OPEN_ACCESSREAD), 0, cred, p, &owp, NULL, &newone,
1738191783Srmacklem		    NULL, 1);
1739191783Srmacklem		if (error)
1740191783Srmacklem			return (error);
1741191783Srmacklem		if (nmp->nm_clp != NULL)
1742191783Srmacklem			clidrev = nmp->nm_clp->nfsc_clientidrev;
1743191783Srmacklem		else
1744191783Srmacklem			clidrev = 0;
1745191783Srmacklem		error = nfsrpc_createv4(dvp, name, namelen, vap, cverf, fmode,
1746191783Srmacklem		  owp, &dp, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1747191783Srmacklem		  dstuff, &unlocked);
1748210034Srmacklem		/*
1749210034Srmacklem		 * There is no need to invalidate cached attributes here,
1750210034Srmacklem		 * since new post-delegation issue attributes are always
1751210034Srmacklem		 * returned by nfsrpc_createv4() and these will update the
1752210034Srmacklem		 * attribute cache.
1753210034Srmacklem		 */
1754191783Srmacklem		if (dp != NULL)
1755191783Srmacklem			(void) nfscl_deleg(nmp->nm_mountp, owp->nfsow_clp,
1756191783Srmacklem			    (*nfhpp)->nfh_fh, (*nfhpp)->nfh_len, cred, p, &dp);
1757191783Srmacklem		nfscl_ownerrelease(owp, error, newone, unlocked);
1758191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1759191783Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY) {
1760207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_open");
1761191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1762191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1763191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1764191783Srmacklem			retrycnt++;
1765191783Srmacklem		}
1766191783Srmacklem	    } while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1767191783Srmacklem		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1768191783Srmacklem		((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1769191783Srmacklem		 expireret == 0 && clidrev != 0 && retrycnt < 4));
1770191783Srmacklem	    if (error && retrycnt >= 4)
1771191783Srmacklem		    error = EIO;
1772191783Srmacklem	} else {
1773191783Srmacklem		error = nfsrpc_createv23(dvp, name, namelen, vap, cverf,
1774191783Srmacklem		    fmode, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1775191783Srmacklem		    dstuff);
1776191783Srmacklem	}
1777191783Srmacklem	return (error);
1778191783Srmacklem}
1779191783Srmacklem
1780191783Srmacklem/*
1781191783Srmacklem * The create rpc for v2 and 3.
1782191783Srmacklem */
1783191783Srmacklemstatic int
1784191783Srmacklemnfsrpc_createv23(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1785191783Srmacklem    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1786191783Srmacklem    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1787191783Srmacklem    int *attrflagp, int *dattrflagp, void *dstuff)
1788191783Srmacklem{
1789191783Srmacklem	u_int32_t *tl;
1790191783Srmacklem	int error = 0;
1791191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1792191783Srmacklem
1793191783Srmacklem	*nfhpp = NULL;
1794191783Srmacklem	*attrflagp = 0;
1795191783Srmacklem	*dattrflagp = 0;
1796191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
1797191783Srmacklem		return (ENAMETOOLONG);
1798191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1799191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
1800191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
1801191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1802191783Srmacklem		if (fmode & O_EXCL) {
1803191783Srmacklem			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
1804191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1805191783Srmacklem			*tl++ = cverf.lval[0];
1806191783Srmacklem			*tl = cverf.lval[1];
1807191783Srmacklem		} else {
1808191783Srmacklem			*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
1809191783Srmacklem			nfscl_fillsattr(nd, vap, dvp, 0, 0);
1810191783Srmacklem		}
1811191783Srmacklem	} else {
1812191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZE0, 0);
1813191783Srmacklem	}
1814191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
1815191783Srmacklem	if (error)
1816191783Srmacklem		return (error);
1817191783Srmacklem	if (nd->nd_repstat == 0) {
1818191783Srmacklem		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1819191783Srmacklem		if (error)
1820191783Srmacklem			goto nfsmout;
1821191783Srmacklem	}
1822191783Srmacklem	if (nd->nd_flag & ND_NFSV3)
1823191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1824191783Srmacklem	if (nd->nd_repstat != 0 && error == 0)
1825191783Srmacklem		error = nd->nd_repstat;
1826191783Srmacklemnfsmout:
1827191783Srmacklem	mbuf_freem(nd->nd_mrep);
1828191783Srmacklem	return (error);
1829191783Srmacklem}
1830191783Srmacklem
1831191783Srmacklemstatic int
1832191783Srmacklemnfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1833191783Srmacklem    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
1834191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
1835191783Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
1836191783Srmacklem    int *dattrflagp, void *dstuff, int *unlockedp)
1837191783Srmacklem{
1838191783Srmacklem	u_int32_t *tl;
1839191783Srmacklem	int error = 0, deleg, newone, ret, acesize, limitby;
1840191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1841191783Srmacklem	struct nfsclopen *op;
1842191783Srmacklem	struct nfscldeleg *dp = NULL;
1843191783Srmacklem	struct nfsnode *np;
1844191783Srmacklem	struct nfsfh *nfhp;
1845191783Srmacklem	nfsattrbit_t attrbits;
1846191783Srmacklem	nfsv4stateid_t stateid;
1847191783Srmacklem	u_int32_t rflags;
1848191783Srmacklem
1849265340Srmacklem	np = VTONFS(dvp);
1850191783Srmacklem	*unlockedp = 0;
1851191783Srmacklem	*nfhpp = NULL;
1852191783Srmacklem	*dpp = NULL;
1853191783Srmacklem	*attrflagp = 0;
1854191783Srmacklem	*dattrflagp = 0;
1855191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
1856191783Srmacklem		return (ENAMETOOLONG);
1857191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1858191783Srmacklem	/*
1859191783Srmacklem	 * For V4, this is actually an Open op.
1860191783Srmacklem	 */
1861191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1862191783Srmacklem	*tl++ = txdr_unsigned(owp->nfsow_seqid);
1863191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
1864191783Srmacklem	    NFSV4OPEN_ACCESSREAD);
1865191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
1866191783Srmacklem	*tl++ = owp->nfsow_clp->nfsc_clientid.lval[0];
1867191783Srmacklem	*tl = owp->nfsow_clp->nfsc_clientid.lval[1];
1868191783Srmacklem	(void) nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
1869191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1870191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
1871191783Srmacklem	if (fmode & O_EXCL) {
1872191783Srmacklem		*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
1873191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1874191783Srmacklem		*tl++ = cverf.lval[0];
1875191783Srmacklem		*tl = cverf.lval[1];
1876191783Srmacklem	} else {
1877191783Srmacklem		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
1878191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1879191783Srmacklem	}
1880191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1881191783Srmacklem	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
1882191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
1883265340Srmacklem	/* Get the new file's handle and attributes. */
1884191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1885191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1886191783Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
1887191783Srmacklem	NFSGETATTR_ATTRBIT(&attrbits);
1888191783Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
1889265340Srmacklem	/* Get the directory's post-op attributes. */
1890265340Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1891265340Srmacklem	*tl = txdr_unsigned(NFSV4OP_PUTFH);
1892265340Srmacklem	(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 0);
1893265340Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1894265340Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
1895265340Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
1896191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
1897191783Srmacklem	if (error)
1898191783Srmacklem		return (error);
1899191783Srmacklem	NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
1900191783Srmacklem	if (nd->nd_repstat == 0) {
1901191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
1902191783Srmacklem		    6 * NFSX_UNSIGNED);
1903191783Srmacklem		stateid.seqid = *tl++;
1904191783Srmacklem		stateid.other[0] = *tl++;
1905191783Srmacklem		stateid.other[1] = *tl++;
1906191783Srmacklem		stateid.other[2] = *tl;
1907191783Srmacklem		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
1908191783Srmacklem		(void) nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1909191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1910191783Srmacklem		deleg = fxdr_unsigned(int, *tl);
1911191783Srmacklem		if (deleg == NFSV4OPEN_DELEGATEREAD ||
1912191783Srmacklem		    deleg == NFSV4OPEN_DELEGATEWRITE) {
1913191783Srmacklem			if (!(owp->nfsow_clp->nfsc_flags &
1914191783Srmacklem			      NFSCLFLAGS_FIRSTDELEG))
1915191783Srmacklem				owp->nfsow_clp->nfsc_flags |=
1916191783Srmacklem				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
1917191783Srmacklem			MALLOC(dp, struct nfscldeleg *,
1918191783Srmacklem			    sizeof (struct nfscldeleg) + NFSX_V4FHMAX,
1919191783Srmacklem			    M_NFSCLDELEG, M_WAITOK);
1920191783Srmacklem			LIST_INIT(&dp->nfsdl_owner);
1921191783Srmacklem			LIST_INIT(&dp->nfsdl_lock);
1922191783Srmacklem			dp->nfsdl_clp = owp->nfsow_clp;
1923191783Srmacklem			newnfs_copyincred(cred, &dp->nfsdl_cred);
1924191783Srmacklem			nfscl_lockinit(&dp->nfsdl_rwlock);
1925191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
1926191783Srmacklem			    NFSX_UNSIGNED);
1927191783Srmacklem			dp->nfsdl_stateid.seqid = *tl++;
1928191783Srmacklem			dp->nfsdl_stateid.other[0] = *tl++;
1929191783Srmacklem			dp->nfsdl_stateid.other[1] = *tl++;
1930191783Srmacklem			dp->nfsdl_stateid.other[2] = *tl++;
1931191783Srmacklem			ret = fxdr_unsigned(int, *tl);
1932191783Srmacklem			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
1933191783Srmacklem				dp->nfsdl_flags = NFSCLDL_WRITE;
1934191783Srmacklem				/*
1935191783Srmacklem				 * Indicates how much the file can grow.
1936191783Srmacklem				 */
1937191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *,
1938191783Srmacklem				    3 * NFSX_UNSIGNED);
1939191783Srmacklem				limitby = fxdr_unsigned(int, *tl++);
1940191783Srmacklem				switch (limitby) {
1941191783Srmacklem				case NFSV4OPEN_LIMITSIZE:
1942191783Srmacklem					dp->nfsdl_sizelimit = fxdr_hyper(tl);
1943191783Srmacklem					break;
1944191783Srmacklem				case NFSV4OPEN_LIMITBLOCKS:
1945191783Srmacklem					dp->nfsdl_sizelimit =
1946191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl++);
1947191783Srmacklem					dp->nfsdl_sizelimit *=
1948191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl);
1949191783Srmacklem					break;
1950191783Srmacklem				default:
1951191783Srmacklem					error = NFSERR_BADXDR;
1952191783Srmacklem					goto nfsmout;
1953191783Srmacklem				};
1954191783Srmacklem			} else {
1955191783Srmacklem				dp->nfsdl_flags = NFSCLDL_READ;
1956191783Srmacklem			}
1957191783Srmacklem			if (ret)
1958191783Srmacklem				dp->nfsdl_flags |= NFSCLDL_RECALL;
1959191783Srmacklem			error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
1960191783Srmacklem			    &acesize, p);
1961191783Srmacklem			if (error)
1962191783Srmacklem				goto nfsmout;
1963191783Srmacklem		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
1964191783Srmacklem			error = NFSERR_BADXDR;
1965191783Srmacklem			goto nfsmout;
1966191783Srmacklem		}
1967191783Srmacklem		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1968191783Srmacklem		if (error)
1969191783Srmacklem			goto nfsmout;
1970265340Srmacklem		/* Get rid of the PutFH and Getattr status values. */
1971265340Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1972265340Srmacklem		/* Load the directory attributes. */
1973265340Srmacklem		error = nfsm_loadattr(nd, dnap);
1974265340Srmacklem		if (error)
1975265340Srmacklem			goto nfsmout;
1976265340Srmacklem		*dattrflagp = 1;
1977191783Srmacklem		if (dp != NULL && *attrflagp) {
1978191783Srmacklem			dp->nfsdl_change = nnap->na_filerev;
1979191783Srmacklem			dp->nfsdl_modtime = nnap->na_mtime;
1980191783Srmacklem			dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
1981191783Srmacklem		}
1982191783Srmacklem		/*
1983191783Srmacklem		 * We can now complete the Open state.
1984191783Srmacklem		 */
1985191783Srmacklem		nfhp = *nfhpp;
1986191783Srmacklem		if (dp != NULL) {
1987191783Srmacklem			dp->nfsdl_fhlen = nfhp->nfh_len;
1988191783Srmacklem			NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh, nfhp->nfh_len);
1989191783Srmacklem		}
1990191783Srmacklem		/*
1991191783Srmacklem		 * Get an Open structure that will be
1992191783Srmacklem		 * attached to the OpenOwner, acquired already.
1993191783Srmacklem		 */
1994191783Srmacklem		error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len,
1995191783Srmacklem		    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
1996191783Srmacklem		    cred, p, NULL, &op, &newone, NULL, 0);
1997191783Srmacklem		if (error)
1998191783Srmacklem			goto nfsmout;
1999191783Srmacklem		op->nfso_stateid = stateid;
2000191783Srmacklem		newnfs_copyincred(cred, &op->nfso_cred);
2001191783Srmacklem		if ((rflags & NFSV4OPEN_RESULTCONFIRM)) {
2002191783Srmacklem		    do {
2003191783Srmacklem			ret = nfsrpc_openconfirm(dvp, nfhp->nfh_fh,
2004191783Srmacklem			    nfhp->nfh_len, op, cred, p);
2005191783Srmacklem			if (ret == NFSERR_DELAY)
2006207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_create");
2007191783Srmacklem		    } while (ret == NFSERR_DELAY);
2008191783Srmacklem		    error = ret;
2009191783Srmacklem		}
2010191783Srmacklem
2011191783Srmacklem		/*
2012191783Srmacklem		 * If the server is handing out delegations, but we didn't
2013191783Srmacklem		 * get one because an OpenConfirm was required, try the
2014191783Srmacklem		 * Open again, to get a delegation. This is a harmless no-op,
2015191783Srmacklem		 * from a server's point of view.
2016191783Srmacklem		 */
2017191783Srmacklem		if ((rflags & NFSV4OPEN_RESULTCONFIRM) &&
2018191783Srmacklem		    (owp->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG) &&
2019191783Srmacklem		    !error && dp == NULL) {
2020191783Srmacklem		    do {
2021191783Srmacklem			ret = nfsrpc_openrpc(VFSTONFS(vnode_mount(dvp)), dvp,
2022191783Srmacklem			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
2023191783Srmacklem			    nfhp->nfh_fh, nfhp->nfh_len,
2024191783Srmacklem			    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), op,
2025191783Srmacklem			    name, namelen, &dp, 0, 0x0, cred, p, 0, 1);
2026191783Srmacklem			if (ret == NFSERR_DELAY)
2027207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_crt2");
2028191783Srmacklem		    } while (ret == NFSERR_DELAY);
2029191783Srmacklem		    if (ret) {
2030191783Srmacklem			if (dp != NULL)
2031191783Srmacklem				FREE((caddr_t)dp, M_NFSCLDELEG);
2032191783Srmacklem			if (ret == NFSERR_STALECLIENTID ||
2033191783Srmacklem			    ret == NFSERR_STALEDONTRECOVER)
2034191783Srmacklem				error = ret;
2035191783Srmacklem		    }
2036191783Srmacklem		}
2037191783Srmacklem		nfscl_openrelease(op, error, newone);
2038191783Srmacklem		*unlockedp = 1;
2039191783Srmacklem	}
2040191783Srmacklem	if (nd->nd_repstat != 0 && error == 0)
2041191783Srmacklem		error = nd->nd_repstat;
2042191783Srmacklem	if (error == NFSERR_STALECLIENTID)
2043191783Srmacklem		nfscl_initiate_recovery(owp->nfsow_clp);
2044191783Srmacklemnfsmout:
2045191783Srmacklem	if (!error)
2046191783Srmacklem		*dpp = dp;
2047191783Srmacklem	else if (dp != NULL)
2048191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
2049191783Srmacklem	mbuf_freem(nd->nd_mrep);
2050191783Srmacklem	return (error);
2051191783Srmacklem}
2052191783Srmacklem
2053191783Srmacklem/*
2054191783Srmacklem * Nfs remove rpc
2055191783Srmacklem */
2056191783SrmacklemAPPLESTATIC int
2057191783Srmacklemnfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp,
2058191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp,
2059191783Srmacklem    void *dstuff)
2060191783Srmacklem{
2061191783Srmacklem	u_int32_t *tl;
2062191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2063191783Srmacklem	struct nfsnode *np;
2064191783Srmacklem	struct nfsmount *nmp;
2065191783Srmacklem	nfsv4stateid_t dstateid;
2066191783Srmacklem	int error, ret = 0, i;
2067191783Srmacklem
2068191783Srmacklem	*dattrflagp = 0;
2069191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2070191783Srmacklem		return (ENAMETOOLONG);
2071191783Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
2072191783Srmacklemtryagain:
2073191783Srmacklem	if (NFSHASNFSV4(nmp) && ret == 0) {
2074191783Srmacklem		ret = nfscl_removedeleg(vp, p, &dstateid);
2075191783Srmacklem		if (ret == 1) {
2076191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp);
2077191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
2078191783Srmacklem			    NFSX_UNSIGNED);
2079191783Srmacklem			*tl++ = dstateid.seqid;
2080191783Srmacklem			*tl++ = dstateid.other[0];
2081191783Srmacklem			*tl++ = dstateid.other[1];
2082191783Srmacklem			*tl++ = dstateid.other[2];
2083191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2084191783Srmacklem			np = VTONFS(dvp);
2085191783Srmacklem			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2086191783Srmacklem			    np->n_fhp->nfh_len, 0);
2087191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2088191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_REMOVE);
2089191783Srmacklem		}
2090191783Srmacklem	} else {
2091191783Srmacklem		ret = 0;
2092191783Srmacklem	}
2093191783Srmacklem	if (ret == 0)
2094191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp);
2095191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2096191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2097191783Srmacklem	if (error)
2098191783Srmacklem		return (error);
2099191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2100191783Srmacklem		/* For NFSv4, parse out any Delereturn replies. */
2101191783Srmacklem		if (ret > 0 && nd->nd_repstat != 0 &&
2102191783Srmacklem		    (nd->nd_flag & ND_NOMOREDATA)) {
2103191783Srmacklem			/*
2104191783Srmacklem			 * If the Delegreturn failed, try again without
2105191783Srmacklem			 * it. The server will Recall, as required.
2106191783Srmacklem			 */
2107191783Srmacklem			mbuf_freem(nd->nd_mrep);
2108191783Srmacklem			goto tryagain;
2109191783Srmacklem		}
2110191783Srmacklem		for (i = 0; i < (ret * 2); i++) {
2111191783Srmacklem			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2112191783Srmacklem			    ND_NFSV4) {
2113191783Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2114191783Srmacklem			    if (*(tl + 1))
2115191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
2116191783Srmacklem			}
2117191783Srmacklem		}
2118191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2119191783Srmacklem	}
2120191783Srmacklem	if (nd->nd_repstat && !error)
2121191783Srmacklem		error = nd->nd_repstat;
2122191783Srmacklemnfsmout:
2123191783Srmacklem	mbuf_freem(nd->nd_mrep);
2124191783Srmacklem	return (error);
2125191783Srmacklem}
2126191783Srmacklem
2127191783Srmacklem/*
2128191783Srmacklem * Do an nfs rename rpc.
2129191783Srmacklem */
2130191783SrmacklemAPPLESTATIC int
2131191783Srmacklemnfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen,
2132191783Srmacklem    vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred *cred,
2133191783Srmacklem    NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap,
2134191783Srmacklem    int *fattrflagp, int *tattrflagp, void *fstuff, void *tstuff)
2135191783Srmacklem{
2136191783Srmacklem	u_int32_t *tl;
2137191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2138191783Srmacklem	struct nfsmount *nmp;
2139191783Srmacklem	struct nfsnode *np;
2140191783Srmacklem	nfsattrbit_t attrbits;
2141191783Srmacklem	nfsv4stateid_t fdstateid, tdstateid;
2142191783Srmacklem	int error = 0, ret = 0, gottd = 0, gotfd = 0, i;
2143191783Srmacklem
2144191783Srmacklem	*fattrflagp = 0;
2145191783Srmacklem	*tattrflagp = 0;
2146191783Srmacklem	nmp = VFSTONFS(vnode_mount(fdvp));
2147191783Srmacklem	if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN)
2148191783Srmacklem		return (ENAMETOOLONG);
2149191783Srmacklemtryagain:
2150191783Srmacklem	if (NFSHASNFSV4(nmp) && ret == 0) {
2151191783Srmacklem		ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp,
2152191783Srmacklem		    &tdstateid, &gottd, p);
2153191783Srmacklem		if (gotfd && gottd) {
2154191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME2, fvp);
2155191783Srmacklem		} else if (gotfd) {
2156191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, fvp);
2157191783Srmacklem		} else if (gottd) {
2158191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, tvp);
2159191783Srmacklem		}
2160191783Srmacklem		if (gotfd) {
2161191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2162191783Srmacklem			*tl++ = fdstateid.seqid;
2163191783Srmacklem			*tl++ = fdstateid.other[0];
2164191783Srmacklem			*tl++ = fdstateid.other[1];
2165191783Srmacklem			*tl = fdstateid.other[2];
2166191783Srmacklem			if (gottd) {
2167191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2168191783Srmacklem				*tl = txdr_unsigned(NFSV4OP_PUTFH);
2169191783Srmacklem				np = VTONFS(tvp);
2170191783Srmacklem				(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2171191783Srmacklem				    np->n_fhp->nfh_len, 0);
2172191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2173191783Srmacklem				*tl = txdr_unsigned(NFSV4OP_DELEGRETURN);
2174191783Srmacklem			}
2175191783Srmacklem		}
2176191783Srmacklem		if (gottd) {
2177191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2178191783Srmacklem			*tl++ = tdstateid.seqid;
2179191783Srmacklem			*tl++ = tdstateid.other[0];
2180191783Srmacklem			*tl++ = tdstateid.other[1];
2181191783Srmacklem			*tl = tdstateid.other[2];
2182191783Srmacklem		}
2183191783Srmacklem		if (ret > 0) {
2184191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2185191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2186191783Srmacklem			np = VTONFS(fdvp);
2187191783Srmacklem			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2188191783Srmacklem			    np->n_fhp->nfh_len, 0);
2189191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2190191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_SAVEFH);
2191191783Srmacklem		}
2192191783Srmacklem	} else {
2193191783Srmacklem		ret = 0;
2194191783Srmacklem	}
2195191783Srmacklem	if (ret == 0)
2196191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp);
2197191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2198191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2199191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2200191783Srmacklem		NFSWCCATTR_ATTRBIT(&attrbits);
2201191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2202191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2203191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2204191783Srmacklem		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2205191783Srmacklem		    VTONFS(tdvp)->n_fhp->nfh_len, 0);
2206191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2207191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2208191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2209191783Srmacklem		nd->nd_flag |= ND_V4WCCATTR;
2210191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2211191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_RENAME);
2212191783Srmacklem	}
2213191783Srmacklem	(void) nfsm_strtom(nd, fnameptr, fnamelen);
2214191783Srmacklem	if (!(nd->nd_flag & ND_NFSV4))
2215191783Srmacklem		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2216191783Srmacklem			VTONFS(tdvp)->n_fhp->nfh_len, 0);
2217191783Srmacklem	(void) nfsm_strtom(nd, tnameptr, tnamelen);
2218191783Srmacklem	error = nfscl_request(nd, fdvp, p, cred, fstuff);
2219191783Srmacklem	if (error)
2220191783Srmacklem		return (error);
2221191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2222191783Srmacklem		/* For NFSv4, parse out any Delereturn replies. */
2223191783Srmacklem		if (ret > 0 && nd->nd_repstat != 0 &&
2224191783Srmacklem		    (nd->nd_flag & ND_NOMOREDATA)) {
2225191783Srmacklem			/*
2226191783Srmacklem			 * If the Delegreturn failed, try again without
2227191783Srmacklem			 * it. The server will Recall, as required.
2228191783Srmacklem			 */
2229191783Srmacklem			mbuf_freem(nd->nd_mrep);
2230191783Srmacklem			goto tryagain;
2231191783Srmacklem		}
2232191783Srmacklem		for (i = 0; i < (ret * 2); i++) {
2233191783Srmacklem			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2234191783Srmacklem			    ND_NFSV4) {
2235191783Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2236191783Srmacklem			    if (*(tl + 1)) {
2237191783Srmacklem				if (i == 0 && ret > 1) {
2238191783Srmacklem				    /*
2239191783Srmacklem				     * If the Delegreturn failed, try again
2240191783Srmacklem				     * without it. The server will Recall, as
2241191783Srmacklem				     * required.
2242191783Srmacklem				     * If ret > 1, the first iteration of this
2243191783Srmacklem				     * loop is the second DelegReturn result.
2244191783Srmacklem				     */
2245191783Srmacklem				    mbuf_freem(nd->nd_mrep);
2246191783Srmacklem				    goto tryagain;
2247191783Srmacklem				} else {
2248191783Srmacklem				    nd->nd_flag |= ND_NOMOREDATA;
2249191783Srmacklem				}
2250191783Srmacklem			    }
2251191783Srmacklem			}
2252191783Srmacklem		}
2253191783Srmacklem		/* Now, the first wcc attribute reply. */
2254191783Srmacklem		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2255191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2256191783Srmacklem			if (*(tl + 1))
2257191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
2258191783Srmacklem		}
2259191783Srmacklem		error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL,
2260191783Srmacklem		    fstuff);
2261191783Srmacklem		/* and the second wcc attribute reply. */
2262191783Srmacklem		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
2263191783Srmacklem		    !error) {
2264191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2265191783Srmacklem			if (*(tl + 1))
2266191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
2267191783Srmacklem		}
2268191783Srmacklem		if (!error)
2269191783Srmacklem			error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp,
2270191783Srmacklem			    NULL, tstuff);
2271191783Srmacklem	}
2272191783Srmacklem	if (nd->nd_repstat && !error)
2273191783Srmacklem		error = nd->nd_repstat;
2274191783Srmacklemnfsmout:
2275191783Srmacklem	mbuf_freem(nd->nd_mrep);
2276191783Srmacklem	return (error);
2277191783Srmacklem}
2278191783Srmacklem
2279191783Srmacklem/*
2280191783Srmacklem * nfs hard link create rpc
2281191783Srmacklem */
2282191783SrmacklemAPPLESTATIC int
2283191783Srmacklemnfsrpc_link(vnode_t dvp, vnode_t vp, char *name, int namelen,
2284191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2285191783Srmacklem    struct nfsvattr *nap, int *attrflagp, int *dattrflagp, void *dstuff)
2286191783Srmacklem{
2287191783Srmacklem	u_int32_t *tl;
2288191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2289191783Srmacklem	nfsattrbit_t attrbits;
2290191783Srmacklem	int error = 0;
2291191783Srmacklem
2292191783Srmacklem	*attrflagp = 0;
2293191783Srmacklem	*dattrflagp = 0;
2294191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2295191783Srmacklem		return (ENAMETOOLONG);
2296191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_LINK, vp);
2297191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2298191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2299191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2300191783Srmacklem	}
2301191783Srmacklem	(void) nfsm_fhtom(nd, VTONFS(dvp)->n_fhp->nfh_fh,
2302191783Srmacklem		VTONFS(dvp)->n_fhp->nfh_len, 0);
2303191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2304191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2305191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2306191783Srmacklem		NFSWCCATTR_ATTRBIT(&attrbits);
2307191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2308191783Srmacklem		nd->nd_flag |= ND_V4WCCATTR;
2309191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2310191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_LINK);
2311191783Srmacklem	}
2312191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2313191783Srmacklem	error = nfscl_request(nd, vp, p, cred, dstuff);
2314191783Srmacklem	if (error)
2315191783Srmacklem		return (error);
2316191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
2317191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, dstuff);
2318191783Srmacklem		if (!error)
2319191783Srmacklem			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2320191783Srmacklem			    NULL, dstuff);
2321191783Srmacklem	} else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2322191783Srmacklem		/*
2323191783Srmacklem		 * First, parse out the PutFH and Getattr result.
2324191783Srmacklem		 */
2325191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2326191783Srmacklem		if (!(*(tl + 1)))
2327191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2328191783Srmacklem		if (*(tl + 1))
2329191783Srmacklem			nd->nd_flag |= ND_NOMOREDATA;
2330191783Srmacklem		/*
2331191783Srmacklem		 * Get the pre-op attributes.
2332191783Srmacklem		 */
2333191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2334191783Srmacklem	}
2335191783Srmacklem	if (nd->nd_repstat && !error)
2336191783Srmacklem		error = nd->nd_repstat;
2337191783Srmacklemnfsmout:
2338191783Srmacklem	mbuf_freem(nd->nd_mrep);
2339191783Srmacklem	return (error);
2340191783Srmacklem}
2341191783Srmacklem
2342191783Srmacklem/*
2343191783Srmacklem * nfs symbolic link create rpc
2344191783Srmacklem */
2345191783SrmacklemAPPLESTATIC int
2346191783Srmacklemnfsrpc_symlink(vnode_t dvp, char *name, int namelen, char *target,
2347191783Srmacklem    struct vattr *vap, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2348191783Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2349191783Srmacklem    int *dattrflagp, void *dstuff)
2350191783Srmacklem{
2351191783Srmacklem	u_int32_t *tl;
2352191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2353191783Srmacklem	struct nfsmount *nmp;
2354191783Srmacklem	int slen, error = 0;
2355191783Srmacklem
2356191783Srmacklem	*nfhpp = NULL;
2357191783Srmacklem	*attrflagp = 0;
2358191783Srmacklem	*dattrflagp = 0;
2359191783Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
2360191783Srmacklem	slen = strlen(target);
2361191783Srmacklem	if (slen > NFS_MAXPATHLEN || namelen > NFS_MAXNAMLEN)
2362191783Srmacklem		return (ENAMETOOLONG);
2363191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_SYMLINK, dvp);
2364191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2365191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2366191783Srmacklem		*tl = txdr_unsigned(NFLNK);
2367191783Srmacklem		(void) nfsm_strtom(nd, target, slen);
2368191783Srmacklem	}
2369191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2370191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2371191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, 0, 0);
2372191783Srmacklem	if (!(nd->nd_flag & ND_NFSV4))
2373191783Srmacklem		(void) nfsm_strtom(nd, target, slen);
2374191783Srmacklem	if (nd->nd_flag & ND_NFSV2)
2375191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2376191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2377191783Srmacklem	if (error)
2378191783Srmacklem		return (error);
2379191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
2380191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2381191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) && !error) {
2382191783Srmacklem		if (!nd->nd_repstat)
2383191783Srmacklem			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2384191783Srmacklem		if (!error)
2385191783Srmacklem			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2386191783Srmacklem			    NULL, dstuff);
2387191783Srmacklem	}
2388191783Srmacklem	if (nd->nd_repstat && !error)
2389191783Srmacklem		error = nd->nd_repstat;
2390191783Srmacklem	mbuf_freem(nd->nd_mrep);
2391191783Srmacklem	/*
2392191783Srmacklem	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2393191783Srmacklem	 */
2394191783Srmacklem	if (error == EEXIST)
2395191783Srmacklem		error = 0;
2396191783Srmacklem	return (error);
2397191783Srmacklem}
2398191783Srmacklem
2399191783Srmacklem/*
2400191783Srmacklem * nfs make dir rpc
2401191783Srmacklem */
2402191783SrmacklemAPPLESTATIC int
2403191783Srmacklemnfsrpc_mkdir(vnode_t dvp, char *name, int namelen, struct vattr *vap,
2404191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2405191783Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2406191783Srmacklem    int *dattrflagp, void *dstuff)
2407191783Srmacklem{
2408191783Srmacklem	u_int32_t *tl;
2409191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2410191783Srmacklem	nfsattrbit_t attrbits;
2411191783Srmacklem	int error = 0;
2412265389Srmacklem	struct nfsfh *fhp;
2413191783Srmacklem
2414191783Srmacklem	*nfhpp = NULL;
2415191783Srmacklem	*attrflagp = 0;
2416191783Srmacklem	*dattrflagp = 0;
2417265389Srmacklem	fhp = VTONFS(dvp)->n_fhp;
2418191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2419191783Srmacklem		return (ENAMETOOLONG);
2420191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_MKDIR, dvp);
2421191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2422191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2423191783Srmacklem		*tl = txdr_unsigned(NFDIR);
2424191783Srmacklem	}
2425191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2426191783Srmacklem	nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2427191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2428191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
2429191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2430191783Srmacklem		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2431191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2432191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2433265389Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2434265389Srmacklem		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2435265389Srmacklem		(void) nfsm_fhtom(nd, fhp->nfh_fh, fhp->nfh_len, 0);
2436265389Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2437265389Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2438265389Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2439191783Srmacklem	}
2440191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2441191783Srmacklem	if (error)
2442191783Srmacklem		return (error);
2443191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
2444191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2445191783Srmacklem	if (!nd->nd_repstat && !error) {
2446191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
2447191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2448191783Srmacklem			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2449191783Srmacklem		}
2450191783Srmacklem		if (!error)
2451191783Srmacklem			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2452265389Srmacklem		if (error == 0 && (nd->nd_flag & ND_NFSV4) != 0) {
2453265389Srmacklem			/* Get rid of the PutFH and Getattr status values. */
2454265389Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
2455265389Srmacklem			/* Load the directory attributes. */
2456265389Srmacklem			error = nfsm_loadattr(nd, dnap);
2457265389Srmacklem			if (error == 0)
2458265389Srmacklem				*dattrflagp = 1;
2459265389Srmacklem		}
2460191783Srmacklem	}
2461191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) && !error)
2462191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2463191783Srmacklem	if (nd->nd_repstat && !error)
2464191783Srmacklem		error = nd->nd_repstat;
2465191783Srmacklemnfsmout:
2466191783Srmacklem	mbuf_freem(nd->nd_mrep);
2467191783Srmacklem	/*
2468191783Srmacklem	 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry.
2469191783Srmacklem	 */
2470191783Srmacklem	if (error == EEXIST)
2471191783Srmacklem		error = 0;
2472191783Srmacklem	return (error);
2473191783Srmacklem}
2474191783Srmacklem
2475191783Srmacklem/*
2476191783Srmacklem * nfs remove directory call
2477191783Srmacklem */
2478191783SrmacklemAPPLESTATIC int
2479191783Srmacklemnfsrpc_rmdir(vnode_t dvp, char *name, int namelen, struct ucred *cred,
2480191783Srmacklem    NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp, void *dstuff)
2481191783Srmacklem{
2482191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2483191783Srmacklem	int error = 0;
2484191783Srmacklem
2485191783Srmacklem	*dattrflagp = 0;
2486191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2487191783Srmacklem		return (ENAMETOOLONG);
2488191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_RMDIR, dvp);
2489191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2490191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2491191783Srmacklem	if (error)
2492191783Srmacklem		return (error);
2493191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2494191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2495191783Srmacklem	if (nd->nd_repstat && !error)
2496191783Srmacklem		error = nd->nd_repstat;
2497191783Srmacklem	mbuf_freem(nd->nd_mrep);
2498191783Srmacklem	/*
2499191783Srmacklem	 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2500191783Srmacklem	 */
2501191783Srmacklem	if (error == ENOENT)
2502191783Srmacklem		error = 0;
2503191783Srmacklem	return (error);
2504191783Srmacklem}
2505191783Srmacklem
2506191783Srmacklem/*
2507191783Srmacklem * Readdir rpc.
2508191783Srmacklem * Always returns with either uio_resid unchanged, if you are at the
2509191783Srmacklem * end of the directory, or uio_resid == 0, with all DIRBLKSIZ chunks
2510191783Srmacklem * filled in.
2511191783Srmacklem * I felt this would allow caching of directory blocks more easily
2512191783Srmacklem * than returning a pertially filled block.
2513191783Srmacklem * Directory offset cookies:
2514191783Srmacklem * Oh my, what to do with them...
2515191783Srmacklem * I can think of three ways to deal with them:
2516191783Srmacklem * 1 - have the layer above these RPCs maintain a map between logical
2517191783Srmacklem *     directory byte offsets and the NFS directory offset cookies
2518191783Srmacklem * 2 - pass the opaque directory offset cookies up into userland
2519191783Srmacklem *     and let the libc functions deal with them, via the system call
2520191783Srmacklem * 3 - return them to userland in the "struct dirent", so future versions
2521191783Srmacklem *     of libc can use them and do whatever is necessary to amke things work
2522191783Srmacklem *     above these rpc calls, in the meantime
2523191783Srmacklem * For now, I do #3 by "hiding" the directory offset cookies after the
2524191783Srmacklem * d_name field in struct dirent. This is space inside d_reclen that
2525191783Srmacklem * will be ignored by anything that doesn't know about them.
2526191783Srmacklem * The directory offset cookies are filled in as the last 8 bytes of
2527191783Srmacklem * each directory entry, after d_name. Someday, the userland libc
2528191783Srmacklem * functions may be able to use these. In the meantime, it satisfies
2529191783Srmacklem * OpenBSD's requirements for cookies being returned.
2530191783Srmacklem * If expects the directory offset cookie for the read to be in uio_offset
2531191783Srmacklem * and returns the one for the next entry after this directory block in
2532191783Srmacklem * there, as well.
2533191783Srmacklem */
2534191783SrmacklemAPPLESTATIC int
2535191783Srmacklemnfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2536191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2537191783Srmacklem    int *eofp, void *stuff)
2538191783Srmacklem{
2539191783Srmacklem	int len, left;
2540191783Srmacklem	struct dirent *dp = NULL;
2541191783Srmacklem	u_int32_t *tl;
2542191783Srmacklem	nfsquad_t cookie, ncookie;
2543191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2544191783Srmacklem	struct nfsnode *dnp = VTONFS(vp);
2545191783Srmacklem	struct nfsvattr nfsva;
2546191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2547191783Srmacklem	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2548191783Srmacklem	int reqsize, tryformoredirs = 1, readsize, eof = 0, gotmnton = 0;
2549191783Srmacklem	long dotfileid, dotdotfileid = 0;
2550191783Srmacklem	u_int32_t fakefileno = 0xffffffff, rderr;
2551191783Srmacklem	char *cp;
2552191783Srmacklem	nfsattrbit_t attrbits, dattrbits;
2553191783Srmacklem	u_int32_t *tl2 = NULL;
2554191783Srmacklem	size_t tresid;
2555191783Srmacklem
2556209120Skib	KASSERT(uiop->uio_iovcnt == 1 &&
2557209120Skib	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
2558209120Skib	    ("nfs readdirrpc bad uio"));
2559191783Srmacklem
2560191783Srmacklem	/*
2561191783Srmacklem	 * There is no point in reading a lot more than uio_resid, however
2562191783Srmacklem	 * adding one additional DIRBLKSIZ makes sense. Since uio_resid
2563191783Srmacklem	 * and nm_readdirsize are both exact multiples of DIRBLKSIZ, this
2564191783Srmacklem	 * will never make readsize > nm_readdirsize.
2565191783Srmacklem	 */
2566191783Srmacklem	readsize = nmp->nm_readdirsize;
2567191783Srmacklem	if (readsize > uio_uio_resid(uiop))
2568191783Srmacklem		readsize = uio_uio_resid(uiop) + DIRBLKSIZ;
2569191783Srmacklem
2570191783Srmacklem	*attrflagp = 0;
2571191783Srmacklem	if (eofp)
2572191783Srmacklem		*eofp = 0;
2573191783Srmacklem	tresid = uio_uio_resid(uiop);
2574191783Srmacklem	cookie.lval[0] = cookiep->nfsuquad[0];
2575191783Srmacklem	cookie.lval[1] = cookiep->nfsuquad[1];
2576191783Srmacklem	nd->nd_mrep = NULL;
2577191783Srmacklem
2578191783Srmacklem	/*
2579191783Srmacklem	 * For NFSv4, first create the "." and ".." entries.
2580191783Srmacklem	 */
2581191783Srmacklem	if (NFSHASNFSV4(nmp)) {
2582191783Srmacklem		reqsize = 6 * NFSX_UNSIGNED;
2583191783Srmacklem		NFSGETATTR_ATTRBIT(&dattrbits);
2584191783Srmacklem		NFSZERO_ATTRBIT(&attrbits);
2585191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
2586191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TYPE);
2587191783Srmacklem		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
2588191783Srmacklem		    NFSATTRBIT_MOUNTEDONFILEID)) {
2589191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits,
2590191783Srmacklem			    NFSATTRBIT_MOUNTEDONFILEID);
2591191783Srmacklem			gotmnton = 1;
2592191783Srmacklem		} else {
2593191783Srmacklem			/*
2594191783Srmacklem			 * Must fake it. Use the fileno, except when the
2595191783Srmacklem			 * fsid is != to that of the directory. For that
2596191783Srmacklem			 * case, generate a fake fileno that is not the same.
2597191783Srmacklem			 */
2598191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
2599191783Srmacklem			gotmnton = 0;
2600191783Srmacklem		}
2601191783Srmacklem
2602191783Srmacklem		/*
2603191783Srmacklem		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
2604191783Srmacklem		 */
2605191783Srmacklem		if (uiop->uio_offset == 0) {
2606191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
2607191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2608191783Srmacklem			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2609191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
2610191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
2611191783Srmacklem			error = nfscl_request(nd, vp, p, cred, stuff);
2612191783Srmacklem			if (error)
2613191783Srmacklem			    return (error);
2614265339Srmacklem			dotfileid = 0;	/* Fake out the compiler. */
2615265339Srmacklem			if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
2616265339Srmacklem			    error = nfsm_loadattr(nd, &nfsva);
2617265339Srmacklem			    if (error != 0)
2618265339Srmacklem				goto nfsmout;
2619265339Srmacklem			    dotfileid = nfsva.na_fileid;
2620265339Srmacklem			}
2621191783Srmacklem			if (nd->nd_repstat == 0) {
2622265339Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2623265339Srmacklem			    len = fxdr_unsigned(int, *(tl + 4));
2624191783Srmacklem			    if (len > 0 && len <= NFSX_V4FHMAX)
2625191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2626191783Srmacklem			    else
2627191783Srmacklem				error = EPERM;
2628191783Srmacklem			    if (!error) {
2629191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2630191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
2631191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2632191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2633191783Srmacklem				    NULL, NULL, NULL, p, cred);
2634191783Srmacklem				if (error) {
2635191783Srmacklem				    dotdotfileid = dotfileid;
2636191783Srmacklem				} else if (gotmnton) {
2637191783Srmacklem				    if (nfsva.na_mntonfileno != 0xffffffff)
2638191783Srmacklem					dotdotfileid = nfsva.na_mntonfileno;
2639191783Srmacklem				    else
2640191783Srmacklem					dotdotfileid = nfsva.na_fileid;
2641191783Srmacklem				} else if (nfsva.na_filesid[0] ==
2642191783Srmacklem				    dnp->n_vattr.na_filesid[0] &&
2643191783Srmacklem				    nfsva.na_filesid[1] ==
2644191783Srmacklem				    dnp->n_vattr.na_filesid[1]) {
2645191783Srmacklem				    dotdotfileid = nfsva.na_fileid;
2646191783Srmacklem				} else {
2647191783Srmacklem				    do {
2648191783Srmacklem					fakefileno--;
2649191783Srmacklem				    } while (fakefileno ==
2650191783Srmacklem					nfsva.na_fileid);
2651191783Srmacklem				    dotdotfileid = fakefileno;
2652191783Srmacklem				}
2653191783Srmacklem			    }
2654191783Srmacklem			} else if (nd->nd_repstat == NFSERR_NOENT) {
2655191783Srmacklem			    /*
2656191783Srmacklem			     * Lookupp returns NFSERR_NOENT when we are
2657191783Srmacklem			     * at the root, so just use the current dir.
2658191783Srmacklem			     */
2659191783Srmacklem			    nd->nd_repstat = 0;
2660191783Srmacklem			    dotdotfileid = dotfileid;
2661191783Srmacklem			} else {
2662191783Srmacklem			    error = nd->nd_repstat;
2663191783Srmacklem			}
2664191783Srmacklem			mbuf_freem(nd->nd_mrep);
2665191783Srmacklem			if (error)
2666191783Srmacklem			    return (error);
2667191783Srmacklem			nd->nd_mrep = NULL;
2668191783Srmacklem			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2669191783Srmacklem			dp->d_type = DT_DIR;
2670191783Srmacklem			dp->d_fileno = dotfileid;
2671191783Srmacklem			dp->d_namlen = 1;
2672191783Srmacklem			dp->d_name[0] = '.';
2673191783Srmacklem			dp->d_name[1] = '\0';
2674191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2675191783Srmacklem			/*
2676191783Srmacklem			 * Just make these offset cookie 0.
2677191783Srmacklem			 */
2678191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
2679191783Srmacklem			*tl++ = 0;
2680191783Srmacklem			*tl = 0;
2681191783Srmacklem			blksiz += dp->d_reclen;
2682191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
2683191783Srmacklem			uiop->uio_offset += dp->d_reclen;
2684191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
2685191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
2686191783Srmacklem			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2687191783Srmacklem			dp->d_type = DT_DIR;
2688191783Srmacklem			dp->d_fileno = dotdotfileid;
2689191783Srmacklem			dp->d_namlen = 2;
2690191783Srmacklem			dp->d_name[0] = '.';
2691191783Srmacklem			dp->d_name[1] = '.';
2692191783Srmacklem			dp->d_name[2] = '\0';
2693191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2694191783Srmacklem			/*
2695191783Srmacklem			 * Just make these offset cookie 0.
2696191783Srmacklem			 */
2697191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
2698191783Srmacklem			*tl++ = 0;
2699191783Srmacklem			*tl = 0;
2700191783Srmacklem			blksiz += dp->d_reclen;
2701191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
2702191783Srmacklem			uiop->uio_offset += dp->d_reclen;
2703191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
2704191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
2705191783Srmacklem		}
2706191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_RDATTRERROR);
2707191783Srmacklem	} else {
2708191783Srmacklem		reqsize = 5 * NFSX_UNSIGNED;
2709191783Srmacklem	}
2710191783Srmacklem
2711191783Srmacklem
2712191783Srmacklem	/*
2713191783Srmacklem	 * Loop around doing readdir rpc's of size readsize.
2714191783Srmacklem	 * The stopping criteria is EOF or buffer full.
2715191783Srmacklem	 */
2716191783Srmacklem	while (more_dirs && bigenough) {
2717191783Srmacklem		*attrflagp = 0;
2718191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_READDIR, vp);
2719191783Srmacklem		if (nd->nd_flag & ND_NFSV2) {
2720191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2721191783Srmacklem			*tl++ = cookie.lval[1];
2722191783Srmacklem			*tl = txdr_unsigned(readsize);
2723191783Srmacklem		} else {
2724191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, reqsize);
2725191783Srmacklem			*tl++ = cookie.lval[0];
2726191783Srmacklem			*tl++ = cookie.lval[1];
2727191783Srmacklem			if (cookie.qval == 0) {
2728191783Srmacklem				*tl++ = 0;
2729191783Srmacklem				*tl++ = 0;
2730191783Srmacklem			} else {
2731191783Srmacklem				NFSLOCKNODE(dnp);
2732191783Srmacklem				*tl++ = dnp->n_cookieverf.nfsuquad[0];
2733191783Srmacklem				*tl++ = dnp->n_cookieverf.nfsuquad[1];
2734191783Srmacklem				NFSUNLOCKNODE(dnp);
2735191783Srmacklem			}
2736191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
2737191783Srmacklem				*tl++ = txdr_unsigned(readsize);
2738191783Srmacklem				*tl = txdr_unsigned(readsize);
2739191783Srmacklem				(void) nfsrv_putattrbit(nd, &attrbits);
2740191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2741191783Srmacklem				*tl = txdr_unsigned(NFSV4OP_GETATTR);
2742191783Srmacklem				(void) nfsrv_putattrbit(nd, &dattrbits);
2743191783Srmacklem			} else {
2744191783Srmacklem				*tl = txdr_unsigned(readsize);
2745191783Srmacklem			}
2746191783Srmacklem		}
2747191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
2748191783Srmacklem		if (error)
2749191783Srmacklem			return (error);
2750191783Srmacklem		if (!(nd->nd_flag & ND_NFSV2)) {
2751191783Srmacklem			if (nd->nd_flag & ND_NFSV3)
2752191783Srmacklem				error = nfscl_postop_attr(nd, nap, attrflagp,
2753191783Srmacklem				    stuff);
2754191783Srmacklem			if (!nd->nd_repstat && !error) {
2755191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
2756191783Srmacklem				NFSLOCKNODE(dnp);
2757191783Srmacklem				dnp->n_cookieverf.nfsuquad[0] = *tl++;
2758191783Srmacklem				dnp->n_cookieverf.nfsuquad[1] = *tl;
2759191783Srmacklem				NFSUNLOCKNODE(dnp);
2760191783Srmacklem			}
2761191783Srmacklem		}
2762191783Srmacklem		if (nd->nd_repstat || error) {
2763191783Srmacklem			if (!error)
2764191783Srmacklem				error = nd->nd_repstat;
2765191783Srmacklem			goto nfsmout;
2766191783Srmacklem		}
2767191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2768191783Srmacklem		more_dirs = fxdr_unsigned(int, *tl);
2769191783Srmacklem		if (!more_dirs)
2770191783Srmacklem			tryformoredirs = 0;
2771191783Srmacklem
2772191783Srmacklem		/* loop thru the dir entries, doctoring them to 4bsd form */
2773191783Srmacklem		while (more_dirs && bigenough) {
2774191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
2775191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2776191783Srmacklem				ncookie.lval[0] = *tl++;
2777191783Srmacklem				ncookie.lval[1] = *tl++;
2778191783Srmacklem				len = fxdr_unsigned(int, *tl);
2779191783Srmacklem			} else if (nd->nd_flag & ND_NFSV3) {
2780191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2781220152Szack				nfsva.na_fileid = fxdr_hyper(tl);
2782220152Szack				tl += 2;
2783220152Szack				len = fxdr_unsigned(int, *tl);
2784191783Srmacklem			} else {
2785191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2786191783Srmacklem				nfsva.na_fileid =
2787191783Srmacklem				    fxdr_unsigned(long, *tl++);
2788191783Srmacklem				len = fxdr_unsigned(int, *tl);
2789191783Srmacklem			}
2790191783Srmacklem			if (len <= 0 || len > NFS_MAXNAMLEN) {
2791191783Srmacklem				error = EBADRPC;
2792191783Srmacklem				goto nfsmout;
2793191783Srmacklem			}
2794191783Srmacklem			tlen = NFSM_RNDUP(len);
2795191783Srmacklem			if (tlen == len)
2796191783Srmacklem				tlen += 4;  /* To ensure null termination */
2797191783Srmacklem			left = DIRBLKSIZ - blksiz;
2798191783Srmacklem			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > left) {
2799191783Srmacklem				dp->d_reclen += left;
2800191783Srmacklem				uio_iov_base_add(uiop, left);
2801191783Srmacklem				uio_iov_len_add(uiop, -(left));
2802191783Srmacklem				uio_uio_resid_add(uiop, -(left));
2803191783Srmacklem				uiop->uio_offset += left;
2804191783Srmacklem				blksiz = 0;
2805191783Srmacklem			}
2806191783Srmacklem			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
2807191783Srmacklem				bigenough = 0;
2808191783Srmacklem			if (bigenough) {
2809191783Srmacklem				dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2810191783Srmacklem				dp->d_namlen = len;
2811191783Srmacklem				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
2812191783Srmacklem				dp->d_type = DT_UNKNOWN;
2813191783Srmacklem				blksiz += dp->d_reclen;
2814191783Srmacklem				if (blksiz == DIRBLKSIZ)
2815191783Srmacklem					blksiz = 0;
2816191783Srmacklem				uio_uio_resid_add(uiop, -(DIRHDSIZ));
2817191783Srmacklem				uiop->uio_offset += DIRHDSIZ;
2818191783Srmacklem				uio_iov_base_add(uiop, DIRHDSIZ);
2819191783Srmacklem				uio_iov_len_add(uiop, -(DIRHDSIZ));
2820191783Srmacklem				error = nfsm_mbufuio(nd, uiop, len);
2821191783Srmacklem				if (error)
2822191783Srmacklem					goto nfsmout;
2823191783Srmacklem				cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
2824191783Srmacklem				tlen -= len;
2825191783Srmacklem				*cp = '\0';	/* null terminate */
2826191783Srmacklem				cp += tlen;	/* points to cookie storage */
2827191783Srmacklem				tl2 = (u_int32_t *)cp;
2828191783Srmacklem				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
2829191783Srmacklem				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
2830191783Srmacklem				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
2831191783Srmacklem				uiop->uio_offset += (tlen + NFSX_HYPER);
2832191783Srmacklem			} else {
2833191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2834191783Srmacklem				if (error)
2835191783Srmacklem					goto nfsmout;
2836191783Srmacklem			}
2837191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
2838191783Srmacklem				rderr = 0;
2839191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
2840191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2841191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2842191783Srmacklem				    NULL, NULL, &rderr, p, cred);
2843191783Srmacklem				if (error)
2844191783Srmacklem					goto nfsmout;
2845191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2846191783Srmacklem			} else if (nd->nd_flag & ND_NFSV3) {
2847191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
2848191783Srmacklem				ncookie.lval[0] = *tl++;
2849191783Srmacklem				ncookie.lval[1] = *tl++;
2850191783Srmacklem			} else {
2851191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2852191783Srmacklem				ncookie.lval[0] = 0;
2853191783Srmacklem				ncookie.lval[1] = *tl++;
2854191783Srmacklem			}
2855191783Srmacklem			if (bigenough) {
2856191783Srmacklem			    if (nd->nd_flag & ND_NFSV4) {
2857191783Srmacklem				if (rderr) {
2858191783Srmacklem				    dp->d_fileno = 0;
2859191783Srmacklem				} else {
2860191783Srmacklem				    if (gotmnton) {
2861191783Srmacklem					if (nfsva.na_mntonfileno != 0xffffffff)
2862191783Srmacklem					    dp->d_fileno = nfsva.na_mntonfileno;
2863191783Srmacklem					else
2864191783Srmacklem					    dp->d_fileno = nfsva.na_fileid;
2865191783Srmacklem				    } else if (nfsva.na_filesid[0] ==
2866191783Srmacklem					dnp->n_vattr.na_filesid[0] &&
2867191783Srmacklem					nfsva.na_filesid[1] ==
2868191783Srmacklem					dnp->n_vattr.na_filesid[1]) {
2869191783Srmacklem					dp->d_fileno = nfsva.na_fileid;
2870191783Srmacklem				    } else {
2871191783Srmacklem					do {
2872191783Srmacklem					    fakefileno--;
2873191783Srmacklem					} while (fakefileno ==
2874191783Srmacklem					    nfsva.na_fileid);
2875191783Srmacklem					dp->d_fileno = fakefileno;
2876191783Srmacklem				    }
2877191783Srmacklem				    dp->d_type = vtonfs_dtype(nfsva.na_type);
2878191783Srmacklem				}
2879191783Srmacklem			    } else {
2880191783Srmacklem				dp->d_fileno = nfsva.na_fileid;
2881191783Srmacklem			    }
2882191783Srmacklem			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
2883191783Srmacklem				ncookie.lval[0];
2884191783Srmacklem			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
2885191783Srmacklem				ncookie.lval[1];
2886191783Srmacklem			}
2887191783Srmacklem			more_dirs = fxdr_unsigned(int, *tl);
2888191783Srmacklem		}
2889191783Srmacklem		/*
2890191783Srmacklem		 * If at end of rpc data, get the eof boolean
2891191783Srmacklem		 */
2892191783Srmacklem		if (!more_dirs) {
2893191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2894191783Srmacklem			eof = fxdr_unsigned(int, *tl);
2895191783Srmacklem			if (tryformoredirs)
2896191783Srmacklem				more_dirs = !eof;
2897191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
2898191783Srmacklem				error = nfscl_postop_attr(nd, nap, attrflagp,
2899191783Srmacklem				    stuff);
2900191783Srmacklem				if (error)
2901191783Srmacklem					goto nfsmout;
2902191783Srmacklem			}
2903191783Srmacklem		}
2904191783Srmacklem		mbuf_freem(nd->nd_mrep);
2905191783Srmacklem		nd->nd_mrep = NULL;
2906191783Srmacklem	}
2907191783Srmacklem	/*
2908191783Srmacklem	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
2909191783Srmacklem	 * by increasing d_reclen for the last record.
2910191783Srmacklem	 */
2911191783Srmacklem	if (blksiz > 0) {
2912191783Srmacklem		left = DIRBLKSIZ - blksiz;
2913191783Srmacklem		dp->d_reclen += left;
2914191783Srmacklem		uio_iov_base_add(uiop, left);
2915191783Srmacklem		uio_iov_len_add(uiop, -(left));
2916191783Srmacklem		uio_uio_resid_add(uiop, -(left));
2917191783Srmacklem		uiop->uio_offset += left;
2918191783Srmacklem	}
2919191783Srmacklem
2920191783Srmacklem	/*
2921191783Srmacklem	 * If returning no data, assume end of file.
2922191783Srmacklem	 * If not bigenough, return not end of file, since you aren't
2923191783Srmacklem	 *    returning all the data
2924191783Srmacklem	 * Otherwise, return the eof flag from the server.
2925191783Srmacklem	 */
2926191783Srmacklem	if (eofp) {
2927191783Srmacklem		if (tresid == ((size_t)(uio_uio_resid(uiop))))
2928191783Srmacklem			*eofp = 1;
2929191783Srmacklem		else if (!bigenough)
2930191783Srmacklem			*eofp = 0;
2931191783Srmacklem		else
2932191783Srmacklem			*eofp = eof;
2933191783Srmacklem	}
2934191783Srmacklem
2935191783Srmacklem	/*
2936191783Srmacklem	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
2937191783Srmacklem	 */
2938191783Srmacklem	while (uio_uio_resid(uiop) > 0 && ((size_t)(uio_uio_resid(uiop))) != tresid) {
2939191783Srmacklem		dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2940191783Srmacklem		dp->d_type = DT_UNKNOWN;
2941191783Srmacklem		dp->d_fileno = 0;
2942191783Srmacklem		dp->d_namlen = 0;
2943191783Srmacklem		dp->d_name[0] = '\0';
2944191783Srmacklem		tl = (u_int32_t *)&dp->d_name[4];
2945191783Srmacklem		*tl++ = cookie.lval[0];
2946191783Srmacklem		*tl = cookie.lval[1];
2947191783Srmacklem		dp->d_reclen = DIRBLKSIZ;
2948191783Srmacklem		uio_iov_base_add(uiop, DIRBLKSIZ);
2949191783Srmacklem		uio_iov_len_add(uiop, -(DIRBLKSIZ));
2950191783Srmacklem		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
2951191783Srmacklem		uiop->uio_offset += DIRBLKSIZ;
2952191783Srmacklem	}
2953191783Srmacklem
2954191783Srmacklemnfsmout:
2955191783Srmacklem	if (nd->nd_mrep != NULL)
2956191783Srmacklem		mbuf_freem(nd->nd_mrep);
2957191783Srmacklem	return (error);
2958191783Srmacklem}
2959191783Srmacklem
2960191783Srmacklem#ifndef APPLE
2961191783Srmacklem/*
2962191783Srmacklem * NFS V3 readdir plus RPC. Used in place of nfsrpc_readdir().
2963191783Srmacklem * (Also used for NFS V4 when mount flag set.)
2964191783Srmacklem * (ditto above w.r.t. multiple of DIRBLKSIZ, etc.)
2965191783Srmacklem */
2966191783SrmacklemAPPLESTATIC int
2967191783Srmacklemnfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2968191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2969191783Srmacklem    int *eofp, void *stuff)
2970191783Srmacklem{
2971191783Srmacklem	int len, left;
2972191783Srmacklem	struct dirent *dp = NULL;
2973191783Srmacklem	u_int32_t *tl;
2974191783Srmacklem	vnode_t newvp = NULLVP;
2975191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2976191783Srmacklem	struct nameidata nami, *ndp = &nami;
2977191783Srmacklem	struct componentname *cnp = &ndp->ni_cnd;
2978191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2979191783Srmacklem	struct nfsnode *dnp = VTONFS(vp), *np;
2980191783Srmacklem	struct nfsvattr nfsva;
2981191783Srmacklem	struct nfsfh *nfhp;
2982191783Srmacklem	nfsquad_t cookie, ncookie;
2983191783Srmacklem	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2984191783Srmacklem	int attrflag, tryformoredirs = 1, eof = 0, gotmnton = 0;
2985220735Srmacklem	int isdotdot = 0, unlocknewvp = 0;
2986191783Srmacklem	long dotfileid, dotdotfileid = 0, fileno = 0;
2987191783Srmacklem	char *cp;
2988191783Srmacklem	nfsattrbit_t attrbits, dattrbits;
2989191783Srmacklem	size_t tresid;
2990191783Srmacklem	u_int32_t *tl2 = NULL, fakefileno = 0xffffffff, rderr;
2991233285Sjhb	struct timespec dctime;
2992191783Srmacklem
2993209120Skib	KASSERT(uiop->uio_iovcnt == 1 &&
2994209120Skib	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
2995209120Skib	    ("nfs readdirplusrpc bad uio"));
2996233285Sjhb	timespecclear(&dctime);
2997191783Srmacklem	*attrflagp = 0;
2998191783Srmacklem	if (eofp != NULL)
2999191783Srmacklem		*eofp = 0;
3000191783Srmacklem	ndp->ni_dvp = vp;
3001191783Srmacklem	nd->nd_mrep = NULL;
3002191783Srmacklem	cookie.lval[0] = cookiep->nfsuquad[0];
3003191783Srmacklem	cookie.lval[1] = cookiep->nfsuquad[1];
3004191783Srmacklem	tresid = uio_uio_resid(uiop);
3005191783Srmacklem
3006191783Srmacklem	/*
3007191783Srmacklem	 * For NFSv4, first create the "." and ".." entries.
3008191783Srmacklem	 */
3009191783Srmacklem	if (NFSHASNFSV4(nmp)) {
3010191783Srmacklem		NFSGETATTR_ATTRBIT(&dattrbits);
3011191783Srmacklem		NFSZERO_ATTRBIT(&attrbits);
3012191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
3013191783Srmacklem		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
3014191783Srmacklem		    NFSATTRBIT_MOUNTEDONFILEID)) {
3015191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits,
3016191783Srmacklem			    NFSATTRBIT_MOUNTEDONFILEID);
3017191783Srmacklem			gotmnton = 1;
3018191783Srmacklem		} else {
3019191783Srmacklem			/*
3020191783Srmacklem			 * Must fake it. Use the fileno, except when the
3021191783Srmacklem			 * fsid is != to that of the directory. For that
3022191783Srmacklem			 * case, generate a fake fileno that is not the same.
3023191783Srmacklem			 */
3024191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
3025191783Srmacklem			gotmnton = 0;
3026191783Srmacklem		}
3027191783Srmacklem
3028191783Srmacklem		/*
3029191783Srmacklem		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
3030191783Srmacklem		 */
3031191783Srmacklem		if (uiop->uio_offset == 0) {
3032191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
3033191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3034191783Srmacklem			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
3035191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3036191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
3037191783Srmacklem			error = nfscl_request(nd, vp, p, cred, stuff);
3038191783Srmacklem			if (error)
3039191783Srmacklem			    return (error);
3040265339Srmacklem			dotfileid = 0;	/* Fake out the compiler. */
3041265339Srmacklem			if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
3042265339Srmacklem			    error = nfsm_loadattr(nd, &nfsva);
3043265339Srmacklem			    if (error != 0)
3044265339Srmacklem				goto nfsmout;
3045265339Srmacklem			    dctime = nfsva.na_ctime;
3046265339Srmacklem			    dotfileid = nfsva.na_fileid;
3047265339Srmacklem			}
3048191783Srmacklem			if (nd->nd_repstat == 0) {
3049265339Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
3050265339Srmacklem			    len = fxdr_unsigned(int, *(tl + 4));
3051191783Srmacklem			    if (len > 0 && len <= NFSX_V4FHMAX)
3052191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3053191783Srmacklem			    else
3054191783Srmacklem				error = EPERM;
3055191783Srmacklem			    if (!error) {
3056191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3057191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
3058191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
3059191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3060191783Srmacklem				    NULL, NULL, NULL, p, cred);
3061191783Srmacklem				if (error) {
3062191783Srmacklem				    dotdotfileid = dotfileid;
3063191783Srmacklem				} else if (gotmnton) {
3064191783Srmacklem				    if (nfsva.na_mntonfileno != 0xffffffff)
3065191783Srmacklem					dotdotfileid = nfsva.na_mntonfileno;
3066191783Srmacklem				    else
3067191783Srmacklem					dotdotfileid = nfsva.na_fileid;
3068191783Srmacklem				} else if (nfsva.na_filesid[0] ==
3069191783Srmacklem				    dnp->n_vattr.na_filesid[0] &&
3070191783Srmacklem				    nfsva.na_filesid[1] ==
3071191783Srmacklem				    dnp->n_vattr.na_filesid[1]) {
3072191783Srmacklem				    dotdotfileid = nfsva.na_fileid;
3073191783Srmacklem				} else {
3074191783Srmacklem				    do {
3075191783Srmacklem					fakefileno--;
3076191783Srmacklem				    } while (fakefileno ==
3077191783Srmacklem					nfsva.na_fileid);
3078191783Srmacklem				    dotdotfileid = fakefileno;
3079191783Srmacklem				}
3080191783Srmacklem			    }
3081191783Srmacklem			} else if (nd->nd_repstat == NFSERR_NOENT) {
3082191783Srmacklem			    /*
3083191783Srmacklem			     * Lookupp returns NFSERR_NOENT when we are
3084191783Srmacklem			     * at the root, so just use the current dir.
3085191783Srmacklem			     */
3086191783Srmacklem			    nd->nd_repstat = 0;
3087191783Srmacklem			    dotdotfileid = dotfileid;
3088191783Srmacklem			} else {
3089191783Srmacklem			    error = nd->nd_repstat;
3090191783Srmacklem			}
3091191783Srmacklem			mbuf_freem(nd->nd_mrep);
3092191783Srmacklem			if (error)
3093191783Srmacklem			    return (error);
3094191783Srmacklem			nd->nd_mrep = NULL;
3095191783Srmacklem			dp = (struct dirent *)uio_iov_base(uiop);
3096191783Srmacklem			dp->d_type = DT_DIR;
3097191783Srmacklem			dp->d_fileno = dotfileid;
3098191783Srmacklem			dp->d_namlen = 1;
3099191783Srmacklem			dp->d_name[0] = '.';
3100191783Srmacklem			dp->d_name[1] = '\0';
3101191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3102191783Srmacklem			/*
3103191783Srmacklem			 * Just make these offset cookie 0.
3104191783Srmacklem			 */
3105191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
3106191783Srmacklem			*tl++ = 0;
3107191783Srmacklem			*tl = 0;
3108191783Srmacklem			blksiz += dp->d_reclen;
3109191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
3110191783Srmacklem			uiop->uio_offset += dp->d_reclen;
3111191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
3112191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
3113191783Srmacklem			dp = (struct dirent *)uio_iov_base(uiop);
3114191783Srmacklem			dp->d_type = DT_DIR;
3115191783Srmacklem			dp->d_fileno = dotdotfileid;
3116191783Srmacklem			dp->d_namlen = 2;
3117191783Srmacklem			dp->d_name[0] = '.';
3118191783Srmacklem			dp->d_name[1] = '.';
3119191783Srmacklem			dp->d_name[2] = '\0';
3120191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3121191783Srmacklem			/*
3122191783Srmacklem			 * Just make these offset cookie 0.
3123191783Srmacklem			 */
3124191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
3125191783Srmacklem			*tl++ = 0;
3126191783Srmacklem			*tl = 0;
3127191783Srmacklem			blksiz += dp->d_reclen;
3128191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
3129191783Srmacklem			uiop->uio_offset += dp->d_reclen;
3130191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
3131191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
3132191783Srmacklem		}
3133191783Srmacklem		NFSREADDIRPLUS_ATTRBIT(&attrbits);
3134191783Srmacklem		if (gotmnton)
3135191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits,
3136191783Srmacklem			    NFSATTRBIT_MOUNTEDONFILEID);
3137191783Srmacklem	}
3138191783Srmacklem
3139191783Srmacklem	/*
3140191783Srmacklem	 * Loop around doing readdir rpc's of size nm_readdirsize.
3141191783Srmacklem	 * The stopping criteria is EOF or buffer full.
3142191783Srmacklem	 */
3143191783Srmacklem	while (more_dirs && bigenough) {
3144191783Srmacklem		*attrflagp = 0;
3145191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_READDIRPLUS, vp);
3146191783Srmacklem 		NFSM_BUILD(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
3147191783Srmacklem		*tl++ = cookie.lval[0];
3148191783Srmacklem		*tl++ = cookie.lval[1];
3149191783Srmacklem		if (cookie.qval == 0) {
3150191783Srmacklem			*tl++ = 0;
3151191783Srmacklem			*tl++ = 0;
3152191783Srmacklem		} else {
3153191783Srmacklem			NFSLOCKNODE(dnp);
3154191783Srmacklem			*tl++ = dnp->n_cookieverf.nfsuquad[0];
3155191783Srmacklem			*tl++ = dnp->n_cookieverf.nfsuquad[1];
3156191783Srmacklem			NFSUNLOCKNODE(dnp);
3157191783Srmacklem		}
3158191783Srmacklem		*tl++ = txdr_unsigned(nmp->nm_readdirsize);
3159191783Srmacklem		*tl = txdr_unsigned(nmp->nm_readdirsize);
3160191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
3161191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
3162191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3163191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3164191783Srmacklem			(void) nfsrv_putattrbit(nd, &dattrbits);
3165191783Srmacklem		}
3166191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
3167191783Srmacklem		if (error)
3168191783Srmacklem			return (error);
3169191783Srmacklem		if (nd->nd_flag & ND_NFSV3)
3170191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3171191783Srmacklem		if (nd->nd_repstat || error) {
3172191783Srmacklem			if (!error)
3173191783Srmacklem				error = nd->nd_repstat;
3174191783Srmacklem			goto nfsmout;
3175191783Srmacklem		}
3176233285Sjhb		if ((nd->nd_flag & ND_NFSV3) != 0 && *attrflagp != 0)
3177233285Sjhb			dctime = nap->na_ctime;
3178191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3179191783Srmacklem		NFSLOCKNODE(dnp);
3180191783Srmacklem		dnp->n_cookieverf.nfsuquad[0] = *tl++;
3181191783Srmacklem		dnp->n_cookieverf.nfsuquad[1] = *tl++;
3182191783Srmacklem		NFSUNLOCKNODE(dnp);
3183191783Srmacklem		more_dirs = fxdr_unsigned(int, *tl);
3184191783Srmacklem		if (!more_dirs)
3185191783Srmacklem			tryformoredirs = 0;
3186191783Srmacklem
3187191783Srmacklem		/* loop thru the dir entries, doctoring them to 4bsd form */
3188191783Srmacklem		while (more_dirs && bigenough) {
3189191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3190191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
3191191783Srmacklem				ncookie.lval[0] = *tl++;
3192191783Srmacklem				ncookie.lval[1] = *tl++;
3193191783Srmacklem			} else {
3194191783Srmacklem				fileno = fxdr_unsigned(long, *++tl);
3195191783Srmacklem				tl++;
3196191783Srmacklem			}
3197191783Srmacklem			len = fxdr_unsigned(int, *tl);
3198191783Srmacklem			if (len <= 0 || len > NFS_MAXNAMLEN) {
3199191783Srmacklem				error = EBADRPC;
3200191783Srmacklem				goto nfsmout;
3201191783Srmacklem			}
3202191783Srmacklem			tlen = NFSM_RNDUP(len);
3203191783Srmacklem			if (tlen == len)
3204191783Srmacklem				tlen += 4;  /* To ensure null termination */
3205191783Srmacklem			left = DIRBLKSIZ - blksiz;
3206191783Srmacklem			if ((tlen + DIRHDSIZ + NFSX_HYPER) > left) {
3207191783Srmacklem				dp->d_reclen += left;
3208191783Srmacklem				uio_iov_base_add(uiop, left);
3209191783Srmacklem				uio_iov_len_add(uiop, -(left));
3210191783Srmacklem				uio_uio_resid_add(uiop, -(left));
3211191783Srmacklem				uiop->uio_offset += left;
3212191783Srmacklem				blksiz = 0;
3213191783Srmacklem			}
3214191783Srmacklem			if ((tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
3215191783Srmacklem				bigenough = 0;
3216191783Srmacklem			if (bigenough) {
3217191783Srmacklem				dp = (struct dirent *)uio_iov_base(uiop);
3218191783Srmacklem				dp->d_namlen = len;
3219191783Srmacklem				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
3220191783Srmacklem				dp->d_type = DT_UNKNOWN;
3221191783Srmacklem				blksiz += dp->d_reclen;
3222191783Srmacklem				if (blksiz == DIRBLKSIZ)
3223191783Srmacklem					blksiz = 0;
3224191783Srmacklem				uio_uio_resid_add(uiop, -(DIRHDSIZ));
3225191783Srmacklem				uiop->uio_offset += DIRHDSIZ;
3226191783Srmacklem				uio_iov_base_add(uiop, DIRHDSIZ);
3227191783Srmacklem				uio_iov_len_add(uiop, -(DIRHDSIZ));
3228191783Srmacklem				cnp->cn_nameptr = uio_iov_base(uiop);
3229191783Srmacklem				cnp->cn_namelen = len;
3230191783Srmacklem				NFSCNHASHZERO(cnp);
3231191783Srmacklem				error = nfsm_mbufuio(nd, uiop, len);
3232191783Srmacklem				if (error)
3233191783Srmacklem					goto nfsmout;
3234191783Srmacklem				cp = uio_iov_base(uiop);
3235191783Srmacklem				tlen -= len;
3236191783Srmacklem				*cp = '\0';
3237191783Srmacklem				cp += tlen;	/* points to cookie storage */
3238191783Srmacklem				tl2 = (u_int32_t *)cp;
3239220735Srmacklem				if (len == 2 && cnp->cn_nameptr[0] == '.' &&
3240220735Srmacklem				    cnp->cn_nameptr[1] == '.')
3241220735Srmacklem					isdotdot = 1;
3242220735Srmacklem				else
3243220735Srmacklem					isdotdot = 0;
3244191783Srmacklem				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
3245191783Srmacklem				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
3246191783Srmacklem				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
3247191783Srmacklem				uiop->uio_offset += (tlen + NFSX_HYPER);
3248191783Srmacklem			} else {
3249191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3250191783Srmacklem				if (error)
3251191783Srmacklem					goto nfsmout;
3252191783Srmacklem			}
3253191783Srmacklem			nfhp = NULL;
3254191783Srmacklem			if (nd->nd_flag & ND_NFSV3) {
3255191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3256191783Srmacklem				ncookie.lval[0] = *tl++;
3257191783Srmacklem				ncookie.lval[1] = *tl++;
3258191783Srmacklem				attrflag = fxdr_unsigned(int, *tl);
3259191783Srmacklem				if (attrflag) {
3260191783Srmacklem				  error = nfsm_loadattr(nd, &nfsva);
3261191783Srmacklem				  if (error)
3262191783Srmacklem					goto nfsmout;
3263191783Srmacklem				}
3264191783Srmacklem				NFSM_DISSECT(tl,u_int32_t *,NFSX_UNSIGNED);
3265191783Srmacklem				if (*tl) {
3266191783Srmacklem					error = nfsm_getfh(nd, &nfhp);
3267191783Srmacklem					if (error)
3268191783Srmacklem					    goto nfsmout;
3269191783Srmacklem				}
3270191783Srmacklem				if (!attrflag && nfhp != NULL) {
3271191783Srmacklem					FREE((caddr_t)nfhp, M_NFSFH);
3272191783Srmacklem					nfhp = NULL;
3273191783Srmacklem				}
3274191783Srmacklem			} else {
3275191783Srmacklem				rderr = 0;
3276191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
3277191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
3278191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3279191783Srmacklem				    NULL, NULL, &rderr, p, cred);
3280191783Srmacklem				if (error)
3281191783Srmacklem					goto nfsmout;
3282191783Srmacklem			}
3283191783Srmacklem
3284191783Srmacklem			if (bigenough) {
3285191783Srmacklem			    if (nd->nd_flag & ND_NFSV4) {
3286191783Srmacklem				if (rderr) {
3287191783Srmacklem				    dp->d_fileno = 0;
3288191783Srmacklem				} else if (gotmnton) {
3289191783Srmacklem				    if (nfsva.na_mntonfileno != 0xffffffff)
3290191783Srmacklem					dp->d_fileno = nfsva.na_mntonfileno;
3291191783Srmacklem				    else
3292191783Srmacklem					dp->d_fileno = nfsva.na_fileid;
3293191783Srmacklem				} else if (nfsva.na_filesid[0] ==
3294191783Srmacklem				    dnp->n_vattr.na_filesid[0] &&
3295191783Srmacklem				    nfsva.na_filesid[1] ==
3296191783Srmacklem				    dnp->n_vattr.na_filesid[1]) {
3297191783Srmacklem				    dp->d_fileno = nfsva.na_fileid;
3298191783Srmacklem				} else {
3299191783Srmacklem				    do {
3300191783Srmacklem					fakefileno--;
3301191783Srmacklem				    } while (fakefileno ==
3302191783Srmacklem					nfsva.na_fileid);
3303191783Srmacklem				    dp->d_fileno = fakefileno;
3304191783Srmacklem				}
3305191783Srmacklem			    } else {
3306191783Srmacklem				dp->d_fileno = fileno;
3307191783Srmacklem			    }
3308191783Srmacklem			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
3309191783Srmacklem				ncookie.lval[0];
3310191783Srmacklem			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
3311191783Srmacklem				ncookie.lval[1];
3312191783Srmacklem
3313191783Srmacklem			    if (nfhp != NULL) {
3314191783Srmacklem				if (NFSRV_CMPFH(nfhp->nfh_fh, nfhp->nfh_len,
3315191783Srmacklem				    dnp->n_fhp->nfh_fh, dnp->n_fhp->nfh_len)) {
3316191783Srmacklem				    VREF(vp);
3317191783Srmacklem				    newvp = vp;
3318191783Srmacklem				    unlocknewvp = 0;
3319191783Srmacklem				    FREE((caddr_t)nfhp, M_NFSFH);
3320191783Srmacklem				    np = dnp;
3321220735Srmacklem				} else if (isdotdot != 0) {
3322220735Srmacklem				    /*
3323220735Srmacklem				     * Skip doing a nfscl_nget() call for "..".
3324220735Srmacklem				     * There's a race between acquiring the nfs
3325220735Srmacklem				     * node here and lookups that look for the
3326220735Srmacklem				     * directory being read (in the parent).
3327220735Srmacklem				     * It would try to get a lock on ".." here,
3328220735Srmacklem				     * owning the lock on the directory being
3329220735Srmacklem				     * read. Lookup will hold the lock on ".."
3330220735Srmacklem				     * and try to acquire the lock on the
3331220735Srmacklem				     * directory being read.
3332220735Srmacklem				     * If the directory is unlocked/relocked,
3333220735Srmacklem				     * then there is a LOR with the buflock
3334220735Srmacklem				     * vp is relocked.
3335220735Srmacklem				     */
3336220735Srmacklem				    free(nfhp, M_NFSFH);
3337191783Srmacklem				} else {
3338191783Srmacklem				    error = nfscl_nget(vnode_mount(vp), vp,
3339220732Srmacklem				      nfhp, cnp, p, &np, NULL, LK_EXCLUSIVE);
3340191783Srmacklem				    if (!error) {
3341191783Srmacklem					newvp = NFSTOV(np);
3342191783Srmacklem					unlocknewvp = 1;
3343191783Srmacklem				    }
3344191783Srmacklem				}
3345191783Srmacklem				nfhp = NULL;
3346191783Srmacklem				if (newvp != NULLVP) {
3347191783Srmacklem				    error = nfscl_loadattrcache(&newvp,
3348191783Srmacklem					&nfsva, NULL, NULL, 0, 0);
3349191783Srmacklem				    if (error) {
3350191783Srmacklem					if (unlocknewvp)
3351191783Srmacklem					    vput(newvp);
3352191783Srmacklem					else
3353191783Srmacklem					    vrele(newvp);
3354191783Srmacklem					goto nfsmout;
3355191783Srmacklem				    }
3356191783Srmacklem				    dp->d_type =
3357191783Srmacklem					vtonfs_dtype(np->n_vattr.na_type);
3358191783Srmacklem				    ndp->ni_vp = newvp;
3359191783Srmacklem				    NFSCNHASH(cnp, HASHINIT);
3360233285Sjhb				    if (cnp->cn_namelen <= NCHNAMLEN &&
3361233285Sjhb					(newvp->v_type != VDIR ||
3362233285Sjhb					 dctime.tv_sec != 0)) {
3363233285Sjhb					cache_enter_time(ndp->ni_dvp,
3364233285Sjhb					    ndp->ni_vp, cnp,
3365233285Sjhb					    &nfsva.na_ctime,
3366233285Sjhb					    newvp->v_type != VDIR ? NULL :
3367233285Sjhb					    &dctime);
3368191783Srmacklem				    }
3369191783Srmacklem				    if (unlocknewvp)
3370191783Srmacklem					vput(newvp);
3371191783Srmacklem				    else
3372191783Srmacklem					vrele(newvp);
3373191783Srmacklem				    newvp = NULLVP;
3374191783Srmacklem				}
3375191783Srmacklem			    }
3376191783Srmacklem			} else if (nfhp != NULL) {
3377191783Srmacklem			    FREE((caddr_t)nfhp, M_NFSFH);
3378191783Srmacklem			}
3379191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3380191783Srmacklem			more_dirs = fxdr_unsigned(int, *tl);
3381191783Srmacklem		}
3382191783Srmacklem		/*
3383191783Srmacklem		 * If at end of rpc data, get the eof boolean
3384191783Srmacklem		 */
3385191783Srmacklem		if (!more_dirs) {
3386191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3387191783Srmacklem			eof = fxdr_unsigned(int, *tl);
3388191783Srmacklem			if (tryformoredirs)
3389191783Srmacklem				more_dirs = !eof;
3390191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
3391191783Srmacklem				error = nfscl_postop_attr(nd, nap, attrflagp,
3392191783Srmacklem				    stuff);
3393191783Srmacklem				if (error)
3394191783Srmacklem					goto nfsmout;
3395191783Srmacklem			}
3396191783Srmacklem		}
3397191783Srmacklem		mbuf_freem(nd->nd_mrep);
3398191783Srmacklem		nd->nd_mrep = NULL;
3399191783Srmacklem	}
3400191783Srmacklem	/*
3401191783Srmacklem	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3402191783Srmacklem	 * by increasing d_reclen for the last record.
3403191783Srmacklem	 */
3404191783Srmacklem	if (blksiz > 0) {
3405191783Srmacklem		left = DIRBLKSIZ - blksiz;
3406191783Srmacklem		dp->d_reclen += left;
3407191783Srmacklem		uio_iov_base_add(uiop, left);
3408191783Srmacklem		uio_iov_len_add(uiop, -(left));
3409191783Srmacklem		uio_uio_resid_add(uiop, -(left));
3410191783Srmacklem		uiop->uio_offset += left;
3411191783Srmacklem	}
3412191783Srmacklem
3413191783Srmacklem	/*
3414191783Srmacklem	 * If returning no data, assume end of file.
3415191783Srmacklem	 * If not bigenough, return not end of file, since you aren't
3416191783Srmacklem	 *    returning all the data
3417191783Srmacklem	 * Otherwise, return the eof flag from the server.
3418191783Srmacklem	 */
3419191783Srmacklem	if (eofp != NULL) {
3420191783Srmacklem		if (tresid == uio_uio_resid(uiop))
3421191783Srmacklem			*eofp = 1;
3422191783Srmacklem		else if (!bigenough)
3423191783Srmacklem			*eofp = 0;
3424191783Srmacklem		else
3425191783Srmacklem			*eofp = eof;
3426191783Srmacklem	}
3427191783Srmacklem
3428191783Srmacklem	/*
3429191783Srmacklem	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
3430191783Srmacklem	 */
3431191783Srmacklem	while (uio_uio_resid(uiop) > 0 && uio_uio_resid(uiop) != tresid) {
3432191783Srmacklem		dp = (struct dirent *)uio_iov_base(uiop);
3433191783Srmacklem		dp->d_type = DT_UNKNOWN;
3434191783Srmacklem		dp->d_fileno = 0;
3435191783Srmacklem		dp->d_namlen = 0;
3436191783Srmacklem		dp->d_name[0] = '\0';
3437191783Srmacklem		tl = (u_int32_t *)&dp->d_name[4];
3438191783Srmacklem		*tl++ = cookie.lval[0];
3439191783Srmacklem		*tl = cookie.lval[1];
3440191783Srmacklem		dp->d_reclen = DIRBLKSIZ;
3441191783Srmacklem		uio_iov_base_add(uiop, DIRBLKSIZ);
3442191783Srmacklem		uio_iov_len_add(uiop, -(DIRBLKSIZ));
3443191783Srmacklem		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
3444191783Srmacklem		uiop->uio_offset += DIRBLKSIZ;
3445191783Srmacklem	}
3446191783Srmacklem
3447191783Srmacklemnfsmout:
3448191783Srmacklem	if (nd->nd_mrep != NULL)
3449191783Srmacklem		mbuf_freem(nd->nd_mrep);
3450191783Srmacklem	return (error);
3451191783Srmacklem}
3452191783Srmacklem#endif	/* !APPLE */
3453191783Srmacklem
3454191783Srmacklem/*
3455191783Srmacklem * Nfs commit rpc
3456191783Srmacklem */
3457191783SrmacklemAPPLESTATIC int
3458191783Srmacklemnfsrpc_commit(vnode_t vp, u_quad_t offset, int cnt, struct ucred *cred,
3459191783Srmacklem    NFSPROC_T *p, u_char *verfp, struct nfsvattr *nap, int *attrflagp,
3460191783Srmacklem    void *stuff)
3461191783Srmacklem{
3462191783Srmacklem	u_int32_t *tl;
3463191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3464191783Srmacklem	nfsattrbit_t attrbits;
3465191783Srmacklem	int error;
3466191783Srmacklem
3467191783Srmacklem	*attrflagp = 0;
3468191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_COMMIT, vp);
3469191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3470191783Srmacklem	txdr_hyper(offset, tl);
3471191783Srmacklem	tl += 2;
3472191783Srmacklem	*tl = txdr_unsigned(cnt);
3473191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
3474191783Srmacklem		/*
3475191783Srmacklem		 * And do a Getattr op.
3476191783Srmacklem		 */
3477191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3478191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
3479191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
3480191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
3481191783Srmacklem	}
3482191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
3483191783Srmacklem	if (error)
3484191783Srmacklem		return (error);
3485191783Srmacklem	error = nfscl_wcc_data(nd, vp, nap, attrflagp, NULL, stuff);
3486191783Srmacklem	if (!error && !nd->nd_repstat) {
3487191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
3488191783Srmacklem		NFSBCOPY((caddr_t)tl, verfp, NFSX_VERF);
3489191783Srmacklem		if (nd->nd_flag & ND_NFSV4)
3490191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3491191783Srmacklem	}
3492191783Srmacklemnfsmout:
3493191783Srmacklem	if (!error && nd->nd_repstat)
3494191783Srmacklem		error = nd->nd_repstat;
3495191783Srmacklem	mbuf_freem(nd->nd_mrep);
3496191783Srmacklem	return (error);
3497191783Srmacklem}
3498191783Srmacklem
3499191783Srmacklem/*
3500191783Srmacklem * NFS byte range lock rpc.
3501191783Srmacklem * (Mostly just calls one of the three lower level RPC routines.)
3502191783Srmacklem */
3503191783SrmacklemAPPLESTATIC int
3504191783Srmacklemnfsrpc_advlock(vnode_t vp, off_t size, int op, struct flock *fl,
3505222719Srmacklem    int reclaim, struct ucred *cred, NFSPROC_T *p, void *id, int flags)
3506191783Srmacklem{
3507191783Srmacklem	struct nfscllockowner *lp;
3508191783Srmacklem	struct nfsclclient *clp;
3509191783Srmacklem	struct nfsfh *nfhp;
3510191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3511191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3512191783Srmacklem	u_int64_t off, len;
3513191783Srmacklem	off_t start, end;
3514191783Srmacklem	u_int32_t clidrev = 0;
3515191783Srmacklem	int error = 0, newone = 0, expireret = 0, retrycnt, donelocally;
3516191783Srmacklem	int callcnt, dorpc;
3517191783Srmacklem
3518191783Srmacklem	/*
3519191783Srmacklem	 * Convert the flock structure into a start and end and do POSIX
3520191783Srmacklem	 * bounds checking.
3521191783Srmacklem	 */
3522191783Srmacklem	switch (fl->l_whence) {
3523191783Srmacklem	case SEEK_SET:
3524191783Srmacklem	case SEEK_CUR:
3525191783Srmacklem		/*
3526191783Srmacklem		 * Caller is responsible for adding any necessary offset
3527191783Srmacklem		 * when SEEK_CUR is used.
3528191783Srmacklem		 */
3529191783Srmacklem		start = fl->l_start;
3530191783Srmacklem		off = fl->l_start;
3531191783Srmacklem		break;
3532191783Srmacklem	case SEEK_END:
3533191783Srmacklem		start = size + fl->l_start;
3534191783Srmacklem		off = size + fl->l_start;
3535191783Srmacklem		break;
3536191783Srmacklem	default:
3537191783Srmacklem		return (EINVAL);
3538191783Srmacklem	};
3539191783Srmacklem	if (start < 0)
3540191783Srmacklem		return (EINVAL);
3541191783Srmacklem	if (fl->l_len != 0) {
3542191783Srmacklem		end = start + fl->l_len - 1;
3543191783Srmacklem		if (end < start)
3544191783Srmacklem			return (EINVAL);
3545191783Srmacklem	}
3546191783Srmacklem
3547191783Srmacklem	len = fl->l_len;
3548191783Srmacklem	if (len == 0)
3549191783Srmacklem		len = NFS64BITSSET;
3550191783Srmacklem	retrycnt = 0;
3551191783Srmacklem	do {
3552191783Srmacklem	    nd->nd_repstat = 0;
3553191783Srmacklem	    if (op == F_GETLK) {
3554191783Srmacklem		error = nfscl_getcl(vp, cred, p, &clp);
3555191783Srmacklem		if (error)
3556191783Srmacklem			return (error);
3557222719Srmacklem		error = nfscl_lockt(vp, clp, off, len, fl, p, id, flags);
3558191783Srmacklem		if (!error) {
3559191783Srmacklem			clidrev = clp->nfsc_clientidrev;
3560191783Srmacklem			error = nfsrpc_lockt(nd, vp, clp, off, len, fl, cred,
3561222719Srmacklem			    p, id, flags);
3562191783Srmacklem		} else if (error == -1) {
3563191783Srmacklem			error = 0;
3564191783Srmacklem		}
3565191783Srmacklem		nfscl_clientrelease(clp);
3566191783Srmacklem	    } else if (op == F_UNLCK && fl->l_type == F_UNLCK) {
3567191783Srmacklem		/*
3568191783Srmacklem		 * We must loop around for all lockowner cases.
3569191783Srmacklem		 */
3570191783Srmacklem		callcnt = 0;
3571191783Srmacklem		error = nfscl_getcl(vp, cred, p, &clp);
3572191783Srmacklem		if (error)
3573191783Srmacklem			return (error);
3574191783Srmacklem		do {
3575191783Srmacklem		    error = nfscl_relbytelock(vp, off, len, cred, p, callcnt,
3576222719Srmacklem			clp, id, flags, &lp, &dorpc);
3577191783Srmacklem		    /*
3578191783Srmacklem		     * If it returns a NULL lp, we're done.
3579191783Srmacklem		     */
3580191783Srmacklem		    if (lp == NULL) {
3581191783Srmacklem			if (callcnt == 0)
3582191783Srmacklem			    nfscl_clientrelease(clp);
3583191783Srmacklem			else
3584222719Srmacklem			    nfscl_releasealllocks(clp, vp, p, id, flags);
3585191783Srmacklem			return (error);
3586191783Srmacklem		    }
3587191783Srmacklem		    if (nmp->nm_clp != NULL)
3588191783Srmacklem			clidrev = nmp->nm_clp->nfsc_clientidrev;
3589191783Srmacklem		    else
3590191783Srmacklem			clidrev = 0;
3591191783Srmacklem		    /*
3592191783Srmacklem		     * If the server doesn't support Posix lock semantics,
3593191783Srmacklem		     * only allow locks on the entire file, since it won't
3594191783Srmacklem		     * handle overlapping byte ranges.
3595191783Srmacklem		     * There might still be a problem when a lock
3596191783Srmacklem		     * upgrade/downgrade (read<->write) occurs, since the
3597191783Srmacklem		     * server "might" expect an unlock first?
3598191783Srmacklem		     */
3599191783Srmacklem		    if (dorpc && (lp->nfsl_open->nfso_posixlock ||
3600191783Srmacklem			(off == 0 && len == NFS64BITSSET))) {
3601191783Srmacklem			/*
3602191783Srmacklem			 * Since the lock records will go away, we must
3603191783Srmacklem			 * wait for grace and delay here.
3604191783Srmacklem			 */
3605191783Srmacklem			do {
3606191783Srmacklem			    error = nfsrpc_locku(nd, nmp, lp, off, len,
3607191783Srmacklem				NFSV4LOCKT_READ, cred, p, 0);
3608191783Srmacklem			    if ((nd->nd_repstat == NFSERR_GRACE ||
3609191783Srmacklem				 nd->nd_repstat == NFSERR_DELAY) &&
3610191783Srmacklem				error == 0)
3611207170Srmacklem				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3612207170Srmacklem				    "nfs_advlock");
3613191783Srmacklem			} while ((nd->nd_repstat == NFSERR_GRACE ||
3614191783Srmacklem			    nd->nd_repstat == NFSERR_DELAY) && error == 0);
3615191783Srmacklem		    }
3616191783Srmacklem		    callcnt++;
3617191783Srmacklem		} while (error == 0 && nd->nd_repstat == 0);
3618222719Srmacklem		nfscl_releasealllocks(clp, vp, p, id, flags);
3619191783Srmacklem	    } else if (op == F_SETLK) {
3620191783Srmacklem		error = nfscl_getbytelock(vp, off, len, fl->l_type, cred, p,
3621222719Srmacklem		    NULL, 0, id, flags, NULL, NULL, &lp, &newone, &donelocally);
3622191783Srmacklem		if (error || donelocally) {
3623191783Srmacklem			return (error);
3624191783Srmacklem		}
3625191783Srmacklem		if (nmp->nm_clp != NULL)
3626191783Srmacklem			clidrev = nmp->nm_clp->nfsc_clientidrev;
3627191783Srmacklem		else
3628191783Srmacklem			clidrev = 0;
3629191783Srmacklem		nfhp = VTONFS(vp)->n_fhp;
3630191783Srmacklem		if (!lp->nfsl_open->nfso_posixlock &&
3631191783Srmacklem		    (off != 0 || len != NFS64BITSSET)) {
3632191783Srmacklem			error = EINVAL;
3633191783Srmacklem		} else {
3634191783Srmacklem			error = nfsrpc_lock(nd, nmp, vp, nfhp->nfh_fh,
3635191783Srmacklem			    nfhp->nfh_len, lp, newone, reclaim, off,
3636191783Srmacklem			    len, fl->l_type, cred, p, 0);
3637191783Srmacklem		}
3638191783Srmacklem		if (!error)
3639191783Srmacklem			error = nd->nd_repstat;
3640191783Srmacklem		nfscl_lockrelease(lp, error, newone);
3641191783Srmacklem	    } else {
3642191783Srmacklem		error = EINVAL;
3643191783Srmacklem	    }
3644191783Srmacklem	    if (!error)
3645191783Srmacklem	        error = nd->nd_repstat;
3646191783Srmacklem	    if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
3647191783Srmacklem		error == NFSERR_STALEDONTRECOVER ||
3648191783Srmacklem		error == NFSERR_STALECLIENTID || error == NFSERR_DELAY) {
3649207170Srmacklem		(void) nfs_catnap(PZERO, error, "nfs_advlock");
3650191783Srmacklem	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
3651191783Srmacklem		&& clidrev != 0) {
3652191783Srmacklem		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
3653191783Srmacklem		retrycnt++;
3654191783Srmacklem	    }
3655191783Srmacklem	} while (error == NFSERR_GRACE ||
3656191783Srmacklem	    error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
3657191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_STALESTATEID ||
3658191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
3659191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
3660191783Srmacklem	if (error && retrycnt >= 4)
3661191783Srmacklem		error = EIO;
3662191783Srmacklem	return (error);
3663191783Srmacklem}
3664191783Srmacklem
3665191783Srmacklem/*
3666191783Srmacklem * The lower level routine for the LockT case.
3667191783Srmacklem */
3668191783SrmacklemAPPLESTATIC int
3669191783Srmacklemnfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
3670191783Srmacklem    struct nfsclclient *clp, u_int64_t off, u_int64_t len, struct flock *fl,
3671222719Srmacklem    struct ucred *cred, NFSPROC_T *p, void *id, int flags)
3672191783Srmacklem{
3673191783Srmacklem	u_int32_t *tl;
3674191783Srmacklem	int error, type, size;
3675223747Srmacklem	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
3676223747Srmacklem	struct nfsnode *np;
3677191783Srmacklem
3678191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_LOCKT, vp);
3679191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3680191783Srmacklem	if (fl->l_type == F_RDLCK)
3681191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3682191783Srmacklem	else
3683191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3684191783Srmacklem	txdr_hyper(off, tl);
3685191783Srmacklem	tl += 2;
3686191783Srmacklem	txdr_hyper(len, tl);
3687191783Srmacklem	tl += 2;
3688191783Srmacklem	*tl++ = clp->nfsc_clientid.lval[0];
3689191783Srmacklem	*tl = clp->nfsc_clientid.lval[1];
3690222719Srmacklem	nfscl_filllockowner(id, own, flags);
3691223747Srmacklem	np = VTONFS(vp);
3692223747Srmacklem	NFSBCOPY(np->n_fhp->nfh_fh, &own[NFSV4CL_LOCKNAMELEN],
3693223747Srmacklem	    np->n_fhp->nfh_len);
3694223747Srmacklem	(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + np->n_fhp->nfh_len);
3695191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
3696191783Srmacklem	if (error)
3697191783Srmacklem		return (error);
3698191783Srmacklem	if (nd->nd_repstat == 0) {
3699191783Srmacklem		fl->l_type = F_UNLCK;
3700191783Srmacklem	} else if (nd->nd_repstat == NFSERR_DENIED) {
3701191783Srmacklem		nd->nd_repstat = 0;
3702191783Srmacklem		fl->l_whence = SEEK_SET;
3703191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3704191783Srmacklem		fl->l_start = fxdr_hyper(tl);
3705191783Srmacklem		tl += 2;
3706191783Srmacklem		len = fxdr_hyper(tl);
3707191783Srmacklem		tl += 2;
3708191783Srmacklem		if (len == NFS64BITSSET)
3709191783Srmacklem			fl->l_len = 0;
3710191783Srmacklem		else
3711191783Srmacklem			fl->l_len = len;
3712191783Srmacklem		type = fxdr_unsigned(int, *tl++);
3713191783Srmacklem		if (type == NFSV4LOCKT_WRITE)
3714191783Srmacklem			fl->l_type = F_WRLCK;
3715191783Srmacklem		else
3716191783Srmacklem			fl->l_type = F_RDLCK;
3717191783Srmacklem		/*
3718191783Srmacklem		 * XXX For now, I have no idea what to do with the
3719191783Srmacklem		 * conflicting lock_owner, so I'll just set the pid == 0
3720191783Srmacklem		 * and skip over the lock_owner.
3721191783Srmacklem		 */
3722191783Srmacklem		fl->l_pid = (pid_t)0;
3723191783Srmacklem		tl += 2;
3724191783Srmacklem		size = fxdr_unsigned(int, *tl);
3725191783Srmacklem		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3726191783Srmacklem			error = EBADRPC;
3727191783Srmacklem		if (!error)
3728191783Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3729191783Srmacklem	} else if (nd->nd_repstat == NFSERR_STALECLIENTID)
3730191783Srmacklem		nfscl_initiate_recovery(clp);
3731191783Srmacklemnfsmout:
3732191783Srmacklem	mbuf_freem(nd->nd_mrep);
3733191783Srmacklem	return (error);
3734191783Srmacklem}
3735191783Srmacklem
3736191783Srmacklem/*
3737191783Srmacklem * Lower level function that performs the LockU RPC.
3738191783Srmacklem */
3739191783Srmacklemstatic int
3740191783Srmacklemnfsrpc_locku(struct nfsrv_descript *nd, struct nfsmount *nmp,
3741191783Srmacklem    struct nfscllockowner *lp, u_int64_t off, u_int64_t len,
3742191783Srmacklem    u_int32_t type, struct ucred *cred, NFSPROC_T *p, int syscred)
3743191783Srmacklem{
3744191783Srmacklem	u_int32_t *tl;
3745191783Srmacklem	int error;
3746191783Srmacklem
3747191783Srmacklem	nfscl_reqstart(nd, NFSPROC_LOCKU, nmp, lp->nfsl_open->nfso_fh,
3748191783Srmacklem	    lp->nfsl_open->nfso_fhlen, NULL);
3749191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
3750191783Srmacklem	*tl++ = txdr_unsigned(type);
3751191783Srmacklem	*tl = txdr_unsigned(lp->nfsl_seqid);
3752191783Srmacklem	if (nfstest_outofseq &&
3753191783Srmacklem	    (arc4random() % nfstest_outofseq) == 0)
3754191783Srmacklem		*tl = txdr_unsigned(lp->nfsl_seqid + 1);
3755191783Srmacklem	tl++;
3756191783Srmacklem	*tl++ = lp->nfsl_stateid.seqid;
3757191783Srmacklem	*tl++ = lp->nfsl_stateid.other[0];
3758191783Srmacklem	*tl++ = lp->nfsl_stateid.other[1];
3759191783Srmacklem	*tl++ = lp->nfsl_stateid.other[2];
3760191783Srmacklem	txdr_hyper(off, tl);
3761191783Srmacklem	tl += 2;
3762191783Srmacklem	txdr_hyper(len, tl);
3763191783Srmacklem	if (syscred)
3764191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
3765191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
3766191783Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
3767191783Srmacklem	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
3768191783Srmacklem	if (error)
3769191783Srmacklem		return (error);
3770191783Srmacklem	if (nd->nd_repstat == 0) {
3771191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3772191783Srmacklem		lp->nfsl_stateid.seqid = *tl++;
3773191783Srmacklem		lp->nfsl_stateid.other[0] = *tl++;
3774191783Srmacklem		lp->nfsl_stateid.other[1] = *tl++;
3775191783Srmacklem		lp->nfsl_stateid.other[2] = *tl;
3776191783Srmacklem	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
3777191783Srmacklem		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
3778191783Srmacklemnfsmout:
3779191783Srmacklem	mbuf_freem(nd->nd_mrep);
3780191783Srmacklem	return (error);
3781191783Srmacklem}
3782191783Srmacklem
3783191783Srmacklem/*
3784191783Srmacklem * The actual Lock RPC.
3785191783Srmacklem */
3786191783SrmacklemAPPLESTATIC int
3787191783Srmacklemnfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
3788191783Srmacklem    u_int8_t *nfhp, int fhlen, struct nfscllockowner *lp, int newone,
3789191783Srmacklem    int reclaim, u_int64_t off, u_int64_t len, short type, struct ucred *cred,
3790191783Srmacklem    NFSPROC_T *p, int syscred)
3791191783Srmacklem{
3792191783Srmacklem	u_int32_t *tl;
3793191783Srmacklem	int error, size;
3794223747Srmacklem	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
3795191783Srmacklem
3796191783Srmacklem	nfscl_reqstart(nd, NFSPROC_LOCK, nmp, nfhp, fhlen, NULL);
3797191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3798191783Srmacklem	if (type == F_RDLCK)
3799191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3800191783Srmacklem	else
3801191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3802191783Srmacklem	*tl++ = txdr_unsigned(reclaim);
3803191783Srmacklem	txdr_hyper(off, tl);
3804191783Srmacklem	tl += 2;
3805191783Srmacklem	txdr_hyper(len, tl);
3806191783Srmacklem	tl += 2;
3807191783Srmacklem	if (newone) {
3808191783Srmacklem	    *tl = newnfs_true;
3809191783Srmacklem	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
3810191783Srmacklem		2 * NFSX_UNSIGNED + NFSX_HYPER);
3811191783Srmacklem	    *tl++ = txdr_unsigned(lp->nfsl_open->nfso_own->nfsow_seqid);
3812191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_stateid.seqid;
3813191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_stateid.other[0];
3814191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_stateid.other[1];
3815191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_stateid.other[2];
3816191783Srmacklem	    *tl++ = txdr_unsigned(lp->nfsl_seqid);
3817191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[0];
3818191783Srmacklem	    *tl = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[1];
3819223747Srmacklem	    NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
3820223747Srmacklem	    NFSBCOPY(nfhp, &own[NFSV4CL_LOCKNAMELEN], fhlen);
3821223747Srmacklem	    (void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
3822191783Srmacklem	} else {
3823191783Srmacklem	    *tl = newnfs_false;
3824191783Srmacklem	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3825191783Srmacklem	    *tl++ = lp->nfsl_stateid.seqid;
3826191783Srmacklem	    *tl++ = lp->nfsl_stateid.other[0];
3827191783Srmacklem	    *tl++ = lp->nfsl_stateid.other[1];
3828191783Srmacklem	    *tl++ = lp->nfsl_stateid.other[2];
3829191783Srmacklem	    *tl = txdr_unsigned(lp->nfsl_seqid);
3830191783Srmacklem	    if (nfstest_outofseq &&
3831191783Srmacklem		(arc4random() % nfstest_outofseq) == 0)
3832191783Srmacklem		    *tl = txdr_unsigned(lp->nfsl_seqid + 1);
3833191783Srmacklem	}
3834191783Srmacklem	if (syscred)
3835191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
3836191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
3837191783Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
3838191783Srmacklem	if (error)
3839191783Srmacklem		return (error);
3840191783Srmacklem	if (newone)
3841191783Srmacklem	    NFSCL_INCRSEQID(lp->nfsl_open->nfso_own->nfsow_seqid, nd);
3842191783Srmacklem	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
3843191783Srmacklem	if (nd->nd_repstat == 0) {
3844191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3845191783Srmacklem		lp->nfsl_stateid.seqid = *tl++;
3846191783Srmacklem		lp->nfsl_stateid.other[0] = *tl++;
3847191783Srmacklem		lp->nfsl_stateid.other[1] = *tl++;
3848191783Srmacklem		lp->nfsl_stateid.other[2] = *tl;
3849191783Srmacklem	} else if (nd->nd_repstat == NFSERR_DENIED) {
3850191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3851191783Srmacklem		size = fxdr_unsigned(int, *(tl + 7));
3852191783Srmacklem		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3853191783Srmacklem			error = EBADRPC;
3854191783Srmacklem		if (!error)
3855191783Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3856191783Srmacklem	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
3857191783Srmacklem		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
3858191783Srmacklemnfsmout:
3859191783Srmacklem	mbuf_freem(nd->nd_mrep);
3860191783Srmacklem	return (error);
3861191783Srmacklem}
3862191783Srmacklem
3863191783Srmacklem/*
3864191783Srmacklem * nfs statfs rpc
3865191783Srmacklem * (always called with the vp for the mount point)
3866191783Srmacklem */
3867191783SrmacklemAPPLESTATIC int
3868191783Srmacklemnfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
3869191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3870191783Srmacklem    void *stuff)
3871191783Srmacklem{
3872191783Srmacklem	u_int32_t *tl = NULL;
3873191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3874191783Srmacklem	struct nfsmount *nmp;
3875191783Srmacklem	nfsattrbit_t attrbits;
3876191783Srmacklem	int error;
3877191783Srmacklem
3878191783Srmacklem	*attrflagp = 0;
3879191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3880191783Srmacklem	if (NFSHASNFSV4(nmp)) {
3881191783Srmacklem		/*
3882191783Srmacklem		 * For V4, you actually do a getattr.
3883191783Srmacklem		 */
3884191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
3885191783Srmacklem		NFSSTATFS_GETATTRBIT(&attrbits);
3886191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
3887191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
3888191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
3889191783Srmacklem		if (error)
3890191783Srmacklem			return (error);
3891191783Srmacklem		if (nd->nd_repstat == 0) {
3892191783Srmacklem			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
3893191783Srmacklem			    NULL, NULL, sbp, fsp, NULL, 0, NULL, NULL, NULL, p,
3894191783Srmacklem			    cred);
3895191783Srmacklem			if (!error) {
3896191783Srmacklem				nmp->nm_fsid[0] = nap->na_filesid[0];
3897191783Srmacklem				nmp->nm_fsid[1] = nap->na_filesid[1];
3898191783Srmacklem				NFSSETHASSETFSID(nmp);
3899191783Srmacklem				*attrflagp = 1;
3900191783Srmacklem			}
3901191783Srmacklem		} else {
3902191783Srmacklem			error = nd->nd_repstat;
3903191783Srmacklem		}
3904191783Srmacklem		if (error)
3905191783Srmacklem			goto nfsmout;
3906191783Srmacklem	} else {
3907191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_FSSTAT, vp);
3908191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
3909191783Srmacklem		if (error)
3910191783Srmacklem			return (error);
3911191783Srmacklem		if (nd->nd_flag & ND_NFSV3) {
3912191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3913191783Srmacklem			if (error)
3914191783Srmacklem				goto nfsmout;
3915191783Srmacklem		}
3916191783Srmacklem		if (nd->nd_repstat) {
3917191783Srmacklem			error = nd->nd_repstat;
3918191783Srmacklem			goto nfsmout;
3919191783Srmacklem		}
3920191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *,
3921191783Srmacklem		    NFSX_STATFS(nd->nd_flag & ND_NFSV3));
3922191783Srmacklem	}
3923191783Srmacklem	if (NFSHASNFSV3(nmp)) {
3924191783Srmacklem		sbp->sf_tbytes = fxdr_hyper(tl); tl += 2;
3925191783Srmacklem		sbp->sf_fbytes = fxdr_hyper(tl); tl += 2;
3926191783Srmacklem		sbp->sf_abytes = fxdr_hyper(tl); tl += 2;
3927191783Srmacklem		sbp->sf_tfiles = fxdr_hyper(tl); tl += 2;
3928191783Srmacklem		sbp->sf_ffiles = fxdr_hyper(tl); tl += 2;
3929191783Srmacklem		sbp->sf_afiles = fxdr_hyper(tl); tl += 2;
3930191783Srmacklem		sbp->sf_invarsec = fxdr_unsigned(u_int32_t, *tl);
3931191783Srmacklem	} else if (NFSHASNFSV4(nmp) == 0) {
3932191783Srmacklem		sbp->sf_tsize = fxdr_unsigned(u_int32_t, *tl++);
3933191783Srmacklem		sbp->sf_bsize = fxdr_unsigned(u_int32_t, *tl++);
3934191783Srmacklem		sbp->sf_blocks = fxdr_unsigned(u_int32_t, *tl++);
3935191783Srmacklem		sbp->sf_bfree = fxdr_unsigned(u_int32_t, *tl++);
3936191783Srmacklem		sbp->sf_bavail = fxdr_unsigned(u_int32_t, *tl);
3937191783Srmacklem	}
3938191783Srmacklemnfsmout:
3939191783Srmacklem	mbuf_freem(nd->nd_mrep);
3940191783Srmacklem	return (error);
3941191783Srmacklem}
3942191783Srmacklem
3943191783Srmacklem/*
3944191783Srmacklem * nfs pathconf rpc
3945191783Srmacklem */
3946191783SrmacklemAPPLESTATIC int
3947191783Srmacklemnfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc,
3948191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3949191783Srmacklem    void *stuff)
3950191783Srmacklem{
3951191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3952191783Srmacklem	struct nfsmount *nmp;
3953191783Srmacklem	u_int32_t *tl;
3954191783Srmacklem	nfsattrbit_t attrbits;
3955191783Srmacklem	int error;
3956191783Srmacklem
3957191783Srmacklem	*attrflagp = 0;
3958191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3959191783Srmacklem	if (NFSHASNFSV4(nmp)) {
3960191783Srmacklem		/*
3961191783Srmacklem		 * For V4, you actually do a getattr.
3962191783Srmacklem		 */
3963191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
3964191783Srmacklem		NFSPATHCONF_GETATTRBIT(&attrbits);
3965191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
3966191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
3967191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
3968191783Srmacklem		if (error)
3969191783Srmacklem			return (error);
3970191783Srmacklem		if (nd->nd_repstat == 0) {
3971191783Srmacklem			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
3972191783Srmacklem			    pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p,
3973191783Srmacklem			    cred);
3974191783Srmacklem			if (!error)
3975191783Srmacklem				*attrflagp = 1;
3976191783Srmacklem		} else {
3977191783Srmacklem			error = nd->nd_repstat;
3978191783Srmacklem		}
3979191783Srmacklem	} else {
3980191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_PATHCONF, vp);
3981191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
3982191783Srmacklem		if (error)
3983191783Srmacklem			return (error);
3984191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3985191783Srmacklem		if (nd->nd_repstat && !error)
3986191783Srmacklem			error = nd->nd_repstat;
3987191783Srmacklem		if (!error) {
3988191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V3PATHCONF);
3989191783Srmacklem			pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl++);
3990191783Srmacklem			pc->pc_namemax = fxdr_unsigned(u_int32_t, *tl++);
3991191783Srmacklem			pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl++);
3992191783Srmacklem			pc->pc_chownrestricted =
3993191783Srmacklem			    fxdr_unsigned(u_int32_t, *tl++);
3994191783Srmacklem			pc->pc_caseinsensitive =
3995191783Srmacklem			    fxdr_unsigned(u_int32_t, *tl++);
3996191783Srmacklem			pc->pc_casepreserving = fxdr_unsigned(u_int32_t, *tl);
3997191783Srmacklem		}
3998191783Srmacklem	}
3999191783Srmacklemnfsmout:
4000191783Srmacklem	mbuf_freem(nd->nd_mrep);
4001191783Srmacklem	return (error);
4002191783Srmacklem}
4003191783Srmacklem
4004191783Srmacklem/*
4005191783Srmacklem * nfs version 3 fsinfo rpc call
4006191783Srmacklem */
4007191783SrmacklemAPPLESTATIC int
4008191783Srmacklemnfsrpc_fsinfo(vnode_t vp, struct nfsfsinfo *fsp, struct ucred *cred,
4009191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
4010191783Srmacklem{
4011191783Srmacklem	u_int32_t *tl;
4012191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4013191783Srmacklem	int error;
4014191783Srmacklem
4015191783Srmacklem	*attrflagp = 0;
4016191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_FSINFO, vp);
4017191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
4018191783Srmacklem	if (error)
4019191783Srmacklem		return (error);
4020191783Srmacklem	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
4021191783Srmacklem	if (nd->nd_repstat && !error)
4022191783Srmacklem		error = nd->nd_repstat;
4023191783Srmacklem	if (!error) {
4024191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_V3FSINFO);
4025191783Srmacklem		fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *tl++);
4026191783Srmacklem		fsp->fs_rtpref = fxdr_unsigned(u_int32_t, *tl++);
4027191783Srmacklem		fsp->fs_rtmult = fxdr_unsigned(u_int32_t, *tl++);
4028191783Srmacklem		fsp->fs_wtmax = fxdr_unsigned(u_int32_t, *tl++);
4029191783Srmacklem		fsp->fs_wtpref = fxdr_unsigned(u_int32_t, *tl++);
4030191783Srmacklem		fsp->fs_wtmult = fxdr_unsigned(u_int32_t, *tl++);
4031191783Srmacklem		fsp->fs_dtpref = fxdr_unsigned(u_int32_t, *tl++);
4032191783Srmacklem		fsp->fs_maxfilesize = fxdr_hyper(tl);
4033191783Srmacklem		tl += 2;
4034191783Srmacklem		fxdr_nfsv3time(tl, &fsp->fs_timedelta);
4035191783Srmacklem		tl += 2;
4036191783Srmacklem		fsp->fs_properties = fxdr_unsigned(u_int32_t, *tl);
4037191783Srmacklem	}
4038191783Srmacklemnfsmout:
4039191783Srmacklem	mbuf_freem(nd->nd_mrep);
4040191783Srmacklem	return (error);
4041191783Srmacklem}
4042191783Srmacklem
4043191783Srmacklem/*
4044191783Srmacklem * This function performs the Renew RPC.
4045191783Srmacklem */
4046191783SrmacklemAPPLESTATIC int
4047191783Srmacklemnfsrpc_renew(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
4048191783Srmacklem{
4049191783Srmacklem	u_int32_t *tl;
4050191783Srmacklem	struct nfsrv_descript nfsd;
4051191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
4052191783Srmacklem	struct nfsmount *nmp;
4053191783Srmacklem	int error;
4054191783Srmacklem
4055191783Srmacklem	nmp = clp->nfsc_nmp;
4056191783Srmacklem	if (nmp == NULL)
4057191783Srmacklem		return (0);
4058191783Srmacklem	nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL);
4059191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4060191783Srmacklem	*tl++ = clp->nfsc_clientid.lval[0];
4061191783Srmacklem	*tl = clp->nfsc_clientid.lval[1];
4062191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4063191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4064191783Srmacklem		NFS_PROG, NFS_VER4, NULL, 1, NULL);
4065191783Srmacklem	if (error)
4066191783Srmacklem		return (error);
4067191783Srmacklem	error = nd->nd_repstat;
4068191783Srmacklem	mbuf_freem(nd->nd_mrep);
4069191783Srmacklem	return (error);
4070191783Srmacklem}
4071191783Srmacklem
4072191783Srmacklem/*
4073191783Srmacklem * This function performs the Releaselockowner RPC.
4074191783Srmacklem */
4075191783SrmacklemAPPLESTATIC int
4076191783Srmacklemnfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
4077229674Srmacklem    uint8_t *fh, int fhlen, struct ucred *cred, NFSPROC_T *p)
4078191783Srmacklem{
4079191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4080191783Srmacklem	u_int32_t *tl;
4081191783Srmacklem	int error;
4082223747Srmacklem	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
4083191783Srmacklem
4084191783Srmacklem	nfscl_reqstart(nd, NFSPROC_RELEASELCKOWN, nmp, NULL, 0, NULL);
4085191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4086191783Srmacklem	*tl++ = nmp->nm_clp->nfsc_clientid.lval[0];
4087191783Srmacklem	*tl = nmp->nm_clp->nfsc_clientid.lval[1];
4088223747Srmacklem	NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
4089229674Srmacklem	NFSBCOPY(fh, &own[NFSV4CL_LOCKNAMELEN], fhlen);
4090229674Srmacklem	(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
4091191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4092191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4093191783Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
4094191783Srmacklem	if (error)
4095191783Srmacklem		return (error);
4096191783Srmacklem	error = nd->nd_repstat;
4097191783Srmacklem	mbuf_freem(nd->nd_mrep);
4098191783Srmacklem	return (error);
4099191783Srmacklem}
4100191783Srmacklem
4101191783Srmacklem/*
4102191783Srmacklem * This function performs the Compound to get the mount pt FH.
4103191783Srmacklem */
4104191783SrmacklemAPPLESTATIC int
4105191783Srmacklemnfsrpc_getdirpath(struct nfsmount *nmp, u_char *dirpath, struct ucred *cred,
4106191783Srmacklem    NFSPROC_T *p)
4107191783Srmacklem{
4108191783Srmacklem	u_int32_t *tl;
4109191783Srmacklem	struct nfsrv_descript nfsd;
4110191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
4111191783Srmacklem	u_char *cp, *cp2;
4112191783Srmacklem	int error, cnt, len, setnil;
4113191783Srmacklem	u_int32_t *opcntp;
4114191783Srmacklem
4115191783Srmacklem	nfscl_reqstart(nd, NFSPROC_PUTROOTFH, nmp, NULL, 0, &opcntp);
4116191783Srmacklem	cp = dirpath;
4117191783Srmacklem	cnt = 0;
4118191783Srmacklem	do {
4119191783Srmacklem		setnil = 0;
4120191783Srmacklem		while (*cp == '/')
4121191783Srmacklem			cp++;
4122191783Srmacklem		cp2 = cp;
4123191783Srmacklem		while (*cp2 != '\0' && *cp2 != '/')
4124191783Srmacklem			cp2++;
4125191783Srmacklem		if (*cp2 == '/') {
4126191783Srmacklem			setnil = 1;
4127191783Srmacklem			*cp2 = '\0';
4128191783Srmacklem		}
4129191783Srmacklem		if (cp2 != cp) {
4130191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4131191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_LOOKUP);
4132191783Srmacklem			nfsm_strtom(nd, cp, strlen(cp));
4133191783Srmacklem			cnt++;
4134191783Srmacklem		}
4135191783Srmacklem		if (setnil)
4136191783Srmacklem			*cp2++ = '/';
4137191783Srmacklem		cp = cp2;
4138191783Srmacklem	} while (*cp != '\0');
4139191783Srmacklem	*opcntp = txdr_unsigned(2 + cnt);
4140191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4141191783Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETFH);
4142191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4143191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4144191783Srmacklem		NFS_PROG, NFS_VER4, NULL, 1, NULL);
4145191783Srmacklem	if (error)
4146191783Srmacklem		return (error);
4147191783Srmacklem	if (nd->nd_repstat == 0) {
4148191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, (3 + 2 * cnt) * NFSX_UNSIGNED);
4149191783Srmacklem		tl += (2 + 2 * cnt);
4150191783Srmacklem		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
4151191783Srmacklem			len > NFSX_FHMAX) {
4152191783Srmacklem			nd->nd_repstat = NFSERR_BADXDR;
4153191783Srmacklem		} else {
4154191783Srmacklem			nd->nd_repstat = nfsrv_mtostr(nd, nmp->nm_fh, len);
4155191783Srmacklem			if (nd->nd_repstat == 0)
4156191783Srmacklem				nmp->nm_fhsize = len;
4157191783Srmacklem		}
4158191783Srmacklem	}
4159191783Srmacklem	error = nd->nd_repstat;
4160191783Srmacklemnfsmout:
4161191783Srmacklem	mbuf_freem(nd->nd_mrep);
4162191783Srmacklem	return (error);
4163191783Srmacklem}
4164191783Srmacklem
4165191783Srmacklem/*
4166191783Srmacklem * This function performs the Delegreturn RPC.
4167191783Srmacklem */
4168191783SrmacklemAPPLESTATIC int
4169191783Srmacklemnfsrpc_delegreturn(struct nfscldeleg *dp, struct ucred *cred,
4170191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p, int syscred)
4171191783Srmacklem{
4172191783Srmacklem	u_int32_t *tl;
4173191783Srmacklem	struct nfsrv_descript nfsd;
4174191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
4175191783Srmacklem	int error;
4176191783Srmacklem
4177191783Srmacklem	nfscl_reqstart(nd, NFSPROC_DELEGRETURN, nmp, dp->nfsdl_fh,
4178191783Srmacklem	    dp->nfsdl_fhlen, NULL);
4179191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
4180191783Srmacklem	*tl++ = dp->nfsdl_stateid.seqid;
4181191783Srmacklem	*tl++ = dp->nfsdl_stateid.other[0];
4182191783Srmacklem	*tl++ = dp->nfsdl_stateid.other[1];
4183191783Srmacklem	*tl = dp->nfsdl_stateid.other[2];
4184191783Srmacklem	if (syscred)
4185191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
4186191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4187191783Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL);
4188191783Srmacklem	if (error)
4189191783Srmacklem		return (error);
4190191783Srmacklem	error = nd->nd_repstat;
4191191783Srmacklem	mbuf_freem(nd->nd_mrep);
4192191783Srmacklem	return (error);
4193191783Srmacklem}
4194191783Srmacklem
4195191783Srmacklem/*
4196191783Srmacklem * nfs getacl call.
4197191783Srmacklem */
4198191783SrmacklemAPPLESTATIC int
4199191783Srmacklemnfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4200191783Srmacklem    struct acl *aclp, void *stuff)
4201191783Srmacklem{
4202191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4203191783Srmacklem	int error;
4204191783Srmacklem	nfsattrbit_t attrbits;
4205191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4206191783Srmacklem
4207191783Srmacklem	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4208191783Srmacklem		return (EOPNOTSUPP);
4209191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_GETACL, vp);
4210191783Srmacklem	NFSZERO_ATTRBIT(&attrbits);
4211191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4212191783Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
4213191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
4214191783Srmacklem	if (error)
4215191783Srmacklem		return (error);
4216191783Srmacklem	if (!nd->nd_repstat)
4217191783Srmacklem		error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
4218191783Srmacklem		    NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, p, cred);
4219191783Srmacklem	else
4220191783Srmacklem		error = nd->nd_repstat;
4221191783Srmacklem	mbuf_freem(nd->nd_mrep);
4222191783Srmacklem	return (error);
4223191783Srmacklem}
4224191783Srmacklem
4225191783Srmacklem/*
4226191783Srmacklem * nfs setacl call.
4227191783Srmacklem */
4228191783SrmacklemAPPLESTATIC int
4229191783Srmacklemnfsrpc_setacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4230191783Srmacklem    struct acl *aclp, void *stuff)
4231191783Srmacklem{
4232191783Srmacklem	int error;
4233191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4234191783Srmacklem
4235191783Srmacklem	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4236191783Srmacklem		return (EOPNOTSUPP);
4237191783Srmacklem	error = nfsrpc_setattr(vp, NULL, aclp, cred, p, NULL, NULL, stuff);
4238191783Srmacklem	return (error);
4239191783Srmacklem}
4240191783Srmacklem
4241191783Srmacklem/*
4242191783Srmacklem * nfs setacl call.
4243191783Srmacklem */
4244191783Srmacklemstatic int
4245191783Srmacklemnfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4246191783Srmacklem    struct acl *aclp, nfsv4stateid_t *stateidp, void *stuff)
4247191783Srmacklem{
4248191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4249191783Srmacklem	int error;
4250191783Srmacklem	nfsattrbit_t attrbits;
4251191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4252191783Srmacklem
4253191783Srmacklem	if (!NFSHASNFSV4(nmp))
4254191783Srmacklem		return (EOPNOTSUPP);
4255191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_SETACL, vp);
4256191783Srmacklem	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
4257191783Srmacklem	NFSZERO_ATTRBIT(&attrbits);
4258191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4259220645Srmacklem	(void) nfsv4_fillattr(nd, vnode_mount(vp), vp, aclp, NULL, NULL, 0,
4260220648Srmacklem	    &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0);
4261191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
4262191783Srmacklem	if (error)
4263191783Srmacklem		return (error);
4264191783Srmacklem	/* Don't care about the pre/postop attributes */
4265191783Srmacklem	mbuf_freem(nd->nd_mrep);
4266191783Srmacklem	return (nd->nd_repstat);
4267191783Srmacklem}
4268