nfs_nfsdserv.c revision 245613
1292915Sdim/*-
2292915Sdim * Copyright (c) 1989, 1993
3292915Sdim *	The Regents of the University of California.  All rights reserved.
4292915Sdim *
5292915Sdim * This code is derived from software contributed to Berkeley by
6292915Sdim * Rick Macklem at The University of Guelph.
7292915Sdim *
8292915Sdim * Redistribution and use in source and binary forms, with or without
9292915Sdim * modification, are permitted provided that the following conditions
10292915Sdim * are met:
11292915Sdim * 1. Redistributions of source code must retain the above copyright
12292915Sdim *    notice, this list of conditions and the following disclaimer.
13292915Sdim * 2. Redistributions in binary form must reproduce the above copyright
14292915Sdim *    notice, this list of conditions and the following disclaimer in the
15292915Sdim *    documentation and/or other materials provided with the distribution.
16292915Sdim * 4. Neither the name of the University nor the names of its contributors
17292915Sdim *    may be used to endorse or promote products derived from this software
18292915Sdim *    without specific prior written permission.
19292915Sdim *
20292915Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21292915Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22292915Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23292915Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24292915Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25292915Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26292915Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27292915Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28292915Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29292915Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30292915Sdim * SUCH DAMAGE.
31292915Sdim *
32292915Sdim */
33292915Sdim
34292915Sdim#include <sys/cdefs.h>
35292915Sdim__FBSDID("$FreeBSD: head/sys/fs/nfsserver/nfs_nfsdserv.c 245613 2013-01-18 19:42:08Z delphij $");
36292915Sdim
37292915Sdim/*
38292915Sdim * nfs version 2, 3 and 4 server calls to vnode ops
39292915Sdim * - these routines generally have 3 phases
40292915Sdim *   1 - break down and validate rpc request in mbuf list
41292915Sdim *   2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
42292915Sdim *       function in nfsd_port.c
43292915Sdim *   3 - build the rpc reply in an mbuf list
44292915Sdim * For nfsv4, these functions are called for each Op within the Compound RPC.
45292915Sdim */
46292915Sdim
47292915Sdim#ifndef APPLEKEXT
48292915Sdim#include <fs/nfs/nfsport.h>
49292915Sdim
50292915Sdim/* Global vars */
51292915Sdimextern u_int32_t newnfs_false, newnfs_true;
52292915Sdimextern enum vtype nv34tov_type[8];
53292915Sdimextern struct timeval nfsboottime;
54292915Sdimextern int nfs_rootfhset;
55292915Sdimextern int nfsrv_enable_crossmntpt;
56292915Sdim#endif	/* !APPLEKEXT */
57292915Sdim
58292915Sdimstatic int	nfs_async = 0;
59292915SdimSYSCTL_DECL(_vfs_nfsd);
60292915SdimSYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
61292915Sdim    "Tell client that writes were synced even though they were not");
62292915Sdim
63292915Sdim/*
64292915Sdim * This list defines the GSS mechanisms supported.
65292915Sdim * (Don't ask me how you get these strings from the RFC stuff like
66292915Sdim *  iso(1), org(3)... but someone did it, so I don't need to know.)
67292915Sdim */
68292915Sdimstatic struct nfsgss_mechlist nfsgss_mechlist[] = {
69292915Sdim	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
70292915Sdim	{ 0, "", 0 },
71292915Sdim};
72292915Sdim
73292915Sdim/* local functions */
74292915Sdimstatic void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
75292915Sdim    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
76292915Sdim    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
77292915Sdim    int *diraft_retp, nfsattrbit_t *attrbitp,
78292915Sdim    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
79292915Sdim    int pathlen);
80292915Sdimstatic void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
81292915Sdim    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
82292915Sdim    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
83292915Sdim    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
84292915Sdim    NFSPROC_T *p, struct nfsexstuff *exp);
85292915Sdim
86292915Sdim/*
87292915Sdim * nfs access service (not a part of NFS V2)
88292915Sdim */
89292915SdimAPPLESTATIC int
90292915Sdimnfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
91292915Sdim    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
92292915Sdim{
93292915Sdim	u_int32_t *tl;
94292915Sdim	int getret, error = 0;
95292915Sdim	struct nfsvattr nva;
96292915Sdim	u_int32_t testmode, nfsmode, supported = 0;
97292915Sdim	accmode_t deletebit;
98292915Sdim
99292915Sdim	if (nd->nd_repstat) {
100292915Sdim		nfsrv_postopattr(nd, 1, &nva);
101292915Sdim		goto out;
102292915Sdim	}
103292915Sdim	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
104292915Sdim	nfsmode = fxdr_unsigned(u_int32_t, *tl);
105292915Sdim	if ((nd->nd_flag & ND_NFSV4) &&
106292915Sdim	    (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
107292915Sdim	     NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
108292915Sdim	     NFSACCESS_EXECUTE))) {
109292915Sdim		nd->nd_repstat = NFSERR_INVAL;
110292915Sdim		vput(vp);
111292915Sdim		goto out;
112292915Sdim	}
113292915Sdim	if (nfsmode & NFSACCESS_READ) {
114292915Sdim		supported |= NFSACCESS_READ;
115292915Sdim		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
116292915Sdim		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
117292915Sdim			nfsmode &= ~NFSACCESS_READ;
118292915Sdim	}
119292915Sdim	if (nfsmode & NFSACCESS_MODIFY) {
120292915Sdim		supported |= NFSACCESS_MODIFY;
121292915Sdim		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
122292915Sdim		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
123292915Sdim			nfsmode &= ~NFSACCESS_MODIFY;
124292915Sdim	}
125292915Sdim	if (nfsmode & NFSACCESS_EXTEND) {
126292915Sdim		supported |= NFSACCESS_EXTEND;
127292915Sdim		if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
128292915Sdim		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
129292915Sdim			nfsmode &= ~NFSACCESS_EXTEND;
130292915Sdim	}
131292915Sdim	if (nfsmode & NFSACCESS_DELETE) {
132292915Sdim		supported |= NFSACCESS_DELETE;
133292915Sdim		if (vp->v_type == VDIR)
134292915Sdim			deletebit = VDELETE_CHILD;
135292915Sdim		else
136292915Sdim			deletebit = VDELETE;
137292915Sdim		if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
138292915Sdim		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
139292915Sdim			nfsmode &= ~NFSACCESS_DELETE;
140292915Sdim	}
141292915Sdim	if (vnode_vtype(vp) == VDIR)
142292915Sdim		testmode = NFSACCESS_LOOKUP;
143292915Sdim	else
144292915Sdim		testmode = NFSACCESS_EXECUTE;
145292915Sdim	if (nfsmode & testmode) {
146292915Sdim		supported |= (nfsmode & testmode);
147292915Sdim		if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
148292915Sdim		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
149292915Sdim			nfsmode &= ~testmode;
150292915Sdim	}
151292915Sdim	nfsmode &= supported;
152292915Sdim	if (nd->nd_flag & ND_NFSV3) {
153292915Sdim		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
154292915Sdim		nfsrv_postopattr(nd, getret, &nva);
155292915Sdim	}
156292915Sdim	vput(vp);
157292915Sdim	if (nd->nd_flag & ND_NFSV4) {
158292915Sdim		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
159292915Sdim		*tl++ = txdr_unsigned(supported);
160292915Sdim	} else
161292915Sdim		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
162292915Sdim	*tl = txdr_unsigned(nfsmode);
163292915Sdim
164292915Sdimout:
165292915Sdim	NFSEXITCODE2(0, nd);
166292915Sdim	return (0);
167292915Sdimnfsmout:
168292915Sdim	vput(vp);
169292915Sdim	NFSEXITCODE2(error, nd);
170292915Sdim	return (error);
171292915Sdim}
172292915Sdim
173292915Sdim/*
174292915Sdim * nfs getattr service
175292915Sdim */
176292915SdimAPPLESTATIC int
177292915Sdimnfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
178292915Sdim    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
179292915Sdim{
180292915Sdim	struct nfsvattr nva;
181292915Sdim	fhandle_t fh;
182292915Sdim	int at_root = 0, error = 0, supports_nfsv4acls;
183292915Sdim	struct nfsreferral *refp;
184292915Sdim	nfsattrbit_t attrbits, tmpbits;
185292915Sdim	struct mount *mp;
186292915Sdim	struct vnode *tvp = NULL;
187292915Sdim	struct vattr va;
188292915Sdim	uint64_t mounted_on_fileno = 0;
189292915Sdim	accmode_t accmode;
190292915Sdim
191292915Sdim	if (nd->nd_repstat)
192292915Sdim		goto out;
193292915Sdim	if (nd->nd_flag & ND_NFSV4) {
194292915Sdim		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
195292915Sdim		if (error) {
196292915Sdim			vput(vp);
197292915Sdim			goto out;
198292915Sdim		}
199292915Sdim
200292915Sdim		/*
201292915Sdim		 * Check for a referral.
202292915Sdim		 */
203292915Sdim		refp = nfsv4root_getreferral(vp, NULL, 0);
204292915Sdim		if (refp != NULL) {
205292915Sdim			(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
206292915Sdim			    &nd->nd_repstat);
207292915Sdim			vput(vp);
208292915Sdim			goto out;
209292915Sdim		}
210292915Sdim		if (nd->nd_repstat == 0) {
211292915Sdim			accmode = 0;
212292915Sdim			NFSSET_ATTRBIT(&tmpbits, &attrbits);
213292915Sdim			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
214292915Sdim				NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
215292915Sdim				accmode |= VREAD_ACL;
216292915Sdim			}
217292915Sdim			if (NFSNONZERO_ATTRBIT(&tmpbits))
218292915Sdim				accmode |= VREAD_ATTRIBUTES;
219292915Sdim			if (accmode != 0)
220292915Sdim				nd->nd_repstat = nfsvno_accchk(vp, accmode,
221292915Sdim				    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
222292915Sdim				    NFSACCCHK_VPISLOCKED, NULL);
223292915Sdim		}
224292915Sdim	}
225292915Sdim	if (!nd->nd_repstat)
226292915Sdim		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
227292915Sdim	if (!nd->nd_repstat) {
228292915Sdim		if (nd->nd_flag & ND_NFSV4) {
229292915Sdim			if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
230292915Sdim				nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
231292915Sdim			if (!nd->nd_repstat)
232292915Sdim				nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
233292915Sdim				    &nva, &attrbits, nd->nd_cred, p);
234292915Sdim			if (nd->nd_repstat == 0) {
235292915Sdim				supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
236292915Sdim				mp = vp->v_mount;
237292915Sdim				if (nfsrv_enable_crossmntpt != 0 &&
238292915Sdim				    vp->v_type == VDIR &&
239292915Sdim				    (vp->v_vflag & VV_ROOT) != 0 &&
240292915Sdim				    vp != rootvnode) {
241292915Sdim					tvp = mp->mnt_vnodecovered;
242292915Sdim					VREF(tvp);
243292915Sdim					at_root = 1;
244292915Sdim				} else
245292915Sdim					at_root = 0;
246292915Sdim				vfs_ref(mp);
247292915Sdim				NFSVOPUNLOCK(vp, 0);
248292915Sdim				if (at_root != 0) {
249292915Sdim					if ((nd->nd_repstat =
250292915Sdim					     NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
251292915Sdim						nd->nd_repstat = VOP_GETATTR(
252292915Sdim						    tvp, &va, nd->nd_cred);
253292915Sdim						vput(tvp);
254292915Sdim					} else
255292915Sdim						vrele(tvp);
256292915Sdim					if (nd->nd_repstat == 0)
257292915Sdim						mounted_on_fileno = (uint64_t)
258292915Sdim						    va.va_fileid;
259292915Sdim					else
260292915Sdim						at_root = 0;
261292915Sdim				}
262292915Sdim				if (nd->nd_repstat == 0)
263292915Sdim					nd->nd_repstat = vfs_busy(mp, 0);
264292915Sdim				vfs_rel(mp);
265292915Sdim				if (nd->nd_repstat == 0) {
266292915Sdim					(void)nfsvno_fillattr(nd, mp, vp, &nva,
267292915Sdim					    &fh, 0, &attrbits, nd->nd_cred, p,
268292915Sdim					    isdgram, 1, supports_nfsv4acls,
269292915Sdim					    at_root, mounted_on_fileno);
270292915Sdim					vfs_unbusy(mp);
271292915Sdim				}
272292915Sdim				vrele(vp);
273292915Sdim			} else
274292915Sdim				vput(vp);
275292915Sdim		} else {
276292915Sdim			nfsrv_fillattr(nd, &nva);
277292915Sdim			vput(vp);
278292915Sdim		}
279292915Sdim	} else {
280292915Sdim		vput(vp);
281292915Sdim	}
282292915Sdim
283292915Sdimout:
284292915Sdim	NFSEXITCODE2(error, nd);
285292915Sdim	return (error);
286292915Sdim}
287292915Sdim
288292915Sdim/*
289292915Sdim * nfs setattr service
290292915Sdim */
291292915SdimAPPLESTATIC int
292292915Sdimnfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
293292915Sdim    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
294292915Sdim{
295292915Sdim	struct nfsvattr nva, nva2;
296292915Sdim	u_int32_t *tl;
297292915Sdim	int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
298292915Sdim	struct timespec guard = { 0, 0 };
299292915Sdim	nfsattrbit_t attrbits, retbits;
300292915Sdim	nfsv4stateid_t stateid;
301292915Sdim	NFSACL_T *aclp = NULL;
302292915Sdim
303292915Sdim	if (nd->nd_repstat) {
304292915Sdim		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
305292915Sdim		goto out;
306292915Sdim	}
307292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
308292915Sdim	aclp = acl_alloc(M_WAITOK);
309292915Sdim	aclp->acl_cnt = 0;
310292915Sdim#endif
311292915Sdim	NFSVNO_ATTRINIT(&nva);
312292915Sdim	NFSZERO_ATTRBIT(&retbits);
313292915Sdim	if (nd->nd_flag & ND_NFSV4) {
314292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
315292915Sdim		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
316292915Sdim		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
317292915Sdim	}
318292915Sdim	error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
319292915Sdim	if (error)
320292915Sdim		goto nfsmout;
321292915Sdim	preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p, 1);
322292915Sdim	if (!nd->nd_repstat)
323292915Sdim		nd->nd_repstat = preat_ret;
324292915Sdim	if (nd->nd_flag & ND_NFSV3) {
325292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
326292915Sdim		gcheck = fxdr_unsigned(int, *tl);
327292915Sdim		if (gcheck) {
328292915Sdim			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
329292915Sdim			fxdr_nfsv3time(tl, &guard);
330292915Sdim		}
331292915Sdim		if (!nd->nd_repstat && gcheck &&
332292915Sdim		    (nva2.na_ctime.tv_sec != guard.tv_sec ||
333292915Sdim		     nva2.na_ctime.tv_nsec != guard.tv_nsec))
334292915Sdim			nd->nd_repstat = NFSERR_NOT_SYNC;
335292915Sdim		if (nd->nd_repstat) {
336292915Sdim			vput(vp);
337292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
338292915Sdim			acl_free(aclp);
339292915Sdim#endif
340292915Sdim			nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
341292915Sdim			goto out;
342292915Sdim		}
343292915Sdim	} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
344292915Sdim		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
345292915Sdim
346292915Sdim	/*
347292915Sdim	 * Now that we have all the fields, lets do it.
348292915Sdim	 * If the size is being changed write access is required, otherwise
349292915Sdim	 * just check for a read only file system.
350292915Sdim	 */
351292915Sdim	if (!nd->nd_repstat) {
352292915Sdim		if (NFSVNO_NOTSETSIZE(&nva)) {
353292915Sdim			if (NFSVNO_EXRDONLY(exp) ||
354292915Sdim			    (vfs_flags(vnode_mount(vp)) & MNT_RDONLY))
355292915Sdim				nd->nd_repstat = EROFS;
356292915Sdim		} else {
357292915Sdim			if (vnode_vtype(vp) != VREG)
358292915Sdim				nd->nd_repstat = EINVAL;
359292915Sdim			else if (nva2.na_uid != nd->nd_cred->cr_uid ||
360292915Sdim			    NFSVNO_EXSTRICTACCESS(exp))
361292915Sdim				nd->nd_repstat = nfsvno_accchk(vp,
362292915Sdim				    VWRITE, nd->nd_cred, exp, p,
363292915Sdim				    NFSACCCHK_NOOVERRIDE,
364292915Sdim				    NFSACCCHK_VPISLOCKED, NULL);
365292915Sdim		}
366292915Sdim	}
367292915Sdim	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
368292915Sdim		nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
369292915Sdim		    &nva, &attrbits, exp, p);
370292915Sdim
371292915Sdim	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
372292915Sdim	    /*
373292915Sdim	     * For V4, try setting the attrbutes in sets, so that the
374292915Sdim	     * reply bitmap will be correct for an error case.
375292915Sdim	     */
376292915Sdim	    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
377292915Sdim		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
378292915Sdim		NFSVNO_ATTRINIT(&nva2);
379292915Sdim		NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
380292915Sdim		NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
381292915Sdim		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
382292915Sdim		    exp);
383292915Sdim		if (!nd->nd_repstat) {
384292915Sdim		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
385292915Sdim			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
386292915Sdim		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
387292915Sdim			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
388292915Sdim		}
389292915Sdim	    }
390292915Sdim	    if (!nd->nd_repstat &&
391292915Sdim		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
392292915Sdim		NFSVNO_ATTRINIT(&nva2);
393292915Sdim		NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
394292915Sdim		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
395292915Sdim		    exp);
396292915Sdim		if (!nd->nd_repstat)
397292915Sdim		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
398292915Sdim	    }
399292915Sdim	    if (!nd->nd_repstat &&
400292915Sdim		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
401292915Sdim		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
402292915Sdim		NFSVNO_ATTRINIT(&nva2);
403292915Sdim		NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
404292915Sdim		NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
405292915Sdim		if (nva.na_vaflags & VA_UTIMES_NULL) {
406292915Sdim			nva2.na_vaflags |= VA_UTIMES_NULL;
407292915Sdim			NFSVNO_SETACTIVE(&nva2, vaflags);
408292915Sdim		}
409292915Sdim		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
410292915Sdim		    exp);
411292915Sdim		if (!nd->nd_repstat) {
412292915Sdim		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
413292915Sdim			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
414292915Sdim		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
415292915Sdim			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
416292915Sdim		}
417292915Sdim	    }
418292915Sdim	    if (!nd->nd_repstat &&
419292915Sdim		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE)) {
420292915Sdim		NFSVNO_ATTRINIT(&nva2);
421292915Sdim		NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
422292915Sdim		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
423292915Sdim		    exp);
424292915Sdim		if (!nd->nd_repstat)
425292915Sdim		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
426292915Sdim	    }
427292915Sdim
428292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
429292915Sdim	    if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
430292915Sdim		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
431292915Sdim		nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
432292915Sdim		if (!nd->nd_repstat)
433292915Sdim		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
434292915Sdim	    }
435292915Sdim#endif
436292915Sdim	} else if (!nd->nd_repstat) {
437292915Sdim		nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
438292915Sdim		    exp);
439292915Sdim	}
440292915Sdim	if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
441292915Sdim		postat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
442292915Sdim		if (!nd->nd_repstat)
443292915Sdim			nd->nd_repstat = postat_ret;
444292915Sdim	}
445292915Sdim	vput(vp);
446292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
447292915Sdim	acl_free(aclp);
448292915Sdim#endif
449292915Sdim	if (nd->nd_flag & ND_NFSV3)
450292915Sdim		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
451292915Sdim	else if (nd->nd_flag & ND_NFSV4)
452292915Sdim		(void) nfsrv_putattrbit(nd, &retbits);
453292915Sdim	else if (!nd->nd_repstat)
454292915Sdim		nfsrv_fillattr(nd, &nva);
455292915Sdim
456292915Sdimout:
457292915Sdim	NFSEXITCODE2(0, nd);
458292915Sdim	return (0);
459292915Sdimnfsmout:
460292915Sdim	vput(vp);
461292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
462292915Sdim	acl_free(aclp);
463292915Sdim#endif
464292915Sdim	if (nd->nd_flag & ND_NFSV4) {
465292915Sdim		/*
466292915Sdim		 * For all nd_repstat, the V4 reply includes a bitmap,
467292915Sdim		 * even NFSERR_BADXDR, which is what this will end up
468292915Sdim		 * returning.
469292915Sdim		 */
470292915Sdim		(void) nfsrv_putattrbit(nd, &retbits);
471292915Sdim	}
472292915Sdim	NFSEXITCODE2(error, nd);
473292915Sdim	return (error);
474292915Sdim}
475292915Sdim
476292915Sdim/*
477292915Sdim * nfs lookup rpc
478292915Sdim * (Also performs lookup parent for v4)
479292915Sdim */
480292915SdimAPPLESTATIC int
481292915Sdimnfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
482292915Sdim    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
483292915Sdim    struct nfsexstuff *exp)
484292915Sdim{
485292915Sdim	struct nameidata named;
486292915Sdim	vnode_t vp, dirp = NULL;
487292915Sdim	int error = 0, dattr_ret = 1;
488292915Sdim	struct nfsvattr nva, dattr;
489292915Sdim	char *bufp;
490292915Sdim	u_long *hashp;
491292915Sdim
492292915Sdim	if (nd->nd_repstat) {
493292915Sdim		nfsrv_postopattr(nd, dattr_ret, &dattr);
494292915Sdim		goto out;
495292915Sdim	}
496292915Sdim
497292915Sdim	/*
498292915Sdim	 * For some reason, if dp is a symlink, the error
499292915Sdim	 * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
500292915Sdim	 */
501292915Sdim	if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
502292915Sdim		nd->nd_repstat = NFSERR_SYMLINK;
503292915Sdim		vrele(dp);
504292915Sdim		goto out;
505292915Sdim	}
506292915Sdim
507292915Sdim	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
508292915Sdim	    LOCKLEAF | SAVESTART);
509292915Sdim	nfsvno_setpathbuf(&named, &bufp, &hashp);
510292915Sdim	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
511292915Sdim	if (error) {
512292915Sdim		vrele(dp);
513292915Sdim		nfsvno_relpathbuf(&named);
514292915Sdim		goto out;
515292915Sdim	}
516292915Sdim	if (!nd->nd_repstat) {
517292915Sdim		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
518292915Sdim	} else {
519292915Sdim		vrele(dp);
520292915Sdim		nfsvno_relpathbuf(&named);
521292915Sdim	}
522292915Sdim	if (nd->nd_repstat) {
523292915Sdim		if (dirp) {
524292915Sdim			if (nd->nd_flag & ND_NFSV3)
525292915Sdim				dattr_ret = nfsvno_getattr(dirp, &dattr,
526292915Sdim				    nd->nd_cred, p, 0);
527292915Sdim			vrele(dirp);
528292915Sdim		}
529292915Sdim		if (nd->nd_flag & ND_NFSV3)
530292915Sdim			nfsrv_postopattr(nd, dattr_ret, &dattr);
531292915Sdim		goto out;
532292915Sdim	}
533292915Sdim	if (named.ni_startdir)
534292915Sdim		vrele(named.ni_startdir);
535292915Sdim	nfsvno_relpathbuf(&named);
536292915Sdim	vp = named.ni_vp;
537292915Sdim	if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
538292915Sdim	    vp->v_type != VDIR && vp->v_type != VLNK)
539292915Sdim		/*
540292915Sdim		 * Only allow lookup of VDIR and VLNK for traversal of
541292915Sdim		 * non-exported volumes during NFSv4 mounting.
542292915Sdim		 */
543292915Sdim		nd->nd_repstat = ENOENT;
544292915Sdim	if (nd->nd_repstat == 0)
545292915Sdim		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
546292915Sdim	if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
547292915Sdim		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
548292915Sdim	if (vpp != NULL && nd->nd_repstat == 0)
549292915Sdim		*vpp = vp;
550292915Sdim	else
551292915Sdim		vput(vp);
552292915Sdim	if (dirp) {
553292915Sdim		if (nd->nd_flag & ND_NFSV3)
554292915Sdim			dattr_ret = nfsvno_getattr(dirp, &dattr, nd->nd_cred,
555292915Sdim			    p, 0);
556292915Sdim		vrele(dirp);
557292915Sdim	}
558292915Sdim	if (nd->nd_repstat) {
559292915Sdim		if (nd->nd_flag & ND_NFSV3)
560292915Sdim			nfsrv_postopattr(nd, dattr_ret, &dattr);
561292915Sdim		goto out;
562292915Sdim	}
563292915Sdim	if (nd->nd_flag & ND_NFSV2) {
564292915Sdim		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
565292915Sdim		nfsrv_fillattr(nd, &nva);
566292915Sdim	} else if (nd->nd_flag & ND_NFSV3) {
567292915Sdim		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
568292915Sdim		nfsrv_postopattr(nd, 0, &nva);
569292915Sdim		nfsrv_postopattr(nd, dattr_ret, &dattr);
570292915Sdim	}
571292915Sdim
572292915Sdimout:
573292915Sdim	NFSEXITCODE2(error, nd);
574292915Sdim	return (error);
575292915Sdim}
576292915Sdim
577292915Sdim/*
578292915Sdim * nfs readlink service
579292915Sdim */
580292915SdimAPPLESTATIC int
581292915Sdimnfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
582292915Sdim    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
583292915Sdim{
584292915Sdim	u_int32_t *tl;
585292915Sdim	mbuf_t mp = NULL, mpend = NULL;
586292915Sdim	int getret = 1, len;
587292915Sdim	struct nfsvattr nva;
588292915Sdim
589292915Sdim	if (nd->nd_repstat) {
590292915Sdim		nfsrv_postopattr(nd, getret, &nva);
591292915Sdim		goto out;
592292915Sdim	}
593292915Sdim	if (vnode_vtype(vp) != VLNK) {
594292915Sdim		if (nd->nd_flag & ND_NFSV2)
595292915Sdim			nd->nd_repstat = ENXIO;
596292915Sdim		else
597292915Sdim			nd->nd_repstat = EINVAL;
598292915Sdim	}
599292915Sdim	if (!nd->nd_repstat)
600292915Sdim		nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p,
601292915Sdim		    &mp, &mpend, &len);
602292915Sdim	if (nd->nd_flag & ND_NFSV3)
603292915Sdim		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
604292915Sdim	vput(vp);
605292915Sdim	if (nd->nd_flag & ND_NFSV3)
606292915Sdim		nfsrv_postopattr(nd, getret, &nva);
607292915Sdim	if (nd->nd_repstat)
608292915Sdim		goto out;
609292915Sdim	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
610292915Sdim	*tl = txdr_unsigned(len);
611292915Sdim	mbuf_setnext(nd->nd_mb, mp);
612292915Sdim	nd->nd_mb = mpend;
613292915Sdim	nd->nd_bpos = NFSMTOD(mpend, caddr_t) + mbuf_len(mpend);
614292915Sdim
615292915Sdimout:
616292915Sdim	NFSEXITCODE2(0, nd);
617292915Sdim	return (0);
618292915Sdim}
619292915Sdim
620292915Sdim/*
621292915Sdim * nfs read service
622292915Sdim */
623292915SdimAPPLESTATIC int
624292915Sdimnfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
625292915Sdim    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
626292915Sdim{
627292915Sdim	u_int32_t *tl;
628292915Sdim	int error = 0, cnt, getret = 1, reqlen, eof = 0;
629292915Sdim	mbuf_t m2, m3;
630292915Sdim	struct nfsvattr nva;
631292915Sdim	off_t off = 0x0;
632292915Sdim	struct nfsstate st, *stp = &st;
633292915Sdim	struct nfslock lo, *lop = &lo;
634292915Sdim	nfsv4stateid_t stateid;
635292915Sdim	nfsquad_t clientid;
636292915Sdim
637292915Sdim	if (nd->nd_repstat) {
638292915Sdim		nfsrv_postopattr(nd, getret, &nva);
639292915Sdim		goto out;
640292915Sdim	}
641292915Sdim	if (nd->nd_flag & ND_NFSV2) {
642292915Sdim		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
643292915Sdim		off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
644292915Sdim		reqlen = fxdr_unsigned(int, *tl);
645292915Sdim	} else if (nd->nd_flag & ND_NFSV3) {
646292915Sdim		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
647292915Sdim		off = fxdr_hyper(tl);
648292915Sdim		tl += 2;
649292915Sdim		reqlen = fxdr_unsigned(int, *tl);
650292915Sdim	} else {
651292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
652292915Sdim		reqlen = fxdr_unsigned(int, *(tl + 6));
653292915Sdim	}
654292915Sdim	if (reqlen > NFS_SRVMAXDATA(nd)) {
655292915Sdim		reqlen = NFS_SRVMAXDATA(nd);
656292915Sdim	} else if (reqlen < 0) {
657292915Sdim		error = EBADRPC;
658292915Sdim		goto nfsmout;
659292915Sdim	}
660292915Sdim	if (nd->nd_flag & ND_NFSV4) {
661292915Sdim		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
662292915Sdim		lop->lo_flags = NFSLCK_READ;
663292915Sdim		stp->ls_ownerlen = 0;
664292915Sdim		stp->ls_op = NULL;
665292915Sdim		stp->ls_uid = nd->nd_cred->cr_uid;
666292915Sdim		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
667292915Sdim		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
668292915Sdim		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
669292915Sdim		if (nd->nd_flag & ND_IMPLIEDCLID) {
670292915Sdim			if (nd->nd_clientid.qval != clientid.qval)
671292915Sdim				printf("EEK! multiple clids\n");
672292915Sdim		} else {
673292915Sdim			nd->nd_flag |= ND_IMPLIEDCLID;
674292915Sdim			nd->nd_clientid.qval = clientid.qval;
675292915Sdim		}
676292915Sdim		stp->ls_stateid.other[2] = *tl++;
677292915Sdim		off = fxdr_hyper(tl);
678292915Sdim		lop->lo_first = off;
679292915Sdim		tl += 2;
680292915Sdim		lop->lo_end = off + reqlen;
681292915Sdim		/*
682292915Sdim		 * Paranoia, just in case it wraps around.
683292915Sdim		 */
684292915Sdim		if (lop->lo_end < off)
685292915Sdim			lop->lo_end = NFS64BITSSET;
686292915Sdim	}
687292915Sdim	if (vnode_vtype(vp) != VREG) {
688292915Sdim		if (nd->nd_flag & ND_NFSV3)
689292915Sdim			nd->nd_repstat = EINVAL;
690292915Sdim		else
691292915Sdim			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
692292915Sdim			    EINVAL;
693292915Sdim	}
694292915Sdim	getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
695292915Sdim	if (!nd->nd_repstat)
696292915Sdim		nd->nd_repstat = getret;
697292915Sdim	if (!nd->nd_repstat &&
698292915Sdim	    (nva.na_uid != nd->nd_cred->cr_uid ||
699292915Sdim	     NFSVNO_EXSTRICTACCESS(exp))) {
700292915Sdim		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
701292915Sdim		    nd->nd_cred, exp, p,
702292915Sdim		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
703292915Sdim		if (nd->nd_repstat)
704292915Sdim			nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
705292915Sdim			    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
706292915Sdim			    NFSACCCHK_VPISLOCKED, NULL);
707292915Sdim	}
708292915Sdim	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
709292915Sdim		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
710292915Sdim		    &stateid, exp, nd, p);
711292915Sdim	if (nd->nd_repstat) {
712292915Sdim		vput(vp);
713292915Sdim		if (nd->nd_flag & ND_NFSV3)
714292915Sdim			nfsrv_postopattr(nd, getret, &nva);
715292915Sdim		goto out;
716292915Sdim	}
717292915Sdim	if (off >= nva.na_size) {
718292915Sdim		cnt = 0;
719292915Sdim		eof = 1;
720292915Sdim	} else if (reqlen == 0)
721292915Sdim		cnt = 0;
722292915Sdim	else if ((off + reqlen) >= nva.na_size) {
723292915Sdim		cnt = nva.na_size - off;
724292915Sdim		eof = 1;
725292915Sdim	} else
726292915Sdim		cnt = reqlen;
727292915Sdim	m3 = NULL;
728292915Sdim	if (cnt > 0) {
729292915Sdim		nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p,
730292915Sdim		    &m3, &m2);
731292915Sdim		if (!(nd->nd_flag & ND_NFSV4)) {
732292915Sdim			getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
733292915Sdim			if (!nd->nd_repstat)
734292915Sdim				nd->nd_repstat = getret;
735292915Sdim		}
736292915Sdim		if (nd->nd_repstat) {
737292915Sdim			vput(vp);
738292915Sdim			if (m3)
739292915Sdim				mbuf_freem(m3);
740292915Sdim			if (nd->nd_flag & ND_NFSV3)
741292915Sdim				nfsrv_postopattr(nd, getret, &nva);
742292915Sdim			goto out;
743292915Sdim		}
744292915Sdim	}
745292915Sdim	vput(vp);
746292915Sdim	if (nd->nd_flag & ND_NFSV2) {
747292915Sdim		nfsrv_fillattr(nd, &nva);
748292915Sdim		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
749292915Sdim	} else {
750292915Sdim		if (nd->nd_flag & ND_NFSV3) {
751292915Sdim			nfsrv_postopattr(nd, getret, &nva);
752292915Sdim			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
753292915Sdim			*tl++ = txdr_unsigned(cnt);
754292915Sdim		} else
755292915Sdim			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
756292915Sdim		if (eof)
757292915Sdim			*tl++ = newnfs_true;
758292915Sdim		else
759292915Sdim			*tl++ = newnfs_false;
760292915Sdim	}
761292915Sdim	*tl = txdr_unsigned(cnt);
762292915Sdim	if (m3) {
763292915Sdim		mbuf_setnext(nd->nd_mb, m3);
764292915Sdim		nd->nd_mb = m2;
765292915Sdim		nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
766292915Sdim	}
767292915Sdim
768292915Sdimout:
769292915Sdim	NFSEXITCODE2(0, nd);
770292915Sdim	return (0);
771292915Sdimnfsmout:
772292915Sdim	vput(vp);
773292915Sdim	NFSEXITCODE2(error, nd);
774292915Sdim	return (error);
775292915Sdim}
776292915Sdim
777292915Sdim/*
778292915Sdim * nfs write service
779292915Sdim */
780292915SdimAPPLESTATIC int
781292915Sdimnfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
782292915Sdim    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
783292915Sdim{
784292915Sdim	int i, cnt;
785292915Sdim	u_int32_t *tl;
786292915Sdim	mbuf_t mp;
787292915Sdim	struct nfsvattr nva, forat;
788292915Sdim	int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
789292915Sdim	int stable = NFSWRITE_FILESYNC;
790292915Sdim	off_t off;
791292915Sdim	struct nfsstate st, *stp = &st;
792292915Sdim	struct nfslock lo, *lop = &lo;
793292915Sdim	nfsv4stateid_t stateid;
794292915Sdim	nfsquad_t clientid;
795292915Sdim
796292915Sdim	if (nd->nd_repstat) {
797292915Sdim		nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
798292915Sdim		goto out;
799292915Sdim	}
800292915Sdim	if (nd->nd_flag & ND_NFSV2) {
801292915Sdim		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
802292915Sdim		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
803292915Sdim		tl += 2;
804292915Sdim		retlen = len = fxdr_unsigned(int32_t, *tl);
805292915Sdim	} else if (nd->nd_flag & ND_NFSV3) {
806292915Sdim		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
807292915Sdim		off = fxdr_hyper(tl);
808292915Sdim		tl += 3;
809292915Sdim		stable = fxdr_unsigned(int, *tl++);
810292915Sdim		retlen = len = fxdr_unsigned(int32_t, *tl);
811292915Sdim	} else {
812292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
813292915Sdim		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
814292915Sdim		lop->lo_flags = NFSLCK_WRITE;
815292915Sdim		stp->ls_ownerlen = 0;
816292915Sdim		stp->ls_op = NULL;
817292915Sdim		stp->ls_uid = nd->nd_cred->cr_uid;
818292915Sdim		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
819292915Sdim		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
820292915Sdim		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
821292915Sdim		if (nd->nd_flag & ND_IMPLIEDCLID) {
822292915Sdim			if (nd->nd_clientid.qval != clientid.qval)
823292915Sdim				printf("EEK! multiple clids\n");
824292915Sdim		} else {
825292915Sdim			nd->nd_flag |= ND_IMPLIEDCLID;
826292915Sdim			nd->nd_clientid.qval = clientid.qval;
827292915Sdim		}
828292915Sdim		stp->ls_stateid.other[2] = *tl++;
829292915Sdim		off = fxdr_hyper(tl);
830292915Sdim		lop->lo_first = off;
831292915Sdim		tl += 2;
832292915Sdim		stable = fxdr_unsigned(int, *tl++);
833292915Sdim		retlen = len = fxdr_unsigned(int32_t, *tl);
834292915Sdim		lop->lo_end = off + len;
835292915Sdim		/*
836292915Sdim		 * Paranoia, just in case it wraps around, which shouldn't
837292915Sdim		 * ever happen anyhow.
838292915Sdim		 */
839292915Sdim		if (lop->lo_end < lop->lo_first)
840292915Sdim			lop->lo_end = NFS64BITSSET;
841292915Sdim	}
842292915Sdim
843292915Sdim	/*
844292915Sdim	 * Loop through the mbuf chain, counting how many mbufs are a
845292915Sdim	 * part of this write operation, so the iovec size is known.
846292915Sdim	 */
847292915Sdim	cnt = 0;
848292915Sdim	mp = nd->nd_md;
849292915Sdim	i = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - nd->nd_dpos;
850292915Sdim	while (len > 0) {
851292915Sdim		if (i > 0) {
852292915Sdim			len -= i;
853292915Sdim			cnt++;
854292915Sdim		}
855292915Sdim		mp = mbuf_next(mp);
856292915Sdim		if (!mp) {
857292915Sdim			if (len > 0) {
858292915Sdim				error = EBADRPC;
859292915Sdim				goto nfsmout;
860292915Sdim			}
861292915Sdim		} else
862292915Sdim			i = mbuf_len(mp);
863292915Sdim	}
864292915Sdim
865292915Sdim	if (retlen > NFS_MAXDATA || retlen < 0)
866292915Sdim		nd->nd_repstat = EIO;
867292915Sdim	if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
868292915Sdim		if (nd->nd_flag & ND_NFSV3)
869292915Sdim			nd->nd_repstat = EINVAL;
870292915Sdim		else
871292915Sdim			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
872292915Sdim			    EINVAL;
873292915Sdim	}
874292915Sdim	forat_ret = nfsvno_getattr(vp, &forat, nd->nd_cred, p, 1);
875292915Sdim	if (!nd->nd_repstat)
876292915Sdim		nd->nd_repstat = forat_ret;
877292915Sdim	if (!nd->nd_repstat &&
878292915Sdim	    (forat.na_uid != nd->nd_cred->cr_uid ||
879292915Sdim	     NFSVNO_EXSTRICTACCESS(exp)))
880292915Sdim		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
881292915Sdim		    nd->nd_cred, exp, p,
882292915Sdim		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
883292915Sdim	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
884292915Sdim		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
885292915Sdim		    &stateid, exp, nd, p);
886292915Sdim	}
887292915Sdim	if (nd->nd_repstat) {
888292915Sdim		vput(vp);
889292915Sdim		if (nd->nd_flag & ND_NFSV3)
890292915Sdim			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
891292915Sdim		goto out;
892292915Sdim	}
893292915Sdim
894292915Sdim	/*
895292915Sdim	 * For NFS Version 2, it is not obvious what a write of zero length
896292915Sdim	 * should do, but I might as well be consistent with Version 3,
897292915Sdim	 * which is to return ok so long as there are no permission problems.
898292915Sdim	 */
899292915Sdim	if (retlen > 0) {
900292915Sdim		nd->nd_repstat = nfsvno_write(vp, off, retlen, cnt, stable,
901292915Sdim		    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
902292915Sdim		error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
903292915Sdim		if (error)
904292915Sdim			panic("nfsrv_write mbuf");
905292915Sdim	}
906292915Sdim	if (nd->nd_flag & ND_NFSV4)
907292915Sdim		aftat_ret = 0;
908292915Sdim	else
909292915Sdim		aftat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
910292915Sdim	vput(vp);
911292915Sdim	if (!nd->nd_repstat)
912292915Sdim		nd->nd_repstat = aftat_ret;
913292915Sdim	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
914292915Sdim		if (nd->nd_flag & ND_NFSV3)
915292915Sdim			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
916292915Sdim		if (nd->nd_repstat)
917292915Sdim			goto out;
918292915Sdim		NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
919292915Sdim		*tl++ = txdr_unsigned(retlen);
920292915Sdim		/*
921292915Sdim		 * If nfs_async is set, then pretend the write was FILESYNC.
922292915Sdim		 * Warning: Doing this violates RFC1813 and runs a risk
923292915Sdim		 * of data written by a client being lost when the server
924292915Sdim		 * crashes/reboots.
925292915Sdim		 */
926292915Sdim		if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
927292915Sdim			*tl++ = txdr_unsigned(stable);
928292915Sdim		else
929292915Sdim			*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
930292915Sdim		/*
931292915Sdim		 * Actually, there is no need to txdr these fields,
932292915Sdim		 * but it may make the values more human readable,
933292915Sdim		 * for debugging purposes.
934292915Sdim		 */
935292915Sdim		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
936292915Sdim		*tl = txdr_unsigned(nfsboottime.tv_usec);
937292915Sdim	} else if (!nd->nd_repstat)
938292915Sdim		nfsrv_fillattr(nd, &nva);
939292915Sdim
940292915Sdimout:
941292915Sdim	NFSEXITCODE2(0, nd);
942292915Sdim	return (0);
943292915Sdimnfsmout:
944292915Sdim	vput(vp);
945292915Sdim	NFSEXITCODE2(error, nd);
946292915Sdim	return (error);
947292915Sdim}
948292915Sdim
949292915Sdim/*
950292915Sdim * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
951292915Sdim * now does a truncate to 0 length via. setattr if it already exists
952292915Sdim * The core creation routine has been extracted out into nfsrv_creatsub(),
953292915Sdim * so it can also be used by nfsrv_open() for V4.
954292915Sdim */
955292915SdimAPPLESTATIC int
956292915Sdimnfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
957292915Sdim    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
958292915Sdim{
959292915Sdim	struct nfsvattr nva, dirfor, diraft;
960292915Sdim	struct nfsv2_sattr *sp;
961292915Sdim	struct nameidata named;
962292915Sdim	u_int32_t *tl;
963292915Sdim	int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
964292915Sdim	int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
965292915Sdim	NFSDEV_T rdev = 0;
966292915Sdim	vnode_t vp = NULL, dirp = NULL;
967292915Sdim	fhandle_t fh;
968292915Sdim	char *bufp;
969292915Sdim	u_long *hashp;
970292915Sdim	enum vtype vtyp;
971292915Sdim	int32_t cverf[2], tverf[2] = { 0, 0 };
972292915Sdim
973292915Sdim	if (nd->nd_repstat) {
974292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
975292915Sdim		goto out;
976292915Sdim	}
977292915Sdim	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
978292915Sdim	    LOCKPARENT | LOCKLEAF | SAVESTART);
979292915Sdim	nfsvno_setpathbuf(&named, &bufp, &hashp);
980292915Sdim	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
981292915Sdim	if (error)
982292915Sdim		goto nfsmout;
983292915Sdim	if (!nd->nd_repstat) {
984292915Sdim		NFSVNO_ATTRINIT(&nva);
985292915Sdim		if (nd->nd_flag & ND_NFSV2) {
986292915Sdim			NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
987292915Sdim			vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
988292915Sdim			if (vtyp == VNON)
989292915Sdim				vtyp = VREG;
990292915Sdim			NFSVNO_SETATTRVAL(&nva, type, vtyp);
991292915Sdim			NFSVNO_SETATTRVAL(&nva, mode,
992292915Sdim			    nfstov_mode(sp->sa_mode));
993292915Sdim			switch (nva.na_type) {
994292915Sdim			case VREG:
995292915Sdim				tsize = fxdr_unsigned(int32_t, sp->sa_size);
996292915Sdim				if (tsize != -1)
997292915Sdim					NFSVNO_SETATTRVAL(&nva, size,
998292915Sdim					    (u_quad_t)tsize);
999292915Sdim				break;
1000292915Sdim			case VCHR:
1001292915Sdim			case VBLK:
1002292915Sdim			case VFIFO:
1003292915Sdim				rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
1004292915Sdim				break;
1005292915Sdim			default:
1006292915Sdim				break;
1007292915Sdim			};
1008292915Sdim		} else {
1009292915Sdim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1010292915Sdim			how = fxdr_unsigned(int, *tl);
1011292915Sdim			switch (how) {
1012292915Sdim			case NFSCREATE_GUARDED:
1013292915Sdim			case NFSCREATE_UNCHECKED:
1014292915Sdim				error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
1015292915Sdim				if (error)
1016292915Sdim					goto nfsmout;
1017292915Sdim				break;
1018292915Sdim			case NFSCREATE_EXCLUSIVE:
1019292915Sdim				NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
1020292915Sdim				cverf[0] = *tl++;
1021292915Sdim				cverf[1] = *tl;
1022292915Sdim				exclusive_flag = 1;
1023292915Sdim				break;
1024292915Sdim			};
1025292915Sdim			NFSVNO_SETATTRVAL(&nva, type, VREG);
1026292915Sdim		}
1027292915Sdim	}
1028292915Sdim	if (nd->nd_repstat) {
1029292915Sdim		nfsvno_relpathbuf(&named);
1030292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1031292915Sdim			dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred,
1032292915Sdim			    p, 1);
1033292915Sdim			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1034292915Sdim			    &diraft);
1035292915Sdim		}
1036292915Sdim		vput(dp);
1037292915Sdim		goto out;
1038292915Sdim	}
1039292915Sdim
1040292915Sdim	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1041292915Sdim	if (dirp) {
1042292915Sdim		if (nd->nd_flag & ND_NFSV2) {
1043292915Sdim			vrele(dirp);
1044292915Sdim			dirp = NULL;
1045292915Sdim		} else {
1046292915Sdim			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1047292915Sdim			    p, 0);
1048292915Sdim		}
1049292915Sdim	}
1050292915Sdim	if (nd->nd_repstat) {
1051292915Sdim		if (nd->nd_flag & ND_NFSV3)
1052292915Sdim			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1053292915Sdim			    &diraft);
1054292915Sdim		if (dirp)
1055292915Sdim			vrele(dirp);
1056292915Sdim		goto out;
1057292915Sdim	}
1058292915Sdim
1059292915Sdim	if (!(nd->nd_flag & ND_NFSV2)) {
1060292915Sdim		switch (how) {
1061292915Sdim		case NFSCREATE_GUARDED:
1062292915Sdim			if (named.ni_vp)
1063292915Sdim				nd->nd_repstat = EEXIST;
1064292915Sdim			break;
1065292915Sdim		case NFSCREATE_UNCHECKED:
1066292915Sdim			break;
1067292915Sdim		case NFSCREATE_EXCLUSIVE:
1068292915Sdim			if (named.ni_vp == NULL)
1069292915Sdim				NFSVNO_SETATTRVAL(&nva, mode, 0);
1070292915Sdim			break;
1071292915Sdim		};
1072292915Sdim	}
1073292915Sdim
1074292915Sdim	/*
1075292915Sdim	 * Iff doesn't exist, create it
1076292915Sdim	 * otherwise just truncate to 0 length
1077292915Sdim	 *   should I set the mode too ?
1078292915Sdim	 */
1079292915Sdim	nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
1080292915Sdim	    &exclusive_flag, cverf, rdev, p, exp);
1081292915Sdim
1082292915Sdim	if (!nd->nd_repstat) {
1083292915Sdim		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
1084292915Sdim		if (!nd->nd_repstat)
1085292915Sdim			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1086292915Sdim			    p, 1);
1087292915Sdim		vput(vp);
1088292915Sdim		if (!nd->nd_repstat) {
1089292915Sdim			tverf[0] = nva.na_atime.tv_sec;
1090292915Sdim			tverf[1] = nva.na_atime.tv_nsec;
1091292915Sdim		}
1092292915Sdim	}
1093292915Sdim	if (nd->nd_flag & ND_NFSV2) {
1094292915Sdim		if (!nd->nd_repstat) {
1095292915Sdim			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
1096292915Sdim			nfsrv_fillattr(nd, &nva);
1097292915Sdim		}
1098292915Sdim	} else {
1099292915Sdim		if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
1100292915Sdim		    || cverf[1] != tverf[1]))
1101292915Sdim			nd->nd_repstat = EEXIST;
1102292915Sdim		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1103292915Sdim		vrele(dirp);
1104292915Sdim		if (!nd->nd_repstat) {
1105292915Sdim			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
1106292915Sdim			nfsrv_postopattr(nd, 0, &nva);
1107292915Sdim		}
1108292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1109292915Sdim	}
1110292915Sdim
1111292915Sdimout:
1112292915Sdim	NFSEXITCODE2(0, nd);
1113292915Sdim	return (0);
1114292915Sdimnfsmout:
1115292915Sdim	vput(dp);
1116292915Sdim	nfsvno_relpathbuf(&named);
1117292915Sdim	NFSEXITCODE2(error, nd);
1118292915Sdim	return (error);
1119292915Sdim}
1120292915Sdim
1121292915Sdim/*
1122292915Sdim * nfs v3 mknod service (and v4 create)
1123292915Sdim */
1124292915SdimAPPLESTATIC int
1125292915Sdimnfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
1126292915Sdim    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1127292915Sdim    struct nfsexstuff *exp)
1128292915Sdim{
1129292915Sdim	struct nfsvattr nva, dirfor, diraft;
1130292915Sdim	u_int32_t *tl;
1131292915Sdim	struct nameidata named;
1132292915Sdim	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1133292915Sdim	u_int32_t major, minor;
1134292915Sdim	enum vtype vtyp = VNON;
1135292915Sdim	nfstype nfs4type = NFNON;
1136292915Sdim	vnode_t vp, dirp = NULL;
1137292915Sdim	nfsattrbit_t attrbits;
1138292915Sdim	char *bufp = NULL, *pathcp = NULL;
1139292915Sdim	u_long *hashp, cnflags;
1140292915Sdim	NFSACL_T *aclp = NULL;
1141292915Sdim
1142292915Sdim	NFSVNO_ATTRINIT(&nva);
1143292915Sdim	cnflags = (LOCKPARENT | SAVESTART);
1144292915Sdim	if (nd->nd_repstat) {
1145292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1146292915Sdim		goto out;
1147292915Sdim	}
1148292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1149292915Sdim	aclp = acl_alloc(M_WAITOK);
1150292915Sdim	aclp->acl_cnt = 0;
1151292915Sdim#endif
1152292915Sdim
1153292915Sdim	/*
1154292915Sdim	 * For V4, the creation stuff is here, Yuck!
1155292915Sdim	 */
1156292915Sdim	if (nd->nd_flag & ND_NFSV4) {
1157292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1158292915Sdim		vtyp = nfsv34tov_type(*tl);
1159292915Sdim		nfs4type = fxdr_unsigned(nfstype, *tl);
1160292915Sdim		switch (nfs4type) {
1161292915Sdim		case NFLNK:
1162292915Sdim			error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
1163292915Sdim			    &pathlen);
1164292915Sdim			if (error)
1165292915Sdim				goto nfsmout;
1166292915Sdim			break;
1167292915Sdim		case NFCHR:
1168292915Sdim		case NFBLK:
1169292915Sdim			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1170292915Sdim			major = fxdr_unsigned(u_int32_t, *tl++);
1171292915Sdim			minor = fxdr_unsigned(u_int32_t, *tl);
1172292915Sdim			nva.na_rdev = NFSMAKEDEV(major, minor);
1173292915Sdim			break;
1174292915Sdim		case NFSOCK:
1175292915Sdim		case NFFIFO:
1176292915Sdim			break;
1177292915Sdim		case NFDIR:
1178292915Sdim			cnflags = (LOCKPARENT | SAVENAME);
1179292915Sdim			break;
1180292915Sdim		default:
1181292915Sdim			nd->nd_repstat = NFSERR_BADTYPE;
1182292915Sdim			vrele(dp);
1183292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1184292915Sdim			acl_free(aclp);
1185292915Sdim#endif
1186292915Sdim			goto out;
1187292915Sdim		}
1188292915Sdim	}
1189292915Sdim	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags);
1190292915Sdim	nfsvno_setpathbuf(&named, &bufp, &hashp);
1191292915Sdim	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1192292915Sdim	if (error)
1193292915Sdim		goto nfsmout;
1194292915Sdim	if (!nd->nd_repstat) {
1195292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1196292915Sdim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1197292915Sdim			vtyp = nfsv34tov_type(*tl);
1198292915Sdim		}
1199292915Sdim		error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
1200292915Sdim		if (error)
1201292915Sdim			goto nfsmout;
1202292915Sdim		nva.na_type = vtyp;
1203292915Sdim		if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
1204292915Sdim		    (vtyp == VCHR || vtyp == VBLK)) {
1205292915Sdim			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1206292915Sdim			major = fxdr_unsigned(u_int32_t, *tl++);
1207292915Sdim			minor = fxdr_unsigned(u_int32_t, *tl);
1208292915Sdim			nva.na_rdev = NFSMAKEDEV(major, minor);
1209292915Sdim		}
1210292915Sdim	}
1211292915Sdim
1212292915Sdim	dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
1213292915Sdim	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
1214292915Sdim		if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
1215292915Sdim		    dirfor.na_gid == nva.na_gid)
1216292915Sdim			NFSVNO_UNSET(&nva, gid);
1217292915Sdim		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
1218292915Sdim	}
1219292915Sdim	if (nd->nd_repstat) {
1220292915Sdim		vrele(dp);
1221292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1222292915Sdim		acl_free(aclp);
1223292915Sdim#endif
1224292915Sdim		nfsvno_relpathbuf(&named);
1225292915Sdim		if (pathcp)
1226292915Sdim			FREE(pathcp, M_TEMP);
1227292915Sdim		if (nd->nd_flag & ND_NFSV3)
1228292915Sdim			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1229292915Sdim			    &diraft);
1230292915Sdim		goto out;
1231292915Sdim	}
1232292915Sdim
1233292915Sdim	/*
1234292915Sdim	 * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
1235292915Sdim	 * in va_mode, so we'll have to set a default here.
1236292915Sdim	 */
1237292915Sdim	if (NFSVNO_NOTSETMODE(&nva)) {
1238292915Sdim		if (vtyp == VLNK)
1239292915Sdim			nva.na_mode = 0755;
1240292915Sdim		else
1241292915Sdim			nva.na_mode = 0400;
1242292915Sdim	}
1243292915Sdim
1244292915Sdim	if (vtyp == VDIR)
1245292915Sdim		named.ni_cnd.cn_flags |= WILLBEDIR;
1246292915Sdim	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1247292915Sdim	if (nd->nd_repstat) {
1248292915Sdim		if (dirp) {
1249292915Sdim			if (nd->nd_flag & ND_NFSV3)
1250292915Sdim				dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1251292915Sdim				    nd->nd_cred, p, 0);
1252292915Sdim			vrele(dirp);
1253292915Sdim		}
1254292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1255292915Sdim		acl_free(aclp);
1256292915Sdim#endif
1257292915Sdim		if (nd->nd_flag & ND_NFSV3)
1258292915Sdim			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1259292915Sdim			    &diraft);
1260292915Sdim		goto out;
1261292915Sdim	}
1262292915Sdim	if (dirp)
1263292915Sdim		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1264292915Sdim
1265292915Sdim	if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
1266292915Sdim		if (vtyp == VDIR) {
1267292915Sdim			nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
1268292915Sdim			    &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
1269292915Sdim			    exp);
1270292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1271292915Sdim			acl_free(aclp);
1272292915Sdim#endif
1273292915Sdim			goto out;
1274292915Sdim		} else if (vtyp == VLNK) {
1275292915Sdim			nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1276292915Sdim			    &dirfor, &diraft, &diraft_ret, &attrbits,
1277292915Sdim			    aclp, p, exp, pathcp, pathlen);
1278294024Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1279294024Sdim			acl_free(aclp);
1280292915Sdim#endif
1281292915Sdim			FREE(pathcp, M_TEMP);
1282292915Sdim			goto out;
1283292915Sdim		}
1284292915Sdim	}
1285292915Sdim
1286292915Sdim	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
1287292915Sdim	if (!nd->nd_repstat) {
1288292915Sdim		vp = named.ni_vp;
1289292915Sdim		nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
1290292915Sdim		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1291292915Sdim		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
1292292915Sdim			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1293292915Sdim			    p, 1);
1294292915Sdim		if (vpp != NULL && nd->nd_repstat == 0) {
1295292915Sdim			NFSVOPUNLOCK(vp, 0);
1296292915Sdim			*vpp = vp;
1297292915Sdim		} else
1298292915Sdim			vput(vp);
1299292915Sdim	}
1300292915Sdim
1301292915Sdim	diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1302292915Sdim	vrele(dirp);
1303294024Sdim	if (!nd->nd_repstat) {
1304292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1305292915Sdim			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1306292915Sdim			nfsrv_postopattr(nd, 0, &nva);
1307292915Sdim		} else {
1308292915Sdim			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1309292915Sdim			*tl++ = newnfs_false;
1310292915Sdim			txdr_hyper(dirfor.na_filerev, tl);
1311292915Sdim			tl += 2;
1312292915Sdim			txdr_hyper(diraft.na_filerev, tl);
1313292915Sdim			(void) nfsrv_putattrbit(nd, &attrbits);
1314292915Sdim		}
1315292915Sdim	}
1316292915Sdim	if (nd->nd_flag & ND_NFSV3)
1317292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1318292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1319292915Sdim	acl_free(aclp);
1320292915Sdim#endif
1321292915Sdim
1322292915Sdimout:
1323292915Sdim	NFSEXITCODE2(0, nd);
1324292915Sdim	return (0);
1325292915Sdimnfsmout:
1326292915Sdim	vrele(dp);
1327292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
1328292915Sdim	acl_free(aclp);
1329292915Sdim#endif
1330292915Sdim	if (bufp)
1331292915Sdim		nfsvno_relpathbuf(&named);
1332292915Sdim	if (pathcp)
1333292915Sdim		FREE(pathcp, M_TEMP);
1334292915Sdim
1335292915Sdim	NFSEXITCODE2(error, nd);
1336292915Sdim	return (error);
1337292915Sdim}
1338292915Sdim
1339292915Sdim/*
1340292915Sdim * nfs remove service
1341292915Sdim */
1342292915SdimAPPLESTATIC int
1343292915Sdimnfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
1344292915Sdim    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
1345292915Sdim{
1346292915Sdim	struct nameidata named;
1347292915Sdim	u_int32_t *tl;
1348292915Sdim	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1349292915Sdim	vnode_t dirp = NULL;
1350292915Sdim	struct nfsvattr dirfor, diraft;
1351292915Sdim	char *bufp;
1352292915Sdim	u_long *hashp;
1353292915Sdim
1354292915Sdim	if (nd->nd_repstat) {
1355292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1356292915Sdim		goto out;
1357292915Sdim	}
1358292915Sdim	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
1359292915Sdim	    LOCKPARENT | LOCKLEAF);
1360292915Sdim	nfsvno_setpathbuf(&named, &bufp, &hashp);
1361292915Sdim	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1362292915Sdim	if (error) {
1363292915Sdim		vput(dp);
1364292915Sdim		nfsvno_relpathbuf(&named);
1365292915Sdim		goto out;
1366292915Sdim	}
1367292915Sdim	if (!nd->nd_repstat) {
1368292915Sdim		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1369292915Sdim	} else {
1370292915Sdim		vput(dp);
1371292915Sdim		nfsvno_relpathbuf(&named);
1372292915Sdim	}
1373292915Sdim	if (dirp) {
1374292915Sdim		if (!(nd->nd_flag & ND_NFSV2)) {
1375292915Sdim			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1376292915Sdim			    nd->nd_cred, p, 0);
1377292915Sdim		} else {
1378292915Sdim			vrele(dirp);
1379292915Sdim			dirp = NULL;
1380292915Sdim		}
1381292915Sdim	}
1382292915Sdim	if (!nd->nd_repstat) {
1383292915Sdim		if (nd->nd_flag & ND_NFSV4) {
1384292915Sdim			if (vnode_vtype(named.ni_vp) == VDIR)
1385292915Sdim				nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
1386292915Sdim				    nd->nd_cred, p, exp);
1387292915Sdim			else
1388292915Sdim				nd->nd_repstat = nfsvno_removesub(&named, 1,
1389292915Sdim				    nd->nd_cred, p, exp);
1390292915Sdim		} else if (nd->nd_procnum == NFSPROC_RMDIR) {
1391292915Sdim			nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
1392292915Sdim			    nd->nd_cred, p, exp);
1393292915Sdim		} else {
1394292915Sdim			nd->nd_repstat = nfsvno_removesub(&named, 0,
1395292915Sdim			    nd->nd_cred, p, exp);
1396292915Sdim		}
1397292915Sdim	}
1398292915Sdim	if (!(nd->nd_flag & ND_NFSV2)) {
1399292915Sdim		if (dirp) {
1400292915Sdim			diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred,
1401292915Sdim			    p, 0);
1402292915Sdim			vrele(dirp);
1403292915Sdim		}
1404292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1405292915Sdim			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1406292915Sdim			    &diraft);
1407292915Sdim		} else if (!nd->nd_repstat) {
1408292915Sdim			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1409292915Sdim			*tl++ = newnfs_false;
1410292915Sdim			txdr_hyper(dirfor.na_filerev, tl);
1411292915Sdim			tl += 2;
1412292915Sdim			txdr_hyper(diraft.na_filerev, tl);
1413292915Sdim		}
1414292915Sdim	}
1415292915Sdim
1416292915Sdimout:
1417292915Sdim	NFSEXITCODE2(error, nd);
1418292915Sdim	return (error);
1419292915Sdim}
1420292915Sdim
1421292915Sdim/*
1422292915Sdim * nfs rename service
1423292915Sdim */
1424292915SdimAPPLESTATIC int
1425292915Sdimnfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
1426292915Sdim    vnode_t dp, vnode_t todp, NFSPROC_T *p, struct nfsexstuff *exp,
1427292915Sdim    struct nfsexstuff *toexp)
1428292915Sdim{
1429292915Sdim	u_int32_t *tl;
1430292915Sdim	int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
1431292915Sdim	int tdirfor_ret = 1, tdiraft_ret = 1;
1432292915Sdim	struct nameidata fromnd, tond;
1433292915Sdim	vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
1434292915Sdim	struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
1435292915Sdim	struct nfsexstuff tnes;
1436292915Sdim	struct nfsrvfh tfh;
1437292915Sdim	char *bufp, *tbufp = NULL;
1438292915Sdim	u_long *hashp;
1439292915Sdim	fhandle_t fh;
1440292915Sdim
1441292915Sdim	if (nd->nd_repstat) {
1442292915Sdim		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1443292915Sdim		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1444292915Sdim		goto out;
1445292915Sdim	}
1446292915Sdim	if (!(nd->nd_flag & ND_NFSV2))
1447292915Sdim		fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd->nd_cred, p, 1);
1448292915Sdim	tond.ni_cnd.cn_nameiop = 0;
1449292915Sdim	tond.ni_startdir = NULL;
1450292915Sdim	NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
1451292915Sdim	nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
1452292915Sdim	error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
1453292915Sdim	if (error) {
1454292915Sdim		vput(dp);
1455292915Sdim		if (todp)
1456292915Sdim			vrele(todp);
1457292915Sdim		nfsvno_relpathbuf(&fromnd);
1458292915Sdim		goto out;
1459292915Sdim	}
1460292915Sdim	if (nd->nd_flag & ND_NFSV4) {
1461292915Sdim		tdp = todp;
1462292915Sdim		tnes = *toexp;
1463292915Sdim		tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, p, 0);
1464292915Sdim	} else {
1465292915Sdim		tfh.nfsrvfh_len = 0;
1466292915Sdim		error = nfsrv_mtofh(nd, &tfh);
1467292915Sdim		if (error == 0)
1468292915Sdim			error = nfsvno_getfh(dp, &fh, p);
1469292915Sdim		if (error) {
1470292915Sdim			vput(dp);
1471292915Sdim			/* todp is always NULL except NFSv4 */
1472292915Sdim			nfsvno_relpathbuf(&fromnd);
1473292915Sdim			goto out;
1474292915Sdim		}
1475292915Sdim
1476292915Sdim		/* If this is the same file handle, just VREF() the vnode. */
1477292915Sdim		if (tfh.nfsrvfh_len == NFSX_MYFH &&
1478292915Sdim		    !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
1479292915Sdim			VREF(dp);
1480292915Sdim			tdp = dp;
1481292915Sdim			tnes = *exp;
1482292915Sdim			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1483292915Sdim			    p, 1);
1484292915Sdim		} else {
1485292915Sdim			nd->nd_cred->cr_uid = nd->nd_saveduid;
1486292915Sdim			nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
1487292915Sdim			    0, p);
1488292915Sdim			if (tdp) {
1489292915Sdim				tdirfor_ret = nfsvno_getattr(tdp, &tdirfor,
1490292915Sdim				    nd->nd_cred, p, 1);
1491292915Sdim				NFSVOPUNLOCK(tdp, 0);
1492292915Sdim			}
1493292915Sdim		}
1494292915Sdim	}
1495292915Sdim	NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
1496292915Sdim	nfsvno_setpathbuf(&tond, &tbufp, &hashp);
1497292915Sdim	if (!nd->nd_repstat) {
1498292915Sdim		error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
1499292915Sdim		if (error) {
1500292915Sdim			if (tdp)
1501292915Sdim				vrele(tdp);
1502292915Sdim			vput(dp);
1503292915Sdim			nfsvno_relpathbuf(&fromnd);
1504292915Sdim			nfsvno_relpathbuf(&tond);
1505292915Sdim			goto out;
1506292915Sdim		}
1507292915Sdim	}
1508292915Sdim	if (nd->nd_repstat) {
1509292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1510292915Sdim			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1511292915Sdim			    &fdiraft);
1512292915Sdim			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1513292915Sdim			    &tdiraft);
1514292915Sdim		}
1515292915Sdim		if (tdp)
1516292915Sdim			vrele(tdp);
1517292915Sdim		vput(dp);
1518292915Sdim		nfsvno_relpathbuf(&fromnd);
1519292915Sdim		nfsvno_relpathbuf(&tond);
1520292915Sdim		goto out;
1521292915Sdim	}
1522292915Sdim
1523292915Sdim	/*
1524292915Sdim	 * Done parsing, now down to business.
1525292915Sdim	 */
1526292915Sdim	nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 1, exp, p, &fdirp);
1527292915Sdim	if (nd->nd_repstat) {
1528292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1529292915Sdim			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1530292915Sdim			    &fdiraft);
1531292915Sdim			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1532292915Sdim			    &tdiraft);
1533292915Sdim		}
1534292915Sdim		if (fdirp)
1535292915Sdim			vrele(fdirp);
1536292915Sdim		if (tdp)
1537292915Sdim			vrele(tdp);
1538292915Sdim		nfsvno_relpathbuf(&tond);
1539292915Sdim		goto out;
1540292915Sdim	}
1541292915Sdim	if (vnode_vtype(fromnd.ni_vp) == VDIR)
1542292915Sdim		tond.ni_cnd.cn_flags |= WILLBEDIR;
1543292915Sdim	nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
1544292915Sdim	nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
1545292915Sdim	    nd->nd_flag, nd->nd_cred, p);
1546292915Sdim	if (fdirp)
1547292915Sdim		fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd->nd_cred, p,
1548292915Sdim		    0);
1549292915Sdim	if (tdirp)
1550292915Sdim		tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd->nd_cred, p,
1551292915Sdim		    0);
1552292915Sdim	if (fdirp)
1553292915Sdim		vrele(fdirp);
1554292915Sdim	if (tdirp)
1555292915Sdim		vrele(tdirp);
1556292915Sdim	if (nd->nd_flag & ND_NFSV3) {
1557292915Sdim		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1558292915Sdim		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1559292915Sdim	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1560292915Sdim		NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
1561292915Sdim		*tl++ = newnfs_false;
1562292915Sdim		txdr_hyper(fdirfor.na_filerev, tl);
1563292915Sdim		tl += 2;
1564292915Sdim		txdr_hyper(fdiraft.na_filerev, tl);
1565292915Sdim		tl += 2;
1566292915Sdim		*tl++ = newnfs_false;
1567292915Sdim		txdr_hyper(tdirfor.na_filerev, tl);
1568292915Sdim		tl += 2;
1569292915Sdim		txdr_hyper(tdiraft.na_filerev, tl);
1570292915Sdim	}
1571292915Sdim
1572292915Sdimout:
1573292915Sdim	NFSEXITCODE2(error, nd);
1574292915Sdim	return (error);
1575292915Sdim}
1576292915Sdim
1577292915Sdim/*
1578292915Sdim * nfs link service
1579292915Sdim */
1580292915SdimAPPLESTATIC int
1581292915Sdimnfsrvd_link(struct nfsrv_descript *nd, int isdgram,
1582292915Sdim    vnode_t vp, vnode_t tovp, NFSPROC_T *p, struct nfsexstuff *exp,
1583292915Sdim    struct nfsexstuff *toexp)
1584292915Sdim{
1585292915Sdim	struct nameidata named;
1586292915Sdim	u_int32_t *tl;
1587292915Sdim	int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
1588292915Sdim	vnode_t dirp = NULL, dp = NULL;
1589292915Sdim	struct nfsvattr dirfor, diraft, at;
1590292915Sdim	struct nfsexstuff tnes;
1591292915Sdim	struct nfsrvfh dfh;
1592292915Sdim	char *bufp;
1593292915Sdim	u_long *hashp;
1594292915Sdim
1595292915Sdim	if (nd->nd_repstat) {
1596292915Sdim		nfsrv_postopattr(nd, getret, &at);
1597292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1598292915Sdim		goto out;
1599292915Sdim	}
1600292915Sdim	NFSVOPUNLOCK(vp, 0);
1601292915Sdim	if (vnode_vtype(vp) == VDIR) {
1602292915Sdim		if (nd->nd_flag & ND_NFSV4)
1603292915Sdim			nd->nd_repstat = NFSERR_ISDIR;
1604292915Sdim		else
1605292915Sdim			nd->nd_repstat = NFSERR_INVAL;
1606292915Sdim		if (tovp)
1607292915Sdim			vrele(tovp);
1608292915Sdim	} else if (vnode_vtype(vp) == VLNK) {
1609292915Sdim		if (nd->nd_flag & ND_NFSV2)
1610292915Sdim			nd->nd_repstat = NFSERR_INVAL;
1611292915Sdim		else
1612292915Sdim			nd->nd_repstat = NFSERR_NOTSUPP;
1613292915Sdim		if (tovp)
1614292915Sdim			vrele(tovp);
1615292915Sdim	}
1616292915Sdim	if (!nd->nd_repstat) {
1617292915Sdim		if (nd->nd_flag & ND_NFSV4) {
1618292915Sdim			dp = tovp;
1619292915Sdim			tnes = *toexp;
1620292915Sdim		} else {
1621292915Sdim			error = nfsrv_mtofh(nd, &dfh);
1622292915Sdim			if (error) {
1623292915Sdim				vrele(vp);
1624292915Sdim				/* tovp is always NULL unless NFSv4 */
1625292915Sdim				goto out;
1626292915Sdim			}
1627292915Sdim			nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0,
1628292915Sdim			    p);
1629292915Sdim			if (dp)
1630292915Sdim				NFSVOPUNLOCK(dp, 0);
1631292915Sdim		}
1632292915Sdim	}
1633292915Sdim	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1634292915Sdim	    LOCKPARENT | SAVENAME);
1635292915Sdim	if (!nd->nd_repstat) {
1636292915Sdim		nfsvno_setpathbuf(&named, &bufp, &hashp);
1637292915Sdim		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1638292915Sdim		if (error) {
1639292915Sdim			vrele(vp);
1640292915Sdim			if (dp)
1641292915Sdim				vrele(dp);
1642292915Sdim			nfsvno_relpathbuf(&named);
1643292915Sdim			goto out;
1644292915Sdim		}
1645292915Sdim		if (!nd->nd_repstat) {
1646292915Sdim			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
1647292915Sdim			    p, &dirp);
1648292915Sdim		} else {
1649292915Sdim			if (dp)
1650292915Sdim				vrele(dp);
1651292915Sdim			nfsvno_relpathbuf(&named);
1652292915Sdim		}
1653292915Sdim	}
1654292915Sdim	if (dirp) {
1655292915Sdim		if (nd->nd_flag & ND_NFSV2) {
1656292915Sdim			vrele(dirp);
1657292915Sdim			dirp = NULL;
1658292915Sdim		} else {
1659292915Sdim			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1660292915Sdim			    nd->nd_cred, p, 0);
1661292915Sdim		}
1662292915Sdim	}
1663292915Sdim	if (!nd->nd_repstat)
1664292915Sdim		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
1665292915Sdim	if (nd->nd_flag & ND_NFSV3)
1666292915Sdim		getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 0);
1667292915Sdim	if (dirp) {
1668292915Sdim		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1669292915Sdim		vrele(dirp);
1670292915Sdim	}
1671292915Sdim	vrele(vp);
1672292915Sdim	if (nd->nd_flag & ND_NFSV3) {
1673292915Sdim		nfsrv_postopattr(nd, getret, &at);
1674292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1675292915Sdim	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1676292915Sdim		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1677292915Sdim		*tl++ = newnfs_false;
1678292915Sdim		txdr_hyper(dirfor.na_filerev, tl);
1679292915Sdim		tl += 2;
1680292915Sdim		txdr_hyper(diraft.na_filerev, tl);
1681292915Sdim	}
1682292915Sdim
1683292915Sdimout:
1684292915Sdim	NFSEXITCODE2(error, nd);
1685292915Sdim	return (error);
1686292915Sdim}
1687292915Sdim
1688292915Sdim/*
1689292915Sdim * nfs symbolic link service
1690292915Sdim */
1691292915SdimAPPLESTATIC int
1692292915Sdimnfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1693292915Sdim    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1694292915Sdim    struct nfsexstuff *exp)
1695292915Sdim{
1696292915Sdim	struct nfsvattr nva, dirfor, diraft;
1697292915Sdim	struct nameidata named;
1698292915Sdim	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1699292915Sdim	vnode_t dirp = NULL;
1700292915Sdim	char *bufp, *pathcp = NULL;
1701292915Sdim	u_long *hashp;
1702292915Sdim
1703292915Sdim	if (nd->nd_repstat) {
1704292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1705292915Sdim		goto out;
1706292915Sdim	}
1707292915Sdim	if (vpp)
1708292915Sdim		*vpp = NULL;
1709292915Sdim	NFSVNO_ATTRINIT(&nva);
1710292915Sdim	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1711292915Sdim	    LOCKPARENT | SAVESTART);
1712292915Sdim	nfsvno_setpathbuf(&named, &bufp, &hashp);
1713292915Sdim	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1714292915Sdim	if (!error && !nd->nd_repstat)
1715292915Sdim		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
1716292915Sdim	if (error) {
1717292915Sdim		vrele(dp);
1718292915Sdim		nfsvno_relpathbuf(&named);
1719292915Sdim		goto out;
1720292915Sdim	}
1721292915Sdim	if (!nd->nd_repstat) {
1722292915Sdim		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1723292915Sdim	} else {
1724292915Sdim		vrele(dp);
1725292915Sdim		nfsvno_relpathbuf(&named);
1726292915Sdim	}
1727292915Sdim	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1728292915Sdim		vrele(dirp);
1729292915Sdim		dirp = NULL;
1730292915Sdim	}
1731292915Sdim
1732292915Sdim	/*
1733292915Sdim	 * And call nfsrvd_symlinksub() to do the common code. It will
1734292915Sdim	 * return EBADRPC upon a parsing error, 0 otherwise.
1735292915Sdim	 */
1736292915Sdim	if (!nd->nd_repstat) {
1737292915Sdim		if (dirp != NULL)
1738292915Sdim			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1739292915Sdim			    p, 0);
1740292915Sdim		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1741292915Sdim		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
1742292915Sdim		    pathcp, pathlen);
1743292915Sdim	} else if (dirp != NULL) {
1744292915Sdim		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1745292915Sdim		vrele(dirp);
1746292915Sdim	}
1747292915Sdim	if (pathcp)
1748292915Sdim		FREE(pathcp, M_TEMP);
1749292915Sdim
1750292915Sdim	if (nd->nd_flag & ND_NFSV3) {
1751292915Sdim		if (!nd->nd_repstat) {
1752292915Sdim			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1753292915Sdim			nfsrv_postopattr(nd, 0, &nva);
1754292915Sdim		}
1755292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1756292915Sdim	}
1757292915Sdim
1758292915Sdimout:
1759292915Sdim	NFSEXITCODE2(error, nd);
1760292915Sdim	return (error);
1761292915Sdim}
1762292915Sdim
1763292915Sdim/*
1764292915Sdim * Common code for creating a symbolic link.
1765292915Sdim */
1766292915Sdimstatic void
1767292915Sdimnfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
1768292915Sdim    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1769292915Sdim    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1770292915Sdim    int *diraft_retp, nfsattrbit_t *attrbitp,
1771292915Sdim    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
1772292915Sdim    int pathlen)
1773292915Sdim{
1774292915Sdim	u_int32_t *tl;
1775292915Sdim
1776292915Sdim	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
1777292915Sdim	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
1778292915Sdim	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
1779292915Sdim		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
1780292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1781292915Sdim			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
1782292915Sdim			if (!nd->nd_repstat)
1783292915Sdim				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
1784292915Sdim				    nvap, nd->nd_cred, p, 1);
1785292915Sdim		}
1786292915Sdim		if (vpp != NULL && nd->nd_repstat == 0) {
1787292915Sdim			NFSVOPUNLOCK(ndp->ni_vp, 0);
1788292915Sdim			*vpp = ndp->ni_vp;
1789292915Sdim		} else
1790292915Sdim			vput(ndp->ni_vp);
1791292915Sdim	}
1792292915Sdim	if (dirp) {
1793292915Sdim		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1794292915Sdim		vrele(dirp);
1795292915Sdim	}
1796292915Sdim	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1797292915Sdim		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1798292915Sdim		*tl++ = newnfs_false;
1799292915Sdim		txdr_hyper(dirforp->na_filerev, tl);
1800292915Sdim		tl += 2;
1801292915Sdim		txdr_hyper(diraftp->na_filerev, tl);
1802292915Sdim		(void) nfsrv_putattrbit(nd, attrbitp);
1803292915Sdim	}
1804292915Sdim
1805292915Sdim	NFSEXITCODE2(0, nd);
1806292915Sdim}
1807292915Sdim
1808292915Sdim/*
1809292915Sdim * nfs mkdir service
1810292915Sdim */
1811292915SdimAPPLESTATIC int
1812292915Sdimnfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
1813292915Sdim    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1814292915Sdim    struct nfsexstuff *exp)
1815292915Sdim{
1816292915Sdim	struct nfsvattr nva, dirfor, diraft;
1817292915Sdim	struct nameidata named;
1818292915Sdim	u_int32_t *tl;
1819292915Sdim	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1820292915Sdim	vnode_t dirp = NULL;
1821292915Sdim	char *bufp;
1822292915Sdim	u_long *hashp;
1823292915Sdim
1824292915Sdim	if (nd->nd_repstat) {
1825292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1826292915Sdim		goto out;
1827292915Sdim	}
1828292915Sdim	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1829292915Sdim	    LOCKPARENT | SAVENAME);
1830292915Sdim	nfsvno_setpathbuf(&named, &bufp, &hashp);
1831292915Sdim	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1832292915Sdim	if (error)
1833292915Sdim		goto nfsmout;
1834292915Sdim	if (!nd->nd_repstat) {
1835292915Sdim		NFSVNO_ATTRINIT(&nva);
1836292915Sdim		if (nd->nd_flag & ND_NFSV3) {
1837292915Sdim			error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
1838292915Sdim			if (error)
1839292915Sdim				goto nfsmout;
1840292915Sdim		} else {
1841292915Sdim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1842292915Sdim			nva.na_mode = nfstov_mode(*tl++);
1843292915Sdim		}
1844292915Sdim	}
1845292915Sdim	if (!nd->nd_repstat) {
1846292915Sdim		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1847292915Sdim	} else {
1848292915Sdim		vrele(dp);
1849292915Sdim		nfsvno_relpathbuf(&named);
1850292915Sdim	}
1851292915Sdim	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1852292915Sdim		vrele(dirp);
1853292915Sdim		dirp = NULL;
1854292915Sdim	}
1855292915Sdim	if (nd->nd_repstat) {
1856292915Sdim		if (dirp != NULL) {
1857292915Sdim			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1858292915Sdim			    p, 0);
1859292915Sdim			vrele(dirp);
1860292915Sdim		}
1861292915Sdim		if (nd->nd_flag & ND_NFSV3)
1862292915Sdim			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1863292915Sdim			    &diraft);
1864292915Sdim		goto out;
1865292915Sdim	}
1866292915Sdim	if (dirp != NULL)
1867292915Sdim		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1868292915Sdim
1869292915Sdim	/*
1870292915Sdim	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
1871292915Sdim	 */
1872292915Sdim	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
1873292915Sdim	    &diraft_ret, NULL, NULL, p, exp);
1874292915Sdim
1875292915Sdim	if (nd->nd_flag & ND_NFSV3) {
1876292915Sdim		if (!nd->nd_repstat) {
1877292915Sdim			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1878292915Sdim			nfsrv_postopattr(nd, 0, &nva);
1879292915Sdim		}
1880292915Sdim		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1881292915Sdim	} else if (!nd->nd_repstat) {
1882292915Sdim		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
1883292915Sdim		nfsrv_fillattr(nd, &nva);
1884292915Sdim	}
1885292915Sdim
1886292915Sdimout:
1887292915Sdim	NFSEXITCODE2(0, nd);
1888292915Sdim	return (0);
1889292915Sdimnfsmout:
1890292915Sdim	vrele(dp);
1891292915Sdim	nfsvno_relpathbuf(&named);
1892292915Sdim	NFSEXITCODE2(error, nd);
1893292915Sdim	return (error);
1894292915Sdim}
1895292915Sdim
1896292915Sdim/*
1897292915Sdim * Code common to mkdir for V2,3 and 4.
1898292915Sdim */
1899292915Sdimstatic void
1900292915Sdimnfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1901292915Sdim    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1902292915Sdim    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1903292915Sdim    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
1904292915Sdim    NFSPROC_T *p, struct nfsexstuff *exp)
1905292915Sdim{
1906292915Sdim	vnode_t vp;
1907292915Sdim	u_int32_t *tl;
1908292915Sdim
1909292915Sdim	NFSVNO_SETATTRVAL(nvap, type, VDIR);
1910292915Sdim	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
1911292915Sdim	    nd->nd_cred, p, exp);
1912292915Sdim	if (!nd->nd_repstat) {
1913292915Sdim		vp = ndp->ni_vp;
1914292915Sdim		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
1915292915Sdim		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1916292915Sdim		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
1917292915Sdim			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd->nd_cred,
1918292915Sdim			    p, 1);
1919292915Sdim		if (vpp && !nd->nd_repstat) {
1920292915Sdim			NFSVOPUNLOCK(vp, 0);
1921292915Sdim			*vpp = vp;
1922292915Sdim		} else {
1923292915Sdim			vput(vp);
1924292915Sdim		}
1925292915Sdim	}
1926292915Sdim	if (dirp) {
1927292915Sdim		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1928292915Sdim		vrele(dirp);
1929292915Sdim	}
1930294024Sdim	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1931294024Sdim		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1932294024Sdim		*tl++ = newnfs_false;
1933292915Sdim		txdr_hyper(dirforp->na_filerev, tl);
1934292915Sdim		tl += 2;
1935292915Sdim		txdr_hyper(diraftp->na_filerev, tl);
1936292915Sdim		(void) nfsrv_putattrbit(nd, attrbitp);
1937292915Sdim	}
1938292915Sdim
1939292915Sdim	NFSEXITCODE2(0, nd);
1940292915Sdim}
1941292915Sdim
1942292915Sdim/*
1943292915Sdim * nfs commit service
1944292915Sdim */
1945292915SdimAPPLESTATIC int
1946292915Sdimnfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
1947292915Sdim    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1948292915Sdim{
1949292915Sdim	struct nfsvattr bfor, aft;
1950292915Sdim	u_int32_t *tl;
1951292915Sdim	int error = 0, for_ret = 1, aft_ret = 1, cnt;
1952292915Sdim	u_int64_t off;
1953292915Sdim
1954292915Sdim	if (nd->nd_repstat) {
1955292915Sdim		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1956292915Sdim		goto out;
1957294024Sdim	}
1958294024Sdim	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1959292915Sdim	/*
1960292915Sdim	 * XXX At this time VOP_FSYNC() does not accept offset and byte
1961294024Sdim	 * count parameters, so these arguments are useless (someday maybe).
1962292915Sdim	 */
1963292915Sdim	off = fxdr_hyper(tl);
1964292915Sdim	tl += 2;
1965292915Sdim	cnt = fxdr_unsigned(int, *tl);
1966292915Sdim	if (nd->nd_flag & ND_NFSV3)
1967292915Sdim		for_ret = nfsvno_getattr(vp, &bfor, nd->nd_cred, p, 1);
1968292915Sdim	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
1969292915Sdim	if (nd->nd_flag & ND_NFSV3) {
1970292915Sdim		aft_ret = nfsvno_getattr(vp, &aft, nd->nd_cred, p, 1);
1971292915Sdim		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1972292915Sdim	}
1973292915Sdim	vput(vp);
1974292915Sdim	if (!nd->nd_repstat) {
1975292915Sdim		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1976292915Sdim		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
1977292915Sdim		*tl = txdr_unsigned(nfsboottime.tv_usec);
1978292915Sdim	}
1979292915Sdim
1980292915Sdimout:
1981292915Sdim	NFSEXITCODE2(0, nd);
1982292915Sdim	return (0);
1983292915Sdimnfsmout:
1984292915Sdim	vput(vp);
1985292915Sdim	NFSEXITCODE2(error, nd);
1986292915Sdim	return (error);
1987292915Sdim}
1988292915Sdim
1989292915Sdim/*
1990292915Sdim * nfs statfs service
1991292915Sdim */
1992292915SdimAPPLESTATIC int
1993292915Sdimnfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
1994292915Sdim    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1995292915Sdim{
1996292915Sdim	struct statfs *sf;
1997292915Sdim	u_int32_t *tl;
1998294024Sdim	int getret = 1;
1999294024Sdim	struct nfsvattr at;
2000294024Sdim	struct statfs sfs;
2001292915Sdim	u_quad_t tval;
2002292915Sdim
2003292915Sdim	if (nd->nd_repstat) {
2004292915Sdim		nfsrv_postopattr(nd, getret, &at);
2005292915Sdim		goto out;
2006292915Sdim	}
2007292915Sdim	sf = &sfs;
2008292915Sdim	nd->nd_repstat = nfsvno_statfs(vp, sf);
2009292915Sdim	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2010292915Sdim	vput(vp);
2011292915Sdim	if (nd->nd_flag & ND_NFSV3)
2012292915Sdim		nfsrv_postopattr(nd, getret, &at);
2013292915Sdim	if (nd->nd_repstat)
2014292915Sdim		goto out;
2015292915Sdim	if (nd->nd_flag & ND_NFSV2) {
2016292915Sdim		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
2017292915Sdim		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
2018292915Sdim		*tl++ = txdr_unsigned(sf->f_bsize);
2019292915Sdim		*tl++ = txdr_unsigned(sf->f_blocks);
2020292915Sdim		*tl++ = txdr_unsigned(sf->f_bfree);
2021292915Sdim		*tl = txdr_unsigned(sf->f_bavail);
2022292915Sdim	} else {
2023292915Sdim		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
2024292915Sdim		tval = (u_quad_t)sf->f_blocks;
2025292915Sdim		tval *= (u_quad_t)sf->f_bsize;
2026292915Sdim		txdr_hyper(tval, tl); tl += 2;
2027292915Sdim		tval = (u_quad_t)sf->f_bfree;
2028292915Sdim		tval *= (u_quad_t)sf->f_bsize;
2029292915Sdim		txdr_hyper(tval, tl); tl += 2;
2030292915Sdim		tval = (u_quad_t)sf->f_bavail;
2031292915Sdim		tval *= (u_quad_t)sf->f_bsize;
2032292915Sdim		txdr_hyper(tval, tl); tl += 2;
2033292915Sdim		tval = (u_quad_t)sf->f_files;
2034292915Sdim		txdr_hyper(tval, tl); tl += 2;
2035292915Sdim		tval = (u_quad_t)sf->f_ffree;
2036292915Sdim		txdr_hyper(tval, tl); tl += 2;
2037292915Sdim		tval = (u_quad_t)sf->f_ffree;
2038292915Sdim		txdr_hyper(tval, tl); tl += 2;
2039292915Sdim		*tl = 0;
2040292915Sdim	}
2041292915Sdim
2042292915Sdimout:
2043292915Sdim	NFSEXITCODE2(0, nd);
2044292915Sdim	return (0);
2045292915Sdim}
2046292915Sdim
2047292915Sdim/*
2048292915Sdim * nfs fsinfo service
2049292915Sdim */
2050292915SdimAPPLESTATIC int
2051292915Sdimnfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2052292915Sdim    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2053292915Sdim{
2054294024Sdim	u_int32_t *tl;
2055294024Sdim	struct nfsfsinfo fs;
2056294024Sdim	int getret = 1;
2057292915Sdim	struct nfsvattr at;
2058292915Sdim
2059292915Sdim	if (nd->nd_repstat) {
2060292915Sdim		nfsrv_postopattr(nd, getret, &at);
2061292915Sdim		goto out;
2062292915Sdim	}
2063292915Sdim	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2064292915Sdim	nfsvno_getfs(&fs, isdgram);
2065292915Sdim	vput(vp);
2066292915Sdim	nfsrv_postopattr(nd, getret, &at);
2067292915Sdim	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
2068292915Sdim	*tl++ = txdr_unsigned(fs.fs_rtmax);
2069292915Sdim	*tl++ = txdr_unsigned(fs.fs_rtpref);
2070292915Sdim	*tl++ = txdr_unsigned(fs.fs_rtmult);
2071292915Sdim	*tl++ = txdr_unsigned(fs.fs_wtmax);
2072292915Sdim	*tl++ = txdr_unsigned(fs.fs_wtpref);
2073292915Sdim	*tl++ = txdr_unsigned(fs.fs_wtmult);
2074292915Sdim	*tl++ = txdr_unsigned(fs.fs_dtpref);
2075292915Sdim	txdr_hyper(fs.fs_maxfilesize, tl);
2076292915Sdim	tl += 2;
2077292915Sdim	txdr_nfsv3time(&fs.fs_timedelta, tl);
2078292915Sdim	tl += 2;
2079292915Sdim	*tl = txdr_unsigned(fs.fs_properties);
2080292915Sdim
2081292915Sdimout:
2082292915Sdim	NFSEXITCODE2(0, nd);
2083292915Sdim	return (0);
2084292915Sdim}
2085292915Sdim
2086292915Sdim/*
2087292915Sdim * nfs pathconf service
2088292915Sdim */
2089294024SdimAPPLESTATIC int
2090294024Sdimnfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2091292915Sdim    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2092292915Sdim{
2093292915Sdim	struct nfsv3_pathconf *pc;
2094292915Sdim	int getret = 1;
2095292915Sdim	register_t linkmax, namemax, chownres, notrunc;
2096292915Sdim	struct nfsvattr at;
2097292915Sdim
2098292915Sdim	if (nd->nd_repstat) {
2099292915Sdim		nfsrv_postopattr(nd, getret, &at);
2100292915Sdim		goto out;
2101292915Sdim	}
2102292915Sdim	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
2103292915Sdim	    nd->nd_cred, p);
2104292915Sdim	if (!nd->nd_repstat)
2105292915Sdim		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
2106292915Sdim		    nd->nd_cred, p);
2107292915Sdim	if (!nd->nd_repstat)
2108292915Sdim		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
2109292915Sdim		    &chownres, nd->nd_cred, p);
2110292915Sdim	if (!nd->nd_repstat)
2111292915Sdim		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
2112292915Sdim		    nd->nd_cred, p);
2113294024Sdim	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2114292915Sdim	vput(vp);
2115292915Sdim	nfsrv_postopattr(nd, getret, &at);
2116292915Sdim	if (!nd->nd_repstat) {
2117292915Sdim		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
2118292915Sdim		pc->pc_linkmax = txdr_unsigned(linkmax);
2119292915Sdim		pc->pc_namemax = txdr_unsigned(namemax);
2120292915Sdim		pc->pc_notrunc = txdr_unsigned(notrunc);
2121292915Sdim		pc->pc_chownrestricted = txdr_unsigned(chownres);
2122292915Sdim
2123294024Sdim		/*
2124292915Sdim		 * These should probably be supported by VOP_PATHCONF(), but
2125292915Sdim		 * until msdosfs is exportable (why would you want to?), the
2126292915Sdim		 * Unix defaults should be ok.
2127292915Sdim		 */
2128292915Sdim		pc->pc_caseinsensitive = newnfs_false;
2129292915Sdim		pc->pc_casepreserving = newnfs_true;
2130292915Sdim	}
2131292915Sdim
2132292915Sdimout:
2133292915Sdim	NFSEXITCODE2(0, nd);
2134292915Sdim	return (0);
2135292915Sdim}
2136292915Sdim
2137292915Sdim/*
2138292915Sdim * nfsv4 lock service
2139292915Sdim */
2140292915SdimAPPLESTATIC int
2141292915Sdimnfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2142292915Sdim    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2143292915Sdim{
2144292915Sdim	u_int32_t *tl;
2145292915Sdim	int i;
2146292915Sdim	struct nfsstate *stp = NULL;
2147292915Sdim	struct nfslock *lop;
2148292915Sdim	struct nfslockconflict cf;
2149292915Sdim	int error = 0;
2150292915Sdim	u_short flags = NFSLCK_LOCK, lflags;
2151292915Sdim	u_int64_t offset, len;
2152292915Sdim	nfsv4stateid_t stateid;
2153292915Sdim	nfsquad_t clientid;
2154292915Sdim
2155292915Sdim	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2156292915Sdim	i = fxdr_unsigned(int, *tl++);
2157292915Sdim	switch (i) {
2158292915Sdim	case NFSV4LOCKT_READW:
2159292915Sdim		flags |= NFSLCK_BLOCKING;
2160292915Sdim	case NFSV4LOCKT_READ:
2161292915Sdim		lflags = NFSLCK_READ;
2162292915Sdim		break;
2163292915Sdim	case NFSV4LOCKT_WRITEW:
2164292915Sdim		flags |= NFSLCK_BLOCKING;
2165292915Sdim	case NFSV4LOCKT_WRITE:
2166292915Sdim		lflags = NFSLCK_WRITE;
2167292915Sdim		break;
2168292915Sdim	default:
2169292915Sdim		nd->nd_repstat = NFSERR_BADXDR;
2170292915Sdim		goto nfsmout;
2171292915Sdim	};
2172292915Sdim	if (*tl++ == newnfs_true)
2173292915Sdim		flags |= NFSLCK_RECLAIM;
2174292915Sdim	offset = fxdr_hyper(tl);
2175292915Sdim	tl += 2;
2176292915Sdim	len = fxdr_hyper(tl);
2177292915Sdim	tl += 2;
2178292915Sdim	if (*tl == newnfs_true)
2179292915Sdim		flags |= NFSLCK_OPENTOLOCK;
2180292915Sdim	if (flags & NFSLCK_OPENTOLOCK) {
2181292915Sdim		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
2182292915Sdim		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
2183292915Sdim		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2184292915Sdim			nd->nd_repstat = NFSERR_BADXDR;
2185292915Sdim			goto nfsmout;
2186292915Sdim		}
2187292915Sdim		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2188292915Sdim			M_NFSDSTATE, M_WAITOK);
2189292915Sdim		stp->ls_ownerlen = i;
2190292915Sdim		stp->ls_op = nd->nd_rp;
2191292915Sdim		stp->ls_seq = fxdr_unsigned(int, *tl++);
2192292915Sdim		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2193292915Sdim		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2194292915Sdim			NFSX_STATEIDOTHER);
2195292915Sdim		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2196292915Sdim		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
2197292915Sdim		clientid.lval[0] = *tl++;
2198292915Sdim		clientid.lval[1] = *tl++;
2199292915Sdim		if (nd->nd_flag & ND_IMPLIEDCLID) {
2200292915Sdim			if (nd->nd_clientid.qval != clientid.qval)
2201292915Sdim				printf("EEK! multiple clids\n");
2202292915Sdim		} else {
2203292915Sdim			nd->nd_flag |= ND_IMPLIEDCLID;
2204292915Sdim			nd->nd_clientid.qval = clientid.qval;
2205292915Sdim		}
2206292915Sdim		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2207292915Sdim		if (error)
2208292915Sdim			goto nfsmout;
2209292915Sdim	} else {
2210292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2211292915Sdim		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2212292915Sdim			M_NFSDSTATE, M_WAITOK);
2213292915Sdim		stp->ls_ownerlen = 0;
2214292915Sdim		stp->ls_op = nd->nd_rp;
2215292915Sdim		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2216292915Sdim		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2217292915Sdim			NFSX_STATEIDOTHER);
2218292915Sdim		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2219292915Sdim		stp->ls_seq = fxdr_unsigned(int, *tl);
2220292915Sdim		clientid.lval[0] = stp->ls_stateid.other[0];
2221292915Sdim		clientid.lval[1] = stp->ls_stateid.other[1];
2222292915Sdim		if (nd->nd_flag & ND_IMPLIEDCLID) {
2223292915Sdim			if (nd->nd_clientid.qval != clientid.qval)
2224292915Sdim				printf("EEK! multiple clids\n");
2225292915Sdim		} else {
2226292915Sdim			nd->nd_flag |= ND_IMPLIEDCLID;
2227292915Sdim			nd->nd_clientid.qval = clientid.qval;
2228292915Sdim		}
2229292915Sdim	}
2230292915Sdim	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2231292915Sdim		M_NFSDLOCK, M_WAITOK);
2232292915Sdim	lop->lo_first = offset;
2233292915Sdim	if (len == NFS64BITSSET) {
2234292915Sdim		lop->lo_end = NFS64BITSSET;
2235292915Sdim	} else {
2236292915Sdim		lop->lo_end = offset + len;
2237292915Sdim		if (lop->lo_end <= lop->lo_first)
2238292915Sdim			nd->nd_repstat = NFSERR_INVAL;
2239292915Sdim	}
2240292915Sdim	lop->lo_flags = lflags;
2241292915Sdim	stp->ls_flags = flags;
2242292915Sdim	stp->ls_uid = nd->nd_cred->cr_uid;
2243292915Sdim
2244292915Sdim	/*
2245292915Sdim	 * Do basic access checking.
2246292915Sdim	 */
2247292915Sdim	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2248292915Sdim	    if (vnode_vtype(vp) == VDIR)
2249292915Sdim		nd->nd_repstat = NFSERR_ISDIR;
2250292915Sdim	    else
2251292915Sdim		nd->nd_repstat = NFSERR_INVAL;
2252292915Sdim	}
2253292915Sdim	if (!nd->nd_repstat) {
2254292915Sdim	    if (lflags & NFSLCK_WRITE) {
2255292915Sdim		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
2256292915Sdim		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2257292915Sdim		    NFSACCCHK_VPISLOCKED, NULL);
2258292915Sdim	    } else {
2259292915Sdim		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
2260292915Sdim		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2261292915Sdim		    NFSACCCHK_VPISLOCKED, NULL);
2262292915Sdim		if (nd->nd_repstat)
2263292915Sdim		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2264292915Sdim			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2265292915Sdim			NFSACCCHK_VPISLOCKED, NULL);
2266292915Sdim	    }
2267292915Sdim	}
2268292915Sdim
2269292915Sdim	/*
2270292915Sdim	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
2271292915Sdim	 * seqid# gets updated. nfsrv_lockctrl() will return the value
2272292915Sdim	 * of nd_repstat, if it gets that far.
2273292915Sdim	 */
2274292915Sdim	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2275292915Sdim		&stateid, exp, nd, p);
2276292915Sdim	if (lop)
2277292915Sdim		FREE((caddr_t)lop, M_NFSDLOCK);
2278292915Sdim	if (stp)
2279292915Sdim		FREE((caddr_t)stp, M_NFSDSTATE);
2280292915Sdim	if (!nd->nd_repstat) {
2281292915Sdim		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2282292915Sdim		*tl++ = txdr_unsigned(stateid.seqid);
2283292915Sdim		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2284292915Sdim	} else if (nd->nd_repstat == NFSERR_DENIED) {
2285292915Sdim		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2286292915Sdim		txdr_hyper(cf.cl_first, tl);
2287292915Sdim		tl += 2;
2288292915Sdim		if (cf.cl_end == NFS64BITSSET)
2289292915Sdim			len = NFS64BITSSET;
2290292915Sdim		else
2291292915Sdim			len = cf.cl_end - cf.cl_first;
2292292915Sdim		txdr_hyper(len, tl);
2293292915Sdim		tl += 2;
2294292915Sdim		if (cf.cl_flags == NFSLCK_WRITE)
2295292915Sdim			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2296292915Sdim		else
2297292915Sdim			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2298292915Sdim		*tl++ = stateid.other[0];
2299292915Sdim		*tl = stateid.other[1];
2300292915Sdim		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2301292915Sdim	}
2302292915Sdim	vput(vp);
2303292915Sdim	NFSEXITCODE2(0, nd);
2304292915Sdim	return (0);
2305292915Sdimnfsmout:
2306292915Sdim	vput(vp);
2307292915Sdim	if (stp)
2308292915Sdim		free((caddr_t)stp, M_NFSDSTATE);
2309292915Sdim	NFSEXITCODE2(error, nd);
2310292915Sdim	return (error);
2311292915Sdim}
2312292915Sdim
2313292915Sdim/*
2314292915Sdim * nfsv4 lock test service
2315292915Sdim */
2316292915SdimAPPLESTATIC int
2317292915Sdimnfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2318292915Sdim    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2319292915Sdim{
2320292915Sdim	u_int32_t *tl;
2321292915Sdim	int i;
2322292915Sdim	struct nfsstate *stp = NULL;
2323292915Sdim	struct nfslock lo, *lop = &lo;
2324292915Sdim	struct nfslockconflict cf;
2325292915Sdim	int error = 0;
2326292915Sdim	nfsv4stateid_t stateid;
2327292915Sdim	nfsquad_t clientid;
2328292915Sdim	u_int64_t len;
2329292915Sdim
2330292915Sdim	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
2331292915Sdim	i = fxdr_unsigned(int, *(tl + 7));
2332292915Sdim	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2333292915Sdim		nd->nd_repstat = NFSERR_BADXDR;
2334292915Sdim		goto nfsmout;
2335292915Sdim	}
2336292915Sdim	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2337292915Sdim	    M_NFSDSTATE, M_WAITOK);
2338292915Sdim	stp->ls_ownerlen = i;
2339292915Sdim	stp->ls_op = NULL;
2340292915Sdim	stp->ls_flags = NFSLCK_TEST;
2341292915Sdim	stp->ls_uid = nd->nd_cred->cr_uid;
2342292915Sdim	i = fxdr_unsigned(int, *tl++);
2343292915Sdim	switch (i) {
2344292915Sdim	case NFSV4LOCKT_READW:
2345292915Sdim		stp->ls_flags |= NFSLCK_BLOCKING;
2346292915Sdim	case NFSV4LOCKT_READ:
2347292915Sdim		lo.lo_flags = NFSLCK_READ;
2348292915Sdim		break;
2349292915Sdim	case NFSV4LOCKT_WRITEW:
2350292915Sdim		stp->ls_flags |= NFSLCK_BLOCKING;
2351292915Sdim	case NFSV4LOCKT_WRITE:
2352292915Sdim		lo.lo_flags = NFSLCK_WRITE;
2353292915Sdim		break;
2354292915Sdim	default:
2355292915Sdim		nd->nd_repstat = NFSERR_BADXDR;
2356292915Sdim		goto nfsmout;
2357292915Sdim	};
2358292915Sdim	lo.lo_first = fxdr_hyper(tl);
2359292915Sdim	tl += 2;
2360292915Sdim	len = fxdr_hyper(tl);
2361292915Sdim	if (len == NFS64BITSSET) {
2362292915Sdim		lo.lo_end = NFS64BITSSET;
2363292915Sdim	} else {
2364292915Sdim		lo.lo_end = lo.lo_first + len;
2365292915Sdim		if (lo.lo_end <= lo.lo_first)
2366292915Sdim			nd->nd_repstat = NFSERR_INVAL;
2367292915Sdim	}
2368292915Sdim	tl += 2;
2369292915Sdim	clientid.lval[0] = *tl++;
2370292915Sdim	clientid.lval[1] = *tl;
2371292915Sdim	if (nd->nd_flag & ND_IMPLIEDCLID) {
2372292915Sdim		if (nd->nd_clientid.qval != clientid.qval)
2373292915Sdim			printf("EEK! multiple clids\n");
2374292915Sdim	} else {
2375292915Sdim		nd->nd_flag |= ND_IMPLIEDCLID;
2376292915Sdim		nd->nd_clientid.qval = clientid.qval;
2377292915Sdim	}
2378292915Sdim	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2379292915Sdim	if (error)
2380292915Sdim		goto nfsmout;
2381292915Sdim	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2382292915Sdim	    if (vnode_vtype(vp) == VDIR)
2383292915Sdim		nd->nd_repstat = NFSERR_ISDIR;
2384292915Sdim	    else
2385292915Sdim		nd->nd_repstat = NFSERR_INVAL;
2386292915Sdim	}
2387292915Sdim	if (!nd->nd_repstat)
2388292915Sdim	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2389292915Sdim	    &stateid, exp, nd, p);
2390292915Sdim	if (stp)
2391292915Sdim		FREE((caddr_t)stp, M_NFSDSTATE);
2392292915Sdim	if (nd->nd_repstat) {
2393292915Sdim	    if (nd->nd_repstat == NFSERR_DENIED) {
2394292915Sdim		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2395292915Sdim		txdr_hyper(cf.cl_first, tl);
2396292915Sdim		tl += 2;
2397292915Sdim		if (cf.cl_end == NFS64BITSSET)
2398292915Sdim			len = NFS64BITSSET;
2399292915Sdim		else
2400292915Sdim			len = cf.cl_end - cf.cl_first;
2401292915Sdim		txdr_hyper(len, tl);
2402292915Sdim		tl += 2;
2403292915Sdim		if (cf.cl_flags == NFSLCK_WRITE)
2404292915Sdim			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2405292915Sdim		else
2406292915Sdim			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2407292915Sdim		*tl++ = stp->ls_stateid.other[0];
2408292915Sdim		*tl = stp->ls_stateid.other[1];
2409292915Sdim		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2410292915Sdim	    }
2411292915Sdim	}
2412292915Sdim	vput(vp);
2413292915Sdim	NFSEXITCODE2(0, nd);
2414292915Sdim	return (0);
2415292915Sdimnfsmout:
2416292915Sdim	vput(vp);
2417292915Sdim	if (stp)
2418292915Sdim		free((caddr_t)stp, M_NFSDSTATE);
2419292915Sdim	NFSEXITCODE2(error, nd);
2420292915Sdim	return (error);
2421292915Sdim}
2422292915Sdim
2423292915Sdim/*
2424292915Sdim * nfsv4 unlock service
2425292915Sdim */
2426292915SdimAPPLESTATIC int
2427292915Sdimnfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2428292915Sdim    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2429292915Sdim{
2430292915Sdim	u_int32_t *tl;
2431292915Sdim	int i;
2432292915Sdim	struct nfsstate *stp;
2433292915Sdim	struct nfslock *lop;
2434292915Sdim	int error = 0;
2435292915Sdim	nfsv4stateid_t stateid;
2436292915Sdim	nfsquad_t clientid;
2437292915Sdim	u_int64_t len;
2438292915Sdim
2439292915Sdim	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2440292915Sdim	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2441292915Sdim	    M_NFSDSTATE, M_WAITOK);
2442292915Sdim	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2443292915Sdim	    M_NFSDLOCK, M_WAITOK);
2444292915Sdim	stp->ls_flags = NFSLCK_UNLOCK;
2445292915Sdim	lop->lo_flags = NFSLCK_UNLOCK;
2446292915Sdim	stp->ls_op = nd->nd_rp;
2447292915Sdim	i = fxdr_unsigned(int, *tl++);
2448292915Sdim	switch (i) {
2449292915Sdim	case NFSV4LOCKT_READW:
2450292915Sdim		stp->ls_flags |= NFSLCK_BLOCKING;
2451292915Sdim	case NFSV4LOCKT_READ:
2452292915Sdim		break;
2453292915Sdim	case NFSV4LOCKT_WRITEW:
2454292915Sdim		stp->ls_flags |= NFSLCK_BLOCKING;
2455292915Sdim	case NFSV4LOCKT_WRITE:
2456292915Sdim		break;
2457292915Sdim	default:
2458292915Sdim		nd->nd_repstat = NFSERR_BADXDR;
2459292915Sdim		free(stp, M_NFSDSTATE);
2460292915Sdim		free(lop, M_NFSDLOCK);
2461292915Sdim		goto nfsmout;
2462292915Sdim	};
2463292915Sdim	stp->ls_ownerlen = 0;
2464292915Sdim	stp->ls_uid = nd->nd_cred->cr_uid;
2465292915Sdim	stp->ls_seq = fxdr_unsigned(int, *tl++);
2466292915Sdim	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2467292915Sdim	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2468292915Sdim	    NFSX_STATEIDOTHER);
2469292915Sdim	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2470292915Sdim	lop->lo_first = fxdr_hyper(tl);
2471292915Sdim	tl += 2;
2472292915Sdim	len = fxdr_hyper(tl);
2473292915Sdim	if (len == NFS64BITSSET) {
2474292915Sdim		lop->lo_end = NFS64BITSSET;
2475292915Sdim	} else {
2476292915Sdim		lop->lo_end = lop->lo_first + len;
2477292915Sdim		if (lop->lo_end <= lop->lo_first)
2478292915Sdim			nd->nd_repstat = NFSERR_INVAL;
2479292915Sdim	}
2480292915Sdim	clientid.lval[0] = stp->ls_stateid.other[0];
2481292915Sdim	clientid.lval[1] = stp->ls_stateid.other[1];
2482292915Sdim	if (nd->nd_flag & ND_IMPLIEDCLID) {
2483292915Sdim		if (nd->nd_clientid.qval != clientid.qval)
2484292915Sdim			printf("EEK! multiple clids\n");
2485292915Sdim	} else {
2486292915Sdim		nd->nd_flag |= ND_IMPLIEDCLID;
2487292915Sdim		nd->nd_clientid.qval = clientid.qval;
2488292915Sdim	}
2489292915Sdim	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2490292915Sdim	    if (vnode_vtype(vp) == VDIR)
2491292915Sdim		nd->nd_repstat = NFSERR_ISDIR;
2492292915Sdim	    else
2493292915Sdim		nd->nd_repstat = NFSERR_INVAL;
2494292915Sdim	}
2495292915Sdim	/*
2496292915Sdim	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
2497292915Sdim	 * seqid# gets incremented. nfsrv_lockctrl() will return the
2498292915Sdim	 * value of nd_repstat, if it gets that far.
2499292915Sdim	 */
2500292915Sdim	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
2501292915Sdim	    &stateid, exp, nd, p);
2502292915Sdim	if (stp)
2503292915Sdim		FREE((caddr_t)stp, M_NFSDSTATE);
2504292915Sdim	if (lop)
2505292915Sdim		free((caddr_t)lop, M_NFSDLOCK);
2506292915Sdim	if (!nd->nd_repstat) {
2507292915Sdim		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2508292915Sdim		*tl++ = txdr_unsigned(stateid.seqid);
2509292915Sdim		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2510292915Sdim	}
2511292915Sdimnfsmout:
2512292915Sdim	vput(vp);
2513292915Sdim	NFSEXITCODE2(error, nd);
2514292915Sdim	return (error);
2515292915Sdim}
2516292915Sdim
2517292915Sdim/*
2518292915Sdim * nfsv4 open service
2519292915Sdim */
2520292915SdimAPPLESTATIC int
2521292915Sdimnfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2522292915Sdim    vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
2523292915Sdim    struct nfsexstuff *exp)
2524292915Sdim{
2525292915Sdim	u_int32_t *tl;
2526292915Sdim	int i;
2527292915Sdim	struct nfsstate *stp = NULL;
2528292915Sdim	int error = 0, create, claim, exclusive_flag = 0;
2529292915Sdim	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
2530292915Sdim	int how = NFSCREATE_UNCHECKED;
2531292915Sdim	int32_t cverf[2], tverf[2] = { 0, 0 };
2532292915Sdim	vnode_t vp = NULL, dirp = NULL;
2533292915Sdim	struct nfsvattr nva, dirfor, diraft;
2534292915Sdim	struct nameidata named;
2535292915Sdim	nfsv4stateid_t stateid, delegstateid;
2536292915Sdim	nfsattrbit_t attrbits;
2537292915Sdim	nfsquad_t clientid;
2538292915Sdim	char *bufp = NULL;
2539292915Sdim	u_long *hashp;
2540292915Sdim	NFSACL_T *aclp = NULL;
2541292915Sdim
2542292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
2543292915Sdim	aclp = acl_alloc(M_WAITOK);
2544292915Sdim	aclp->acl_cnt = 0;
2545292915Sdim#endif
2546292915Sdim	NFSZERO_ATTRBIT(&attrbits);
2547292915Sdim	named.ni_startdir = NULL;
2548292915Sdim	named.ni_cnd.cn_nameiop = 0;
2549292915Sdim	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
2550292915Sdim	i = fxdr_unsigned(int, *(tl + 5));
2551292915Sdim	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2552292915Sdim		nd->nd_repstat = NFSERR_BADXDR;
2553292915Sdim		goto nfsmout;
2554292915Sdim	}
2555292915Sdim	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2556292915Sdim	    M_NFSDSTATE, M_WAITOK);
2557292915Sdim	stp->ls_ownerlen = i;
2558292915Sdim	stp->ls_op = nd->nd_rp;
2559292915Sdim	stp->ls_flags = NFSLCK_OPEN;
2560292915Sdim	stp->ls_uid = nd->nd_cred->cr_uid;
2561292915Sdim	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2562292915Sdim	i = fxdr_unsigned(int, *tl++);
2563292915Sdim	switch (i) {
2564292915Sdim	case NFSV4OPEN_ACCESSREAD:
2565292915Sdim		stp->ls_flags |= NFSLCK_READACCESS;
2566292915Sdim		break;
2567292915Sdim	case NFSV4OPEN_ACCESSWRITE:
2568292915Sdim		stp->ls_flags |= NFSLCK_WRITEACCESS;
2569292915Sdim		break;
2570292915Sdim	case NFSV4OPEN_ACCESSBOTH:
2571292915Sdim		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
2572292915Sdim		break;
2573292915Sdim	default:
2574292915Sdim		nd->nd_repstat = NFSERR_INVAL;
2575292915Sdim	};
2576292915Sdim	i = fxdr_unsigned(int, *tl++);
2577292915Sdim	switch (i) {
2578292915Sdim	case NFSV4OPEN_DENYNONE:
2579292915Sdim		break;
2580292915Sdim	case NFSV4OPEN_DENYREAD:
2581292915Sdim		stp->ls_flags |= NFSLCK_READDENY;
2582292915Sdim		break;
2583292915Sdim	case NFSV4OPEN_DENYWRITE:
2584292915Sdim		stp->ls_flags |= NFSLCK_WRITEDENY;
2585292915Sdim		break;
2586292915Sdim	case NFSV4OPEN_DENYBOTH:
2587292915Sdim		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
2588292915Sdim		break;
2589292915Sdim	default:
2590292915Sdim		nd->nd_repstat = NFSERR_INVAL;
2591292915Sdim	};
2592292915Sdim	clientid.lval[0] = *tl++;
2593292915Sdim	clientid.lval[1] = *tl;
2594292915Sdim	if (nd->nd_flag & ND_IMPLIEDCLID) {
2595292915Sdim		if (nd->nd_clientid.qval != clientid.qval)
2596292915Sdim			printf("EEK! multiple clids\n");
2597292915Sdim	} else {
2598292915Sdim		nd->nd_flag |= ND_IMPLIEDCLID;
2599292915Sdim		nd->nd_clientid.qval = clientid.qval;
2600292915Sdim	}
2601292915Sdim	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2602292915Sdim	if (error)
2603292915Sdim		goto nfsmout;
2604292915Sdim	NFSVNO_ATTRINIT(&nva);
2605292915Sdim	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2606292915Sdim	create = fxdr_unsigned(int, *tl);
2607292915Sdim	if (!nd->nd_repstat)
2608292915Sdim		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
2609292915Sdim	if (create == NFSV4OPEN_CREATE) {
2610292915Sdim		nva.na_type = VREG;
2611292915Sdim		nva.na_mode = 0;
2612292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2613292915Sdim		how = fxdr_unsigned(int, *tl);
2614292915Sdim		switch (how) {
2615292915Sdim		case NFSCREATE_UNCHECKED:
2616292915Sdim		case NFSCREATE_GUARDED:
2617292915Sdim			error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p);
2618292915Sdim			if (error)
2619292915Sdim				goto nfsmout;
2620292915Sdim			/*
2621292915Sdim			 * If the na_gid being set is the same as that of
2622292915Sdim			 * the directory it is going in, clear it, since
2623292915Sdim			 * that is what will be set by default. This allows
2624292915Sdim			 * a user that isn't in that group to do the create.
2625292915Sdim			 */
2626292915Sdim			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
2627292915Sdim			    nva.na_gid == dirfor.na_gid)
2628292915Sdim				NFSVNO_UNSET(&nva, gid);
2629292915Sdim			if (!nd->nd_repstat)
2630292915Sdim				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2631292915Sdim			break;
2632292915Sdim		case NFSCREATE_EXCLUSIVE:
2633292915Sdim			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2634292915Sdim			cverf[0] = *tl++;
2635292915Sdim			cverf[1] = *tl;
2636292915Sdim			break;
2637292915Sdim		default:
2638292915Sdim			nd->nd_repstat = NFSERR_BADXDR;
2639292915Sdim			goto nfsmout;
2640292915Sdim		};
2641292915Sdim	} else if (create != NFSV4OPEN_NOCREATE) {
2642292915Sdim		nd->nd_repstat = NFSERR_BADXDR;
2643292915Sdim		goto nfsmout;
2644292915Sdim	}
2645292915Sdim
2646292915Sdim	/*
2647292915Sdim	 * Now, handle the claim, which usually includes looking up a
2648292915Sdim	 * name in the directory referenced by dp. The exception is
2649292915Sdim	 * NFSV4OPEN_CLAIMPREVIOUS.
2650292915Sdim	 */
2651292915Sdim	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2652292915Sdim	claim = fxdr_unsigned(int, *tl);
2653292915Sdim	if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
2654292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2655292915Sdim		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2656292915Sdim		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
2657292915Sdim		stp->ls_flags |= NFSLCK_DELEGCUR;
2658292915Sdim	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2659292915Sdim		stp->ls_flags |= NFSLCK_DELEGPREV;
2660292915Sdim	}
2661292915Sdim	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
2662292915Sdim	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2663292915Sdim		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
2664292915Sdim		    claim != NFSV4OPEN_CLAIMNULL)
2665292915Sdim			nd->nd_repstat = NFSERR_INVAL;
2666292915Sdim		if (nd->nd_repstat) {
2667292915Sdim			nd->nd_repstat = nfsrv_opencheck(clientid,
2668292915Sdim			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
2669292915Sdim			goto nfsmout;
2670292915Sdim		}
2671292915Sdim		if (create == NFSV4OPEN_CREATE)
2672292915Sdim		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
2673292915Sdim			LOCKPARENT | LOCKLEAF | SAVESTART);
2674292915Sdim		else
2675292915Sdim		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
2676292915Sdim			LOCKLEAF | SAVESTART);
2677292915Sdim		nfsvno_setpathbuf(&named, &bufp, &hashp);
2678292915Sdim		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2679292915Sdim		if (error) {
2680292915Sdim			vrele(dp);
2681292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME
2682292915Sdim			acl_free(aclp);
2683292915Sdim#endif
2684292915Sdim			FREE((caddr_t)stp, M_NFSDSTATE);
2685292915Sdim			nfsvno_relpathbuf(&named);
2686292915Sdim			NFSEXITCODE2(error, nd);
2687292915Sdim			return (error);
2688292915Sdim		}
2689292915Sdim		if (!nd->nd_repstat) {
2690292915Sdim			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
2691292915Sdim			    p, &dirp);
2692292915Sdim		} else {
2693292915Sdim			vrele(dp);
2694292915Sdim			nfsvno_relpathbuf(&named);
2695292915Sdim		}
2696292915Sdim		if (create == NFSV4OPEN_CREATE) {
2697292915Sdim		    switch (how) {
2698292915Sdim		    case NFSCREATE_UNCHECKED:
2699292915Sdim			if (named.ni_vp) {
2700292915Sdim				/*
2701292915Sdim				 * Clear the setable attribute bits, except
2702292915Sdim				 * for Size, if it is being truncated.
2703292915Sdim				 */
2704292915Sdim				NFSZERO_ATTRBIT(&attrbits);
2705292915Sdim				if (NFSVNO_ISSETSIZE(&nva))
2706292915Sdim					NFSSETBIT_ATTRBIT(&attrbits,
2707292915Sdim					    NFSATTRBIT_SIZE);
2708292915Sdim			}
2709292915Sdim			break;
2710292915Sdim		    case NFSCREATE_GUARDED:
2711292915Sdim			if (named.ni_vp && !nd->nd_repstat)
2712292915Sdim				nd->nd_repstat = EEXIST;
2713292915Sdim			break;
2714292915Sdim		    case NFSCREATE_EXCLUSIVE:
2715292915Sdim			exclusive_flag = 1;
2716292915Sdim			if (!named.ni_vp)
2717292915Sdim				nva.na_mode = 0;
2718292915Sdim		    };
2719292915Sdim		}
2720292915Sdim		nfsvno_open(nd, &named, clientid, &stateid, stp,
2721292915Sdim		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
2722292915Sdim		    nd->nd_cred, p, exp, &vp);
2723292915Sdim	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2724292915Sdim		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2725292915Sdim		i = fxdr_unsigned(int, *tl);
2726292915Sdim		switch (i) {
2727292915Sdim		case NFSV4OPEN_DELEGATEREAD:
2728292915Sdim			stp->ls_flags |= NFSLCK_DELEGREAD;
2729292915Sdim			break;
2730292915Sdim		case NFSV4OPEN_DELEGATEWRITE:
2731292915Sdim			stp->ls_flags |= NFSLCK_DELEGWRITE;
2732292915Sdim		case NFSV4OPEN_DELEGATENONE:
2733292915Sdim			break;
2734292915Sdim		default:
2735292915Sdim			nd->nd_repstat = NFSERR_BADXDR;
2736292915Sdim			goto nfsmout;
2737292915Sdim		};
2738292915Sdim		stp->ls_flags |= NFSLCK_RECLAIM;
2739292915Sdim		vp = dp;
2740292915Sdim		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
2741292915Sdim		if ((vp->v_iflag & VI_DOOMED) == 0)
2742292915Sdim			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
2743292915Sdim			    stp, vp, nd, p, nd->nd_repstat);
2744292915Sdim		else
2745292915Sdim			nd->nd_repstat = NFSERR_PERM;
2746292915Sdim	} else {
2747292915Sdim		nd->nd_repstat = NFSERR_BADXDR;
2748292915Sdim		goto nfsmout;
2749292915Sdim	}
2750292915Sdim
2751292915Sdim	/*
2752292915Sdim	 * Do basic access checking.
2753292915Sdim	 */
2754292915Sdim	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2755292915Sdim		/*
2756292915Sdim		 * The IETF working group decided that this is the correct
2757292915Sdim		 * error return for all non-regular files.
2758292915Sdim		 */
2759292915Sdim		nd->nd_repstat = NFSERR_SYMLINK;
2760292915Sdim	}
2761292915Sdim	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
2762292915Sdim	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
2763292915Sdim	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2764292915Sdim	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
2765292915Sdim	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
2766292915Sdim	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2767292915Sdim	    if (nd->nd_repstat)
2768292915Sdim		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2769292915Sdim		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2770292915Sdim		    NFSACCCHK_VPISLOCKED, NULL);
2771292915Sdim	}
2772292915Sdim
2773292915Sdim	if (!nd->nd_repstat) {
2774292915Sdim		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
2775292915Sdim		if (!nd->nd_repstat) {
2776292915Sdim			tverf[0] = nva.na_atime.tv_sec;
2777292915Sdim			tverf[1] = nva.na_atime.tv_nsec;
2778292915Sdim		}
2779292915Sdim	}
2780292915Sdim	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
2781292915Sdim	    cverf[1] != tverf[1]))
2782292915Sdim		nd->nd_repstat = EEXIST;
2783292915Sdim	/*
2784292915Sdim	 * Do the open locking/delegation stuff.
2785292915Sdim	 */
2786292915Sdim	if (!nd->nd_repstat)
2787292915Sdim	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
2788292915Sdim		&delegstateid, &rflags, exp, p, nva.na_filerev);
2789292915Sdim
2790292915Sdim	/*
2791	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
2792	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
2793	 * (ie: Leave the NFSVOPUNLOCK() about here.)
2794	 */
2795	if (vp)
2796		NFSVOPUNLOCK(vp, 0);
2797	if (stp)
2798		FREE((caddr_t)stp, M_NFSDSTATE);
2799	if (!nd->nd_repstat && dirp)
2800		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p,
2801		    0);
2802	if (!nd->nd_repstat) {
2803		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
2804		*tl++ = txdr_unsigned(stateid.seqid);
2805		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2806		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2807		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2808			*tl++ = newnfs_true;
2809			*tl++ = 0;
2810			*tl++ = 0;
2811			*tl++ = 0;
2812			*tl++ = 0;
2813		} else {
2814			*tl++ = newnfs_false;	/* Since dirp is not locked */
2815			txdr_hyper(dirfor.na_filerev, tl);
2816			tl += 2;
2817			txdr_hyper(diraft.na_filerev, tl);
2818			tl += 2;
2819		}
2820		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
2821		(void) nfsrv_putattrbit(nd, &attrbits);
2822		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2823		if (rflags & NFSV4OPEN_READDELEGATE)
2824			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
2825		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
2826			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
2827		else
2828			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
2829		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
2830			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
2831			*tl++ = txdr_unsigned(delegstateid.seqid);
2832			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
2833			    NFSX_STATEIDOTHER);
2834			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2835			if (rflags & NFSV4OPEN_RECALL)
2836				*tl = newnfs_true;
2837			else
2838				*tl = newnfs_false;
2839			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
2840				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2841				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
2842				txdr_hyper(nva.na_size, tl);
2843			}
2844			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2845			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
2846			*tl++ = txdr_unsigned(0x0);
2847			acemask = NFSV4ACE_ALLFILESMASK;
2848			if (nva.na_mode & S_IRUSR)
2849			    acemask |= NFSV4ACE_READMASK;
2850			if (nva.na_mode & S_IWUSR)
2851			    acemask |= NFSV4ACE_WRITEMASK;
2852			if (nva.na_mode & S_IXUSR)
2853			    acemask |= NFSV4ACE_EXECUTEMASK;
2854			*tl = txdr_unsigned(acemask);
2855			(void) nfsm_strtom(nd, "OWNER@", 6);
2856		}
2857		*vpp = vp;
2858	} else if (vp) {
2859		vrele(vp);
2860	}
2861	if (dirp)
2862		vrele(dirp);
2863#ifdef NFS4_ACL_EXTATTR_NAME
2864	acl_free(aclp);
2865#endif
2866	NFSEXITCODE2(0, nd);
2867	return (0);
2868nfsmout:
2869	vrele(dp);
2870#ifdef NFS4_ACL_EXTATTR_NAME
2871	acl_free(aclp);
2872#endif
2873	if (stp)
2874		FREE((caddr_t)stp, M_NFSDSTATE);
2875	NFSEXITCODE2(error, nd);
2876	return (error);
2877}
2878
2879/*
2880 * nfsv4 close service
2881 */
2882APPLESTATIC int
2883nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
2884    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2885{
2886	u_int32_t *tl;
2887	struct nfsstate st, *stp = &st;
2888	int error = 0;
2889	nfsv4stateid_t stateid;
2890	nfsquad_t clientid;
2891
2892	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
2893	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2894	stp->ls_ownerlen = 0;
2895	stp->ls_op = nd->nd_rp;
2896	stp->ls_uid = nd->nd_cred->cr_uid;
2897	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2898	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2899	    NFSX_STATEIDOTHER);
2900	stp->ls_flags = NFSLCK_CLOSE;
2901	clientid.lval[0] = stp->ls_stateid.other[0];
2902	clientid.lval[1] = stp->ls_stateid.other[1];
2903	if (nd->nd_flag & ND_IMPLIEDCLID) {
2904		if (nd->nd_clientid.qval != clientid.qval)
2905			printf("EEK! multiple clids\n");
2906	} else {
2907		nd->nd_flag |= ND_IMPLIEDCLID;
2908		nd->nd_clientid.qval = clientid.qval;
2909	}
2910	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
2911	vput(vp);
2912	if (!nd->nd_repstat) {
2913		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2914		*tl++ = txdr_unsigned(stateid.seqid);
2915		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2916	}
2917	NFSEXITCODE2(0, nd);
2918	return (0);
2919nfsmout:
2920	vput(vp);
2921	NFSEXITCODE2(error, nd);
2922	return (error);
2923}
2924
2925/*
2926 * nfsv4 delegpurge service
2927 */
2928APPLESTATIC int
2929nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
2930    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
2931{
2932	u_int32_t *tl;
2933	int error = 0;
2934	nfsquad_t clientid;
2935
2936	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
2937		nd->nd_repstat = NFSERR_WRONGSEC;
2938		goto nfsmout;
2939	}
2940	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2941	clientid.lval[0] = *tl++;
2942	clientid.lval[1] = *tl;
2943	if (nd->nd_flag & ND_IMPLIEDCLID) {
2944		if (nd->nd_clientid.qval != clientid.qval)
2945			printf("EEK! multiple clids\n");
2946	} else {
2947		nd->nd_flag |= ND_IMPLIEDCLID;
2948		nd->nd_clientid.qval = clientid.qval;
2949	}
2950	nd->nd_repstat = nfsrv_delegupdate(clientid, NULL, NULL,
2951	    NFSV4OP_DELEGPURGE, nd->nd_cred, p);
2952nfsmout:
2953	NFSEXITCODE2(error, nd);
2954	return (error);
2955}
2956
2957/*
2958 * nfsv4 delegreturn service
2959 */
2960APPLESTATIC int
2961nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
2962    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2963{
2964	u_int32_t *tl;
2965	int error = 0;
2966	nfsv4stateid_t stateid;
2967	nfsquad_t clientid;
2968
2969	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2970	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2971	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
2972	clientid.lval[0] = stateid.other[0];
2973	clientid.lval[1] = stateid.other[1];
2974	if (nd->nd_flag & ND_IMPLIEDCLID) {
2975		if (nd->nd_clientid.qval != clientid.qval)
2976			printf("EEK! multiple clids\n");
2977	} else {
2978		nd->nd_flag |= ND_IMPLIEDCLID;
2979		nd->nd_clientid.qval = clientid.qval;
2980	}
2981	nd->nd_repstat = nfsrv_delegupdate(clientid, &stateid, vp,
2982	    NFSV4OP_DELEGRETURN, nd->nd_cred, p);
2983nfsmout:
2984	vput(vp);
2985	NFSEXITCODE2(error, nd);
2986	return (error);
2987}
2988
2989/*
2990 * nfsv4 get file handle service
2991 */
2992APPLESTATIC int
2993nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
2994    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2995{
2996	fhandle_t fh;
2997
2998	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
2999	vput(vp);
3000	if (!nd->nd_repstat)
3001		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3002	NFSEXITCODE2(0, nd);
3003	return (0);
3004}
3005
3006/*
3007 * nfsv4 open confirm service
3008 */
3009APPLESTATIC int
3010nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3011    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3012{
3013	u_int32_t *tl;
3014	struct nfsstate st, *stp = &st;
3015	int error = 0;
3016	nfsv4stateid_t stateid;
3017	nfsquad_t clientid;
3018
3019	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3020	stp->ls_ownerlen = 0;
3021	stp->ls_op = nd->nd_rp;
3022	stp->ls_uid = nd->nd_cred->cr_uid;
3023	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3024	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3025	    NFSX_STATEIDOTHER);
3026	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3027	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
3028	stp->ls_flags = NFSLCK_CONFIRM;
3029	clientid.lval[0] = stp->ls_stateid.other[0];
3030	clientid.lval[1] = stp->ls_stateid.other[1];
3031	if (nd->nd_flag & ND_IMPLIEDCLID) {
3032		if (nd->nd_clientid.qval != clientid.qval)
3033			printf("EEK! multiple clids\n");
3034	} else {
3035		nd->nd_flag |= ND_IMPLIEDCLID;
3036		nd->nd_clientid.qval = clientid.qval;
3037	}
3038	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3039	if (!nd->nd_repstat) {
3040		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3041		*tl++ = txdr_unsigned(stateid.seqid);
3042		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3043	}
3044nfsmout:
3045	vput(vp);
3046	NFSEXITCODE2(error, nd);
3047	return (error);
3048}
3049
3050/*
3051 * nfsv4 open downgrade service
3052 */
3053APPLESTATIC int
3054nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3055    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3056{
3057	u_int32_t *tl;
3058	int i;
3059	struct nfsstate st, *stp = &st;
3060	int error = 0;
3061	nfsv4stateid_t stateid;
3062	nfsquad_t clientid;
3063
3064	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
3065	stp->ls_ownerlen = 0;
3066	stp->ls_op = nd->nd_rp;
3067	stp->ls_uid = nd->nd_cred->cr_uid;
3068	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3069	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3070	    NFSX_STATEIDOTHER);
3071	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3072	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3073	i = fxdr_unsigned(int, *tl++);
3074	switch (i) {
3075	case NFSV4OPEN_ACCESSREAD:
3076		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
3077		break;
3078	case NFSV4OPEN_ACCESSWRITE:
3079		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
3080		break;
3081	case NFSV4OPEN_ACCESSBOTH:
3082		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
3083		    NFSLCK_DOWNGRADE);
3084		break;
3085	default:
3086		nd->nd_repstat = NFSERR_BADXDR;
3087	};
3088	i = fxdr_unsigned(int, *tl);
3089	switch (i) {
3090	case NFSV4OPEN_DENYNONE:
3091		break;
3092	case NFSV4OPEN_DENYREAD:
3093		stp->ls_flags |= NFSLCK_READDENY;
3094		break;
3095	case NFSV4OPEN_DENYWRITE:
3096		stp->ls_flags |= NFSLCK_WRITEDENY;
3097		break;
3098	case NFSV4OPEN_DENYBOTH:
3099		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
3100		break;
3101	default:
3102		nd->nd_repstat = NFSERR_BADXDR;
3103	};
3104
3105	clientid.lval[0] = stp->ls_stateid.other[0];
3106	clientid.lval[1] = stp->ls_stateid.other[1];
3107	if (nd->nd_flag & ND_IMPLIEDCLID) {
3108		if (nd->nd_clientid.qval != clientid.qval)
3109			printf("EEK! multiple clids\n");
3110	} else {
3111		nd->nd_flag |= ND_IMPLIEDCLID;
3112		nd->nd_clientid.qval = clientid.qval;
3113	}
3114	if (!nd->nd_repstat)
3115		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
3116		    nd, p);
3117	if (!nd->nd_repstat) {
3118		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3119		*tl++ = txdr_unsigned(stateid.seqid);
3120		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3121	}
3122nfsmout:
3123	vput(vp);
3124	NFSEXITCODE2(error, nd);
3125	return (error);
3126}
3127
3128/*
3129 * nfsv4 renew lease service
3130 */
3131APPLESTATIC int
3132nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3133    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3134{
3135	u_int32_t *tl;
3136	int error = 0;
3137	nfsquad_t clientid;
3138
3139	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3140		nd->nd_repstat = NFSERR_WRONGSEC;
3141		goto nfsmout;
3142	}
3143	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
3144	clientid.lval[0] = *tl++;
3145	clientid.lval[1] = *tl;
3146	if (nd->nd_flag & ND_IMPLIEDCLID) {
3147		if (nd->nd_clientid.qval != clientid.qval)
3148			printf("EEK! multiple clids\n");
3149	} else {
3150		nd->nd_flag |= ND_IMPLIEDCLID;
3151		nd->nd_clientid.qval = clientid.qval;
3152	}
3153	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3154	    NULL, (nfsquad_t)((u_quad_t)0), nd, p);
3155nfsmout:
3156	NFSEXITCODE2(error, nd);
3157	return (error);
3158}
3159
3160/*
3161 * nfsv4 security info service
3162 */
3163APPLESTATIC int
3164nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3165    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
3166{
3167	u_int32_t *tl;
3168	int len;
3169	struct nameidata named;
3170	vnode_t dirp = NULL, vp;
3171	struct nfsrvfh fh;
3172	struct nfsexstuff retnes;
3173	u_int32_t *sizp;
3174	int error = 0, savflag, i;
3175	char *bufp;
3176	u_long *hashp;
3177
3178	/*
3179	 * All this just to get the export flags for the name.
3180	 */
3181	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
3182	    LOCKLEAF | SAVESTART);
3183	nfsvno_setpathbuf(&named, &bufp, &hashp);
3184	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3185	if (error) {
3186		vput(dp);
3187		nfsvno_relpathbuf(&named);
3188		goto out;
3189	}
3190	if (!nd->nd_repstat) {
3191		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
3192	} else {
3193		vput(dp);
3194		nfsvno_relpathbuf(&named);
3195	}
3196	if (dirp)
3197		vrele(dirp);
3198	if (nd->nd_repstat)
3199		goto out;
3200	vrele(named.ni_startdir);
3201	nfsvno_relpathbuf(&named);
3202	fh.nfsrvfh_len = NFSX_MYFH;
3203	vp = named.ni_vp;
3204	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3205	vput(vp);
3206	savflag = nd->nd_flag;
3207	if (!nd->nd_repstat) {
3208		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0, p);
3209		if (vp)
3210			vput(vp);
3211	}
3212	nd->nd_flag = savflag;
3213	if (nd->nd_repstat)
3214		goto out;
3215
3216	/*
3217	 * Finally have the export flags for name, so we can create
3218	 * the security info.
3219	 */
3220	len = 0;
3221	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
3222	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3223		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3224			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3225			*tl = txdr_unsigned(RPCAUTH_UNIX);
3226			len++;
3227		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3228			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3229			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3230			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3231			    nfsgss_mechlist[KERBV_MECH].len);
3232			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3233			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3234			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3235			len++;
3236		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
3237			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3238			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3239			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3240			    nfsgss_mechlist[KERBV_MECH].len);
3241			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3242			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3243			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3244			len++;
3245		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
3246			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3247			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3248			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3249			    nfsgss_mechlist[KERBV_MECH].len);
3250			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3251			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3252			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3253			len++;
3254		}
3255	}
3256	*sizp = txdr_unsigned(len);
3257
3258out:
3259	NFSEXITCODE2(error, nd);
3260	return (error);
3261}
3262
3263/*
3264 * nfsv4 set client id service
3265 */
3266APPLESTATIC int
3267nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3268    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3269{
3270	u_int32_t *tl;
3271	int i;
3272	int error = 0, idlen;
3273	struct nfsclient *clp = NULL;
3274	struct sockaddr_in *rad;
3275	u_char *verf, *ucp, *ucp2, addrbuf[24];
3276	nfsquad_t clientid, confirm;
3277
3278	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3279		nd->nd_repstat = NFSERR_WRONGSEC;
3280		goto out;
3281	}
3282	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3283	verf = (u_char *)tl;
3284	tl += (NFSX_VERF / NFSX_UNSIGNED);
3285	i = fxdr_unsigned(int, *tl);
3286	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3287		nd->nd_repstat = NFSERR_BADXDR;
3288		goto nfsmout;
3289	}
3290	idlen = i;
3291	if (nd->nd_flag & ND_GSS)
3292		i += nd->nd_princlen;
3293	MALLOC(clp, struct nfsclient *, sizeof (struct nfsclient) + i,
3294	    M_NFSDCLIENT, M_WAITOK);
3295	NFSBZERO((caddr_t)clp, sizeof (struct nfsclient) + i);
3296	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3297	NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
3298	NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
3299	clp->lc_req.nr_cred = NULL;
3300	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3301	clp->lc_idlen = idlen;
3302	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3303	if (error)
3304		goto nfsmout;
3305	if (nd->nd_flag & ND_GSS) {
3306		clp->lc_flags = LCL_GSS;
3307		if (nd->nd_flag & ND_GSSINTEGRITY)
3308			clp->lc_flags |= LCL_GSSINTEGRITY;
3309		else if (nd->nd_flag & ND_GSSPRIVACY)
3310			clp->lc_flags |= LCL_GSSPRIVACY;
3311	} else {
3312		clp->lc_flags = 0;
3313	}
3314	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
3315		clp->lc_flags |= LCL_NAME;
3316		clp->lc_namelen = nd->nd_princlen;
3317		clp->lc_name = &clp->lc_id[idlen];
3318		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3319	} else {
3320		clp->lc_uid = nd->nd_cred->cr_uid;
3321		clp->lc_gid = nd->nd_cred->cr_gid;
3322	}
3323	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3324	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
3325	error = nfsrv_getclientipaddr(nd, clp);
3326	if (error)
3327		goto nfsmout;
3328	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3329	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
3330
3331	/*
3332	 * nfsrv_setclient() does the actual work of adding it to the
3333	 * client list. If there is no error, the structure has been
3334	 * linked into the client list and clp should no longer be used
3335	 * here. When an error is returned, it has not been linked in,
3336	 * so it should be free'd.
3337	 */
3338	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3339	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
3340		if (clp->lc_flags & LCL_TCPCALLBACK)
3341			(void) nfsm_strtom(nd, "tcp", 3);
3342		else
3343			(void) nfsm_strtom(nd, "udp", 3);
3344		rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *);
3345		ucp = (u_char *)&rad->sin_addr.s_addr;
3346		ucp2 = (u_char *)&rad->sin_port;
3347		sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
3348		    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
3349		    ucp2[0] & 0xff, ucp2[1] & 0xff);
3350		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
3351	}
3352	if (clp) {
3353		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3354		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3355		free((caddr_t)clp, M_NFSDCLIENT);
3356	}
3357	if (!nd->nd_repstat) {
3358		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
3359		*tl++ = clientid.lval[0];
3360		*tl++ = clientid.lval[1];
3361		*tl++ = confirm.lval[0];
3362		*tl = confirm.lval[1];
3363	}
3364
3365out:
3366	NFSEXITCODE2(0, nd);
3367	return (0);
3368nfsmout:
3369	if (clp) {
3370		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3371		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3372		free((caddr_t)clp, M_NFSDCLIENT);
3373	}
3374	NFSEXITCODE2(error, nd);
3375	return (error);
3376}
3377
3378/*
3379 * nfsv4 set client id confirm service
3380 */
3381APPLESTATIC int
3382nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
3383    __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
3384    __unused struct nfsexstuff *exp)
3385{
3386	u_int32_t *tl;
3387	int error = 0;
3388	nfsquad_t clientid, confirm;
3389
3390	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3391		nd->nd_repstat = NFSERR_WRONGSEC;
3392		goto nfsmout;
3393	}
3394	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
3395	clientid.lval[0] = *tl++;
3396	clientid.lval[1] = *tl++;
3397	confirm.lval[0] = *tl++;
3398	confirm.lval[1] = *tl;
3399
3400	/*
3401	 * nfsrv_getclient() searches the client list for a match and
3402	 * returns the appropriate NFSERR status.
3403	 */
3404	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
3405	    NULL, confirm, nd, p);
3406nfsmout:
3407	NFSEXITCODE2(error, nd);
3408	return (error);
3409}
3410
3411/*
3412 * nfsv4 verify service
3413 */
3414APPLESTATIC int
3415nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
3416    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3417{
3418	int error = 0, ret, fhsize = NFSX_MYFH;
3419	struct nfsvattr nva;
3420	struct statfs sf;
3421	struct nfsfsinfo fs;
3422	fhandle_t fh;
3423
3424	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
3425	if (!nd->nd_repstat)
3426		nd->nd_repstat = nfsvno_statfs(vp, &sf);
3427	if (!nd->nd_repstat)
3428		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3429	if (!nd->nd_repstat) {
3430		nfsvno_getfs(&fs, isdgram);
3431		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
3432		    &sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
3433		if (!error) {
3434			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
3435				if (ret == 0)
3436					nd->nd_repstat = NFSERR_SAME;
3437				else if (ret != NFSERR_NOTSAME)
3438					nd->nd_repstat = ret;
3439			} else if (ret)
3440				nd->nd_repstat = ret;
3441		}
3442	}
3443	vput(vp);
3444	NFSEXITCODE2(error, nd);
3445	return (error);
3446}
3447
3448/*
3449 * nfs openattr rpc
3450 */
3451APPLESTATIC int
3452nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
3453    vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
3454    __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3455{
3456	u_int32_t *tl;
3457	int error = 0, createdir;
3458
3459	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3460	createdir = fxdr_unsigned(int, *tl);
3461	nd->nd_repstat = NFSERR_NOTSUPP;
3462nfsmout:
3463	vrele(dp);
3464	NFSEXITCODE2(error, nd);
3465	return (error);
3466}
3467
3468/*
3469 * nfsv4 release lock owner service
3470 */
3471APPLESTATIC int
3472nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
3473    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3474{
3475	u_int32_t *tl;
3476	struct nfsstate *stp = NULL;
3477	int error = 0, len;
3478	nfsquad_t clientid;
3479
3480	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3481		nd->nd_repstat = NFSERR_WRONGSEC;
3482		goto nfsmout;
3483	}
3484	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3485	len = fxdr_unsigned(int, *(tl + 2));
3486	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
3487		nd->nd_repstat = NFSERR_BADXDR;
3488		goto nfsmout;
3489	}
3490	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len,
3491	    M_NFSDSTATE, M_WAITOK);
3492	stp->ls_ownerlen = len;
3493	stp->ls_op = NULL;
3494	stp->ls_flags = NFSLCK_RELEASE;
3495	stp->ls_uid = nd->nd_cred->cr_uid;
3496	clientid.lval[0] = *tl++;
3497	clientid.lval[1] = *tl;
3498	if (nd->nd_flag & ND_IMPLIEDCLID) {
3499		if (nd->nd_clientid.qval != clientid.qval)
3500			printf("EEK! multiple clids\n");
3501	} else {
3502		nd->nd_flag |= ND_IMPLIEDCLID;
3503		nd->nd_clientid.qval = clientid.qval;
3504	}
3505	error = nfsrv_mtostr(nd, stp->ls_owner, len);
3506	if (error)
3507		goto nfsmout;
3508	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
3509	FREE((caddr_t)stp, M_NFSDSTATE);
3510
3511	NFSEXITCODE2(0, nd);
3512	return (0);
3513nfsmout:
3514	if (stp)
3515		free((caddr_t)stp, M_NFSDSTATE);
3516	NFSEXITCODE2(error, nd);
3517	return (error);
3518}
3519