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