1/*	$NetBSD: nfs_nfsdserv.c,v 1.5 2024/02/05 21:46:06 andvar Exp $	*/
2/*-
3 * Copyright (c) 1989, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Rick Macklem at The University of Guelph.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 */
34
35#include <sys/cdefs.h>
36/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdserv.c 299514 2016-05-12 05:03:12Z cem "); */
37__RCSID("$NetBSD: nfs_nfsdserv.c,v 1.5 2024/02/05 21:46:06 andvar Exp $");
38
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/common/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 attributes 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			panic("nfsrv_write mbuf");
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	struct statfs sfs;
2041	u_quad_t tval;
2042
2043	if (nd->nd_repstat) {
2044		nfsrv_postopattr(nd, getret, &at);
2045		goto out;
2046	}
2047	sf = &sfs;
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	NFSEXITCODE2(0, nd);
2084	return (0);
2085}
2086
2087/*
2088 * nfs fsinfo service
2089 */
2090APPLESTATIC int
2091nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2092    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2093{
2094	u_int32_t *tl;
2095	struct nfsfsinfo fs;
2096	int getret = 1;
2097	struct nfsvattr at;
2098
2099	if (nd->nd_repstat) {
2100		nfsrv_postopattr(nd, getret, &at);
2101		goto out;
2102	}
2103	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2104	nfsvno_getfs(&fs, isdgram);
2105	vput(vp);
2106	nfsrv_postopattr(nd, getret, &at);
2107	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
2108	*tl++ = txdr_unsigned(fs.fs_rtmax);
2109	*tl++ = txdr_unsigned(fs.fs_rtpref);
2110	*tl++ = txdr_unsigned(fs.fs_rtmult);
2111	*tl++ = txdr_unsigned(fs.fs_wtmax);
2112	*tl++ = txdr_unsigned(fs.fs_wtpref);
2113	*tl++ = txdr_unsigned(fs.fs_wtmult);
2114	*tl++ = txdr_unsigned(fs.fs_dtpref);
2115	txdr_hyper(fs.fs_maxfilesize, tl);
2116	tl += 2;
2117	txdr_nfsv3time(&fs.fs_timedelta, tl);
2118	tl += 2;
2119	*tl = txdr_unsigned(fs.fs_properties);
2120
2121out:
2122	NFSEXITCODE2(0, nd);
2123	return (0);
2124}
2125
2126/*
2127 * nfs pathconf service
2128 */
2129APPLESTATIC int
2130nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2131    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2132{
2133	struct nfsv3_pathconf *pc;
2134	int getret = 1;
2135	register_t linkmax, namemax, chownres, notrunc;
2136	struct nfsvattr at;
2137
2138	if (nd->nd_repstat) {
2139		nfsrv_postopattr(nd, getret, &at);
2140		goto out;
2141	}
2142	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
2143	    nd->nd_cred, p);
2144	if (!nd->nd_repstat)
2145		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
2146		    nd->nd_cred, p);
2147	if (!nd->nd_repstat)
2148		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
2149		    &chownres, nd->nd_cred, p);
2150	if (!nd->nd_repstat)
2151		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
2152		    nd->nd_cred, p);
2153	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2154	vput(vp);
2155	nfsrv_postopattr(nd, getret, &at);
2156	if (!nd->nd_repstat) {
2157		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
2158		pc->pc_linkmax = txdr_unsigned(linkmax);
2159		pc->pc_namemax = txdr_unsigned(namemax);
2160		pc->pc_notrunc = txdr_unsigned(notrunc);
2161		pc->pc_chownrestricted = txdr_unsigned(chownres);
2162
2163		/*
2164		 * These should probably be supported by VOP_PATHCONF(), but
2165		 * until msdosfs is exportable (why would you want to?), the
2166		 * Unix defaults should be ok.
2167		 */
2168		pc->pc_caseinsensitive = newnfs_false;
2169		pc->pc_casepreserving = newnfs_true;
2170	}
2171
2172out:
2173	NFSEXITCODE2(0, nd);
2174	return (0);
2175}
2176
2177/*
2178 * nfsv4 lock service
2179 */
2180APPLESTATIC int
2181nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2182    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2183{
2184	u_int32_t *tl;
2185	int i;
2186	struct nfsstate *stp = NULL;
2187	struct nfslock *lop;
2188	struct nfslockconflict cf;
2189	int error = 0;
2190	u_short flags = NFSLCK_LOCK, lflags;
2191	u_int64_t offset, len;
2192	nfsv4stateid_t stateid;
2193	nfsquad_t clientid;
2194
2195	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2196	i = fxdr_unsigned(int, *tl++);
2197	switch (i) {
2198	case NFSV4LOCKT_READW:
2199		flags |= NFSLCK_BLOCKING;
2200	case NFSV4LOCKT_READ:
2201		lflags = NFSLCK_READ;
2202		break;
2203	case NFSV4LOCKT_WRITEW:
2204		flags |= NFSLCK_BLOCKING;
2205	case NFSV4LOCKT_WRITE:
2206		lflags = NFSLCK_WRITE;
2207		break;
2208	default:
2209		nd->nd_repstat = NFSERR_BADXDR;
2210		goto nfsmout;
2211	}
2212	if (*tl++ == newnfs_true)
2213		flags |= NFSLCK_RECLAIM;
2214	offset = fxdr_hyper(tl);
2215	tl += 2;
2216	len = fxdr_hyper(tl);
2217	tl += 2;
2218	if (*tl == newnfs_true)
2219		flags |= NFSLCK_OPENTOLOCK;
2220	if (flags & NFSLCK_OPENTOLOCK) {
2221		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
2222		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
2223		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2224			nd->nd_repstat = NFSERR_BADXDR;
2225			goto nfsmout;
2226		}
2227		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2228			M_NFSDSTATE, M_WAITOK);
2229		stp->ls_ownerlen = i;
2230		stp->ls_op = nd->nd_rp;
2231		stp->ls_seq = fxdr_unsigned(int, *tl++);
2232		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2233		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2234			NFSX_STATEIDOTHER);
2235		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2236		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
2237		clientid.lval[0] = *tl++;
2238		clientid.lval[1] = *tl++;
2239		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2240			if ((nd->nd_flag & ND_NFSV41) != 0)
2241				clientid.qval = nd->nd_clientid.qval;
2242			else if (nd->nd_clientid.qval != clientid.qval)
2243				printf("EEK3 multiple clids\n");
2244		} else {
2245			if ((nd->nd_flag & ND_NFSV41) != 0)
2246				printf("EEK! no clientid from session\n");
2247			nd->nd_flag |= ND_IMPLIEDCLID;
2248			nd->nd_clientid.qval = clientid.qval;
2249		}
2250		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2251		if (error)
2252			goto nfsmout;
2253	} else {
2254		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2255		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2256			M_NFSDSTATE, M_WAITOK);
2257		stp->ls_ownerlen = 0;
2258		stp->ls_op = nd->nd_rp;
2259		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2260		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2261			NFSX_STATEIDOTHER);
2262		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2263		stp->ls_seq = fxdr_unsigned(int, *tl);
2264		clientid.lval[0] = stp->ls_stateid.other[0];
2265		clientid.lval[1] = stp->ls_stateid.other[1];
2266		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2267			if ((nd->nd_flag & ND_NFSV41) != 0)
2268				clientid.qval = nd->nd_clientid.qval;
2269			else if (nd->nd_clientid.qval != clientid.qval)
2270				printf("EEK4 multiple clids\n");
2271		} else {
2272			if ((nd->nd_flag & ND_NFSV41) != 0)
2273				printf("EEK! no clientid from session\n");
2274			nd->nd_flag |= ND_IMPLIEDCLID;
2275			nd->nd_clientid.qval = clientid.qval;
2276		}
2277	}
2278	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2279		M_NFSDLOCK, M_WAITOK);
2280	lop->lo_first = offset;
2281	if (len == NFS64BITSSET) {
2282		lop->lo_end = NFS64BITSSET;
2283	} else {
2284		lop->lo_end = offset + len;
2285		if (lop->lo_end <= lop->lo_first)
2286			nd->nd_repstat = NFSERR_INVAL;
2287	}
2288	lop->lo_flags = lflags;
2289	stp->ls_flags = flags;
2290	stp->ls_uid = nd->nd_cred->cr_uid;
2291
2292	/*
2293	 * Do basic access checking.
2294	 */
2295	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2296	    if (vnode_vtype(vp) == VDIR)
2297		nd->nd_repstat = NFSERR_ISDIR;
2298	    else
2299		nd->nd_repstat = NFSERR_INVAL;
2300	}
2301	if (!nd->nd_repstat) {
2302	    if (lflags & NFSLCK_WRITE) {
2303		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
2304		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2305		    NFSACCCHK_VPISLOCKED, NULL);
2306	    } else {
2307		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
2308		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2309		    NFSACCCHK_VPISLOCKED, NULL);
2310		if (nd->nd_repstat)
2311		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2312			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2313			NFSACCCHK_VPISLOCKED, NULL);
2314	    }
2315	}
2316
2317	/*
2318	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
2319	 * seqid# gets updated. nfsrv_lockctrl() will return the value
2320	 * of nd_repstat, if it gets that far.
2321	 */
2322	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2323		&stateid, exp, nd, p);
2324	if (lop)
2325		FREE((caddr_t)lop, M_NFSDLOCK);
2326	if (stp)
2327		FREE((caddr_t)stp, M_NFSDSTATE);
2328	if (!nd->nd_repstat) {
2329		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2330		*tl++ = txdr_unsigned(stateid.seqid);
2331		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2332	} else if (nd->nd_repstat == NFSERR_DENIED) {
2333		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2334		txdr_hyper(cf.cl_first, tl);
2335		tl += 2;
2336		if (cf.cl_end == NFS64BITSSET)
2337			len = NFS64BITSSET;
2338		else
2339			len = cf.cl_end - cf.cl_first;
2340		txdr_hyper(len, tl);
2341		tl += 2;
2342		if (cf.cl_flags == NFSLCK_WRITE)
2343			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2344		else
2345			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2346		*tl++ = stateid.other[0];
2347		*tl = stateid.other[1];
2348		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2349	}
2350	vput(vp);
2351	NFSEXITCODE2(0, nd);
2352	return (0);
2353nfsmout:
2354	vput(vp);
2355	if (stp)
2356		free((caddr_t)stp, M_NFSDSTATE);
2357	NFSEXITCODE2(error, nd);
2358	return (error);
2359}
2360
2361/*
2362 * nfsv4 lock test service
2363 */
2364APPLESTATIC int
2365nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2366    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2367{
2368	u_int32_t *tl;
2369	int i;
2370	struct nfsstate *stp = NULL;
2371	struct nfslock lo, *lop = &lo;
2372	struct nfslockconflict cf;
2373	int error = 0;
2374	nfsv4stateid_t stateid;
2375	nfsquad_t clientid;
2376	u_int64_t len;
2377
2378	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
2379	i = fxdr_unsigned(int, *(tl + 7));
2380	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2381		nd->nd_repstat = NFSERR_BADXDR;
2382		goto nfsmout;
2383	}
2384	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2385	    M_NFSDSTATE, M_WAITOK);
2386	stp->ls_ownerlen = i;
2387	stp->ls_op = NULL;
2388	stp->ls_flags = NFSLCK_TEST;
2389	stp->ls_uid = nd->nd_cred->cr_uid;
2390	i = fxdr_unsigned(int, *tl++);
2391	switch (i) {
2392	case NFSV4LOCKT_READW:
2393		stp->ls_flags |= NFSLCK_BLOCKING;
2394	case NFSV4LOCKT_READ:
2395		lo.lo_flags = NFSLCK_READ;
2396		break;
2397	case NFSV4LOCKT_WRITEW:
2398		stp->ls_flags |= NFSLCK_BLOCKING;
2399	case NFSV4LOCKT_WRITE:
2400		lo.lo_flags = NFSLCK_WRITE;
2401		break;
2402	default:
2403		nd->nd_repstat = NFSERR_BADXDR;
2404		goto nfsmout;
2405	}
2406	lo.lo_first = fxdr_hyper(tl);
2407	tl += 2;
2408	len = fxdr_hyper(tl);
2409	if (len == NFS64BITSSET) {
2410		lo.lo_end = NFS64BITSSET;
2411	} else {
2412		lo.lo_end = lo.lo_first + len;
2413		if (lo.lo_end <= lo.lo_first)
2414			nd->nd_repstat = NFSERR_INVAL;
2415	}
2416	tl += 2;
2417	clientid.lval[0] = *tl++;
2418	clientid.lval[1] = *tl;
2419	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2420		if ((nd->nd_flag & ND_NFSV41) != 0)
2421			clientid.qval = nd->nd_clientid.qval;
2422		else if (nd->nd_clientid.qval != clientid.qval)
2423			printf("EEK5 multiple clids\n");
2424	} else {
2425		if ((nd->nd_flag & ND_NFSV41) != 0)
2426			printf("EEK! no clientid from session\n");
2427		nd->nd_flag |= ND_IMPLIEDCLID;
2428		nd->nd_clientid.qval = clientid.qval;
2429	}
2430	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2431	if (error)
2432		goto nfsmout;
2433	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2434	    if (vnode_vtype(vp) == VDIR)
2435		nd->nd_repstat = NFSERR_ISDIR;
2436	    else
2437		nd->nd_repstat = NFSERR_INVAL;
2438	}
2439	if (!nd->nd_repstat)
2440	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2441	    &stateid, exp, nd, p);
2442	if (nd->nd_repstat) {
2443	    if (nd->nd_repstat == NFSERR_DENIED) {
2444		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2445		txdr_hyper(cf.cl_first, tl);
2446		tl += 2;
2447		if (cf.cl_end == NFS64BITSSET)
2448			len = NFS64BITSSET;
2449		else
2450			len = cf.cl_end - cf.cl_first;
2451		txdr_hyper(len, tl);
2452		tl += 2;
2453		if (cf.cl_flags == NFSLCK_WRITE)
2454			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2455		else
2456			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2457		*tl++ = stp->ls_stateid.other[0];
2458		*tl = stp->ls_stateid.other[1];
2459		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2460	    }
2461	}
2462	vput(vp);
2463	if (stp)
2464		FREE((caddr_t)stp, M_NFSDSTATE);
2465	NFSEXITCODE2(0, nd);
2466	return (0);
2467nfsmout:
2468	vput(vp);
2469	if (stp)
2470		free((caddr_t)stp, M_NFSDSTATE);
2471	NFSEXITCODE2(error, nd);
2472	return (error);
2473}
2474
2475/*
2476 * nfsv4 unlock service
2477 */
2478APPLESTATIC int
2479nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2480    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2481{
2482	u_int32_t *tl;
2483	int i;
2484	struct nfsstate *stp;
2485	struct nfslock *lop;
2486	int error = 0;
2487	nfsv4stateid_t stateid;
2488	nfsquad_t clientid;
2489	u_int64_t len;
2490
2491	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2492	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2493	    M_NFSDSTATE, M_WAITOK);
2494	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2495	    M_NFSDLOCK, M_WAITOK);
2496	stp->ls_flags = NFSLCK_UNLOCK;
2497	lop->lo_flags = NFSLCK_UNLOCK;
2498	stp->ls_op = nd->nd_rp;
2499	i = fxdr_unsigned(int, *tl++);
2500	switch (i) {
2501	case NFSV4LOCKT_READW:
2502		stp->ls_flags |= NFSLCK_BLOCKING;
2503	case NFSV4LOCKT_READ:
2504		break;
2505	case NFSV4LOCKT_WRITEW:
2506		stp->ls_flags |= NFSLCK_BLOCKING;
2507	case NFSV4LOCKT_WRITE:
2508		break;
2509	default:
2510		nd->nd_repstat = NFSERR_BADXDR;
2511		free(stp, M_NFSDSTATE);
2512		free(lop, M_NFSDLOCK);
2513		goto nfsmout;
2514	}
2515	stp->ls_ownerlen = 0;
2516	stp->ls_uid = nd->nd_cred->cr_uid;
2517	stp->ls_seq = fxdr_unsigned(int, *tl++);
2518	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2519	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2520	    NFSX_STATEIDOTHER);
2521	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2522	lop->lo_first = fxdr_hyper(tl);
2523	tl += 2;
2524	len = fxdr_hyper(tl);
2525	if (len == NFS64BITSSET) {
2526		lop->lo_end = NFS64BITSSET;
2527	} else {
2528		lop->lo_end = lop->lo_first + len;
2529		if (lop->lo_end <= lop->lo_first)
2530			nd->nd_repstat = NFSERR_INVAL;
2531	}
2532	clientid.lval[0] = stp->ls_stateid.other[0];
2533	clientid.lval[1] = stp->ls_stateid.other[1];
2534	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2535		if ((nd->nd_flag & ND_NFSV41) != 0)
2536			clientid.qval = nd->nd_clientid.qval;
2537		else if (nd->nd_clientid.qval != clientid.qval)
2538			printf("EEK6 multiple clids\n");
2539	} else {
2540		if ((nd->nd_flag & ND_NFSV41) != 0)
2541			printf("EEK! no clientid from session\n");
2542		nd->nd_flag |= ND_IMPLIEDCLID;
2543		nd->nd_clientid.qval = clientid.qval;
2544	}
2545	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2546	    if (vnode_vtype(vp) == VDIR)
2547		nd->nd_repstat = NFSERR_ISDIR;
2548	    else
2549		nd->nd_repstat = NFSERR_INVAL;
2550	}
2551	/*
2552	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
2553	 * seqid# gets incremented. nfsrv_lockctrl() will return the
2554	 * value of nd_repstat, if it gets that far.
2555	 */
2556	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
2557	    &stateid, exp, nd, p);
2558	if (stp)
2559		FREE((caddr_t)stp, M_NFSDSTATE);
2560	if (lop)
2561		free((caddr_t)lop, M_NFSDLOCK);
2562	if (!nd->nd_repstat) {
2563		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2564		*tl++ = txdr_unsigned(stateid.seqid);
2565		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2566	}
2567nfsmout:
2568	vput(vp);
2569	NFSEXITCODE2(error, nd);
2570	return (error);
2571}
2572
2573/*
2574 * nfsv4 open service
2575 */
2576APPLESTATIC int
2577nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2578    vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
2579    struct nfsexstuff *exp)
2580{
2581	u_int32_t *tl;
2582	int i, retext;
2583	struct nfsstate *stp = NULL;
2584	int error = 0, create, claim, exclusive_flag = 0;
2585	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
2586	int how = NFSCREATE_UNCHECKED;
2587	int32_t cverf[2], tverf[2] = { 0, 0 };
2588	vnode_t vp = NULL, dirp = NULL;
2589	struct nfsvattr nva, dirfor, diraft;
2590	struct nameidata named;
2591	nfsv4stateid_t stateid, delegstateid;
2592	nfsattrbit_t attrbits;
2593	nfsquad_t clientid;
2594	char *bufp = NULL;
2595	u_long *hashp;
2596	NFSACL_T *aclp = NULL;
2597
2598#ifdef NFS4_ACL_EXTATTR_NAME
2599	aclp = acl_alloc(M_WAITOK);
2600	aclp->acl_cnt = 0;
2601#endif
2602	NFSZERO_ATTRBIT(&attrbits);
2603	named.ni_startdir = NULL;
2604	named.ni_cnd.cn_nameiop = 0;
2605	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
2606	i = fxdr_unsigned(int, *(tl + 5));
2607	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2608		nd->nd_repstat = NFSERR_BADXDR;
2609		goto nfsmout;
2610	}
2611	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2612	    M_NFSDSTATE, M_WAITOK);
2613	stp->ls_ownerlen = i;
2614	stp->ls_op = nd->nd_rp;
2615	stp->ls_flags = NFSLCK_OPEN;
2616	stp->ls_uid = nd->nd_cred->cr_uid;
2617	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2618	i = fxdr_unsigned(int, *tl++);
2619	retext = 0;
2620	if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
2621	    NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
2622		retext = 1;
2623		/* For now, ignore these. */
2624		i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
2625		switch (i & NFSV4OPEN_WANTDELEGMASK) {
2626		case NFSV4OPEN_WANTANYDELEG:
2627			stp->ls_flags |= (NFSLCK_WANTRDELEG |
2628			    NFSLCK_WANTWDELEG);
2629			i &= ~NFSV4OPEN_WANTDELEGMASK;
2630			break;
2631		case NFSV4OPEN_WANTREADDELEG:
2632			stp->ls_flags |= NFSLCK_WANTRDELEG;
2633			i &= ~NFSV4OPEN_WANTDELEGMASK;
2634			break;
2635		case NFSV4OPEN_WANTWRITEDELEG:
2636			stp->ls_flags |= NFSLCK_WANTWDELEG;
2637			i &= ~NFSV4OPEN_WANTDELEGMASK;
2638			break;
2639		case NFSV4OPEN_WANTNODELEG:
2640			stp->ls_flags |= NFSLCK_WANTNODELEG;
2641			i &= ~NFSV4OPEN_WANTDELEGMASK;
2642			break;
2643		case NFSV4OPEN_WANTCANCEL:
2644			printf("NFSv4: ignore Open WantCancel\n");
2645			i &= ~NFSV4OPEN_WANTDELEGMASK;
2646			break;
2647		default:
2648			/* nd_repstat will be set to NFSERR_INVAL below. */
2649			break;
2650		}
2651	}
2652	switch (i) {
2653	case NFSV4OPEN_ACCESSREAD:
2654		stp->ls_flags |= NFSLCK_READACCESS;
2655		break;
2656	case NFSV4OPEN_ACCESSWRITE:
2657		stp->ls_flags |= NFSLCK_WRITEACCESS;
2658		break;
2659	case NFSV4OPEN_ACCESSBOTH:
2660		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
2661		break;
2662	default:
2663		nd->nd_repstat = NFSERR_INVAL;
2664	}
2665	i = fxdr_unsigned(int, *tl++);
2666	switch (i) {
2667	case NFSV4OPEN_DENYNONE:
2668		break;
2669	case NFSV4OPEN_DENYREAD:
2670		stp->ls_flags |= NFSLCK_READDENY;
2671		break;
2672	case NFSV4OPEN_DENYWRITE:
2673		stp->ls_flags |= NFSLCK_WRITEDENY;
2674		break;
2675	case NFSV4OPEN_DENYBOTH:
2676		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
2677		break;
2678	default:
2679		nd->nd_repstat = NFSERR_INVAL;
2680	}
2681	clientid.lval[0] = *tl++;
2682	clientid.lval[1] = *tl;
2683	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2684		if ((nd->nd_flag & ND_NFSV41) != 0)
2685			clientid.qval = nd->nd_clientid.qval;
2686		else if (nd->nd_clientid.qval != clientid.qval)
2687			printf("EEK7 multiple clids\n");
2688	} else {
2689		if ((nd->nd_flag & ND_NFSV41) != 0)
2690			printf("EEK! no clientid from session\n");
2691		nd->nd_flag |= ND_IMPLIEDCLID;
2692		nd->nd_clientid.qval = clientid.qval;
2693	}
2694	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2695	if (error)
2696		goto nfsmout;
2697	NFSVNO_ATTRINIT(&nva);
2698	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2699	create = fxdr_unsigned(int, *tl);
2700	if (!nd->nd_repstat)
2701		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
2702	if (create == NFSV4OPEN_CREATE) {
2703		nva.na_type = VREG;
2704		nva.na_mode = 0;
2705		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2706		how = fxdr_unsigned(int, *tl);
2707		switch (how) {
2708		case NFSCREATE_UNCHECKED:
2709		case NFSCREATE_GUARDED:
2710			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2711			if (error)
2712				goto nfsmout;
2713			/*
2714			 * If the na_gid being set is the same as that of
2715			 * the directory it is going in, clear it, since
2716			 * that is what will be set by default. This allows
2717			 * a user that isn't in that group to do the create.
2718			 */
2719			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
2720			    nva.na_gid == dirfor.na_gid)
2721				NFSVNO_UNSET(&nva, gid);
2722			if (!nd->nd_repstat)
2723				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2724			break;
2725		case NFSCREATE_EXCLUSIVE:
2726			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2727			cverf[0] = *tl++;
2728			cverf[1] = *tl;
2729			break;
2730		case NFSCREATE_EXCLUSIVE41:
2731			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2732			cverf[0] = *tl++;
2733			cverf[1] = *tl;
2734			error = nfsv4_sattr(nd, vp, &nva, &attrbits, aclp, p);
2735			if (error != 0)
2736				goto nfsmout;
2737			if (NFSISSET_ATTRBIT(&attrbits,
2738			    NFSATTRBIT_TIMEACCESSSET))
2739				nd->nd_repstat = NFSERR_INVAL;
2740			/*
2741			 * If the na_gid being set is the same as that of
2742			 * the directory it is going in, clear it, since
2743			 * that is what will be set by default. This allows
2744			 * a user that isn't in that group to do the create.
2745			 */
2746			if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
2747			    nva.na_gid == dirfor.na_gid)
2748				NFSVNO_UNSET(&nva, gid);
2749			if (nd->nd_repstat == 0)
2750				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2751			break;
2752		default:
2753			nd->nd_repstat = NFSERR_BADXDR;
2754			goto nfsmout;
2755		}
2756	} else if (create != NFSV4OPEN_NOCREATE) {
2757		nd->nd_repstat = NFSERR_BADXDR;
2758		goto nfsmout;
2759	}
2760
2761	/*
2762	 * Now, handle the claim, which usually includes looking up a
2763	 * name in the directory referenced by dp. The exception is
2764	 * NFSV4OPEN_CLAIMPREVIOUS.
2765	 */
2766	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2767	claim = fxdr_unsigned(int, *tl);
2768	if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
2769		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2770		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2771		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
2772		stp->ls_flags |= NFSLCK_DELEGCUR;
2773	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2774		stp->ls_flags |= NFSLCK_DELEGPREV;
2775	}
2776	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
2777	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2778		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
2779		    claim != NFSV4OPEN_CLAIMNULL)
2780			nd->nd_repstat = NFSERR_INVAL;
2781		if (nd->nd_repstat) {
2782			nd->nd_repstat = nfsrv_opencheck(clientid,
2783			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
2784			goto nfsmout;
2785		}
2786		if (create == NFSV4OPEN_CREATE)
2787		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
2788			LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
2789		else
2790		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
2791			LOCKLEAF | SAVESTART);
2792		nfsvno_setpathbuf(&named, &bufp, &hashp);
2793		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2794		if (error) {
2795			vrele(dp);
2796#ifdef NFS4_ACL_EXTATTR_NAME
2797			acl_free(aclp);
2798#endif
2799			FREE((caddr_t)stp, M_NFSDSTATE);
2800			nfsvno_relpathbuf(&named);
2801			NFSEXITCODE2(error, nd);
2802			return (error);
2803		}
2804		if (!nd->nd_repstat) {
2805			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
2806			    p, &dirp);
2807		} else {
2808			vrele(dp);
2809			nfsvno_relpathbuf(&named);
2810		}
2811		if (create == NFSV4OPEN_CREATE) {
2812		    switch (how) {
2813		    case NFSCREATE_UNCHECKED:
2814			if (named.ni_vp) {
2815				/*
2816				 * Clear the setable attribute bits, except
2817				 * for Size, if it is being truncated.
2818				 */
2819				NFSZERO_ATTRBIT(&attrbits);
2820				if (NFSVNO_ISSETSIZE(&nva))
2821					NFSSETBIT_ATTRBIT(&attrbits,
2822					    NFSATTRBIT_SIZE);
2823			}
2824			break;
2825		    case NFSCREATE_GUARDED:
2826			if (named.ni_vp && !nd->nd_repstat)
2827				nd->nd_repstat = EEXIST;
2828			break;
2829		    case NFSCREATE_EXCLUSIVE:
2830			exclusive_flag = 1;
2831			if (!named.ni_vp)
2832				nva.na_mode = 0;
2833			break;
2834		    case NFSCREATE_EXCLUSIVE41:
2835			exclusive_flag = 1;
2836			break;
2837		    }
2838		}
2839		nfsvno_open(nd, &named, clientid, &stateid, stp,
2840		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
2841		    nd->nd_cred, p, exp, &vp);
2842	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
2843	    NFSV4OPEN_CLAIMFH) {
2844		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2845			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2846			i = fxdr_unsigned(int, *tl);
2847			switch (i) {
2848			case NFSV4OPEN_DELEGATEREAD:
2849				stp->ls_flags |= NFSLCK_DELEGREAD;
2850				break;
2851			case NFSV4OPEN_DELEGATEWRITE:
2852				stp->ls_flags |= NFSLCK_DELEGWRITE;
2853			case NFSV4OPEN_DELEGATENONE:
2854				break;
2855			default:
2856				nd->nd_repstat = NFSERR_BADXDR;
2857				goto nfsmout;
2858			}
2859			stp->ls_flags |= NFSLCK_RECLAIM;
2860		} else {
2861			/* CLAIM_NULL_FH */
2862			if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
2863				nd->nd_repstat = NFSERR_INVAL;
2864		}
2865		vp = dp;
2866		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
2867		if ((vp->v_iflag & VI_DOOMED) == 0)
2868			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
2869			    stp, vp, nd, p, nd->nd_repstat);
2870		else
2871			nd->nd_repstat = NFSERR_PERM;
2872	} else {
2873		nd->nd_repstat = NFSERR_BADXDR;
2874		goto nfsmout;
2875	}
2876
2877	/*
2878	 * Do basic access checking.
2879	 */
2880	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2881		/*
2882		 * The IETF working group decided that this is the correct
2883		 * error return for all non-regular files.
2884		 */
2885		nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK;
2886	}
2887	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
2888	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
2889	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2890	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
2891	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
2892	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2893	    if (nd->nd_repstat)
2894		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2895		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2896		    NFSACCCHK_VPISLOCKED, NULL);
2897	}
2898
2899	if (!nd->nd_repstat) {
2900		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
2901		if (!nd->nd_repstat) {
2902			tverf[0] = nva.na_atime.tv_sec;
2903			tverf[1] = nva.na_atime.tv_nsec;
2904		}
2905	}
2906	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
2907	    cverf[1] != tverf[1]))
2908		nd->nd_repstat = EEXIST;
2909	/*
2910	 * Do the open locking/delegation stuff.
2911	 */
2912	if (!nd->nd_repstat)
2913	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
2914		&delegstateid, &rflags, exp, p, nva.na_filerev);
2915
2916	/*
2917	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
2918	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
2919	 * (ie: Leave the NFSVOPUNLOCK() about here.)
2920	 */
2921	if (vp)
2922		NFSVOPUNLOCK(vp, 0);
2923	if (stp)
2924		FREE((caddr_t)stp, M_NFSDSTATE);
2925	if (!nd->nd_repstat && dirp)
2926		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p,
2927		    0);
2928	if (!nd->nd_repstat) {
2929		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
2930		*tl++ = txdr_unsigned(stateid.seqid);
2931		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2932		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2933		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2934			*tl++ = newnfs_true;
2935			*tl++ = 0;
2936			*tl++ = 0;
2937			*tl++ = 0;
2938			*tl++ = 0;
2939		} else {
2940			*tl++ = newnfs_false;	/* Since dirp is not locked */
2941			txdr_hyper(dirfor.na_filerev, tl);
2942			tl += 2;
2943			txdr_hyper(diraft.na_filerev, tl);
2944			tl += 2;
2945		}
2946		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
2947		(void) nfsrv_putattrbit(nd, &attrbits);
2948		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2949		if (rflags & NFSV4OPEN_READDELEGATE)
2950			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
2951		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
2952			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
2953		else if (retext != 0) {
2954			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
2955			if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
2956				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2957				*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
2958				*tl = newnfs_false;
2959			} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
2960				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2961				*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
2962				*tl = newnfs_false;
2963			} else {
2964				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2965				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
2966			}
2967		} else
2968			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
2969		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
2970			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
2971			*tl++ = txdr_unsigned(delegstateid.seqid);
2972			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
2973			    NFSX_STATEIDOTHER);
2974			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2975			if (rflags & NFSV4OPEN_RECALL)
2976				*tl = newnfs_true;
2977			else
2978				*tl = newnfs_false;
2979			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
2980				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2981				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
2982				txdr_hyper(nva.na_size, tl);
2983			}
2984			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2985			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
2986			*tl++ = txdr_unsigned(0x0);
2987			acemask = NFSV4ACE_ALLFILESMASK;
2988			if (nva.na_mode & S_IRUSR)
2989			    acemask |= NFSV4ACE_READMASK;
2990			if (nva.na_mode & S_IWUSR)
2991			    acemask |= NFSV4ACE_WRITEMASK;
2992			if (nva.na_mode & S_IXUSR)
2993			    acemask |= NFSV4ACE_EXECUTEMASK;
2994			*tl = txdr_unsigned(acemask);
2995			(void) nfsm_strtom(nd, "OWNER@", 6);
2996		}
2997		*vpp = vp;
2998	} else if (vp) {
2999		vrele(vp);
3000	}
3001	if (dirp)
3002		vrele(dirp);
3003#ifdef NFS4_ACL_EXTATTR_NAME
3004	acl_free(aclp);
3005#endif
3006	NFSEXITCODE2(0, nd);
3007	return (0);
3008nfsmout:
3009	vrele(dp);
3010#ifdef NFS4_ACL_EXTATTR_NAME
3011	acl_free(aclp);
3012#endif
3013	if (stp)
3014		FREE((caddr_t)stp, M_NFSDSTATE);
3015	NFSEXITCODE2(error, nd);
3016	return (error);
3017}
3018
3019/*
3020 * nfsv4 close service
3021 */
3022APPLESTATIC int
3023nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
3024    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3025{
3026	u_int32_t *tl;
3027	struct nfsstate st, *stp = &st;
3028	int error = 0;
3029	nfsv4stateid_t stateid;
3030	nfsquad_t clientid;
3031
3032	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
3033	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3034	stp->ls_ownerlen = 0;
3035	stp->ls_op = nd->nd_rp;
3036	stp->ls_uid = nd->nd_cred->cr_uid;
3037	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3038	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3039	    NFSX_STATEIDOTHER);
3040	stp->ls_flags = NFSLCK_CLOSE;
3041	clientid.lval[0] = stp->ls_stateid.other[0];
3042	clientid.lval[1] = stp->ls_stateid.other[1];
3043	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3044		if ((nd->nd_flag & ND_NFSV41) != 0)
3045			clientid.qval = nd->nd_clientid.qval;
3046		else if (nd->nd_clientid.qval != clientid.qval)
3047			printf("EEK8 multiple clids\n");
3048	} else {
3049		if ((nd->nd_flag & ND_NFSV41) != 0)
3050			printf("EEK! no clientid from session\n");
3051		nd->nd_flag |= ND_IMPLIEDCLID;
3052		nd->nd_clientid.qval = clientid.qval;
3053	}
3054	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3055	vput(vp);
3056	if (!nd->nd_repstat) {
3057		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3058		*tl++ = txdr_unsigned(stateid.seqid);
3059		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3060	}
3061	NFSEXITCODE2(0, nd);
3062	return (0);
3063nfsmout:
3064	vput(vp);
3065	NFSEXITCODE2(error, nd);
3066	return (error);
3067}
3068
3069/*
3070 * nfsv4 delegpurge service
3071 */
3072APPLESTATIC int
3073nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
3074    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3075{
3076	u_int32_t *tl;
3077	int error = 0;
3078	nfsquad_t clientid;
3079
3080	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3081		nd->nd_repstat = NFSERR_WRONGSEC;
3082		goto nfsmout;
3083	}
3084	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3085	clientid.lval[0] = *tl++;
3086	clientid.lval[1] = *tl;
3087	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3088		if ((nd->nd_flag & ND_NFSV41) != 0)
3089			clientid.qval = nd->nd_clientid.qval;
3090		else if (nd->nd_clientid.qval != clientid.qval)
3091			printf("EEK9 multiple clids\n");
3092	} else {
3093		if ((nd->nd_flag & ND_NFSV41) != 0)
3094			printf("EEK! no clientid from session\n");
3095		nd->nd_flag |= ND_IMPLIEDCLID;
3096		nd->nd_clientid.qval = clientid.qval;
3097	}
3098	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
3099	    NFSV4OP_DELEGPURGE, nd->nd_cred, p);
3100nfsmout:
3101	NFSEXITCODE2(error, nd);
3102	return (error);
3103}
3104
3105/*
3106 * nfsv4 delegreturn service
3107 */
3108APPLESTATIC int
3109nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
3110    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3111{
3112	u_int32_t *tl;
3113	int error = 0;
3114	nfsv4stateid_t stateid;
3115	nfsquad_t clientid;
3116
3117	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3118	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3119	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
3120	clientid.lval[0] = stateid.other[0];
3121	clientid.lval[1] = stateid.other[1];
3122	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3123		if ((nd->nd_flag & ND_NFSV41) != 0)
3124			clientid.qval = nd->nd_clientid.qval;
3125		else if (nd->nd_clientid.qval != clientid.qval)
3126			printf("EEK10 multiple clids\n");
3127	} else {
3128		if ((nd->nd_flag & ND_NFSV41) != 0)
3129			printf("EEK! no clientid from session\n");
3130		nd->nd_flag |= ND_IMPLIEDCLID;
3131		nd->nd_clientid.qval = clientid.qval;
3132	}
3133	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
3134	    NFSV4OP_DELEGRETURN, nd->nd_cred, p);
3135nfsmout:
3136	vput(vp);
3137	NFSEXITCODE2(error, nd);
3138	return (error);
3139}
3140
3141/*
3142 * nfsv4 get file handle service
3143 */
3144APPLESTATIC int
3145nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
3146    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3147{
3148	fhandle_t fh;
3149
3150	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3151	vput(vp);
3152	if (!nd->nd_repstat)
3153		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3154	NFSEXITCODE2(0, nd);
3155	return (0);
3156}
3157
3158/*
3159 * nfsv4 open confirm service
3160 */
3161APPLESTATIC int
3162nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3163    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3164{
3165	u_int32_t *tl;
3166	struct nfsstate st, *stp = &st;
3167	int error = 0;
3168	nfsv4stateid_t stateid;
3169	nfsquad_t clientid;
3170
3171	if ((nd->nd_flag & ND_NFSV41) != 0) {
3172		nd->nd_repstat = NFSERR_NOTSUPP;
3173		goto nfsmout;
3174	}
3175	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3176	stp->ls_ownerlen = 0;
3177	stp->ls_op = nd->nd_rp;
3178	stp->ls_uid = nd->nd_cred->cr_uid;
3179	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3180	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3181	    NFSX_STATEIDOTHER);
3182	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3183	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
3184	stp->ls_flags = NFSLCK_CONFIRM;
3185	clientid.lval[0] = stp->ls_stateid.other[0];
3186	clientid.lval[1] = stp->ls_stateid.other[1];
3187	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3188		if ((nd->nd_flag & ND_NFSV41) != 0)
3189			clientid.qval = nd->nd_clientid.qval;
3190		else if (nd->nd_clientid.qval != clientid.qval)
3191			printf("EEK11 multiple clids\n");
3192	} else {
3193		if ((nd->nd_flag & ND_NFSV41) != 0)
3194			printf("EEK! no clientid from session\n");
3195		nd->nd_flag |= ND_IMPLIEDCLID;
3196		nd->nd_clientid.qval = clientid.qval;
3197	}
3198	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3199	if (!nd->nd_repstat) {
3200		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3201		*tl++ = txdr_unsigned(stateid.seqid);
3202		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3203	}
3204nfsmout:
3205	vput(vp);
3206	NFSEXITCODE2(error, nd);
3207	return (error);
3208}
3209
3210/*
3211 * nfsv4 open downgrade service
3212 */
3213APPLESTATIC int
3214nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3215    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3216{
3217	u_int32_t *tl;
3218	int i;
3219	struct nfsstate st, *stp = &st;
3220	int error = 0;
3221	nfsv4stateid_t stateid;
3222	nfsquad_t clientid;
3223
3224	/* opendowngrade can only work on a file object.*/
3225	if (vp->v_type != VREG) {
3226		error = NFSERR_INVAL;
3227		goto nfsmout;
3228	}
3229	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
3230	stp->ls_ownerlen = 0;
3231	stp->ls_op = nd->nd_rp;
3232	stp->ls_uid = nd->nd_cred->cr_uid;
3233	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3234	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3235	    NFSX_STATEIDOTHER);
3236	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3237	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3238	i = fxdr_unsigned(int, *tl++);
3239	switch (i) {
3240	case NFSV4OPEN_ACCESSREAD:
3241		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
3242		break;
3243	case NFSV4OPEN_ACCESSWRITE:
3244		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
3245		break;
3246	case NFSV4OPEN_ACCESSBOTH:
3247		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
3248		    NFSLCK_DOWNGRADE);
3249		break;
3250	default:
3251		nd->nd_repstat = NFSERR_BADXDR;
3252	}
3253	i = fxdr_unsigned(int, *tl);
3254	switch (i) {
3255	case NFSV4OPEN_DENYNONE:
3256		break;
3257	case NFSV4OPEN_DENYREAD:
3258		stp->ls_flags |= NFSLCK_READDENY;
3259		break;
3260	case NFSV4OPEN_DENYWRITE:
3261		stp->ls_flags |= NFSLCK_WRITEDENY;
3262		break;
3263	case NFSV4OPEN_DENYBOTH:
3264		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
3265		break;
3266	default:
3267		nd->nd_repstat = NFSERR_BADXDR;
3268	}
3269
3270	clientid.lval[0] = stp->ls_stateid.other[0];
3271	clientid.lval[1] = stp->ls_stateid.other[1];
3272	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3273		if ((nd->nd_flag & ND_NFSV41) != 0)
3274			clientid.qval = nd->nd_clientid.qval;
3275		else if (nd->nd_clientid.qval != clientid.qval)
3276			printf("EEK12 multiple clids\n");
3277	} else {
3278		if ((nd->nd_flag & ND_NFSV41) != 0)
3279			printf("EEK! no clientid from session\n");
3280		nd->nd_flag |= ND_IMPLIEDCLID;
3281		nd->nd_clientid.qval = clientid.qval;
3282	}
3283	if (!nd->nd_repstat)
3284		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
3285		    nd, p);
3286	if (!nd->nd_repstat) {
3287		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3288		*tl++ = txdr_unsigned(stateid.seqid);
3289		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3290	}
3291nfsmout:
3292	vput(vp);
3293	NFSEXITCODE2(error, nd);
3294	return (error);
3295}
3296
3297/*
3298 * nfsv4 renew lease service
3299 */
3300APPLESTATIC int
3301nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3302    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3303{
3304	u_int32_t *tl;
3305	int error = 0;
3306	nfsquad_t clientid;
3307
3308	if ((nd->nd_flag & ND_NFSV41) != 0) {
3309		nd->nd_repstat = NFSERR_NOTSUPP;
3310		goto nfsmout;
3311	}
3312	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3313		nd->nd_repstat = NFSERR_WRONGSEC;
3314		goto nfsmout;
3315	}
3316	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
3317	clientid.lval[0] = *tl++;
3318	clientid.lval[1] = *tl;
3319	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3320		if ((nd->nd_flag & ND_NFSV41) != 0)
3321			clientid.qval = nd->nd_clientid.qval;
3322		else if (nd->nd_clientid.qval != clientid.qval)
3323			printf("EEK13 multiple clids\n");
3324	} else {
3325		if ((nd->nd_flag & ND_NFSV41) != 0)
3326			printf("EEK! no clientid from session\n");
3327		nd->nd_flag |= ND_IMPLIEDCLID;
3328		nd->nd_clientid.qval = clientid.qval;
3329	}
3330	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3331	    NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
3332nfsmout:
3333	NFSEXITCODE2(error, nd);
3334	return (error);
3335}
3336
3337/*
3338 * nfsv4 security info service
3339 */
3340APPLESTATIC int
3341nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3342    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
3343{
3344	u_int32_t *tl;
3345	int len;
3346	struct nameidata named;
3347	vnode_t dirp = NULL, vp;
3348	struct nfsrvfh fh;
3349	struct nfsexstuff retnes;
3350	u_int32_t *sizp;
3351	int error = 0, savflag, i;
3352	char *bufp;
3353	u_long *hashp;
3354
3355	/*
3356	 * All this just to get the export flags for the name.
3357	 */
3358	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
3359	    LOCKLEAF | SAVESTART);
3360	nfsvno_setpathbuf(&named, &bufp, &hashp);
3361	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3362	if (error) {
3363		vput(dp);
3364		nfsvno_relpathbuf(&named);
3365		goto out;
3366	}
3367	if (!nd->nd_repstat) {
3368		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
3369	} else {
3370		vput(dp);
3371		nfsvno_relpathbuf(&named);
3372	}
3373	if (dirp)
3374		vrele(dirp);
3375	if (nd->nd_repstat)
3376		goto out;
3377	vrele(named.ni_startdir);
3378	nfsvno_relpathbuf(&named);
3379	fh.nfsrvfh_len = NFSX_MYFH;
3380	vp = named.ni_vp;
3381	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3382	vput(vp);
3383	savflag = nd->nd_flag;
3384	if (!nd->nd_repstat) {
3385		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0, p);
3386		if (vp)
3387			vput(vp);
3388	}
3389	nd->nd_flag = savflag;
3390	if (nd->nd_repstat)
3391		goto out;
3392
3393	/*
3394	 * Finally have the export flags for name, so we can create
3395	 * the security info.
3396	 */
3397	len = 0;
3398	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
3399	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3400		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3401			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3402			*tl = txdr_unsigned(RPCAUTH_UNIX);
3403			len++;
3404		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3405			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3406			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3407			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3408			    nfsgss_mechlist[KERBV_MECH].len);
3409			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3410			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3411			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3412			len++;
3413		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
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_SVCINTEGRITY);
3421			len++;
3422		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
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_SVCPRIVACY);
3430			len++;
3431		}
3432	}
3433	*sizp = txdr_unsigned(len);
3434
3435out:
3436	NFSEXITCODE2(error, nd);
3437	return (error);
3438}
3439
3440/*
3441 * nfsv4 set client id service
3442 */
3443APPLESTATIC int
3444nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3445    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3446{
3447	u_int32_t *tl;
3448	int i;
3449	int error = 0, idlen;
3450	struct nfsclient *clp = NULL;
3451	struct sockaddr_in *rad;
3452	u_char *verf, *ucp, *ucp2, addrbuf[24];
3453	nfsquad_t clientid, confirm;
3454
3455	if ((nd->nd_flag & ND_NFSV41) != 0) {
3456		nd->nd_repstat = NFSERR_NOTSUPP;
3457		goto nfsmout;
3458	}
3459	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3460		nd->nd_repstat = NFSERR_WRONGSEC;
3461		goto out;
3462	}
3463	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3464	verf = (u_char *)tl;
3465	tl += (NFSX_VERF / NFSX_UNSIGNED);
3466	i = fxdr_unsigned(int, *tl);
3467	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3468		nd->nd_repstat = NFSERR_BADXDR;
3469		goto nfsmout;
3470	}
3471	idlen = i;
3472	if (nd->nd_flag & ND_GSS)
3473		i += nd->nd_princlen;
3474	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3475	    M_ZERO);
3476	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3477	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3478	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3479	NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
3480	NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
3481	clp->lc_req.nr_cred = NULL;
3482	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3483	clp->lc_idlen = idlen;
3484	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3485	if (error)
3486		goto nfsmout;
3487	if (nd->nd_flag & ND_GSS) {
3488		clp->lc_flags = LCL_GSS;
3489		if (nd->nd_flag & ND_GSSINTEGRITY)
3490			clp->lc_flags |= LCL_GSSINTEGRITY;
3491		else if (nd->nd_flag & ND_GSSPRIVACY)
3492			clp->lc_flags |= LCL_GSSPRIVACY;
3493	} else {
3494		clp->lc_flags = 0;
3495	}
3496	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
3497		clp->lc_flags |= LCL_NAME;
3498		clp->lc_namelen = nd->nd_princlen;
3499		clp->lc_name = &clp->lc_id[idlen];
3500		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3501	} else {
3502		clp->lc_uid = nd->nd_cred->cr_uid;
3503		clp->lc_gid = nd->nd_cred->cr_gid;
3504	}
3505	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3506	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
3507	error = nfsrv_getclientipaddr(nd, clp);
3508	if (error)
3509		goto nfsmout;
3510	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3511	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
3512
3513	/*
3514	 * nfsrv_setclient() does the actual work of adding it to the
3515	 * client list. If there is no error, the structure has been
3516	 * linked into the client list and clp should no longer be used
3517	 * here. When an error is returned, it has not been linked in,
3518	 * so it should be free'd.
3519	 */
3520	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3521	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
3522		if (clp->lc_flags & LCL_TCPCALLBACK)
3523			(void) nfsm_strtom(nd, "tcp", 3);
3524		else
3525			(void) nfsm_strtom(nd, "udp", 3);
3526		rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *);
3527		ucp = (u_char *)&rad->sin_addr.s_addr;
3528		ucp2 = (u_char *)&rad->sin_port;
3529		snprintf(addrbuf, sizeof(addrbuf), "%d.%d.%d.%d.%d.%d",
3530		    ucp[0] & 0xff, ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
3531		    ucp2[0] & 0xff, ucp2[1] & 0xff);
3532		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
3533	}
3534	if (clp) {
3535		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3536		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3537		free(clp->lc_stateid, M_NFSDCLIENT);
3538		free(clp, M_NFSDCLIENT);
3539	}
3540	if (!nd->nd_repstat) {
3541		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
3542		*tl++ = clientid.lval[0];
3543		*tl++ = clientid.lval[1];
3544		*tl++ = confirm.lval[0];
3545		*tl = confirm.lval[1];
3546	}
3547
3548out:
3549	NFSEXITCODE2(0, nd);
3550	return (0);
3551nfsmout:
3552	if (clp) {
3553		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3554		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3555		free(clp->lc_stateid, M_NFSDCLIENT);
3556		free(clp, M_NFSDCLIENT);
3557	}
3558	NFSEXITCODE2(error, nd);
3559	return (error);
3560}
3561
3562/*
3563 * nfsv4 set client id confirm service
3564 */
3565APPLESTATIC int
3566nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
3567    __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
3568    __unused struct nfsexstuff *exp)
3569{
3570	u_int32_t *tl;
3571	int error = 0;
3572	nfsquad_t clientid, confirm;
3573
3574	if ((nd->nd_flag & ND_NFSV41) != 0) {
3575		nd->nd_repstat = NFSERR_NOTSUPP;
3576		goto nfsmout;
3577	}
3578	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3579		nd->nd_repstat = NFSERR_WRONGSEC;
3580		goto nfsmout;
3581	}
3582	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
3583	clientid.lval[0] = *tl++;
3584	clientid.lval[1] = *tl++;
3585	confirm.lval[0] = *tl++;
3586	confirm.lval[1] = *tl;
3587
3588	/*
3589	 * nfsrv_getclient() searches the client list for a match and
3590	 * returns the appropriate NFSERR status.
3591	 */
3592	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
3593	    NULL, NULL, confirm, 0, nd, p);
3594nfsmout:
3595	NFSEXITCODE2(error, nd);
3596	return (error);
3597}
3598
3599/*
3600 * nfsv4 verify service
3601 */
3602APPLESTATIC int
3603nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
3604    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3605{
3606	int error = 0, ret, fhsize = NFSX_MYFH;
3607	struct nfsvattr nva;
3608	struct statfs sf;
3609	struct nfsfsinfo fs;
3610	fhandle_t fh;
3611
3612	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
3613	if (!nd->nd_repstat)
3614		nd->nd_repstat = nfsvno_statfs(vp, &sf);
3615	if (!nd->nd_repstat)
3616		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3617	if (!nd->nd_repstat) {
3618		nfsvno_getfs(&fs, isdgram);
3619		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
3620		    &sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
3621		if (!error) {
3622			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
3623				if (ret == 0)
3624					nd->nd_repstat = NFSERR_SAME;
3625				else if (ret != NFSERR_NOTSAME)
3626					nd->nd_repstat = ret;
3627			} else if (ret)
3628				nd->nd_repstat = ret;
3629		}
3630	}
3631	vput(vp);
3632	NFSEXITCODE2(error, nd);
3633	return (error);
3634}
3635
3636/*
3637 * nfs openattr rpc
3638 */
3639APPLESTATIC int
3640nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
3641    vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
3642    __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3643{
3644	u_int32_t *tl;
3645	int error = 0, createdir;
3646
3647	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3648	createdir = fxdr_unsigned(int, *tl);
3649	nd->nd_repstat = NFSERR_NOTSUPP;
3650nfsmout:
3651	vrele(dp);
3652	NFSEXITCODE2(error, nd);
3653	return (error);
3654}
3655
3656/*
3657 * nfsv4 release lock owner service
3658 */
3659APPLESTATIC int
3660nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
3661    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3662{
3663	u_int32_t *tl;
3664	struct nfsstate *stp = NULL;
3665	int error = 0, len;
3666	nfsquad_t clientid;
3667
3668	if ((nd->nd_flag & ND_NFSV41) != 0) {
3669		nd->nd_repstat = NFSERR_NOTSUPP;
3670		goto nfsmout;
3671	}
3672	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3673		nd->nd_repstat = NFSERR_WRONGSEC;
3674		goto nfsmout;
3675	}
3676	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3677	len = fxdr_unsigned(int, *(tl + 2));
3678	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
3679		nd->nd_repstat = NFSERR_BADXDR;
3680		goto nfsmout;
3681	}
3682	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len,
3683	    M_NFSDSTATE, M_WAITOK);
3684	stp->ls_ownerlen = len;
3685	stp->ls_op = NULL;
3686	stp->ls_flags = NFSLCK_RELEASE;
3687	stp->ls_uid = nd->nd_cred->cr_uid;
3688	clientid.lval[0] = *tl++;
3689	clientid.lval[1] = *tl;
3690	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3691		if ((nd->nd_flag & ND_NFSV41) != 0)
3692			clientid.qval = nd->nd_clientid.qval;
3693		else if (nd->nd_clientid.qval != clientid.qval)
3694			printf("EEK14 multiple clids\n");
3695	} else {
3696		if ((nd->nd_flag & ND_NFSV41) != 0)
3697			printf("EEK! no clientid from session\n");
3698		nd->nd_flag |= ND_IMPLIEDCLID;
3699		nd->nd_clientid.qval = clientid.qval;
3700	}
3701	error = nfsrv_mtostr(nd, stp->ls_owner, len);
3702	if (error)
3703		goto nfsmout;
3704	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
3705	FREE((caddr_t)stp, M_NFSDSTATE);
3706
3707	NFSEXITCODE2(0, nd);
3708	return (0);
3709nfsmout:
3710	if (stp)
3711		free((caddr_t)stp, M_NFSDSTATE);
3712	NFSEXITCODE2(error, nd);
3713	return (error);
3714}
3715
3716/*
3717 * nfsv4 exchange_id service
3718 */
3719APPLESTATIC int
3720nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
3721    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3722{
3723	uint32_t *tl;
3724	int error = 0, i, idlen;
3725	struct nfsclient *clp = NULL;
3726	nfsquad_t clientid, confirm;
3727	uint8_t *verf;
3728	uint32_t sp4type, v41flags;
3729	uint64_t owner_minor;
3730	struct timespec verstime;
3731
3732	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3733		nd->nd_repstat = NFSERR_WRONGSEC;
3734		goto nfsmout;
3735	}
3736	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3737	verf = (uint8_t *)tl;
3738	tl += (NFSX_VERF / NFSX_UNSIGNED);
3739	i = fxdr_unsigned(int, *tl);
3740	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3741		nd->nd_repstat = NFSERR_BADXDR;
3742		goto nfsmout;
3743	}
3744	idlen = i;
3745	if (nd->nd_flag & ND_GSS)
3746		i += nd->nd_princlen;
3747	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3748	    M_ZERO);
3749	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3750	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3751	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3752	NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
3753	NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
3754	clp->lc_req.nr_cred = NULL;
3755	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3756	clp->lc_idlen = idlen;
3757	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3758	if (error != 0)
3759		goto nfsmout;
3760	if ((nd->nd_flag & ND_GSS) != 0) {
3761		clp->lc_flags = LCL_GSS | LCL_NFSV41;
3762		if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
3763			clp->lc_flags |= LCL_GSSINTEGRITY;
3764		else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
3765			clp->lc_flags |= LCL_GSSPRIVACY;
3766	} else
3767		clp->lc_flags = LCL_NFSV41;
3768	if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
3769		clp->lc_flags |= LCL_NAME;
3770		clp->lc_namelen = nd->nd_princlen;
3771		clp->lc_name = &clp->lc_id[idlen];
3772		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3773	} else {
3774		clp->lc_uid = nd->nd_cred->cr_uid;
3775		clp->lc_gid = nd->nd_cred->cr_gid;
3776	}
3777	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3778	v41flags = fxdr_unsigned(uint32_t, *tl++);
3779	if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
3780	    NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
3781	    NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
3782		nd->nd_repstat = NFSERR_INVAL;
3783		goto nfsmout;
3784	}
3785	if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
3786		confirm.lval[1] = 1;
3787	else
3788		confirm.lval[1] = 0;
3789	v41flags = NFSV4EXCH_USENONPNFS;
3790	sp4type = fxdr_unsigned(uint32_t, *tl);
3791	if (sp4type != NFSV4EXCH_SP4NONE) {
3792		nd->nd_repstat = NFSERR_NOTSUPP;
3793		goto nfsmout;
3794	}
3795
3796	/*
3797	 * nfsrv_setclient() does the actual work of adding it to the
3798	 * client list. If there is no error, the structure has been
3799	 * linked into the client list and clp should no longer be used
3800	 * here. When an error is returned, it has not been linked in,
3801	 * so it should be free'd.
3802	 */
3803	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3804	if (clp != NULL) {
3805		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3806		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3807		free(clp->lc_stateid, M_NFSDCLIENT);
3808		free(clp, M_NFSDCLIENT);
3809	}
3810	if (nd->nd_repstat == 0) {
3811		if (confirm.lval[1] != 0)
3812			v41flags |= NFSV4EXCH_CONFIRMEDR;
3813		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
3814		*tl++ = clientid.lval[0];			/* ClientID */
3815		*tl++ = clientid.lval[1];
3816		*tl++ = txdr_unsigned(confirm.lval[0]);		/* SequenceID */
3817		*tl++ = txdr_unsigned(v41flags);		/* Exch flags */
3818		*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);	/* No SSV */
3819		owner_minor = 0;				/* Owner */
3820		txdr_hyper(owner_minor, tl);			/* Minor */
3821		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
3822		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */
3823		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
3824		*tl++ = txdr_unsigned(NFSX_UNSIGNED);
3825		*tl++ = time_uptime;		/* Make scope a unique value. */
3826		*tl = txdr_unsigned(1);
3827		(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
3828		(void)nfsm_strtom(nd, version, strlen(version));
3829		NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
3830		verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
3831		verstime.tv_nsec = 0;
3832		txdr_nfsv4time(&verstime, tl);
3833	}
3834	NFSEXITCODE2(0, nd);
3835	return (0);
3836nfsmout:
3837	if (clp != NULL) {
3838		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3839		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3840		free(clp->lc_stateid, M_NFSDCLIENT);
3841		free(clp, M_NFSDCLIENT);
3842	}
3843	NFSEXITCODE2(error, nd);
3844	return (error);
3845}
3846
3847/*
3848 * nfsv4 create session service
3849 */
3850APPLESTATIC int
3851nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
3852    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3853{
3854	uint32_t *tl;
3855	int error = 0;
3856	nfsquad_t clientid, confirm;
3857	struct nfsdsession *sep = NULL;
3858	uint32_t rdmacnt;
3859
3860	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3861		nd->nd_repstat = NFSERR_WRONGSEC;
3862		goto nfsmout;
3863	}
3864	sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
3865	    M_NFSDSESSION, M_WAITOK | M_ZERO);
3866	sep->sess_refcnt = 1;
3867	mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
3868	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
3869	clientid.lval[0] = *tl++;
3870	clientid.lval[1] = *tl++;
3871	confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
3872	sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
3873	/* Persistent sessions and RDMA are not supported. */
3874	sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
3875
3876	/* Fore channel attributes. */
3877	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
3878	tl++;					/* Header pad always 0. */
3879	sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
3880	sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
3881	sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
3882	sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
3883	sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
3884	if (sep->sess_maxslots > NFSV4_SLOTS)
3885		sep->sess_maxslots = NFSV4_SLOTS;
3886	rdmacnt = fxdr_unsigned(uint32_t, *tl);
3887	if (rdmacnt > 1) {
3888		nd->nd_repstat = NFSERR_BADXDR;
3889		goto nfsmout;
3890	} else if (rdmacnt == 1)
3891		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3892
3893	/* Back channel attributes. */
3894	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
3895	tl++;					/* Header pad always 0. */
3896	sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
3897	sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
3898	sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
3899	sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
3900	sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
3901	rdmacnt = fxdr_unsigned(uint32_t, *tl);
3902	if (rdmacnt > 1) {
3903		nd->nd_repstat = NFSERR_BADXDR;
3904		goto nfsmout;
3905	} else if (rdmacnt == 1)
3906		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3907
3908	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3909	sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
3910
3911	/*
3912	 * nfsrv_getclient() searches the client list for a match and
3913	 * returns the appropriate NFSERR status.
3914	 */
3915	nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
3916	    NULL, sep, confirm, sep->sess_cbprogram, nd, p);
3917	if (nd->nd_repstat == 0) {
3918		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
3919		NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
3920		NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
3921		*tl++ = txdr_unsigned(confirm.lval[0]);	/* sequenceid */
3922		*tl++ = txdr_unsigned(sep->sess_crflags);
3923
3924		/* Fore channel attributes. */
3925		*tl++ = 0;
3926		*tl++ = txdr_unsigned(sep->sess_maxreq);
3927		*tl++ = txdr_unsigned(sep->sess_maxresp);
3928		*tl++ = txdr_unsigned(sep->sess_maxrespcached);
3929		*tl++ = txdr_unsigned(sep->sess_maxops);
3930		*tl++ = txdr_unsigned(sep->sess_maxslots);
3931		*tl++ = txdr_unsigned(1);
3932		*tl++ = txdr_unsigned(0);			/* No RDMA. */
3933
3934		/* Back channel attributes. */
3935		*tl++ = 0;
3936		*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
3937		*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
3938		*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
3939		*tl++ = txdr_unsigned(sep->sess_cbmaxops);
3940		*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
3941		*tl++ = txdr_unsigned(1);
3942		*tl = txdr_unsigned(0);			/* No RDMA. */
3943	}
3944nfsmout:
3945	if (nd->nd_repstat != 0 && sep != NULL)
3946		free(sep, M_NFSDSESSION);
3947	NFSEXITCODE2(error, nd);
3948	return (error);
3949}
3950
3951/*
3952 * nfsv4 sequence service
3953 */
3954APPLESTATIC int
3955nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
3956    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3957{
3958	uint32_t *tl;
3959	uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
3960	int cache_this, error = 0;
3961
3962	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3963		nd->nd_repstat = NFSERR_WRONGSEC;
3964		goto nfsmout;
3965	}
3966	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
3967	NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
3968	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
3969	sequenceid = fxdr_unsigned(uint32_t, *tl++);
3970	nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
3971	highest_slotid = fxdr_unsigned(uint32_t, *tl++);
3972	if (*tl == newnfs_true)
3973		cache_this = 1;
3974	else
3975		cache_this = 0;
3976	nd->nd_flag |= ND_HASSEQUENCE;
3977	nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
3978	    &target_highest_slotid, cache_this, &sflags, p);
3979	if (nd->nd_repstat == 0) {
3980		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
3981		NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
3982		NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
3983		*tl++ = txdr_unsigned(sequenceid);
3984		*tl++ = txdr_unsigned(nd->nd_slotid);
3985		*tl++ = txdr_unsigned(highest_slotid);
3986		*tl++ = txdr_unsigned(target_highest_slotid);
3987		*tl = txdr_unsigned(sflags);
3988	}
3989nfsmout:
3990	NFSEXITCODE2(error, nd);
3991	return (error);
3992}
3993
3994/*
3995 * nfsv4 reclaim complete service
3996 */
3997APPLESTATIC int
3998nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
3999    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4000{
4001	uint32_t *tl;
4002	int error = 0;
4003
4004	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4005		nd->nd_repstat = NFSERR_WRONGSEC;
4006		goto nfsmout;
4007	}
4008	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4009	if (*tl == newnfs_true)
4010		nd->nd_repstat = NFSERR_NOTSUPP;
4011	else
4012		nd->nd_repstat = nfsrv_checkreclaimcomplete(nd);
4013nfsmout:
4014	NFSEXITCODE2(error, nd);
4015	return (error);
4016}
4017
4018/*
4019 * nfsv4 destroy clientid service
4020 */
4021APPLESTATIC int
4022nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
4023    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4024{
4025	uint32_t *tl;
4026	nfsquad_t clientid;
4027	int error = 0;
4028
4029	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4030		nd->nd_repstat = NFSERR_WRONGSEC;
4031		goto nfsmout;
4032	}
4033	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4034	clientid.lval[0] = *tl++;
4035	clientid.lval[1] = *tl;
4036	nd->nd_repstat = nfsrv_destroyclient(clientid, p);
4037nfsmout:
4038	NFSEXITCODE2(error, nd);
4039	return (error);
4040}
4041
4042/*
4043 * nfsv4 destroy session service
4044 */
4045APPLESTATIC int
4046nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
4047    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4048{
4049	uint8_t *cp, sessid[NFSX_V4SESSIONID];
4050	int error = 0;
4051
4052	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4053		nd->nd_repstat = NFSERR_WRONGSEC;
4054		goto nfsmout;
4055	}
4056	NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
4057	NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
4058	nd->nd_repstat = nfsrv_destroysession(nd, sessid);
4059nfsmout:
4060	NFSEXITCODE2(error, nd);
4061	return (error);
4062}
4063
4064/*
4065 * nfsv4 free stateid service
4066 */
4067APPLESTATIC int
4068nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
4069    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4070{
4071	uint32_t *tl;
4072	nfsv4stateid_t stateid;
4073	int error = 0;
4074
4075	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4076		nd->nd_repstat = NFSERR_WRONGSEC;
4077		goto nfsmout;
4078	}
4079	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4080	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4081	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4082	nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
4083nfsmout:
4084	NFSEXITCODE2(error, nd);
4085	return (error);
4086}
4087
4088/*
4089 * nfsv4 service not supported
4090 */
4091APPLESTATIC int
4092nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
4093    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4094{
4095
4096	nd->nd_repstat = NFSERR_NOTSUPP;
4097	NFSEXITCODE2(0, nd);
4098	return (0);
4099}
4100
4101