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