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