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