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