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