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