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