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