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