nfs_nfsdserv.c revision 346779
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: stable/10/sys/fs/nfsserver/nfs_nfsdserv.c 346779 2019-04-27 02:43:27Z rmacklem $");
36
37#include "opt_inet.h"
38#include "opt_inet6.h"
39/*
40 * nfs version 2, 3 and 4 server calls to vnode ops
41 * - these routines generally have 3 phases
42 *   1 - break down and validate rpc request in mbuf list
43 *   2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
44 *       function in nfsd_port.c
45 *   3 - build the rpc reply in an mbuf list
46 * For nfsv4, these functions are called for each Op within the Compound RPC.
47 */
48
49#ifndef APPLEKEXT
50#include <fs/nfs/nfsport.h>
51
52/* Global vars */
53extern u_int32_t newnfs_false, newnfs_true;
54extern enum vtype nv34tov_type[8];
55extern struct timeval nfsboottime;
56extern int nfs_rootfhset;
57extern int nfsrv_enable_crossmntpt;
58extern int nfsrv_statehashsize;
59#endif	/* !APPLEKEXT */
60
61static int	nfs_async = 0;
62SYSCTL_DECL(_vfs_nfsd);
63SYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
64    "Tell client that writes were synced even though they were not");
65
66/*
67 * This list defines the GSS mechanisms supported.
68 * (Don't ask me how you get these strings from the RFC stuff like
69 *  iso(1), org(3)... but someone did it, so I don't need to know.)
70 */
71static struct nfsgss_mechlist nfsgss_mechlist[] = {
72	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
73	{ 0, "", 0 },
74};
75
76/* local functions */
77static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
78    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
79    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
80    int *diraft_retp, nfsattrbit_t *attrbitp,
81    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
82    int pathlen);
83static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
84    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
85    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
86    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
87    NFSPROC_T *p, struct nfsexstuff *exp);
88
89/*
90 * nfs access service (not a part of NFS V2)
91 */
92APPLESTATIC int
93nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
94    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
95{
96	u_int32_t *tl;
97	int getret, error = 0;
98	struct nfsvattr nva;
99	u_int32_t testmode, nfsmode, supported = 0;
100	accmode_t deletebit;
101
102	if (nd->nd_repstat) {
103		nfsrv_postopattr(nd, 1, &nva);
104		goto out;
105	}
106	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
107	nfsmode = fxdr_unsigned(u_int32_t, *tl);
108	if ((nd->nd_flag & ND_NFSV4) &&
109	    (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
110	     NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
111	     NFSACCESS_EXECUTE))) {
112		nd->nd_repstat = NFSERR_INVAL;
113		vput(vp);
114		goto out;
115	}
116	if (nfsmode & NFSACCESS_READ) {
117		supported |= NFSACCESS_READ;
118		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
119		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
120			nfsmode &= ~NFSACCESS_READ;
121	}
122	if (nfsmode & NFSACCESS_MODIFY) {
123		supported |= NFSACCESS_MODIFY;
124		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
125		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
126			nfsmode &= ~NFSACCESS_MODIFY;
127	}
128	if (nfsmode & NFSACCESS_EXTEND) {
129		supported |= NFSACCESS_EXTEND;
130		if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
131		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
132			nfsmode &= ~NFSACCESS_EXTEND;
133	}
134	if (nfsmode & NFSACCESS_DELETE) {
135		supported |= NFSACCESS_DELETE;
136		if (vp->v_type == VDIR)
137			deletebit = VDELETE_CHILD;
138		else
139			deletebit = VDELETE;
140		if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
141		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
142			nfsmode &= ~NFSACCESS_DELETE;
143	}
144	if (vnode_vtype(vp) == VDIR)
145		testmode = NFSACCESS_LOOKUP;
146	else
147		testmode = NFSACCESS_EXECUTE;
148	if (nfsmode & testmode) {
149		supported |= (nfsmode & testmode);
150		if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
151		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
152			nfsmode &= ~testmode;
153	}
154	nfsmode &= supported;
155	if (nd->nd_flag & ND_NFSV3) {
156		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
157		nfsrv_postopattr(nd, getret, &nva);
158	}
159	vput(vp);
160	if (nd->nd_flag & ND_NFSV4) {
161		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
162		*tl++ = txdr_unsigned(supported);
163	} else
164		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
165	*tl = txdr_unsigned(nfsmode);
166
167out:
168	NFSEXITCODE2(0, nd);
169	return (0);
170nfsmout:
171	vput(vp);
172	NFSEXITCODE2(error, nd);
173	return (error);
174}
175
176/*
177 * nfs getattr service
178 */
179APPLESTATIC int
180nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
181    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
182{
183	struct nfsvattr nva;
184	fhandle_t fh;
185	int at_root = 0, error = 0, supports_nfsv4acls;
186	struct nfsreferral *refp;
187	nfsattrbit_t attrbits, tmpbits;
188	struct mount *mp;
189	struct vnode *tvp = NULL;
190	struct vattr va;
191	uint64_t mounted_on_fileno = 0;
192	accmode_t accmode;
193
194	if (nd->nd_repstat)
195		goto out;
196	if (nd->nd_flag & ND_NFSV4) {
197		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
198		if (error) {
199			vput(vp);
200			goto out;
201		}
202
203		/*
204		 * Check for a referral.
205		 */
206		refp = nfsv4root_getreferral(vp, NULL, 0);
207		if (refp != NULL) {
208			(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
209			    &nd->nd_repstat);
210			vput(vp);
211			goto out;
212		}
213		if (nd->nd_repstat == 0) {
214			accmode = 0;
215			NFSSET_ATTRBIT(&tmpbits, &attrbits);
216			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
217				NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
218				accmode |= VREAD_ACL;
219			}
220			if (NFSNONZERO_ATTRBIT(&tmpbits))
221				accmode |= VREAD_ATTRIBUTES;
222			if (accmode != 0)
223				nd->nd_repstat = nfsvno_accchk(vp, accmode,
224				    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
225				    NFSACCCHK_VPISLOCKED, NULL);
226		}
227	}
228	if (!nd->nd_repstat)
229		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
230	if (!nd->nd_repstat) {
231		if (nd->nd_flag & ND_NFSV4) {
232			if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
233				nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
234			if (!nd->nd_repstat)
235				nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
236				    &nva, &attrbits, nd->nd_cred, p);
237			if (nd->nd_repstat == 0) {
238				supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
239				mp = vp->v_mount;
240				if (nfsrv_enable_crossmntpt != 0 &&
241				    vp->v_type == VDIR &&
242				    (vp->v_vflag & VV_ROOT) != 0 &&
243				    vp != rootvnode) {
244					tvp = mp->mnt_vnodecovered;
245					VREF(tvp);
246					at_root = 1;
247				} else
248					at_root = 0;
249				vfs_ref(mp);
250				NFSVOPUNLOCK(vp, 0);
251				if (at_root != 0) {
252					if ((nd->nd_repstat =
253					     NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
254						nd->nd_repstat = VOP_GETATTR(
255						    tvp, &va, nd->nd_cred);
256						vput(tvp);
257					} else
258						vrele(tvp);
259					if (nd->nd_repstat == 0)
260						mounted_on_fileno = (uint64_t)
261						    va.va_fileid;
262					else
263						at_root = 0;
264				}
265				if (nd->nd_repstat == 0)
266					nd->nd_repstat = vfs_busy(mp, 0);
267				vfs_rel(mp);
268				if (nd->nd_repstat == 0) {
269					(void)nfsvno_fillattr(nd, mp, vp, &nva,
270					    &fh, 0, &attrbits, nd->nd_cred, p,
271					    isdgram, 1, supports_nfsv4acls,
272					    at_root, mounted_on_fileno);
273					vfs_unbusy(mp);
274				}
275				vrele(vp);
276			} else
277				vput(vp);
278		} else {
279			nfsrv_fillattr(nd, &nva);
280			vput(vp);
281		}
282	} else {
283		vput(vp);
284	}
285
286out:
287	NFSEXITCODE2(error, nd);
288	return (error);
289}
290
291/*
292 * nfs setattr service
293 */
294APPLESTATIC int
295nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
296    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
297{
298	struct nfsvattr nva, nva2;
299	u_int32_t *tl;
300	int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
301	struct timespec guard = { 0, 0 };
302	nfsattrbit_t attrbits, retbits;
303	nfsv4stateid_t stateid;
304	NFSACL_T *aclp = NULL;
305
306	if (nd->nd_repstat) {
307		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
308		goto out;
309	}
310#ifdef NFS4_ACL_EXTATTR_NAME
311	aclp = acl_alloc(M_WAITOK);
312	aclp->acl_cnt = 0;
313#endif
314	NFSVNO_ATTRINIT(&nva);
315	NFSZERO_ATTRBIT(&retbits);
316	if (nd->nd_flag & ND_NFSV4) {
317		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
318		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
319		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
320	}
321	error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
322	if (error)
323		goto nfsmout;
324	preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p, 1);
325	if (!nd->nd_repstat)
326		nd->nd_repstat = preat_ret;
327	if (nd->nd_flag & ND_NFSV3) {
328		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
329		gcheck = fxdr_unsigned(int, *tl);
330		if (gcheck) {
331			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
332			fxdr_nfsv3time(tl, &guard);
333		}
334		if (!nd->nd_repstat && gcheck &&
335		    (nva2.na_ctime.tv_sec != guard.tv_sec ||
336		     nva2.na_ctime.tv_nsec != guard.tv_nsec))
337			nd->nd_repstat = NFSERR_NOT_SYNC;
338		if (nd->nd_repstat) {
339			vput(vp);
340#ifdef NFS4_ACL_EXTATTR_NAME
341			acl_free(aclp);
342#endif
343			nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
344			goto out;
345		}
346	} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
347		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
348
349	/*
350	 * Now that we have all the fields, lets do it.
351	 * If the size is being changed write access is required, otherwise
352	 * just check for a read only file system.
353	 */
354	if (!nd->nd_repstat) {
355		if (NFSVNO_NOTSETSIZE(&nva)) {
356			if (NFSVNO_EXRDONLY(exp) ||
357			    (vfs_flags(vnode_mount(vp)) & MNT_RDONLY))
358				nd->nd_repstat = EROFS;
359		} else {
360			if (vnode_vtype(vp) != VREG)
361				nd->nd_repstat = EINVAL;
362			else if (nva2.na_uid != nd->nd_cred->cr_uid ||
363			    NFSVNO_EXSTRICTACCESS(exp))
364				nd->nd_repstat = nfsvno_accchk(vp,
365				    VWRITE, nd->nd_cred, exp, p,
366				    NFSACCCHK_NOOVERRIDE,
367				    NFSACCCHK_VPISLOCKED, NULL);
368		}
369	}
370	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
371		nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
372		    &nva, &attrbits, exp, p);
373
374	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
375	    /*
376	     * For V4, try setting the attrbutes in sets, so that the
377	     * reply bitmap will be correct for an error case.
378	     */
379	    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
380		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
381		NFSVNO_ATTRINIT(&nva2);
382		NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
383		NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
384		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
385		    exp);
386		if (!nd->nd_repstat) {
387		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
388			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
389		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
390			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
391		}
392	    }
393	    if (!nd->nd_repstat &&
394		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
395		NFSVNO_ATTRINIT(&nva2);
396		NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
397		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
398		    exp);
399		if (!nd->nd_repstat)
400		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
401	    }
402	    if (!nd->nd_repstat &&
403		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
404		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
405		NFSVNO_ATTRINIT(&nva2);
406		NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
407		NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
408		if (nva.na_vaflags & VA_UTIMES_NULL) {
409			nva2.na_vaflags |= VA_UTIMES_NULL;
410			NFSVNO_SETACTIVE(&nva2, vaflags);
411		}
412		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
413		    exp);
414		if (!nd->nd_repstat) {
415		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
416			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
417		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
418			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
419		}
420	    }
421	    if (!nd->nd_repstat &&
422		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE)) {
423		NFSVNO_ATTRINIT(&nva2);
424		NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
425		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
426		    exp);
427		if (!nd->nd_repstat)
428		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
429	    }
430
431#ifdef NFS4_ACL_EXTATTR_NAME
432	    if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
433		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
434		nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
435		if (!nd->nd_repstat)
436		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
437	    }
438#endif
439	} else if (!nd->nd_repstat) {
440		nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
441		    exp);
442	}
443	if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
444		postat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
445		if (!nd->nd_repstat)
446			nd->nd_repstat = postat_ret;
447	}
448	vput(vp);
449#ifdef NFS4_ACL_EXTATTR_NAME
450	acl_free(aclp);
451#endif
452	if (nd->nd_flag & ND_NFSV3)
453		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
454	else if (nd->nd_flag & ND_NFSV4)
455		(void) nfsrv_putattrbit(nd, &retbits);
456	else if (!nd->nd_repstat)
457		nfsrv_fillattr(nd, &nva);
458
459out:
460	NFSEXITCODE2(0, nd);
461	return (0);
462nfsmout:
463	vput(vp);
464#ifdef NFS4_ACL_EXTATTR_NAME
465	acl_free(aclp);
466#endif
467	if (nd->nd_flag & ND_NFSV4) {
468		/*
469		 * For all nd_repstat, the V4 reply includes a bitmap,
470		 * even NFSERR_BADXDR, which is what this will end up
471		 * returning.
472		 */
473		(void) nfsrv_putattrbit(nd, &retbits);
474	}
475	NFSEXITCODE2(error, nd);
476	return (error);
477}
478
479/*
480 * nfs lookup rpc
481 * (Also performs lookup parent for v4)
482 */
483APPLESTATIC int
484nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
485    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
486    struct nfsexstuff *exp)
487{
488	struct nameidata named;
489	vnode_t vp, dirp = NULL;
490	int error = 0, dattr_ret = 1;
491	struct nfsvattr nva, dattr;
492	char *bufp;
493	u_long *hashp;
494
495	if (nd->nd_repstat) {
496		nfsrv_postopattr(nd, dattr_ret, &dattr);
497		goto out;
498	}
499
500	/*
501	 * For some reason, if dp is a symlink, the error
502	 * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
503	 */
504	if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
505		nd->nd_repstat = NFSERR_SYMLINK;
506		vrele(dp);
507		goto out;
508	}
509
510	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
511	    LOCKLEAF | SAVESTART);
512	nfsvno_setpathbuf(&named, &bufp, &hashp);
513	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
514	if (error) {
515		vrele(dp);
516		nfsvno_relpathbuf(&named);
517		goto out;
518	}
519	if (!nd->nd_repstat) {
520		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
521	} else {
522		vrele(dp);
523		nfsvno_relpathbuf(&named);
524	}
525	if (nd->nd_repstat) {
526		if (dirp) {
527			if (nd->nd_flag & ND_NFSV3)
528				dattr_ret = nfsvno_getattr(dirp, &dattr,
529				    nd->nd_cred, p, 0);
530			vrele(dirp);
531		}
532		if (nd->nd_flag & ND_NFSV3)
533			nfsrv_postopattr(nd, dattr_ret, &dattr);
534		goto out;
535	}
536	if (named.ni_startdir)
537		vrele(named.ni_startdir);
538	nfsvno_relpathbuf(&named);
539	vp = named.ni_vp;
540	if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
541	    vp->v_type != VDIR && vp->v_type != VLNK)
542		/*
543		 * Only allow lookup of VDIR and VLNK for traversal of
544		 * non-exported volumes during NFSv4 mounting.
545		 */
546		nd->nd_repstat = ENOENT;
547	if (nd->nd_repstat == 0)
548		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
549	if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
550		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
551	if (vpp != NULL && nd->nd_repstat == 0)
552		*vpp = vp;
553	else
554		vput(vp);
555	if (dirp) {
556		if (nd->nd_flag & ND_NFSV3)
557			dattr_ret = nfsvno_getattr(dirp, &dattr, nd->nd_cred,
558			    p, 0);
559		vrele(dirp);
560	}
561	if (nd->nd_repstat) {
562		if (nd->nd_flag & ND_NFSV3)
563			nfsrv_postopattr(nd, dattr_ret, &dattr);
564		goto out;
565	}
566	if (nd->nd_flag & ND_NFSV2) {
567		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
568		nfsrv_fillattr(nd, &nva);
569	} else if (nd->nd_flag & ND_NFSV3) {
570		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
571		nfsrv_postopattr(nd, 0, &nva);
572		nfsrv_postopattr(nd, dattr_ret, &dattr);
573	}
574
575out:
576	NFSEXITCODE2(error, nd);
577	return (error);
578}
579
580/*
581 * nfs readlink service
582 */
583APPLESTATIC int
584nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
585    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
586{
587	u_int32_t *tl;
588	mbuf_t mp = NULL, mpend = NULL;
589	int getret = 1, len;
590	struct nfsvattr nva;
591
592	if (nd->nd_repstat) {
593		nfsrv_postopattr(nd, getret, &nva);
594		goto out;
595	}
596	if (vnode_vtype(vp) != VLNK) {
597		if (nd->nd_flag & ND_NFSV2)
598			nd->nd_repstat = ENXIO;
599		else
600			nd->nd_repstat = EINVAL;
601	}
602	if (!nd->nd_repstat)
603		nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p,
604		    &mp, &mpend, &len);
605	if (nd->nd_flag & ND_NFSV3)
606		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
607	vput(vp);
608	if (nd->nd_flag & ND_NFSV3)
609		nfsrv_postopattr(nd, getret, &nva);
610	if (nd->nd_repstat)
611		goto out;
612	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
613	*tl = txdr_unsigned(len);
614	mbuf_setnext(nd->nd_mb, mp);
615	nd->nd_mb = mpend;
616	nd->nd_bpos = NFSMTOD(mpend, caddr_t) + mbuf_len(mpend);
617
618out:
619	NFSEXITCODE2(0, nd);
620	return (0);
621}
622
623/*
624 * nfs read service
625 */
626APPLESTATIC int
627nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
628    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
629{
630	u_int32_t *tl;
631	int error = 0, cnt, getret = 1, reqlen, eof = 0;
632	mbuf_t m2, m3;
633	struct nfsvattr nva;
634	off_t off = 0x0;
635	struct nfsstate st, *stp = &st;
636	struct nfslock lo, *lop = &lo;
637	nfsv4stateid_t stateid;
638	nfsquad_t clientid;
639
640	if (nd->nd_repstat) {
641		nfsrv_postopattr(nd, getret, &nva);
642		goto out;
643	}
644	if (nd->nd_flag & ND_NFSV2) {
645		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
646		off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
647		reqlen = fxdr_unsigned(int, *tl);
648	} else if (nd->nd_flag & ND_NFSV3) {
649		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
650		off = fxdr_hyper(tl);
651		tl += 2;
652		reqlen = fxdr_unsigned(int, *tl);
653	} else {
654		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
655		reqlen = fxdr_unsigned(int, *(tl + 6));
656	}
657	if (reqlen > NFS_SRVMAXDATA(nd)) {
658		reqlen = NFS_SRVMAXDATA(nd);
659	} else if (reqlen < 0) {
660		error = EBADRPC;
661		goto nfsmout;
662	}
663	if (nd->nd_flag & ND_NFSV4) {
664		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
665		lop->lo_flags = NFSLCK_READ;
666		stp->ls_ownerlen = 0;
667		stp->ls_op = NULL;
668		stp->ls_uid = nd->nd_cred->cr_uid;
669		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
670		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
671		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
672		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
673			if ((nd->nd_flag & ND_NFSV41) != 0)
674				clientid.qval = nd->nd_clientid.qval;
675			else if (nd->nd_clientid.qval != clientid.qval)
676				printf("EEK1 multiple clids\n");
677		} else {
678			if ((nd->nd_flag & ND_NFSV41) != 0)
679				printf("EEK! no clientid from session\n");
680			nd->nd_flag |= ND_IMPLIEDCLID;
681			nd->nd_clientid.qval = clientid.qval;
682		}
683		stp->ls_stateid.other[2] = *tl++;
684		off = fxdr_hyper(tl);
685		lop->lo_first = off;
686		tl += 2;
687		lop->lo_end = off + reqlen;
688		/*
689		 * Paranoia, just in case it wraps around.
690		 */
691		if (lop->lo_end < off)
692			lop->lo_end = NFS64BITSSET;
693	}
694	if (vnode_vtype(vp) != VREG) {
695		if (nd->nd_flag & ND_NFSV3)
696			nd->nd_repstat = EINVAL;
697		else
698			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
699			    EINVAL;
700	}
701	getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
702	if (!nd->nd_repstat)
703		nd->nd_repstat = getret;
704	if (!nd->nd_repstat &&
705	    (nva.na_uid != nd->nd_cred->cr_uid ||
706	     NFSVNO_EXSTRICTACCESS(exp))) {
707		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
708		    nd->nd_cred, exp, p,
709		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
710		if (nd->nd_repstat)
711			nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
712			    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
713			    NFSACCCHK_VPISLOCKED, NULL);
714	}
715	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
716		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
717		    &stateid, exp, nd, p);
718	if (nd->nd_repstat) {
719		vput(vp);
720		if (nd->nd_flag & ND_NFSV3)
721			nfsrv_postopattr(nd, getret, &nva);
722		goto out;
723	}
724	if (off >= nva.na_size) {
725		cnt = 0;
726		eof = 1;
727	} else if (reqlen == 0)
728		cnt = 0;
729	else if ((off + reqlen) >= nva.na_size) {
730		cnt = nva.na_size - off;
731		eof = 1;
732	} else
733		cnt = reqlen;
734	m3 = NULL;
735	if (cnt > 0) {
736		nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p,
737		    &m3, &m2);
738		if (!(nd->nd_flag & ND_NFSV4)) {
739			getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
740			if (!nd->nd_repstat)
741				nd->nd_repstat = getret;
742		}
743		if (nd->nd_repstat) {
744			vput(vp);
745			if (m3)
746				mbuf_freem(m3);
747			if (nd->nd_flag & ND_NFSV3)
748				nfsrv_postopattr(nd, getret, &nva);
749			goto out;
750		}
751	}
752	vput(vp);
753	if (nd->nd_flag & ND_NFSV2) {
754		nfsrv_fillattr(nd, &nva);
755		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
756	} else {
757		if (nd->nd_flag & ND_NFSV3) {
758			nfsrv_postopattr(nd, getret, &nva);
759			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
760			*tl++ = txdr_unsigned(cnt);
761		} else
762			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
763		if (eof)
764			*tl++ = newnfs_true;
765		else
766			*tl++ = newnfs_false;
767	}
768	*tl = txdr_unsigned(cnt);
769	if (m3) {
770		mbuf_setnext(nd->nd_mb, m3);
771		nd->nd_mb = m2;
772		nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
773	}
774
775out:
776	NFSEXITCODE2(0, nd);
777	return (0);
778nfsmout:
779	vput(vp);
780	NFSEXITCODE2(error, nd);
781	return (error);
782}
783
784/*
785 * nfs write service
786 */
787APPLESTATIC int
788nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
789    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
790{
791	int i, cnt;
792	u_int32_t *tl;
793	mbuf_t mp;
794	struct nfsvattr nva, forat;
795	int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
796	int stable = NFSWRITE_FILESYNC;
797	off_t off;
798	struct nfsstate st, *stp = &st;
799	struct nfslock lo, *lop = &lo;
800	nfsv4stateid_t stateid;
801	nfsquad_t clientid;
802
803	if (nd->nd_repstat) {
804		nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
805		goto out;
806	}
807	if (nd->nd_flag & ND_NFSV2) {
808		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
809		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
810		tl += 2;
811		retlen = len = fxdr_unsigned(int32_t, *tl);
812	} else if (nd->nd_flag & ND_NFSV3) {
813		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
814		off = fxdr_hyper(tl);
815		tl += 3;
816		stable = fxdr_unsigned(int, *tl++);
817		retlen = len = fxdr_unsigned(int32_t, *tl);
818	} else {
819		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
820		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
821		lop->lo_flags = NFSLCK_WRITE;
822		stp->ls_ownerlen = 0;
823		stp->ls_op = NULL;
824		stp->ls_uid = nd->nd_cred->cr_uid;
825		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
826		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
827		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
828		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
829			if ((nd->nd_flag & ND_NFSV41) != 0)
830				clientid.qval = nd->nd_clientid.qval;
831			else if (nd->nd_clientid.qval != clientid.qval)
832				printf("EEK2 multiple clids\n");
833		} else {
834			if ((nd->nd_flag & ND_NFSV41) != 0)
835				printf("EEK! no clientid from session\n");
836			nd->nd_flag |= ND_IMPLIEDCLID;
837			nd->nd_clientid.qval = clientid.qval;
838		}
839		stp->ls_stateid.other[2] = *tl++;
840		off = fxdr_hyper(tl);
841		lop->lo_first = off;
842		tl += 2;
843		stable = fxdr_unsigned(int, *tl++);
844		retlen = len = fxdr_unsigned(int32_t, *tl);
845		lop->lo_end = off + len;
846		/*
847		 * Paranoia, just in case it wraps around, which shouldn't
848		 * ever happen anyhow.
849		 */
850		if (lop->lo_end < lop->lo_first)
851			lop->lo_end = NFS64BITSSET;
852	}
853
854	/*
855	 * Loop through the mbuf chain, counting how many mbufs are a
856	 * part of this write operation, so the iovec size is known.
857	 */
858	cnt = 0;
859	mp = nd->nd_md;
860	i = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - nd->nd_dpos;
861	while (len > 0) {
862		if (i > 0) {
863			len -= i;
864			cnt++;
865		}
866		mp = mbuf_next(mp);
867		if (!mp) {
868			if (len > 0) {
869				error = EBADRPC;
870				goto nfsmout;
871			}
872		} else
873			i = mbuf_len(mp);
874	}
875
876	if (retlen > NFS_SRVMAXIO || retlen < 0)
877		nd->nd_repstat = EIO;
878	if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
879		if (nd->nd_flag & ND_NFSV3)
880			nd->nd_repstat = EINVAL;
881		else
882			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
883			    EINVAL;
884	}
885	forat_ret = nfsvno_getattr(vp, &forat, nd->nd_cred, p, 1);
886	if (!nd->nd_repstat)
887		nd->nd_repstat = forat_ret;
888	if (!nd->nd_repstat &&
889	    (forat.na_uid != nd->nd_cred->cr_uid ||
890	     NFSVNO_EXSTRICTACCESS(exp)))
891		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
892		    nd->nd_cred, exp, p,
893		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
894	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
895		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
896		    &stateid, exp, nd, p);
897	}
898	if (nd->nd_repstat) {
899		vput(vp);
900		if (nd->nd_flag & ND_NFSV3)
901			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
902		goto out;
903	}
904
905	/*
906	 * For NFS Version 2, it is not obvious what a write of zero length
907	 * should do, but I might as well be consistent with Version 3,
908	 * which is to return ok so long as there are no permission problems.
909	 */
910	if (retlen > 0) {
911		nd->nd_repstat = nfsvno_write(vp, off, retlen, cnt, stable,
912		    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
913		error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
914		if (error)
915			goto nfsmout;
916	}
917	if (nd->nd_flag & ND_NFSV4)
918		aftat_ret = 0;
919	else
920		aftat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
921	vput(vp);
922	if (!nd->nd_repstat)
923		nd->nd_repstat = aftat_ret;
924	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
925		if (nd->nd_flag & ND_NFSV3)
926			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
927		if (nd->nd_repstat)
928			goto out;
929		NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
930		*tl++ = txdr_unsigned(retlen);
931		/*
932		 * If nfs_async is set, then pretend the write was FILESYNC.
933		 * Warning: Doing this violates RFC1813 and runs a risk
934		 * of data written by a client being lost when the server
935		 * crashes/reboots.
936		 */
937		if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
938			*tl++ = txdr_unsigned(stable);
939		else
940			*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
941		/*
942		 * Actually, there is no need to txdr these fields,
943		 * but it may make the values more human readable,
944		 * for debugging purposes.
945		 */
946		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
947		*tl = txdr_unsigned(nfsboottime.tv_usec);
948	} else if (!nd->nd_repstat)
949		nfsrv_fillattr(nd, &nva);
950
951out:
952	NFSEXITCODE2(0, nd);
953	return (0);
954nfsmout:
955	vput(vp);
956	NFSEXITCODE2(error, nd);
957	return (error);
958}
959
960/*
961 * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
962 * now does a truncate to 0 length via. setattr if it already exists
963 * The core creation routine has been extracted out into nfsrv_creatsub(),
964 * so it can also be used by nfsrv_open() for V4.
965 */
966APPLESTATIC int
967nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
968    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
969{
970	struct nfsvattr nva, dirfor, diraft;
971	struct nfsv2_sattr *sp;
972	struct nameidata named;
973	u_int32_t *tl;
974	int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
975	int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
976	NFSDEV_T rdev = 0;
977	vnode_t vp = NULL, dirp = NULL;
978	fhandle_t fh;
979	char *bufp;
980	u_long *hashp;
981	enum vtype vtyp;
982	int32_t cverf[2], tverf[2] = { 0, 0 };
983
984	if (nd->nd_repstat) {
985		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
986		goto out;
987	}
988	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
989	    LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
990	nfsvno_setpathbuf(&named, &bufp, &hashp);
991	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
992	if (error)
993		goto nfsmout;
994	if (!nd->nd_repstat) {
995		NFSVNO_ATTRINIT(&nva);
996		if (nd->nd_flag & ND_NFSV2) {
997			NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
998			vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
999			if (vtyp == VNON)
1000				vtyp = VREG;
1001			NFSVNO_SETATTRVAL(&nva, type, vtyp);
1002			NFSVNO_SETATTRVAL(&nva, mode,
1003			    nfstov_mode(sp->sa_mode));
1004			switch (nva.na_type) {
1005			case VREG:
1006				tsize = fxdr_unsigned(int32_t, sp->sa_size);
1007				if (tsize != -1)
1008					NFSVNO_SETATTRVAL(&nva, size,
1009					    (u_quad_t)tsize);
1010				break;
1011			case VCHR:
1012			case VBLK:
1013			case VFIFO:
1014				rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
1015				break;
1016			default:
1017				break;
1018			};
1019		} else {
1020			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1021			how = fxdr_unsigned(int, *tl);
1022			switch (how) {
1023			case NFSCREATE_GUARDED:
1024			case NFSCREATE_UNCHECKED:
1025				error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
1026				if (error)
1027					goto nfsmout;
1028				break;
1029			case NFSCREATE_EXCLUSIVE:
1030				NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
1031				cverf[0] = *tl++;
1032				cverf[1] = *tl;
1033				exclusive_flag = 1;
1034				break;
1035			};
1036			NFSVNO_SETATTRVAL(&nva, type, VREG);
1037		}
1038	}
1039	if (nd->nd_repstat) {
1040		nfsvno_relpathbuf(&named);
1041		if (nd->nd_flag & ND_NFSV3) {
1042			dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred,
1043			    p, 1);
1044			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1045			    &diraft);
1046		}
1047		vput(dp);
1048		goto out;
1049	}
1050
1051	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1052	if (dirp) {
1053		if (nd->nd_flag & ND_NFSV2) {
1054			vrele(dirp);
1055			dirp = NULL;
1056		} else {
1057			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1058			    p, 0);
1059		}
1060	}
1061	if (nd->nd_repstat) {
1062		if (nd->nd_flag & ND_NFSV3)
1063			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1064			    &diraft);
1065		if (dirp)
1066			vrele(dirp);
1067		goto out;
1068	}
1069
1070	if (!(nd->nd_flag & ND_NFSV2)) {
1071		switch (how) {
1072		case NFSCREATE_GUARDED:
1073			if (named.ni_vp)
1074				nd->nd_repstat = EEXIST;
1075			break;
1076		case NFSCREATE_UNCHECKED:
1077			break;
1078		case NFSCREATE_EXCLUSIVE:
1079			if (named.ni_vp == NULL)
1080				NFSVNO_SETATTRVAL(&nva, mode, 0);
1081			break;
1082		};
1083	}
1084
1085	/*
1086	 * Iff doesn't exist, create it
1087	 * otherwise just truncate to 0 length
1088	 *   should I set the mode too ?
1089	 */
1090	nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
1091	    &exclusive_flag, cverf, rdev, p, exp);
1092
1093	if (!nd->nd_repstat) {
1094		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
1095		if (!nd->nd_repstat)
1096			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1097			    p, 1);
1098		vput(vp);
1099		if (!nd->nd_repstat) {
1100			tverf[0] = nva.na_atime.tv_sec;
1101			tverf[1] = nva.na_atime.tv_nsec;
1102		}
1103	}
1104	if (nd->nd_flag & ND_NFSV2) {
1105		if (!nd->nd_repstat) {
1106			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
1107			nfsrv_fillattr(nd, &nva);
1108		}
1109	} else {
1110		if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
1111		    || cverf[1] != tverf[1]))
1112			nd->nd_repstat = EEXIST;
1113		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1114		vrele(dirp);
1115		if (!nd->nd_repstat) {
1116			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
1117			nfsrv_postopattr(nd, 0, &nva);
1118		}
1119		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1120	}
1121
1122out:
1123	NFSEXITCODE2(0, nd);
1124	return (0);
1125nfsmout:
1126	vput(dp);
1127	nfsvno_relpathbuf(&named);
1128	NFSEXITCODE2(error, nd);
1129	return (error);
1130}
1131
1132/*
1133 * nfs v3 mknod service (and v4 create)
1134 */
1135APPLESTATIC int
1136nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
1137    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1138    struct nfsexstuff *exp)
1139{
1140	struct nfsvattr nva, dirfor, diraft;
1141	u_int32_t *tl;
1142	struct nameidata named;
1143	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1144	u_int32_t major, minor;
1145	enum vtype vtyp = VNON;
1146	nfstype nfs4type = NFNON;
1147	vnode_t vp, dirp = NULL;
1148	nfsattrbit_t attrbits;
1149	char *bufp = NULL, *pathcp = NULL;
1150	u_long *hashp, cnflags;
1151	NFSACL_T *aclp = NULL;
1152
1153	NFSVNO_ATTRINIT(&nva);
1154	cnflags = (LOCKPARENT | SAVESTART);
1155	if (nd->nd_repstat) {
1156		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1157		goto out;
1158	}
1159#ifdef NFS4_ACL_EXTATTR_NAME
1160	aclp = acl_alloc(M_WAITOK);
1161	aclp->acl_cnt = 0;
1162#endif
1163
1164	/*
1165	 * For V4, the creation stuff is here, Yuck!
1166	 */
1167	if (nd->nd_flag & ND_NFSV4) {
1168		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1169		vtyp = nfsv34tov_type(*tl);
1170		nfs4type = fxdr_unsigned(nfstype, *tl);
1171		switch (nfs4type) {
1172		case NFLNK:
1173			error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
1174			    &pathlen);
1175			if (error)
1176				goto nfsmout;
1177			break;
1178		case NFCHR:
1179		case NFBLK:
1180			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1181			major = fxdr_unsigned(u_int32_t, *tl++);
1182			minor = fxdr_unsigned(u_int32_t, *tl);
1183			nva.na_rdev = NFSMAKEDEV(major, minor);
1184			break;
1185		case NFSOCK:
1186		case NFFIFO:
1187			break;
1188		case NFDIR:
1189			cnflags = (LOCKPARENT | SAVENAME);
1190			break;
1191		default:
1192			nd->nd_repstat = NFSERR_BADTYPE;
1193			vrele(dp);
1194#ifdef NFS4_ACL_EXTATTR_NAME
1195			acl_free(aclp);
1196#endif
1197			goto out;
1198		}
1199	}
1200	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
1201	nfsvno_setpathbuf(&named, &bufp, &hashp);
1202	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1203	if (error)
1204		goto nfsmout;
1205	if (!nd->nd_repstat) {
1206		if (nd->nd_flag & ND_NFSV3) {
1207			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1208			vtyp = nfsv34tov_type(*tl);
1209		}
1210		error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
1211		if (error)
1212			goto nfsmout;
1213		nva.na_type = vtyp;
1214		if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
1215		    (vtyp == VCHR || vtyp == VBLK)) {
1216			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1217			major = fxdr_unsigned(u_int32_t, *tl++);
1218			minor = fxdr_unsigned(u_int32_t, *tl);
1219			nva.na_rdev = NFSMAKEDEV(major, minor);
1220		}
1221	}
1222
1223	dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
1224	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
1225		if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
1226		    dirfor.na_gid == nva.na_gid)
1227			NFSVNO_UNSET(&nva, gid);
1228		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
1229	}
1230	if (nd->nd_repstat) {
1231		vrele(dp);
1232#ifdef NFS4_ACL_EXTATTR_NAME
1233		acl_free(aclp);
1234#endif
1235		nfsvno_relpathbuf(&named);
1236		if (pathcp)
1237			FREE(pathcp, M_TEMP);
1238		if (nd->nd_flag & ND_NFSV3)
1239			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1240			    &diraft);
1241		goto out;
1242	}
1243
1244	/*
1245	 * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
1246	 * in va_mode, so we'll have to set a default here.
1247	 */
1248	if (NFSVNO_NOTSETMODE(&nva)) {
1249		if (vtyp == VLNK)
1250			nva.na_mode = 0755;
1251		else
1252			nva.na_mode = 0400;
1253	}
1254
1255	if (vtyp == VDIR)
1256		named.ni_cnd.cn_flags |= WILLBEDIR;
1257	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1258	if (nd->nd_repstat) {
1259		if (dirp) {
1260			if (nd->nd_flag & ND_NFSV3)
1261				dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1262				    nd->nd_cred, p, 0);
1263			vrele(dirp);
1264		}
1265#ifdef NFS4_ACL_EXTATTR_NAME
1266		acl_free(aclp);
1267#endif
1268		if (nd->nd_flag & ND_NFSV3)
1269			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1270			    &diraft);
1271		goto out;
1272	}
1273	if (dirp)
1274		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1275
1276	if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
1277		if (vtyp == VDIR) {
1278			nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
1279			    &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
1280			    exp);
1281#ifdef NFS4_ACL_EXTATTR_NAME
1282			acl_free(aclp);
1283#endif
1284			goto out;
1285		} else if (vtyp == VLNK) {
1286			nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1287			    &dirfor, &diraft, &diraft_ret, &attrbits,
1288			    aclp, p, exp, pathcp, pathlen);
1289#ifdef NFS4_ACL_EXTATTR_NAME
1290			acl_free(aclp);
1291#endif
1292			FREE(pathcp, M_TEMP);
1293			goto out;
1294		}
1295	}
1296
1297	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
1298	if (!nd->nd_repstat) {
1299		vp = named.ni_vp;
1300		nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
1301		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1302		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
1303			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1304			    p, 1);
1305		if (vpp != NULL && nd->nd_repstat == 0) {
1306			NFSVOPUNLOCK(vp, 0);
1307			*vpp = vp;
1308		} else
1309			vput(vp);
1310	}
1311
1312	diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1313	vrele(dirp);
1314	if (!nd->nd_repstat) {
1315		if (nd->nd_flag & ND_NFSV3) {
1316			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1317			nfsrv_postopattr(nd, 0, &nva);
1318		} else {
1319			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1320			*tl++ = newnfs_false;
1321			txdr_hyper(dirfor.na_filerev, tl);
1322			tl += 2;
1323			txdr_hyper(diraft.na_filerev, tl);
1324			(void) nfsrv_putattrbit(nd, &attrbits);
1325		}
1326	}
1327	if (nd->nd_flag & ND_NFSV3)
1328		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1329#ifdef NFS4_ACL_EXTATTR_NAME
1330	acl_free(aclp);
1331#endif
1332
1333out:
1334	NFSEXITCODE2(0, nd);
1335	return (0);
1336nfsmout:
1337	vrele(dp);
1338#ifdef NFS4_ACL_EXTATTR_NAME
1339	acl_free(aclp);
1340#endif
1341	if (bufp)
1342		nfsvno_relpathbuf(&named);
1343	if (pathcp)
1344		FREE(pathcp, M_TEMP);
1345
1346	NFSEXITCODE2(error, nd);
1347	return (error);
1348}
1349
1350/*
1351 * nfs remove service
1352 */
1353APPLESTATIC int
1354nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
1355    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
1356{
1357	struct nameidata named;
1358	u_int32_t *tl;
1359	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1360	vnode_t dirp = NULL;
1361	struct nfsvattr dirfor, diraft;
1362	char *bufp;
1363	u_long *hashp;
1364
1365	if (nd->nd_repstat) {
1366		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1367		goto out;
1368	}
1369	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
1370	    LOCKPARENT | LOCKLEAF);
1371	nfsvno_setpathbuf(&named, &bufp, &hashp);
1372	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1373	if (error) {
1374		vput(dp);
1375		nfsvno_relpathbuf(&named);
1376		goto out;
1377	}
1378	if (!nd->nd_repstat) {
1379		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1380	} else {
1381		vput(dp);
1382		nfsvno_relpathbuf(&named);
1383	}
1384	if (dirp) {
1385		if (!(nd->nd_flag & ND_NFSV2)) {
1386			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1387			    nd->nd_cred, p, 0);
1388		} else {
1389			vrele(dirp);
1390			dirp = NULL;
1391		}
1392	}
1393	if (!nd->nd_repstat) {
1394		if (nd->nd_flag & ND_NFSV4) {
1395			if (vnode_vtype(named.ni_vp) == VDIR)
1396				nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
1397				    nd->nd_cred, p, exp);
1398			else
1399				nd->nd_repstat = nfsvno_removesub(&named, 1,
1400				    nd->nd_cred, p, exp);
1401		} else if (nd->nd_procnum == NFSPROC_RMDIR) {
1402			nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
1403			    nd->nd_cred, p, exp);
1404		} else {
1405			nd->nd_repstat = nfsvno_removesub(&named, 0,
1406			    nd->nd_cred, p, exp);
1407		}
1408	}
1409	if (!(nd->nd_flag & ND_NFSV2)) {
1410		if (dirp) {
1411			diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred,
1412			    p, 0);
1413			vrele(dirp);
1414		}
1415		if (nd->nd_flag & ND_NFSV3) {
1416			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1417			    &diraft);
1418		} else if (!nd->nd_repstat) {
1419			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1420			*tl++ = newnfs_false;
1421			txdr_hyper(dirfor.na_filerev, tl);
1422			tl += 2;
1423			txdr_hyper(diraft.na_filerev, tl);
1424		}
1425	}
1426
1427out:
1428	NFSEXITCODE2(error, nd);
1429	return (error);
1430}
1431
1432/*
1433 * nfs rename service
1434 */
1435APPLESTATIC int
1436nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
1437    vnode_t dp, vnode_t todp, NFSPROC_T *p, struct nfsexstuff *exp,
1438    struct nfsexstuff *toexp)
1439{
1440	u_int32_t *tl;
1441	int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
1442	int tdirfor_ret = 1, tdiraft_ret = 1;
1443	struct nameidata fromnd, tond;
1444	vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
1445	struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
1446	struct nfsexstuff tnes;
1447	struct nfsrvfh tfh;
1448	char *bufp, *tbufp = NULL;
1449	u_long *hashp;
1450	fhandle_t fh;
1451
1452	if (nd->nd_repstat) {
1453		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1454		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1455		goto out;
1456	}
1457	if (!(nd->nd_flag & ND_NFSV2))
1458		fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd->nd_cred, p, 1);
1459	tond.ni_cnd.cn_nameiop = 0;
1460	tond.ni_startdir = NULL;
1461	NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
1462	nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
1463	error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
1464	if (error) {
1465		vput(dp);
1466		if (todp)
1467			vrele(todp);
1468		nfsvno_relpathbuf(&fromnd);
1469		goto out;
1470	}
1471	/*
1472	 * Unlock dp in this code section, so it is unlocked before
1473	 * tdp gets locked. This avoids a potential LOR if tdp is the
1474	 * parent directory of dp.
1475	 */
1476	if (nd->nd_flag & ND_NFSV4) {
1477		tdp = todp;
1478		tnes = *toexp;
1479		if (dp != tdp) {
1480			NFSVOPUNLOCK(dp, 0);
1481			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1482			    p, 0);	/* Might lock tdp. */
1483		} else {
1484			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1485			    p, 1);
1486			NFSVOPUNLOCK(dp, 0);
1487		}
1488	} else {
1489		tfh.nfsrvfh_len = 0;
1490		error = nfsrv_mtofh(nd, &tfh);
1491		if (error == 0)
1492			error = nfsvno_getfh(dp, &fh, p);
1493		if (error) {
1494			vput(dp);
1495			/* todp is always NULL except NFSv4 */
1496			nfsvno_relpathbuf(&fromnd);
1497			goto out;
1498		}
1499
1500		/* If this is the same file handle, just VREF() the vnode. */
1501		if (tfh.nfsrvfh_len == NFSX_MYFH &&
1502		    !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
1503			VREF(dp);
1504			tdp = dp;
1505			tnes = *exp;
1506			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1507			    p, 1);
1508			NFSVOPUNLOCK(dp, 0);
1509		} else {
1510			NFSVOPUNLOCK(dp, 0);
1511			nd->nd_cred->cr_uid = nd->nd_saveduid;
1512			nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
1513			    0, p);	/* Locks tdp. */
1514			if (tdp) {
1515				tdirfor_ret = nfsvno_getattr(tdp, &tdirfor,
1516				    nd->nd_cred, p, 1);
1517				NFSVOPUNLOCK(tdp, 0);
1518			}
1519		}
1520	}
1521	NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
1522	nfsvno_setpathbuf(&tond, &tbufp, &hashp);
1523	if (!nd->nd_repstat) {
1524		error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
1525		if (error) {
1526			if (tdp)
1527				vrele(tdp);
1528			vrele(dp);
1529			nfsvno_relpathbuf(&fromnd);
1530			nfsvno_relpathbuf(&tond);
1531			goto out;
1532		}
1533	}
1534	if (nd->nd_repstat) {
1535		if (nd->nd_flag & ND_NFSV3) {
1536			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1537			    &fdiraft);
1538			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1539			    &tdiraft);
1540		}
1541		if (tdp)
1542			vrele(tdp);
1543		vrele(dp);
1544		nfsvno_relpathbuf(&fromnd);
1545		nfsvno_relpathbuf(&tond);
1546		goto out;
1547	}
1548
1549	/*
1550	 * Done parsing, now down to business.
1551	 */
1552	nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, p, &fdirp);
1553	if (nd->nd_repstat) {
1554		if (nd->nd_flag & ND_NFSV3) {
1555			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1556			    &fdiraft);
1557			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1558			    &tdiraft);
1559		}
1560		if (fdirp)
1561			vrele(fdirp);
1562		if (tdp)
1563			vrele(tdp);
1564		nfsvno_relpathbuf(&tond);
1565		goto out;
1566	}
1567	if (vnode_vtype(fromnd.ni_vp) == VDIR)
1568		tond.ni_cnd.cn_flags |= WILLBEDIR;
1569	nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
1570	nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
1571	    nd->nd_flag, nd->nd_cred, p);
1572	if (fdirp)
1573		fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd->nd_cred, p,
1574		    0);
1575	if (tdirp)
1576		tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd->nd_cred, p,
1577		    0);
1578	if (fdirp)
1579		vrele(fdirp);
1580	if (tdirp)
1581		vrele(tdirp);
1582	if (nd->nd_flag & ND_NFSV3) {
1583		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1584		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1585	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1586		NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
1587		*tl++ = newnfs_false;
1588		txdr_hyper(fdirfor.na_filerev, tl);
1589		tl += 2;
1590		txdr_hyper(fdiraft.na_filerev, tl);
1591		tl += 2;
1592		*tl++ = newnfs_false;
1593		txdr_hyper(tdirfor.na_filerev, tl);
1594		tl += 2;
1595		txdr_hyper(tdiraft.na_filerev, tl);
1596	}
1597
1598out:
1599	NFSEXITCODE2(error, nd);
1600	return (error);
1601}
1602
1603/*
1604 * nfs link service
1605 */
1606APPLESTATIC int
1607nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
1608    vnode_t vp, vnode_t tovp, NFSPROC_T *p, struct nfsexstuff *exp,
1609    struct nfsexstuff *toexp)
1610{
1611	struct nameidata named;
1612	u_int32_t *tl;
1613	int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
1614	vnode_t dirp = NULL, dp = NULL;
1615	struct nfsvattr dirfor, diraft, at;
1616	struct nfsexstuff tnes;
1617	struct nfsrvfh dfh;
1618	char *bufp;
1619	u_long *hashp;
1620
1621	if (nd->nd_repstat) {
1622		nfsrv_postopattr(nd, getret, &at);
1623		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1624		goto out;
1625	}
1626	NFSVOPUNLOCK(vp, 0);
1627	if (vnode_vtype(vp) == VDIR) {
1628		if (nd->nd_flag & ND_NFSV4)
1629			nd->nd_repstat = NFSERR_ISDIR;
1630		else
1631			nd->nd_repstat = NFSERR_INVAL;
1632		if (tovp)
1633			vrele(tovp);
1634	}
1635	if (!nd->nd_repstat) {
1636		if (nd->nd_flag & ND_NFSV4) {
1637			dp = tovp;
1638			tnes = *toexp;
1639		} else {
1640			error = nfsrv_mtofh(nd, &dfh);
1641			if (error) {
1642				vrele(vp);
1643				/* tovp is always NULL unless NFSv4 */
1644				goto out;
1645			}
1646			nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0,
1647			    p);
1648			if (dp)
1649				NFSVOPUNLOCK(dp, 0);
1650		}
1651	}
1652	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1653	    LOCKPARENT | SAVENAME | NOCACHE);
1654	if (!nd->nd_repstat) {
1655		nfsvno_setpathbuf(&named, &bufp, &hashp);
1656		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1657		if (error) {
1658			vrele(vp);
1659			if (dp)
1660				vrele(dp);
1661			nfsvno_relpathbuf(&named);
1662			goto out;
1663		}
1664		if (!nd->nd_repstat) {
1665			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
1666			    p, &dirp);
1667		} else {
1668			if (dp)
1669				vrele(dp);
1670			nfsvno_relpathbuf(&named);
1671		}
1672	}
1673	if (dirp) {
1674		if (nd->nd_flag & ND_NFSV2) {
1675			vrele(dirp);
1676			dirp = NULL;
1677		} else {
1678			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1679			    nd->nd_cred, p, 0);
1680		}
1681	}
1682	if (!nd->nd_repstat)
1683		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
1684	if (nd->nd_flag & ND_NFSV3)
1685		getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 0);
1686	if (dirp) {
1687		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1688		vrele(dirp);
1689	}
1690	vrele(vp);
1691	if (nd->nd_flag & ND_NFSV3) {
1692		nfsrv_postopattr(nd, getret, &at);
1693		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1694	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1695		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1696		*tl++ = newnfs_false;
1697		txdr_hyper(dirfor.na_filerev, tl);
1698		tl += 2;
1699		txdr_hyper(diraft.na_filerev, tl);
1700	}
1701
1702out:
1703	NFSEXITCODE2(error, nd);
1704	return (error);
1705}
1706
1707/*
1708 * nfs symbolic link service
1709 */
1710APPLESTATIC int
1711nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1712    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1713    struct nfsexstuff *exp)
1714{
1715	struct nfsvattr nva, dirfor, diraft;
1716	struct nameidata named;
1717	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1718	vnode_t dirp = NULL;
1719	char *bufp, *pathcp = NULL;
1720	u_long *hashp;
1721
1722	if (nd->nd_repstat) {
1723		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1724		goto out;
1725	}
1726	if (vpp)
1727		*vpp = NULL;
1728	NFSVNO_ATTRINIT(&nva);
1729	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1730	    LOCKPARENT | SAVESTART | NOCACHE);
1731	nfsvno_setpathbuf(&named, &bufp, &hashp);
1732	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1733	if (!error && !nd->nd_repstat)
1734		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
1735	if (error) {
1736		vrele(dp);
1737		nfsvno_relpathbuf(&named);
1738		goto out;
1739	}
1740	if (!nd->nd_repstat) {
1741		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1742	} else {
1743		vrele(dp);
1744		nfsvno_relpathbuf(&named);
1745	}
1746	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1747		vrele(dirp);
1748		dirp = NULL;
1749	}
1750
1751	/*
1752	 * And call nfsrvd_symlinksub() to do the common code. It will
1753	 * return EBADRPC upon a parsing error, 0 otherwise.
1754	 */
1755	if (!nd->nd_repstat) {
1756		if (dirp != NULL)
1757			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1758			    p, 0);
1759		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1760		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
1761		    pathcp, pathlen);
1762	} else if (dirp != NULL) {
1763		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1764		vrele(dirp);
1765	}
1766	if (pathcp)
1767		FREE(pathcp, M_TEMP);
1768
1769	if (nd->nd_flag & ND_NFSV3) {
1770		if (!nd->nd_repstat) {
1771			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1772			nfsrv_postopattr(nd, 0, &nva);
1773		}
1774		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1775	}
1776
1777out:
1778	NFSEXITCODE2(error, nd);
1779	return (error);
1780}
1781
1782/*
1783 * Common code for creating a symbolic link.
1784 */
1785static void
1786nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
1787    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1788    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1789    int *diraft_retp, nfsattrbit_t *attrbitp,
1790    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
1791    int pathlen)
1792{
1793	u_int32_t *tl;
1794
1795	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
1796	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
1797	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
1798		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
1799		if (nd->nd_flag & ND_NFSV3) {
1800			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
1801			if (!nd->nd_repstat)
1802				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
1803				    nvap, nd->nd_cred, p, 1);
1804		}
1805		if (vpp != NULL && nd->nd_repstat == 0) {
1806			NFSVOPUNLOCK(ndp->ni_vp, 0);
1807			*vpp = ndp->ni_vp;
1808		} else
1809			vput(ndp->ni_vp);
1810	}
1811	if (dirp) {
1812		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1813		vrele(dirp);
1814	}
1815	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1816		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1817		*tl++ = newnfs_false;
1818		txdr_hyper(dirforp->na_filerev, tl);
1819		tl += 2;
1820		txdr_hyper(diraftp->na_filerev, tl);
1821		(void) nfsrv_putattrbit(nd, attrbitp);
1822	}
1823
1824	NFSEXITCODE2(0, nd);
1825}
1826
1827/*
1828 * nfs mkdir service
1829 */
1830APPLESTATIC int
1831nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
1832    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1833    struct nfsexstuff *exp)
1834{
1835	struct nfsvattr nva, dirfor, diraft;
1836	struct nameidata named;
1837	u_int32_t *tl;
1838	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1839	vnode_t dirp = NULL;
1840	char *bufp;
1841	u_long *hashp;
1842
1843	if (nd->nd_repstat) {
1844		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1845		goto out;
1846	}
1847	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1848	    LOCKPARENT | SAVENAME | NOCACHE);
1849	nfsvno_setpathbuf(&named, &bufp, &hashp);
1850	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1851	if (error)
1852		goto nfsmout;
1853	if (!nd->nd_repstat) {
1854		NFSVNO_ATTRINIT(&nva);
1855		if (nd->nd_flag & ND_NFSV3) {
1856			error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
1857			if (error)
1858				goto nfsmout;
1859		} else {
1860			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1861			nva.na_mode = nfstov_mode(*tl++);
1862		}
1863	}
1864	if (!nd->nd_repstat) {
1865		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1866	} else {
1867		vrele(dp);
1868		nfsvno_relpathbuf(&named);
1869	}
1870	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1871		vrele(dirp);
1872		dirp = NULL;
1873	}
1874	if (nd->nd_repstat) {
1875		if (dirp != NULL) {
1876			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1877			    p, 0);
1878			vrele(dirp);
1879		}
1880		if (nd->nd_flag & ND_NFSV3)
1881			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1882			    &diraft);
1883		goto out;
1884	}
1885	if (dirp != NULL)
1886		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1887
1888	/*
1889	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
1890	 */
1891	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
1892	    &diraft_ret, NULL, NULL, p, exp);
1893
1894	if (nd->nd_flag & ND_NFSV3) {
1895		if (!nd->nd_repstat) {
1896			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1897			nfsrv_postopattr(nd, 0, &nva);
1898		}
1899		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1900	} else if (!nd->nd_repstat) {
1901		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
1902		nfsrv_fillattr(nd, &nva);
1903	}
1904
1905out:
1906	NFSEXITCODE2(0, nd);
1907	return (0);
1908nfsmout:
1909	vrele(dp);
1910	nfsvno_relpathbuf(&named);
1911	NFSEXITCODE2(error, nd);
1912	return (error);
1913}
1914
1915/*
1916 * Code common to mkdir for V2,3 and 4.
1917 */
1918static void
1919nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1920    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1921    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1922    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
1923    NFSPROC_T *p, struct nfsexstuff *exp)
1924{
1925	vnode_t vp;
1926	u_int32_t *tl;
1927
1928	NFSVNO_SETATTRVAL(nvap, type, VDIR);
1929	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
1930	    nd->nd_cred, p, exp);
1931	if (!nd->nd_repstat) {
1932		vp = ndp->ni_vp;
1933		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
1934		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1935		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
1936			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd->nd_cred,
1937			    p, 1);
1938		if (vpp && !nd->nd_repstat) {
1939			NFSVOPUNLOCK(vp, 0);
1940			*vpp = vp;
1941		} else {
1942			vput(vp);
1943		}
1944	}
1945	if (dirp) {
1946		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1947		vrele(dirp);
1948	}
1949	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1950		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1951		*tl++ = newnfs_false;
1952		txdr_hyper(dirforp->na_filerev, tl);
1953		tl += 2;
1954		txdr_hyper(diraftp->na_filerev, tl);
1955		(void) nfsrv_putattrbit(nd, attrbitp);
1956	}
1957
1958	NFSEXITCODE2(0, nd);
1959}
1960
1961/*
1962 * nfs commit service
1963 */
1964APPLESTATIC int
1965nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
1966    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1967{
1968	struct nfsvattr bfor, aft;
1969	u_int32_t *tl;
1970	int error = 0, for_ret = 1, aft_ret = 1, cnt;
1971	u_int64_t off;
1972
1973	if (nd->nd_repstat) {
1974		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1975		goto out;
1976	}
1977	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1978	/*
1979	 * XXX At this time VOP_FSYNC() does not accept offset and byte
1980	 * count parameters, so these arguments are useless (someday maybe).
1981	 */
1982	off = fxdr_hyper(tl);
1983	tl += 2;
1984	cnt = fxdr_unsigned(int, *tl);
1985	if (nd->nd_flag & ND_NFSV3)
1986		for_ret = nfsvno_getattr(vp, &bfor, nd->nd_cred, p, 1);
1987	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
1988	if (nd->nd_flag & ND_NFSV3) {
1989		aft_ret = nfsvno_getattr(vp, &aft, nd->nd_cred, p, 1);
1990		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1991	}
1992	vput(vp);
1993	if (!nd->nd_repstat) {
1994		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1995		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
1996		*tl = txdr_unsigned(nfsboottime.tv_usec);
1997	}
1998
1999out:
2000	NFSEXITCODE2(0, nd);
2001	return (0);
2002nfsmout:
2003	vput(vp);
2004	NFSEXITCODE2(error, nd);
2005	return (error);
2006}
2007
2008/*
2009 * nfs statfs service
2010 */
2011APPLESTATIC int
2012nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
2013    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2014{
2015	struct statfs *sf;
2016	u_int32_t *tl;
2017	int getret = 1;
2018	struct nfsvattr at;
2019	struct statfs sfs;
2020	u_quad_t tval;
2021
2022	if (nd->nd_repstat) {
2023		nfsrv_postopattr(nd, getret, &at);
2024		goto out;
2025	}
2026	sf = &sfs;
2027	nd->nd_repstat = nfsvno_statfs(vp, sf);
2028	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2029	vput(vp);
2030	if (nd->nd_flag & ND_NFSV3)
2031		nfsrv_postopattr(nd, getret, &at);
2032	if (nd->nd_repstat)
2033		goto out;
2034	if (nd->nd_flag & ND_NFSV2) {
2035		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
2036		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
2037		*tl++ = txdr_unsigned(sf->f_bsize);
2038		*tl++ = txdr_unsigned(sf->f_blocks);
2039		*tl++ = txdr_unsigned(sf->f_bfree);
2040		*tl = txdr_unsigned(sf->f_bavail);
2041	} else {
2042		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
2043		tval = (u_quad_t)sf->f_blocks;
2044		tval *= (u_quad_t)sf->f_bsize;
2045		txdr_hyper(tval, tl); tl += 2;
2046		tval = (u_quad_t)sf->f_bfree;
2047		tval *= (u_quad_t)sf->f_bsize;
2048		txdr_hyper(tval, tl); tl += 2;
2049		tval = (u_quad_t)sf->f_bavail;
2050		tval *= (u_quad_t)sf->f_bsize;
2051		txdr_hyper(tval, tl); tl += 2;
2052		tval = (u_quad_t)sf->f_files;
2053		txdr_hyper(tval, tl); tl += 2;
2054		tval = (u_quad_t)sf->f_ffree;
2055		txdr_hyper(tval, tl); tl += 2;
2056		tval = (u_quad_t)sf->f_ffree;
2057		txdr_hyper(tval, tl); tl += 2;
2058		*tl = 0;
2059	}
2060
2061out:
2062	NFSEXITCODE2(0, nd);
2063	return (0);
2064}
2065
2066/*
2067 * nfs fsinfo service
2068 */
2069APPLESTATIC int
2070nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2071    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2072{
2073	u_int32_t *tl;
2074	struct nfsfsinfo fs;
2075	int getret = 1;
2076	struct nfsvattr at;
2077
2078	if (nd->nd_repstat) {
2079		nfsrv_postopattr(nd, getret, &at);
2080		goto out;
2081	}
2082	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2083	nfsvno_getfs(&fs, isdgram);
2084	vput(vp);
2085	nfsrv_postopattr(nd, getret, &at);
2086	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
2087	*tl++ = txdr_unsigned(fs.fs_rtmax);
2088	*tl++ = txdr_unsigned(fs.fs_rtpref);
2089	*tl++ = txdr_unsigned(fs.fs_rtmult);
2090	*tl++ = txdr_unsigned(fs.fs_wtmax);
2091	*tl++ = txdr_unsigned(fs.fs_wtpref);
2092	*tl++ = txdr_unsigned(fs.fs_wtmult);
2093	*tl++ = txdr_unsigned(fs.fs_dtpref);
2094	txdr_hyper(fs.fs_maxfilesize, tl);
2095	tl += 2;
2096	txdr_nfsv3time(&fs.fs_timedelta, tl);
2097	tl += 2;
2098	*tl = txdr_unsigned(fs.fs_properties);
2099
2100out:
2101	NFSEXITCODE2(0, nd);
2102	return (0);
2103}
2104
2105/*
2106 * nfs pathconf service
2107 */
2108APPLESTATIC int
2109nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2110    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2111{
2112	struct nfsv3_pathconf *pc;
2113	int getret = 1;
2114	register_t linkmax, namemax, chownres, notrunc;
2115	struct nfsvattr at;
2116
2117	if (nd->nd_repstat) {
2118		nfsrv_postopattr(nd, getret, &at);
2119		goto out;
2120	}
2121	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
2122	    nd->nd_cred, p);
2123	if (!nd->nd_repstat)
2124		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
2125		    nd->nd_cred, p);
2126	if (!nd->nd_repstat)
2127		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
2128		    &chownres, nd->nd_cred, p);
2129	if (!nd->nd_repstat)
2130		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
2131		    nd->nd_cred, p);
2132	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2133	vput(vp);
2134	nfsrv_postopattr(nd, getret, &at);
2135	if (!nd->nd_repstat) {
2136		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
2137		pc->pc_linkmax = txdr_unsigned(linkmax);
2138		pc->pc_namemax = txdr_unsigned(namemax);
2139		pc->pc_notrunc = txdr_unsigned(notrunc);
2140		pc->pc_chownrestricted = txdr_unsigned(chownres);
2141
2142		/*
2143		 * These should probably be supported by VOP_PATHCONF(), but
2144		 * until msdosfs is exportable (why would you want to?), the
2145		 * Unix defaults should be ok.
2146		 */
2147		pc->pc_caseinsensitive = newnfs_false;
2148		pc->pc_casepreserving = newnfs_true;
2149	}
2150
2151out:
2152	NFSEXITCODE2(0, nd);
2153	return (0);
2154}
2155
2156/*
2157 * nfsv4 lock service
2158 */
2159APPLESTATIC int
2160nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2161    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2162{
2163	u_int32_t *tl;
2164	int i;
2165	struct nfsstate *stp = NULL;
2166	struct nfslock *lop;
2167	struct nfslockconflict cf;
2168	int error = 0;
2169	u_short flags = NFSLCK_LOCK, lflags;
2170	u_int64_t offset, len;
2171	nfsv4stateid_t stateid;
2172	nfsquad_t clientid;
2173
2174	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2175	i = fxdr_unsigned(int, *tl++);
2176	switch (i) {
2177	case NFSV4LOCKT_READW:
2178		flags |= NFSLCK_BLOCKING;
2179	case NFSV4LOCKT_READ:
2180		lflags = NFSLCK_READ;
2181		break;
2182	case NFSV4LOCKT_WRITEW:
2183		flags |= NFSLCK_BLOCKING;
2184	case NFSV4LOCKT_WRITE:
2185		lflags = NFSLCK_WRITE;
2186		break;
2187	default:
2188		nd->nd_repstat = NFSERR_BADXDR;
2189		goto nfsmout;
2190	};
2191	if (*tl++ == newnfs_true)
2192		flags |= NFSLCK_RECLAIM;
2193	offset = fxdr_hyper(tl);
2194	tl += 2;
2195	len = fxdr_hyper(tl);
2196	tl += 2;
2197	if (*tl == newnfs_true)
2198		flags |= NFSLCK_OPENTOLOCK;
2199	if (flags & NFSLCK_OPENTOLOCK) {
2200		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
2201		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
2202		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2203			nd->nd_repstat = NFSERR_BADXDR;
2204			goto nfsmout;
2205		}
2206		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2207			M_NFSDSTATE, M_WAITOK);
2208		stp->ls_ownerlen = i;
2209		stp->ls_op = nd->nd_rp;
2210		stp->ls_seq = fxdr_unsigned(int, *tl++);
2211		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2212		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2213			NFSX_STATEIDOTHER);
2214		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2215		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
2216		clientid.lval[0] = *tl++;
2217		clientid.lval[1] = *tl++;
2218		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2219			if ((nd->nd_flag & ND_NFSV41) != 0)
2220				clientid.qval = nd->nd_clientid.qval;
2221			else if (nd->nd_clientid.qval != clientid.qval)
2222				printf("EEK3 multiple clids\n");
2223		} else {
2224			if ((nd->nd_flag & ND_NFSV41) != 0)
2225				printf("EEK! no clientid from session\n");
2226			nd->nd_flag |= ND_IMPLIEDCLID;
2227			nd->nd_clientid.qval = clientid.qval;
2228		}
2229		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2230		if (error)
2231			goto nfsmout;
2232	} else {
2233		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2234		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2235			M_NFSDSTATE, M_WAITOK);
2236		stp->ls_ownerlen = 0;
2237		stp->ls_op = nd->nd_rp;
2238		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2239		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2240			NFSX_STATEIDOTHER);
2241		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2242		stp->ls_seq = fxdr_unsigned(int, *tl);
2243		clientid.lval[0] = stp->ls_stateid.other[0];
2244		clientid.lval[1] = stp->ls_stateid.other[1];
2245		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2246			if ((nd->nd_flag & ND_NFSV41) != 0)
2247				clientid.qval = nd->nd_clientid.qval;
2248			else if (nd->nd_clientid.qval != clientid.qval)
2249				printf("EEK4 multiple clids\n");
2250		} else {
2251			if ((nd->nd_flag & ND_NFSV41) != 0)
2252				printf("EEK! no clientid from session\n");
2253			nd->nd_flag |= ND_IMPLIEDCLID;
2254			nd->nd_clientid.qval = clientid.qval;
2255		}
2256	}
2257	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2258		M_NFSDLOCK, M_WAITOK);
2259	lop->lo_first = offset;
2260	if (len == NFS64BITSSET) {
2261		lop->lo_end = NFS64BITSSET;
2262	} else {
2263		lop->lo_end = offset + len;
2264		if (lop->lo_end <= lop->lo_first)
2265			nd->nd_repstat = NFSERR_INVAL;
2266	}
2267	lop->lo_flags = lflags;
2268	stp->ls_flags = flags;
2269	stp->ls_uid = nd->nd_cred->cr_uid;
2270
2271	/*
2272	 * Do basic access checking.
2273	 */
2274	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2275	    if (vnode_vtype(vp) == VDIR)
2276		nd->nd_repstat = NFSERR_ISDIR;
2277	    else
2278		nd->nd_repstat = NFSERR_INVAL;
2279	}
2280	if (!nd->nd_repstat) {
2281	    if (lflags & NFSLCK_WRITE) {
2282		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
2283		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2284		    NFSACCCHK_VPISLOCKED, NULL);
2285	    } else {
2286		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
2287		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2288		    NFSACCCHK_VPISLOCKED, NULL);
2289		if (nd->nd_repstat)
2290		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2291			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2292			NFSACCCHK_VPISLOCKED, NULL);
2293	    }
2294	}
2295
2296	/*
2297	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
2298	 * seqid# gets updated. nfsrv_lockctrl() will return the value
2299	 * of nd_repstat, if it gets that far.
2300	 */
2301	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2302		&stateid, exp, nd, p);
2303	if (lop)
2304		FREE((caddr_t)lop, M_NFSDLOCK);
2305	if (stp)
2306		FREE((caddr_t)stp, M_NFSDSTATE);
2307	if (!nd->nd_repstat) {
2308		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2309		*tl++ = txdr_unsigned(stateid.seqid);
2310		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2311	} else if (nd->nd_repstat == NFSERR_DENIED) {
2312		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2313		txdr_hyper(cf.cl_first, tl);
2314		tl += 2;
2315		if (cf.cl_end == NFS64BITSSET)
2316			len = NFS64BITSSET;
2317		else
2318			len = cf.cl_end - cf.cl_first;
2319		txdr_hyper(len, tl);
2320		tl += 2;
2321		if (cf.cl_flags == NFSLCK_WRITE)
2322			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2323		else
2324			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2325		*tl++ = stateid.other[0];
2326		*tl = stateid.other[1];
2327		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2328	}
2329	vput(vp);
2330	NFSEXITCODE2(0, nd);
2331	return (0);
2332nfsmout:
2333	vput(vp);
2334	if (stp)
2335		free((caddr_t)stp, M_NFSDSTATE);
2336	NFSEXITCODE2(error, nd);
2337	return (error);
2338}
2339
2340/*
2341 * nfsv4 lock test service
2342 */
2343APPLESTATIC int
2344nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2345    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2346{
2347	u_int32_t *tl;
2348	int i;
2349	struct nfsstate *stp = NULL;
2350	struct nfslock lo, *lop = &lo;
2351	struct nfslockconflict cf;
2352	int error = 0;
2353	nfsv4stateid_t stateid;
2354	nfsquad_t clientid;
2355	u_int64_t len;
2356
2357	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
2358	i = fxdr_unsigned(int, *(tl + 7));
2359	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2360		nd->nd_repstat = NFSERR_BADXDR;
2361		goto nfsmout;
2362	}
2363	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2364	    M_NFSDSTATE, M_WAITOK);
2365	stp->ls_ownerlen = i;
2366	stp->ls_op = NULL;
2367	stp->ls_flags = NFSLCK_TEST;
2368	stp->ls_uid = nd->nd_cred->cr_uid;
2369	i = fxdr_unsigned(int, *tl++);
2370	switch (i) {
2371	case NFSV4LOCKT_READW:
2372		stp->ls_flags |= NFSLCK_BLOCKING;
2373	case NFSV4LOCKT_READ:
2374		lo.lo_flags = NFSLCK_READ;
2375		break;
2376	case NFSV4LOCKT_WRITEW:
2377		stp->ls_flags |= NFSLCK_BLOCKING;
2378	case NFSV4LOCKT_WRITE:
2379		lo.lo_flags = NFSLCK_WRITE;
2380		break;
2381	default:
2382		nd->nd_repstat = NFSERR_BADXDR;
2383		goto nfsmout;
2384	};
2385	lo.lo_first = fxdr_hyper(tl);
2386	tl += 2;
2387	len = fxdr_hyper(tl);
2388	if (len == NFS64BITSSET) {
2389		lo.lo_end = NFS64BITSSET;
2390	} else {
2391		lo.lo_end = lo.lo_first + len;
2392		if (lo.lo_end <= lo.lo_first)
2393			nd->nd_repstat = NFSERR_INVAL;
2394	}
2395	tl += 2;
2396	clientid.lval[0] = *tl++;
2397	clientid.lval[1] = *tl;
2398	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2399		if ((nd->nd_flag & ND_NFSV41) != 0)
2400			clientid.qval = nd->nd_clientid.qval;
2401		else if (nd->nd_clientid.qval != clientid.qval)
2402			printf("EEK5 multiple clids\n");
2403	} else {
2404		if ((nd->nd_flag & ND_NFSV41) != 0)
2405			printf("EEK! no clientid from session\n");
2406		nd->nd_flag |= ND_IMPLIEDCLID;
2407		nd->nd_clientid.qval = clientid.qval;
2408	}
2409	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2410	if (error)
2411		goto nfsmout;
2412	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2413	    if (vnode_vtype(vp) == VDIR)
2414		nd->nd_repstat = NFSERR_ISDIR;
2415	    else
2416		nd->nd_repstat = NFSERR_INVAL;
2417	}
2418	if (!nd->nd_repstat)
2419	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2420	    &stateid, exp, nd, p);
2421	if (nd->nd_repstat) {
2422	    if (nd->nd_repstat == NFSERR_DENIED) {
2423		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2424		txdr_hyper(cf.cl_first, tl);
2425		tl += 2;
2426		if (cf.cl_end == NFS64BITSSET)
2427			len = NFS64BITSSET;
2428		else
2429			len = cf.cl_end - cf.cl_first;
2430		txdr_hyper(len, tl);
2431		tl += 2;
2432		if (cf.cl_flags == NFSLCK_WRITE)
2433			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2434		else
2435			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2436		*tl++ = stp->ls_stateid.other[0];
2437		*tl = stp->ls_stateid.other[1];
2438		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2439	    }
2440	}
2441	vput(vp);
2442	if (stp)
2443		FREE((caddr_t)stp, M_NFSDSTATE);
2444	NFSEXITCODE2(0, nd);
2445	return (0);
2446nfsmout:
2447	vput(vp);
2448	if (stp)
2449		free((caddr_t)stp, M_NFSDSTATE);
2450	NFSEXITCODE2(error, nd);
2451	return (error);
2452}
2453
2454/*
2455 * nfsv4 unlock service
2456 */
2457APPLESTATIC int
2458nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2459    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2460{
2461	u_int32_t *tl;
2462	int i;
2463	struct nfsstate *stp;
2464	struct nfslock *lop;
2465	int error = 0;
2466	nfsv4stateid_t stateid;
2467	nfsquad_t clientid;
2468	u_int64_t len;
2469
2470	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2471	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2472	    M_NFSDSTATE, M_WAITOK);
2473	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2474	    M_NFSDLOCK, M_WAITOK);
2475	stp->ls_flags = NFSLCK_UNLOCK;
2476	lop->lo_flags = NFSLCK_UNLOCK;
2477	stp->ls_op = nd->nd_rp;
2478	i = fxdr_unsigned(int, *tl++);
2479	switch (i) {
2480	case NFSV4LOCKT_READW:
2481		stp->ls_flags |= NFSLCK_BLOCKING;
2482	case NFSV4LOCKT_READ:
2483		break;
2484	case NFSV4LOCKT_WRITEW:
2485		stp->ls_flags |= NFSLCK_BLOCKING;
2486	case NFSV4LOCKT_WRITE:
2487		break;
2488	default:
2489		nd->nd_repstat = NFSERR_BADXDR;
2490		free(stp, M_NFSDSTATE);
2491		free(lop, M_NFSDLOCK);
2492		goto nfsmout;
2493	};
2494	stp->ls_ownerlen = 0;
2495	stp->ls_uid = nd->nd_cred->cr_uid;
2496	stp->ls_seq = fxdr_unsigned(int, *tl++);
2497	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2498	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2499	    NFSX_STATEIDOTHER);
2500	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2501	lop->lo_first = fxdr_hyper(tl);
2502	tl += 2;
2503	len = fxdr_hyper(tl);
2504	if (len == NFS64BITSSET) {
2505		lop->lo_end = NFS64BITSSET;
2506	} else {
2507		lop->lo_end = lop->lo_first + len;
2508		if (lop->lo_end <= lop->lo_first)
2509			nd->nd_repstat = NFSERR_INVAL;
2510	}
2511	clientid.lval[0] = stp->ls_stateid.other[0];
2512	clientid.lval[1] = stp->ls_stateid.other[1];
2513	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2514		if ((nd->nd_flag & ND_NFSV41) != 0)
2515			clientid.qval = nd->nd_clientid.qval;
2516		else if (nd->nd_clientid.qval != clientid.qval)
2517			printf("EEK6 multiple clids\n");
2518	} else {
2519		if ((nd->nd_flag & ND_NFSV41) != 0)
2520			printf("EEK! no clientid from session\n");
2521		nd->nd_flag |= ND_IMPLIEDCLID;
2522		nd->nd_clientid.qval = clientid.qval;
2523	}
2524	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2525	    if (vnode_vtype(vp) == VDIR)
2526		nd->nd_repstat = NFSERR_ISDIR;
2527	    else
2528		nd->nd_repstat = NFSERR_INVAL;
2529	}
2530	/*
2531	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
2532	 * seqid# gets incremented. nfsrv_lockctrl() will return the
2533	 * value of nd_repstat, if it gets that far.
2534	 */
2535	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
2536	    &stateid, exp, nd, p);
2537	if (stp)
2538		FREE((caddr_t)stp, M_NFSDSTATE);
2539	if (lop)
2540		free((caddr_t)lop, M_NFSDLOCK);
2541	if (!nd->nd_repstat) {
2542		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2543		*tl++ = txdr_unsigned(stateid.seqid);
2544		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2545	}
2546nfsmout:
2547	vput(vp);
2548	NFSEXITCODE2(error, nd);
2549	return (error);
2550}
2551
2552/*
2553 * nfsv4 open service
2554 */
2555APPLESTATIC int
2556nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2557    vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
2558    struct nfsexstuff *exp)
2559{
2560	u_int32_t *tl;
2561	int i, retext;
2562	struct nfsstate *stp = NULL;
2563	int error = 0, create, claim, exclusive_flag = 0;
2564	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
2565	int how = NFSCREATE_UNCHECKED;
2566	int32_t cverf[2], tverf[2] = { 0, 0 };
2567	vnode_t vp = NULL, dirp = NULL;
2568	struct nfsvattr nva, dirfor, diraft;
2569	struct nameidata named;
2570	nfsv4stateid_t stateid, delegstateid;
2571	nfsattrbit_t attrbits;
2572	nfsquad_t clientid;
2573	char *bufp = NULL;
2574	u_long *hashp;
2575	NFSACL_T *aclp = NULL;
2576
2577#ifdef NFS4_ACL_EXTATTR_NAME
2578	aclp = acl_alloc(M_WAITOK);
2579	aclp->acl_cnt = 0;
2580#endif
2581	NFSZERO_ATTRBIT(&attrbits);
2582	named.ni_startdir = NULL;
2583	named.ni_cnd.cn_nameiop = 0;
2584	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
2585	i = fxdr_unsigned(int, *(tl + 5));
2586	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2587		nd->nd_repstat = NFSERR_BADXDR;
2588		goto nfsmout;
2589	}
2590	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2591	    M_NFSDSTATE, M_WAITOK);
2592	stp->ls_ownerlen = i;
2593	stp->ls_op = nd->nd_rp;
2594	stp->ls_flags = NFSLCK_OPEN;
2595	stp->ls_uid = nd->nd_cred->cr_uid;
2596	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2597	i = fxdr_unsigned(int, *tl++);
2598	retext = 0;
2599	if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
2600	    NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
2601		retext = 1;
2602		/* For now, ignore these. */
2603		i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
2604		switch (i & NFSV4OPEN_WANTDELEGMASK) {
2605		case NFSV4OPEN_WANTANYDELEG:
2606			stp->ls_flags |= (NFSLCK_WANTRDELEG |
2607			    NFSLCK_WANTWDELEG);
2608			i &= ~NFSV4OPEN_WANTDELEGMASK;
2609			break;
2610		case NFSV4OPEN_WANTREADDELEG:
2611			stp->ls_flags |= NFSLCK_WANTRDELEG;
2612			i &= ~NFSV4OPEN_WANTDELEGMASK;
2613			break;
2614		case NFSV4OPEN_WANTWRITEDELEG:
2615			stp->ls_flags |= NFSLCK_WANTWDELEG;
2616			i &= ~NFSV4OPEN_WANTDELEGMASK;
2617			break;
2618		case NFSV4OPEN_WANTNODELEG:
2619			stp->ls_flags |= NFSLCK_WANTNODELEG;
2620			i &= ~NFSV4OPEN_WANTDELEGMASK;
2621			break;
2622		case NFSV4OPEN_WANTCANCEL:
2623			printf("NFSv4: ignore Open WantCancel\n");
2624			i &= ~NFSV4OPEN_WANTDELEGMASK;
2625			break;
2626		default:
2627			/* nd_repstat will be set to NFSERR_INVAL below. */
2628			break;
2629		};
2630	}
2631	switch (i) {
2632	case NFSV4OPEN_ACCESSREAD:
2633		stp->ls_flags |= NFSLCK_READACCESS;
2634		break;
2635	case NFSV4OPEN_ACCESSWRITE:
2636		stp->ls_flags |= NFSLCK_WRITEACCESS;
2637		break;
2638	case NFSV4OPEN_ACCESSBOTH:
2639		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
2640		break;
2641	default:
2642		nd->nd_repstat = NFSERR_INVAL;
2643	};
2644	i = fxdr_unsigned(int, *tl++);
2645	switch (i) {
2646	case NFSV4OPEN_DENYNONE:
2647		break;
2648	case NFSV4OPEN_DENYREAD:
2649		stp->ls_flags |= NFSLCK_READDENY;
2650		break;
2651	case NFSV4OPEN_DENYWRITE:
2652		stp->ls_flags |= NFSLCK_WRITEDENY;
2653		break;
2654	case NFSV4OPEN_DENYBOTH:
2655		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
2656		break;
2657	default:
2658		nd->nd_repstat = NFSERR_INVAL;
2659	};
2660	clientid.lval[0] = *tl++;
2661	clientid.lval[1] = *tl;
2662	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2663		if ((nd->nd_flag & ND_NFSV41) != 0)
2664			clientid.qval = nd->nd_clientid.qval;
2665		else if (nd->nd_clientid.qval != clientid.qval)
2666			printf("EEK7 multiple clids\n");
2667	} else {
2668		if ((nd->nd_flag & ND_NFSV41) != 0)
2669			printf("EEK! no clientid from session\n");
2670		nd->nd_flag |= ND_IMPLIEDCLID;
2671		nd->nd_clientid.qval = clientid.qval;
2672	}
2673	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2674	if (error)
2675		goto nfsmout;
2676	NFSVNO_ATTRINIT(&nva);
2677	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2678	create = fxdr_unsigned(int, *tl);
2679	if (!nd->nd_repstat)
2680		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
2681	if (create == NFSV4OPEN_CREATE) {
2682		nva.na_type = VREG;
2683		nva.na_mode = 0;
2684		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2685		how = fxdr_unsigned(int, *tl);
2686		switch (how) {
2687		case NFSCREATE_UNCHECKED:
2688		case NFSCREATE_GUARDED:
2689			error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p);
2690			if (error)
2691				goto nfsmout;
2692			/*
2693			 * If the na_gid being set is the same as that of
2694			 * the directory it is going in, clear it, since
2695			 * that is what will be set by default. This allows
2696			 * a user that isn't in that group to do the create.
2697			 */
2698			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
2699			    nva.na_gid == dirfor.na_gid)
2700				NFSVNO_UNSET(&nva, gid);
2701			if (!nd->nd_repstat)
2702				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2703			break;
2704		case NFSCREATE_EXCLUSIVE:
2705			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2706			cverf[0] = *tl++;
2707			cverf[1] = *tl;
2708			break;
2709		case NFSCREATE_EXCLUSIVE41:
2710			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2711			cverf[0] = *tl++;
2712			cverf[1] = *tl;
2713			error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p);
2714			if (error != 0)
2715				goto nfsmout;
2716			if (NFSISSET_ATTRBIT(&attrbits,
2717			    NFSATTRBIT_TIMEACCESSSET))
2718				nd->nd_repstat = NFSERR_INVAL;
2719			/*
2720			 * If the na_gid being set is the same as that of
2721			 * the directory it is going in, clear it, since
2722			 * that is what will be set by default. This allows
2723			 * a user that isn't in that group to do the create.
2724			 */
2725			if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
2726			    nva.na_gid == dirfor.na_gid)
2727				NFSVNO_UNSET(&nva, gid);
2728			if (nd->nd_repstat == 0)
2729				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2730			break;
2731		default:
2732			nd->nd_repstat = NFSERR_BADXDR;
2733			goto nfsmout;
2734		};
2735	} else if (create != NFSV4OPEN_NOCREATE) {
2736		nd->nd_repstat = NFSERR_BADXDR;
2737		goto nfsmout;
2738	}
2739
2740	/*
2741	 * Now, handle the claim, which usually includes looking up a
2742	 * name in the directory referenced by dp. The exception is
2743	 * NFSV4OPEN_CLAIMPREVIOUS.
2744	 */
2745	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2746	claim = fxdr_unsigned(int, *tl);
2747	if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
2748		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2749		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2750		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
2751		stp->ls_flags |= NFSLCK_DELEGCUR;
2752	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2753		stp->ls_flags |= NFSLCK_DELEGPREV;
2754	}
2755	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
2756	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2757		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
2758		    claim != NFSV4OPEN_CLAIMNULL)
2759			nd->nd_repstat = NFSERR_INVAL;
2760		if (nd->nd_repstat) {
2761			nd->nd_repstat = nfsrv_opencheck(clientid,
2762			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
2763			goto nfsmout;
2764		}
2765		if (create == NFSV4OPEN_CREATE)
2766		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
2767			LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
2768		else
2769		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
2770			LOCKLEAF | SAVESTART);
2771		nfsvno_setpathbuf(&named, &bufp, &hashp);
2772		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2773		if (error) {
2774			vrele(dp);
2775#ifdef NFS4_ACL_EXTATTR_NAME
2776			acl_free(aclp);
2777#endif
2778			FREE((caddr_t)stp, M_NFSDSTATE);
2779			nfsvno_relpathbuf(&named);
2780			NFSEXITCODE2(error, nd);
2781			return (error);
2782		}
2783		if (!nd->nd_repstat) {
2784			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
2785			    p, &dirp);
2786		} else {
2787			vrele(dp);
2788			nfsvno_relpathbuf(&named);
2789		}
2790		if (create == NFSV4OPEN_CREATE) {
2791		    switch (how) {
2792		    case NFSCREATE_UNCHECKED:
2793			if (named.ni_vp) {
2794				/*
2795				 * Clear the setable attribute bits, except
2796				 * for Size, if it is being truncated.
2797				 */
2798				NFSZERO_ATTRBIT(&attrbits);
2799				if (NFSVNO_ISSETSIZE(&nva))
2800					NFSSETBIT_ATTRBIT(&attrbits,
2801					    NFSATTRBIT_SIZE);
2802			}
2803			break;
2804		    case NFSCREATE_GUARDED:
2805			if (named.ni_vp && !nd->nd_repstat)
2806				nd->nd_repstat = EEXIST;
2807			break;
2808		    case NFSCREATE_EXCLUSIVE:
2809			exclusive_flag = 1;
2810			if (!named.ni_vp)
2811				nva.na_mode = 0;
2812			break;
2813		    case NFSCREATE_EXCLUSIVE41:
2814			exclusive_flag = 1;
2815			break;
2816		    };
2817		}
2818		nfsvno_open(nd, &named, clientid, &stateid, stp,
2819		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
2820		    nd->nd_cred, p, exp, &vp);
2821	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
2822	    NFSV4OPEN_CLAIMFH) {
2823		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2824			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2825			i = fxdr_unsigned(int, *tl);
2826			switch (i) {
2827			case NFSV4OPEN_DELEGATEREAD:
2828				stp->ls_flags |= NFSLCK_DELEGREAD;
2829				break;
2830			case NFSV4OPEN_DELEGATEWRITE:
2831				stp->ls_flags |= NFSLCK_DELEGWRITE;
2832			case NFSV4OPEN_DELEGATENONE:
2833				break;
2834			default:
2835				nd->nd_repstat = NFSERR_BADXDR;
2836				goto nfsmout;
2837			};
2838			stp->ls_flags |= NFSLCK_RECLAIM;
2839		} else {
2840			/* CLAIM_NULL_FH */
2841			if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
2842				nd->nd_repstat = NFSERR_INVAL;
2843		}
2844		vp = dp;
2845		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
2846		if ((vp->v_iflag & VI_DOOMED) == 0)
2847			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
2848			    stp, vp, nd, p, nd->nd_repstat);
2849		else
2850			nd->nd_repstat = NFSERR_PERM;
2851	} else {
2852		nd->nd_repstat = NFSERR_BADXDR;
2853		goto nfsmout;
2854	}
2855
2856	/*
2857	 * Do basic access checking.
2858	 */
2859	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2860		/*
2861		 * The IETF working group decided that this is the correct
2862		 * error return for all non-regular files.
2863		 */
2864		nd->nd_repstat = NFSERR_SYMLINK;
2865	}
2866	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
2867	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
2868	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2869	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
2870	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
2871	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2872	    if (nd->nd_repstat)
2873		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2874		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2875		    NFSACCCHK_VPISLOCKED, NULL);
2876	}
2877
2878	if (!nd->nd_repstat) {
2879		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
2880		if (!nd->nd_repstat) {
2881			tverf[0] = nva.na_atime.tv_sec;
2882			tverf[1] = nva.na_atime.tv_nsec;
2883		}
2884	}
2885	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
2886	    cverf[1] != tverf[1]))
2887		nd->nd_repstat = EEXIST;
2888	/*
2889	 * Do the open locking/delegation stuff.
2890	 */
2891	if (!nd->nd_repstat)
2892	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
2893		&delegstateid, &rflags, exp, p, nva.na_filerev);
2894
2895	/*
2896	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
2897	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
2898	 * (ie: Leave the NFSVOPUNLOCK() about here.)
2899	 */
2900	if (vp)
2901		NFSVOPUNLOCK(vp, 0);
2902	if (stp)
2903		FREE((caddr_t)stp, M_NFSDSTATE);
2904	if (!nd->nd_repstat && dirp)
2905		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p,
2906		    0);
2907	if (!nd->nd_repstat) {
2908		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
2909		*tl++ = txdr_unsigned(stateid.seqid);
2910		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2911		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2912		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2913			*tl++ = newnfs_true;
2914			*tl++ = 0;
2915			*tl++ = 0;
2916			*tl++ = 0;
2917			*tl++ = 0;
2918		} else {
2919			*tl++ = newnfs_false;	/* Since dirp is not locked */
2920			txdr_hyper(dirfor.na_filerev, tl);
2921			tl += 2;
2922			txdr_hyper(diraft.na_filerev, tl);
2923			tl += 2;
2924		}
2925		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
2926		(void) nfsrv_putattrbit(nd, &attrbits);
2927		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2928		if (rflags & NFSV4OPEN_READDELEGATE)
2929			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
2930		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
2931			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
2932		else if (retext != 0) {
2933			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
2934			if ((rflags & NFSV4OPEN_WDNOTWANTED) != 0) {
2935				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2936				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
2937			} else if ((rflags & NFSV4OPEN_WDSUPPFTYPE) != 0) {
2938				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2939				*tl = txdr_unsigned(NFSV4OPEN_NOTSUPPFTYPE);
2940			} else if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
2941				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2942				*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
2943				*tl = newnfs_false;
2944			} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
2945				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2946				*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
2947				*tl = newnfs_false;
2948			} else {
2949				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2950				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
2951			}
2952		} else
2953			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
2954		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
2955			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
2956			*tl++ = txdr_unsigned(delegstateid.seqid);
2957			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
2958			    NFSX_STATEIDOTHER);
2959			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2960			if (rflags & NFSV4OPEN_RECALL)
2961				*tl = newnfs_true;
2962			else
2963				*tl = newnfs_false;
2964			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
2965				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2966				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
2967				txdr_hyper(nva.na_size, tl);
2968			}
2969			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2970			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
2971			*tl++ = txdr_unsigned(0x0);
2972			acemask = NFSV4ACE_ALLFILESMASK;
2973			if (nva.na_mode & S_IRUSR)
2974			    acemask |= NFSV4ACE_READMASK;
2975			if (nva.na_mode & S_IWUSR)
2976			    acemask |= NFSV4ACE_WRITEMASK;
2977			if (nva.na_mode & S_IXUSR)
2978			    acemask |= NFSV4ACE_EXECUTEMASK;
2979			*tl = txdr_unsigned(acemask);
2980			(void) nfsm_strtom(nd, "OWNER@", 6);
2981		}
2982		*vpp = vp;
2983	} else if (vp) {
2984		vrele(vp);
2985	}
2986	if (dirp)
2987		vrele(dirp);
2988#ifdef NFS4_ACL_EXTATTR_NAME
2989	acl_free(aclp);
2990#endif
2991	NFSEXITCODE2(0, nd);
2992	return (0);
2993nfsmout:
2994	vrele(dp);
2995#ifdef NFS4_ACL_EXTATTR_NAME
2996	acl_free(aclp);
2997#endif
2998	if (stp)
2999		FREE((caddr_t)stp, M_NFSDSTATE);
3000	NFSEXITCODE2(error, nd);
3001	return (error);
3002}
3003
3004/*
3005 * nfsv4 close service
3006 */
3007APPLESTATIC int
3008nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
3009    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3010{
3011	u_int32_t *tl;
3012	struct nfsstate st, *stp = &st;
3013	int error = 0;
3014	nfsv4stateid_t stateid;
3015	nfsquad_t clientid;
3016
3017	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
3018	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3019	stp->ls_ownerlen = 0;
3020	stp->ls_op = nd->nd_rp;
3021	stp->ls_uid = nd->nd_cred->cr_uid;
3022	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3023	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3024	    NFSX_STATEIDOTHER);
3025	stp->ls_flags = NFSLCK_CLOSE;
3026	clientid.lval[0] = stp->ls_stateid.other[0];
3027	clientid.lval[1] = stp->ls_stateid.other[1];
3028	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3029		if ((nd->nd_flag & ND_NFSV41) != 0)
3030			clientid.qval = nd->nd_clientid.qval;
3031		else if (nd->nd_clientid.qval != clientid.qval)
3032			printf("EEK8 multiple clids\n");
3033	} else {
3034		if ((nd->nd_flag & ND_NFSV41) != 0)
3035			printf("EEK! no clientid from session\n");
3036		nd->nd_flag |= ND_IMPLIEDCLID;
3037		nd->nd_clientid.qval = clientid.qval;
3038	}
3039	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3040	vput(vp);
3041	if (!nd->nd_repstat) {
3042		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3043		*tl++ = txdr_unsigned(stateid.seqid);
3044		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3045	}
3046	NFSEXITCODE2(0, nd);
3047	return (0);
3048nfsmout:
3049	vput(vp);
3050	NFSEXITCODE2(error, nd);
3051	return (error);
3052}
3053
3054/*
3055 * nfsv4 delegpurge service
3056 */
3057APPLESTATIC int
3058nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
3059    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3060{
3061	u_int32_t *tl;
3062	int error = 0;
3063	nfsquad_t clientid;
3064
3065	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3066		nd->nd_repstat = NFSERR_WRONGSEC;
3067		goto nfsmout;
3068	}
3069	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3070	clientid.lval[0] = *tl++;
3071	clientid.lval[1] = *tl;
3072	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3073		if ((nd->nd_flag & ND_NFSV41) != 0)
3074			clientid.qval = nd->nd_clientid.qval;
3075		else if (nd->nd_clientid.qval != clientid.qval)
3076			printf("EEK9 multiple clids\n");
3077	} else {
3078		if ((nd->nd_flag & ND_NFSV41) != 0)
3079			printf("EEK! no clientid from session\n");
3080		nd->nd_flag |= ND_IMPLIEDCLID;
3081		nd->nd_clientid.qval = clientid.qval;
3082	}
3083	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
3084	    NFSV4OP_DELEGPURGE, nd->nd_cred, p);
3085nfsmout:
3086	NFSEXITCODE2(error, nd);
3087	return (error);
3088}
3089
3090/*
3091 * nfsv4 delegreturn service
3092 */
3093APPLESTATIC int
3094nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
3095    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3096{
3097	u_int32_t *tl;
3098	int error = 0;
3099	nfsv4stateid_t stateid;
3100	nfsquad_t clientid;
3101
3102	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3103	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3104	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
3105	clientid.lval[0] = stateid.other[0];
3106	clientid.lval[1] = stateid.other[1];
3107	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3108		if ((nd->nd_flag & ND_NFSV41) != 0)
3109			clientid.qval = nd->nd_clientid.qval;
3110		else if (nd->nd_clientid.qval != clientid.qval)
3111			printf("EEK10 multiple clids\n");
3112	} else {
3113		if ((nd->nd_flag & ND_NFSV41) != 0)
3114			printf("EEK! no clientid from session\n");
3115		nd->nd_flag |= ND_IMPLIEDCLID;
3116		nd->nd_clientid.qval = clientid.qval;
3117	}
3118	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
3119	    NFSV4OP_DELEGRETURN, nd->nd_cred, p);
3120nfsmout:
3121	vput(vp);
3122	NFSEXITCODE2(error, nd);
3123	return (error);
3124}
3125
3126/*
3127 * nfsv4 get file handle service
3128 */
3129APPLESTATIC int
3130nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
3131    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3132{
3133	fhandle_t fh;
3134
3135	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3136	vput(vp);
3137	if (!nd->nd_repstat)
3138		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3139	NFSEXITCODE2(0, nd);
3140	return (0);
3141}
3142
3143/*
3144 * nfsv4 open confirm service
3145 */
3146APPLESTATIC int
3147nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3148    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3149{
3150	u_int32_t *tl;
3151	struct nfsstate st, *stp = &st;
3152	int error = 0;
3153	nfsv4stateid_t stateid;
3154	nfsquad_t clientid;
3155
3156	if ((nd->nd_flag & ND_NFSV41) != 0) {
3157		nd->nd_repstat = NFSERR_NOTSUPP;
3158		goto nfsmout;
3159	}
3160	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3161	stp->ls_ownerlen = 0;
3162	stp->ls_op = nd->nd_rp;
3163	stp->ls_uid = nd->nd_cred->cr_uid;
3164	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3165	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3166	    NFSX_STATEIDOTHER);
3167	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3168	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
3169	stp->ls_flags = NFSLCK_CONFIRM;
3170	clientid.lval[0] = stp->ls_stateid.other[0];
3171	clientid.lval[1] = stp->ls_stateid.other[1];
3172	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3173		if ((nd->nd_flag & ND_NFSV41) != 0)
3174			clientid.qval = nd->nd_clientid.qval;
3175		else if (nd->nd_clientid.qval != clientid.qval)
3176			printf("EEK11 multiple clids\n");
3177	} else {
3178		if ((nd->nd_flag & ND_NFSV41) != 0)
3179			printf("EEK! no clientid from session\n");
3180		nd->nd_flag |= ND_IMPLIEDCLID;
3181		nd->nd_clientid.qval = clientid.qval;
3182	}
3183	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3184	if (!nd->nd_repstat) {
3185		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3186		*tl++ = txdr_unsigned(stateid.seqid);
3187		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3188	}
3189nfsmout:
3190	vput(vp);
3191	NFSEXITCODE2(error, nd);
3192	return (error);
3193}
3194
3195/*
3196 * nfsv4 open downgrade service
3197 */
3198APPLESTATIC int
3199nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3200    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3201{
3202	u_int32_t *tl;
3203	int i;
3204	struct nfsstate st, *stp = &st;
3205	int error = 0;
3206	nfsv4stateid_t stateid;
3207	nfsquad_t clientid;
3208
3209	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
3210	stp->ls_ownerlen = 0;
3211	stp->ls_op = nd->nd_rp;
3212	stp->ls_uid = nd->nd_cred->cr_uid;
3213	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3214	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3215	    NFSX_STATEIDOTHER);
3216	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3217	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3218	i = fxdr_unsigned(int, *tl++);
3219	switch (i) {
3220	case NFSV4OPEN_ACCESSREAD:
3221		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
3222		break;
3223	case NFSV4OPEN_ACCESSWRITE:
3224		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
3225		break;
3226	case NFSV4OPEN_ACCESSBOTH:
3227		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
3228		    NFSLCK_DOWNGRADE);
3229		break;
3230	default:
3231		nd->nd_repstat = NFSERR_BADXDR;
3232	};
3233	i = fxdr_unsigned(int, *tl);
3234	switch (i) {
3235	case NFSV4OPEN_DENYNONE:
3236		break;
3237	case NFSV4OPEN_DENYREAD:
3238		stp->ls_flags |= NFSLCK_READDENY;
3239		break;
3240	case NFSV4OPEN_DENYWRITE:
3241		stp->ls_flags |= NFSLCK_WRITEDENY;
3242		break;
3243	case NFSV4OPEN_DENYBOTH:
3244		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
3245		break;
3246	default:
3247		nd->nd_repstat = NFSERR_BADXDR;
3248	};
3249
3250	clientid.lval[0] = stp->ls_stateid.other[0];
3251	clientid.lval[1] = stp->ls_stateid.other[1];
3252	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3253		if ((nd->nd_flag & ND_NFSV41) != 0)
3254			clientid.qval = nd->nd_clientid.qval;
3255		else if (nd->nd_clientid.qval != clientid.qval)
3256			printf("EEK12 multiple clids\n");
3257	} else {
3258		if ((nd->nd_flag & ND_NFSV41) != 0)
3259			printf("EEK! no clientid from session\n");
3260		nd->nd_flag |= ND_IMPLIEDCLID;
3261		nd->nd_clientid.qval = clientid.qval;
3262	}
3263	if (!nd->nd_repstat)
3264		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
3265		    nd, p);
3266	if (!nd->nd_repstat) {
3267		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3268		*tl++ = txdr_unsigned(stateid.seqid);
3269		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3270	}
3271nfsmout:
3272	vput(vp);
3273	NFSEXITCODE2(error, nd);
3274	return (error);
3275}
3276
3277/*
3278 * nfsv4 renew lease service
3279 */
3280APPLESTATIC int
3281nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3282    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3283{
3284	u_int32_t *tl;
3285	int error = 0;
3286	nfsquad_t clientid;
3287
3288	if ((nd->nd_flag & ND_NFSV41) != 0) {
3289		nd->nd_repstat = NFSERR_NOTSUPP;
3290		goto nfsmout;
3291	}
3292	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3293		nd->nd_repstat = NFSERR_WRONGSEC;
3294		goto nfsmout;
3295	}
3296	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
3297	clientid.lval[0] = *tl++;
3298	clientid.lval[1] = *tl;
3299	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3300		if ((nd->nd_flag & ND_NFSV41) != 0)
3301			clientid.qval = nd->nd_clientid.qval;
3302		else if (nd->nd_clientid.qval != clientid.qval)
3303			printf("EEK13 multiple clids\n");
3304	} else {
3305		if ((nd->nd_flag & ND_NFSV41) != 0)
3306			printf("EEK! no clientid from session\n");
3307		nd->nd_flag |= ND_IMPLIEDCLID;
3308		nd->nd_clientid.qval = clientid.qval;
3309	}
3310	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3311	    NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
3312nfsmout:
3313	NFSEXITCODE2(error, nd);
3314	return (error);
3315}
3316
3317/*
3318 * nfsv4 security info service
3319 */
3320APPLESTATIC int
3321nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3322    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
3323{
3324	u_int32_t *tl;
3325	int len;
3326	struct nameidata named;
3327	vnode_t dirp = NULL, vp;
3328	struct nfsrvfh fh;
3329	struct nfsexstuff retnes;
3330	u_int32_t *sizp;
3331	int error = 0, savflag, i;
3332	char *bufp;
3333	u_long *hashp;
3334
3335	/*
3336	 * All this just to get the export flags for the name.
3337	 */
3338	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
3339	    LOCKLEAF | SAVESTART);
3340	nfsvno_setpathbuf(&named, &bufp, &hashp);
3341	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3342	if (error) {
3343		vput(dp);
3344		nfsvno_relpathbuf(&named);
3345		goto out;
3346	}
3347	if (!nd->nd_repstat) {
3348		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
3349	} else {
3350		vput(dp);
3351		nfsvno_relpathbuf(&named);
3352	}
3353	if (dirp)
3354		vrele(dirp);
3355	if (nd->nd_repstat)
3356		goto out;
3357	vrele(named.ni_startdir);
3358	nfsvno_relpathbuf(&named);
3359	fh.nfsrvfh_len = NFSX_MYFH;
3360	vp = named.ni_vp;
3361	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3362	vput(vp);
3363	savflag = nd->nd_flag;
3364	if (!nd->nd_repstat) {
3365		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0, p);
3366		if (vp)
3367			vput(vp);
3368	}
3369	nd->nd_flag = savflag;
3370	if (nd->nd_repstat)
3371		goto out;
3372
3373	/*
3374	 * Finally have the export flags for name, so we can create
3375	 * the security info.
3376	 */
3377	len = 0;
3378	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
3379	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3380		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3381			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3382			*tl = txdr_unsigned(RPCAUTH_UNIX);
3383			len++;
3384		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3385			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3386			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3387			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3388			    nfsgss_mechlist[KERBV_MECH].len);
3389			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3390			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3391			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3392			len++;
3393		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
3394			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3395			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3396			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3397			    nfsgss_mechlist[KERBV_MECH].len);
3398			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3399			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3400			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3401			len++;
3402		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
3403			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3404			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3405			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3406			    nfsgss_mechlist[KERBV_MECH].len);
3407			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3408			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3409			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3410			len++;
3411		}
3412	}
3413	*sizp = txdr_unsigned(len);
3414
3415out:
3416	NFSEXITCODE2(error, nd);
3417	return (error);
3418}
3419
3420/*
3421 * nfsv4 set client id service
3422 */
3423APPLESTATIC int
3424nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3425    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3426{
3427	u_int32_t *tl;
3428	int i;
3429	int error = 0, idlen;
3430	struct nfsclient *clp = NULL;
3431#ifdef INET
3432	struct sockaddr_in *rin;
3433#endif
3434#ifdef INET6
3435	struct sockaddr_in6 *rin6;
3436#endif
3437#if defined(INET) || defined(INET6)
3438	u_char *ucp, *ucp2;
3439#endif
3440	u_char *verf, *addrbuf;
3441	nfsquad_t clientid, confirm;
3442
3443	if ((nd->nd_flag & ND_NFSV41) != 0) {
3444		nd->nd_repstat = NFSERR_NOTSUPP;
3445		goto nfsmout;
3446	}
3447	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3448		nd->nd_repstat = NFSERR_WRONGSEC;
3449		goto out;
3450	}
3451	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3452	verf = (u_char *)tl;
3453	tl += (NFSX_VERF / NFSX_UNSIGNED);
3454	i = fxdr_unsigned(int, *tl);
3455	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3456		nd->nd_repstat = NFSERR_BADXDR;
3457		goto nfsmout;
3458	}
3459	idlen = i;
3460	if (nd->nd_flag & ND_GSS)
3461		i += nd->nd_princlen;
3462	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3463	    M_ZERO);
3464	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3465	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3466	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3467	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
3468	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
3469	    M_WAITOK | M_ZERO);
3470	clp->lc_req.nr_cred = NULL;
3471	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3472	clp->lc_idlen = idlen;
3473	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3474	if (error)
3475		goto nfsmout;
3476	if (nd->nd_flag & ND_GSS) {
3477		clp->lc_flags = LCL_GSS;
3478		if (nd->nd_flag & ND_GSSINTEGRITY)
3479			clp->lc_flags |= LCL_GSSINTEGRITY;
3480		else if (nd->nd_flag & ND_GSSPRIVACY)
3481			clp->lc_flags |= LCL_GSSPRIVACY;
3482	} else {
3483		clp->lc_flags = 0;
3484	}
3485	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
3486		clp->lc_flags |= LCL_NAME;
3487		clp->lc_namelen = nd->nd_princlen;
3488		clp->lc_name = &clp->lc_id[idlen];
3489		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3490	} else {
3491		clp->lc_uid = nd->nd_cred->cr_uid;
3492		clp->lc_gid = nd->nd_cred->cr_gid;
3493	}
3494	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3495	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
3496	error = nfsrv_getclientipaddr(nd, clp);
3497	if (error)
3498		goto nfsmout;
3499	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3500	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
3501
3502	/*
3503	 * nfsrv_setclient() does the actual work of adding it to the
3504	 * client list. If there is no error, the structure has been
3505	 * linked into the client list and clp should no longer be used
3506	 * here. When an error is returned, it has not been linked in,
3507	 * so it should be free'd.
3508	 */
3509	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3510	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
3511		/*
3512		 * 8 is the maximum length of the port# string.
3513		 */
3514		addrbuf = malloc(INET6_ADDRSTRLEN + 8, M_TEMP, M_WAITOK);
3515		switch (clp->lc_req.nr_nam->sa_family) {
3516#ifdef INET
3517		case AF_INET:
3518			if (clp->lc_flags & LCL_TCPCALLBACK)
3519				(void) nfsm_strtom(nd, "tcp", 3);
3520			else
3521				(void) nfsm_strtom(nd, "udp", 3);
3522			rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
3523			ucp = (u_char *)&rin->sin_addr.s_addr;
3524			ucp2 = (u_char *)&rin->sin_port;
3525			sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
3526			    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
3527			    ucp2[0] & 0xff, ucp2[1] & 0xff);
3528			break;
3529#endif
3530#ifdef INET6
3531		case AF_INET6:
3532			if (clp->lc_flags & LCL_TCPCALLBACK)
3533				(void) nfsm_strtom(nd, "tcp6", 4);
3534			else
3535				(void) nfsm_strtom(nd, "udp6", 4);
3536			rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
3537			ucp = inet_ntop(AF_INET6, &rin6->sin6_addr, addrbuf,
3538			    INET6_ADDRSTRLEN);
3539			if (ucp != NULL)
3540				i = strlen(ucp);
3541			else
3542				i = 0;
3543			ucp2 = (u_char *)&rin6->sin6_port;
3544			sprintf(&addrbuf[i], ".%d.%d", ucp2[0] & 0xff,
3545			    ucp2[1] & 0xff);
3546			break;
3547#endif
3548		}
3549		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
3550		free(addrbuf, M_TEMP);
3551	}
3552	if (clp) {
3553		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3554		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3555		free(clp->lc_stateid, M_NFSDCLIENT);
3556		free(clp, M_NFSDCLIENT);
3557	}
3558	if (!nd->nd_repstat) {
3559		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
3560		*tl++ = clientid.lval[0];
3561		*tl++ = clientid.lval[1];
3562		*tl++ = confirm.lval[0];
3563		*tl = confirm.lval[1];
3564	}
3565
3566out:
3567	NFSEXITCODE2(0, nd);
3568	return (0);
3569nfsmout:
3570	if (clp) {
3571		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3572		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3573		free(clp->lc_stateid, M_NFSDCLIENT);
3574		free(clp, M_NFSDCLIENT);
3575	}
3576	NFSEXITCODE2(error, nd);
3577	return (error);
3578}
3579
3580/*
3581 * nfsv4 set client id confirm service
3582 */
3583APPLESTATIC int
3584nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
3585    __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
3586    __unused struct nfsexstuff *exp)
3587{
3588	u_int32_t *tl;
3589	int error = 0;
3590	nfsquad_t clientid, confirm;
3591
3592	if ((nd->nd_flag & ND_NFSV41) != 0) {
3593		nd->nd_repstat = NFSERR_NOTSUPP;
3594		goto nfsmout;
3595	}
3596	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3597		nd->nd_repstat = NFSERR_WRONGSEC;
3598		goto nfsmout;
3599	}
3600	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
3601	clientid.lval[0] = *tl++;
3602	clientid.lval[1] = *tl++;
3603	confirm.lval[0] = *tl++;
3604	confirm.lval[1] = *tl;
3605
3606	/*
3607	 * nfsrv_getclient() searches the client list for a match and
3608	 * returns the appropriate NFSERR status.
3609	 */
3610	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
3611	    NULL, NULL, confirm, 0, nd, p);
3612nfsmout:
3613	NFSEXITCODE2(error, nd);
3614	return (error);
3615}
3616
3617/*
3618 * nfsv4 verify service
3619 */
3620APPLESTATIC int
3621nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
3622    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3623{
3624	int error = 0, ret, fhsize = NFSX_MYFH;
3625	struct nfsvattr nva;
3626	struct statfs sf;
3627	struct nfsfsinfo fs;
3628	fhandle_t fh;
3629
3630	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
3631	if (!nd->nd_repstat)
3632		nd->nd_repstat = nfsvno_statfs(vp, &sf);
3633	if (!nd->nd_repstat)
3634		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3635	if (!nd->nd_repstat) {
3636		nfsvno_getfs(&fs, isdgram);
3637		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
3638		    &sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
3639		if (!error) {
3640			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
3641				if (ret == 0)
3642					nd->nd_repstat = NFSERR_SAME;
3643				else if (ret != NFSERR_NOTSAME)
3644					nd->nd_repstat = ret;
3645			} else if (ret)
3646				nd->nd_repstat = ret;
3647		}
3648	}
3649	vput(vp);
3650	NFSEXITCODE2(error, nd);
3651	return (error);
3652}
3653
3654/*
3655 * nfs openattr rpc
3656 */
3657APPLESTATIC int
3658nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
3659    vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
3660    __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3661{
3662	u_int32_t *tl;
3663	int error = 0, createdir;
3664
3665	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3666	createdir = fxdr_unsigned(int, *tl);
3667	nd->nd_repstat = NFSERR_NOTSUPP;
3668nfsmout:
3669	vrele(dp);
3670	NFSEXITCODE2(error, nd);
3671	return (error);
3672}
3673
3674/*
3675 * nfsv4 release lock owner service
3676 */
3677APPLESTATIC int
3678nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
3679    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3680{
3681	u_int32_t *tl;
3682	struct nfsstate *stp = NULL;
3683	int error = 0, len;
3684	nfsquad_t clientid;
3685
3686	if ((nd->nd_flag & ND_NFSV41) != 0) {
3687		nd->nd_repstat = NFSERR_NOTSUPP;
3688		goto nfsmout;
3689	}
3690	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3691		nd->nd_repstat = NFSERR_WRONGSEC;
3692		goto nfsmout;
3693	}
3694	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3695	len = fxdr_unsigned(int, *(tl + 2));
3696	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
3697		nd->nd_repstat = NFSERR_BADXDR;
3698		goto nfsmout;
3699	}
3700	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len,
3701	    M_NFSDSTATE, M_WAITOK);
3702	stp->ls_ownerlen = len;
3703	stp->ls_op = NULL;
3704	stp->ls_flags = NFSLCK_RELEASE;
3705	stp->ls_uid = nd->nd_cred->cr_uid;
3706	clientid.lval[0] = *tl++;
3707	clientid.lval[1] = *tl;
3708	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3709		if ((nd->nd_flag & ND_NFSV41) != 0)
3710			clientid.qval = nd->nd_clientid.qval;
3711		else if (nd->nd_clientid.qval != clientid.qval)
3712			printf("EEK14 multiple clids\n");
3713	} else {
3714		if ((nd->nd_flag & ND_NFSV41) != 0)
3715			printf("EEK! no clientid from session\n");
3716		nd->nd_flag |= ND_IMPLIEDCLID;
3717		nd->nd_clientid.qval = clientid.qval;
3718	}
3719	error = nfsrv_mtostr(nd, stp->ls_owner, len);
3720	if (error)
3721		goto nfsmout;
3722	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
3723	FREE((caddr_t)stp, M_NFSDSTATE);
3724
3725	NFSEXITCODE2(0, nd);
3726	return (0);
3727nfsmout:
3728	if (stp)
3729		free((caddr_t)stp, M_NFSDSTATE);
3730	NFSEXITCODE2(error, nd);
3731	return (error);
3732}
3733
3734/*
3735 * nfsv4 exchange_id service
3736 */
3737APPLESTATIC int
3738nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
3739    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3740{
3741	uint32_t *tl;
3742	int error = 0, i, idlen;
3743	struct nfsclient *clp = NULL;
3744	nfsquad_t clientid, confirm;
3745	uint8_t *verf;
3746	uint32_t sp4type, v41flags;
3747	uint64_t owner_minor;
3748	struct timespec verstime;
3749#ifdef INET
3750	struct sockaddr_in *sin, *rin;
3751#endif
3752#ifdef INET6
3753	struct sockaddr_in6 *sin6, *rin6;
3754#endif
3755
3756	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3757		nd->nd_repstat = NFSERR_WRONGSEC;
3758		goto nfsmout;
3759	}
3760	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3761	verf = (uint8_t *)tl;
3762	tl += (NFSX_VERF / NFSX_UNSIGNED);
3763	i = fxdr_unsigned(int, *tl);
3764	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3765		nd->nd_repstat = NFSERR_BADXDR;
3766		goto nfsmout;
3767	}
3768	idlen = i;
3769	if (nd->nd_flag & ND_GSS)
3770		i += nd->nd_princlen;
3771	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3772	    M_ZERO);
3773	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3774	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3775	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3776	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
3777	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
3778	    M_WAITOK | M_ZERO);
3779	switch (nd->nd_nam->sa_family) {
3780#ifdef INET
3781	case AF_INET:
3782		rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
3783		sin = (struct sockaddr_in *)nd->nd_nam;
3784		rin->sin_family = AF_INET;
3785		rin->sin_len = sizeof(struct sockaddr_in);
3786		rin->sin_port = 0;
3787		rin->sin_addr.s_addr = sin->sin_addr.s_addr;
3788		break;
3789#endif
3790#ifdef INET6
3791	case AF_INET6:
3792		rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
3793		sin6 = (struct sockaddr_in6 *)nd->nd_nam;
3794		rin6->sin6_family = AF_INET6;
3795		rin6->sin6_len = sizeof(struct sockaddr_in6);
3796		rin6->sin6_port = 0;
3797		rin6->sin6_addr = sin6->sin6_addr;
3798		break;
3799#endif
3800	}
3801	clp->lc_req.nr_cred = NULL;
3802	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3803	clp->lc_idlen = idlen;
3804	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3805	if (error != 0)
3806		goto nfsmout;
3807	if ((nd->nd_flag & ND_GSS) != 0) {
3808		clp->lc_flags = LCL_GSS | LCL_NFSV41;
3809		if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
3810			clp->lc_flags |= LCL_GSSINTEGRITY;
3811		else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
3812			clp->lc_flags |= LCL_GSSPRIVACY;
3813	} else
3814		clp->lc_flags = LCL_NFSV41;
3815	if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
3816		clp->lc_flags |= LCL_NAME;
3817		clp->lc_namelen = nd->nd_princlen;
3818		clp->lc_name = &clp->lc_id[idlen];
3819		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3820	} else {
3821		clp->lc_uid = nd->nd_cred->cr_uid;
3822		clp->lc_gid = nd->nd_cred->cr_gid;
3823	}
3824	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3825	v41flags = fxdr_unsigned(uint32_t, *tl++);
3826	if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
3827	    NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
3828	    NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
3829		nd->nd_repstat = NFSERR_INVAL;
3830		goto nfsmout;
3831	}
3832	if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
3833		confirm.lval[1] = 1;
3834	else
3835		confirm.lval[1] = 0;
3836	v41flags = NFSV4EXCH_USENONPNFS;
3837	sp4type = fxdr_unsigned(uint32_t, *tl);
3838	if (sp4type != NFSV4EXCH_SP4NONE) {
3839		nd->nd_repstat = NFSERR_NOTSUPP;
3840		goto nfsmout;
3841	}
3842
3843	/*
3844	 * nfsrv_setclient() does the actual work of adding it to the
3845	 * client list. If there is no error, the structure has been
3846	 * linked into the client list and clp should no longer be used
3847	 * here. When an error is returned, it has not been linked in,
3848	 * so it should be free'd.
3849	 */
3850	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3851	if (clp != NULL) {
3852		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3853		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3854		free(clp->lc_stateid, M_NFSDCLIENT);
3855		free(clp, M_NFSDCLIENT);
3856	}
3857	if (nd->nd_repstat == 0) {
3858		if (confirm.lval[1] != 0)
3859			v41flags |= NFSV4EXCH_CONFIRMEDR;
3860		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
3861		*tl++ = clientid.lval[0];			/* ClientID */
3862		*tl++ = clientid.lval[1];
3863		*tl++ = txdr_unsigned(confirm.lval[0]);		/* SequenceID */
3864		*tl++ = txdr_unsigned(v41flags);		/* Exch flags */
3865		*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);	/* No SSV */
3866		owner_minor = 0;				/* Owner */
3867		txdr_hyper(owner_minor, tl);			/* Minor */
3868		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
3869		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */
3870		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
3871		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Scope */
3872		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3873		*tl = txdr_unsigned(1);
3874		(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
3875		(void)nfsm_strtom(nd, version, strlen(version));
3876		NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
3877		verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
3878		verstime.tv_nsec = 0;
3879		txdr_nfsv4time(&verstime, tl);
3880	}
3881	NFSEXITCODE2(0, nd);
3882	return (0);
3883nfsmout:
3884	if (clp != NULL) {
3885		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3886		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3887		free(clp->lc_stateid, M_NFSDCLIENT);
3888		free(clp, M_NFSDCLIENT);
3889	}
3890	NFSEXITCODE2(error, nd);
3891	return (error);
3892}
3893
3894/*
3895 * nfsv4 create session service
3896 */
3897APPLESTATIC int
3898nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
3899    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3900{
3901	uint32_t *tl;
3902	int error = 0;
3903	nfsquad_t clientid, confirm;
3904	struct nfsdsession *sep = NULL;
3905	uint32_t rdmacnt;
3906
3907	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3908		nd->nd_repstat = NFSERR_WRONGSEC;
3909		goto nfsmout;
3910	}
3911	sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
3912	    M_NFSDSESSION, M_WAITOK | M_ZERO);
3913	sep->sess_refcnt = 1;
3914	mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
3915	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
3916	clientid.lval[0] = *tl++;
3917	clientid.lval[1] = *tl++;
3918	confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
3919	sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
3920	/* Persistent sessions and RDMA are not supported. */
3921	sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
3922
3923	/* Fore channel attributes. */
3924	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
3925	tl++;					/* Header pad always 0. */
3926	sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
3927	sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
3928	sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
3929	sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
3930	sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
3931	if (sep->sess_maxslots > NFSV4_SLOTS)
3932		sep->sess_maxslots = NFSV4_SLOTS;
3933	rdmacnt = fxdr_unsigned(uint32_t, *tl);
3934	if (rdmacnt > 1) {
3935		nd->nd_repstat = NFSERR_BADXDR;
3936		goto nfsmout;
3937	} else if (rdmacnt == 1)
3938		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3939
3940	/* Back channel attributes. */
3941	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
3942	tl++;					/* Header pad always 0. */
3943	sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
3944	sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
3945	sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
3946	sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
3947	sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
3948	rdmacnt = fxdr_unsigned(uint32_t, *tl);
3949	if (rdmacnt > 1) {
3950		nd->nd_repstat = NFSERR_BADXDR;
3951		goto nfsmout;
3952	} else if (rdmacnt == 1)
3953		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3954
3955	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3956	sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
3957
3958	/*
3959	 * nfsrv_getclient() searches the client list for a match and
3960	 * returns the appropriate NFSERR status.
3961	 */
3962	nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
3963	    NULL, sep, confirm, sep->sess_cbprogram, nd, p);
3964	if (nd->nd_repstat == 0) {
3965		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
3966		NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
3967		NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
3968		*tl++ = txdr_unsigned(confirm.lval[0]);	/* sequenceid */
3969		*tl++ = txdr_unsigned(sep->sess_crflags);
3970
3971		/* Fore channel attributes. */
3972		*tl++ = 0;
3973		*tl++ = txdr_unsigned(sep->sess_maxreq);
3974		*tl++ = txdr_unsigned(sep->sess_maxresp);
3975		*tl++ = txdr_unsigned(sep->sess_maxrespcached);
3976		*tl++ = txdr_unsigned(sep->sess_maxops);
3977		*tl++ = txdr_unsigned(sep->sess_maxslots);
3978		*tl++ = txdr_unsigned(1);
3979		*tl++ = txdr_unsigned(0);			/* No RDMA. */
3980
3981		/* Back channel attributes. */
3982		*tl++ = 0;
3983		*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
3984		*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
3985		*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
3986		*tl++ = txdr_unsigned(sep->sess_cbmaxops);
3987		*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
3988		*tl++ = txdr_unsigned(1);
3989		*tl = txdr_unsigned(0);			/* No RDMA. */
3990	}
3991nfsmout:
3992	if (nd->nd_repstat != 0 && sep != NULL)
3993		free(sep, M_NFSDSESSION);
3994	NFSEXITCODE2(error, nd);
3995	return (error);
3996}
3997
3998/*
3999 * nfsv4 sequence service
4000 */
4001APPLESTATIC int
4002nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
4003    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4004{
4005	uint32_t *tl;
4006	uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
4007	int cache_this, error = 0;
4008
4009	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4010		nd->nd_repstat = NFSERR_WRONGSEC;
4011		goto nfsmout;
4012	}
4013	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
4014	NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
4015	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
4016	sequenceid = fxdr_unsigned(uint32_t, *tl++);
4017	nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
4018	highest_slotid = fxdr_unsigned(uint32_t, *tl++);
4019	if (*tl == newnfs_true)
4020		cache_this = 1;
4021	else
4022		cache_this = 0;
4023	nd->nd_flag |= ND_HASSEQUENCE;
4024	nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
4025	    &target_highest_slotid, cache_this, &sflags, p);
4026	if (nd->nd_repstat == 0) {
4027		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4028		NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
4029		NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
4030		*tl++ = txdr_unsigned(sequenceid);
4031		*tl++ = txdr_unsigned(nd->nd_slotid);
4032		*tl++ = txdr_unsigned(highest_slotid);
4033		*tl++ = txdr_unsigned(target_highest_slotid);
4034		*tl = txdr_unsigned(sflags);
4035	}
4036nfsmout:
4037	NFSEXITCODE2(error, nd);
4038	return (error);
4039}
4040
4041/*
4042 * nfsv4 reclaim complete service
4043 */
4044APPLESTATIC int
4045nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
4046    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4047{
4048	uint32_t *tl;
4049	int error = 0, onefs;
4050
4051	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4052		nd->nd_repstat = NFSERR_WRONGSEC;
4053		goto nfsmout;
4054	}
4055	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4056	/*
4057	 * I believe that a ReclaimComplete with rca_one_fs == TRUE is only
4058	 * to be used after a file system has been transferred to a different
4059	 * file server.  However, RFC5661 is somewhat vague w.r.t. this and
4060	 * the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
4061	 * == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
4062	 * Therefore, just ignore the rca_one_fs == TRUE operation and return
4063	 * NFS_OK without doing anything.
4064	 */
4065	onefs = 0;
4066	if (*tl == newnfs_true)
4067		onefs = 1;
4068	nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
4069nfsmout:
4070	NFSEXITCODE2(error, nd);
4071	return (error);
4072}
4073
4074/*
4075 * nfsv4 destroy clientid service
4076 */
4077APPLESTATIC int
4078nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
4079    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4080{
4081	uint32_t *tl;
4082	nfsquad_t clientid;
4083	int error = 0;
4084
4085	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4086		nd->nd_repstat = NFSERR_WRONGSEC;
4087		goto nfsmout;
4088	}
4089	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4090	clientid.lval[0] = *tl++;
4091	clientid.lval[1] = *tl;
4092	nd->nd_repstat = nfsrv_destroyclient(clientid, p);
4093nfsmout:
4094	NFSEXITCODE2(error, nd);
4095	return (error);
4096}
4097
4098/*
4099 * nfsv4 bind connection to session service
4100 */
4101APPLESTATIC int
4102nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
4103    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4104{
4105	uint32_t *tl;
4106	uint8_t sessid[NFSX_V4SESSIONID];
4107	int error = 0, foreaft;
4108
4109	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4110		nd->nd_repstat = NFSERR_WRONGSEC;
4111		goto nfsmout;
4112	}
4113	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
4114	NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
4115	tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
4116	foreaft = fxdr_unsigned(int, *tl++);
4117	if (*tl == newnfs_true) {
4118		/* RDMA is not supported. */
4119		nd->nd_repstat = NFSERR_NOTSUPP;
4120		goto nfsmout;
4121	}
4122
4123	nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
4124	if (nd->nd_repstat == 0) {
4125		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
4126		    NFSX_UNSIGNED);
4127		NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
4128		tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
4129		*tl++ = txdr_unsigned(foreaft);
4130		*tl = newnfs_false;
4131	}
4132nfsmout:
4133	NFSEXITCODE2(error, nd);
4134	return (error);
4135}
4136
4137/*
4138 * nfsv4 destroy session service
4139 */
4140APPLESTATIC int
4141nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
4142    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4143{
4144	uint8_t *cp, sessid[NFSX_V4SESSIONID];
4145	int error = 0;
4146
4147	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4148		nd->nd_repstat = NFSERR_WRONGSEC;
4149		goto nfsmout;
4150	}
4151	NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
4152	NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
4153	nd->nd_repstat = nfsrv_destroysession(nd, sessid);
4154nfsmout:
4155	NFSEXITCODE2(error, nd);
4156	return (error);
4157}
4158
4159/*
4160 * nfsv4 free stateid service
4161 */
4162APPLESTATIC int
4163nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
4164    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4165{
4166	uint32_t *tl;
4167	nfsv4stateid_t stateid;
4168	int error = 0;
4169
4170	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4171		nd->nd_repstat = NFSERR_WRONGSEC;
4172		goto nfsmout;
4173	}
4174	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4175	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4176	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4177	nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
4178nfsmout:
4179	NFSEXITCODE2(error, nd);
4180	return (error);
4181}
4182
4183/*
4184 * nfsv4 test stateid service
4185 */
4186APPLESTATIC int
4187nfsrvd_teststateid(struct nfsrv_descript *nd, __unused int isdgram,
4188    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4189{
4190	uint32_t *tl;
4191	nfsv4stateid_t *stateidp = NULL, *tstateidp;
4192	int cnt, error = 0, i, ret;
4193
4194	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4195		nd->nd_repstat = NFSERR_WRONGSEC;
4196		goto nfsmout;
4197	}
4198	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4199	cnt = fxdr_unsigned(int, *tl);
4200	if (cnt <= 0 || cnt > 1024) {
4201		nd->nd_repstat = NFSERR_BADXDR;
4202		goto nfsmout;
4203	}
4204	stateidp = mallocarray(cnt, sizeof(nfsv4stateid_t), M_TEMP, M_WAITOK);
4205	tstateidp = stateidp;
4206	for (i = 0; i < cnt; i++) {
4207		NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4208		tstateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
4209		NFSBCOPY(tl, tstateidp->other, NFSX_STATEIDOTHER);
4210		tstateidp++;
4211	}
4212	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4213	*tl = txdr_unsigned(cnt);
4214	tstateidp = stateidp;
4215	for (i = 0; i < cnt; i++) {
4216		ret = nfsrv_teststateid(nd, tstateidp, p);
4217		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4218		*tl = txdr_unsigned(ret);
4219		tstateidp++;
4220	}
4221nfsmout:
4222	free(stateidp, M_TEMP);
4223	NFSEXITCODE2(error, nd);
4224	return (error);
4225}
4226
4227/*
4228 * nfsv4 service not supported
4229 */
4230APPLESTATIC int
4231nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
4232    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4233{
4234
4235	nd->nd_repstat = NFSERR_NOTSUPP;
4236	NFSEXITCODE2(0, nd);
4237	return (0);
4238}
4239
4240