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