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