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