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