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