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