xref: /freebsd/sys/fs/nfs/nfs_commonsubs.c (revision 8ef24a0d4b28fe230e20637f56869cc4148cd2ca)
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$");
36 
37 /*
38  * These functions support the macros and help fiddle mbuf chains for
39  * the nfs op functions. They do things like create the rpc header and
40  * copy data between mbuf chains and uio lists.
41  */
42 #ifndef APPLEKEXT
43 #include "opt_inet6.h"
44 
45 #include <fs/nfs/nfsport.h>
46 
47 #include <security/mac/mac_framework.h>
48 
49 /*
50  * Data items converted to xdr at startup, since they are constant
51  * This is kinda hokey, but may save a little time doing byte swaps
52  */
53 u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
54 
55 /* And other global data */
56 nfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
57 		      NFFIFO, NFNON };
58 enum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
59 enum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
60 struct timeval nfsboottime;	/* Copy boottime once, so it never changes */
61 int nfscl_ticks;
62 int nfsrv_useacl = 1;
63 struct nfssockreq nfsrv_nfsuserdsock;
64 int nfsrv_nfsuserd = 0;
65 struct nfsreqhead nfsd_reqq;
66 uid_t nfsrv_defaultuid;
67 gid_t nfsrv_defaultgid;
68 int nfsrv_lease = NFSRV_LEASE;
69 int ncl_mbuf_mlen = MLEN;
70 int nfsd_enable_stringtouid = 0;
71 NFSNAMEIDMUTEX;
72 NFSSOCKMUTEX;
73 extern int nfsrv_lughashsize;
74 
75 /*
76  * This array of structures indicates, for V4:
77  * retfh - which of 3 types of calling args are used
78  *	0 - doesn't change cfh or use a sfh
79  *	1 - replaces cfh with a new one (unless it returns an error status)
80  *	2 - uses cfh and sfh
81  * needscfh - if the op wants a cfh and premtime
82  *	0 - doesn't use a cfh
83  *	1 - uses a cfh, but doesn't want pre-op attributes
84  *	2 - uses a cfh and wants pre-op attributes
85  * savereply - indicates a non-idempotent Op
86  *	0 - not non-idempotent
87  *	1 - non-idempotent
88  * Ops that are ordered via seqid# are handled separately from these
89  * non-idempotent Ops.
90  * Define it here, since it is used by both the client and server.
91  */
92 struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS] = {
93 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
94 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
95 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
96 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Access */
97 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Close */
98 	{ 0, 2, 0, 1, LK_EXCLUSIVE, 1 },		/* Commit */
99 	{ 1, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Create */
100 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegpurge */
101 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegreturn */
102 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Getattr */
103 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* GetFH */
104 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Link */
105 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Lock */
106 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockT */
107 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockU */
108 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookup */
109 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookupp */
110 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* NVerify */
111 	{ 1, 1, 0, 1, LK_EXCLUSIVE, 1 },		/* Open */
112 	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenAttr */
113 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenConfirm */
114 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenDowngrade */
115 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutFH */
116 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutPubFH */
117 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutRootFH */
118 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Read */
119 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Readdir */
120 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* ReadLink */
121 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Remove */
122 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Rename */
123 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Renew */
124 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* RestoreFH */
125 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SaveFH */
126 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SecInfo */
127 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Setattr */
128 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientID */
129 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientIDConfirm */
130 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Verify */
131 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Write */
132 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* ReleaseLockOwner */
133 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Backchannel Ctrl */
134 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Bind Conn to Sess */
135 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Exchange ID */
136 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Create Session */
137 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy Session */
138 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Free StateID */
139 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Dir Deleg */
140 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device Info */
141 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device List */
142 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Commit */
143 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Get */
144 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Return */
145 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Secinfo No name */
146 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Sequence */
147 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Set SSV */
148 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Test StateID */
149 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Want Delegation */
150 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy ClientID */
151 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Reclaim Complete */
152 };
153 #endif	/* !APPLEKEXT */
154 
155 static int ncl_mbuf_mhlen = MHLEN;
156 static int nfsrv_usercnt = 0;
157 static int nfsrv_dnsnamelen;
158 static u_char *nfsrv_dnsname = NULL;
159 static int nfsrv_usermax = 999999999;
160 struct nfsrv_lughash {
161 	struct mtx		mtx;
162 	struct nfsuserhashhead	lughead;
163 };
164 static struct nfsrv_lughash	*nfsuserhash;
165 static struct nfsrv_lughash	*nfsusernamehash;
166 static struct nfsrv_lughash	*nfsgrouphash;
167 static struct nfsrv_lughash	*nfsgroupnamehash;
168 
169 /*
170  * This static array indicates whether or not the RPC generates a large
171  * reply. This is used by nfs_reply() to decide whether or not an mbuf
172  * cluster should be allocated. (If a cluster is required by an RPC
173  * marked 0 in this array, the code will still work, just not quite as
174  * efficiently.)
175  */
176 int nfs_bigreply[NFSV41_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
177     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
178     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
179 
180 /* local functions */
181 static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
182 static void nfsv4_wanted(struct nfsv4lock *lp);
183 static int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
184 static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
185     NFSPROC_T *p);
186 static void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
187 static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
188     int *, int *);
189 static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
190 
191 
192 #ifndef APPLE
193 /*
194  * copies mbuf chain to the uio scatter/gather list
195  */
196 int
197 nfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
198 {
199 	char *mbufcp, *uiocp;
200 	int xfer, left, len;
201 	mbuf_t mp;
202 	long uiosiz, rem;
203 	int error = 0;
204 
205 	mp = nd->nd_md;
206 	mbufcp = nd->nd_dpos;
207 	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - mbufcp;
208 	rem = NFSM_RNDUP(siz) - siz;
209 	while (siz > 0) {
210 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) {
211 			error = EBADRPC;
212 			goto out;
213 		}
214 		left = uiop->uio_iov->iov_len;
215 		uiocp = uiop->uio_iov->iov_base;
216 		if (left > siz)
217 			left = siz;
218 		uiosiz = left;
219 		while (left > 0) {
220 			while (len == 0) {
221 				mp = mbuf_next(mp);
222 				if (mp == NULL) {
223 					error = EBADRPC;
224 					goto out;
225 				}
226 				mbufcp = NFSMTOD(mp, caddr_t);
227 				len = mbuf_len(mp);
228 				KASSERT(len >= 0,
229 				    ("len %d, corrupted mbuf?", len));
230 			}
231 			xfer = (left > len) ? len : left;
232 #ifdef notdef
233 			/* Not Yet.. */
234 			if (uiop->uio_iov->iov_op != NULL)
235 				(*(uiop->uio_iov->iov_op))
236 				(mbufcp, uiocp, xfer);
237 			else
238 #endif
239 			if (uiop->uio_segflg == UIO_SYSSPACE)
240 				NFSBCOPY(mbufcp, uiocp, xfer);
241 			else
242 				copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
243 			left -= xfer;
244 			len -= xfer;
245 			mbufcp += xfer;
246 			uiocp += xfer;
247 			uiop->uio_offset += xfer;
248 			uiop->uio_resid -= xfer;
249 		}
250 		if (uiop->uio_iov->iov_len <= siz) {
251 			uiop->uio_iovcnt--;
252 			uiop->uio_iov++;
253 		} else {
254 			uiop->uio_iov->iov_base = (void *)
255 				((char *)uiop->uio_iov->iov_base + uiosiz);
256 			uiop->uio_iov->iov_len -= uiosiz;
257 		}
258 		siz -= uiosiz;
259 	}
260 	nd->nd_dpos = mbufcp;
261 	nd->nd_md = mp;
262 	if (rem > 0) {
263 		if (len < rem)
264 			error = nfsm_advance(nd, rem, len);
265 		else
266 			nd->nd_dpos += rem;
267 	}
268 
269 out:
270 	NFSEXITCODE2(error, nd);
271 	return (error);
272 }
273 #endif	/* !APPLE */
274 
275 /*
276  * Help break down an mbuf chain by setting the first siz bytes contiguous
277  * pointed to by returned val.
278  * This is used by the macro NFSM_DISSECT for tough
279  * cases.
280  */
281 APPLESTATIC void *
282 nfsm_dissct(struct nfsrv_descript *nd, int siz, int how)
283 {
284 	mbuf_t mp2;
285 	int siz2, xfer;
286 	caddr_t p;
287 	int left;
288 	caddr_t retp;
289 
290 	retp = NULL;
291 	left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
292 	while (left == 0) {
293 		nd->nd_md = mbuf_next(nd->nd_md);
294 		if (nd->nd_md == NULL)
295 			return (retp);
296 		left = mbuf_len(nd->nd_md);
297 		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
298 	}
299 	if (left >= siz) {
300 		retp = nd->nd_dpos;
301 		nd->nd_dpos += siz;
302 	} else if (mbuf_next(nd->nd_md) == NULL) {
303 		return (retp);
304 	} else if (siz > ncl_mbuf_mhlen) {
305 		panic("nfs S too big");
306 	} else {
307 		MGET(mp2, MT_DATA, how);
308 		if (mp2 == NULL)
309 			return (NULL);
310 		mbuf_setnext(mp2, mbuf_next(nd->nd_md));
311 		mbuf_setnext(nd->nd_md, mp2);
312 		mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
313 		nd->nd_md = mp2;
314 		retp = p = NFSMTOD(mp2, caddr_t);
315 		NFSBCOPY(nd->nd_dpos, p, left);	/* Copy what was left */
316 		siz2 = siz - left;
317 		p += left;
318 		mp2 = mbuf_next(mp2);
319 		/* Loop around copying up the siz2 bytes */
320 		while (siz2 > 0) {
321 			if (mp2 == NULL)
322 				return (NULL);
323 			xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
324 			if (xfer > 0) {
325 				NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
326 				NFSM_DATAP(mp2, xfer);
327 				mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
328 				p += xfer;
329 				siz2 -= xfer;
330 			}
331 			if (siz2 > 0)
332 				mp2 = mbuf_next(mp2);
333 		}
334 		mbuf_setlen(nd->nd_md, siz);
335 		nd->nd_md = mp2;
336 		nd->nd_dpos = NFSMTOD(mp2, caddr_t);
337 	}
338 	return (retp);
339 }
340 
341 /*
342  * Advance the position in the mbuf chain.
343  * If offs == 0, this is a no-op, but it is simpler to just return from
344  * here than check for offs > 0 for all calls to nfsm_advance.
345  * If left == -1, it should be calculated here.
346  */
347 APPLESTATIC int
348 nfsm_advance(struct nfsrv_descript *nd, int offs, int left)
349 {
350 	int error = 0;
351 
352 	if (offs == 0)
353 		goto out;
354 	/*
355 	 * A negative offs should be considered a serious problem.
356 	 */
357 	if (offs < 0)
358 		panic("nfsrv_advance");
359 
360 	/*
361 	 * If left == -1, calculate it here.
362 	 */
363 	if (left == -1)
364 		left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
365 		    nd->nd_dpos;
366 
367 	/*
368 	 * Loop around, advancing over the mbuf data.
369 	 */
370 	while (offs > left) {
371 		offs -= left;
372 		nd->nd_md = mbuf_next(nd->nd_md);
373 		if (nd->nd_md == NULL) {
374 			error = EBADRPC;
375 			goto out;
376 		}
377 		left = mbuf_len(nd->nd_md);
378 		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
379 	}
380 	nd->nd_dpos += offs;
381 
382 out:
383 	NFSEXITCODE(error);
384 	return (error);
385 }
386 
387 /*
388  * Copy a string into mbuf(s).
389  * Return the number of bytes output, including XDR overheads.
390  */
391 APPLESTATIC int
392 nfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
393 {
394 	mbuf_t m2;
395 	int xfer, left;
396 	mbuf_t m1;
397 	int rem, bytesize;
398 	u_int32_t *tl;
399 	char *cp2;
400 
401 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
402 	*tl = txdr_unsigned(siz);
403 	rem = NFSM_RNDUP(siz) - siz;
404 	bytesize = NFSX_UNSIGNED + siz + rem;
405 	m2 = nd->nd_mb;
406 	cp2 = nd->nd_bpos;
407 	left = M_TRAILINGSPACE(m2);
408 
409 	/*
410 	 * Loop around copying the string to mbuf(s).
411 	 */
412 	while (siz > 0) {
413 		if (left == 0) {
414 			if (siz > ncl_mbuf_mlen)
415 				NFSMCLGET(m1, M_WAITOK);
416 			else
417 				NFSMGET(m1);
418 			mbuf_setlen(m1, 0);
419 			mbuf_setnext(m2, m1);
420 			m2 = m1;
421 			cp2 = NFSMTOD(m2, caddr_t);
422 			left = M_TRAILINGSPACE(m2);
423 		}
424 		if (left >= siz)
425 			xfer = siz;
426 		else
427 			xfer = left;
428 		NFSBCOPY(cp, cp2, xfer);
429 		cp += xfer;
430 		mbuf_setlen(m2, mbuf_len(m2) + xfer);
431 		siz -= xfer;
432 		left -= xfer;
433 		if (siz == 0 && rem) {
434 			if (left < rem)
435 				panic("nfsm_strtom");
436 			NFSBZERO(cp2 + xfer, rem);
437 			mbuf_setlen(m2, mbuf_len(m2) + rem);
438 		}
439 	}
440 	nd->nd_mb = m2;
441 	nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
442 	return (bytesize);
443 }
444 
445 /*
446  * Called once to initialize data structures...
447  */
448 APPLESTATIC void
449 newnfs_init(void)
450 {
451 	static int nfs_inited = 0;
452 
453 	if (nfs_inited)
454 		return;
455 	nfs_inited = 1;
456 
457 	newnfs_true = txdr_unsigned(TRUE);
458 	newnfs_false = txdr_unsigned(FALSE);
459 	newnfs_xdrneg1 = txdr_unsigned(-1);
460 	nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
461 	if (nfscl_ticks < 1)
462 		nfscl_ticks = 1;
463 	NFSSETBOOTTIME(nfsboottime);
464 
465 	/*
466 	 * Initialize reply list and start timer
467 	 */
468 	TAILQ_INIT(&nfsd_reqq);
469 	NFS_TIMERINIT;
470 }
471 
472 /*
473  * Put a file handle in an mbuf list.
474  * If the size argument == 0, just use the default size.
475  * set_true == 1 if there should be an newnfs_true prepended on the file handle.
476  * Return the number of bytes output, including XDR overhead.
477  */
478 APPLESTATIC int
479 nfsm_fhtom(struct nfsrv_descript *nd, u_int8_t *fhp, int size, int set_true)
480 {
481 	u_int32_t *tl;
482 	u_int8_t *cp;
483 	int fullsiz, rem, bytesize = 0;
484 
485 	if (size == 0)
486 		size = NFSX_MYFH;
487 	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
488 	case ND_NFSV2:
489 		if (size > NFSX_V2FH)
490 			panic("fh size > NFSX_V2FH for NFSv2");
491 		NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
492 		NFSBCOPY(fhp, cp, size);
493 		if (size < NFSX_V2FH)
494 			NFSBZERO(cp + size, NFSX_V2FH - size);
495 		bytesize = NFSX_V2FH;
496 		break;
497 	case ND_NFSV3:
498 	case ND_NFSV4:
499 		fullsiz = NFSM_RNDUP(size);
500 		rem = fullsiz - size;
501 		if (set_true) {
502 		    bytesize = 2 * NFSX_UNSIGNED + fullsiz;
503 		    NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
504 		    *tl = newnfs_true;
505 		} else {
506 		    bytesize = NFSX_UNSIGNED + fullsiz;
507 		}
508 		(void) nfsm_strtom(nd, fhp, size);
509 		break;
510 	}
511 	return (bytesize);
512 }
513 
514 /*
515  * This function compares two net addresses by family and returns TRUE
516  * if they are the same host.
517  * If there is any doubt, return FALSE.
518  * The AF_INET family is handled as a special case so that address mbufs
519  * don't need to be saved to store "struct in_addr", which is only 4 bytes.
520  */
521 APPLESTATIC int
522 nfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
523 {
524 	struct sockaddr_in *inetaddr;
525 
526 	switch (family) {
527 	case AF_INET:
528 		inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
529 		if (inetaddr->sin_family == AF_INET &&
530 		    inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
531 			return (1);
532 		break;
533 #ifdef INET6
534 	case AF_INET6:
535 		{
536 		struct sockaddr_in6 *inetaddr6;
537 
538 		inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
539 		/* XXX - should test sin6_scope_id ? */
540 		if (inetaddr6->sin6_family == AF_INET6 &&
541 		    IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
542 			  &haddr->had_inet6))
543 			return (1);
544 		}
545 		break;
546 #endif
547 	}
548 	return (0);
549 }
550 
551 /*
552  * Similar to the above, but takes to NFSSOCKADDR_T args.
553  */
554 APPLESTATIC int
555 nfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
556 {
557 	struct sockaddr_in *addr1, *addr2;
558 	struct sockaddr *inaddr;
559 
560 	inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
561 	switch (inaddr->sa_family) {
562 	case AF_INET:
563 		addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
564 		addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
565 		if (addr2->sin_family == AF_INET &&
566 		    addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
567 			return (1);
568 		break;
569 #ifdef INET6
570 	case AF_INET6:
571 		{
572 		struct sockaddr_in6 *inet6addr1, *inet6addr2;
573 
574 		inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
575 		inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
576 		/* XXX - should test sin6_scope_id ? */
577 		if (inet6addr2->sin6_family == AF_INET6 &&
578 		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
579 			  &inet6addr2->sin6_addr))
580 			return (1);
581 		}
582 		break;
583 #endif
584 	}
585 	return (0);
586 }
587 
588 
589 /*
590  * Trim the stuff already dissected off the mbuf list.
591  */
592 APPLESTATIC void
593 newnfs_trimleading(nd)
594 	struct nfsrv_descript *nd;
595 {
596 	mbuf_t m, n;
597 	int offs;
598 
599 	/*
600 	 * First, free up leading mbufs.
601 	 */
602 	if (nd->nd_mrep != nd->nd_md) {
603 		m = nd->nd_mrep;
604 		while (mbuf_next(m) != nd->nd_md) {
605 			if (mbuf_next(m) == NULL)
606 				panic("nfsm trim leading");
607 			m = mbuf_next(m);
608 		}
609 		mbuf_setnext(m, NULL);
610 		mbuf_freem(nd->nd_mrep);
611 	}
612 	m = nd->nd_md;
613 
614 	/*
615 	 * Now, adjust this mbuf, based on nd_dpos.
616 	 */
617 	offs = nd->nd_dpos - NFSMTOD(m, caddr_t);
618 	if (offs == mbuf_len(m)) {
619 		n = m;
620 		m = mbuf_next(m);
621 		if (m == NULL)
622 			panic("nfsm trim leading2");
623 		mbuf_setnext(n, NULL);
624 		mbuf_freem(n);
625 	} else if (offs > 0) {
626 		mbuf_setlen(m, mbuf_len(m) - offs);
627 		NFSM_DATAP(m, offs);
628 	} else if (offs < 0)
629 		panic("nfsm trimleading offs");
630 	nd->nd_mrep = m;
631 	nd->nd_md = m;
632 	nd->nd_dpos = NFSMTOD(m, caddr_t);
633 }
634 
635 /*
636  * Trim trailing data off the mbuf list being built.
637  */
638 APPLESTATIC void
639 newnfs_trimtrailing(nd, mb, bpos)
640 	struct nfsrv_descript *nd;
641 	mbuf_t mb;
642 	caddr_t bpos;
643 {
644 
645 	if (mbuf_next(mb)) {
646 		mbuf_freem(mbuf_next(mb));
647 		mbuf_setnext(mb, NULL);
648 	}
649 	mbuf_setlen(mb, bpos - NFSMTOD(mb, caddr_t));
650 	nd->nd_mb = mb;
651 	nd->nd_bpos = bpos;
652 }
653 
654 /*
655  * Dissect a file handle on the client.
656  */
657 APPLESTATIC int
658 nfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
659 {
660 	u_int32_t *tl;
661 	struct nfsfh *nfhp;
662 	int error, len;
663 
664 	*nfhpp = NULL;
665 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
666 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
667 		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
668 			len > NFSX_FHMAX) {
669 			error = EBADRPC;
670 			goto nfsmout;
671 		}
672 	} else
673 		len = NFSX_V2FH;
674 	MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + len,
675 	    M_NFSFH, M_WAITOK);
676 	error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
677 	if (error) {
678 		FREE((caddr_t)nfhp, M_NFSFH);
679 		goto nfsmout;
680 	}
681 	nfhp->nfh_len = len;
682 	*nfhpp = nfhp;
683 nfsmout:
684 	NFSEXITCODE2(error, nd);
685 	return (error);
686 }
687 
688 /*
689  * Break down the nfsv4 acl.
690  * If the aclp == NULL or won't fit in an acl, just discard the acl info.
691  */
692 APPLESTATIC int
693 nfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, int *aclerrp,
694     int *aclsizep, __unused NFSPROC_T *p)
695 {
696 	u_int32_t *tl;
697 	int i, aclsize;
698 	int acecnt, error = 0, aceerr = 0, acesize;
699 
700 	*aclerrp = 0;
701 	if (aclp)
702 		aclp->acl_cnt = 0;
703 	/*
704 	 * Parse out the ace entries and expect them to conform to
705 	 * what can be supported by R/W/X bits.
706 	 */
707 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
708 	aclsize = NFSX_UNSIGNED;
709 	acecnt = fxdr_unsigned(int, *tl);
710 	if (acecnt > ACL_MAX_ENTRIES)
711 		aceerr = NFSERR_ATTRNOTSUPP;
712 	if (nfsrv_useacl == 0)
713 		aceerr = NFSERR_ATTRNOTSUPP;
714 	for (i = 0; i < acecnt; i++) {
715 		if (aclp && !aceerr)
716 			error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
717 			    &aceerr, &acesize, p);
718 		else
719 			error = nfsrv_skipace(nd, &acesize);
720 		if (error)
721 			goto nfsmout;
722 		aclsize += acesize;
723 	}
724 	if (aclp && !aceerr)
725 		aclp->acl_cnt = acecnt;
726 	if (aceerr)
727 		*aclerrp = aceerr;
728 	if (aclsizep)
729 		*aclsizep = aclsize;
730 nfsmout:
731 	NFSEXITCODE2(error, nd);
732 	return (error);
733 }
734 
735 /*
736  * Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
737  */
738 static int
739 nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
740 {
741 	u_int32_t *tl;
742 	int error, len = 0;
743 
744 	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
745 	len = fxdr_unsigned(int, *(tl + 3));
746 	error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
747 nfsmout:
748 	*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
749 	NFSEXITCODE2(error, nd);
750 	return (error);
751 }
752 
753 /*
754  * Get attribute bits from an mbuf list.
755  * Returns EBADRPC for a parsing error, 0 otherwise.
756  * If the clearinvalid flag is set, clear the bits not supported.
757  */
758 APPLESTATIC int
759 nfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
760     int *retnotsupp)
761 {
762 	u_int32_t *tl;
763 	int cnt, i, outcnt;
764 	int error = 0;
765 
766 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
767 	cnt = fxdr_unsigned(int, *tl);
768 	if (cnt < 0) {
769 		error = NFSERR_BADXDR;
770 		goto nfsmout;
771 	}
772 	if (cnt > NFSATTRBIT_MAXWORDS)
773 		outcnt = NFSATTRBIT_MAXWORDS;
774 	else
775 		outcnt = cnt;
776 	NFSZERO_ATTRBIT(attrbitp);
777 	if (outcnt > 0) {
778 		NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
779 		for (i = 0; i < outcnt; i++)
780 			attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
781 	}
782 	for (i = 0; i < (cnt - outcnt); i++) {
783 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
784 		if (retnotsupp != NULL && *tl != 0)
785 			*retnotsupp = NFSERR_ATTRNOTSUPP;
786 	}
787 	if (cntp)
788 		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
789 nfsmout:
790 	NFSEXITCODE2(error, nd);
791 	return (error);
792 }
793 
794 /*
795  * Get the attributes for V4.
796  * If the compare flag is true, test for any attribute changes,
797  * otherwise return the attribute values.
798  * These attributes cover fields in "struct vattr", "struct statfs",
799  * "struct nfsfsinfo", the file handle and the lease duration.
800  * The value of retcmpp is set to 1 if all attributes are the same,
801  * and 0 otherwise.
802  * Returns EBADRPC if it can't be parsed, 0 otherwise.
803  */
804 APPLESTATIC int
805 nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
806     struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
807     struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
808     struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
809     u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
810 {
811 	u_int32_t *tl;
812 	int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
813 	int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
814 	u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
815 	nfsattrbit_t attrbits, retattrbits, checkattrbits;
816 	struct nfsfh *tnfhp;
817 	struct nfsreferral *refp;
818 	u_quad_t tquad;
819 	nfsquad_t tnfsquad;
820 	struct timespec temptime;
821 	uid_t uid;
822 	gid_t gid;
823 	long fid;
824 	u_int32_t freenum = 0, tuint;
825 	u_int64_t uquad = 0, thyp, thyp2;
826 #ifdef QUOTA
827 	struct dqblk dqb;
828 	uid_t savuid;
829 #endif
830 	if (compare) {
831 		retnotsup = 0;
832 		error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
833 	} else {
834 		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
835 	}
836 	if (error)
837 		goto nfsmout;
838 
839 	if (compare) {
840 		*retcmpp = retnotsup;
841 	} else {
842 		/*
843 		 * Just set default values to some of the important ones.
844 		 */
845 		if (nap != NULL) {
846 			nap->na_type = VREG;
847 			nap->na_mode = 0;
848 			nap->na_rdev = (NFSDEV_T)0;
849 			nap->na_mtime.tv_sec = 0;
850 			nap->na_mtime.tv_nsec = 0;
851 			nap->na_gen = 0;
852 			nap->na_flags = 0;
853 			nap->na_blocksize = NFS_FABLKSIZE;
854 		}
855 		if (sbp != NULL) {
856 			sbp->f_bsize = NFS_FABLKSIZE;
857 			sbp->f_blocks = 0;
858 			sbp->f_bfree = 0;
859 			sbp->f_bavail = 0;
860 			sbp->f_files = 0;
861 			sbp->f_ffree = 0;
862 		}
863 		if (fsp != NULL) {
864 			fsp->fs_rtmax = 8192;
865 			fsp->fs_rtpref = 8192;
866 			fsp->fs_maxname = NFS_MAXNAMLEN;
867 			fsp->fs_wtmax = 8192;
868 			fsp->fs_wtpref = 8192;
869 			fsp->fs_wtmult = NFS_FABLKSIZE;
870 			fsp->fs_dtpref = 8192;
871 			fsp->fs_maxfilesize = 0xffffffffffffffffull;
872 			fsp->fs_timedelta.tv_sec = 0;
873 			fsp->fs_timedelta.tv_nsec = 1;
874 			fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
875 				NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
876 		}
877 		if (pc != NULL) {
878 			pc->pc_linkmax = LINK_MAX;
879 			pc->pc_namemax = NAME_MAX;
880 			pc->pc_notrunc = 0;
881 			pc->pc_chownrestricted = 0;
882 			pc->pc_caseinsensitive = 0;
883 			pc->pc_casepreserving = 1;
884 		}
885 	}
886 
887 	/*
888 	 * Loop around getting the attributes.
889 	 */
890 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
891 	attrsize = fxdr_unsigned(int, *tl);
892 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
893 	    if (attrsum > attrsize) {
894 		error = NFSERR_BADXDR;
895 		goto nfsmout;
896 	    }
897 	    if (NFSISSET_ATTRBIT(&attrbits, bitpos))
898 		switch (bitpos) {
899 		case NFSATTRBIT_SUPPORTEDATTRS:
900 			retnotsup = 0;
901 			if (compare || nap == NULL)
902 			    error = nfsrv_getattrbits(nd, &retattrbits,
903 				&cnt, &retnotsup);
904 			else
905 			    error = nfsrv_getattrbits(nd, &nap->na_suppattr,
906 				&cnt, &retnotsup);
907 			if (error)
908 			    goto nfsmout;
909 			if (compare && !(*retcmpp)) {
910 			   NFSSETSUPP_ATTRBIT(&checkattrbits);
911 
912 			   /* Some filesystem do not support NFSv4ACL   */
913 			   if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
914 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACL);
915 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACLSUPPORT);
916 		   	   }
917 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
918 			       || retnotsup)
919 				*retcmpp = NFSERR_NOTSAME;
920 			}
921 			attrsum += cnt;
922 			break;
923 		case NFSATTRBIT_TYPE:
924 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
925 			if (compare) {
926 				if (!(*retcmpp)) {
927 				    if (nap->na_type != nfsv34tov_type(*tl))
928 					*retcmpp = NFSERR_NOTSAME;
929 				}
930 			} else if (nap != NULL) {
931 				nap->na_type = nfsv34tov_type(*tl);
932 			}
933 			attrsum += NFSX_UNSIGNED;
934 			break;
935 		case NFSATTRBIT_FHEXPIRETYPE:
936 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
937 			if (compare && !(*retcmpp)) {
938 				if (fxdr_unsigned(int, *tl) !=
939 					NFSV4FHTYPE_PERSISTENT)
940 					*retcmpp = NFSERR_NOTSAME;
941 			}
942 			attrsum += NFSX_UNSIGNED;
943 			break;
944 		case NFSATTRBIT_CHANGE:
945 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
946 			if (compare) {
947 				if (!(*retcmpp)) {
948 				    if (nap->na_filerev != fxdr_hyper(tl))
949 					*retcmpp = NFSERR_NOTSAME;
950 				}
951 			} else if (nap != NULL) {
952 				nap->na_filerev = fxdr_hyper(tl);
953 			}
954 			attrsum += NFSX_HYPER;
955 			break;
956 		case NFSATTRBIT_SIZE:
957 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
958 			if (compare) {
959 				if (!(*retcmpp)) {
960 				    if (nap->na_size != fxdr_hyper(tl))
961 					*retcmpp = NFSERR_NOTSAME;
962 				}
963 			} else if (nap != NULL) {
964 				nap->na_size = fxdr_hyper(tl);
965 			}
966 			attrsum += NFSX_HYPER;
967 			break;
968 		case NFSATTRBIT_LINKSUPPORT:
969 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
970 			if (compare) {
971 				if (!(*retcmpp)) {
972 				    if (fsp->fs_properties & NFSV3_FSFLINK) {
973 					if (*tl == newnfs_false)
974 						*retcmpp = NFSERR_NOTSAME;
975 				    } else {
976 					if (*tl == newnfs_true)
977 						*retcmpp = NFSERR_NOTSAME;
978 				    }
979 				}
980 			} else if (fsp != NULL) {
981 				if (*tl == newnfs_true)
982 					fsp->fs_properties |= NFSV3_FSFLINK;
983 				else
984 					fsp->fs_properties &= ~NFSV3_FSFLINK;
985 			}
986 			attrsum += NFSX_UNSIGNED;
987 			break;
988 		case NFSATTRBIT_SYMLINKSUPPORT:
989 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
990 			if (compare) {
991 				if (!(*retcmpp)) {
992 				    if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
993 					if (*tl == newnfs_false)
994 						*retcmpp = NFSERR_NOTSAME;
995 				    } else {
996 					if (*tl == newnfs_true)
997 						*retcmpp = NFSERR_NOTSAME;
998 				    }
999 				}
1000 			} else if (fsp != NULL) {
1001 				if (*tl == newnfs_true)
1002 					fsp->fs_properties |= NFSV3_FSFSYMLINK;
1003 				else
1004 					fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
1005 			}
1006 			attrsum += NFSX_UNSIGNED;
1007 			break;
1008 		case NFSATTRBIT_NAMEDATTR:
1009 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1010 			if (compare && !(*retcmpp)) {
1011 				if (*tl != newnfs_false)
1012 					*retcmpp = NFSERR_NOTSAME;
1013 			}
1014 			attrsum += NFSX_UNSIGNED;
1015 			break;
1016 		case NFSATTRBIT_FSID:
1017 			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1018 			thyp = fxdr_hyper(tl);
1019 			tl += 2;
1020 			thyp2 = fxdr_hyper(tl);
1021 			if (compare) {
1022 			    if (*retcmpp == 0) {
1023 				if (thyp != (u_int64_t)
1024 				    vfs_statfs(vnode_mount(vp))->f_fsid.val[0] ||
1025 				    thyp2 != (u_int64_t)
1026 				    vfs_statfs(vnode_mount(vp))->f_fsid.val[1])
1027 					*retcmpp = NFSERR_NOTSAME;
1028 			    }
1029 			} else if (nap != NULL) {
1030 				nap->na_filesid[0] = thyp;
1031 				nap->na_filesid[1] = thyp2;
1032 			}
1033 			attrsum += (4 * NFSX_UNSIGNED);
1034 			break;
1035 		case NFSATTRBIT_UNIQUEHANDLES:
1036 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1037 			if (compare && !(*retcmpp)) {
1038 				if (*tl != newnfs_true)
1039 					*retcmpp = NFSERR_NOTSAME;
1040 			}
1041 			attrsum += NFSX_UNSIGNED;
1042 			break;
1043 		case NFSATTRBIT_LEASETIME:
1044 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1045 			if (compare) {
1046 				if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
1047 				    !(*retcmpp))
1048 					*retcmpp = NFSERR_NOTSAME;
1049 			} else if (leasep != NULL) {
1050 				*leasep = fxdr_unsigned(u_int32_t, *tl);
1051 			}
1052 			attrsum += NFSX_UNSIGNED;
1053 			break;
1054 		case NFSATTRBIT_RDATTRERROR:
1055 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1056 			if (compare) {
1057 				 if (!(*retcmpp))
1058 					*retcmpp = NFSERR_INVAL;
1059 			} else if (rderrp != NULL) {
1060 				*rderrp = fxdr_unsigned(u_int32_t, *tl);
1061 			}
1062 			attrsum += NFSX_UNSIGNED;
1063 			break;
1064 		case NFSATTRBIT_ACL:
1065 			if (compare) {
1066 			  if (!(*retcmpp)) {
1067 			    if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
1068 				NFSACL_T *naclp;
1069 
1070 				naclp = acl_alloc(M_WAITOK);
1071 				error = nfsrv_dissectacl(nd, naclp, &aceerr,
1072 				    &cnt, p);
1073 				if (error) {
1074 				    acl_free(naclp);
1075 				    goto nfsmout;
1076 				}
1077 				if (aceerr || aclp == NULL ||
1078 				    nfsrv_compareacl(aclp, naclp))
1079 				    *retcmpp = NFSERR_NOTSAME;
1080 				acl_free(naclp);
1081 			    } else {
1082 				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1083 				    &cnt, p);
1084 				*retcmpp = NFSERR_ATTRNOTSUPP;
1085 			    }
1086 			  }
1087 			} else {
1088 				if (vp != NULL && aclp != NULL)
1089 				    error = nfsrv_dissectacl(nd, aclp, &aceerr,
1090 					&cnt, p);
1091 				else
1092 				    error = nfsrv_dissectacl(nd, NULL, &aceerr,
1093 					&cnt, p);
1094 				if (error)
1095 				    goto nfsmout;
1096 			}
1097 
1098 			attrsum += cnt;
1099 			break;
1100 		case NFSATTRBIT_ACLSUPPORT:
1101 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1102 			if (compare && !(*retcmpp)) {
1103 				if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
1104 					if (fxdr_unsigned(u_int32_t, *tl) !=
1105 					    NFSV4ACE_SUPTYPES)
1106 						*retcmpp = NFSERR_NOTSAME;
1107 				} else {
1108 					*retcmpp = NFSERR_ATTRNOTSUPP;
1109 				}
1110 			}
1111 			attrsum += NFSX_UNSIGNED;
1112 			break;
1113 		case NFSATTRBIT_ARCHIVE:
1114 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1115 			if (compare && !(*retcmpp))
1116 				*retcmpp = NFSERR_ATTRNOTSUPP;
1117 			attrsum += NFSX_UNSIGNED;
1118 			break;
1119 		case NFSATTRBIT_CANSETTIME:
1120 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1121 			if (compare) {
1122 				if (!(*retcmpp)) {
1123 				    if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
1124 					if (*tl == newnfs_false)
1125 						*retcmpp = NFSERR_NOTSAME;
1126 				    } else {
1127 					if (*tl == newnfs_true)
1128 						*retcmpp = NFSERR_NOTSAME;
1129 				    }
1130 				}
1131 			} else if (fsp != NULL) {
1132 				if (*tl == newnfs_true)
1133 					fsp->fs_properties |= NFSV3_FSFCANSETTIME;
1134 				else
1135 					fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
1136 			}
1137 			attrsum += NFSX_UNSIGNED;
1138 			break;
1139 		case NFSATTRBIT_CASEINSENSITIVE:
1140 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1141 			if (compare) {
1142 				if (!(*retcmpp)) {
1143 				    if (*tl != newnfs_false)
1144 					*retcmpp = NFSERR_NOTSAME;
1145 				}
1146 			} else if (pc != NULL) {
1147 				pc->pc_caseinsensitive =
1148 				    fxdr_unsigned(u_int32_t, *tl);
1149 			}
1150 			attrsum += NFSX_UNSIGNED;
1151 			break;
1152 		case NFSATTRBIT_CASEPRESERVING:
1153 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1154 			if (compare) {
1155 				if (!(*retcmpp)) {
1156 				    if (*tl != newnfs_true)
1157 					*retcmpp = NFSERR_NOTSAME;
1158 				}
1159 			} else if (pc != NULL) {
1160 				pc->pc_casepreserving =
1161 				    fxdr_unsigned(u_int32_t, *tl);
1162 			}
1163 			attrsum += NFSX_UNSIGNED;
1164 			break;
1165 		case NFSATTRBIT_CHOWNRESTRICTED:
1166 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1167 			if (compare) {
1168 				if (!(*retcmpp)) {
1169 				    if (*tl != newnfs_true)
1170 					*retcmpp = NFSERR_NOTSAME;
1171 				}
1172 			} else if (pc != NULL) {
1173 				pc->pc_chownrestricted =
1174 				    fxdr_unsigned(u_int32_t, *tl);
1175 			}
1176 			attrsum += NFSX_UNSIGNED;
1177 			break;
1178 		case NFSATTRBIT_FILEHANDLE:
1179 			error = nfsm_getfh(nd, &tnfhp);
1180 			if (error)
1181 				goto nfsmout;
1182 			tfhsize = tnfhp->nfh_len;
1183 			if (compare) {
1184 				if (!(*retcmpp) &&
1185 				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
1186 				     fhp, fhsize))
1187 					*retcmpp = NFSERR_NOTSAME;
1188 				FREE((caddr_t)tnfhp, M_NFSFH);
1189 			} else if (nfhpp != NULL) {
1190 				*nfhpp = tnfhp;
1191 			} else {
1192 				FREE((caddr_t)tnfhp, M_NFSFH);
1193 			}
1194 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
1195 			break;
1196 		case NFSATTRBIT_FILEID:
1197 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1198 			thyp = fxdr_hyper(tl);
1199 			if (compare) {
1200 				if (!(*retcmpp)) {
1201 				    if ((u_int64_t)nap->na_fileid != thyp)
1202 					*retcmpp = NFSERR_NOTSAME;
1203 				}
1204 			} else if (nap != NULL) {
1205 				if (*tl++)
1206 					printf("NFSv4 fileid > 32bits\n");
1207 				nap->na_fileid = thyp;
1208 			}
1209 			attrsum += NFSX_HYPER;
1210 			break;
1211 		case NFSATTRBIT_FILESAVAIL:
1212 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1213 			if (compare) {
1214 				if (!(*retcmpp) &&
1215 				    sfp->sf_afiles != fxdr_hyper(tl))
1216 					*retcmpp = NFSERR_NOTSAME;
1217 			} else if (sfp != NULL) {
1218 				sfp->sf_afiles = fxdr_hyper(tl);
1219 			}
1220 			attrsum += NFSX_HYPER;
1221 			break;
1222 		case NFSATTRBIT_FILESFREE:
1223 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1224 			if (compare) {
1225 				if (!(*retcmpp) &&
1226 				    sfp->sf_ffiles != fxdr_hyper(tl))
1227 					*retcmpp = NFSERR_NOTSAME;
1228 			} else if (sfp != NULL) {
1229 				sfp->sf_ffiles = fxdr_hyper(tl);
1230 			}
1231 			attrsum += NFSX_HYPER;
1232 			break;
1233 		case NFSATTRBIT_FILESTOTAL:
1234 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1235 			if (compare) {
1236 				if (!(*retcmpp) &&
1237 				    sfp->sf_tfiles != fxdr_hyper(tl))
1238 					*retcmpp = NFSERR_NOTSAME;
1239 			} else if (sfp != NULL) {
1240 				sfp->sf_tfiles = fxdr_hyper(tl);
1241 			}
1242 			attrsum += NFSX_HYPER;
1243 			break;
1244 		case NFSATTRBIT_FSLOCATIONS:
1245 			error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
1246 			if (error)
1247 				goto nfsmout;
1248 			attrsum += l;
1249 			if (compare && !(*retcmpp)) {
1250 				refp = nfsv4root_getreferral(vp, NULL, 0);
1251 				if (refp != NULL) {
1252 					if (cp == NULL || cp2 == NULL ||
1253 					    strcmp(cp, "/") ||
1254 					    strcmp(cp2, refp->nfr_srvlist))
1255 						*retcmpp = NFSERR_NOTSAME;
1256 				} else if (m == 0) {
1257 					*retcmpp = NFSERR_NOTSAME;
1258 				}
1259 			}
1260 			if (cp != NULL)
1261 				free(cp, M_NFSSTRING);
1262 			if (cp2 != NULL)
1263 				free(cp2, M_NFSSTRING);
1264 			break;
1265 		case NFSATTRBIT_HIDDEN:
1266 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1267 			if (compare && !(*retcmpp))
1268 				*retcmpp = NFSERR_ATTRNOTSUPP;
1269 			attrsum += NFSX_UNSIGNED;
1270 			break;
1271 		case NFSATTRBIT_HOMOGENEOUS:
1272 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1273 			if (compare) {
1274 				if (!(*retcmpp)) {
1275 				    if (fsp->fs_properties &
1276 					NFSV3_FSFHOMOGENEOUS) {
1277 					if (*tl == newnfs_false)
1278 						*retcmpp = NFSERR_NOTSAME;
1279 				    } else {
1280 					if (*tl == newnfs_true)
1281 						*retcmpp = NFSERR_NOTSAME;
1282 				    }
1283 				}
1284 			} else if (fsp != NULL) {
1285 				if (*tl == newnfs_true)
1286 				    fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
1287 				else
1288 				    fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
1289 			}
1290 			attrsum += NFSX_UNSIGNED;
1291 			break;
1292 		case NFSATTRBIT_MAXFILESIZE:
1293 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1294 			tnfsquad.qval = fxdr_hyper(tl);
1295 			if (compare) {
1296 				if (!(*retcmpp)) {
1297 					tquad = NFSRV_MAXFILESIZE;
1298 					if (tquad != tnfsquad.qval)
1299 						*retcmpp = NFSERR_NOTSAME;
1300 				}
1301 			} else if (fsp != NULL) {
1302 				fsp->fs_maxfilesize = tnfsquad.qval;
1303 			}
1304 			attrsum += NFSX_HYPER;
1305 			break;
1306 		case NFSATTRBIT_MAXLINK:
1307 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1308 			if (compare) {
1309 				if (!(*retcmpp)) {
1310 				    if (fxdr_unsigned(int, *tl) != LINK_MAX)
1311 					*retcmpp = NFSERR_NOTSAME;
1312 				}
1313 			} else if (pc != NULL) {
1314 				pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
1315 			}
1316 			attrsum += NFSX_UNSIGNED;
1317 			break;
1318 		case NFSATTRBIT_MAXNAME:
1319 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1320 			if (compare) {
1321 				if (!(*retcmpp)) {
1322 				    if (fsp->fs_maxname !=
1323 					fxdr_unsigned(u_int32_t, *tl))
1324 						*retcmpp = NFSERR_NOTSAME;
1325 				}
1326 			} else {
1327 				tuint = fxdr_unsigned(u_int32_t, *tl);
1328 				/*
1329 				 * Some Linux NFSv4 servers report this
1330 				 * as 0 or 4billion, so I'll set it to
1331 				 * NFS_MAXNAMLEN. If a server actually creates
1332 				 * a name longer than NFS_MAXNAMLEN, it will
1333 				 * get an error back.
1334 				 */
1335 				if (tuint == 0 || tuint > NFS_MAXNAMLEN)
1336 					tuint = NFS_MAXNAMLEN;
1337 				if (fsp != NULL)
1338 					fsp->fs_maxname = tuint;
1339 				if (pc != NULL)
1340 					pc->pc_namemax = tuint;
1341 			}
1342 			attrsum += NFSX_UNSIGNED;
1343 			break;
1344 		case NFSATTRBIT_MAXREAD:
1345 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1346 			if (compare) {
1347 				if (!(*retcmpp)) {
1348 				    if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
1349 					*(tl + 1)) || *tl != 0)
1350 					*retcmpp = NFSERR_NOTSAME;
1351 				}
1352 			} else if (fsp != NULL) {
1353 				fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
1354 				fsp->fs_rtpref = fsp->fs_rtmax;
1355 				fsp->fs_dtpref = fsp->fs_rtpref;
1356 			}
1357 			attrsum += NFSX_HYPER;
1358 			break;
1359 		case NFSATTRBIT_MAXWRITE:
1360 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1361 			if (compare) {
1362 				if (!(*retcmpp)) {
1363 				    if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
1364 					*(tl + 1)) || *tl != 0)
1365 					*retcmpp = NFSERR_NOTSAME;
1366 				}
1367 			} else if (fsp != NULL) {
1368 				fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
1369 				fsp->fs_wtpref = fsp->fs_wtmax;
1370 			}
1371 			attrsum += NFSX_HYPER;
1372 			break;
1373 		case NFSATTRBIT_MIMETYPE:
1374 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1375 			i = fxdr_unsigned(int, *tl);
1376 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
1377 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
1378 			if (error)
1379 				goto nfsmout;
1380 			if (compare && !(*retcmpp))
1381 				*retcmpp = NFSERR_ATTRNOTSUPP;
1382 			break;
1383 		case NFSATTRBIT_MODE:
1384 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1385 			if (compare) {
1386 				if (!(*retcmpp)) {
1387 				    if (nap->na_mode != nfstov_mode(*tl))
1388 					*retcmpp = NFSERR_NOTSAME;
1389 				}
1390 			} else if (nap != NULL) {
1391 				nap->na_mode = nfstov_mode(*tl);
1392 			}
1393 			attrsum += NFSX_UNSIGNED;
1394 			break;
1395 		case NFSATTRBIT_NOTRUNC:
1396 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1397 			if (compare) {
1398 				if (!(*retcmpp)) {
1399 				    if (*tl != newnfs_true)
1400 					*retcmpp = NFSERR_NOTSAME;
1401 				}
1402 			} else if (pc != NULL) {
1403 				pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
1404 			}
1405 			attrsum += NFSX_UNSIGNED;
1406 			break;
1407 		case NFSATTRBIT_NUMLINKS:
1408 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1409 			tuint = fxdr_unsigned(u_int32_t, *tl);
1410 			if (compare) {
1411 			    if (!(*retcmpp)) {
1412 				if ((u_int32_t)nap->na_nlink != tuint)
1413 					*retcmpp = NFSERR_NOTSAME;
1414 			    }
1415 			} else if (nap != NULL) {
1416 				nap->na_nlink = tuint;
1417 			}
1418 			attrsum += NFSX_UNSIGNED;
1419 			break;
1420 		case NFSATTRBIT_OWNER:
1421 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1422 			j = fxdr_unsigned(int, *tl);
1423 			if (j < 0) {
1424 				error = NFSERR_BADXDR;
1425 				goto nfsmout;
1426 			}
1427 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1428 			if (j > NFSV4_SMALLSTR)
1429 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1430 			else
1431 				cp = namestr;
1432 			error = nfsrv_mtostr(nd, cp, j);
1433 			if (error) {
1434 				if (j > NFSV4_SMALLSTR)
1435 					free(cp, M_NFSSTRING);
1436 				goto nfsmout;
1437 			}
1438 			if (compare) {
1439 			    if (!(*retcmpp)) {
1440 				if (nfsv4_strtouid(nd, cp, j, &uid, p) ||
1441 				    nap->na_uid != uid)
1442 				    *retcmpp = NFSERR_NOTSAME;
1443 			    }
1444 			} else if (nap != NULL) {
1445 				if (nfsv4_strtouid(nd, cp, j, &uid, p))
1446 					nap->na_uid = nfsrv_defaultuid;
1447 				else
1448 					nap->na_uid = uid;
1449 			}
1450 			if (j > NFSV4_SMALLSTR)
1451 				free(cp, M_NFSSTRING);
1452 			break;
1453 		case NFSATTRBIT_OWNERGROUP:
1454 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1455 			j = fxdr_unsigned(int, *tl);
1456 			if (j < 0) {
1457 				error =  NFSERR_BADXDR;
1458 				goto nfsmout;
1459 			}
1460 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1461 			if (j > NFSV4_SMALLSTR)
1462 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1463 			else
1464 				cp = namestr;
1465 			error = nfsrv_mtostr(nd, cp, j);
1466 			if (error) {
1467 				if (j > NFSV4_SMALLSTR)
1468 					free(cp, M_NFSSTRING);
1469 				goto nfsmout;
1470 			}
1471 			if (compare) {
1472 			    if (!(*retcmpp)) {
1473 				if (nfsv4_strtogid(nd, cp, j, &gid, p) ||
1474 				    nap->na_gid != gid)
1475 				    *retcmpp = NFSERR_NOTSAME;
1476 			    }
1477 			} else if (nap != NULL) {
1478 				if (nfsv4_strtogid(nd, cp, j, &gid, p))
1479 					nap->na_gid = nfsrv_defaultgid;
1480 				else
1481 					nap->na_gid = gid;
1482 			}
1483 			if (j > NFSV4_SMALLSTR)
1484 				free(cp, M_NFSSTRING);
1485 			break;
1486 		case NFSATTRBIT_QUOTAHARD:
1487 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1488 			if (sbp != NULL) {
1489 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1490 				freenum = sbp->f_bfree;
1491 			    else
1492 				freenum = sbp->f_bavail;
1493 #ifdef QUOTA
1494 			    /*
1495 			     * ufs_quotactl() insists that the uid argument
1496 			     * equal p_ruid for non-root quota access, so
1497 			     * we'll just make sure that's the case.
1498 			     */
1499 			    savuid = p->p_cred->p_ruid;
1500 			    p->p_cred->p_ruid = cred->cr_uid;
1501 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1502 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1503 				freenum = min(dqb.dqb_bhardlimit, freenum);
1504 			    p->p_cred->p_ruid = savuid;
1505 #endif	/* QUOTA */
1506 			    uquad = (u_int64_t)freenum;
1507 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1508 			}
1509 			if (compare && !(*retcmpp)) {
1510 				if (uquad != fxdr_hyper(tl))
1511 					*retcmpp = NFSERR_NOTSAME;
1512 			}
1513 			attrsum += NFSX_HYPER;
1514 			break;
1515 		case NFSATTRBIT_QUOTASOFT:
1516 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1517 			if (sbp != NULL) {
1518 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1519 				freenum = sbp->f_bfree;
1520 			    else
1521 				freenum = sbp->f_bavail;
1522 #ifdef QUOTA
1523 			    /*
1524 			     * ufs_quotactl() insists that the uid argument
1525 			     * equal p_ruid for non-root quota access, so
1526 			     * we'll just make sure that's the case.
1527 			     */
1528 			    savuid = p->p_cred->p_ruid;
1529 			    p->p_cred->p_ruid = cred->cr_uid;
1530 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1531 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1532 				freenum = min(dqb.dqb_bsoftlimit, freenum);
1533 			    p->p_cred->p_ruid = savuid;
1534 #endif	/* QUOTA */
1535 			    uquad = (u_int64_t)freenum;
1536 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1537 			}
1538 			if (compare && !(*retcmpp)) {
1539 				if (uquad != fxdr_hyper(tl))
1540 					*retcmpp = NFSERR_NOTSAME;
1541 			}
1542 			attrsum += NFSX_HYPER;
1543 			break;
1544 		case NFSATTRBIT_QUOTAUSED:
1545 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1546 			if (sbp != NULL) {
1547 			    freenum = 0;
1548 #ifdef QUOTA
1549 			    /*
1550 			     * ufs_quotactl() insists that the uid argument
1551 			     * equal p_ruid for non-root quota access, so
1552 			     * we'll just make sure that's the case.
1553 			     */
1554 			    savuid = p->p_cred->p_ruid;
1555 			    p->p_cred->p_ruid = cred->cr_uid;
1556 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1557 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1558 				freenum = dqb.dqb_curblocks;
1559 			    p->p_cred->p_ruid = savuid;
1560 #endif	/* QUOTA */
1561 			    uquad = (u_int64_t)freenum;
1562 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1563 			}
1564 			if (compare && !(*retcmpp)) {
1565 				if (uquad != fxdr_hyper(tl))
1566 					*retcmpp = NFSERR_NOTSAME;
1567 			}
1568 			attrsum += NFSX_HYPER;
1569 			break;
1570 		case NFSATTRBIT_RAWDEV:
1571 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
1572 			j = fxdr_unsigned(int, *tl++);
1573 			k = fxdr_unsigned(int, *tl);
1574 			if (compare) {
1575 			    if (!(*retcmpp)) {
1576 				if (nap->na_rdev != NFSMAKEDEV(j, k))
1577 					*retcmpp = NFSERR_NOTSAME;
1578 			    }
1579 			} else if (nap != NULL) {
1580 				nap->na_rdev = NFSMAKEDEV(j, k);
1581 			}
1582 			attrsum += NFSX_V4SPECDATA;
1583 			break;
1584 		case NFSATTRBIT_SPACEAVAIL:
1585 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1586 			if (compare) {
1587 				if (!(*retcmpp) &&
1588 				    sfp->sf_abytes != fxdr_hyper(tl))
1589 					*retcmpp = NFSERR_NOTSAME;
1590 			} else if (sfp != NULL) {
1591 				sfp->sf_abytes = fxdr_hyper(tl);
1592 			}
1593 			attrsum += NFSX_HYPER;
1594 			break;
1595 		case NFSATTRBIT_SPACEFREE:
1596 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1597 			if (compare) {
1598 				if (!(*retcmpp) &&
1599 				    sfp->sf_fbytes != fxdr_hyper(tl))
1600 					*retcmpp = NFSERR_NOTSAME;
1601 			} else if (sfp != NULL) {
1602 				sfp->sf_fbytes = fxdr_hyper(tl);
1603 			}
1604 			attrsum += NFSX_HYPER;
1605 			break;
1606 		case NFSATTRBIT_SPACETOTAL:
1607 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1608 			if (compare) {
1609 				if (!(*retcmpp) &&
1610 				    sfp->sf_tbytes != fxdr_hyper(tl))
1611 					*retcmpp = NFSERR_NOTSAME;
1612 			} else if (sfp != NULL) {
1613 				sfp->sf_tbytes = fxdr_hyper(tl);
1614 			}
1615 			attrsum += NFSX_HYPER;
1616 			break;
1617 		case NFSATTRBIT_SPACEUSED:
1618 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1619 			thyp = fxdr_hyper(tl);
1620 			if (compare) {
1621 			    if (!(*retcmpp)) {
1622 				if ((u_int64_t)nap->na_bytes != thyp)
1623 					*retcmpp = NFSERR_NOTSAME;
1624 			    }
1625 			} else if (nap != NULL) {
1626 				nap->na_bytes = thyp;
1627 			}
1628 			attrsum += NFSX_HYPER;
1629 			break;
1630 		case NFSATTRBIT_SYSTEM:
1631 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1632 			if (compare && !(*retcmpp))
1633 				*retcmpp = NFSERR_ATTRNOTSUPP;
1634 			attrsum += NFSX_UNSIGNED;
1635 			break;
1636 		case NFSATTRBIT_TIMEACCESS:
1637 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1638 			fxdr_nfsv4time(tl, &temptime);
1639 			if (compare) {
1640 			    if (!(*retcmpp)) {
1641 				if (!NFS_CMPTIME(temptime, nap->na_atime))
1642 					*retcmpp = NFSERR_NOTSAME;
1643 			    }
1644 			} else if (nap != NULL) {
1645 				nap->na_atime = temptime;
1646 			}
1647 			attrsum += NFSX_V4TIME;
1648 			break;
1649 		case NFSATTRBIT_TIMEACCESSSET:
1650 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1651 			attrsum += NFSX_UNSIGNED;
1652 			i = fxdr_unsigned(int, *tl);
1653 			if (i == NFSV4SATTRTIME_TOCLIENT) {
1654 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1655 				attrsum += NFSX_V4TIME;
1656 			}
1657 			if (compare && !(*retcmpp))
1658 				*retcmpp = NFSERR_INVAL;
1659 			break;
1660 		case NFSATTRBIT_TIMEBACKUP:
1661 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1662 			if (compare && !(*retcmpp))
1663 				*retcmpp = NFSERR_ATTRNOTSUPP;
1664 			attrsum += NFSX_V4TIME;
1665 			break;
1666 		case NFSATTRBIT_TIMECREATE:
1667 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1668 			if (compare && !(*retcmpp))
1669 				*retcmpp = NFSERR_ATTRNOTSUPP;
1670 			attrsum += NFSX_V4TIME;
1671 			break;
1672 		case NFSATTRBIT_TIMEDELTA:
1673 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1674 			if (fsp != NULL) {
1675 			    if (compare) {
1676 				if (!(*retcmpp)) {
1677 				    if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
1678 					fxdr_unsigned(u_int32_t, *(tl + 1)) ||
1679 				        (u_int32_t)fsp->fs_timedelta.tv_nsec !=
1680 					(fxdr_unsigned(u_int32_t, *(tl + 2)) %
1681 					 1000000000) ||
1682 					*tl != 0)
1683 					    *retcmpp = NFSERR_NOTSAME;
1684 				}
1685 			    } else {
1686 				fxdr_nfsv4time(tl, &fsp->fs_timedelta);
1687 			    }
1688 			}
1689 			attrsum += NFSX_V4TIME;
1690 			break;
1691 		case NFSATTRBIT_TIMEMETADATA:
1692 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1693 			fxdr_nfsv4time(tl, &temptime);
1694 			if (compare) {
1695 			    if (!(*retcmpp)) {
1696 				if (!NFS_CMPTIME(temptime, nap->na_ctime))
1697 					*retcmpp = NFSERR_NOTSAME;
1698 			    }
1699 			} else if (nap != NULL) {
1700 				nap->na_ctime = temptime;
1701 			}
1702 			attrsum += NFSX_V4TIME;
1703 			break;
1704 		case NFSATTRBIT_TIMEMODIFY:
1705 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1706 			fxdr_nfsv4time(tl, &temptime);
1707 			if (compare) {
1708 			    if (!(*retcmpp)) {
1709 				if (!NFS_CMPTIME(temptime, nap->na_mtime))
1710 					*retcmpp = NFSERR_NOTSAME;
1711 			    }
1712 			} else if (nap != NULL) {
1713 				nap->na_mtime = temptime;
1714 			}
1715 			attrsum += NFSX_V4TIME;
1716 			break;
1717 		case NFSATTRBIT_TIMEMODIFYSET:
1718 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1719 			attrsum += NFSX_UNSIGNED;
1720 			i = fxdr_unsigned(int, *tl);
1721 			if (i == NFSV4SATTRTIME_TOCLIENT) {
1722 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1723 				attrsum += NFSX_V4TIME;
1724 			}
1725 			if (compare && !(*retcmpp))
1726 				*retcmpp = NFSERR_INVAL;
1727 			break;
1728 		case NFSATTRBIT_MOUNTEDONFILEID:
1729 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1730 			thyp = fxdr_hyper(tl);
1731 			if (compare) {
1732 			    if (!(*retcmpp)) {
1733 				if (*tl++) {
1734 					*retcmpp = NFSERR_NOTSAME;
1735 				} else {
1736 					if (!vp || !nfsrv_atroot(vp, &fid))
1737 						fid = nap->na_fileid;
1738 					if ((u_int64_t)fid != thyp)
1739 						*retcmpp = NFSERR_NOTSAME;
1740 				}
1741 			    }
1742 			} else if (nap != NULL) {
1743 			    if (*tl++)
1744 				printf("NFSv4 mounted on fileid > 32bits\n");
1745 			    nap->na_mntonfileno = thyp;
1746 			}
1747 			attrsum += NFSX_HYPER;
1748 			break;
1749 		case NFSATTRBIT_SUPPATTREXCLCREAT:
1750 			retnotsup = 0;
1751 			error = nfsrv_getattrbits(nd, &retattrbits,
1752 			    &cnt, &retnotsup);
1753 			if (error)
1754 			    goto nfsmout;
1755 			if (compare && !(*retcmpp)) {
1756 			   NFSSETSUPP_ATTRBIT(&checkattrbits);
1757 			   NFSCLRNOTSETABLE_ATTRBIT(&checkattrbits);
1758 			   NFSCLRBIT_ATTRBIT(&checkattrbits,
1759 				NFSATTRBIT_TIMEACCESSSET);
1760 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
1761 			       || retnotsup)
1762 				*retcmpp = NFSERR_NOTSAME;
1763 			}
1764 			attrsum += cnt;
1765 			break;
1766 		default:
1767 			printf("EEK! nfsv4_loadattr unknown attr=%d\n",
1768 				bitpos);
1769 			if (compare && !(*retcmpp))
1770 				*retcmpp = NFSERR_ATTRNOTSUPP;
1771 			/*
1772 			 * and get out of the loop, since we can't parse
1773 			 * the unknown attrbute data.
1774 			 */
1775 			bitpos = NFSATTRBIT_MAX;
1776 			break;
1777 		}
1778 	}
1779 
1780 	/*
1781 	 * some clients pad the attrlist, so we need to skip over the
1782 	 * padding.
1783 	 */
1784 	if (attrsum > attrsize) {
1785 		error = NFSERR_BADXDR;
1786 	} else {
1787 		attrsize = NFSM_RNDUP(attrsize);
1788 		if (attrsum < attrsize)
1789 			error = nfsm_advance(nd, attrsize - attrsum, -1);
1790 	}
1791 nfsmout:
1792 	NFSEXITCODE2(error, nd);
1793 	return (error);
1794 }
1795 
1796 /*
1797  * Implement sleep locks for newnfs. The nfslock_usecnt allows for a
1798  * shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
1799  * The first argument is a pointer to an nfsv4lock structure.
1800  * The second argument is 1 iff a blocking lock is wanted.
1801  * If this argument is 0, the call waits until no thread either wants nor
1802  * holds an exclusive lock.
1803  * It returns 1 if the lock was acquired, 0 otherwise.
1804  * If several processes call this function concurrently wanting the exclusive
1805  * lock, one will get the lock and the rest will return without getting the
1806  * lock. (If the caller must have the lock, it simply calls this function in a
1807  *  loop until the function returns 1 to indicate the lock was acquired.)
1808  * Any usecnt must be decremented by calling nfsv4_relref() before
1809  * calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
1810  * be called in a loop.
1811  * The isleptp argument is set to indicate if the call slept, iff not NULL
1812  * and the mp argument indicates to check for a forced dismount, iff not
1813  * NULL.
1814  */
1815 APPLESTATIC int
1816 nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
1817     void *mutex, struct mount *mp)
1818 {
1819 
1820 	if (isleptp)
1821 		*isleptp = 0;
1822 	/*
1823 	 * If a lock is wanted, loop around until the lock is acquired by
1824 	 * someone and then released. If I want the lock, try to acquire it.
1825 	 * For a lock to be issued, no lock must be in force and the usecnt
1826 	 * must be zero.
1827 	 */
1828 	if (iwantlock) {
1829 	    if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1830 		lp->nfslock_usecnt == 0) {
1831 		lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1832 		lp->nfslock_lock |= NFSV4LOCK_LOCK;
1833 		return (1);
1834 	    }
1835 	    lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
1836 	}
1837 	while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
1838 		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1839 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1840 			return (0);
1841 		}
1842 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1843 		if (isleptp)
1844 			*isleptp = 1;
1845 		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1846 		    PZERO - 1, "nfsv4lck", NULL);
1847 		if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1848 		    lp->nfslock_usecnt == 0) {
1849 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1850 			lp->nfslock_lock |= NFSV4LOCK_LOCK;
1851 			return (1);
1852 		}
1853 	}
1854 	return (0);
1855 }
1856 
1857 /*
1858  * Release the lock acquired by nfsv4_lock().
1859  * The second argument is set to 1 to indicate the nfslock_usecnt should be
1860  * incremented, as well.
1861  */
1862 APPLESTATIC void
1863 nfsv4_unlock(struct nfsv4lock *lp, int incref)
1864 {
1865 
1866 	lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
1867 	if (incref)
1868 		lp->nfslock_usecnt++;
1869 	nfsv4_wanted(lp);
1870 }
1871 
1872 /*
1873  * Release a reference cnt.
1874  */
1875 APPLESTATIC void
1876 nfsv4_relref(struct nfsv4lock *lp)
1877 {
1878 
1879 	if (lp->nfslock_usecnt <= 0)
1880 		panic("nfsv4root ref cnt");
1881 	lp->nfslock_usecnt--;
1882 	if (lp->nfslock_usecnt == 0)
1883 		nfsv4_wanted(lp);
1884 }
1885 
1886 /*
1887  * Get a reference cnt.
1888  * This function will wait for any exclusive lock to be released, but will
1889  * not wait for threads that want the exclusive lock. If priority needs
1890  * to be given to threads that need the exclusive lock, a call to nfsv4_lock()
1891  * with the 2nd argument == 0 should be done before calling nfsv4_getref().
1892  * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and
1893  * return without getting a refcnt for that case.
1894  */
1895 APPLESTATIC void
1896 nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex,
1897     struct mount *mp)
1898 {
1899 
1900 	if (isleptp)
1901 		*isleptp = 0;
1902 
1903 	/*
1904 	 * Wait for a lock held.
1905 	 */
1906 	while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
1907 		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1908 			return;
1909 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1910 		if (isleptp)
1911 			*isleptp = 1;
1912 		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1913 		    PZERO - 1, "nfsv4gr", NULL);
1914 	}
1915 	if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1916 		return;
1917 
1918 	lp->nfslock_usecnt++;
1919 }
1920 
1921 /*
1922  * Get a reference as above, but return failure instead of sleeping if
1923  * an exclusive lock is held.
1924  */
1925 APPLESTATIC int
1926 nfsv4_getref_nonblock(struct nfsv4lock *lp)
1927 {
1928 
1929 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
1930 		return (0);
1931 
1932 	lp->nfslock_usecnt++;
1933 	return (1);
1934 }
1935 
1936 /*
1937  * Test for a lock. Return 1 if locked, 0 otherwise.
1938  */
1939 APPLESTATIC int
1940 nfsv4_testlock(struct nfsv4lock *lp)
1941 {
1942 
1943 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
1944 	    lp->nfslock_usecnt == 0)
1945 		return (0);
1946 	return (1);
1947 }
1948 
1949 /*
1950  * Wake up anyone sleeping, waiting for this lock.
1951  */
1952 static void
1953 nfsv4_wanted(struct nfsv4lock *lp)
1954 {
1955 
1956 	if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
1957 		lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
1958 		wakeup((caddr_t)&lp->nfslock_lock);
1959 	}
1960 }
1961 
1962 /*
1963  * Copy a string from an mbuf list into a character array.
1964  * Return EBADRPC if there is an mbuf error,
1965  * 0 otherwise.
1966  */
1967 APPLESTATIC int
1968 nfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
1969 {
1970 	char *cp;
1971 	int xfer, len;
1972 	mbuf_t mp;
1973 	int rem, error = 0;
1974 
1975 	mp = nd->nd_md;
1976 	cp = nd->nd_dpos;
1977 	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - cp;
1978 	rem = NFSM_RNDUP(siz) - siz;
1979 	while (siz > 0) {
1980 		if (len > siz)
1981 			xfer = siz;
1982 		else
1983 			xfer = len;
1984 		NFSBCOPY(cp, str, xfer);
1985 		str += xfer;
1986 		siz -= xfer;
1987 		if (siz > 0) {
1988 			mp = mbuf_next(mp);
1989 			if (mp == NULL) {
1990 				error = EBADRPC;
1991 				goto out;
1992 			}
1993 			cp = NFSMTOD(mp, caddr_t);
1994 			len = mbuf_len(mp);
1995 		} else {
1996 			cp += xfer;
1997 			len -= xfer;
1998 		}
1999 	}
2000 	*str = '\0';
2001 	nd->nd_dpos = cp;
2002 	nd->nd_md = mp;
2003 	if (rem > 0) {
2004 		if (len < rem)
2005 			error = nfsm_advance(nd, rem, len);
2006 		else
2007 			nd->nd_dpos += rem;
2008 	}
2009 
2010 out:
2011 	NFSEXITCODE2(error, nd);
2012 	return (error);
2013 }
2014 
2015 /*
2016  * Fill in the attributes as marked by the bitmap (V4).
2017  */
2018 APPLESTATIC int
2019 nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
2020     NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
2021     nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
2022     int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
2023 {
2024 	int bitpos, retnum = 0;
2025 	u_int32_t *tl;
2026 	int siz, prefixnum, error;
2027 	u_char *cp, namestr[NFSV4_SMALLSTR];
2028 	nfsattrbit_t attrbits, retbits;
2029 	nfsattrbit_t *retbitp = &retbits;
2030 	u_int32_t freenum, *retnump;
2031 	u_int64_t uquad;
2032 	struct statfs fs;
2033 	struct nfsfsinfo fsinf;
2034 	struct timespec temptime;
2035 	NFSACL_T *aclp, *naclp = NULL;
2036 #ifdef QUOTA
2037 	struct dqblk dqb;
2038 	uid_t savuid;
2039 #endif
2040 
2041 	/*
2042 	 * First, set the bits that can be filled and get fsinfo.
2043 	 */
2044 	NFSSET_ATTRBIT(retbitp, attrbitp);
2045 	/*
2046 	 * If both p and cred are NULL, it is a client side setattr call.
2047 	 * If both p and cred are not NULL, it is a server side reply call.
2048 	 * If p is not NULL and cred is NULL, it is a client side callback
2049 	 * reply call.
2050 	 */
2051 	if (p == NULL && cred == NULL) {
2052 		NFSCLRNOTSETABLE_ATTRBIT(retbitp);
2053 		aclp = saclp;
2054 	} else {
2055 		NFSCLRNOTFILLABLE_ATTRBIT(retbitp);
2056 		naclp = acl_alloc(M_WAITOK);
2057 		aclp = naclp;
2058 	}
2059 	nfsvno_getfs(&fsinf, isdgram);
2060 #ifndef APPLE
2061 	/*
2062 	 * Get the VFS_STATFS(), since some attributes need them.
2063 	 */
2064 	if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
2065 		error = VFS_STATFS(mp, &fs);
2066 		if (error != 0) {
2067 			if (reterr) {
2068 				nd->nd_repstat = NFSERR_ACCES;
2069 				return (0);
2070 			}
2071 			NFSCLRSTATFS_ATTRBIT(retbitp);
2072 		}
2073 	}
2074 #endif
2075 
2076 	/*
2077 	 * And the NFSv4 ACL...
2078 	 */
2079 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
2080 	    (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2081 		supports_nfsv4acls == 0))) {
2082 		NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
2083 	}
2084 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
2085 		if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2086 		    supports_nfsv4acls == 0)) {
2087 			NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2088 		} else if (naclp != NULL) {
2089 			if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
2090 				error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
2091 				if (error == 0)
2092 					error = VOP_GETACL(vp, ACL_TYPE_NFS4,
2093 					    naclp, cred, p);
2094 				NFSVOPUNLOCK(vp, 0);
2095 			} else
2096 				error = NFSERR_PERM;
2097 			if (error != 0) {
2098 				if (reterr) {
2099 					nd->nd_repstat = NFSERR_ACCES;
2100 					return (0);
2101 				}
2102 				NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2103 			}
2104 		}
2105 	}
2106 
2107 	/*
2108 	 * Put out the attribute bitmap for the ones being filled in
2109 	 * and get the field for the number of attributes returned.
2110 	 */
2111 	prefixnum = nfsrv_putattrbit(nd, retbitp);
2112 	NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
2113 	prefixnum += NFSX_UNSIGNED;
2114 
2115 	/*
2116 	 * Now, loop around filling in the attributes for each bit set.
2117 	 */
2118 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
2119 	    if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
2120 		switch (bitpos) {
2121 		case NFSATTRBIT_SUPPORTEDATTRS:
2122 			NFSSETSUPP_ATTRBIT(&attrbits);
2123 			if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
2124 			    && supports_nfsv4acls == 0)) {
2125 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
2126 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
2127 			}
2128 			retnum += nfsrv_putattrbit(nd, &attrbits);
2129 			break;
2130 		case NFSATTRBIT_TYPE:
2131 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2132 			*tl = vtonfsv34_type(vap->va_type);
2133 			retnum += NFSX_UNSIGNED;
2134 			break;
2135 		case NFSATTRBIT_FHEXPIRETYPE:
2136 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2137 			*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
2138 			retnum += NFSX_UNSIGNED;
2139 			break;
2140 		case NFSATTRBIT_CHANGE:
2141 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2142 			txdr_hyper(vap->va_filerev, tl);
2143 			retnum += NFSX_HYPER;
2144 			break;
2145 		case NFSATTRBIT_SIZE:
2146 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2147 			txdr_hyper(vap->va_size, tl);
2148 			retnum += NFSX_HYPER;
2149 			break;
2150 		case NFSATTRBIT_LINKSUPPORT:
2151 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2152 			if (fsinf.fs_properties & NFSV3FSINFO_LINK)
2153 				*tl = newnfs_true;
2154 			else
2155 				*tl = newnfs_false;
2156 			retnum += NFSX_UNSIGNED;
2157 			break;
2158 		case NFSATTRBIT_SYMLINKSUPPORT:
2159 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2160 			if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
2161 				*tl = newnfs_true;
2162 			else
2163 				*tl = newnfs_false;
2164 			retnum += NFSX_UNSIGNED;
2165 			break;
2166 		case NFSATTRBIT_NAMEDATTR:
2167 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2168 			*tl = newnfs_false;
2169 			retnum += NFSX_UNSIGNED;
2170 			break;
2171 		case NFSATTRBIT_FSID:
2172 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
2173 			*tl++ = 0;
2174 			*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
2175 			*tl++ = 0;
2176 			*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
2177 			retnum += NFSX_V4FSID;
2178 			break;
2179 		case NFSATTRBIT_UNIQUEHANDLES:
2180 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2181 			*tl = newnfs_true;
2182 			retnum += NFSX_UNSIGNED;
2183 			break;
2184 		case NFSATTRBIT_LEASETIME:
2185 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2186 			*tl = txdr_unsigned(nfsrv_lease);
2187 			retnum += NFSX_UNSIGNED;
2188 			break;
2189 		case NFSATTRBIT_RDATTRERROR:
2190 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2191 			*tl = txdr_unsigned(rderror);
2192 			retnum += NFSX_UNSIGNED;
2193 			break;
2194 		/*
2195 		 * Recommended Attributes. (Only the supported ones.)
2196 		 */
2197 		case NFSATTRBIT_ACL:
2198 			retnum += nfsrv_buildacl(nd, aclp, vnode_vtype(vp), p);
2199 			break;
2200 		case NFSATTRBIT_ACLSUPPORT:
2201 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2202 			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
2203 			retnum += NFSX_UNSIGNED;
2204 			break;
2205 		case NFSATTRBIT_CANSETTIME:
2206 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2207 			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
2208 				*tl = newnfs_true;
2209 			else
2210 				*tl = newnfs_false;
2211 			retnum += NFSX_UNSIGNED;
2212 			break;
2213 		case NFSATTRBIT_CASEINSENSITIVE:
2214 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2215 			*tl = newnfs_false;
2216 			retnum += NFSX_UNSIGNED;
2217 			break;
2218 		case NFSATTRBIT_CASEPRESERVING:
2219 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2220 			*tl = newnfs_true;
2221 			retnum += NFSX_UNSIGNED;
2222 			break;
2223 		case NFSATTRBIT_CHOWNRESTRICTED:
2224 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2225 			*tl = newnfs_true;
2226 			retnum += NFSX_UNSIGNED;
2227 			break;
2228 		case NFSATTRBIT_FILEHANDLE:
2229 			retnum += nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
2230 			break;
2231 		case NFSATTRBIT_FILEID:
2232 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2233 			*tl++ = 0;
2234 			*tl = txdr_unsigned(vap->va_fileid);
2235 			retnum += NFSX_HYPER;
2236 			break;
2237 		case NFSATTRBIT_FILESAVAIL:
2238 			/*
2239 			 * Check quota and use min(quota, f_ffree).
2240 			 */
2241 			freenum = fs.f_ffree;
2242 #ifdef QUOTA
2243 			/*
2244 			 * ufs_quotactl() insists that the uid argument
2245 			 * equal p_ruid for non-root quota access, so
2246 			 * we'll just make sure that's the case.
2247 			 */
2248 			savuid = p->p_cred->p_ruid;
2249 			p->p_cred->p_ruid = cred->cr_uid;
2250 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2251 			    cred->cr_uid, (caddr_t)&dqb))
2252 			    freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
2253 				freenum);
2254 			p->p_cred->p_ruid = savuid;
2255 #endif	/* QUOTA */
2256 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2257 			*tl++ = 0;
2258 			*tl = txdr_unsigned(freenum);
2259 			retnum += NFSX_HYPER;
2260 			break;
2261 		case NFSATTRBIT_FILESFREE:
2262 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2263 			*tl++ = 0;
2264 			*tl = txdr_unsigned(fs.f_ffree);
2265 			retnum += NFSX_HYPER;
2266 			break;
2267 		case NFSATTRBIT_FILESTOTAL:
2268 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2269 			*tl++ = 0;
2270 			*tl = txdr_unsigned(fs.f_files);
2271 			retnum += NFSX_HYPER;
2272 			break;
2273 		case NFSATTRBIT_FSLOCATIONS:
2274 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2275 			*tl++ = 0;
2276 			*tl = 0;
2277 			retnum += 2 * NFSX_UNSIGNED;
2278 			break;
2279 		case NFSATTRBIT_HOMOGENEOUS:
2280 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2281 			if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
2282 				*tl = newnfs_true;
2283 			else
2284 				*tl = newnfs_false;
2285 			retnum += NFSX_UNSIGNED;
2286 			break;
2287 		case NFSATTRBIT_MAXFILESIZE:
2288 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2289 			uquad = NFSRV_MAXFILESIZE;
2290 			txdr_hyper(uquad, tl);
2291 			retnum += NFSX_HYPER;
2292 			break;
2293 		case NFSATTRBIT_MAXLINK:
2294 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2295 			*tl = txdr_unsigned(LINK_MAX);
2296 			retnum += NFSX_UNSIGNED;
2297 			break;
2298 		case NFSATTRBIT_MAXNAME:
2299 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2300 			*tl = txdr_unsigned(NFS_MAXNAMLEN);
2301 			retnum += NFSX_UNSIGNED;
2302 			break;
2303 		case NFSATTRBIT_MAXREAD:
2304 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2305 			*tl++ = 0;
2306 			*tl = txdr_unsigned(fsinf.fs_rtmax);
2307 			retnum += NFSX_HYPER;
2308 			break;
2309 		case NFSATTRBIT_MAXWRITE:
2310 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2311 			*tl++ = 0;
2312 			*tl = txdr_unsigned(fsinf.fs_wtmax);
2313 			retnum += NFSX_HYPER;
2314 			break;
2315 		case NFSATTRBIT_MODE:
2316 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2317 			*tl = vtonfsv34_mode(vap->va_mode);
2318 			retnum += NFSX_UNSIGNED;
2319 			break;
2320 		case NFSATTRBIT_NOTRUNC:
2321 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2322 			*tl = newnfs_true;
2323 			retnum += NFSX_UNSIGNED;
2324 			break;
2325 		case NFSATTRBIT_NUMLINKS:
2326 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2327 			*tl = txdr_unsigned(vap->va_nlink);
2328 			retnum += NFSX_UNSIGNED;
2329 			break;
2330 		case NFSATTRBIT_OWNER:
2331 			cp = namestr;
2332 			nfsv4_uidtostr(vap->va_uid, &cp, &siz, p);
2333 			retnum += nfsm_strtom(nd, cp, siz);
2334 			if (cp != namestr)
2335 				free(cp, M_NFSSTRING);
2336 			break;
2337 		case NFSATTRBIT_OWNERGROUP:
2338 			cp = namestr;
2339 			nfsv4_gidtostr(vap->va_gid, &cp, &siz, p);
2340 			retnum += nfsm_strtom(nd, cp, siz);
2341 			if (cp != namestr)
2342 				free(cp, M_NFSSTRING);
2343 			break;
2344 		case NFSATTRBIT_QUOTAHARD:
2345 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2346 				freenum = fs.f_bfree;
2347 			else
2348 				freenum = fs.f_bavail;
2349 #ifdef QUOTA
2350 			/*
2351 			 * ufs_quotactl() insists that the uid argument
2352 			 * equal p_ruid for non-root quota access, so
2353 			 * we'll just make sure that's the case.
2354 			 */
2355 			savuid = p->p_cred->p_ruid;
2356 			p->p_cred->p_ruid = cred->cr_uid;
2357 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2358 			    cred->cr_uid, (caddr_t)&dqb))
2359 			    freenum = min(dqb.dqb_bhardlimit, freenum);
2360 			p->p_cred->p_ruid = savuid;
2361 #endif	/* QUOTA */
2362 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2363 			uquad = (u_int64_t)freenum;
2364 			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2365 			txdr_hyper(uquad, tl);
2366 			retnum += NFSX_HYPER;
2367 			break;
2368 		case NFSATTRBIT_QUOTASOFT:
2369 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2370 				freenum = fs.f_bfree;
2371 			else
2372 				freenum = fs.f_bavail;
2373 #ifdef QUOTA
2374 			/*
2375 			 * ufs_quotactl() insists that the uid argument
2376 			 * equal p_ruid for non-root quota access, so
2377 			 * we'll just make sure that's the case.
2378 			 */
2379 			savuid = p->p_cred->p_ruid;
2380 			p->p_cred->p_ruid = cred->cr_uid;
2381 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2382 			    cred->cr_uid, (caddr_t)&dqb))
2383 			    freenum = min(dqb.dqb_bsoftlimit, freenum);
2384 			p->p_cred->p_ruid = savuid;
2385 #endif	/* QUOTA */
2386 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2387 			uquad = (u_int64_t)freenum;
2388 			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2389 			txdr_hyper(uquad, tl);
2390 			retnum += NFSX_HYPER;
2391 			break;
2392 		case NFSATTRBIT_QUOTAUSED:
2393 			freenum = 0;
2394 #ifdef QUOTA
2395 			/*
2396 			 * ufs_quotactl() insists that the uid argument
2397 			 * equal p_ruid for non-root quota access, so
2398 			 * we'll just make sure that's the case.
2399 			 */
2400 			savuid = p->p_cred->p_ruid;
2401 			p->p_cred->p_ruid = cred->cr_uid;
2402 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2403 			    cred->cr_uid, (caddr_t)&dqb))
2404 			    freenum = dqb.dqb_curblocks;
2405 			p->p_cred->p_ruid = savuid;
2406 #endif	/* QUOTA */
2407 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2408 			uquad = (u_int64_t)freenum;
2409 			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2410 			txdr_hyper(uquad, tl);
2411 			retnum += NFSX_HYPER;
2412 			break;
2413 		case NFSATTRBIT_RAWDEV:
2414 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
2415 			*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
2416 			*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
2417 			retnum += NFSX_V4SPECDATA;
2418 			break;
2419 		case NFSATTRBIT_SPACEAVAIL:
2420 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2421 			if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE, 0))
2422 				uquad = (u_int64_t)fs.f_bfree;
2423 			else
2424 				uquad = (u_int64_t)fs.f_bavail;
2425 			uquad *= fs.f_bsize;
2426 			txdr_hyper(uquad, tl);
2427 			retnum += NFSX_HYPER;
2428 			break;
2429 		case NFSATTRBIT_SPACEFREE:
2430 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2431 			uquad = (u_int64_t)fs.f_bfree;
2432 			uquad *= fs.f_bsize;
2433 			txdr_hyper(uquad, tl);
2434 			retnum += NFSX_HYPER;
2435 			break;
2436 		case NFSATTRBIT_SPACETOTAL:
2437 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2438 			uquad = (u_int64_t)fs.f_blocks;
2439 			uquad *= fs.f_bsize;
2440 			txdr_hyper(uquad, tl);
2441 			retnum += NFSX_HYPER;
2442 			break;
2443 		case NFSATTRBIT_SPACEUSED:
2444 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2445 			txdr_hyper(vap->va_bytes, tl);
2446 			retnum += NFSX_HYPER;
2447 			break;
2448 		case NFSATTRBIT_TIMEACCESS:
2449 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2450 			txdr_nfsv4time(&vap->va_atime, tl);
2451 			retnum += NFSX_V4TIME;
2452 			break;
2453 		case NFSATTRBIT_TIMEACCESSSET:
2454 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2455 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2456 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2457 				txdr_nfsv4time(&vap->va_atime, tl);
2458 				retnum += NFSX_V4SETTIME;
2459 			} else {
2460 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2461 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2462 				retnum += NFSX_UNSIGNED;
2463 			}
2464 			break;
2465 		case NFSATTRBIT_TIMEDELTA:
2466 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2467 			temptime.tv_sec = 0;
2468 			temptime.tv_nsec = 1000000000 / hz;
2469 			txdr_nfsv4time(&temptime, tl);
2470 			retnum += NFSX_V4TIME;
2471 			break;
2472 		case NFSATTRBIT_TIMEMETADATA:
2473 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2474 			txdr_nfsv4time(&vap->va_ctime, tl);
2475 			retnum += NFSX_V4TIME;
2476 			break;
2477 		case NFSATTRBIT_TIMEMODIFY:
2478 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2479 			txdr_nfsv4time(&vap->va_mtime, tl);
2480 			retnum += NFSX_V4TIME;
2481 			break;
2482 		case NFSATTRBIT_TIMEMODIFYSET:
2483 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2484 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2485 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2486 				txdr_nfsv4time(&vap->va_mtime, tl);
2487 				retnum += NFSX_V4SETTIME;
2488 			} else {
2489 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2490 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2491 				retnum += NFSX_UNSIGNED;
2492 			}
2493 			break;
2494 		case NFSATTRBIT_MOUNTEDONFILEID:
2495 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2496 			if (at_root != 0)
2497 				uquad = mounted_on_fileno;
2498 			else
2499 				uquad = (u_int64_t)vap->va_fileid;
2500 			txdr_hyper(uquad, tl);
2501 			retnum += NFSX_HYPER;
2502 			break;
2503 		case NFSATTRBIT_SUPPATTREXCLCREAT:
2504 			NFSSETSUPP_ATTRBIT(&attrbits);
2505 			NFSCLRNOTSETABLE_ATTRBIT(&attrbits);
2506 			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
2507 			retnum += nfsrv_putattrbit(nd, &attrbits);
2508 			break;
2509 		default:
2510 			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
2511 		}
2512 	    }
2513 	}
2514 	if (naclp != NULL)
2515 		acl_free(naclp);
2516 	*retnump = txdr_unsigned(retnum);
2517 	return (retnum + prefixnum);
2518 }
2519 
2520 /*
2521  * Put the attribute bits onto an mbuf list.
2522  * Return the number of bytes of output generated.
2523  */
2524 APPLESTATIC int
2525 nfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
2526 {
2527 	u_int32_t *tl;
2528 	int cnt, i, bytesize;
2529 
2530 	for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
2531 		if (attrbitp->bits[cnt - 1])
2532 			break;
2533 	bytesize = (cnt + 1) * NFSX_UNSIGNED;
2534 	NFSM_BUILD(tl, u_int32_t *, bytesize);
2535 	*tl++ = txdr_unsigned(cnt);
2536 	for (i = 0; i < cnt; i++)
2537 		*tl++ = txdr_unsigned(attrbitp->bits[i]);
2538 	return (bytesize);
2539 }
2540 
2541 /*
2542  * Convert a uid to a string.
2543  * If the lookup fails, just output the digits.
2544  * uid - the user id
2545  * cpp - points to a buffer of size NFSV4_SMALLSTR
2546  *       (malloc a larger one, as required)
2547  * retlenp - pointer to length to be returned
2548  */
2549 APPLESTATIC void
2550 nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2551 {
2552 	int i;
2553 	struct nfsusrgrp *usrp;
2554 	u_char *cp = *cpp;
2555 	uid_t tmp;
2556 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2557 	struct nfsrv_lughash *hp;
2558 
2559 	cnt = 0;
2560 tryagain:
2561 	if (nfsrv_dnsnamelen > 0) {
2562 		/*
2563 		 * Always map nfsrv_defaultuid to "nobody".
2564 		 */
2565 		if (uid == nfsrv_defaultuid) {
2566 			i = nfsrv_dnsnamelen + 7;
2567 			if (i > len) {
2568 				if (len > NFSV4_SMALLSTR)
2569 					free(cp, M_NFSSTRING);
2570 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2571 				*cpp = cp;
2572 				len = i;
2573 				goto tryagain;
2574 			}
2575 			*retlenp = i;
2576 			NFSBCOPY("nobody@", cp, 7);
2577 			cp += 7;
2578 			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2579 			return;
2580 		}
2581 		hasampersand = 0;
2582 		hp = NFSUSERHASH(uid);
2583 		mtx_lock(&hp->mtx);
2584 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2585 			if (usrp->lug_uid == uid) {
2586 				if (usrp->lug_expiry < NFSD_MONOSEC)
2587 					break;
2588 				/*
2589 				 * If the name doesn't already have an '@'
2590 				 * in it, append @domainname to it.
2591 				 */
2592 				for (i = 0; i < usrp->lug_namelen; i++) {
2593 					if (usrp->lug_name[i] == '@') {
2594 						hasampersand = 1;
2595 						break;
2596 					}
2597 				}
2598 				if (hasampersand)
2599 					i = usrp->lug_namelen;
2600 				else
2601 					i = usrp->lug_namelen +
2602 					    nfsrv_dnsnamelen + 1;
2603 				if (i > len) {
2604 					mtx_unlock(&hp->mtx);
2605 					if (len > NFSV4_SMALLSTR)
2606 						free(cp, M_NFSSTRING);
2607 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2608 					*cpp = cp;
2609 					len = i;
2610 					goto tryagain;
2611 				}
2612 				*retlenp = i;
2613 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2614 				if (!hasampersand) {
2615 					cp += usrp->lug_namelen;
2616 					*cp++ = '@';
2617 					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2618 				}
2619 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2620 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2621 				    lug_numhash);
2622 				mtx_unlock(&hp->mtx);
2623 				return;
2624 			}
2625 		}
2626 		mtx_unlock(&hp->mtx);
2627 		cnt++;
2628 		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2629 		    NULL, p);
2630 		if (ret == 0 && cnt < 2)
2631 			goto tryagain;
2632 	}
2633 
2634 	/*
2635 	 * No match, just return a string of digits.
2636 	 */
2637 	tmp = uid;
2638 	i = 0;
2639 	while (tmp || i == 0) {
2640 		tmp /= 10;
2641 		i++;
2642 	}
2643 	len = (i > len) ? len : i;
2644 	*retlenp = len;
2645 	cp += (len - 1);
2646 	tmp = uid;
2647 	for (i = 0; i < len; i++) {
2648 		*cp-- = '0' + (tmp % 10);
2649 		tmp /= 10;
2650 	}
2651 	return;
2652 }
2653 
2654 /*
2655  * Get a credential for the uid with the server's group list.
2656  * If none is found, just return the credential passed in after
2657  * logging a warning message.
2658  */
2659 struct ucred *
2660 nfsrv_getgrpscred(struct ucred *oldcred)
2661 {
2662 	struct nfsusrgrp *usrp;
2663 	struct ucred *newcred;
2664 	int cnt, ret;
2665 	uid_t uid;
2666 	struct nfsrv_lughash *hp;
2667 
2668 	cnt = 0;
2669 	uid = oldcred->cr_uid;
2670 tryagain:
2671 	if (nfsrv_dnsnamelen > 0) {
2672 		hp = NFSUSERHASH(uid);
2673 		mtx_lock(&hp->mtx);
2674 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2675 			if (usrp->lug_uid == uid) {
2676 				if (usrp->lug_expiry < NFSD_MONOSEC)
2677 					break;
2678 				if (usrp->lug_cred != NULL) {
2679 					newcred = crhold(usrp->lug_cred);
2680 					crfree(oldcred);
2681 				} else
2682 					newcred = oldcred;
2683 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2684 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2685 				    lug_numhash);
2686 				mtx_unlock(&hp->mtx);
2687 				return (newcred);
2688 			}
2689 		}
2690 		mtx_unlock(&hp->mtx);
2691 		cnt++;
2692 		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2693 		    NULL, curthread);
2694 		if (ret == 0 && cnt < 2)
2695 			goto tryagain;
2696 	}
2697 	return (oldcred);
2698 }
2699 
2700 /*
2701  * Convert a string to a uid.
2702  * If no conversion is possible return NFSERR_BADOWNER, otherwise
2703  * return 0.
2704  * If this is called from a client side mount using AUTH_SYS and the
2705  * string is made up entirely of digits, just convert the string to
2706  * a number.
2707  */
2708 APPLESTATIC int
2709 nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
2710     NFSPROC_T *p)
2711 {
2712 	int i;
2713 	char *cp, *endstr, *str0;
2714 	struct nfsusrgrp *usrp;
2715 	int cnt, ret;
2716 	int error = 0;
2717 	uid_t tuid;
2718 	struct nfsrv_lughash *hp, *hp2;
2719 
2720 	if (len == 0) {
2721 		error = NFSERR_BADOWNER;
2722 		goto out;
2723 	}
2724 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2725 	str0 = str;
2726 	tuid = (uid_t)strtoul(str0, &endstr, 10);
2727 	if ((endstr - str0) == len) {
2728 		/* A numeric string. */
2729 		if ((nd->nd_flag & ND_KERBV) == 0 &&
2730 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2731 		      nfsd_enable_stringtouid != 0))
2732 			*uidp = tuid;
2733 		else
2734 			error = NFSERR_BADOWNER;
2735 		goto out;
2736 	}
2737 	/*
2738 	 * Look for an '@'.
2739 	 */
2740 	cp = strchr(str0, '@');
2741 	if (cp != NULL)
2742 		i = (int)(cp++ - str0);
2743 	else
2744 		i = len;
2745 
2746 	cnt = 0;
2747 tryagain:
2748 	if (nfsrv_dnsnamelen > 0) {
2749 		/*
2750 		 * If an '@' is found and the domain name matches, search for
2751 		 * the name with dns stripped off.
2752 		 * Mixed case alpahbetics will match for the domain name, but
2753 		 * all upper case will not.
2754 		 */
2755 		if (cnt == 0 && i < len && i > 0 &&
2756 		    (len - 1 - i) == nfsrv_dnsnamelen &&
2757 		    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2758 			len -= (nfsrv_dnsnamelen + 1);
2759 			*(cp - 1) = '\0';
2760 		}
2761 
2762 		/*
2763 		 * Check for the special case of "nobody".
2764 		 */
2765 		if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
2766 			*uidp = nfsrv_defaultuid;
2767 			error = 0;
2768 			goto out;
2769 		}
2770 
2771 		hp = NFSUSERNAMEHASH(str, len);
2772 		mtx_lock(&hp->mtx);
2773 		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
2774 			if (usrp->lug_namelen == len &&
2775 			    !NFSBCMP(usrp->lug_name, str, len)) {
2776 				if (usrp->lug_expiry < NFSD_MONOSEC)
2777 					break;
2778 				hp2 = NFSUSERHASH(usrp->lug_uid);
2779 				mtx_lock(&hp2->mtx);
2780 				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
2781 				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
2782 				    lug_numhash);
2783 				*uidp = usrp->lug_uid;
2784 				mtx_unlock(&hp2->mtx);
2785 				mtx_unlock(&hp->mtx);
2786 				error = 0;
2787 				goto out;
2788 			}
2789 		}
2790 		mtx_unlock(&hp->mtx);
2791 		cnt++;
2792 		ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
2793 		    str, p);
2794 		if (ret == 0 && cnt < 2)
2795 			goto tryagain;
2796 	}
2797 	error = NFSERR_BADOWNER;
2798 
2799 out:
2800 	NFSEXITCODE(error);
2801 	return (error);
2802 }
2803 
2804 /*
2805  * Convert a gid to a string.
2806  * gid - the group id
2807  * cpp - points to a buffer of size NFSV4_SMALLSTR
2808  *       (malloc a larger one, as required)
2809  * retlenp - pointer to length to be returned
2810  */
2811 APPLESTATIC void
2812 nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2813 {
2814 	int i;
2815 	struct nfsusrgrp *usrp;
2816 	u_char *cp = *cpp;
2817 	gid_t tmp;
2818 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2819 	struct nfsrv_lughash *hp;
2820 
2821 	cnt = 0;
2822 tryagain:
2823 	if (nfsrv_dnsnamelen > 0) {
2824 		/*
2825 		 * Always map nfsrv_defaultgid to "nogroup".
2826 		 */
2827 		if (gid == nfsrv_defaultgid) {
2828 			i = nfsrv_dnsnamelen + 8;
2829 			if (i > len) {
2830 				if (len > NFSV4_SMALLSTR)
2831 					free(cp, M_NFSSTRING);
2832 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2833 				*cpp = cp;
2834 				len = i;
2835 				goto tryagain;
2836 			}
2837 			*retlenp = i;
2838 			NFSBCOPY("nogroup@", cp, 8);
2839 			cp += 8;
2840 			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2841 			return;
2842 		}
2843 		hasampersand = 0;
2844 		hp = NFSGROUPHASH(gid);
2845 		mtx_lock(&hp->mtx);
2846 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2847 			if (usrp->lug_gid == gid) {
2848 				if (usrp->lug_expiry < NFSD_MONOSEC)
2849 					break;
2850 				/*
2851 				 * If the name doesn't already have an '@'
2852 				 * in it, append @domainname to it.
2853 				 */
2854 				for (i = 0; i < usrp->lug_namelen; i++) {
2855 					if (usrp->lug_name[i] == '@') {
2856 						hasampersand = 1;
2857 						break;
2858 					}
2859 				}
2860 				if (hasampersand)
2861 					i = usrp->lug_namelen;
2862 				else
2863 					i = usrp->lug_namelen +
2864 					    nfsrv_dnsnamelen + 1;
2865 				if (i > len) {
2866 					mtx_unlock(&hp->mtx);
2867 					if (len > NFSV4_SMALLSTR)
2868 						free(cp, M_NFSSTRING);
2869 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2870 					*cpp = cp;
2871 					len = i;
2872 					goto tryagain;
2873 				}
2874 				*retlenp = i;
2875 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2876 				if (!hasampersand) {
2877 					cp += usrp->lug_namelen;
2878 					*cp++ = '@';
2879 					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2880 				}
2881 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2882 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2883 				    lug_numhash);
2884 				mtx_unlock(&hp->mtx);
2885 				return;
2886 			}
2887 		}
2888 		mtx_unlock(&hp->mtx);
2889 		cnt++;
2890 		ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
2891 		    NULL, p);
2892 		if (ret == 0 && cnt < 2)
2893 			goto tryagain;
2894 	}
2895 
2896 	/*
2897 	 * No match, just return a string of digits.
2898 	 */
2899 	tmp = gid;
2900 	i = 0;
2901 	while (tmp || i == 0) {
2902 		tmp /= 10;
2903 		i++;
2904 	}
2905 	len = (i > len) ? len : i;
2906 	*retlenp = len;
2907 	cp += (len - 1);
2908 	tmp = gid;
2909 	for (i = 0; i < len; i++) {
2910 		*cp-- = '0' + (tmp % 10);
2911 		tmp /= 10;
2912 	}
2913 	return;
2914 }
2915 
2916 /*
2917  * Convert a string to a gid.
2918  * If no conversion is possible return NFSERR_BADOWNER, otherwise
2919  * return 0.
2920  * If this is called from a client side mount using AUTH_SYS and the
2921  * string is made up entirely of digits, just convert the string to
2922  * a number.
2923  */
2924 APPLESTATIC int
2925 nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
2926     NFSPROC_T *p)
2927 {
2928 	int i;
2929 	char *cp, *endstr, *str0;
2930 	struct nfsusrgrp *usrp;
2931 	int cnt, ret;
2932 	int error = 0;
2933 	gid_t tgid;
2934 	struct nfsrv_lughash *hp, *hp2;
2935 
2936 	if (len == 0) {
2937 		error =  NFSERR_BADOWNER;
2938 		goto out;
2939 	}
2940 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2941 	str0 = str;
2942 	tgid = (gid_t)strtoul(str0, &endstr, 10);
2943 	if ((endstr - str0) == len) {
2944 		/* A numeric string. */
2945 		if ((nd->nd_flag & ND_KERBV) == 0 &&
2946 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2947 		      nfsd_enable_stringtouid != 0))
2948 			*gidp = tgid;
2949 		else
2950 			error = NFSERR_BADOWNER;
2951 		goto out;
2952 	}
2953 	/*
2954 	 * Look for an '@'.
2955 	 */
2956 	cp = strchr(str0, '@');
2957 	if (cp != NULL)
2958 		i = (int)(cp++ - str0);
2959 	else
2960 		i = len;
2961 
2962 	cnt = 0;
2963 tryagain:
2964 	if (nfsrv_dnsnamelen > 0) {
2965 		/*
2966 		 * If an '@' is found and the dns name matches, search for the
2967 		 * name with the dns stripped off.
2968 		 */
2969 		if (cnt == 0 && i < len && i > 0 &&
2970 		    (len - 1 - i) == nfsrv_dnsnamelen &&
2971 		    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2972 			len -= (nfsrv_dnsnamelen + 1);
2973 			*(cp - 1) = '\0';
2974 		}
2975 
2976 		/*
2977 		 * Check for the special case of "nogroup".
2978 		 */
2979 		if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
2980 			*gidp = nfsrv_defaultgid;
2981 			error = 0;
2982 			goto out;
2983 		}
2984 
2985 		hp = NFSGROUPNAMEHASH(str, len);
2986 		mtx_lock(&hp->mtx);
2987 		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
2988 			if (usrp->lug_namelen == len &&
2989 			    !NFSBCMP(usrp->lug_name, str, len)) {
2990 				if (usrp->lug_expiry < NFSD_MONOSEC)
2991 					break;
2992 				hp2 = NFSGROUPHASH(usrp->lug_gid);
2993 				mtx_lock(&hp2->mtx);
2994 				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
2995 				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
2996 				    lug_numhash);
2997 				*gidp = usrp->lug_gid;
2998 				mtx_unlock(&hp2->mtx);
2999 				mtx_unlock(&hp->mtx);
3000 				error = 0;
3001 				goto out;
3002 			}
3003 		}
3004 		mtx_unlock(&hp->mtx);
3005 		cnt++;
3006 		ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
3007 		    str, p);
3008 		if (ret == 0 && cnt < 2)
3009 			goto tryagain;
3010 	}
3011 	error = NFSERR_BADOWNER;
3012 
3013 out:
3014 	NFSEXITCODE(error);
3015 	return (error);
3016 }
3017 
3018 /*
3019  * Cmp len chars, allowing mixed case in the first argument to match lower
3020  * case in the second, but not if the first argument is all upper case.
3021  * Return 0 for a match, 1 otherwise.
3022  */
3023 static int
3024 nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len)
3025 {
3026 	int i;
3027 	u_char tmp;
3028 	int fndlower = 0;
3029 
3030 	for (i = 0; i < len; i++) {
3031 		if (*cp >= 'A' && *cp <= 'Z') {
3032 			tmp = *cp++ + ('a' - 'A');
3033 		} else {
3034 			tmp = *cp++;
3035 			if (tmp >= 'a' && tmp <= 'z')
3036 				fndlower = 1;
3037 		}
3038 		if (tmp != *cp2++)
3039 			return (1);
3040 	}
3041 	if (fndlower)
3042 		return (0);
3043 	else
3044 		return (1);
3045 }
3046 
3047 /*
3048  * Set the port for the nfsuserd.
3049  */
3050 APPLESTATIC int
3051 nfsrv_nfsuserdport(u_short port, NFSPROC_T *p)
3052 {
3053 	struct nfssockreq *rp;
3054 	struct sockaddr_in *ad;
3055 	int error;
3056 
3057 	NFSLOCKNAMEID();
3058 	if (nfsrv_nfsuserd) {
3059 		NFSUNLOCKNAMEID();
3060 		error = EPERM;
3061 		goto out;
3062 	}
3063 	nfsrv_nfsuserd = 1;
3064 	NFSUNLOCKNAMEID();
3065 	/*
3066 	 * Set up the socket record and connect.
3067 	 */
3068 	rp = &nfsrv_nfsuserdsock;
3069 	rp->nr_client = NULL;
3070 	rp->nr_sotype = SOCK_DGRAM;
3071 	rp->nr_soproto = IPPROTO_UDP;
3072 	rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
3073 	rp->nr_cred = NULL;
3074 	NFSSOCKADDRALLOC(rp->nr_nam);
3075 	NFSSOCKADDRSIZE(rp->nr_nam, sizeof (struct sockaddr_in));
3076 	ad = NFSSOCKADDR(rp->nr_nam, struct sockaddr_in *);
3077 	ad->sin_family = AF_INET;
3078 	ad->sin_addr.s_addr = htonl((u_int32_t)0x7f000001);	/* 127.0.0.1 */
3079 	ad->sin_port = port;
3080 	rp->nr_prog = RPCPROG_NFSUSERD;
3081 	rp->nr_vers = RPCNFSUSERD_VERS;
3082 	error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0);
3083 	if (error) {
3084 		NFSSOCKADDRFREE(rp->nr_nam);
3085 		nfsrv_nfsuserd = 0;
3086 	}
3087 out:
3088 	NFSEXITCODE(error);
3089 	return (error);
3090 }
3091 
3092 /*
3093  * Delete the nfsuserd port.
3094  */
3095 APPLESTATIC void
3096 nfsrv_nfsuserddelport(void)
3097 {
3098 
3099 	NFSLOCKNAMEID();
3100 	if (nfsrv_nfsuserd == 0) {
3101 		NFSUNLOCKNAMEID();
3102 		return;
3103 	}
3104 	nfsrv_nfsuserd = 0;
3105 	NFSUNLOCKNAMEID();
3106 	newnfs_disconnect(&nfsrv_nfsuserdsock);
3107 	NFSSOCKADDRFREE(nfsrv_nfsuserdsock.nr_nam);
3108 }
3109 
3110 /*
3111  * Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
3112  * name<-->id cache.
3113  * Returns 0 upon success, non-zero otherwise.
3114  */
3115 static int
3116 nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name, NFSPROC_T *p)
3117 {
3118 	u_int32_t *tl;
3119 	struct nfsrv_descript *nd;
3120 	int len;
3121 	struct nfsrv_descript nfsd;
3122 	struct ucred *cred;
3123 	int error;
3124 
3125 	NFSLOCKNAMEID();
3126 	if (nfsrv_nfsuserd == 0) {
3127 		NFSUNLOCKNAMEID();
3128 		error = EPERM;
3129 		goto out;
3130 	}
3131 	NFSUNLOCKNAMEID();
3132 	nd = &nfsd;
3133 	cred = newnfs_getcred();
3134 	nd->nd_flag = ND_GSSINITREPLY;
3135 	nfsrvd_rephead(nd);
3136 
3137 	nd->nd_procnum = procnum;
3138 	if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
3139 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3140 		if (procnum == RPCNFSUSERD_GETUID)
3141 			*tl = txdr_unsigned(uid);
3142 		else
3143 			*tl = txdr_unsigned(gid);
3144 	} else {
3145 		len = strlen(name);
3146 		(void) nfsm_strtom(nd, name, len);
3147 	}
3148 	error = newnfs_request(nd, NULL, NULL, &nfsrv_nfsuserdsock, NULL, NULL,
3149 		cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0, NULL, NULL);
3150 	NFSFREECRED(cred);
3151 	if (!error) {
3152 		mbuf_freem(nd->nd_mrep);
3153 		error = nd->nd_repstat;
3154 	}
3155 out:
3156 	NFSEXITCODE(error);
3157 	return (error);
3158 }
3159 
3160 /*
3161  * This function is called from the nfssvc(2) system call, to update the
3162  * kernel user/group name list(s) for the V4 owner and ownergroup attributes.
3163  */
3164 APPLESTATIC int
3165 nfssvc_idname(struct nfsd_idargs *nidp)
3166 {
3167 	struct nfsusrgrp *nusrp, *usrp, *newusrp;
3168 	struct nfsrv_lughash *hp_name, *hp_idnum, *thp;
3169 	int i, group_locked, groupname_locked, user_locked, username_locked;
3170 	int error = 0;
3171 	u_char *cp;
3172 	gid_t *grps;
3173 	struct ucred *cr;
3174 	static int onethread = 0;
3175 	static time_t lasttime = 0;
3176 
3177 	if (nidp->nid_namelen <= 0 || nidp->nid_namelen > MAXHOSTNAMELEN) {
3178 		error = EINVAL;
3179 		goto out;
3180 	}
3181 	if (nidp->nid_flag & NFSID_INITIALIZE) {
3182 		cp = malloc(nidp->nid_namelen + 1, M_NFSSTRING, M_WAITOK);
3183 		error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
3184 		    nidp->nid_namelen);
3185 		if (error != 0) {
3186 			free(cp, M_NFSSTRING);
3187 			goto out;
3188 		}
3189 		if (atomic_cmpset_acq_int(&nfsrv_dnsnamelen, 0, 0) == 0) {
3190 			/*
3191 			 * Free up all the old stuff and reinitialize hash
3192 			 * lists.  All mutexes for both lists must be locked,
3193 			 * with the user/group name ones before the uid/gid
3194 			 * ones, to avoid a LOR.
3195 			 */
3196 			for (i = 0; i < nfsrv_lughashsize; i++)
3197 				mtx_lock(&nfsusernamehash[i].mtx);
3198 			for (i = 0; i < nfsrv_lughashsize; i++)
3199 				mtx_lock(&nfsuserhash[i].mtx);
3200 			for (i = 0; i < nfsrv_lughashsize; i++)
3201 				TAILQ_FOREACH_SAFE(usrp,
3202 				    &nfsuserhash[i].lughead, lug_numhash, nusrp)
3203 					nfsrv_removeuser(usrp, 1);
3204 			for (i = 0; i < nfsrv_lughashsize; i++)
3205 				mtx_unlock(&nfsuserhash[i].mtx);
3206 			for (i = 0; i < nfsrv_lughashsize; i++)
3207 				mtx_unlock(&nfsusernamehash[i].mtx);
3208 			for (i = 0; i < nfsrv_lughashsize; i++)
3209 				mtx_lock(&nfsgroupnamehash[i].mtx);
3210 			for (i = 0; i < nfsrv_lughashsize; i++)
3211 				mtx_lock(&nfsgrouphash[i].mtx);
3212 			for (i = 0; i < nfsrv_lughashsize; i++)
3213 				TAILQ_FOREACH_SAFE(usrp,
3214 				    &nfsgrouphash[i].lughead, lug_numhash,
3215 				    nusrp)
3216 					nfsrv_removeuser(usrp, 0);
3217 			for (i = 0; i < nfsrv_lughashsize; i++)
3218 				mtx_unlock(&nfsgrouphash[i].mtx);
3219 			for (i = 0; i < nfsrv_lughashsize; i++)
3220 				mtx_unlock(&nfsgroupnamehash[i].mtx);
3221 			free(nfsrv_dnsname, M_NFSSTRING);
3222 			nfsrv_dnsname = NULL;
3223 		}
3224 		if (nfsuserhash == NULL) {
3225 			/* Allocate the hash tables. */
3226 			nfsuserhash = malloc(sizeof(struct nfsrv_lughash) *
3227 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3228 			    M_ZERO);
3229 			for (i = 0; i < nfsrv_lughashsize; i++)
3230 				mtx_init(&nfsuserhash[i].mtx, "nfsuidhash",
3231 				    NULL, MTX_DEF | MTX_DUPOK);
3232 			nfsusernamehash = malloc(sizeof(struct nfsrv_lughash) *
3233 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3234 			    M_ZERO);
3235 			for (i = 0; i < nfsrv_lughashsize; i++)
3236 				mtx_init(&nfsusernamehash[i].mtx,
3237 				    "nfsusrhash", NULL, MTX_DEF |
3238 				    MTX_DUPOK);
3239 			nfsgrouphash = malloc(sizeof(struct nfsrv_lughash) *
3240 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3241 			    M_ZERO);
3242 			for (i = 0; i < nfsrv_lughashsize; i++)
3243 				mtx_init(&nfsgrouphash[i].mtx, "nfsgidhash",
3244 				    NULL, MTX_DEF | MTX_DUPOK);
3245 			nfsgroupnamehash = malloc(sizeof(struct nfsrv_lughash) *
3246 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3247 			    M_ZERO);
3248 			for (i = 0; i < nfsrv_lughashsize; i++)
3249 			    mtx_init(&nfsgroupnamehash[i].mtx,
3250 			    "nfsgrphash", NULL, MTX_DEF | MTX_DUPOK);
3251 		}
3252 		/* (Re)initialize the list heads. */
3253 		for (i = 0; i < nfsrv_lughashsize; i++)
3254 			TAILQ_INIT(&nfsuserhash[i].lughead);
3255 		for (i = 0; i < nfsrv_lughashsize; i++)
3256 			TAILQ_INIT(&nfsusernamehash[i].lughead);
3257 		for (i = 0; i < nfsrv_lughashsize; i++)
3258 			TAILQ_INIT(&nfsgrouphash[i].lughead);
3259 		for (i = 0; i < nfsrv_lughashsize; i++)
3260 			TAILQ_INIT(&nfsgroupnamehash[i].lughead);
3261 
3262 		/*
3263 		 * Put name in "DNS" string.
3264 		 */
3265 		nfsrv_dnsname = cp;
3266 		nfsrv_defaultuid = nidp->nid_uid;
3267 		nfsrv_defaultgid = nidp->nid_gid;
3268 		nfsrv_usercnt = 0;
3269 		nfsrv_usermax = nidp->nid_usermax;
3270 		atomic_store_rel_int(&nfsrv_dnsnamelen, nidp->nid_namelen);
3271 		goto out;
3272 	}
3273 
3274 	/*
3275 	 * malloc the new one now, so any potential sleep occurs before
3276 	 * manipulation of the lists.
3277 	 */
3278 	newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
3279 	    M_NFSUSERGROUP, M_WAITOK | M_ZERO);
3280 	error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
3281 	    nidp->nid_namelen);
3282 	if (error == 0 && nidp->nid_ngroup > 0 &&
3283 	    (nidp->nid_flag & NFSID_ADDUID) != 0) {
3284 		grps = malloc(sizeof(gid_t) * nidp->nid_ngroup, M_TEMP,
3285 		    M_WAITOK);
3286 		error = copyin(CAST_USER_ADDR_T(nidp->nid_grps), grps,
3287 		    sizeof(gid_t) * nidp->nid_ngroup);
3288 		if (error == 0) {
3289 			/*
3290 			 * Create a credential just like svc_getcred(),
3291 			 * but using the group list provided.
3292 			 */
3293 			cr = crget();
3294 			cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid;
3295 			crsetgroups(cr, nidp->nid_ngroup, grps);
3296 			cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
3297 			cr->cr_prison = &prison0;
3298 			prison_hold(cr->cr_prison);
3299 #ifdef MAC
3300 			mac_cred_associate_nfsd(cr);
3301 #endif
3302 			newusrp->lug_cred = cr;
3303 		}
3304 		free(grps, M_TEMP);
3305 	}
3306 	if (error) {
3307 		free(newusrp, M_NFSUSERGROUP);
3308 		goto out;
3309 	}
3310 	newusrp->lug_namelen = nidp->nid_namelen;
3311 
3312 	/*
3313 	 * The lock order is username[0]->[nfsrv_lughashsize - 1] followed
3314 	 * by uid[0]->[nfsrv_lughashsize - 1], with the same for group.
3315 	 * The flags user_locked, username_locked, group_locked and
3316 	 * groupname_locked are set to indicate all of those hash lists are
3317 	 * locked. hp_name != NULL  and hp_idnum != NULL indicates that
3318 	 * the respective one mutex is locked.
3319 	 */
3320 	user_locked = username_locked = group_locked = groupname_locked = 0;
3321 	hp_name = hp_idnum = NULL;
3322 
3323 	/*
3324 	 * Delete old entries, as required.
3325 	 */
3326 	if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
3327 		/* Must lock all username hash lists first, to avoid a LOR. */
3328 		for (i = 0; i < nfsrv_lughashsize; i++)
3329 			mtx_lock(&nfsusernamehash[i].mtx);
3330 		username_locked = 1;
3331 		hp_idnum = NFSUSERHASH(nidp->nid_uid);
3332 		mtx_lock(&hp_idnum->mtx);
3333 		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3334 		    nusrp) {
3335 			if (usrp->lug_uid == nidp->nid_uid)
3336 				nfsrv_removeuser(usrp, 1);
3337 		}
3338 	} else if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
3339 		hp_name = NFSUSERNAMEHASH(newusrp->lug_name,
3340 		    newusrp->lug_namelen);
3341 		mtx_lock(&hp_name->mtx);
3342 		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3343 		    nusrp) {
3344 			if (usrp->lug_namelen == newusrp->lug_namelen &&
3345 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3346 			    usrp->lug_namelen)) {
3347 				thp = NFSUSERHASH(usrp->lug_uid);
3348 				mtx_lock(&thp->mtx);
3349 				nfsrv_removeuser(usrp, 1);
3350 				mtx_unlock(&thp->mtx);
3351 			}
3352 		}
3353 		hp_idnum = NFSUSERHASH(nidp->nid_uid);
3354 		mtx_lock(&hp_idnum->mtx);
3355 	} else if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
3356 		/* Must lock all groupname hash lists first, to avoid a LOR. */
3357 		for (i = 0; i < nfsrv_lughashsize; i++)
3358 			mtx_lock(&nfsgroupnamehash[i].mtx);
3359 		groupname_locked = 1;
3360 		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3361 		mtx_lock(&hp_idnum->mtx);
3362 		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3363 		    nusrp) {
3364 			if (usrp->lug_gid == nidp->nid_gid)
3365 				nfsrv_removeuser(usrp, 0);
3366 		}
3367 	} else if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
3368 		hp_name = NFSGROUPNAMEHASH(newusrp->lug_name,
3369 		    newusrp->lug_namelen);
3370 		mtx_lock(&hp_name->mtx);
3371 		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3372 		    nusrp) {
3373 			if (usrp->lug_namelen == newusrp->lug_namelen &&
3374 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3375 			    usrp->lug_namelen)) {
3376 				thp = NFSGROUPHASH(usrp->lug_gid);
3377 				mtx_lock(&thp->mtx);
3378 				nfsrv_removeuser(usrp, 0);
3379 				mtx_unlock(&thp->mtx);
3380 			}
3381 		}
3382 		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3383 		mtx_lock(&hp_idnum->mtx);
3384 	}
3385 
3386 	/*
3387 	 * Now, we can add the new one.
3388 	 */
3389 	if (nidp->nid_usertimeout)
3390 		newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
3391 	else
3392 		newusrp->lug_expiry = NFSD_MONOSEC + 5;
3393 	if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
3394 		newusrp->lug_uid = nidp->nid_uid;
3395 		thp = NFSUSERHASH(newusrp->lug_uid);
3396 		mtx_assert(&thp->mtx, MA_OWNED);
3397 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3398 		thp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3399 		mtx_assert(&thp->mtx, MA_OWNED);
3400 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3401 		atomic_add_int(&nfsrv_usercnt, 1);
3402 	} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
3403 		newusrp->lug_gid = nidp->nid_gid;
3404 		thp = NFSGROUPHASH(newusrp->lug_gid);
3405 		mtx_assert(&thp->mtx, MA_OWNED);
3406 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3407 		thp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3408 		mtx_assert(&thp->mtx, MA_OWNED);
3409 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3410 		atomic_add_int(&nfsrv_usercnt, 1);
3411 	} else {
3412 		if (newusrp->lug_cred != NULL)
3413 			crfree(newusrp->lug_cred);
3414 		free(newusrp, M_NFSUSERGROUP);
3415 	}
3416 
3417 	/*
3418 	 * Once per second, allow one thread to trim the cache.
3419 	 */
3420 	if (lasttime < NFSD_MONOSEC &&
3421 	    atomic_cmpset_acq_int(&onethread, 0, 1) != 0) {
3422 		/*
3423 		 * First, unlock the single mutexes, so that all entries
3424 		 * can be locked and any LOR is avoided.
3425 		 */
3426 		if (hp_name != NULL) {
3427 			mtx_unlock(&hp_name->mtx);
3428 			hp_name = NULL;
3429 		}
3430 		if (hp_idnum != NULL) {
3431 			mtx_unlock(&hp_idnum->mtx);
3432 			hp_idnum = NULL;
3433 		}
3434 
3435 		if ((nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID |
3436 		    NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) != 0) {
3437 			if (username_locked == 0) {
3438 				for (i = 0; i < nfsrv_lughashsize; i++)
3439 					mtx_lock(&nfsusernamehash[i].mtx);
3440 				username_locked = 1;
3441 			}
3442 			KASSERT(user_locked == 0,
3443 			    ("nfssvc_idname: user_locked"));
3444 			for (i = 0; i < nfsrv_lughashsize; i++)
3445 				mtx_lock(&nfsuserhash[i].mtx);
3446 			user_locked = 1;
3447 			for (i = 0; i < nfsrv_lughashsize; i++) {
3448 				TAILQ_FOREACH_SAFE(usrp,
3449 				    &nfsuserhash[i].lughead, lug_numhash,
3450 				    nusrp)
3451 					if (usrp->lug_expiry < NFSD_MONOSEC)
3452 						nfsrv_removeuser(usrp, 1);
3453 			}
3454 			for (i = 0; i < nfsrv_lughashsize; i++) {
3455 				/*
3456 				 * Trim the cache using an approximate LRU
3457 				 * algorithm.  This code deletes the least
3458 				 * recently used entry on each hash list.
3459 				 */
3460 				if (nfsrv_usercnt <= nfsrv_usermax)
3461 					break;
3462 				usrp = TAILQ_FIRST(&nfsuserhash[i].lughead);
3463 				if (usrp != NULL)
3464 					nfsrv_removeuser(usrp, 1);
3465 			}
3466 		} else {
3467 			if (groupname_locked == 0) {
3468 				for (i = 0; i < nfsrv_lughashsize; i++)
3469 					mtx_lock(&nfsgroupnamehash[i].mtx);
3470 				groupname_locked = 1;
3471 			}
3472 			KASSERT(group_locked == 0,
3473 			    ("nfssvc_idname: group_locked"));
3474 			for (i = 0; i < nfsrv_lughashsize; i++)
3475 				mtx_lock(&nfsgrouphash[i].mtx);
3476 			group_locked = 1;
3477 			for (i = 0; i < nfsrv_lughashsize; i++) {
3478 				TAILQ_FOREACH_SAFE(usrp,
3479 				    &nfsgrouphash[i].lughead, lug_numhash,
3480 				    nusrp)
3481 					if (usrp->lug_expiry < NFSD_MONOSEC)
3482 						nfsrv_removeuser(usrp, 0);
3483 			}
3484 			for (i = 0; i < nfsrv_lughashsize; i++) {
3485 				/*
3486 				 * Trim the cache using an approximate LRU
3487 				 * algorithm.  This code deletes the least
3488 				 * recently user entry on each hash list.
3489 				 */
3490 				if (nfsrv_usercnt <= nfsrv_usermax)
3491 					break;
3492 				usrp = TAILQ_FIRST(&nfsgrouphash[i].lughead);
3493 				if (usrp != NULL)
3494 					nfsrv_removeuser(usrp, 0);
3495 			}
3496 		}
3497 		lasttime = NFSD_MONOSEC;
3498 		atomic_store_rel_int(&onethread, 0);
3499 	}
3500 
3501 	/* Now, unlock all locked mutexes. */
3502 	if (hp_idnum != NULL)
3503 		mtx_unlock(&hp_idnum->mtx);
3504 	if (hp_name != NULL)
3505 		mtx_unlock(&hp_name->mtx);
3506 	if (user_locked != 0)
3507 		for (i = 0; i < nfsrv_lughashsize; i++)
3508 			mtx_unlock(&nfsuserhash[i].mtx);
3509 	if (username_locked != 0)
3510 		for (i = 0; i < nfsrv_lughashsize; i++)
3511 			mtx_unlock(&nfsusernamehash[i].mtx);
3512 	if (group_locked != 0)
3513 		for (i = 0; i < nfsrv_lughashsize; i++)
3514 			mtx_unlock(&nfsgrouphash[i].mtx);
3515 	if (groupname_locked != 0)
3516 		for (i = 0; i < nfsrv_lughashsize; i++)
3517 			mtx_unlock(&nfsgroupnamehash[i].mtx);
3518 out:
3519 	NFSEXITCODE(error);
3520 	return (error);
3521 }
3522 
3523 /*
3524  * Remove a user/group name element.
3525  */
3526 static void
3527 nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
3528 {
3529 	struct nfsrv_lughash *hp;
3530 
3531 	if (isuser != 0) {
3532 		hp = NFSUSERHASH(usrp->lug_uid);
3533 		mtx_assert(&hp->mtx, MA_OWNED);
3534 		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3535 		hp = NFSUSERNAMEHASH(usrp->lug_name, usrp->lug_namelen);
3536 		mtx_assert(&hp->mtx, MA_OWNED);
3537 		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
3538 	} else {
3539 		hp = NFSGROUPHASH(usrp->lug_gid);
3540 		mtx_assert(&hp->mtx, MA_OWNED);
3541 		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3542 		hp = NFSGROUPNAMEHASH(usrp->lug_name, usrp->lug_namelen);
3543 		mtx_assert(&hp->mtx, MA_OWNED);
3544 		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
3545 	}
3546 	atomic_add_int(&nfsrv_usercnt, -1);
3547 	if (usrp->lug_cred != NULL)
3548 		crfree(usrp->lug_cred);
3549 	free(usrp, M_NFSUSERGROUP);
3550 }
3551 
3552 /*
3553  * Free up all the allocations related to the name<-->id cache.
3554  * This function should only be called when the nfsuserd daemon isn't
3555  * running, since it doesn't do any locking.
3556  * This function is meant to be used when the nfscommon module is unloaded.
3557  */
3558 APPLESTATIC void
3559 nfsrv_cleanusergroup(void)
3560 {
3561 	struct nfsrv_lughash *hp, *hp2;
3562 	struct nfsusrgrp *nusrp, *usrp;
3563 	int i;
3564 
3565 	if (nfsuserhash == NULL)
3566 		return;
3567 
3568 	for (i = 0; i < nfsrv_lughashsize; i++) {
3569 		hp = &nfsuserhash[i];
3570 		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
3571 			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3572 			hp2 = NFSUSERNAMEHASH(usrp->lug_name,
3573 			    usrp->lug_namelen);
3574 			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
3575 			if (usrp->lug_cred != NULL)
3576 				crfree(usrp->lug_cred);
3577 			free(usrp, M_NFSUSERGROUP);
3578 		}
3579 		hp = &nfsgrouphash[i];
3580 		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
3581 			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3582 			hp2 = NFSGROUPNAMEHASH(usrp->lug_name,
3583 			    usrp->lug_namelen);
3584 			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
3585 			if (usrp->lug_cred != NULL)
3586 				crfree(usrp->lug_cred);
3587 			free(usrp, M_NFSUSERGROUP);
3588 		}
3589 		mtx_destroy(&nfsuserhash[i].mtx);
3590 		mtx_destroy(&nfsusernamehash[i].mtx);
3591 		mtx_destroy(&nfsgroupnamehash[i].mtx);
3592 		mtx_destroy(&nfsgrouphash[i].mtx);
3593 	}
3594 	free(nfsuserhash, M_NFSUSERGROUP);
3595 	free(nfsusernamehash, M_NFSUSERGROUP);
3596 	free(nfsgrouphash, M_NFSUSERGROUP);
3597 	free(nfsgroupnamehash, M_NFSUSERGROUP);
3598 	free(nfsrv_dnsname, M_NFSSTRING);
3599 }
3600 
3601 /*
3602  * This function scans a byte string and checks for UTF-8 compliance.
3603  * It returns 0 if it conforms and NFSERR_INVAL if not.
3604  */
3605 APPLESTATIC int
3606 nfsrv_checkutf8(u_int8_t *cp, int len)
3607 {
3608 	u_int32_t val = 0x0;
3609 	int cnt = 0, gotd = 0, shift = 0;
3610 	u_int8_t byte;
3611 	static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
3612 	int error = 0;
3613 
3614 	/*
3615 	 * Here are what the variables are used for:
3616 	 * val - the calculated value of a multibyte char, used to check
3617 	 *       that it was coded with the correct range
3618 	 * cnt - the number of 10xxxxxx bytes to follow
3619 	 * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
3620 	 * shift - lower order bits of range (ie. "val >> shift" should
3621 	 *       not be 0, in other words, dividing by the lower bound
3622 	 *       of the range should get a non-zero value)
3623 	 * byte - used to calculate cnt
3624 	 */
3625 	while (len > 0) {
3626 		if (cnt > 0) {
3627 			/* This handles the 10xxxxxx bytes */
3628 			if ((*cp & 0xc0) != 0x80 ||
3629 			    (gotd && (*cp & 0x20))) {
3630 				error = NFSERR_INVAL;
3631 				goto out;
3632 			}
3633 			gotd = 0;
3634 			val <<= 6;
3635 			val |= (*cp & 0x3f);
3636 			cnt--;
3637 			if (cnt == 0 && (val >> shift) == 0x0) {
3638 				error = NFSERR_INVAL;
3639 				goto out;
3640 			}
3641 		} else if (*cp & 0x80) {
3642 			/* first byte of multi byte char */
3643 			byte = *cp;
3644 			while ((byte & 0x40) && cnt < 6) {
3645 				cnt++;
3646 				byte <<= 1;
3647 			}
3648 			if (cnt == 0 || cnt == 6) {
3649 				error = NFSERR_INVAL;
3650 				goto out;
3651 			}
3652 			val = (*cp & (0x3f >> cnt));
3653 			shift = utf8_shift[cnt - 1];
3654 			if (cnt == 2 && val == 0xd)
3655 				/* Check for the 0xd800-0xdfff case */
3656 				gotd = 1;
3657 		}
3658 		cp++;
3659 		len--;
3660 	}
3661 	if (cnt > 0)
3662 		error = NFSERR_INVAL;
3663 
3664 out:
3665 	NFSEXITCODE(error);
3666 	return (error);
3667 }
3668 
3669 /*
3670  * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
3671  * strings, one with the root path in it and the other with the list of
3672  * locations. The list is in the same format as is found in nfr_refs.
3673  * It is a "," separated list of entries, where each of them is of the
3674  * form <server>:<rootpath>. For example
3675  * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
3676  * The nilp argument is set to 1 for the special case of a null fs_root
3677  * and an empty server list.
3678  * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
3679  * number of xdr bytes parsed in sump.
3680  */
3681 static int
3682 nfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
3683     int *sump, int *nilp)
3684 {
3685 	u_int32_t *tl;
3686 	u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
3687 	int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
3688 	struct list {
3689 		SLIST_ENTRY(list) next;
3690 		int len;
3691 		u_char host[1];
3692 	} *lsp, *nlsp;
3693 	SLIST_HEAD(, list) head;
3694 
3695 	*fsrootp = NULL;
3696 	*srvp = NULL;
3697 	*nilp = 0;
3698 
3699 	/*
3700 	 * Get the fs_root path and check for the special case of null path
3701 	 * and 0 length server list.
3702 	 */
3703 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3704 	len = fxdr_unsigned(int, *tl);
3705 	if (len < 0 || len > 10240) {
3706 		error = NFSERR_BADXDR;
3707 		goto nfsmout;
3708 	}
3709 	if (len == 0) {
3710 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3711 		if (*tl != 0) {
3712 			error = NFSERR_BADXDR;
3713 			goto nfsmout;
3714 		}
3715 		*nilp = 1;
3716 		*sump = 2 * NFSX_UNSIGNED;
3717 		error = 0;
3718 		goto nfsmout;
3719 	}
3720 	cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
3721 	error = nfsrv_mtostr(nd, cp, len);
3722 	if (!error) {
3723 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3724 		cnt = fxdr_unsigned(int, *tl);
3725 		if (cnt <= 0)
3726 			error = NFSERR_BADXDR;
3727 	}
3728 	if (error)
3729 		goto nfsmout;
3730 
3731 	/*
3732 	 * Now, loop through the location list and make up the srvlist.
3733 	 */
3734 	xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3735 	cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
3736 	slen = 1024;
3737 	siz = 0;
3738 	for (i = 0; i < cnt; i++) {
3739 		SLIST_INIT(&head);
3740 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3741 		nsrv = fxdr_unsigned(int, *tl);
3742 		if (nsrv <= 0) {
3743 			error = NFSERR_BADXDR;
3744 			goto nfsmout;
3745 		}
3746 
3747 		/*
3748 		 * Handle the first server by putting it in the srvstr.
3749 		 */
3750 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3751 		len = fxdr_unsigned(int, *tl);
3752 		if (len <= 0 || len > 1024) {
3753 			error = NFSERR_BADXDR;
3754 			goto nfsmout;
3755 		}
3756 		nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
3757 		if (cp3 != cp2) {
3758 			*cp3++ = ',';
3759 			siz++;
3760 		}
3761 		error = nfsrv_mtostr(nd, cp3, len);
3762 		if (error)
3763 			goto nfsmout;
3764 		cp3 += len;
3765 		*cp3++ = ':';
3766 		siz += (len + 1);
3767 		xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3768 		for (j = 1; j < nsrv; j++) {
3769 			/*
3770 			 * Yuck, put them in an slist and process them later.
3771 			 */
3772 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3773 			len = fxdr_unsigned(int, *tl);
3774 			if (len <= 0 || len > 1024) {
3775 				error = NFSERR_BADXDR;
3776 				goto nfsmout;
3777 			}
3778 			lsp = (struct list *)malloc(sizeof (struct list)
3779 			    + len, M_TEMP, M_WAITOK);
3780 			error = nfsrv_mtostr(nd, lsp->host, len);
3781 			if (error)
3782 				goto nfsmout;
3783 			xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3784 			lsp->len = len;
3785 			SLIST_INSERT_HEAD(&head, lsp, next);
3786 		}
3787 
3788 		/*
3789 		 * Finally, we can get the path.
3790 		 */
3791 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3792 		len = fxdr_unsigned(int, *tl);
3793 		if (len <= 0 || len > 1024) {
3794 			error = NFSERR_BADXDR;
3795 			goto nfsmout;
3796 		}
3797 		nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
3798 		error = nfsrv_mtostr(nd, cp3, len);
3799 		if (error)
3800 			goto nfsmout;
3801 		xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3802 		str = cp3;
3803 		stringlen = len;
3804 		cp3 += len;
3805 		siz += len;
3806 		SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
3807 			nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
3808 			    &cp2, &cp3, &slen);
3809 			*cp3++ = ',';
3810 			NFSBCOPY(lsp->host, cp3, lsp->len);
3811 			cp3 += lsp->len;
3812 			*cp3++ = ':';
3813 			NFSBCOPY(str, cp3, stringlen);
3814 			cp3 += stringlen;
3815 			*cp3 = '\0';
3816 			siz += (lsp->len + stringlen + 2);
3817 			free((caddr_t)lsp, M_TEMP);
3818 		}
3819 	}
3820 	*fsrootp = cp;
3821 	*srvp = cp2;
3822 	*sump = xdrsum;
3823 	NFSEXITCODE2(0, nd);
3824 	return (0);
3825 nfsmout:
3826 	if (cp != NULL)
3827 		free(cp, M_NFSSTRING);
3828 	if (cp2 != NULL)
3829 		free(cp2, M_NFSSTRING);
3830 	NFSEXITCODE2(error, nd);
3831 	return (error);
3832 }
3833 
3834 /*
3835  * Make the malloc'd space large enough. This is a pain, but the xdr
3836  * doesn't set an upper bound on the side, so...
3837  */
3838 static void
3839 nfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
3840 {
3841 	u_char *cp;
3842 	int i;
3843 
3844 	if (siz <= *slenp)
3845 		return;
3846 	cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
3847 	NFSBCOPY(*cpp, cp, *slenp);
3848 	free(*cpp, M_NFSSTRING);
3849 	i = *cpp2 - *cpp;
3850 	*cpp = cp;
3851 	*cpp2 = cp + i;
3852 	*slenp = siz + 1024;
3853 }
3854 
3855 /*
3856  * Initialize the reply header data structures.
3857  */
3858 APPLESTATIC void
3859 nfsrvd_rephead(struct nfsrv_descript *nd)
3860 {
3861 	mbuf_t mreq;
3862 
3863 	/*
3864 	 * If this is a big reply, use a cluster.
3865 	 */
3866 	if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
3867 	    nfs_bigreply[nd->nd_procnum]) {
3868 		NFSMCLGET(mreq, M_WAITOK);
3869 		nd->nd_mreq = mreq;
3870 		nd->nd_mb = mreq;
3871 	} else {
3872 		NFSMGET(mreq);
3873 		nd->nd_mreq = mreq;
3874 		nd->nd_mb = mreq;
3875 	}
3876 	nd->nd_bpos = NFSMTOD(mreq, caddr_t);
3877 	mbuf_setlen(mreq, 0);
3878 
3879 	if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
3880 		NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
3881 }
3882 
3883 /*
3884  * Lock a socket against others.
3885  * Currently used to serialize connect/disconnect attempts.
3886  */
3887 int
3888 newnfs_sndlock(int *flagp)
3889 {
3890 	struct timespec ts;
3891 
3892 	NFSLOCKSOCK();
3893 	while (*flagp & NFSR_SNDLOCK) {
3894 		*flagp |= NFSR_WANTSND;
3895 		ts.tv_sec = 0;
3896 		ts.tv_nsec = 0;
3897 		(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
3898 		    PZERO - 1, "nfsndlck", &ts);
3899 	}
3900 	*flagp |= NFSR_SNDLOCK;
3901 	NFSUNLOCKSOCK();
3902 	return (0);
3903 }
3904 
3905 /*
3906  * Unlock the stream socket for others.
3907  */
3908 void
3909 newnfs_sndunlock(int *flagp)
3910 {
3911 
3912 	NFSLOCKSOCK();
3913 	if ((*flagp & NFSR_SNDLOCK) == 0)
3914 		panic("nfs sndunlock");
3915 	*flagp &= ~NFSR_SNDLOCK;
3916 	if (*flagp & NFSR_WANTSND) {
3917 		*flagp &= ~NFSR_WANTSND;
3918 		wakeup((caddr_t)flagp);
3919 	}
3920 	NFSUNLOCKSOCK();
3921 }
3922 
3923 APPLESTATIC int
3924 nfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_storage *sa,
3925     int *isudp)
3926 {
3927 	struct sockaddr_in *sad;
3928 	struct sockaddr_in6 *sad6;
3929 	struct in_addr saddr;
3930 	uint32_t portnum, *tl;
3931 	int af = 0, i, j, k;
3932 	char addr[64], protocol[5], *cp;
3933 	int cantparse = 0, error = 0;
3934 	uint16_t portv;
3935 
3936 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3937 	i = fxdr_unsigned(int, *tl);
3938 	if (i >= 3 && i <= 4) {
3939 		error = nfsrv_mtostr(nd, protocol, i);
3940 		if (error)
3941 			goto nfsmout;
3942 		if (strcmp(protocol, "tcp") == 0) {
3943 			af = AF_INET;
3944 			*isudp = 0;
3945 		} else if (strcmp(protocol, "udp") == 0) {
3946 			af = AF_INET;
3947 			*isudp = 1;
3948 		} else if (strcmp(protocol, "tcp6") == 0) {
3949 			af = AF_INET6;
3950 			*isudp = 0;
3951 		} else if (strcmp(protocol, "udp6") == 0) {
3952 			af = AF_INET6;
3953 			*isudp = 1;
3954 		} else
3955 			cantparse = 1;
3956 	} else {
3957 		cantparse = 1;
3958 		if (i > 0) {
3959 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
3960 			if (error)
3961 				goto nfsmout;
3962 		}
3963 	}
3964 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3965 	i = fxdr_unsigned(int, *tl);
3966 	if (i < 0) {
3967 		error = NFSERR_BADXDR;
3968 		goto nfsmout;
3969 	} else if (cantparse == 0 && i >= 11 && i < 64) {
3970 		/*
3971 		 * The shortest address is 11chars and the longest is < 64.
3972 		 */
3973 		error = nfsrv_mtostr(nd, addr, i);
3974 		if (error)
3975 			goto nfsmout;
3976 
3977 		/* Find the port# at the end and extract that. */
3978 		i = strlen(addr);
3979 		k = 0;
3980 		cp = &addr[i - 1];
3981 		/* Count back two '.'s from end to get port# field. */
3982 		for (j = 0; j < i; j++) {
3983 			if (*cp == '.') {
3984 				k++;
3985 				if (k == 2)
3986 					break;
3987 			}
3988 			cp--;
3989 		}
3990 		if (k == 2) {
3991 			/*
3992 			 * The NFSv4 port# is appended as .N.N, where N is
3993 			 * a decimal # in the range 0-255, just like an inet4
3994 			 * address. Cheat and use inet_aton(), which will
3995 			 * return a Class A address and then shift the high
3996 			 * order 8bits over to convert it to the port#.
3997 			 */
3998 			*cp++ = '\0';
3999 			if (inet_aton(cp, &saddr) == 1) {
4000 				portnum = ntohl(saddr.s_addr);
4001 				portv = (uint16_t)((portnum >> 16) |
4002 				    (portnum & 0xff));
4003 			} else
4004 				cantparse = 1;
4005 		} else
4006 			cantparse = 1;
4007 		if (cantparse == 0) {
4008 			if (af == AF_INET) {
4009 				sad = (struct sockaddr_in *)sa;
4010 				if (inet_pton(af, addr, &sad->sin_addr) == 1) {
4011 					sad->sin_len = sizeof(*sad);
4012 					sad->sin_family = AF_INET;
4013 					sad->sin_port = htons(portv);
4014 					return (0);
4015 				}
4016 			} else {
4017 				sad6 = (struct sockaddr_in6 *)sa;
4018 				if (inet_pton(af, addr, &sad6->sin6_addr)
4019 				    == 1) {
4020 					sad6->sin6_len = sizeof(*sad6);
4021 					sad6->sin6_family = AF_INET6;
4022 					sad6->sin6_port = htons(portv);
4023 					return (0);
4024 				}
4025 			}
4026 		}
4027 	} else {
4028 		if (i > 0) {
4029 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
4030 			if (error)
4031 				goto nfsmout;
4032 		}
4033 	}
4034 	error = EPERM;
4035 nfsmout:
4036 	return (error);
4037 }
4038 
4039 /*
4040  * Handle an NFSv4.1 Sequence request for the session.
4041  * If reply != NULL, use it to return the cached reply, as required.
4042  * The client gets a cached reply via this call for callbacks, however the
4043  * server gets a cached reply via the nfsv4_seqsess_cachereply() call.
4044  */
4045 int
4046 nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot,
4047     struct nfsslot *slots, struct mbuf **reply, uint16_t maxslot)
4048 {
4049 	int error;
4050 
4051 	error = 0;
4052 	if (reply != NULL)
4053 		*reply = NULL;
4054 	if (slotid > maxslot)
4055 		return (NFSERR_BADSLOT);
4056 	if (seqid == slots[slotid].nfssl_seq) {
4057 		/* A retry. */
4058 		if (slots[slotid].nfssl_inprog != 0)
4059 			error = NFSERR_DELAY;
4060 		else if (slots[slotid].nfssl_reply != NULL) {
4061 			if (reply != NULL) {
4062 				*reply = slots[slotid].nfssl_reply;
4063 				slots[slotid].nfssl_reply = NULL;
4064 			}
4065 			slots[slotid].nfssl_inprog = 1;
4066 			error = NFSERR_REPLYFROMCACHE;
4067 		} else
4068 			/* No reply cached, so just do it. */
4069 			slots[slotid].nfssl_inprog = 1;
4070 	} else if ((slots[slotid].nfssl_seq + 1) == seqid) {
4071 		if (slots[slotid].nfssl_reply != NULL)
4072 			m_freem(slots[slotid].nfssl_reply);
4073 		slots[slotid].nfssl_reply = NULL;
4074 		slots[slotid].nfssl_inprog = 1;
4075 		slots[slotid].nfssl_seq++;
4076 	} else
4077 		error = NFSERR_SEQMISORDERED;
4078 	return (error);
4079 }
4080 
4081 /*
4082  * Cache this reply for the slot.
4083  * Use the "rep" argument to return the cached reply if repstat is set to
4084  * NFSERR_REPLYFROMCACHE. The client never sets repstat to this value.
4085  */
4086 void
4087 nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, int repstat,
4088    struct mbuf **rep)
4089 {
4090 
4091 	if (repstat == NFSERR_REPLYFROMCACHE) {
4092 		*rep = slots[slotid].nfssl_reply;
4093 		slots[slotid].nfssl_reply = NULL;
4094 	} else {
4095 		if (slots[slotid].nfssl_reply != NULL)
4096 			m_freem(slots[slotid].nfssl_reply);
4097 		slots[slotid].nfssl_reply = *rep;
4098 	}
4099 	slots[slotid].nfssl_inprog = 0;
4100 }
4101 
4102 /*
4103  * Generate the xdr for an NFSv4.1 Sequence Operation.
4104  */
4105 APPLESTATIC void
4106 nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd,
4107     struct nfsclsession *sep, int dont_replycache)
4108 {
4109 	uint32_t *tl, slotseq = 0;
4110 	int error, maxslot, slotpos;
4111 	uint8_t sessionid[NFSX_V4SESSIONID];
4112 
4113 	error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot, &slotseq,
4114 	    sessionid);
4115 	if (error != 0)
4116 		return;
4117 	KASSERT(maxslot >= 0, ("nfscl_setsequence neg maxslot"));
4118 
4119 	/* Build the Sequence arguments. */
4120 	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
4121 	bcopy(sessionid, tl, NFSX_V4SESSIONID);
4122 	tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
4123 	nd->nd_slotseq = tl;
4124 	*tl++ = txdr_unsigned(slotseq);
4125 	*tl++ = txdr_unsigned(slotpos);
4126 	*tl++ = txdr_unsigned(maxslot);
4127 	if (dont_replycache == 0)
4128 		*tl = newnfs_true;
4129 	else
4130 		*tl = newnfs_false;
4131 	nd->nd_flag |= ND_HASSEQUENCE;
4132 }
4133 
4134 int
4135 nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep,
4136     int *slotposp, int *maxslotp, uint32_t *slotseqp, uint8_t *sessionid)
4137 {
4138 	int i, maxslot, slotpos;
4139 	uint64_t bitval;
4140 
4141 	/* Find an unused slot. */
4142 	slotpos = -1;
4143 	maxslot = -1;
4144 	mtx_lock(&sep->nfsess_mtx);
4145 	do {
4146 		bitval = 1;
4147 		for (i = 0; i < sep->nfsess_foreslots; i++) {
4148 			if ((bitval & sep->nfsess_slots) == 0) {
4149 				slotpos = i;
4150 				sep->nfsess_slots |= bitval;
4151 				sep->nfsess_slotseq[i]++;
4152 				*slotseqp = sep->nfsess_slotseq[i];
4153 				break;
4154 			}
4155 			bitval <<= 1;
4156 		}
4157 		if (slotpos == -1) {
4158 			/*
4159 			 * If a forced dismount is in progress, just return.
4160 			 * This RPC attempt will fail when it calls
4161 			 * newnfs_request().
4162 			 */
4163 			if (nmp != NULL &&
4164 			    (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)
4165 			    != 0) {
4166 				mtx_unlock(&sep->nfsess_mtx);
4167 				return (ESTALE);
4168 			}
4169 			/* Wake up once/sec, to check for a forced dismount. */
4170 			(void)mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx,
4171 			    PZERO, "nfsclseq", hz);
4172 		}
4173 	} while (slotpos == -1);
4174 	/* Now, find the highest slot in use. (nfsc_slots is 64bits) */
4175 	bitval = 1;
4176 	for (i = 0; i < 64; i++) {
4177 		if ((bitval & sep->nfsess_slots) != 0)
4178 			maxslot = i;
4179 		bitval <<= 1;
4180 	}
4181 	bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID);
4182 	mtx_unlock(&sep->nfsess_mtx);
4183 	*slotposp = slotpos;
4184 	*maxslotp = maxslot;
4185 	return (0);
4186 }
4187 
4188 /*
4189  * Free a session slot.
4190  */
4191 APPLESTATIC void
4192 nfsv4_freeslot(struct nfsclsession *sep, int slot)
4193 {
4194 	uint64_t bitval;
4195 
4196 	bitval = 1;
4197 	if (slot > 0)
4198 		bitval <<= slot;
4199 	mtx_lock(&sep->nfsess_mtx);
4200 	if ((bitval & sep->nfsess_slots) == 0)
4201 		printf("freeing free slot!!\n");
4202 	sep->nfsess_slots &= ~bitval;
4203 	wakeup(&sep->nfsess_slots);
4204 	mtx_unlock(&sep->nfsess_mtx);
4205 }
4206 
4207