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