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