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