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