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