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