nfs_nfsdserv.c revision 216693
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 216693 2010-12-24 21:31:18Z 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, nfsv4root_set;
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, &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, &dp, &tnes, &mp, 0, p);
1550			if (dp)
1551				NFSVOPUNLOCK(dp, 0, p);
1552		}
1553	}
1554	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1555	    LOCKPARENT | SAVENAME);
1556	if (!nd->nd_repstat) {
1557		nfsvno_setpathbuf(&named, &bufp, &hashp);
1558		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1559		if (error) {
1560			vrele(vp);
1561			if (dp) {
1562				if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
1563				    !(nd->nd_flag & ND_NFSV4))
1564					nfsvno_unlockvfs(mp);
1565				vrele(dp);
1566			}
1567			nfsvno_relpathbuf(&named);
1568			return (error);
1569		}
1570		if (!nd->nd_repstat) {
1571			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
1572			    p, &dirp);
1573		} else {
1574			if (dp)
1575				vrele(dp);
1576			nfsvno_relpathbuf(&named);
1577		}
1578	}
1579	if (dirp) {
1580		if (nd->nd_flag & ND_NFSV2) {
1581			vrele(dirp);
1582			dirp = NULL;
1583		} else {
1584			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1585			    nd->nd_cred, p, 0);
1586		}
1587	}
1588	if (!nd->nd_repstat)
1589		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
1590	if (nd->nd_flag & ND_NFSV3)
1591		getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 0);
1592	if (dirp) {
1593		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1594		vrele(dirp);
1595	}
1596	if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
1597	    !(nd->nd_flag & ND_NFSV4))
1598		nfsvno_unlockvfs(mp);
1599	vrele(vp);
1600	if (nd->nd_flag & ND_NFSV3) {
1601		nfsrv_postopattr(nd, getret, &at);
1602		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1603	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1604		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1605		*tl++ = newnfs_false;
1606		txdr_hyper(dirfor.na_filerev, tl);
1607		tl += 2;
1608		txdr_hyper(diraft.na_filerev, tl);
1609	}
1610	return (0);
1611}
1612
1613/*
1614 * nfs symbolic link service
1615 */
1616APPLESTATIC int
1617nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1618    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1619    struct nfsexstuff *exp)
1620{
1621	struct nfsvattr nva, dirfor, diraft;
1622	struct nameidata named;
1623	int error, dirfor_ret = 1, diraft_ret = 1, pathlen;
1624	vnode_t dirp = NULL;
1625	char *bufp, *pathcp = NULL;
1626	u_long *hashp;
1627
1628	if (nd->nd_repstat) {
1629		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1630		return (0);
1631	}
1632	if (vpp)
1633		*vpp = NULL;
1634	NFSVNO_ATTRINIT(&nva);
1635	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1636	    LOCKPARENT | SAVESTART);
1637	nfsvno_setpathbuf(&named, &bufp, &hashp);
1638	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1639	if (!error && !nd->nd_repstat)
1640		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
1641	if (error) {
1642		vrele(dp);
1643		nfsvno_relpathbuf(&named);
1644		return (error);
1645	}
1646	if (!nd->nd_repstat) {
1647		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1648	} else {
1649		vrele(dp);
1650		nfsvno_relpathbuf(&named);
1651	}
1652	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1653		vrele(dirp);
1654		dirp = NULL;
1655	}
1656
1657	/*
1658	 * And call nfsrvd_symlinksub() to do the common code. It will
1659	 * return EBADRPC upon a parsing error, 0 otherwise.
1660	 */
1661	if (!nd->nd_repstat) {
1662		if (dirp != NULL)
1663			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1664			    p, 0);
1665		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1666		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
1667		    pathcp, pathlen);
1668	} else if (dirp != NULL) {
1669		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1670		vrele(dirp);
1671	}
1672	if (pathcp)
1673		FREE(pathcp, M_TEMP);
1674
1675	if (nd->nd_flag & ND_NFSV3) {
1676		if (!nd->nd_repstat) {
1677			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1678			nfsrv_postopattr(nd, 0, &nva);
1679		}
1680		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1681	}
1682	return (0);
1683}
1684
1685/*
1686 * Common code for creating a symbolic link.
1687 */
1688static void
1689nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
1690    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1691    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1692    int *diraft_retp, nfsattrbit_t *attrbitp,
1693    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
1694    int pathlen)
1695{
1696	u_int32_t *tl;
1697
1698	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
1699	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
1700	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
1701		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
1702		if (nd->nd_flag & ND_NFSV3) {
1703			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
1704			if (!nd->nd_repstat)
1705				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
1706				    nvap, nd->nd_cred, p, 1);
1707		}
1708		if (vpp) {
1709			NFSVOPUNLOCK(ndp->ni_vp, 0, p);
1710			*vpp = ndp->ni_vp;
1711		} else {
1712			vput(ndp->ni_vp);
1713		}
1714	}
1715	if (dirp) {
1716		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1717		vrele(dirp);
1718	}
1719	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1720		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1721		*tl++ = newnfs_false;
1722		txdr_hyper(dirforp->na_filerev, tl);
1723		tl += 2;
1724		txdr_hyper(diraftp->na_filerev, tl);
1725		(void) nfsrv_putattrbit(nd, attrbitp);
1726	}
1727}
1728
1729/*
1730 * nfs mkdir service
1731 */
1732APPLESTATIC int
1733nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
1734    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1735    struct nfsexstuff *exp)
1736{
1737	struct nfsvattr nva, dirfor, diraft;
1738	struct nameidata named;
1739	u_int32_t *tl;
1740	int error, dirfor_ret = 1, diraft_ret = 1;
1741	vnode_t dirp = NULL;
1742	char *bufp;
1743	u_long *hashp;
1744
1745	if (nd->nd_repstat) {
1746		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1747		return (0);
1748	}
1749	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1750	    LOCKPARENT | SAVENAME);
1751	nfsvno_setpathbuf(&named, &bufp, &hashp);
1752	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1753	if (error) {
1754		vrele(dp);
1755		nfsvno_relpathbuf(&named);
1756		return (error);
1757	}
1758	if (!nd->nd_repstat) {
1759		NFSVNO_ATTRINIT(&nva);
1760		if (nd->nd_flag & ND_NFSV3) {
1761			error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
1762			if (error) {
1763				vrele(dp);
1764				nfsvno_relpathbuf(&named);
1765				return (error);
1766			}
1767		} else {
1768			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1769			nva.na_mode = nfstov_mode(*tl++);
1770		}
1771	}
1772	if (!nd->nd_repstat) {
1773		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1774	} else {
1775		vrele(dp);
1776		nfsvno_relpathbuf(&named);
1777	}
1778	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1779		vrele(dirp);
1780		dirp = NULL;
1781	}
1782	if (nd->nd_repstat) {
1783		if (dirp != NULL) {
1784			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1785			    p, 0);
1786			vrele(dirp);
1787		}
1788		if (nd->nd_flag & ND_NFSV3)
1789			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1790			    &diraft);
1791		return (0);
1792	}
1793	if (dirp != NULL)
1794		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1795
1796	/*
1797	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
1798	 */
1799	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
1800	    &diraft_ret, NULL, NULL, p, exp);
1801
1802	if (nd->nd_flag & ND_NFSV3) {
1803		if (!nd->nd_repstat) {
1804			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1805			nfsrv_postopattr(nd, 0, &nva);
1806		}
1807		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1808	} else if (!nd->nd_repstat) {
1809		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
1810		nfsrv_fillattr(nd, &nva);
1811	}
1812	return (0);
1813nfsmout:
1814	vrele(dp);
1815	nfsvno_relpathbuf(&named);
1816	return (error);
1817}
1818
1819/*
1820 * Code common to mkdir for V2,3 and 4.
1821 */
1822static void
1823nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1824    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1825    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1826    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
1827    NFSPROC_T *p, struct nfsexstuff *exp)
1828{
1829	vnode_t vp;
1830	u_int32_t *tl;
1831
1832	NFSVNO_SETATTRVAL(nvap, type, VDIR);
1833	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
1834	    nd->nd_cred, p, exp);
1835	if (!nd->nd_repstat) {
1836		vp = ndp->ni_vp;
1837		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
1838		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1839		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
1840			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd->nd_cred,
1841			    p, 1);
1842		if (vpp && !nd->nd_repstat) {
1843			NFSVOPUNLOCK(vp, 0, p);
1844			*vpp = vp;
1845		} else {
1846			vput(vp);
1847		}
1848	}
1849	if (dirp) {
1850		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1851		vrele(dirp);
1852	}
1853	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1854		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1855		*tl++ = newnfs_false;
1856		txdr_hyper(dirforp->na_filerev, tl);
1857		tl += 2;
1858		txdr_hyper(diraftp->na_filerev, tl);
1859		(void) nfsrv_putattrbit(nd, attrbitp);
1860	}
1861}
1862
1863/*
1864 * nfs commit service
1865 */
1866APPLESTATIC int
1867nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
1868    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1869{
1870	struct nfsvattr bfor, aft;
1871	u_int32_t *tl;
1872	int error = 0, for_ret = 1, aft_ret = 1, cnt;
1873	u_int64_t off;
1874
1875	if (nd->nd_repstat) {
1876		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1877		return (0);
1878	}
1879	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1880	/*
1881	 * XXX At this time VOP_FSYNC() does not accept offset and byte
1882	 * count parameters, so these arguments are useless (someday maybe).
1883	 */
1884	off = fxdr_hyper(tl);
1885	tl += 2;
1886	cnt = fxdr_unsigned(int, *tl);
1887	if (nd->nd_flag & ND_NFSV3)
1888		for_ret = nfsvno_getattr(vp, &bfor, nd->nd_cred, p, 1);
1889	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
1890	if (nd->nd_flag & ND_NFSV3) {
1891		aft_ret = nfsvno_getattr(vp, &aft, nd->nd_cred, p, 1);
1892		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1893	}
1894	vput(vp);
1895	if (!nd->nd_repstat) {
1896		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
1897		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
1898		*tl = txdr_unsigned(nfsboottime.tv_usec);
1899	}
1900	return (0);
1901nfsmout:
1902	vput(vp);
1903	return (error);
1904}
1905
1906/*
1907 * nfs statfs service
1908 */
1909APPLESTATIC int
1910nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
1911    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1912{
1913	struct statfs *sf;
1914	u_int32_t *tl;
1915	int getret = 1;
1916	struct nfsvattr at;
1917	struct statfs sfs;
1918	u_quad_t tval;
1919
1920	if (nd->nd_repstat) {
1921		nfsrv_postopattr(nd, getret, &at);
1922		return (0);
1923	}
1924	sf = &sfs;
1925	nd->nd_repstat = nfsvno_statfs(vp, sf);
1926	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
1927	vput(vp);
1928	if (nd->nd_flag & ND_NFSV3)
1929		nfsrv_postopattr(nd, getret, &at);
1930	if (nd->nd_repstat)
1931		return (0);
1932	if (nd->nd_flag & ND_NFSV2) {
1933		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
1934		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
1935		*tl++ = txdr_unsigned(sf->f_bsize);
1936		*tl++ = txdr_unsigned(sf->f_blocks);
1937		*tl++ = txdr_unsigned(sf->f_bfree);
1938		*tl = txdr_unsigned(sf->f_bavail);
1939	} else {
1940		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
1941		tval = (u_quad_t)sf->f_blocks;
1942		tval *= (u_quad_t)sf->f_bsize;
1943		txdr_hyper(tval, tl); tl += 2;
1944		tval = (u_quad_t)sf->f_bfree;
1945		tval *= (u_quad_t)sf->f_bsize;
1946		txdr_hyper(tval, tl); tl += 2;
1947		tval = (u_quad_t)sf->f_bavail;
1948		tval *= (u_quad_t)sf->f_bsize;
1949		txdr_hyper(tval, tl); tl += 2;
1950		tval = (u_quad_t)sf->f_files;
1951		txdr_hyper(tval, tl); tl += 2;
1952		tval = (u_quad_t)sf->f_ffree;
1953		txdr_hyper(tval, tl); tl += 2;
1954		tval = (u_quad_t)sf->f_ffree;
1955		txdr_hyper(tval, tl); tl += 2;
1956		*tl = 0;
1957	}
1958	return (0);
1959}
1960
1961/*
1962 * nfs fsinfo service
1963 */
1964APPLESTATIC int
1965nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
1966    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1967{
1968	u_int32_t *tl;
1969	struct nfsfsinfo fs;
1970	int getret = 1;
1971	struct nfsvattr at;
1972
1973	if (nd->nd_repstat) {
1974		nfsrv_postopattr(nd, getret, &at);
1975		return (0);
1976	}
1977	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
1978	nfsvno_getfs(&fs, isdgram);
1979	vput(vp);
1980	nfsrv_postopattr(nd, getret, &at);
1981	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
1982	*tl++ = txdr_unsigned(fs.fs_rtmax);
1983	*tl++ = txdr_unsigned(fs.fs_rtpref);
1984	*tl++ = txdr_unsigned(fs.fs_rtmult);
1985	*tl++ = txdr_unsigned(fs.fs_wtmax);
1986	*tl++ = txdr_unsigned(fs.fs_wtpref);
1987	*tl++ = txdr_unsigned(fs.fs_wtmult);
1988	*tl++ = txdr_unsigned(fs.fs_dtpref);
1989	txdr_hyper(fs.fs_maxfilesize, tl);
1990	tl += 2;
1991	txdr_nfsv3time(&fs.fs_timedelta, tl);
1992	tl += 2;
1993	*tl = txdr_unsigned(fs.fs_properties);
1994	return (0);
1995}
1996
1997/*
1998 * nfs pathconf service
1999 */
2000APPLESTATIC int
2001nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2002    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2003{
2004	struct nfsv3_pathconf *pc;
2005	int getret = 1;
2006	register_t linkmax, namemax, chownres, notrunc;
2007	struct nfsvattr at;
2008
2009	if (nd->nd_repstat) {
2010		nfsrv_postopattr(nd, getret, &at);
2011		return (0);
2012	}
2013	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
2014	    nd->nd_cred, p);
2015	if (!nd->nd_repstat)
2016		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
2017		    nd->nd_cred, p);
2018	if (!nd->nd_repstat)
2019		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
2020		    &chownres, nd->nd_cred, p);
2021	if (!nd->nd_repstat)
2022		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
2023		    nd->nd_cred, p);
2024	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2025	vput(vp);
2026	nfsrv_postopattr(nd, getret, &at);
2027	if (!nd->nd_repstat) {
2028		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
2029		pc->pc_linkmax = txdr_unsigned(linkmax);
2030		pc->pc_namemax = txdr_unsigned(namemax);
2031		pc->pc_notrunc = txdr_unsigned(notrunc);
2032		pc->pc_chownrestricted = txdr_unsigned(chownres);
2033
2034		/*
2035		 * These should probably be supported by VOP_PATHCONF(), but
2036		 * until msdosfs is exportable (why would you want to?), the
2037		 * Unix defaults should be ok.
2038		 */
2039		pc->pc_caseinsensitive = newnfs_false;
2040		pc->pc_casepreserving = newnfs_true;
2041	}
2042	return (0);
2043}
2044
2045/*
2046 * nfsv4 lock service
2047 */
2048APPLESTATIC int
2049nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2050    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2051{
2052	u_int32_t *tl;
2053	int i;
2054	struct nfsstate *stp = NULL;
2055	struct nfslock *lop;
2056	struct nfslockconflict cf;
2057	int error = 0;
2058	u_short flags = NFSLCK_LOCK, lflags;
2059	u_int64_t offset, len;
2060	nfsv4stateid_t stateid;
2061	nfsquad_t clientid;
2062
2063	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2064	i = fxdr_unsigned(int, *tl++);
2065	switch (i) {
2066	case NFSV4LOCKT_READW:
2067		flags |= NFSLCK_BLOCKING;
2068	case NFSV4LOCKT_READ:
2069		lflags = NFSLCK_READ;
2070		break;
2071	case NFSV4LOCKT_WRITEW:
2072		flags |= NFSLCK_BLOCKING;
2073	case NFSV4LOCKT_WRITE:
2074		lflags = NFSLCK_WRITE;
2075		break;
2076	default:
2077		nd->nd_repstat = NFSERR_BADXDR;
2078		goto nfsmout;
2079	};
2080	if (*tl++ == newnfs_true)
2081		flags |= NFSLCK_RECLAIM;
2082	offset = fxdr_hyper(tl);
2083	tl += 2;
2084	len = fxdr_hyper(tl);
2085	tl += 2;
2086	if (*tl == newnfs_true)
2087		flags |= NFSLCK_OPENTOLOCK;
2088	if (flags & NFSLCK_OPENTOLOCK) {
2089		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
2090		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
2091		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2092			nd->nd_repstat = NFSERR_BADXDR;
2093			goto nfsmout;
2094		}
2095		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2096			M_NFSDSTATE, M_WAITOK);
2097		stp->ls_ownerlen = i;
2098		stp->ls_op = nd->nd_rp;
2099		stp->ls_seq = fxdr_unsigned(int, *tl++);
2100		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2101		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2102			NFSX_STATEIDOTHER);
2103		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2104		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
2105		clientid.lval[0] = *tl++;
2106		clientid.lval[1] = *tl++;
2107		if (nd->nd_flag & ND_IMPLIEDCLID) {
2108			if (nd->nd_clientid.qval != clientid.qval)
2109				printf("EEK! multiple clids\n");
2110		} else {
2111			nd->nd_flag |= ND_IMPLIEDCLID;
2112			nd->nd_clientid.qval = clientid.qval;
2113		}
2114		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2115		if (error)
2116			goto nfsmout;
2117	} else {
2118		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2119		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2120			M_NFSDSTATE, M_WAITOK);
2121		stp->ls_ownerlen = 0;
2122		stp->ls_op = nd->nd_rp;
2123		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2124		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2125			NFSX_STATEIDOTHER);
2126		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2127		stp->ls_seq = fxdr_unsigned(int, *tl);
2128		clientid.lval[0] = stp->ls_stateid.other[0];
2129		clientid.lval[1] = stp->ls_stateid.other[1];
2130		if (nd->nd_flag & ND_IMPLIEDCLID) {
2131			if (nd->nd_clientid.qval != clientid.qval)
2132				printf("EEK! multiple clids\n");
2133		} else {
2134			nd->nd_flag |= ND_IMPLIEDCLID;
2135			nd->nd_clientid.qval = clientid.qval;
2136		}
2137	}
2138	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2139		M_NFSDLOCK, M_WAITOK);
2140	lop->lo_first = offset;
2141	if (len == NFS64BITSSET) {
2142		lop->lo_end = NFS64BITSSET;
2143	} else {
2144		lop->lo_end = offset + len;
2145		if (lop->lo_end <= lop->lo_first)
2146			nd->nd_repstat = NFSERR_INVAL;
2147	}
2148	lop->lo_flags = lflags;
2149	stp->ls_flags = flags;
2150	stp->ls_uid = nd->nd_cred->cr_uid;
2151
2152	/*
2153	 * Do basic access checking.
2154	 */
2155	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2156	    if (vnode_vtype(vp) == VDIR)
2157		nd->nd_repstat = NFSERR_ISDIR;
2158	    else
2159		nd->nd_repstat = NFSERR_INVAL;
2160	}
2161	if (!nd->nd_repstat) {
2162	    if (lflags & NFSLCK_WRITE) {
2163		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
2164		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2165		    NFSACCCHK_VPISLOCKED, NULL);
2166	    } else {
2167		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
2168		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2169		    NFSACCCHK_VPISLOCKED, NULL);
2170		if (nd->nd_repstat)
2171		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2172			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2173			NFSACCCHK_VPISLOCKED, NULL);
2174	    }
2175	}
2176
2177	/*
2178	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
2179	 * seqid# gets updated. nfsrv_lockctrl() will return the value
2180	 * of nd_repstat, if it gets that far.
2181	 */
2182	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2183		&stateid, exp, nd, p);
2184	if (lop)
2185		FREE((caddr_t)lop, M_NFSDLOCK);
2186	if (stp)
2187		FREE((caddr_t)stp, M_NFSDSTATE);
2188	if (!nd->nd_repstat) {
2189		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2190		*tl++ = txdr_unsigned(stateid.seqid);
2191		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2192	} else if (nd->nd_repstat == NFSERR_DENIED) {
2193		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2194		txdr_hyper(cf.cl_first, tl);
2195		tl += 2;
2196		if (cf.cl_end == NFS64BITSSET)
2197			len = NFS64BITSSET;
2198		else
2199			len = cf.cl_end - cf.cl_first;
2200		txdr_hyper(len, tl);
2201		tl += 2;
2202		if (cf.cl_flags == NFSLCK_WRITE)
2203			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2204		else
2205			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2206		*tl++ = stateid.other[0];
2207		*tl = stateid.other[1];
2208		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2209	}
2210	vput(vp);
2211	return (0);
2212nfsmout:
2213	vput(vp);
2214	if (stp)
2215		free((caddr_t)stp, M_NFSDSTATE);
2216	return (error);
2217}
2218
2219/*
2220 * nfsv4 lock test service
2221 */
2222APPLESTATIC int
2223nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2224    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2225{
2226	u_int32_t *tl;
2227	int i;
2228	struct nfsstate *stp = NULL;
2229	struct nfslock lo, *lop = &lo;
2230	struct nfslockconflict cf;
2231	int error = 0;
2232	nfsv4stateid_t stateid;
2233	nfsquad_t clientid;
2234	u_int64_t len;
2235
2236	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
2237	i = fxdr_unsigned(int, *(tl + 7));
2238	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2239		nd->nd_repstat = NFSERR_BADXDR;
2240		goto nfsmout;
2241	}
2242	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2243	    M_NFSDSTATE, M_WAITOK);
2244	stp->ls_ownerlen = i;
2245	stp->ls_op = NULL;
2246	stp->ls_flags = NFSLCK_TEST;
2247	stp->ls_uid = nd->nd_cred->cr_uid;
2248	i = fxdr_unsigned(int, *tl++);
2249	switch (i) {
2250	case NFSV4LOCKT_READW:
2251		stp->ls_flags |= NFSLCK_BLOCKING;
2252	case NFSV4LOCKT_READ:
2253		lo.lo_flags = NFSLCK_READ;
2254		break;
2255	case NFSV4LOCKT_WRITEW:
2256		stp->ls_flags |= NFSLCK_BLOCKING;
2257	case NFSV4LOCKT_WRITE:
2258		lo.lo_flags = NFSLCK_WRITE;
2259		break;
2260	default:
2261		nd->nd_repstat = NFSERR_BADXDR;
2262		goto nfsmout;
2263	};
2264	lo.lo_first = fxdr_hyper(tl);
2265	tl += 2;
2266	len = fxdr_hyper(tl);
2267	if (len == NFS64BITSSET) {
2268		lo.lo_end = NFS64BITSSET;
2269	} else {
2270		lo.lo_end = lo.lo_first + len;
2271		if (lo.lo_end <= lo.lo_first)
2272			nd->nd_repstat = NFSERR_INVAL;
2273	}
2274	tl += 2;
2275	clientid.lval[0] = *tl++;
2276	clientid.lval[1] = *tl;
2277	if (nd->nd_flag & ND_IMPLIEDCLID) {
2278		if (nd->nd_clientid.qval != clientid.qval)
2279			printf("EEK! multiple clids\n");
2280	} else {
2281		nd->nd_flag |= ND_IMPLIEDCLID;
2282		nd->nd_clientid.qval = clientid.qval;
2283	}
2284	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2285	if (error)
2286		goto nfsmout;
2287	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2288	    if (vnode_vtype(vp) == VDIR)
2289		nd->nd_repstat = NFSERR_ISDIR;
2290	    else
2291		nd->nd_repstat = NFSERR_INVAL;
2292	}
2293	if (!nd->nd_repstat)
2294	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2295	    &stateid, exp, nd, p);
2296	if (stp)
2297		FREE((caddr_t)stp, M_NFSDSTATE);
2298	if (nd->nd_repstat) {
2299	    if (nd->nd_repstat == NFSERR_DENIED) {
2300		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2301		txdr_hyper(cf.cl_first, tl);
2302		tl += 2;
2303		if (cf.cl_end == NFS64BITSSET)
2304			len = NFS64BITSSET;
2305		else
2306			len = cf.cl_end - cf.cl_first;
2307		txdr_hyper(len, tl);
2308		tl += 2;
2309		if (cf.cl_flags == NFSLCK_WRITE)
2310			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2311		else
2312			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2313		*tl++ = stp->ls_stateid.other[0];
2314		*tl = stp->ls_stateid.other[1];
2315		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2316	    }
2317	}
2318	vput(vp);
2319	return (0);
2320nfsmout:
2321	vput(vp);
2322	if (stp)
2323		free((caddr_t)stp, M_NFSDSTATE);
2324	return (error);
2325}
2326
2327/*
2328 * nfsv4 unlock service
2329 */
2330APPLESTATIC int
2331nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2332    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2333{
2334	u_int32_t *tl;
2335	int i;
2336	struct nfsstate *stp;
2337	struct nfslock *lop;
2338	int error = 0;
2339	nfsv4stateid_t stateid;
2340	nfsquad_t clientid;
2341	u_int64_t len;
2342
2343	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2344	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2345	    M_NFSDSTATE, M_WAITOK);
2346	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2347	    M_NFSDLOCK, M_WAITOK);
2348	stp->ls_flags = NFSLCK_UNLOCK;
2349	lop->lo_flags = NFSLCK_UNLOCK;
2350	stp->ls_op = nd->nd_rp;
2351	i = fxdr_unsigned(int, *tl++);
2352	switch (i) {
2353	case NFSV4LOCKT_READW:
2354		stp->ls_flags |= NFSLCK_BLOCKING;
2355	case NFSV4LOCKT_READ:
2356		break;
2357	case NFSV4LOCKT_WRITEW:
2358		stp->ls_flags |= NFSLCK_BLOCKING;
2359	case NFSV4LOCKT_WRITE:
2360		break;
2361	default:
2362		nd->nd_repstat = NFSERR_BADXDR;
2363		free(stp, M_NFSDSTATE);
2364		free(lop, M_NFSDLOCK);
2365		goto nfsmout;
2366	};
2367	stp->ls_ownerlen = 0;
2368	stp->ls_uid = nd->nd_cred->cr_uid;
2369	stp->ls_seq = fxdr_unsigned(int, *tl++);
2370	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2371	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2372	    NFSX_STATEIDOTHER);
2373	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2374	lop->lo_first = fxdr_hyper(tl);
2375	tl += 2;
2376	len = fxdr_hyper(tl);
2377	if (len == NFS64BITSSET) {
2378		lop->lo_end = NFS64BITSSET;
2379	} else {
2380		lop->lo_end = lop->lo_first + len;
2381		if (lop->lo_end <= lop->lo_first)
2382			nd->nd_repstat = NFSERR_INVAL;
2383	}
2384	clientid.lval[0] = stp->ls_stateid.other[0];
2385	clientid.lval[1] = stp->ls_stateid.other[1];
2386	if (nd->nd_flag & ND_IMPLIEDCLID) {
2387		if (nd->nd_clientid.qval != clientid.qval)
2388			printf("EEK! multiple clids\n");
2389	} else {
2390		nd->nd_flag |= ND_IMPLIEDCLID;
2391		nd->nd_clientid.qval = clientid.qval;
2392	}
2393	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2394	    if (vnode_vtype(vp) == VDIR)
2395		nd->nd_repstat = NFSERR_ISDIR;
2396	    else
2397		nd->nd_repstat = NFSERR_INVAL;
2398	}
2399	/*
2400	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
2401	 * seqid# gets incremented. nfsrv_lockctrl() will return the
2402	 * value of nd_repstat, if it gets that far.
2403	 */
2404	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
2405	    &stateid, exp, nd, p);
2406	if (stp)
2407		FREE((caddr_t)stp, M_NFSDSTATE);
2408	if (lop)
2409		free((caddr_t)lop, M_NFSDLOCK);
2410	if (!nd->nd_repstat) {
2411		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2412		*tl++ = txdr_unsigned(stateid.seqid);
2413		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2414	}
2415nfsmout:
2416	vput(vp);
2417	return (error);
2418}
2419
2420/*
2421 * nfsv4 open service
2422 */
2423APPLESTATIC int
2424nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2425    vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
2426    struct nfsexstuff *exp)
2427{
2428	u_int32_t *tl;
2429	int i;
2430	struct nfsstate *stp = NULL;
2431	int error = 0, create, claim, exclusive_flag = 0;
2432	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
2433	int how = NFSCREATE_UNCHECKED;
2434	int32_t cverf[2], tverf[2] = { 0, 0 };
2435	vnode_t vp = NULL, dirp = NULL;
2436	struct nfsvattr nva, dirfor, diraft;
2437	struct nameidata named;
2438	nfsv4stateid_t stateid, delegstateid;
2439	nfsattrbit_t attrbits;
2440	nfsquad_t clientid;
2441	char *bufp = NULL;
2442	u_long *hashp;
2443	NFSACL_T *aclp = NULL;
2444
2445#ifdef NFS4_ACL_EXTATTR_NAME
2446	aclp = acl_alloc(M_WAITOK);
2447	aclp->acl_cnt = 0;
2448#endif
2449	NFSZERO_ATTRBIT(&attrbits);
2450	named.ni_startdir = NULL;
2451	named.ni_cnd.cn_nameiop = 0;
2452	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
2453	i = fxdr_unsigned(int, *(tl + 5));
2454	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2455		nd->nd_repstat = NFSERR_BADXDR;
2456		vrele(dp);
2457#ifdef NFS4_ACL_EXTATTR_NAME
2458		acl_free(aclp);
2459#endif
2460		return (0);
2461	}
2462	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2463	    M_NFSDSTATE, M_WAITOK);
2464	stp->ls_ownerlen = i;
2465	stp->ls_op = nd->nd_rp;
2466	stp->ls_flags = NFSLCK_OPEN;
2467	stp->ls_uid = nd->nd_cred->cr_uid;
2468	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2469	i = fxdr_unsigned(int, *tl++);
2470	switch (i) {
2471	case NFSV4OPEN_ACCESSREAD:
2472		stp->ls_flags |= NFSLCK_READACCESS;
2473		break;
2474	case NFSV4OPEN_ACCESSWRITE:
2475		stp->ls_flags |= NFSLCK_WRITEACCESS;
2476		break;
2477	case NFSV4OPEN_ACCESSBOTH:
2478		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
2479		break;
2480	default:
2481		nd->nd_repstat = NFSERR_INVAL;
2482	};
2483	i = fxdr_unsigned(int, *tl++);
2484	switch (i) {
2485	case NFSV4OPEN_DENYNONE:
2486		break;
2487	case NFSV4OPEN_DENYREAD:
2488		stp->ls_flags |= NFSLCK_READDENY;
2489		break;
2490	case NFSV4OPEN_DENYWRITE:
2491		stp->ls_flags |= NFSLCK_WRITEDENY;
2492		break;
2493	case NFSV4OPEN_DENYBOTH:
2494		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
2495		break;
2496	default:
2497		nd->nd_repstat = NFSERR_INVAL;
2498	};
2499	clientid.lval[0] = *tl++;
2500	clientid.lval[1] = *tl;
2501	if (nd->nd_flag & ND_IMPLIEDCLID) {
2502		if (nd->nd_clientid.qval != clientid.qval)
2503			printf("EEK! multiple clids\n");
2504	} else {
2505		nd->nd_flag |= ND_IMPLIEDCLID;
2506		nd->nd_clientid.qval = clientid.qval;
2507	}
2508	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2509	if (error) {
2510		vrele(dp);
2511#ifdef NFS4_ACL_EXTATTR_NAME
2512		acl_free(aclp);
2513#endif
2514		FREE((caddr_t)stp, M_NFSDSTATE);
2515		return (error);
2516	}
2517	NFSVNO_ATTRINIT(&nva);
2518	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2519	create = fxdr_unsigned(int, *tl);
2520	if (!nd->nd_repstat)
2521		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
2522	if (create == NFSV4OPEN_CREATE) {
2523		nva.na_type = VREG;
2524		nva.na_mode = 0;
2525		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2526		how = fxdr_unsigned(int, *tl);
2527		switch (how) {
2528		case NFSCREATE_UNCHECKED:
2529		case NFSCREATE_GUARDED:
2530			error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p);
2531			if (error) {
2532				vrele(dp);
2533#ifdef NFS4_ACL_EXTATTR_NAME
2534				acl_free(aclp);
2535#endif
2536				FREE((caddr_t)stp, M_NFSDSTATE);
2537				return (error);
2538			}
2539			/*
2540			 * If the na_gid being set is the same as that of
2541			 * the directory it is going in, clear it, since
2542			 * that is what will be set by default. This allows
2543			 * a user that isn't in that group to do the create.
2544			 */
2545			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
2546			    nva.na_gid == dirfor.na_gid)
2547				NFSVNO_UNSET(&nva, gid);
2548			if (!nd->nd_repstat)
2549				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2550			break;
2551		case NFSCREATE_EXCLUSIVE:
2552			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2553			cverf[0] = *tl++;
2554			cverf[1] = *tl;
2555			break;
2556		default:
2557			nd->nd_repstat = NFSERR_BADXDR;
2558			vrele(dp);
2559#ifdef NFS4_ACL_EXTATTR_NAME
2560			acl_free(aclp);
2561#endif
2562			FREE((caddr_t)stp, M_NFSDSTATE);
2563			return (0);
2564		};
2565	} else if (create != NFSV4OPEN_NOCREATE) {
2566		nd->nd_repstat = NFSERR_BADXDR;
2567		vrele(dp);
2568#ifdef NFS4_ACL_EXTATTR_NAME
2569		acl_free(aclp);
2570#endif
2571		FREE((caddr_t)stp, M_NFSDSTATE);
2572		return (0);
2573	}
2574
2575	/*
2576	 * Now, handle the claim, which usually includes looking up a
2577	 * name in the directory referenced by dp. The exception is
2578	 * NFSV4OPEN_CLAIMPREVIOUS.
2579	 */
2580	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2581	claim = fxdr_unsigned(int, *tl);
2582	if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
2583		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2584		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2585		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
2586		stp->ls_flags |= NFSLCK_DELEGCUR;
2587	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2588		stp->ls_flags |= NFSLCK_DELEGPREV;
2589	}
2590	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
2591	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2592		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
2593		    claim != NFSV4OPEN_CLAIMNULL)
2594			nd->nd_repstat = NFSERR_INVAL;
2595		if (nd->nd_repstat) {
2596			nd->nd_repstat = nfsrv_opencheck(clientid,
2597			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
2598			vrele(dp);
2599#ifdef NFS4_ACL_EXTATTR_NAME
2600			acl_free(aclp);
2601#endif
2602			FREE((caddr_t)stp, M_NFSDSTATE);
2603			return (0);
2604		}
2605		if (create == NFSV4OPEN_CREATE)
2606		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
2607			LOCKPARENT | LOCKLEAF | SAVESTART);
2608		else
2609		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
2610			LOCKLEAF | SAVESTART);
2611		nfsvno_setpathbuf(&named, &bufp, &hashp);
2612		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2613		if (error) {
2614			vrele(dp);
2615#ifdef NFS4_ACL_EXTATTR_NAME
2616			acl_free(aclp);
2617#endif
2618			FREE((caddr_t)stp, M_NFSDSTATE);
2619			nfsvno_relpathbuf(&named);
2620			return (error);
2621		}
2622		if (!nd->nd_repstat) {
2623			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
2624			    p, &dirp);
2625		} else {
2626			vrele(dp);
2627			nfsvno_relpathbuf(&named);
2628		}
2629		if (create == NFSV4OPEN_CREATE) {
2630		    switch (how) {
2631		    case NFSCREATE_UNCHECKED:
2632			if (named.ni_vp) {
2633				/*
2634				 * Clear the setable attribute bits, except
2635				 * for Size, if it is being truncated.
2636				 */
2637				NFSZERO_ATTRBIT(&attrbits);
2638				if (NFSVNO_ISSETSIZE(&nva))
2639					NFSSETBIT_ATTRBIT(&attrbits,
2640					    NFSATTRBIT_SIZE);
2641			}
2642			break;
2643		    case NFSCREATE_GUARDED:
2644			if (named.ni_vp && !nd->nd_repstat)
2645				nd->nd_repstat = EEXIST;
2646			break;
2647		    case NFSCREATE_EXCLUSIVE:
2648			exclusive_flag = 1;
2649			if (!named.ni_vp)
2650				nva.na_mode = 0;
2651		    };
2652		}
2653		nfsvno_open(nd, &named, clientid, &stateid, stp,
2654		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
2655		    nd->nd_cred, p, exp, &vp);
2656	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2657		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2658		i = fxdr_unsigned(int, *tl);
2659		switch (i) {
2660		case NFSV4OPEN_DELEGATEREAD:
2661			stp->ls_flags |= NFSLCK_DELEGREAD;
2662			break;
2663		case NFSV4OPEN_DELEGATEWRITE:
2664			stp->ls_flags |= NFSLCK_DELEGWRITE;
2665		case NFSV4OPEN_DELEGATENONE:
2666			break;
2667		default:
2668			nd->nd_repstat = NFSERR_BADXDR;
2669			vrele(dp);
2670#ifdef NFS4_ACL_EXTATTR_NAME
2671			acl_free(aclp);
2672#endif
2673			FREE((caddr_t)stp, M_NFSDSTATE);
2674			return (0);
2675		};
2676		stp->ls_flags |= NFSLCK_RECLAIM;
2677		vp = dp;
2678		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY, p);
2679		nd->nd_repstat = nfsrv_opencheck(clientid, &stateid, stp, vp,
2680		    nd, p, nd->nd_repstat);
2681	} else {
2682		nd->nd_repstat = NFSERR_BADXDR;
2683		vrele(dp);
2684#ifdef NFS4_ACL_EXTATTR_NAME
2685		acl_free(aclp);
2686#endif
2687		FREE((caddr_t)stp, M_NFSDSTATE);
2688		return (0);
2689	}
2690
2691	/*
2692	 * Do basic access checking.
2693	 */
2694	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2695	    if (vnode_vtype(vp) == VDIR)
2696		nd->nd_repstat = NFSERR_ISDIR;
2697	    else if (vnode_vtype(vp) == VLNK)
2698		nd->nd_repstat = NFSERR_SYMLINK;
2699	    else
2700		nd->nd_repstat = NFSERR_INVAL;
2701	}
2702	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
2703	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
2704	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2705	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
2706	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
2707	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2708	    if (nd->nd_repstat)
2709		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2710		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2711		    NFSACCCHK_VPISLOCKED, NULL);
2712	}
2713
2714	if (!nd->nd_repstat) {
2715		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
2716		if (!nd->nd_repstat) {
2717			tverf[0] = nva.na_atime.tv_sec;
2718			tverf[1] = nva.na_atime.tv_nsec;
2719		}
2720	}
2721	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
2722	    cverf[1] != tverf[1]))
2723		nd->nd_repstat = EEXIST;
2724	/*
2725	 * Do the open locking/delegation stuff.
2726	 */
2727	if (!nd->nd_repstat)
2728	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
2729		&delegstateid, &rflags, exp, p, nva.na_filerev);
2730
2731	/*
2732	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
2733	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
2734	 * (ie: Leave the NFSVOPUNLOCK() about here.)
2735	 */
2736	if (vp)
2737		NFSVOPUNLOCK(vp, 0, p);
2738	if (stp)
2739		FREE((caddr_t)stp, M_NFSDSTATE);
2740	if (!nd->nd_repstat && dirp)
2741		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p,
2742		    0);
2743	if (!nd->nd_repstat) {
2744		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
2745		*tl++ = txdr_unsigned(stateid.seqid);
2746		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2747		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2748		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2749			*tl++ = newnfs_true;
2750			*tl++ = 0;
2751			*tl++ = 0;
2752			*tl++ = 0;
2753			*tl++ = 0;
2754		} else {
2755			*tl++ = newnfs_false;	/* Since dirp is not locked */
2756			txdr_hyper(dirfor.na_filerev, tl);
2757			tl += 2;
2758			txdr_hyper(diraft.na_filerev, tl);
2759			tl += 2;
2760		}
2761		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
2762		(void) nfsrv_putattrbit(nd, &attrbits);
2763		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2764		if (rflags & NFSV4OPEN_READDELEGATE)
2765			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
2766		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
2767			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
2768		else
2769			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
2770		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
2771			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
2772			*tl++ = txdr_unsigned(delegstateid.seqid);
2773			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
2774			    NFSX_STATEIDOTHER);
2775			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2776			if (rflags & NFSV4OPEN_RECALL)
2777				*tl = newnfs_true;
2778			else
2779				*tl = newnfs_false;
2780			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
2781				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2782				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
2783				txdr_hyper(nva.na_size, tl);
2784			}
2785			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2786			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
2787			*tl++ = txdr_unsigned(0x0);
2788			acemask = NFSV4ACE_ALLFILESMASK;
2789			if (nva.na_mode & S_IRUSR)
2790			    acemask |= NFSV4ACE_READMASK;
2791			if (nva.na_mode & S_IWUSR)
2792			    acemask |= NFSV4ACE_WRITEMASK;
2793			if (nva.na_mode & S_IXUSR)
2794			    acemask |= NFSV4ACE_EXECUTEMASK;
2795			*tl = txdr_unsigned(acemask);
2796			(void) nfsm_strtom(nd, "OWNER@", 6);
2797		}
2798		*vpp = vp;
2799	} else if (vp) {
2800		vrele(vp);
2801	}
2802	if (dirp)
2803		vrele(dirp);
2804#ifdef NFS4_ACL_EXTATTR_NAME
2805	acl_free(aclp);
2806#endif
2807	return (0);
2808nfsmout:
2809	vrele(dp);
2810#ifdef NFS4_ACL_EXTATTR_NAME
2811	acl_free(aclp);
2812#endif
2813	if (stp)
2814		FREE((caddr_t)stp, M_NFSDSTATE);
2815	return (error);
2816}
2817
2818/*
2819 * nfsv4 close service
2820 */
2821APPLESTATIC int
2822nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
2823    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2824{
2825	u_int32_t *tl;
2826	struct nfsstate st, *stp = &st;
2827	int error = 0;
2828	nfsv4stateid_t stateid;
2829	nfsquad_t clientid;
2830
2831	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
2832	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2833	stp->ls_ownerlen = 0;
2834	stp->ls_op = nd->nd_rp;
2835	stp->ls_uid = nd->nd_cred->cr_uid;
2836	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2837	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2838	    NFSX_STATEIDOTHER);
2839	stp->ls_flags = NFSLCK_CLOSE;
2840	clientid.lval[0] = stp->ls_stateid.other[0];
2841	clientid.lval[1] = stp->ls_stateid.other[1];
2842	if (nd->nd_flag & ND_IMPLIEDCLID) {
2843		if (nd->nd_clientid.qval != clientid.qval)
2844			printf("EEK! multiple clids\n");
2845	} else {
2846		nd->nd_flag |= ND_IMPLIEDCLID;
2847		nd->nd_clientid.qval = clientid.qval;
2848	}
2849	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
2850	vput(vp);
2851	if (!nd->nd_repstat) {
2852		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2853		*tl++ = txdr_unsigned(stateid.seqid);
2854		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2855	}
2856	return (0);
2857nfsmout:
2858	vput(vp);
2859	return (error);
2860}
2861
2862/*
2863 * nfsv4 delegpurge service
2864 */
2865APPLESTATIC int
2866nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
2867    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
2868{
2869	u_int32_t *tl;
2870	int error = 0;
2871	nfsquad_t clientid;
2872
2873	if ((!nfs_rootfhset && !nfsv4root_set) ||
2874	    nfsd_checkrootexp(nd)) {
2875		nd->nd_repstat = NFSERR_WRONGSEC;
2876		return (0);
2877	}
2878	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2879	clientid.lval[0] = *tl++;
2880	clientid.lval[1] = *tl;
2881	if (nd->nd_flag & ND_IMPLIEDCLID) {
2882		if (nd->nd_clientid.qval != clientid.qval)
2883			printf("EEK! multiple clids\n");
2884	} else {
2885		nd->nd_flag |= ND_IMPLIEDCLID;
2886		nd->nd_clientid.qval = clientid.qval;
2887	}
2888	nd->nd_repstat = nfsrv_delegupdate(clientid, NULL, NULL,
2889	    NFSV4OP_DELEGPURGE, nd->nd_cred, p);
2890nfsmout:
2891	return (error);
2892}
2893
2894/*
2895 * nfsv4 delegreturn service
2896 */
2897APPLESTATIC int
2898nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
2899    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2900{
2901	u_int32_t *tl;
2902	int error = 0;
2903	nfsv4stateid_t stateid;
2904	nfsquad_t clientid;
2905
2906	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2907	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2908	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
2909	clientid.lval[0] = stateid.other[0];
2910	clientid.lval[1] = stateid.other[1];
2911	if (nd->nd_flag & ND_IMPLIEDCLID) {
2912		if (nd->nd_clientid.qval != clientid.qval)
2913			printf("EEK! multiple clids\n");
2914	} else {
2915		nd->nd_flag |= ND_IMPLIEDCLID;
2916		nd->nd_clientid.qval = clientid.qval;
2917	}
2918	nd->nd_repstat = nfsrv_delegupdate(clientid, &stateid, vp,
2919	    NFSV4OP_DELEGRETURN, nd->nd_cred, p);
2920nfsmout:
2921	vput(vp);
2922	return (error);
2923}
2924
2925/*
2926 * nfsv4 get file handle service
2927 */
2928APPLESTATIC int
2929nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
2930    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2931{
2932	fhandle_t fh;
2933
2934	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
2935	vput(vp);
2936	if (!nd->nd_repstat)
2937		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
2938	return (0);
2939}
2940
2941/*
2942 * nfsv4 open confirm service
2943 */
2944APPLESTATIC int
2945nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
2946    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2947{
2948	u_int32_t *tl;
2949	struct nfsstate st, *stp = &st;
2950	int error = 0;
2951	nfsv4stateid_t stateid;
2952	nfsquad_t clientid;
2953
2954	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2955	stp->ls_ownerlen = 0;
2956	stp->ls_op = nd->nd_rp;
2957	stp->ls_uid = nd->nd_cred->cr_uid;
2958	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2959	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2960	    NFSX_STATEIDOTHER);
2961	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2962	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
2963	stp->ls_flags = NFSLCK_CONFIRM;
2964	clientid.lval[0] = stp->ls_stateid.other[0];
2965	clientid.lval[1] = stp->ls_stateid.other[1];
2966	if (nd->nd_flag & ND_IMPLIEDCLID) {
2967		if (nd->nd_clientid.qval != clientid.qval)
2968			printf("EEK! multiple clids\n");
2969	} else {
2970		nd->nd_flag |= ND_IMPLIEDCLID;
2971		nd->nd_clientid.qval = clientid.qval;
2972	}
2973	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
2974	if (!nd->nd_repstat) {
2975		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2976		*tl++ = txdr_unsigned(stateid.seqid);
2977		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2978	}
2979nfsmout:
2980	vput(vp);
2981	return (error);
2982}
2983
2984/*
2985 * nfsv4 open downgrade service
2986 */
2987APPLESTATIC int
2988nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
2989    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2990{
2991	u_int32_t *tl;
2992	int i;
2993	struct nfsstate st, *stp = &st;
2994	int error = 0;
2995	nfsv4stateid_t stateid;
2996	nfsquad_t clientid;
2997
2998	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
2999	stp->ls_ownerlen = 0;
3000	stp->ls_op = nd->nd_rp;
3001	stp->ls_uid = nd->nd_cred->cr_uid;
3002	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3003	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3004	    NFSX_STATEIDOTHER);
3005	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3006	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3007	i = fxdr_unsigned(int, *tl++);
3008	switch (i) {
3009	case NFSV4OPEN_ACCESSREAD:
3010		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
3011		break;
3012	case NFSV4OPEN_ACCESSWRITE:
3013		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
3014		break;
3015	case NFSV4OPEN_ACCESSBOTH:
3016		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
3017		    NFSLCK_DOWNGRADE);
3018		break;
3019	default:
3020		nd->nd_repstat = NFSERR_BADXDR;
3021	};
3022	i = fxdr_unsigned(int, *tl);
3023	switch (i) {
3024	case NFSV4OPEN_DENYNONE:
3025		break;
3026	case NFSV4OPEN_DENYREAD:
3027		stp->ls_flags |= NFSLCK_READDENY;
3028		break;
3029	case NFSV4OPEN_DENYWRITE:
3030		stp->ls_flags |= NFSLCK_WRITEDENY;
3031		break;
3032	case NFSV4OPEN_DENYBOTH:
3033		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
3034		break;
3035	default:
3036		nd->nd_repstat = NFSERR_BADXDR;
3037	};
3038
3039	clientid.lval[0] = stp->ls_stateid.other[0];
3040	clientid.lval[1] = stp->ls_stateid.other[1];
3041	if (nd->nd_flag & ND_IMPLIEDCLID) {
3042		if (nd->nd_clientid.qval != clientid.qval)
3043			printf("EEK! multiple clids\n");
3044	} else {
3045		nd->nd_flag |= ND_IMPLIEDCLID;
3046		nd->nd_clientid.qval = clientid.qval;
3047	}
3048	if (!nd->nd_repstat)
3049		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
3050		    nd, p);
3051	if (!nd->nd_repstat) {
3052		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3053		*tl++ = txdr_unsigned(stateid.seqid);
3054		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3055	}
3056nfsmout:
3057	vput(vp);
3058	return (error);
3059}
3060
3061/*
3062 * nfsv4 renew lease service
3063 */
3064APPLESTATIC int
3065nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3066    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3067{
3068	u_int32_t *tl;
3069	int error = 0;
3070	nfsquad_t clientid;
3071
3072	if ((!nfs_rootfhset && !nfsv4root_set) ||
3073	    nfsd_checkrootexp(nd)) {
3074		nd->nd_repstat = NFSERR_WRONGSEC;
3075		return (0);
3076	}
3077	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
3078	clientid.lval[0] = *tl++;
3079	clientid.lval[1] = *tl;
3080	if (nd->nd_flag & ND_IMPLIEDCLID) {
3081		if (nd->nd_clientid.qval != clientid.qval)
3082			printf("EEK! multiple clids\n");
3083	} else {
3084		nd->nd_flag |= ND_IMPLIEDCLID;
3085		nd->nd_clientid.qval = clientid.qval;
3086	}
3087	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3088	    NULL, (nfsquad_t)((u_quad_t)0), nd, p);
3089nfsmout:
3090	return (error);
3091}
3092
3093/*
3094 * nfsv4 security info service
3095 */
3096APPLESTATIC int
3097nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3098    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
3099{
3100	u_int32_t *tl;
3101	int len;
3102	struct nameidata named;
3103	vnode_t dirp = NULL, vp;
3104	struct nfsrvfh fh;
3105	struct nfsexstuff retnes;
3106	mount_t mp;
3107	u_int32_t *sizp;
3108	int error, savflag, i;
3109	char *bufp;
3110	u_long *hashp;
3111
3112	/*
3113	 * All this just to get the export flags for the name.
3114	 */
3115	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
3116	    LOCKLEAF | SAVESTART);
3117	nfsvno_setpathbuf(&named, &bufp, &hashp);
3118	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3119	if (error) {
3120		vput(dp);
3121		nfsvno_relpathbuf(&named);
3122		return (error);
3123	}
3124	if (!nd->nd_repstat) {
3125		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
3126	} else {
3127		vput(dp);
3128		nfsvno_relpathbuf(&named);
3129	}
3130	if (dirp)
3131		vrele(dirp);
3132	if (nd->nd_repstat)
3133		return (0);
3134	vrele(named.ni_startdir);
3135	nfsvno_relpathbuf(&named);
3136	fh.nfsrvfh_len = NFSX_MYFH;
3137	vp = named.ni_vp;
3138	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3139	mp = vnode_mount(vp);	/* so it won't try to re-lock filesys */
3140	retnes.nes_vfslocked = exp->nes_vfslocked;
3141	vput(vp);
3142	savflag = nd->nd_flag;
3143	if (!nd->nd_repstat) {
3144		nfsd_fhtovp(nd, &fh, &vp, &retnes, &mp, 0, p);
3145		if (vp)
3146			vput(vp);
3147	}
3148	nd->nd_flag = savflag;
3149	if (nd->nd_repstat)
3150		return (0);
3151
3152	/*
3153	 * Finally have the export flags for name, so we can create
3154	 * the security info.
3155	 */
3156	len = 0;
3157	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
3158	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3159		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3160			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3161			*tl = txdr_unsigned(RPCAUTH_UNIX);
3162			len++;
3163		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3164			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3165			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3166			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3167			    nfsgss_mechlist[KERBV_MECH].len);
3168			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3169			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3170			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3171			len++;
3172		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
3173			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3174			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3175			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3176			    nfsgss_mechlist[KERBV_MECH].len);
3177			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3178			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3179			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3180			len++;
3181		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
3182			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3183			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3184			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3185			    nfsgss_mechlist[KERBV_MECH].len);
3186			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3187			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3188			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3189			len++;
3190		}
3191	}
3192	*sizp = txdr_unsigned(len);
3193	return (0);
3194}
3195
3196/*
3197 * nfsv4 set client id service
3198 */
3199APPLESTATIC int
3200nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3201    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3202{
3203	u_int32_t *tl;
3204	int i;
3205	int error = 0, idlen;
3206	struct nfsclient *clp = NULL;
3207	struct sockaddr_in *rad;
3208	u_char *verf, *ucp, *ucp2, addrbuf[24];
3209	nfsquad_t clientid, confirm;
3210
3211	if ((!nfs_rootfhset && !nfsv4root_set) ||
3212	    nfsd_checkrootexp(nd)) {
3213		nd->nd_repstat = NFSERR_WRONGSEC;
3214		return (0);
3215	}
3216	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3217	verf = (u_char *)tl;
3218	tl += (NFSX_VERF / NFSX_UNSIGNED);
3219	i = fxdr_unsigned(int, *tl);
3220	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3221		nd->nd_repstat = NFSERR_BADXDR;
3222		return (error);
3223	}
3224	idlen = i;
3225	if (nd->nd_flag & ND_GSS)
3226		i += nd->nd_princlen;
3227	MALLOC(clp, struct nfsclient *, sizeof (struct nfsclient) + i,
3228	    M_NFSDCLIENT, M_WAITOK);
3229	NFSBZERO((caddr_t)clp, sizeof (struct nfsclient) + i);
3230	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3231	NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
3232	NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
3233	clp->lc_req.nr_cred = NULL;
3234	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3235	clp->lc_idlen = idlen;
3236	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3237	if (error)
3238		goto nfsmout;
3239	if (nd->nd_flag & ND_GSS) {
3240		clp->lc_flags = LCL_GSS;
3241		if (nd->nd_flag & ND_GSSINTEGRITY)
3242			clp->lc_flags |= LCL_GSSINTEGRITY;
3243		else if (nd->nd_flag & ND_GSSPRIVACY)
3244			clp->lc_flags |= LCL_GSSPRIVACY;
3245	} else {
3246		clp->lc_flags = 0;
3247	}
3248	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
3249		clp->lc_flags |= LCL_NAME;
3250		clp->lc_namelen = nd->nd_princlen;
3251		clp->lc_name = &clp->lc_id[idlen];
3252		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3253	} else {
3254		clp->lc_uid = nd->nd_cred->cr_uid;
3255		clp->lc_gid = nd->nd_cred->cr_gid;
3256	}
3257	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3258	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
3259	error = nfsrv_getclientipaddr(nd, clp);
3260	if (error)
3261		goto nfsmout;
3262	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3263	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
3264
3265	/*
3266	 * nfsrv_setclient() does the actual work of adding it to the
3267	 * client list. If there is no error, the structure has been
3268	 * linked into the client list and clp should no longer be used
3269	 * here. When an error is returned, it has not been linked in,
3270	 * so it should be free'd.
3271	 */
3272	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3273	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
3274		if (clp->lc_flags & LCL_TCPCALLBACK)
3275			(void) nfsm_strtom(nd, "tcp", 3);
3276		else
3277			(void) nfsm_strtom(nd, "udp", 3);
3278		rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *);
3279		ucp = (u_char *)&rad->sin_addr.s_addr;
3280		ucp2 = (u_char *)&rad->sin_port;
3281		sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
3282		    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
3283		    ucp2[0] & 0xff, ucp2[1] & 0xff);
3284		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
3285	}
3286	if (clp) {
3287		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3288		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3289		free((caddr_t)clp, M_NFSDCLIENT);
3290	}
3291	if (!nd->nd_repstat) {
3292		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
3293		*tl++ = clientid.lval[0];
3294		*tl++ = clientid.lval[1];
3295		*tl++ = confirm.lval[0];
3296		*tl = confirm.lval[1];
3297	}
3298	return (0);
3299nfsmout:
3300	if (clp) {
3301		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3302		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3303		free((caddr_t)clp, M_NFSDCLIENT);
3304	}
3305	return (error);
3306}
3307
3308/*
3309 * nfsv4 set client id confirm service
3310 */
3311APPLESTATIC int
3312nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
3313    __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
3314    __unused struct nfsexstuff *exp)
3315{
3316	u_int32_t *tl;
3317	int error = 0;
3318	nfsquad_t clientid, confirm;
3319
3320	if ((!nfs_rootfhset && !nfsv4root_set) ||
3321	    nfsd_checkrootexp(nd)) {
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 && !nfsv4root_set) ||
3409	    nfsd_checkrootexp(nd)) {
3410		nd->nd_repstat = NFSERR_WRONGSEC;
3411		return (0);
3412	}
3413	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3414	len = fxdr_unsigned(int, *(tl + 2));
3415	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
3416		nd->nd_repstat = NFSERR_BADXDR;
3417		return (0);
3418	}
3419	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len,
3420	    M_NFSDSTATE, M_WAITOK);
3421	stp->ls_ownerlen = len;
3422	stp->ls_op = NULL;
3423	stp->ls_flags = NFSLCK_RELEASE;
3424	stp->ls_uid = nd->nd_cred->cr_uid;
3425	clientid.lval[0] = *tl++;
3426	clientid.lval[1] = *tl;
3427	if (nd->nd_flag & ND_IMPLIEDCLID) {
3428		if (nd->nd_clientid.qval != clientid.qval)
3429			printf("EEK! multiple clids\n");
3430	} else {
3431		nd->nd_flag |= ND_IMPLIEDCLID;
3432		nd->nd_clientid.qval = clientid.qval;
3433	}
3434	error = nfsrv_mtostr(nd, stp->ls_owner, len);
3435	if (error)
3436		goto nfsmout;
3437	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
3438	FREE((caddr_t)stp, M_NFSDSTATE);
3439	return (0);
3440nfsmout:
3441	if (stp)
3442		free((caddr_t)stp, M_NFSDSTATE);
3443	return (error);
3444}
3445