1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem */
33191783Srmacklem
34191783Srmacklem#include <sys/cdefs.h>
35191783Srmacklem__FBSDID("$FreeBSD$");
36191783Srmacklem
37191783Srmacklem/*
38191783Srmacklem * These functions support the macros and help fiddle mbuf chains for
39191783Srmacklem * the nfs op functions. They do things like create the rpc header and
40191783Srmacklem * copy data between mbuf chains and uio lists.
41191783Srmacklem */
42191783Srmacklem#ifndef APPLEKEXT
43230446Srmacklem#include "opt_inet6.h"
44230446Srmacklem
45191783Srmacklem#include <fs/nfs/nfsport.h>
46191783Srmacklem
47191783Srmacklem/*
48191783Srmacklem * Data items converted to xdr at startup, since they are constant
49191783Srmacklem * This is kinda hokey, but may save a little time doing byte swaps
50191783Srmacklem */
51191783Srmacklemu_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
52191783Srmacklem
53191783Srmacklem/* And other global data */
54191783Srmacklemnfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
55191783Srmacklem		      NFFIFO, NFNON };
56191783Srmacklemenum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
57191783Srmacklemenum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
58191783Srmacklemstruct timeval nfsboottime;	/* Copy boottime once, so it never changes */
59191783Srmacklemint nfscl_ticks;
60191783Srmacklemint nfsrv_useacl = 1;
61191783Srmacklemstruct nfssockreq nfsrv_nfsuserdsock;
62191783Srmacklemint nfsrv_nfsuserd = 0;
63191783Srmacklemstruct nfsreqhead nfsd_reqq;
64191783Srmacklemuid_t nfsrv_defaultuid;
65191783Srmacklemgid_t nfsrv_defaultgid;
66191783Srmacklemint nfsrv_lease = NFSRV_LEASE;
67191783Srmacklemint ncl_mbuf_mlen = MLEN;
68265724Srmacklemint nfsd_enable_stringtouid = 0;
69191783SrmacklemNFSNAMEIDMUTEX;
70191783SrmacklemNFSSOCKMUTEX;
71191783Srmacklem
72191783Srmacklem/*
73191783Srmacklem * This array of structures indicates, for V4:
74191783Srmacklem * retfh - which of 3 types of calling args are used
75191783Srmacklem *	0 - doesn't change cfh or use a sfh
76191783Srmacklem *	1 - replaces cfh with a new one (unless it returns an error status)
77191783Srmacklem *	2 - uses cfh and sfh
78191783Srmacklem * needscfh - if the op wants a cfh and premtime
79191783Srmacklem *	0 - doesn't use a cfh
80191783Srmacklem *	1 - uses a cfh, but doesn't want pre-op attributes
81191783Srmacklem *	2 - uses a cfh and wants pre-op attributes
82191783Srmacklem * savereply - indicates a non-idempotent Op
83191783Srmacklem *	0 - not non-idempotent
84191783Srmacklem *	1 - non-idempotent
85191783Srmacklem * Ops that are ordered via seqid# are handled separately from these
86191783Srmacklem * non-idempotent Ops.
87191783Srmacklem * Define it here, since it is used by both the client and server.
88191783Srmacklem */
89191783Srmacklemstruct nfsv4_opflag nfsv4_opflag[NFSV4OP_NOPS] = {
90216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* undef */
91216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* undef */
92216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* undef */
93216700Srmacklem	{ 0, 1, 0, 0, LK_SHARED },		/* Access */
94216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* Close */
95216700Srmacklem	{ 0, 2, 0, 1, LK_EXCLUSIVE },		/* Commit */
96216700Srmacklem	{ 1, 2, 1, 1, LK_EXCLUSIVE },		/* Create */
97216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* Delegpurge */
98216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* Delegreturn */
99216700Srmacklem	{ 0, 1, 0, 0, LK_SHARED },		/* Getattr */
100216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* GetFH */
101216700Srmacklem	{ 2, 1, 1, 1, LK_EXCLUSIVE },		/* Link */
102216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* Lock */
103216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* LockT */
104216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* LockU */
105265339Srmacklem	{ 1, 2, 0, 0, LK_EXCLUSIVE },		/* Lookup */
106265339Srmacklem	{ 1, 2, 0, 0, LK_EXCLUSIVE },		/* Lookupp */
107216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* NVerify */
108216700Srmacklem	{ 1, 1, 0, 1, LK_EXCLUSIVE },		/* Open */
109216700Srmacklem	{ 1, 1, 0, 0, LK_EXCLUSIVE },		/* OpenAttr */
110216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* OpenConfirm */
111216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* OpenDowngrade */
112216700Srmacklem	{ 1, 0, 0, 0, LK_EXCLUSIVE },		/* PutFH */
113216700Srmacklem	{ 1, 0, 0, 0, LK_EXCLUSIVE },		/* PutPubFH */
114216700Srmacklem	{ 1, 0, 0, 0, LK_EXCLUSIVE },		/* PutRootFH */
115216875Srmacklem	{ 0, 1, 0, 0, LK_SHARED },		/* Read */
116216700Srmacklem	{ 0, 1, 0, 0, LK_SHARED },		/* Readdir */
117216700Srmacklem	{ 0, 1, 0, 0, LK_SHARED },		/* ReadLink */
118216700Srmacklem	{ 0, 2, 1, 1, LK_EXCLUSIVE },		/* Remove */
119216700Srmacklem	{ 2, 1, 1, 1, LK_EXCLUSIVE },		/* Rename */
120216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* Renew */
121216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* RestoreFH */
122216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* SaveFH */
123216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* SecInfo */
124216700Srmacklem	{ 0, 2, 1, 1, LK_EXCLUSIVE },		/* Setattr */
125216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* SetClientID */
126216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* SetClientIDConfirm */
127216700Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE },		/* Verify */
128216700Srmacklem	{ 0, 2, 1, 1, LK_EXCLUSIVE },		/* Write */
129216700Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE },		/* ReleaseLockOwner */
130191783Srmacklem};
131191783Srmacklem#endif	/* !APPLEKEXT */
132191783Srmacklem
133191783Srmacklemstatic int ncl_mbuf_mhlen = MHLEN;
134191783Srmacklemstatic int nfsrv_usercnt = 0;
135191783Srmacklemstatic int nfsrv_dnsnamelen;
136191783Srmacklemstatic u_char *nfsrv_dnsname = NULL;
137191783Srmacklemstatic int nfsrv_usermax = 999999999;
138191783Srmacklemstatic struct nfsuserhashhead nfsuserhash[NFSUSERHASHSIZE];
139191783Srmacklemstatic struct nfsuserhashhead nfsusernamehash[NFSUSERHASHSIZE];
140191783Srmacklemstatic struct nfsuserhashhead nfsgrouphash[NFSGROUPHASHSIZE];
141191783Srmacklemstatic struct nfsuserhashhead nfsgroupnamehash[NFSGROUPHASHSIZE];
142191783Srmacklemstatic struct nfsuserlruhead nfsuserlruhead;
143191783Srmacklem
144191783Srmacklem/*
145191783Srmacklem * This static array indicates whether or not the RPC generates a large
146191783Srmacklem * reply. This is used by nfs_reply() to decide whether or not an mbuf
147191783Srmacklem * cluster should be allocated. (If a cluster is required by an RPC
148191783Srmacklem * marked 0 in this array, the code will still work, just not quite as
149191783Srmacklem * efficiently.)
150191783Srmacklem */
151191783Srmacklemstatic int nfs_bigreply[NFS_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
152191783Srmacklem    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,
153191783Srmacklem    0, 0, 0, 0, 0 };
154191783Srmacklem
155191783Srmacklem/* local functions */
156191783Srmacklemstatic int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
157191783Srmacklemstatic void nfsv4_wanted(struct nfsv4lock *lp);
158191783Srmacklemstatic int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
159191783Srmacklemstatic int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
160191783Srmacklem    NFSPROC_T *p);
161191783Srmacklemstatic void nfsrv_removeuser(struct nfsusrgrp *usrp);
162191783Srmacklemstatic int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
163191783Srmacklem    int *, int *);
164191783Srmacklemstatic void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
165191783Srmacklem
166191783Srmacklem
167191783Srmacklem#ifndef APPLE
168191783Srmacklem/*
169191783Srmacklem * copies mbuf chain to the uio scatter/gather list
170191783Srmacklem */
171191783Srmacklemint
172191783Srmacklemnfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
173191783Srmacklem{
174191783Srmacklem	char *mbufcp, *uiocp;
175191783Srmacklem	int xfer, left, len;
176191783Srmacklem	mbuf_t mp;
177191783Srmacklem	long uiosiz, rem;
178191783Srmacklem	int error = 0;
179191783Srmacklem
180191783Srmacklem	mp = nd->nd_md;
181191783Srmacklem	mbufcp = nd->nd_dpos;
182191783Srmacklem	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - mbufcp;
183191783Srmacklem	rem = NFSM_RNDUP(siz) - siz;
184191783Srmacklem	while (siz > 0) {
185224086Szack		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) {
186224086Szack			error = EBADRPC;
187224086Szack			goto out;
188224086Szack		}
189191783Srmacklem		left = uiop->uio_iov->iov_len;
190191783Srmacklem		uiocp = uiop->uio_iov->iov_base;
191191783Srmacklem		if (left > siz)
192191783Srmacklem			left = siz;
193191783Srmacklem		uiosiz = left;
194191783Srmacklem		while (left > 0) {
195191783Srmacklem			while (len == 0) {
196191783Srmacklem				mp = mbuf_next(mp);
197224086Szack				if (mp == NULL) {
198224086Szack					error = EBADRPC;
199224086Szack					goto out;
200224086Szack				}
201191783Srmacklem				mbufcp = NFSMTOD(mp, caddr_t);
202191783Srmacklem				len = mbuf_len(mp);
203246539Skib				KASSERT(len > 0, ("len %d", len));
204191783Srmacklem			}
205191783Srmacklem			xfer = (left > len) ? len : left;
206191783Srmacklem#ifdef notdef
207191783Srmacklem			/* Not Yet.. */
208191783Srmacklem			if (uiop->uio_iov->iov_op != NULL)
209191783Srmacklem				(*(uiop->uio_iov->iov_op))
210191783Srmacklem				(mbufcp, uiocp, xfer);
211191783Srmacklem			else
212191783Srmacklem#endif
213191783Srmacklem			if (uiop->uio_segflg == UIO_SYSSPACE)
214191783Srmacklem				NFSBCOPY(mbufcp, uiocp, xfer);
215191783Srmacklem			else
216191783Srmacklem				copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
217191783Srmacklem			left -= xfer;
218191783Srmacklem			len -= xfer;
219191783Srmacklem			mbufcp += xfer;
220191783Srmacklem			uiocp += xfer;
221191783Srmacklem			uiop->uio_offset += xfer;
222191783Srmacklem			uiop->uio_resid -= xfer;
223191783Srmacklem		}
224191783Srmacklem		if (uiop->uio_iov->iov_len <= siz) {
225191783Srmacklem			uiop->uio_iovcnt--;
226191783Srmacklem			uiop->uio_iov++;
227191783Srmacklem		} else {
228191783Srmacklem			uiop->uio_iov->iov_base = (void *)
229191783Srmacklem				((char *)uiop->uio_iov->iov_base + uiosiz);
230191783Srmacklem			uiop->uio_iov->iov_len -= uiosiz;
231191783Srmacklem		}
232191783Srmacklem		siz -= uiosiz;
233191783Srmacklem	}
234191783Srmacklem	nd->nd_dpos = mbufcp;
235191783Srmacklem	nd->nd_md = mp;
236191783Srmacklem	if (rem > 0) {
237191783Srmacklem		if (len < rem)
238191783Srmacklem			error = nfsm_advance(nd, rem, len);
239191783Srmacklem		else
240191783Srmacklem			nd->nd_dpos += rem;
241191783Srmacklem	}
242224086Szack
243224086Szackout:
244224086Szack	NFSEXITCODE2(error, nd);
245191783Srmacklem	return (error);
246191783Srmacklem}
247191783Srmacklem#endif	/* !APPLE */
248191783Srmacklem
249191783Srmacklem/*
250191783Srmacklem * Help break down an mbuf chain by setting the first siz bytes contiguous
251191783Srmacklem * pointed to by returned val.
252191783Srmacklem * This is used by the macro NFSM_DISSECT for tough
253191783Srmacklem * cases.
254191783Srmacklem */
255191783SrmacklemAPPLESTATIC void *
256251641Skennfsm_dissct(struct nfsrv_descript *nd, int siz, int how)
257191783Srmacklem{
258191783Srmacklem	mbuf_t mp2;
259191783Srmacklem	int siz2, xfer;
260191783Srmacklem	caddr_t p;
261191783Srmacklem	int left;
262191783Srmacklem	caddr_t retp;
263191783Srmacklem
264191783Srmacklem	retp = NULL;
265191783Srmacklem	left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
266191783Srmacklem	while (left == 0) {
267191783Srmacklem		nd->nd_md = mbuf_next(nd->nd_md);
268191783Srmacklem		if (nd->nd_md == NULL)
269191783Srmacklem			return (retp);
270191783Srmacklem		left = mbuf_len(nd->nd_md);
271191783Srmacklem		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
272191783Srmacklem	}
273191783Srmacklem	if (left >= siz) {
274191783Srmacklem		retp = nd->nd_dpos;
275191783Srmacklem		nd->nd_dpos += siz;
276191783Srmacklem	} else if (mbuf_next(nd->nd_md) == NULL) {
277191783Srmacklem		return (retp);
278191783Srmacklem	} else if (siz > ncl_mbuf_mhlen) {
279191783Srmacklem		panic("nfs S too big");
280191783Srmacklem	} else {
281251641Sken		MGET(mp2, MT_DATA, how);
282251641Sken		if (mp2 == NULL)
283251641Sken			return (NULL);
284191783Srmacklem		mbuf_setnext(mp2, mbuf_next(nd->nd_md));
285191783Srmacklem		mbuf_setnext(nd->nd_md, mp2);
286191783Srmacklem		mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
287191783Srmacklem		nd->nd_md = mp2;
288191783Srmacklem		retp = p = NFSMTOD(mp2, caddr_t);
289191783Srmacklem		NFSBCOPY(nd->nd_dpos, p, left);	/* Copy what was left */
290191783Srmacklem		siz2 = siz - left;
291191783Srmacklem		p += left;
292191783Srmacklem		mp2 = mbuf_next(mp2);
293191783Srmacklem		/* Loop around copying up the siz2 bytes */
294191783Srmacklem		while (siz2 > 0) {
295191783Srmacklem			if (mp2 == NULL)
296191783Srmacklem				return (NULL);
297191783Srmacklem			xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
298191783Srmacklem			if (xfer > 0) {
299191783Srmacklem				NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
300191783Srmacklem				NFSM_DATAP(mp2, xfer);
301191783Srmacklem				mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
302191783Srmacklem				p += xfer;
303191783Srmacklem				siz2 -= xfer;
304191783Srmacklem			}
305191783Srmacklem			if (siz2 > 0)
306191783Srmacklem				mp2 = mbuf_next(mp2);
307191783Srmacklem		}
308191783Srmacklem		mbuf_setlen(nd->nd_md, siz);
309191783Srmacklem		nd->nd_md = mp2;
310191783Srmacklem		nd->nd_dpos = NFSMTOD(mp2, caddr_t);
311191783Srmacklem	}
312191783Srmacklem	return (retp);
313191783Srmacklem}
314191783Srmacklem
315191783Srmacklem/*
316191783Srmacklem * Advance the position in the mbuf chain.
317191783Srmacklem * If offs == 0, this is a no-op, but it is simpler to just return from
318191783Srmacklem * here than check for offs > 0 for all calls to nfsm_advance.
319191783Srmacklem * If left == -1, it should be calculated here.
320191783Srmacklem */
321191783SrmacklemAPPLESTATIC int
322191783Srmacklemnfsm_advance(struct nfsrv_descript *nd, int offs, int left)
323191783Srmacklem{
324224086Szack	int error = 0;
325191783Srmacklem
326191783Srmacklem	if (offs == 0)
327224086Szack		goto out;
328191783Srmacklem	/*
329191783Srmacklem	 * A negative offs should be considered a serious problem.
330191783Srmacklem	 */
331191783Srmacklem	if (offs < 0)
332191783Srmacklem		panic("nfsrv_advance");
333191783Srmacklem
334191783Srmacklem	/*
335191783Srmacklem	 * If left == -1, calculate it here.
336191783Srmacklem	 */
337191783Srmacklem	if (left == -1)
338191783Srmacklem		left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
339191783Srmacklem		    nd->nd_dpos;
340191783Srmacklem
341191783Srmacklem	/*
342191783Srmacklem	 * Loop around, advancing over the mbuf data.
343191783Srmacklem	 */
344191783Srmacklem	while (offs > left) {
345191783Srmacklem		offs -= left;
346191783Srmacklem		nd->nd_md = mbuf_next(nd->nd_md);
347224086Szack		if (nd->nd_md == NULL) {
348224086Szack			error = EBADRPC;
349224086Szack			goto out;
350224086Szack		}
351191783Srmacklem		left = mbuf_len(nd->nd_md);
352191783Srmacklem		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
353191783Srmacklem	}
354191783Srmacklem	nd->nd_dpos += offs;
355224086Szack
356224086Szackout:
357224086Szack	NFSEXITCODE(error);
358224086Szack	return (error);
359191783Srmacklem}
360191783Srmacklem
361191783Srmacklem/*
362191783Srmacklem * Copy a string into mbuf(s).
363191783Srmacklem * Return the number of bytes output, including XDR overheads.
364191783Srmacklem */
365191783SrmacklemAPPLESTATIC int
366191783Srmacklemnfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
367191783Srmacklem{
368191783Srmacklem	mbuf_t m2;
369191783Srmacklem	int xfer, left;
370191783Srmacklem	mbuf_t m1;
371191783Srmacklem	int rem, bytesize;
372191783Srmacklem	u_int32_t *tl;
373191783Srmacklem	char *cp2;
374191783Srmacklem
375191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
376191783Srmacklem	*tl = txdr_unsigned(siz);
377191783Srmacklem	rem = NFSM_RNDUP(siz) - siz;
378191783Srmacklem	bytesize = NFSX_UNSIGNED + siz + rem;
379191783Srmacklem	m2 = nd->nd_mb;
380191783Srmacklem	cp2 = nd->nd_bpos;
381191783Srmacklem	left = M_TRAILINGSPACE(m2);
382191783Srmacklem
383191783Srmacklem	/*
384191783Srmacklem	 * Loop around copying the string to mbuf(s).
385191783Srmacklem	 */
386191783Srmacklem	while (siz > 0) {
387191783Srmacklem		if (left == 0) {
388191783Srmacklem			if (siz > ncl_mbuf_mlen)
389191783Srmacklem				NFSMCLGET(m1, M_WAIT);
390191783Srmacklem			else
391191783Srmacklem				NFSMGET(m1);
392191783Srmacklem			mbuf_setlen(m1, 0);
393191783Srmacklem			mbuf_setnext(m2, m1);
394191783Srmacklem			m2 = m1;
395191783Srmacklem			cp2 = NFSMTOD(m2, caddr_t);
396191783Srmacklem			left = M_TRAILINGSPACE(m2);
397191783Srmacklem		}
398191783Srmacklem		if (left >= siz)
399191783Srmacklem			xfer = siz;
400191783Srmacklem		else
401191783Srmacklem			xfer = left;
402191783Srmacklem		NFSBCOPY(cp, cp2, xfer);
403191783Srmacklem		cp += xfer;
404191783Srmacklem		mbuf_setlen(m2, mbuf_len(m2) + xfer);
405191783Srmacklem		siz -= xfer;
406191783Srmacklem		left -= xfer;
407191783Srmacklem		if (siz == 0 && rem) {
408191783Srmacklem			if (left < rem)
409191783Srmacklem				panic("nfsm_strtom");
410191783Srmacklem			NFSBZERO(cp2 + xfer, rem);
411191783Srmacklem			mbuf_setlen(m2, mbuf_len(m2) + rem);
412191783Srmacklem		}
413191783Srmacklem	}
414191783Srmacklem	nd->nd_mb = m2;
415191783Srmacklem	nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
416191783Srmacklem	return (bytesize);
417191783Srmacklem}
418191783Srmacklem
419191783Srmacklem/*
420191783Srmacklem * Called once to initialize data structures...
421191783Srmacklem */
422191783SrmacklemAPPLESTATIC void
423191783Srmacklemnewnfs_init(void)
424191783Srmacklem{
425191783Srmacklem	static int nfs_inited = 0;
426191783Srmacklem
427191783Srmacklem	if (nfs_inited)
428191783Srmacklem		return;
429191783Srmacklem	nfs_inited = 1;
430191783Srmacklem
431191783Srmacklem	newnfs_true = txdr_unsigned(TRUE);
432191783Srmacklem	newnfs_false = txdr_unsigned(FALSE);
433191783Srmacklem	newnfs_xdrneg1 = txdr_unsigned(-1);
434191783Srmacklem	nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
435191783Srmacklem	if (nfscl_ticks < 1)
436191783Srmacklem		nfscl_ticks = 1;
437191783Srmacklem	NFSSETBOOTTIME(nfsboottime);
438191783Srmacklem
439191783Srmacklem	/*
440191783Srmacklem	 * Initialize reply list and start timer
441191783Srmacklem	 */
442191783Srmacklem	TAILQ_INIT(&nfsd_reqq);
443191783Srmacklem	NFS_TIMERINIT;
444191783Srmacklem}
445191783Srmacklem
446191783Srmacklem/*
447191783Srmacklem * Put a file handle in an mbuf list.
448191783Srmacklem * If the size argument == 0, just use the default size.
449191783Srmacklem * set_true == 1 if there should be an newnfs_true prepended on the file handle.
450191783Srmacklem * Return the number of bytes output, including XDR overhead.
451191783Srmacklem */
452191783SrmacklemAPPLESTATIC int
453191783Srmacklemnfsm_fhtom(struct nfsrv_descript *nd, u_int8_t *fhp, int size, int set_true)
454191783Srmacklem{
455191783Srmacklem	u_int32_t *tl;
456191783Srmacklem	u_int8_t *cp;
457191783Srmacklem	int fullsiz, rem, bytesize = 0;
458191783Srmacklem
459191783Srmacklem	if (size == 0)
460191783Srmacklem		size = NFSX_MYFH;
461191783Srmacklem	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
462191783Srmacklem	case ND_NFSV2:
463191783Srmacklem		if (size > NFSX_V2FH)
464191783Srmacklem			panic("fh size > NFSX_V2FH for NFSv2");
465191783Srmacklem		NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
466191783Srmacklem		NFSBCOPY(fhp, cp, size);
467191783Srmacklem		if (size < NFSX_V2FH)
468191783Srmacklem			NFSBZERO(cp + size, NFSX_V2FH - size);
469191783Srmacklem		bytesize = NFSX_V2FH;
470191783Srmacklem		break;
471191783Srmacklem	case ND_NFSV3:
472191783Srmacklem	case ND_NFSV4:
473191783Srmacklem		fullsiz = NFSM_RNDUP(size);
474191783Srmacklem		rem = fullsiz - size;
475191783Srmacklem		if (set_true) {
476191783Srmacklem		    bytesize = 2 * NFSX_UNSIGNED + fullsiz;
477191783Srmacklem		    NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
478191783Srmacklem		    *tl = newnfs_true;
479191783Srmacklem		} else {
480191783Srmacklem		    bytesize = NFSX_UNSIGNED + fullsiz;
481191783Srmacklem		}
482191783Srmacklem		(void) nfsm_strtom(nd, fhp, size);
483191783Srmacklem		break;
484191783Srmacklem	};
485191783Srmacklem	return (bytesize);
486191783Srmacklem}
487191783Srmacklem
488191783Srmacklem/*
489191783Srmacklem * This function compares two net addresses by family and returns TRUE
490191783Srmacklem * if they are the same host.
491191783Srmacklem * If there is any doubt, return FALSE.
492191783Srmacklem * The AF_INET family is handled as a special case so that address mbufs
493191783Srmacklem * don't need to be saved to store "struct in_addr", which is only 4 bytes.
494191783Srmacklem */
495191783SrmacklemAPPLESTATIC int
496191783Srmacklemnfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
497191783Srmacklem{
498191783Srmacklem	struct sockaddr_in *inetaddr;
499191783Srmacklem
500191783Srmacklem	switch (family) {
501191783Srmacklem	case AF_INET:
502191783Srmacklem		inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
503191783Srmacklem		if (inetaddr->sin_family == AF_INET &&
504191783Srmacklem		    inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
505191783Srmacklem			return (1);
506191783Srmacklem		break;
507191783Srmacklem#ifdef INET6
508191783Srmacklem	case AF_INET6:
509191783Srmacklem		{
510191783Srmacklem		struct sockaddr_in6 *inetaddr6;
511191783Srmacklem
512191783Srmacklem		inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
513191783Srmacklem		/* XXX - should test sin6_scope_id ? */
514191783Srmacklem		if (inetaddr6->sin6_family == AF_INET6 &&
515191783Srmacklem		    IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
516191783Srmacklem			  &haddr->had_inet6))
517191783Srmacklem			return (1);
518191783Srmacklem		}
519191783Srmacklem		break;
520191783Srmacklem#endif
521191783Srmacklem	};
522191783Srmacklem	return (0);
523191783Srmacklem}
524191783Srmacklem
525191783Srmacklem/*
526191783Srmacklem * Similar to the above, but takes to NFSSOCKADDR_T args.
527191783Srmacklem */
528191783SrmacklemAPPLESTATIC int
529191783Srmacklemnfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
530191783Srmacklem{
531191783Srmacklem	struct sockaddr_in *addr1, *addr2;
532191783Srmacklem	struct sockaddr *inaddr;
533191783Srmacklem
534191783Srmacklem	inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
535191783Srmacklem	switch (inaddr->sa_family) {
536191783Srmacklem	case AF_INET:
537191783Srmacklem		addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
538191783Srmacklem		addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
539191783Srmacklem		if (addr2->sin_family == AF_INET &&
540191783Srmacklem		    addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
541191783Srmacklem			return (1);
542191783Srmacklem		break;
543191783Srmacklem#ifdef INET6
544191783Srmacklem	case AF_INET6:
545191783Srmacklem		{
546191783Srmacklem		struct sockaddr_in6 *inet6addr1, *inet6addr2;
547191783Srmacklem
548191783Srmacklem		inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
549191783Srmacklem		inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
550191783Srmacklem		/* XXX - should test sin6_scope_id ? */
551191783Srmacklem		if (inet6addr2->sin6_family == AF_INET6 &&
552191783Srmacklem		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
553191783Srmacklem			  &inet6addr2->sin6_addr))
554191783Srmacklem			return (1);
555191783Srmacklem		}
556191783Srmacklem		break;
557191783Srmacklem#endif
558191783Srmacklem	};
559191783Srmacklem	return (0);
560191783Srmacklem}
561191783Srmacklem
562191783Srmacklem
563191783Srmacklem/*
564191783Srmacklem * Trim the stuff already dissected off the mbuf list.
565191783Srmacklem */
566191783SrmacklemAPPLESTATIC void
567191783Srmacklemnewnfs_trimleading(nd)
568191783Srmacklem	struct nfsrv_descript *nd;
569191783Srmacklem{
570191783Srmacklem	mbuf_t m, n;
571191783Srmacklem	int offs;
572191783Srmacklem
573191783Srmacklem	/*
574191783Srmacklem	 * First, free up leading mbufs.
575191783Srmacklem	 */
576191783Srmacklem	if (nd->nd_mrep != nd->nd_md) {
577191783Srmacklem		m = nd->nd_mrep;
578191783Srmacklem		while (mbuf_next(m) != nd->nd_md) {
579191783Srmacklem			if (mbuf_next(m) == NULL)
580191783Srmacklem				panic("nfsm trim leading");
581191783Srmacklem			m = mbuf_next(m);
582191783Srmacklem		}
583191783Srmacklem		mbuf_setnext(m, NULL);
584191783Srmacklem		mbuf_freem(nd->nd_mrep);
585191783Srmacklem	}
586191783Srmacklem	m = nd->nd_md;
587191783Srmacklem
588191783Srmacklem	/*
589191783Srmacklem	 * Now, adjust this mbuf, based on nd_dpos.
590191783Srmacklem	 */
591191783Srmacklem	offs = nd->nd_dpos - NFSMTOD(m, caddr_t);
592191783Srmacklem	if (offs == mbuf_len(m)) {
593191783Srmacklem		n = m;
594191783Srmacklem		m = mbuf_next(m);
595191783Srmacklem		if (m == NULL)
596191783Srmacklem			panic("nfsm trim leading2");
597191783Srmacklem		mbuf_setnext(n, NULL);
598191783Srmacklem		mbuf_freem(n);
599191783Srmacklem	} else if (offs > 0) {
600191783Srmacklem		mbuf_setlen(m, mbuf_len(m) - offs);
601191783Srmacklem		NFSM_DATAP(m, offs);
602191783Srmacklem	} else if (offs < 0)
603191783Srmacklem		panic("nfsm trimleading offs");
604191783Srmacklem	nd->nd_mrep = m;
605191783Srmacklem	nd->nd_md = m;
606191783Srmacklem	nd->nd_dpos = NFSMTOD(m, caddr_t);
607191783Srmacklem}
608191783Srmacklem
609191783Srmacklem/*
610191783Srmacklem * Trim trailing data off the mbuf list being built.
611191783Srmacklem */
612191783SrmacklemAPPLESTATIC void
613191783Srmacklemnewnfs_trimtrailing(nd, mb, bpos)
614191783Srmacklem	struct nfsrv_descript *nd;
615191783Srmacklem	mbuf_t mb;
616191783Srmacklem	caddr_t bpos;
617191783Srmacklem{
618191783Srmacklem
619191783Srmacklem	if (mbuf_next(mb)) {
620191783Srmacklem		mbuf_freem(mbuf_next(mb));
621191783Srmacklem		mbuf_setnext(mb, NULL);
622191783Srmacklem	}
623191783Srmacklem	mbuf_setlen(mb, bpos - NFSMTOD(mb, caddr_t));
624191783Srmacklem	nd->nd_mb = mb;
625191783Srmacklem	nd->nd_bpos = bpos;
626191783Srmacklem}
627191783Srmacklem
628191783Srmacklem/*
629191783Srmacklem * Dissect a file handle on the client.
630191783Srmacklem */
631191783SrmacklemAPPLESTATIC int
632191783Srmacklemnfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
633191783Srmacklem{
634191783Srmacklem	u_int32_t *tl;
635191783Srmacklem	struct nfsfh *nfhp;
636191783Srmacklem	int error, len;
637191783Srmacklem
638191783Srmacklem	*nfhpp = NULL;
639191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
640191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
641191783Srmacklem		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
642224086Szack			len > NFSX_FHMAX) {
643224086Szack			error = EBADRPC;
644224086Szack			goto nfsmout;
645224086Szack		}
646191783Srmacklem	} else
647191783Srmacklem		len = NFSX_V2FH;
648191783Srmacklem	MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + len,
649191783Srmacklem	    M_NFSFH, M_WAITOK);
650191783Srmacklem	error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
651191783Srmacklem	if (error) {
652191783Srmacklem		FREE((caddr_t)nfhp, M_NFSFH);
653224086Szack		goto nfsmout;
654191783Srmacklem	}
655191783Srmacklem	nfhp->nfh_len = len;
656191783Srmacklem	*nfhpp = nfhp;
657191783Srmacklemnfsmout:
658224086Szack	NFSEXITCODE2(error, nd);
659191783Srmacklem	return (error);
660191783Srmacklem}
661191783Srmacklem
662191783Srmacklem/*
663191783Srmacklem * Break down the nfsv4 acl.
664191783Srmacklem * If the aclp == NULL or won't fit in an acl, just discard the acl info.
665191783Srmacklem */
666191783SrmacklemAPPLESTATIC int
667191783Srmacklemnfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, int *aclerrp,
668191783Srmacklem    int *aclsizep, __unused NFSPROC_T *p)
669191783Srmacklem{
670191783Srmacklem	u_int32_t *tl;
671191783Srmacklem	int i, aclsize;
672191783Srmacklem	int acecnt, error = 0, aceerr = 0, acesize;
673191783Srmacklem
674191783Srmacklem	*aclerrp = 0;
675191783Srmacklem	if (aclp)
676191783Srmacklem		aclp->acl_cnt = 0;
677191783Srmacklem	/*
678191783Srmacklem	 * Parse out the ace entries and expect them to conform to
679191783Srmacklem	 * what can be supported by R/W/X bits.
680191783Srmacklem	 */
681191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
682191783Srmacklem	aclsize = NFSX_UNSIGNED;
683191783Srmacklem	acecnt = fxdr_unsigned(int, *tl);
684191783Srmacklem	if (acecnt > ACL_MAX_ENTRIES)
685224077Szack		aceerr = NFSERR_ATTRNOTSUPP;
686191783Srmacklem	if (nfsrv_useacl == 0)
687224077Szack		aceerr = NFSERR_ATTRNOTSUPP;
688191783Srmacklem	for (i = 0; i < acecnt; i++) {
689191783Srmacklem		if (aclp && !aceerr)
690191783Srmacklem			error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
691191783Srmacklem			    &aceerr, &acesize, p);
692191783Srmacklem		else
693191783Srmacklem			error = nfsrv_skipace(nd, &acesize);
694191783Srmacklem		if (error)
695224086Szack			goto nfsmout;
696191783Srmacklem		aclsize += acesize;
697191783Srmacklem	}
698191783Srmacklem	if (aclp && !aceerr)
699191783Srmacklem		aclp->acl_cnt = acecnt;
700191783Srmacklem	if (aceerr)
701191783Srmacklem		*aclerrp = aceerr;
702191783Srmacklem	if (aclsizep)
703191783Srmacklem		*aclsizep = aclsize;
704191783Srmacklemnfsmout:
705224086Szack	NFSEXITCODE2(error, nd);
706191783Srmacklem	return (error);
707191783Srmacklem}
708191783Srmacklem
709191783Srmacklem/*
710191783Srmacklem * Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
711191783Srmacklem */
712191783Srmacklemstatic int
713191783Srmacklemnfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
714191783Srmacklem{
715191783Srmacklem	u_int32_t *tl;
716191783Srmacklem	int error, len = 0;
717191783Srmacklem
718191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
719191783Srmacklem	len = fxdr_unsigned(int, *(tl + 3));
720191783Srmacklem	error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
721191783Srmacklemnfsmout:
722191783Srmacklem	*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
723224086Szack	NFSEXITCODE2(error, nd);
724191783Srmacklem	return (error);
725191783Srmacklem}
726191783Srmacklem
727191783Srmacklem/*
728191783Srmacklem * Get attribute bits from an mbuf list.
729191783Srmacklem * Returns EBADRPC for a parsing error, 0 otherwise.
730191783Srmacklem * If the clearinvalid flag is set, clear the bits not supported.
731191783Srmacklem */
732191783SrmacklemAPPLESTATIC int
733191783Srmacklemnfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
734191783Srmacklem    int *retnotsupp)
735191783Srmacklem{
736191783Srmacklem	u_int32_t *tl;
737191783Srmacklem	int cnt, i, outcnt;
738191783Srmacklem	int error = 0;
739191783Srmacklem
740191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
741191783Srmacklem	cnt = fxdr_unsigned(int, *tl);
742224086Szack	if (cnt < 0) {
743224086Szack		error = NFSERR_BADXDR;
744224086Szack		goto nfsmout;
745224086Szack	}
746253608Srmacklem	if (cnt > NFSATTRBIT_MAXWORDS)
747191783Srmacklem		outcnt = NFSATTRBIT_MAXWORDS;
748253608Srmacklem	else
749191783Srmacklem		outcnt = cnt;
750191783Srmacklem	NFSZERO_ATTRBIT(attrbitp);
751191783Srmacklem	if (outcnt > 0) {
752191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
753191783Srmacklem		for (i = 0; i < outcnt; i++)
754191783Srmacklem			attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
755191783Srmacklem	}
756253608Srmacklem	for (i = 0; i < (cnt - outcnt); i++) {
757253608Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
758253608Srmacklem		if (retnotsupp != NULL && *tl != 0)
759253608Srmacklem			*retnotsupp = NFSERR_ATTRNOTSUPP;
760253608Srmacklem	}
761191783Srmacklem	if (cntp)
762191783Srmacklem		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
763191783Srmacklemnfsmout:
764224086Szack	NFSEXITCODE2(error, nd);
765191783Srmacklem	return (error);
766191783Srmacklem}
767191783Srmacklem
768191783Srmacklem/*
769191783Srmacklem * Get the attributes for V4.
770191783Srmacklem * If the compare flag is true, test for any attribute changes,
771191783Srmacklem * otherwise return the attribute values.
772191783Srmacklem * These attributes cover fields in "struct vattr", "struct statfs",
773191783Srmacklem * "struct nfsfsinfo", the file handle and the lease duration.
774191783Srmacklem * The value of retcmpp is set to 1 if all attributes are the same,
775191783Srmacklem * and 0 otherwise.
776191783Srmacklem * Returns EBADRPC if it can't be parsed, 0 otherwise.
777191783Srmacklem */
778191783SrmacklemAPPLESTATIC int
779191783Srmacklemnfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
780191783Srmacklem    struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
781191783Srmacklem    struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
782191783Srmacklem    struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
783191783Srmacklem    u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
784191783Srmacklem{
785191783Srmacklem	u_int32_t *tl;
786224086Szack	int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
787191783Srmacklem	int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
788191783Srmacklem	u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
789191783Srmacklem	nfsattrbit_t attrbits, retattrbits, checkattrbits;
790191783Srmacklem	struct nfsfh *tnfhp;
791191783Srmacklem	struct nfsreferral *refp;
792191783Srmacklem	u_quad_t tquad;
793191783Srmacklem	nfsquad_t tnfsquad;
794191783Srmacklem	struct timespec temptime;
795191783Srmacklem	uid_t uid;
796191783Srmacklem	gid_t gid;
797191783Srmacklem	long fid;
798191783Srmacklem	u_int32_t freenum = 0, tuint;
799191783Srmacklem	u_int64_t uquad = 0, thyp, thyp2;
800191783Srmacklem#ifdef QUOTA
801191783Srmacklem	struct dqblk dqb;
802191783Srmacklem	uid_t savuid;
803191783Srmacklem#endif
804191783Srmacklem
805191783Srmacklem	if (compare) {
806191783Srmacklem		retnotsup = 0;
807191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
808191783Srmacklem	} else {
809191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
810191783Srmacklem	}
811191783Srmacklem	if (error)
812224086Szack		goto nfsmout;
813191783Srmacklem
814191783Srmacklem	if (compare) {
815191783Srmacklem		*retcmpp = retnotsup;
816191783Srmacklem	} else {
817191783Srmacklem		/*
818191783Srmacklem		 * Just set default values to some of the important ones.
819191783Srmacklem		 */
820191783Srmacklem		if (nap != NULL) {
821191783Srmacklem			nap->na_type = VREG;
822191783Srmacklem			nap->na_mode = 0;
823191783Srmacklem			nap->na_rdev = (NFSDEV_T)0;
824191783Srmacklem			nap->na_mtime.tv_sec = 0;
825191783Srmacklem			nap->na_mtime.tv_nsec = 0;
826191783Srmacklem			nap->na_gen = 0;
827191783Srmacklem			nap->na_flags = 0;
828191783Srmacklem			nap->na_blocksize = NFS_FABLKSIZE;
829191783Srmacklem		}
830191783Srmacklem		if (sbp != NULL) {
831191783Srmacklem			sbp->f_bsize = NFS_FABLKSIZE;
832191783Srmacklem			sbp->f_blocks = 0;
833191783Srmacklem			sbp->f_bfree = 0;
834191783Srmacklem			sbp->f_bavail = 0;
835191783Srmacklem			sbp->f_files = 0;
836191783Srmacklem			sbp->f_ffree = 0;
837191783Srmacklem		}
838191783Srmacklem		if (fsp != NULL) {
839191783Srmacklem			fsp->fs_rtmax = 8192;
840191783Srmacklem			fsp->fs_rtpref = 8192;
841191783Srmacklem			fsp->fs_maxname = NFS_MAXNAMLEN;
842191783Srmacklem			fsp->fs_wtmax = 8192;
843191783Srmacklem			fsp->fs_wtpref = 8192;
844191783Srmacklem			fsp->fs_wtmult = NFS_FABLKSIZE;
845191783Srmacklem			fsp->fs_dtpref = 8192;
846191783Srmacklem			fsp->fs_maxfilesize = 0xffffffffffffffffull;
847191783Srmacklem			fsp->fs_timedelta.tv_sec = 0;
848191783Srmacklem			fsp->fs_timedelta.tv_nsec = 1;
849191783Srmacklem			fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
850191783Srmacklem				NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
851191783Srmacklem		}
852191783Srmacklem		if (pc != NULL) {
853191783Srmacklem			pc->pc_linkmax = LINK_MAX;
854191783Srmacklem			pc->pc_namemax = NAME_MAX;
855191783Srmacklem			pc->pc_notrunc = 0;
856191783Srmacklem			pc->pc_chownrestricted = 0;
857191783Srmacklem			pc->pc_caseinsensitive = 0;
858191783Srmacklem			pc->pc_casepreserving = 1;
859191783Srmacklem		}
860191783Srmacklem	}
861191783Srmacklem
862191783Srmacklem	/*
863191783Srmacklem	 * Loop around getting the attributes.
864191783Srmacklem	 */
865191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
866191783Srmacklem	attrsize = fxdr_unsigned(int, *tl);
867191783Srmacklem	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
868191783Srmacklem	    if (attrsum > attrsize) {
869191783Srmacklem		error = NFSERR_BADXDR;
870191783Srmacklem		goto nfsmout;
871191783Srmacklem	    }
872191783Srmacklem	    if (NFSISSET_ATTRBIT(&attrbits, bitpos))
873191783Srmacklem		switch (bitpos) {
874191783Srmacklem		case NFSATTRBIT_SUPPORTEDATTRS:
875191783Srmacklem			retnotsup = 0;
876191783Srmacklem			if (compare || nap == NULL)
877191783Srmacklem			    error = nfsrv_getattrbits(nd, &retattrbits,
878191783Srmacklem				&cnt, &retnotsup);
879191783Srmacklem			else
880191783Srmacklem			    error = nfsrv_getattrbits(nd, &nap->na_suppattr,
881191783Srmacklem				&cnt, &retnotsup);
882191783Srmacklem			if (error)
883224086Szack			    goto nfsmout;
884191783Srmacklem			if (compare && !(*retcmpp)) {
885191783Srmacklem			   NFSSETSUPP_ATTRBIT(&checkattrbits);
886191783Srmacklem			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
887191783Srmacklem			       || retnotsup)
888191783Srmacklem				*retcmpp = NFSERR_NOTSAME;
889191783Srmacklem			}
890191783Srmacklem			attrsum += cnt;
891191783Srmacklem			break;
892191783Srmacklem		case NFSATTRBIT_TYPE:
893191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
894191783Srmacklem			if (compare) {
895191783Srmacklem				if (!(*retcmpp)) {
896191783Srmacklem				    if (nap->na_type != nfsv34tov_type(*tl))
897191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
898191783Srmacklem				}
899191783Srmacklem			} else if (nap != NULL) {
900191783Srmacklem				nap->na_type = nfsv34tov_type(*tl);
901191783Srmacklem			}
902191783Srmacklem			attrsum += NFSX_UNSIGNED;
903191783Srmacklem			break;
904191783Srmacklem		case NFSATTRBIT_FHEXPIRETYPE:
905191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
906191783Srmacklem			if (compare && !(*retcmpp)) {
907191783Srmacklem				if (fxdr_unsigned(int, *tl) !=
908191783Srmacklem					NFSV4FHTYPE_PERSISTENT)
909191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
910191783Srmacklem			}
911191783Srmacklem			attrsum += NFSX_UNSIGNED;
912191783Srmacklem			break;
913191783Srmacklem		case NFSATTRBIT_CHANGE:
914191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
915191783Srmacklem			if (compare) {
916191783Srmacklem				if (!(*retcmpp)) {
917191783Srmacklem				    if (nap->na_filerev != fxdr_hyper(tl))
918191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
919191783Srmacklem				}
920191783Srmacklem			} else if (nap != NULL) {
921191783Srmacklem				nap->na_filerev = fxdr_hyper(tl);
922191783Srmacklem			}
923191783Srmacklem			attrsum += NFSX_HYPER;
924191783Srmacklem			break;
925191783Srmacklem		case NFSATTRBIT_SIZE:
926191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
927191783Srmacklem			if (compare) {
928191783Srmacklem				if (!(*retcmpp)) {
929191783Srmacklem				    if (nap->na_size != fxdr_hyper(tl))
930191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
931191783Srmacklem				}
932191783Srmacklem			} else if (nap != NULL) {
933191783Srmacklem				nap->na_size = fxdr_hyper(tl);
934191783Srmacklem			}
935191783Srmacklem			attrsum += NFSX_HYPER;
936191783Srmacklem			break;
937191783Srmacklem		case NFSATTRBIT_LINKSUPPORT:
938191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
939191783Srmacklem			if (compare) {
940191783Srmacklem				if (!(*retcmpp)) {
941191783Srmacklem				    if (fsp->fs_properties & NFSV3_FSFLINK) {
942191783Srmacklem					if (*tl == newnfs_false)
943191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
944191783Srmacklem				    } else {
945191783Srmacklem					if (*tl == newnfs_true)
946191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
947191783Srmacklem				    }
948191783Srmacklem				}
949191783Srmacklem			} else if (fsp != NULL) {
950191783Srmacklem				if (*tl == newnfs_true)
951191783Srmacklem					fsp->fs_properties |= NFSV3_FSFLINK;
952191783Srmacklem				else
953191783Srmacklem					fsp->fs_properties &= ~NFSV3_FSFLINK;
954191783Srmacklem			}
955191783Srmacklem			attrsum += NFSX_UNSIGNED;
956191783Srmacklem			break;
957191783Srmacklem		case NFSATTRBIT_SYMLINKSUPPORT:
958191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
959191783Srmacklem			if (compare) {
960191783Srmacklem				if (!(*retcmpp)) {
961191783Srmacklem				    if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
962191783Srmacklem					if (*tl == newnfs_false)
963191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
964191783Srmacklem				    } else {
965191783Srmacklem					if (*tl == newnfs_true)
966191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
967191783Srmacklem				    }
968191783Srmacklem				}
969191783Srmacklem			} else if (fsp != NULL) {
970191783Srmacklem				if (*tl == newnfs_true)
971191783Srmacklem					fsp->fs_properties |= NFSV3_FSFSYMLINK;
972191783Srmacklem				else
973191783Srmacklem					fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
974191783Srmacklem			}
975191783Srmacklem			attrsum += NFSX_UNSIGNED;
976191783Srmacklem			break;
977191783Srmacklem		case NFSATTRBIT_NAMEDATTR:
978191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
979191783Srmacklem			if (compare && !(*retcmpp)) {
980191783Srmacklem				if (*tl != newnfs_false)
981191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
982191783Srmacklem			}
983191783Srmacklem			attrsum += NFSX_UNSIGNED;
984191783Srmacklem			break;
985191783Srmacklem		case NFSATTRBIT_FSID:
986191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
987191783Srmacklem			thyp = fxdr_hyper(tl);
988191783Srmacklem			tl += 2;
989191783Srmacklem			thyp2 = fxdr_hyper(tl);
990191783Srmacklem			if (compare) {
991191783Srmacklem			    if (*retcmpp == 0) {
992191783Srmacklem				if (thyp != (u_int64_t)
993191783Srmacklem				    vfs_statfs(vnode_mount(vp))->f_fsid.val[0] ||
994191783Srmacklem				    thyp2 != (u_int64_t)
995191783Srmacklem				    vfs_statfs(vnode_mount(vp))->f_fsid.val[1])
996191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
997191783Srmacklem			    }
998191783Srmacklem			} else if (nap != NULL) {
999191783Srmacklem				nap->na_filesid[0] = thyp;
1000191783Srmacklem				nap->na_filesid[1] = thyp2;
1001191783Srmacklem			}
1002191783Srmacklem			attrsum += (4 * NFSX_UNSIGNED);
1003191783Srmacklem			break;
1004191783Srmacklem		case NFSATTRBIT_UNIQUEHANDLES:
1005191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1006191783Srmacklem			if (compare && !(*retcmpp)) {
1007191783Srmacklem				if (*tl != newnfs_true)
1008191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1009191783Srmacklem			}
1010191783Srmacklem			attrsum += NFSX_UNSIGNED;
1011191783Srmacklem			break;
1012191783Srmacklem		case NFSATTRBIT_LEASETIME:
1013191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1014191783Srmacklem			if (compare) {
1015191783Srmacklem				if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
1016191783Srmacklem				    !(*retcmpp))
1017191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1018191783Srmacklem			} else if (leasep != NULL) {
1019191783Srmacklem				*leasep = fxdr_unsigned(u_int32_t, *tl);
1020191783Srmacklem			}
1021191783Srmacklem			attrsum += NFSX_UNSIGNED;
1022191783Srmacklem			break;
1023191783Srmacklem		case NFSATTRBIT_RDATTRERROR:
1024191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1025191783Srmacklem			if (compare) {
1026191783Srmacklem				 if (!(*retcmpp))
1027191783Srmacklem					*retcmpp = NFSERR_INVAL;
1028191783Srmacklem			} else if (rderrp != NULL) {
1029191783Srmacklem				*rderrp = fxdr_unsigned(u_int32_t, *tl);
1030191783Srmacklem			}
1031191783Srmacklem			attrsum += NFSX_UNSIGNED;
1032191783Srmacklem			break;
1033191783Srmacklem		case NFSATTRBIT_ACL:
1034191783Srmacklem			if (compare) {
1035191783Srmacklem			  if (!(*retcmpp)) {
1036191783Srmacklem			    if (nfsrv_useacl) {
1037191783Srmacklem				NFSACL_T *naclp;
1038191783Srmacklem
1039192861Srmacklem				naclp = acl_alloc(M_WAITOK);
1040191783Srmacklem				error = nfsrv_dissectacl(nd, naclp, &aceerr,
1041191783Srmacklem				    &cnt, p);
1042191783Srmacklem				if (error) {
1043191783Srmacklem				    acl_free(naclp);
1044224086Szack				    goto nfsmout;
1045191783Srmacklem				}
1046224121Szack				if (aceerr || aclp == NULL ||
1047224121Szack				    nfsrv_compareacl(aclp, naclp))
1048191783Srmacklem				    *retcmpp = NFSERR_NOTSAME;
1049191783Srmacklem				acl_free(naclp);
1050200069Strasz			    } else {
1051191783Srmacklem				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1052191783Srmacklem				    &cnt, p);
1053191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1054191783Srmacklem			    }
1055191783Srmacklem			  }
1056191783Srmacklem			} else {
1057191783Srmacklem			    if (vp != NULL && aclp != NULL)
1058191783Srmacklem				error = nfsrv_dissectacl(nd, aclp, &aceerr,
1059191783Srmacklem				    &cnt, p);
1060191783Srmacklem			    else
1061191783Srmacklem				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1062191783Srmacklem				    &cnt, p);
1063191783Srmacklem			    if (error)
1064224086Szack				goto nfsmout;
1065191783Srmacklem			}
1066191783Srmacklem			attrsum += cnt;
1067191783Srmacklem			break;
1068191783Srmacklem		case NFSATTRBIT_ACLSUPPORT:
1069191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1070191783Srmacklem			if (compare && !(*retcmpp)) {
1071191783Srmacklem				if (nfsrv_useacl) {
1072191783Srmacklem					if (fxdr_unsigned(u_int32_t, *tl) !=
1073191783Srmacklem					    NFSV4ACE_SUPTYPES)
1074191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1075191783Srmacklem				} else {
1076191783Srmacklem					*retcmpp = NFSERR_ATTRNOTSUPP;
1077191783Srmacklem				}
1078191783Srmacklem			}
1079191783Srmacklem			attrsum += NFSX_UNSIGNED;
1080191783Srmacklem			break;
1081191783Srmacklem		case NFSATTRBIT_ARCHIVE:
1082191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1083191783Srmacklem			if (compare && !(*retcmpp))
1084191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1085191783Srmacklem			attrsum += NFSX_UNSIGNED;
1086191783Srmacklem			break;
1087191783Srmacklem		case NFSATTRBIT_CANSETTIME:
1088191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1089191783Srmacklem			if (compare) {
1090191783Srmacklem				if (!(*retcmpp)) {
1091191783Srmacklem				    if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
1092191783Srmacklem					if (*tl == newnfs_false)
1093191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1094191783Srmacklem				    } else {
1095191783Srmacklem					if (*tl == newnfs_true)
1096191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1097191783Srmacklem				    }
1098191783Srmacklem				}
1099191783Srmacklem			} else if (fsp != NULL) {
1100191783Srmacklem				if (*tl == newnfs_true)
1101191783Srmacklem					fsp->fs_properties |= NFSV3_FSFCANSETTIME;
1102191783Srmacklem				else
1103191783Srmacklem					fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
1104191783Srmacklem			}
1105191783Srmacklem			attrsum += NFSX_UNSIGNED;
1106191783Srmacklem			break;
1107191783Srmacklem		case NFSATTRBIT_CASEINSENSITIVE:
1108191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1109191783Srmacklem			if (compare) {
1110191783Srmacklem				if (!(*retcmpp)) {
1111191783Srmacklem				    if (*tl != newnfs_false)
1112191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1113191783Srmacklem				}
1114191783Srmacklem			} else if (pc != NULL) {
1115191783Srmacklem				pc->pc_caseinsensitive =
1116191783Srmacklem				    fxdr_unsigned(u_int32_t, *tl);
1117191783Srmacklem			}
1118191783Srmacklem			attrsum += NFSX_UNSIGNED;
1119191783Srmacklem			break;
1120191783Srmacklem		case NFSATTRBIT_CASEPRESERVING:
1121191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1122191783Srmacklem			if (compare) {
1123191783Srmacklem				if (!(*retcmpp)) {
1124191783Srmacklem				    if (*tl != newnfs_true)
1125191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1126191783Srmacklem				}
1127191783Srmacklem			} else if (pc != NULL) {
1128191783Srmacklem				pc->pc_casepreserving =
1129191783Srmacklem				    fxdr_unsigned(u_int32_t, *tl);
1130191783Srmacklem			}
1131191783Srmacklem			attrsum += NFSX_UNSIGNED;
1132191783Srmacklem			break;
1133191783Srmacklem		case NFSATTRBIT_CHOWNRESTRICTED:
1134191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1135191783Srmacklem			if (compare) {
1136191783Srmacklem				if (!(*retcmpp)) {
1137224121Szack				    if (*tl != newnfs_true)
1138224121Szack					*retcmpp = NFSERR_NOTSAME;
1139191783Srmacklem				}
1140191783Srmacklem			} else if (pc != NULL) {
1141191783Srmacklem				pc->pc_chownrestricted =
1142191783Srmacklem				    fxdr_unsigned(u_int32_t, *tl);
1143191783Srmacklem			}
1144191783Srmacklem			attrsum += NFSX_UNSIGNED;
1145191783Srmacklem			break;
1146191783Srmacklem		case NFSATTRBIT_FILEHANDLE:
1147191783Srmacklem			error = nfsm_getfh(nd, &tnfhp);
1148191783Srmacklem			if (error)
1149224086Szack				goto nfsmout;
1150191783Srmacklem			tfhsize = tnfhp->nfh_len;
1151191783Srmacklem			if (compare) {
1152191783Srmacklem				if (!(*retcmpp) &&
1153191783Srmacklem				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
1154191783Srmacklem				     fhp, fhsize))
1155191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1156191783Srmacklem				FREE((caddr_t)tnfhp, M_NFSFH);
1157191783Srmacklem			} else if (nfhpp != NULL) {
1158191783Srmacklem				*nfhpp = tnfhp;
1159191783Srmacklem			} else {
1160191783Srmacklem				FREE((caddr_t)tnfhp, M_NFSFH);
1161191783Srmacklem			}
1162191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
1163191783Srmacklem			break;
1164191783Srmacklem		case NFSATTRBIT_FILEID:
1165191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1166191783Srmacklem			thyp = fxdr_hyper(tl);
1167191783Srmacklem			if (compare) {
1168191783Srmacklem				if (!(*retcmpp)) {
1169191783Srmacklem				    if ((u_int64_t)nap->na_fileid != thyp)
1170191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1171191783Srmacklem				}
1172191783Srmacklem			} else if (nap != NULL) {
1173191783Srmacklem				if (*tl++)
1174191783Srmacklem					printf("NFSv4 fileid > 32bits\n");
1175191783Srmacklem				nap->na_fileid = thyp;
1176191783Srmacklem			}
1177191783Srmacklem			attrsum += NFSX_HYPER;
1178191783Srmacklem			break;
1179191783Srmacklem		case NFSATTRBIT_FILESAVAIL:
1180191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1181191783Srmacklem			if (compare) {
1182191783Srmacklem				if (!(*retcmpp) &&
1183191783Srmacklem				    sfp->sf_afiles != fxdr_hyper(tl))
1184191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1185191783Srmacklem			} else if (sfp != NULL) {
1186191783Srmacklem				sfp->sf_afiles = fxdr_hyper(tl);
1187191783Srmacklem			}
1188191783Srmacklem			attrsum += NFSX_HYPER;
1189191783Srmacklem			break;
1190191783Srmacklem		case NFSATTRBIT_FILESFREE:
1191191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1192191783Srmacklem			if (compare) {
1193191783Srmacklem				if (!(*retcmpp) &&
1194191783Srmacklem				    sfp->sf_ffiles != fxdr_hyper(tl))
1195191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1196191783Srmacklem			} else if (sfp != NULL) {
1197191783Srmacklem				sfp->sf_ffiles = fxdr_hyper(tl);
1198191783Srmacklem			}
1199191783Srmacklem			attrsum += NFSX_HYPER;
1200191783Srmacklem			break;
1201191783Srmacklem		case NFSATTRBIT_FILESTOTAL:
1202191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1203191783Srmacklem			if (compare) {
1204191783Srmacklem				if (!(*retcmpp) &&
1205191783Srmacklem				    sfp->sf_tfiles != fxdr_hyper(tl))
1206191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1207191783Srmacklem			} else if (sfp != NULL) {
1208191783Srmacklem				sfp->sf_tfiles = fxdr_hyper(tl);
1209191783Srmacklem			}
1210191783Srmacklem			attrsum += NFSX_HYPER;
1211191783Srmacklem			break;
1212191783Srmacklem		case NFSATTRBIT_FSLOCATIONS:
1213191783Srmacklem			error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
1214191783Srmacklem			if (error)
1215224086Szack				goto nfsmout;
1216191783Srmacklem			attrsum += l;
1217191783Srmacklem			if (compare && !(*retcmpp)) {
1218191783Srmacklem				refp = nfsv4root_getreferral(vp, NULL, 0);
1219191783Srmacklem				if (refp != NULL) {
1220191783Srmacklem					if (cp == NULL || cp2 == NULL ||
1221191783Srmacklem					    strcmp(cp, "/") ||
1222191783Srmacklem					    strcmp(cp2, refp->nfr_srvlist))
1223191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1224191783Srmacklem				} else if (m == 0) {
1225191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1226191783Srmacklem				}
1227191783Srmacklem			}
1228191783Srmacklem			if (cp != NULL)
1229191783Srmacklem				free(cp, M_NFSSTRING);
1230191783Srmacklem			if (cp2 != NULL)
1231191783Srmacklem				free(cp2, M_NFSSTRING);
1232191783Srmacklem			break;
1233191783Srmacklem		case NFSATTRBIT_HIDDEN:
1234191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1235191783Srmacklem			if (compare && !(*retcmpp))
1236191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1237191783Srmacklem			attrsum += NFSX_UNSIGNED;
1238191783Srmacklem			break;
1239191783Srmacklem		case NFSATTRBIT_HOMOGENEOUS:
1240191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1241191783Srmacklem			if (compare) {
1242191783Srmacklem				if (!(*retcmpp)) {
1243191783Srmacklem				    if (fsp->fs_properties &
1244191783Srmacklem					NFSV3_FSFHOMOGENEOUS) {
1245191783Srmacklem					if (*tl == newnfs_false)
1246191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1247191783Srmacklem				    } else {
1248191783Srmacklem					if (*tl == newnfs_true)
1249191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1250191783Srmacklem				    }
1251191783Srmacklem				}
1252191783Srmacklem			} else if (fsp != NULL) {
1253191783Srmacklem				if (*tl == newnfs_true)
1254191783Srmacklem				    fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
1255191783Srmacklem				else
1256191783Srmacklem				    fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
1257191783Srmacklem			}
1258191783Srmacklem			attrsum += NFSX_UNSIGNED;
1259191783Srmacklem			break;
1260191783Srmacklem		case NFSATTRBIT_MAXFILESIZE:
1261191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1262191783Srmacklem			tnfsquad.qval = fxdr_hyper(tl);
1263191783Srmacklem			if (compare) {
1264191783Srmacklem				if (!(*retcmpp)) {
1265191783Srmacklem					tquad = NFSRV_MAXFILESIZE;
1266191783Srmacklem					if (tquad != tnfsquad.qval)
1267191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1268191783Srmacklem				}
1269191783Srmacklem			} else if (fsp != NULL) {
1270191783Srmacklem				fsp->fs_maxfilesize = tnfsquad.qval;
1271191783Srmacklem			}
1272191783Srmacklem			attrsum += NFSX_HYPER;
1273191783Srmacklem			break;
1274191783Srmacklem		case NFSATTRBIT_MAXLINK:
1275191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1276191783Srmacklem			if (compare) {
1277191783Srmacklem				if (!(*retcmpp)) {
1278191783Srmacklem				    if (fxdr_unsigned(int, *tl) != LINK_MAX)
1279191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1280191783Srmacklem				}
1281191783Srmacklem			} else if (pc != NULL) {
1282191783Srmacklem				pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
1283191783Srmacklem			}
1284191783Srmacklem			attrsum += NFSX_UNSIGNED;
1285191783Srmacklem			break;
1286191783Srmacklem		case NFSATTRBIT_MAXNAME:
1287191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1288191783Srmacklem			if (compare) {
1289191783Srmacklem				if (!(*retcmpp)) {
1290191783Srmacklem				    if (fsp->fs_maxname !=
1291191783Srmacklem					fxdr_unsigned(u_int32_t, *tl))
1292191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1293191783Srmacklem				}
1294191783Srmacklem			} else {
1295191783Srmacklem				tuint = fxdr_unsigned(u_int32_t, *tl);
1296191783Srmacklem				/*
1297191783Srmacklem				 * Some Linux NFSv4 servers report this
1298191783Srmacklem				 * as 0 or 4billion, so I'll set it to
1299191783Srmacklem				 * NFS_MAXNAMLEN. If a server actually creates
1300191783Srmacklem				 * a name longer than NFS_MAXNAMLEN, it will
1301191783Srmacklem				 * get an error back.
1302191783Srmacklem				 */
1303191783Srmacklem				if (tuint == 0 || tuint > NFS_MAXNAMLEN)
1304191783Srmacklem					tuint = NFS_MAXNAMLEN;
1305191783Srmacklem				if (fsp != NULL)
1306191783Srmacklem					fsp->fs_maxname = tuint;
1307191783Srmacklem				if (pc != NULL)
1308191783Srmacklem					pc->pc_namemax = tuint;
1309191783Srmacklem			}
1310191783Srmacklem			attrsum += NFSX_UNSIGNED;
1311191783Srmacklem			break;
1312191783Srmacklem		case NFSATTRBIT_MAXREAD:
1313191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1314191783Srmacklem			if (compare) {
1315191783Srmacklem				if (!(*retcmpp)) {
1316191783Srmacklem				    if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
1317191783Srmacklem					*(tl + 1)) || *tl != 0)
1318191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1319191783Srmacklem				}
1320191783Srmacklem			} else if (fsp != NULL) {
1321191783Srmacklem				fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
1322191783Srmacklem				fsp->fs_rtpref = fsp->fs_rtmax;
1323191783Srmacklem				fsp->fs_dtpref = fsp->fs_rtpref;
1324191783Srmacklem			}
1325191783Srmacklem			attrsum += NFSX_HYPER;
1326191783Srmacklem			break;
1327191783Srmacklem		case NFSATTRBIT_MAXWRITE:
1328191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1329191783Srmacklem			if (compare) {
1330191783Srmacklem				if (!(*retcmpp)) {
1331191783Srmacklem				    if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
1332191783Srmacklem					*(tl + 1)) || *tl != 0)
1333191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1334191783Srmacklem				}
1335191783Srmacklem			} else if (fsp != NULL) {
1336191783Srmacklem				fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
1337191783Srmacklem				fsp->fs_wtpref = fsp->fs_wtmax;
1338191783Srmacklem			}
1339191783Srmacklem			attrsum += NFSX_HYPER;
1340191783Srmacklem			break;
1341191783Srmacklem		case NFSATTRBIT_MIMETYPE:
1342191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1343191783Srmacklem			i = fxdr_unsigned(int, *tl);
1344191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
1345191783Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
1346191783Srmacklem			if (error)
1347191783Srmacklem				goto nfsmout;
1348191783Srmacklem			if (compare && !(*retcmpp))
1349191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1350191783Srmacklem			break;
1351191783Srmacklem		case NFSATTRBIT_MODE:
1352191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1353191783Srmacklem			if (compare) {
1354191783Srmacklem				if (!(*retcmpp)) {
1355191783Srmacklem				    if (nap->na_mode != nfstov_mode(*tl))
1356191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1357191783Srmacklem				}
1358191783Srmacklem			} else if (nap != NULL) {
1359191783Srmacklem				nap->na_mode = nfstov_mode(*tl);
1360191783Srmacklem			}
1361191783Srmacklem			attrsum += NFSX_UNSIGNED;
1362191783Srmacklem			break;
1363191783Srmacklem		case NFSATTRBIT_NOTRUNC:
1364191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1365191783Srmacklem			if (compare) {
1366191783Srmacklem				if (!(*retcmpp)) {
1367191783Srmacklem				    if (*tl != newnfs_true)
1368191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1369191783Srmacklem				}
1370191783Srmacklem			} else if (pc != NULL) {
1371191783Srmacklem				pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
1372191783Srmacklem			}
1373191783Srmacklem			attrsum += NFSX_UNSIGNED;
1374191783Srmacklem			break;
1375191783Srmacklem		case NFSATTRBIT_NUMLINKS:
1376191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1377191783Srmacklem			tuint = fxdr_unsigned(u_int32_t, *tl);
1378191783Srmacklem			if (compare) {
1379191783Srmacklem			    if (!(*retcmpp)) {
1380191783Srmacklem				if ((u_int32_t)nap->na_nlink != tuint)
1381191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1382191783Srmacklem			    }
1383191783Srmacklem			} else if (nap != NULL) {
1384191783Srmacklem				nap->na_nlink = tuint;
1385191783Srmacklem			}
1386191783Srmacklem			attrsum += NFSX_UNSIGNED;
1387191783Srmacklem			break;
1388191783Srmacklem		case NFSATTRBIT_OWNER:
1389191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1390191783Srmacklem			j = fxdr_unsigned(int, *tl);
1391224086Szack			if (j < 0) {
1392224086Szack				error = NFSERR_BADXDR;
1393224086Szack				goto nfsmout;
1394224086Szack			}
1395191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1396191783Srmacklem			if (j > NFSV4_SMALLSTR)
1397191783Srmacklem				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1398191783Srmacklem			else
1399191783Srmacklem				cp = namestr;
1400191783Srmacklem			error = nfsrv_mtostr(nd, cp, j);
1401191783Srmacklem			if (error) {
1402191783Srmacklem				if (j > NFSV4_SMALLSTR)
1403191783Srmacklem					free(cp, M_NFSSTRING);
1404224086Szack				goto nfsmout;
1405191783Srmacklem			}
1406191783Srmacklem			if (compare) {
1407191783Srmacklem			    if (!(*retcmpp)) {
1408241194Srmacklem				if (nfsv4_strtouid(nd, cp, j, &uid, p) ||
1409191783Srmacklem				    nap->na_uid != uid)
1410191783Srmacklem				    *retcmpp = NFSERR_NOTSAME;
1411191783Srmacklem			    }
1412191783Srmacklem			} else if (nap != NULL) {
1413241194Srmacklem				if (nfsv4_strtouid(nd, cp, j, &uid, p))
1414191783Srmacklem					nap->na_uid = nfsrv_defaultuid;
1415191783Srmacklem				else
1416191783Srmacklem					nap->na_uid = uid;
1417191783Srmacklem			}
1418191783Srmacklem			if (j > NFSV4_SMALLSTR)
1419191783Srmacklem				free(cp, M_NFSSTRING);
1420191783Srmacklem			break;
1421191783Srmacklem		case NFSATTRBIT_OWNERGROUP:
1422191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1423191783Srmacklem			j = fxdr_unsigned(int, *tl);
1424224086Szack			if (j < 0) {
1425224086Szack				error =  NFSERR_BADXDR;
1426224086Szack				goto nfsmout;
1427224086Szack			}
1428191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1429191783Srmacklem			if (j > NFSV4_SMALLSTR)
1430191783Srmacklem				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1431191783Srmacklem			else
1432191783Srmacklem				cp = namestr;
1433191783Srmacklem			error = nfsrv_mtostr(nd, cp, j);
1434191783Srmacklem			if (error) {
1435191783Srmacklem				if (j > NFSV4_SMALLSTR)
1436191783Srmacklem					free(cp, M_NFSSTRING);
1437224086Szack				goto nfsmout;
1438191783Srmacklem			}
1439191783Srmacklem			if (compare) {
1440191783Srmacklem			    if (!(*retcmpp)) {
1441241194Srmacklem				if (nfsv4_strtogid(nd, cp, j, &gid, p) ||
1442191783Srmacklem				    nap->na_gid != gid)
1443191783Srmacklem				    *retcmpp = NFSERR_NOTSAME;
1444191783Srmacklem			    }
1445191783Srmacklem			} else if (nap != NULL) {
1446241194Srmacklem				if (nfsv4_strtogid(nd, cp, j, &gid, p))
1447191783Srmacklem					nap->na_gid = nfsrv_defaultgid;
1448191783Srmacklem				else
1449191783Srmacklem					nap->na_gid = gid;
1450191783Srmacklem			}
1451191783Srmacklem			if (j > NFSV4_SMALLSTR)
1452191783Srmacklem				free(cp, M_NFSSTRING);
1453191783Srmacklem			break;
1454191783Srmacklem		case NFSATTRBIT_QUOTAHARD:
1455191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1456191783Srmacklem			if (sbp != NULL) {
1457191783Srmacklem			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1458191783Srmacklem				freenum = sbp->f_bfree;
1459191783Srmacklem			    else
1460191783Srmacklem				freenum = sbp->f_bavail;
1461191783Srmacklem#ifdef QUOTA
1462191783Srmacklem			    /*
1463191783Srmacklem			     * ufs_quotactl() insists that the uid argument
1464191783Srmacklem			     * equal p_ruid for non-root quota access, so
1465191783Srmacklem			     * we'll just make sure that's the case.
1466191783Srmacklem			     */
1467191783Srmacklem			    savuid = p->p_cred->p_ruid;
1468191783Srmacklem			    p->p_cred->p_ruid = cred->cr_uid;
1469191783Srmacklem			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1470191990Sattilio				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1471191783Srmacklem				freenum = min(dqb.dqb_bhardlimit, freenum);
1472191783Srmacklem			    p->p_cred->p_ruid = savuid;
1473191783Srmacklem#endif	/* QUOTA */
1474191783Srmacklem			    uquad = (u_int64_t)freenum;
1475191783Srmacklem			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1476191783Srmacklem			}
1477191783Srmacklem			if (compare && !(*retcmpp)) {
1478191783Srmacklem				if (uquad != fxdr_hyper(tl))
1479191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1480191783Srmacklem			}
1481191783Srmacklem			attrsum += NFSX_HYPER;
1482191783Srmacklem			break;
1483191783Srmacklem		case NFSATTRBIT_QUOTASOFT:
1484191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1485191783Srmacklem			if (sbp != NULL) {
1486191783Srmacklem			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1487191783Srmacklem				freenum = sbp->f_bfree;
1488191783Srmacklem			    else
1489191783Srmacklem				freenum = sbp->f_bavail;
1490191783Srmacklem#ifdef QUOTA
1491191783Srmacklem			    /*
1492191783Srmacklem			     * ufs_quotactl() insists that the uid argument
1493191783Srmacklem			     * equal p_ruid for non-root quota access, so
1494191783Srmacklem			     * we'll just make sure that's the case.
1495191783Srmacklem			     */
1496191783Srmacklem			    savuid = p->p_cred->p_ruid;
1497191783Srmacklem			    p->p_cred->p_ruid = cred->cr_uid;
1498191783Srmacklem			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1499191990Sattilio				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1500191783Srmacklem				freenum = min(dqb.dqb_bsoftlimit, freenum);
1501191783Srmacklem			    p->p_cred->p_ruid = savuid;
1502191783Srmacklem#endif	/* QUOTA */
1503191783Srmacklem			    uquad = (u_int64_t)freenum;
1504191783Srmacklem			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1505191783Srmacklem			}
1506191783Srmacklem			if (compare && !(*retcmpp)) {
1507191783Srmacklem				if (uquad != fxdr_hyper(tl))
1508191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1509191783Srmacklem			}
1510191783Srmacklem			attrsum += NFSX_HYPER;
1511191783Srmacklem			break;
1512191783Srmacklem		case NFSATTRBIT_QUOTAUSED:
1513191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1514191783Srmacklem			if (sbp != NULL) {
1515191783Srmacklem			    freenum = 0;
1516191783Srmacklem#ifdef QUOTA
1517191783Srmacklem			    /*
1518191783Srmacklem			     * ufs_quotactl() insists that the uid argument
1519191783Srmacklem			     * equal p_ruid for non-root quota access, so
1520191783Srmacklem			     * we'll just make sure that's the case.
1521191783Srmacklem			     */
1522191783Srmacklem			    savuid = p->p_cred->p_ruid;
1523191783Srmacklem			    p->p_cred->p_ruid = cred->cr_uid;
1524191783Srmacklem			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1525191990Sattilio				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1526191783Srmacklem				freenum = dqb.dqb_curblocks;
1527191783Srmacklem			    p->p_cred->p_ruid = savuid;
1528191783Srmacklem#endif	/* QUOTA */
1529191783Srmacklem			    uquad = (u_int64_t)freenum;
1530191783Srmacklem			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1531191783Srmacklem			}
1532191783Srmacklem			if (compare && !(*retcmpp)) {
1533191783Srmacklem				if (uquad != fxdr_hyper(tl))
1534191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1535191783Srmacklem			}
1536191783Srmacklem			attrsum += NFSX_HYPER;
1537191783Srmacklem			break;
1538191783Srmacklem		case NFSATTRBIT_RAWDEV:
1539191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
1540191783Srmacklem			j = fxdr_unsigned(int, *tl++);
1541191783Srmacklem			k = fxdr_unsigned(int, *tl);
1542191783Srmacklem			if (compare) {
1543191783Srmacklem			    if (!(*retcmpp)) {
1544191783Srmacklem				if (nap->na_rdev != NFSMAKEDEV(j, k))
1545191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1546191783Srmacklem			    }
1547191783Srmacklem			} else if (nap != NULL) {
1548191783Srmacklem				nap->na_rdev = NFSMAKEDEV(j, k);
1549191783Srmacklem			}
1550191783Srmacklem			attrsum += NFSX_V4SPECDATA;
1551191783Srmacklem			break;
1552191783Srmacklem		case NFSATTRBIT_SPACEAVAIL:
1553191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1554191783Srmacklem			if (compare) {
1555191783Srmacklem				if (!(*retcmpp) &&
1556191783Srmacklem				    sfp->sf_abytes != fxdr_hyper(tl))
1557191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1558191783Srmacklem			} else if (sfp != NULL) {
1559191783Srmacklem				sfp->sf_abytes = fxdr_hyper(tl);
1560191783Srmacklem			}
1561191783Srmacklem			attrsum += NFSX_HYPER;
1562191783Srmacklem			break;
1563191783Srmacklem		case NFSATTRBIT_SPACEFREE:
1564191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1565191783Srmacklem			if (compare) {
1566191783Srmacklem				if (!(*retcmpp) &&
1567191783Srmacklem				    sfp->sf_fbytes != fxdr_hyper(tl))
1568191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1569191783Srmacklem			} else if (sfp != NULL) {
1570191783Srmacklem				sfp->sf_fbytes = fxdr_hyper(tl);
1571191783Srmacklem			}
1572191783Srmacklem			attrsum += NFSX_HYPER;
1573191783Srmacklem			break;
1574191783Srmacklem		case NFSATTRBIT_SPACETOTAL:
1575191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1576191783Srmacklem			if (compare) {
1577191783Srmacklem				if (!(*retcmpp) &&
1578191783Srmacklem				    sfp->sf_tbytes != fxdr_hyper(tl))
1579191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1580191783Srmacklem			} else if (sfp != NULL) {
1581191783Srmacklem				sfp->sf_tbytes = fxdr_hyper(tl);
1582191783Srmacklem			}
1583191783Srmacklem			attrsum += NFSX_HYPER;
1584191783Srmacklem			break;
1585191783Srmacklem		case NFSATTRBIT_SPACEUSED:
1586191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1587191783Srmacklem			thyp = fxdr_hyper(tl);
1588191783Srmacklem			if (compare) {
1589191783Srmacklem			    if (!(*retcmpp)) {
1590191783Srmacklem				if ((u_int64_t)nap->na_bytes != thyp)
1591191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1592191783Srmacklem			    }
1593191783Srmacklem			} else if (nap != NULL) {
1594191783Srmacklem				nap->na_bytes = thyp;
1595191783Srmacklem			}
1596191783Srmacklem			attrsum += NFSX_HYPER;
1597191783Srmacklem			break;
1598191783Srmacklem		case NFSATTRBIT_SYSTEM:
1599191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1600191783Srmacklem			if (compare && !(*retcmpp))
1601191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1602191783Srmacklem			attrsum += NFSX_UNSIGNED;
1603191783Srmacklem			break;
1604191783Srmacklem		case NFSATTRBIT_TIMEACCESS:
1605191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1606191783Srmacklem			fxdr_nfsv4time(tl, &temptime);
1607191783Srmacklem			if (compare) {
1608191783Srmacklem			    if (!(*retcmpp)) {
1609191783Srmacklem				if (!NFS_CMPTIME(temptime, nap->na_atime))
1610191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1611191783Srmacklem			    }
1612191783Srmacklem			} else if (nap != NULL) {
1613191783Srmacklem				nap->na_atime = temptime;
1614191783Srmacklem			}
1615191783Srmacklem			attrsum += NFSX_V4TIME;
1616191783Srmacklem			break;
1617191783Srmacklem		case NFSATTRBIT_TIMEACCESSSET:
1618191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1619191783Srmacklem			attrsum += NFSX_UNSIGNED;
1620191783Srmacklem			i = fxdr_unsigned(int, *tl);
1621191783Srmacklem			if (i == NFSV4SATTRTIME_TOCLIENT) {
1622191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1623191783Srmacklem				attrsum += NFSX_V4TIME;
1624191783Srmacklem			}
1625191783Srmacklem			if (compare && !(*retcmpp))
1626191783Srmacklem				*retcmpp = NFSERR_INVAL;
1627191783Srmacklem			break;
1628191783Srmacklem		case NFSATTRBIT_TIMEBACKUP:
1629191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1630191783Srmacklem			if (compare && !(*retcmpp))
1631191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1632191783Srmacklem			attrsum += NFSX_V4TIME;
1633191783Srmacklem			break;
1634191783Srmacklem		case NFSATTRBIT_TIMECREATE:
1635191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1636191783Srmacklem			if (compare && !(*retcmpp))
1637191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1638191783Srmacklem			attrsum += NFSX_V4TIME;
1639191783Srmacklem			break;
1640191783Srmacklem		case NFSATTRBIT_TIMEDELTA:
1641191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1642191783Srmacklem			if (fsp != NULL) {
1643191783Srmacklem			    if (compare) {
1644191783Srmacklem				if (!(*retcmpp)) {
1645191783Srmacklem				    if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
1646191783Srmacklem					fxdr_unsigned(u_int32_t, *(tl + 1)) ||
1647191783Srmacklem				        (u_int32_t)fsp->fs_timedelta.tv_nsec !=
1648191783Srmacklem					(fxdr_unsigned(u_int32_t, *(tl + 2)) %
1649191783Srmacklem					 1000000000) ||
1650191783Srmacklem					*tl != 0)
1651191783Srmacklem					    *retcmpp = NFSERR_NOTSAME;
1652191783Srmacklem				}
1653191783Srmacklem			    } else {
1654191783Srmacklem				fxdr_nfsv4time(tl, &fsp->fs_timedelta);
1655191783Srmacklem			    }
1656191783Srmacklem			}
1657191783Srmacklem			attrsum += NFSX_V4TIME;
1658191783Srmacklem			break;
1659191783Srmacklem		case NFSATTRBIT_TIMEMETADATA:
1660191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1661191783Srmacklem			fxdr_nfsv4time(tl, &temptime);
1662191783Srmacklem			if (compare) {
1663191783Srmacklem			    if (!(*retcmpp)) {
1664191783Srmacklem				if (!NFS_CMPTIME(temptime, nap->na_ctime))
1665191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1666191783Srmacklem			    }
1667191783Srmacklem			} else if (nap != NULL) {
1668191783Srmacklem				nap->na_ctime = temptime;
1669191783Srmacklem			}
1670191783Srmacklem			attrsum += NFSX_V4TIME;
1671191783Srmacklem			break;
1672191783Srmacklem		case NFSATTRBIT_TIMEMODIFY:
1673191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1674191783Srmacklem			fxdr_nfsv4time(tl, &temptime);
1675191783Srmacklem			if (compare) {
1676191783Srmacklem			    if (!(*retcmpp)) {
1677191783Srmacklem				if (!NFS_CMPTIME(temptime, nap->na_mtime))
1678191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1679191783Srmacklem			    }
1680191783Srmacklem			} else if (nap != NULL) {
1681191783Srmacklem				nap->na_mtime = temptime;
1682191783Srmacklem			}
1683191783Srmacklem			attrsum += NFSX_V4TIME;
1684191783Srmacklem			break;
1685191783Srmacklem		case NFSATTRBIT_TIMEMODIFYSET:
1686191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1687191783Srmacklem			attrsum += NFSX_UNSIGNED;
1688191783Srmacklem			i = fxdr_unsigned(int, *tl);
1689191783Srmacklem			if (i == NFSV4SATTRTIME_TOCLIENT) {
1690191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1691191783Srmacklem				attrsum += NFSX_V4TIME;
1692191783Srmacklem			}
1693191783Srmacklem			if (compare && !(*retcmpp))
1694191783Srmacklem				*retcmpp = NFSERR_INVAL;
1695191783Srmacklem			break;
1696191783Srmacklem		case NFSATTRBIT_MOUNTEDONFILEID:
1697191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1698191783Srmacklem			thyp = fxdr_hyper(tl);
1699191783Srmacklem			if (compare) {
1700191783Srmacklem			    if (!(*retcmpp)) {
1701191783Srmacklem				if (*tl++) {
1702191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1703191783Srmacklem				} else {
1704191783Srmacklem					if (!vp || !nfsrv_atroot(vp, &fid))
1705191783Srmacklem						fid = nap->na_fileid;
1706191783Srmacklem					if ((u_int64_t)fid != thyp)
1707191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1708191783Srmacklem				}
1709191783Srmacklem			    }
1710191783Srmacklem			} else if (nap != NULL) {
1711191783Srmacklem			    if (*tl++)
1712191783Srmacklem				printf("NFSv4 mounted on fileid > 32bits\n");
1713191783Srmacklem			    nap->na_mntonfileno = thyp;
1714191783Srmacklem			}
1715191783Srmacklem			attrsum += NFSX_HYPER;
1716191783Srmacklem			break;
1717191783Srmacklem		default:
1718191783Srmacklem			printf("EEK! nfsv4_loadattr unknown attr=%d\n",
1719191783Srmacklem				bitpos);
1720191783Srmacklem			if (compare && !(*retcmpp))
1721191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1722191783Srmacklem			/*
1723191783Srmacklem			 * and get out of the loop, since we can't parse
1724191783Srmacklem			 * the unknown attrbute data.
1725191783Srmacklem			 */
1726191783Srmacklem			bitpos = NFSATTRBIT_MAX;
1727191783Srmacklem			break;
1728191783Srmacklem		};
1729191783Srmacklem	}
1730191783Srmacklem
1731191783Srmacklem	/*
1732191783Srmacklem	 * some clients pad the attrlist, so we need to skip over the
1733191783Srmacklem	 * padding.
1734191783Srmacklem	 */
1735191783Srmacklem	if (attrsum > attrsize) {
1736191783Srmacklem		error = NFSERR_BADXDR;
1737191783Srmacklem	} else {
1738191783Srmacklem		attrsize = NFSM_RNDUP(attrsize);
1739191783Srmacklem		if (attrsum < attrsize)
1740191783Srmacklem			error = nfsm_advance(nd, attrsize - attrsum, -1);
1741191783Srmacklem	}
1742191783Srmacklemnfsmout:
1743224086Szack	NFSEXITCODE2(error, nd);
1744191783Srmacklem	return (error);
1745191783Srmacklem}
1746191783Srmacklem
1747191783Srmacklem/*
1748191783Srmacklem * Implement sleep locks for newnfs. The nfslock_usecnt allows for a
1749191783Srmacklem * shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
1750191783Srmacklem * The first argument is a pointer to an nfsv4lock structure.
1751191783Srmacklem * The second argument is 1 iff a blocking lock is wanted.
1752191783Srmacklem * If this argument is 0, the call waits until no thread either wants nor
1753191783Srmacklem * holds an exclusive lock.
1754191783Srmacklem * It returns 1 if the lock was acquired, 0 otherwise.
1755191783Srmacklem * If several processes call this function concurrently wanting the exclusive
1756191783Srmacklem * lock, one will get the lock and the rest will return without getting the
1757191783Srmacklem * lock. (If the caller must have the lock, it simply calls this function in a
1758191783Srmacklem *  loop until the function returns 1 to indicate the lock was acquired.)
1759191783Srmacklem * Any usecnt must be decremented by calling nfsv4_relref() before
1760191783Srmacklem * calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
1761191783Srmacklem * be called in a loop.
1762222389Srmacklem * The isleptp argument is set to indicate if the call slept, iff not NULL
1763222389Srmacklem * and the mp argument indicates to check for a forced dismount, iff not
1764222389Srmacklem * NULL.
1765191783Srmacklem */
1766191783SrmacklemAPPLESTATIC int
1767191783Srmacklemnfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
1768222389Srmacklem    void *mutex, struct mount *mp)
1769191783Srmacklem{
1770191783Srmacklem
1771191783Srmacklem	if (isleptp)
1772191783Srmacklem		*isleptp = 0;
1773191783Srmacklem	/*
1774191783Srmacklem	 * If a lock is wanted, loop around until the lock is acquired by
1775191783Srmacklem	 * someone and then released. If I want the lock, try to acquire it.
1776191783Srmacklem	 * For a lock to be issued, no lock must be in force and the usecnt
1777191783Srmacklem	 * must be zero.
1778191783Srmacklem	 */
1779191783Srmacklem	if (iwantlock) {
1780191783Srmacklem	    if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1781191783Srmacklem		lp->nfslock_usecnt == 0) {
1782191783Srmacklem		lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1783191783Srmacklem		lp->nfslock_lock |= NFSV4LOCK_LOCK;
1784191783Srmacklem		return (1);
1785191783Srmacklem	    }
1786191783Srmacklem	    lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
1787191783Srmacklem	}
1788191783Srmacklem	while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
1789222389Srmacklem		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1790222389Srmacklem			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1791222389Srmacklem			return (0);
1792222389Srmacklem		}
1793191783Srmacklem		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1794191783Srmacklem		if (isleptp)
1795191783Srmacklem			*isleptp = 1;
1796191783Srmacklem		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1797191783Srmacklem		    PZERO - 1, "nfsv4lck", NULL);
1798191783Srmacklem		if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1799191783Srmacklem		    lp->nfslock_usecnt == 0) {
1800191783Srmacklem			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1801191783Srmacklem			lp->nfslock_lock |= NFSV4LOCK_LOCK;
1802191783Srmacklem			return (1);
1803191783Srmacklem		}
1804191783Srmacklem	}
1805191783Srmacklem	return (0);
1806191783Srmacklem}
1807191783Srmacklem
1808191783Srmacklem/*
1809191783Srmacklem * Release the lock acquired by nfsv4_lock().
1810191783Srmacklem * The second argument is set to 1 to indicate the nfslock_usecnt should be
1811191783Srmacklem * incremented, as well.
1812191783Srmacklem */
1813191783SrmacklemAPPLESTATIC void
1814191783Srmacklemnfsv4_unlock(struct nfsv4lock *lp, int incref)
1815191783Srmacklem{
1816191783Srmacklem
1817191783Srmacklem	lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
1818191783Srmacklem	if (incref)
1819191783Srmacklem		lp->nfslock_usecnt++;
1820191783Srmacklem	nfsv4_wanted(lp);
1821191783Srmacklem}
1822191783Srmacklem
1823191783Srmacklem/*
1824191783Srmacklem * Release a reference cnt.
1825191783Srmacklem */
1826191783SrmacklemAPPLESTATIC void
1827191783Srmacklemnfsv4_relref(struct nfsv4lock *lp)
1828191783Srmacklem{
1829191783Srmacklem
1830191783Srmacklem	if (lp->nfslock_usecnt <= 0)
1831191783Srmacklem		panic("nfsv4root ref cnt");
1832191783Srmacklem	lp->nfslock_usecnt--;
1833191783Srmacklem	if (lp->nfslock_usecnt == 0)
1834191783Srmacklem		nfsv4_wanted(lp);
1835191783Srmacklem}
1836191783Srmacklem
1837191783Srmacklem/*
1838191783Srmacklem * Get a reference cnt.
1839191783Srmacklem * This function will wait for any exclusive lock to be released, but will
1840191783Srmacklem * not wait for threads that want the exclusive lock. If priority needs
1841191783Srmacklem * to be given to threads that need the exclusive lock, a call to nfsv4_lock()
1842191783Srmacklem * with the 2nd argument == 0 should be done before calling nfsv4_getref().
1843222389Srmacklem * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and
1844222389Srmacklem * return without getting a refcnt for that case.
1845191783Srmacklem */
1846191783SrmacklemAPPLESTATIC void
1847222389Srmacklemnfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex,
1848222389Srmacklem    struct mount *mp)
1849191783Srmacklem{
1850191783Srmacklem
1851191783Srmacklem	if (isleptp)
1852191783Srmacklem		*isleptp = 0;
1853191783Srmacklem
1854191783Srmacklem	/*
1855191783Srmacklem	 * Wait for a lock held.
1856191783Srmacklem	 */
1857191783Srmacklem	while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
1858222389Srmacklem		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1859222389Srmacklem			return;
1860191783Srmacklem		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1861191783Srmacklem		if (isleptp)
1862191783Srmacklem			*isleptp = 1;
1863191783Srmacklem		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1864191783Srmacklem		    PZERO - 1, "nfsv4lck", NULL);
1865191783Srmacklem	}
1866222389Srmacklem	if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1867222389Srmacklem		return;
1868191783Srmacklem
1869191783Srmacklem	lp->nfslock_usecnt++;
1870191783Srmacklem}
1871191783Srmacklem
1872191783Srmacklem/*
1873211951Srmacklem * Get a reference as above, but return failure instead of sleeping if
1874211951Srmacklem * an exclusive lock is held.
1875211951Srmacklem */
1876211951SrmacklemAPPLESTATIC int
1877211951Srmacklemnfsv4_getref_nonblock(struct nfsv4lock *lp)
1878211951Srmacklem{
1879211951Srmacklem
1880211951Srmacklem	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
1881211951Srmacklem		return (0);
1882211951Srmacklem
1883211951Srmacklem	lp->nfslock_usecnt++;
1884211951Srmacklem	return (1);
1885211951Srmacklem}
1886211951Srmacklem
1887211951Srmacklem/*
1888205941Srmacklem * Test for a lock. Return 1 if locked, 0 otherwise.
1889205941Srmacklem */
1890205941SrmacklemAPPLESTATIC int
1891205941Srmacklemnfsv4_testlock(struct nfsv4lock *lp)
1892205941Srmacklem{
1893205941Srmacklem
1894205941Srmacklem	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
1895205941Srmacklem	    lp->nfslock_usecnt == 0)
1896205941Srmacklem		return (0);
1897205941Srmacklem	return (1);
1898205941Srmacklem}
1899205941Srmacklem
1900205941Srmacklem/*
1901191783Srmacklem * Wake up anyone sleeping, waiting for this lock.
1902191783Srmacklem */
1903191783Srmacklemstatic void
1904191783Srmacklemnfsv4_wanted(struct nfsv4lock *lp)
1905191783Srmacklem{
1906191783Srmacklem
1907191783Srmacklem	if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
1908191783Srmacklem		lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
1909191783Srmacklem		wakeup((caddr_t)&lp->nfslock_lock);
1910191783Srmacklem	}
1911191783Srmacklem}
1912191783Srmacklem
1913191783Srmacklem/*
1914191783Srmacklem * Copy a string from an mbuf list into a character array.
1915191783Srmacklem * Return EBADRPC if there is an mbuf error,
1916191783Srmacklem * 0 otherwise.
1917191783Srmacklem */
1918191783SrmacklemAPPLESTATIC int
1919191783Srmacklemnfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
1920191783Srmacklem{
1921191783Srmacklem	char *cp;
1922191783Srmacklem	int xfer, len;
1923191783Srmacklem	mbuf_t mp;
1924191783Srmacklem	int rem, error = 0;
1925191783Srmacklem
1926191783Srmacklem	mp = nd->nd_md;
1927191783Srmacklem	cp = nd->nd_dpos;
1928191783Srmacklem	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - cp;
1929191783Srmacklem	rem = NFSM_RNDUP(siz) - siz;
1930191783Srmacklem	while (siz > 0) {
1931191783Srmacklem		if (len > siz)
1932191783Srmacklem			xfer = siz;
1933191783Srmacklem		else
1934191783Srmacklem			xfer = len;
1935191783Srmacklem		NFSBCOPY(cp, str, xfer);
1936191783Srmacklem		str += xfer;
1937191783Srmacklem		siz -= xfer;
1938191783Srmacklem		if (siz > 0) {
1939191783Srmacklem			mp = mbuf_next(mp);
1940224086Szack			if (mp == NULL) {
1941224086Szack				error = EBADRPC;
1942224086Szack				goto out;
1943224086Szack			}
1944191783Srmacklem			cp = NFSMTOD(mp, caddr_t);
1945191783Srmacklem			len = mbuf_len(mp);
1946191783Srmacklem		} else {
1947191783Srmacklem			cp += xfer;
1948191783Srmacklem			len -= xfer;
1949191783Srmacklem		}
1950191783Srmacklem	}
1951191783Srmacklem	*str = '\0';
1952191783Srmacklem	nd->nd_dpos = cp;
1953191783Srmacklem	nd->nd_md = mp;
1954191783Srmacklem	if (rem > 0) {
1955191783Srmacklem		if (len < rem)
1956191783Srmacklem			error = nfsm_advance(nd, rem, len);
1957191783Srmacklem		else
1958191783Srmacklem			nd->nd_dpos += rem;
1959191783Srmacklem	}
1960224086Szack
1961224086Szackout:
1962224086Szack	NFSEXITCODE2(error, nd);
1963191783Srmacklem	return (error);
1964191783Srmacklem}
1965191783Srmacklem
1966191783Srmacklem/*
1967191783Srmacklem * Fill in the attributes as marked by the bitmap (V4).
1968191783Srmacklem */
1969191783SrmacklemAPPLESTATIC int
1970220645Srmacklemnfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
1971220645Srmacklem    NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
1972220645Srmacklem    nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
1973220648Srmacklem    int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
1974191783Srmacklem{
1975191783Srmacklem	int bitpos, retnum = 0;
1976191783Srmacklem	u_int32_t *tl;
1977191783Srmacklem	int siz, prefixnum, error;
1978191783Srmacklem	u_char *cp, namestr[NFSV4_SMALLSTR];
1979191783Srmacklem	nfsattrbit_t attrbits, retbits;
1980191783Srmacklem	nfsattrbit_t *retbitp = &retbits;
1981191783Srmacklem	u_int32_t freenum, *retnump;
1982191783Srmacklem	u_int64_t uquad;
1983191783Srmacklem	struct statfs fs;
1984191783Srmacklem	struct nfsfsinfo fsinf;
1985191783Srmacklem	struct timespec temptime;
1986191783Srmacklem	NFSACL_T *aclp, *naclp = NULL;
1987191783Srmacklem#ifdef QUOTA
1988191783Srmacklem	struct dqblk dqb;
1989191783Srmacklem	uid_t savuid;
1990191783Srmacklem#endif
1991191783Srmacklem
1992191783Srmacklem	/*
1993191783Srmacklem	 * First, set the bits that can be filled and get fsinfo.
1994191783Srmacklem	 */
1995191783Srmacklem	NFSSET_ATTRBIT(retbitp, attrbitp);
1996260172Srmacklem	/*
1997260172Srmacklem	 * If both p and cred are NULL, it is a client side setattr call.
1998260172Srmacklem	 * If both p and cred are not NULL, it is a server side reply call.
1999260172Srmacklem	 * If p is not NULL and cred is NULL, it is a client side callback
2000260172Srmacklem	 * reply call.
2001260172Srmacklem	 */
2002191783Srmacklem	if (p == NULL && cred == NULL) {
2003191783Srmacklem		NFSCLRNOTSETABLE_ATTRBIT(retbitp);
2004191783Srmacklem		aclp = saclp;
2005191783Srmacklem	} else {
2006191783Srmacklem		NFSCLRNOTFILLABLE_ATTRBIT(retbitp);
2007192861Srmacklem		naclp = acl_alloc(M_WAITOK);
2008191783Srmacklem		aclp = naclp;
2009191783Srmacklem	}
2010191783Srmacklem	nfsvno_getfs(&fsinf, isdgram);
2011191783Srmacklem#ifndef APPLE
2012191783Srmacklem	/*
2013191783Srmacklem	 * Get the VFS_STATFS(), since some attributes need them.
2014191783Srmacklem	 */
2015191783Srmacklem	if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
2016220645Srmacklem		error = VFS_STATFS(mp, &fs);
2017191783Srmacklem		if (error != 0) {
2018191783Srmacklem			if (reterr) {
2019191783Srmacklem				nd->nd_repstat = NFSERR_ACCES;
2020191783Srmacklem				return (0);
2021191783Srmacklem			}
2022191783Srmacklem			NFSCLRSTATFS_ATTRBIT(retbitp);
2023191783Srmacklem		}
2024191783Srmacklem	}
2025191783Srmacklem#endif
2026191783Srmacklem
2027191783Srmacklem	/*
2028191783Srmacklem	 * And the NFSv4 ACL...
2029191783Srmacklem	 */
2030200069Strasz	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
2031200069Strasz	    (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2032220648Srmacklem		supports_nfsv4acls == 0))) {
2033191783Srmacklem		NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
2034191783Srmacklem	}
2035191783Srmacklem	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
2036191783Srmacklem		if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2037220648Srmacklem		    supports_nfsv4acls == 0)) {
2038191783Srmacklem			NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2039191783Srmacklem		} else if (naclp != NULL) {
2040224081Szack			if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
2041217535Srmacklem				error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
2042216700Srmacklem				if (error == 0)
2043216700Srmacklem					error = VOP_GETACL(vp, ACL_TYPE_NFS4,
2044216700Srmacklem					    naclp, cred, p);
2045224082Szack				NFSVOPUNLOCK(vp, 0);
2046216700Srmacklem			} else
2047216700Srmacklem				error = NFSERR_PERM;
2048191783Srmacklem			if (error != 0) {
2049191783Srmacklem				if (reterr) {
2050191783Srmacklem					nd->nd_repstat = NFSERR_ACCES;
2051191783Srmacklem					return (0);
2052191783Srmacklem				}
2053191783Srmacklem				NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2054191783Srmacklem			}
2055191783Srmacklem		}
2056191783Srmacklem	}
2057191783Srmacklem	/*
2058191783Srmacklem	 * Put out the attribute bitmap for the ones being filled in
2059191783Srmacklem	 * and get the field for the number of attributes returned.
2060191783Srmacklem	 */
2061191783Srmacklem	prefixnum = nfsrv_putattrbit(nd, retbitp);
2062191783Srmacklem	NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
2063191783Srmacklem	prefixnum += NFSX_UNSIGNED;
2064191783Srmacklem
2065191783Srmacklem	/*
2066191783Srmacklem	 * Now, loop around filling in the attributes for each bit set.
2067191783Srmacklem	 */
2068191783Srmacklem	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
2069191783Srmacklem	    if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
2070191783Srmacklem		switch (bitpos) {
2071191783Srmacklem		case NFSATTRBIT_SUPPORTEDATTRS:
2072191783Srmacklem			NFSSETSUPP_ATTRBIT(&attrbits);
2073191783Srmacklem			if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
2074220648Srmacklem			    && supports_nfsv4acls == 0)) {
2075191783Srmacklem			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
2076191783Srmacklem			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
2077191783Srmacklem			}
2078191783Srmacklem			retnum += nfsrv_putattrbit(nd, &attrbits);
2079191783Srmacklem			break;
2080191783Srmacklem		case NFSATTRBIT_TYPE:
2081191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2082191783Srmacklem			*tl = vtonfsv34_type(vap->va_type);
2083191783Srmacklem			retnum += NFSX_UNSIGNED;
2084191783Srmacklem			break;
2085191783Srmacklem		case NFSATTRBIT_FHEXPIRETYPE:
2086191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2087191783Srmacklem			*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
2088191783Srmacklem			retnum += NFSX_UNSIGNED;
2089191783Srmacklem			break;
2090191783Srmacklem		case NFSATTRBIT_CHANGE:
2091191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2092191783Srmacklem			txdr_hyper(vap->va_filerev, tl);
2093191783Srmacklem			retnum += NFSX_HYPER;
2094191783Srmacklem			break;
2095191783Srmacklem		case NFSATTRBIT_SIZE:
2096191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2097191783Srmacklem			txdr_hyper(vap->va_size, tl);
2098191783Srmacklem			retnum += NFSX_HYPER;
2099191783Srmacklem			break;
2100191783Srmacklem		case NFSATTRBIT_LINKSUPPORT:
2101191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2102191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_LINK)
2103191783Srmacklem				*tl = newnfs_true;
2104191783Srmacklem			else
2105191783Srmacklem				*tl = newnfs_false;
2106191783Srmacklem			retnum += NFSX_UNSIGNED;
2107191783Srmacklem			break;
2108191783Srmacklem		case NFSATTRBIT_SYMLINKSUPPORT:
2109191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2110191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
2111191783Srmacklem				*tl = newnfs_true;
2112191783Srmacklem			else
2113191783Srmacklem				*tl = newnfs_false;
2114191783Srmacklem			retnum += NFSX_UNSIGNED;
2115191783Srmacklem			break;
2116191783Srmacklem		case NFSATTRBIT_NAMEDATTR:
2117191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2118191783Srmacklem			*tl = newnfs_false;
2119191783Srmacklem			retnum += NFSX_UNSIGNED;
2120191783Srmacklem			break;
2121191783Srmacklem		case NFSATTRBIT_FSID:
2122191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
2123191783Srmacklem			*tl++ = 0;
2124220645Srmacklem			*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
2125191783Srmacklem			*tl++ = 0;
2126220645Srmacklem			*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
2127191783Srmacklem			retnum += NFSX_V4FSID;
2128191783Srmacklem			break;
2129191783Srmacklem		case NFSATTRBIT_UNIQUEHANDLES:
2130191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2131191783Srmacklem			*tl = newnfs_true;
2132191783Srmacklem			retnum += NFSX_UNSIGNED;
2133191783Srmacklem			break;
2134191783Srmacklem		case NFSATTRBIT_LEASETIME:
2135191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2136191783Srmacklem			*tl = txdr_unsigned(nfsrv_lease);
2137191783Srmacklem			retnum += NFSX_UNSIGNED;
2138191783Srmacklem			break;
2139191783Srmacklem		case NFSATTRBIT_RDATTRERROR:
2140191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2141191783Srmacklem			*tl = txdr_unsigned(rderror);
2142191783Srmacklem			retnum += NFSX_UNSIGNED;
2143191783Srmacklem			break;
2144191783Srmacklem		/*
2145191783Srmacklem		 * Recommended Attributes. (Only the supported ones.)
2146191783Srmacklem		 */
2147191783Srmacklem		case NFSATTRBIT_ACL:
2148191783Srmacklem			retnum += nfsrv_buildacl(nd, aclp, vnode_vtype(vp), p);
2149191783Srmacklem			break;
2150191783Srmacklem		case NFSATTRBIT_ACLSUPPORT:
2151191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2152191783Srmacklem			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
2153191783Srmacklem			retnum += NFSX_UNSIGNED;
2154191783Srmacklem			break;
2155191783Srmacklem		case NFSATTRBIT_CANSETTIME:
2156191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2157191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
2158191783Srmacklem				*tl = newnfs_true;
2159191783Srmacklem			else
2160191783Srmacklem				*tl = newnfs_false;
2161191783Srmacklem			retnum += NFSX_UNSIGNED;
2162191783Srmacklem			break;
2163191783Srmacklem		case NFSATTRBIT_CASEINSENSITIVE:
2164191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2165191783Srmacklem			*tl = newnfs_false;
2166191783Srmacklem			retnum += NFSX_UNSIGNED;
2167191783Srmacklem			break;
2168191783Srmacklem		case NFSATTRBIT_CASEPRESERVING:
2169191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2170191783Srmacklem			*tl = newnfs_true;
2171191783Srmacklem			retnum += NFSX_UNSIGNED;
2172191783Srmacklem			break;
2173191783Srmacklem		case NFSATTRBIT_CHOWNRESTRICTED:
2174191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2175224121Szack			*tl = newnfs_true;
2176191783Srmacklem			retnum += NFSX_UNSIGNED;
2177191783Srmacklem			break;
2178191783Srmacklem		case NFSATTRBIT_FILEHANDLE:
2179191783Srmacklem			retnum += nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
2180191783Srmacklem			break;
2181191783Srmacklem		case NFSATTRBIT_FILEID:
2182191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2183191783Srmacklem			*tl++ = 0;
2184191783Srmacklem			*tl = txdr_unsigned(vap->va_fileid);
2185191783Srmacklem			retnum += NFSX_HYPER;
2186191783Srmacklem			break;
2187191783Srmacklem		case NFSATTRBIT_FILESAVAIL:
2188191783Srmacklem			/*
2189191783Srmacklem			 * Check quota and use min(quota, f_ffree).
2190191783Srmacklem			 */
2191191783Srmacklem			freenum = fs.f_ffree;
2192191783Srmacklem#ifdef QUOTA
2193191783Srmacklem			/*
2194191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2195191783Srmacklem			 * equal p_ruid for non-root quota access, so
2196191783Srmacklem			 * we'll just make sure that's the case.
2197191783Srmacklem			 */
2198191783Srmacklem			savuid = p->p_cred->p_ruid;
2199191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2200220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2201191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2202191783Srmacklem			    freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
2203191783Srmacklem				freenum);
2204191783Srmacklem			p->p_cred->p_ruid = savuid;
2205191783Srmacklem#endif	/* QUOTA */
2206191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2207191783Srmacklem			*tl++ = 0;
2208191783Srmacklem			*tl = txdr_unsigned(freenum);
2209191783Srmacklem			retnum += NFSX_HYPER;
2210191783Srmacklem			break;
2211191783Srmacklem		case NFSATTRBIT_FILESFREE:
2212191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2213191783Srmacklem			*tl++ = 0;
2214191783Srmacklem			*tl = txdr_unsigned(fs.f_ffree);
2215191783Srmacklem			retnum += NFSX_HYPER;
2216191783Srmacklem			break;
2217191783Srmacklem		case NFSATTRBIT_FILESTOTAL:
2218191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2219191783Srmacklem			*tl++ = 0;
2220191783Srmacklem			*tl = txdr_unsigned(fs.f_files);
2221191783Srmacklem			retnum += NFSX_HYPER;
2222191783Srmacklem			break;
2223191783Srmacklem		case NFSATTRBIT_FSLOCATIONS:
2224191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2225191783Srmacklem			*tl++ = 0;
2226191783Srmacklem			*tl = 0;
2227191783Srmacklem			retnum += 2 * NFSX_UNSIGNED;
2228191783Srmacklem			break;
2229191783Srmacklem		case NFSATTRBIT_HOMOGENEOUS:
2230191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2231191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
2232191783Srmacklem				*tl = newnfs_true;
2233191783Srmacklem			else
2234191783Srmacklem				*tl = newnfs_false;
2235191783Srmacklem			retnum += NFSX_UNSIGNED;
2236191783Srmacklem			break;
2237191783Srmacklem		case NFSATTRBIT_MAXFILESIZE:
2238191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2239191783Srmacklem			uquad = NFSRV_MAXFILESIZE;
2240191783Srmacklem			txdr_hyper(uquad, tl);
2241191783Srmacklem			retnum += NFSX_HYPER;
2242191783Srmacklem			break;
2243191783Srmacklem		case NFSATTRBIT_MAXLINK:
2244191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2245191783Srmacklem			*tl = txdr_unsigned(LINK_MAX);
2246191783Srmacklem			retnum += NFSX_UNSIGNED;
2247191783Srmacklem			break;
2248191783Srmacklem		case NFSATTRBIT_MAXNAME:
2249191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2250191783Srmacklem			*tl = txdr_unsigned(NFS_MAXNAMLEN);
2251191783Srmacklem			retnum += NFSX_UNSIGNED;
2252191783Srmacklem			break;
2253191783Srmacklem		case NFSATTRBIT_MAXREAD:
2254191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2255191783Srmacklem			*tl++ = 0;
2256191783Srmacklem			*tl = txdr_unsigned(fsinf.fs_rtmax);
2257191783Srmacklem			retnum += NFSX_HYPER;
2258191783Srmacklem			break;
2259191783Srmacklem		case NFSATTRBIT_MAXWRITE:
2260191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2261191783Srmacklem			*tl++ = 0;
2262191783Srmacklem			*tl = txdr_unsigned(fsinf.fs_wtmax);
2263191783Srmacklem			retnum += NFSX_HYPER;
2264191783Srmacklem			break;
2265191783Srmacklem		case NFSATTRBIT_MODE:
2266191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2267191783Srmacklem			*tl = vtonfsv34_mode(vap->va_mode);
2268191783Srmacklem			retnum += NFSX_UNSIGNED;
2269191783Srmacklem			break;
2270191783Srmacklem		case NFSATTRBIT_NOTRUNC:
2271191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2272191783Srmacklem			*tl = newnfs_true;
2273191783Srmacklem			retnum += NFSX_UNSIGNED;
2274191783Srmacklem			break;
2275191783Srmacklem		case NFSATTRBIT_NUMLINKS:
2276191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2277191783Srmacklem			*tl = txdr_unsigned(vap->va_nlink);
2278191783Srmacklem			retnum += NFSX_UNSIGNED;
2279191783Srmacklem			break;
2280191783Srmacklem		case NFSATTRBIT_OWNER:
2281191783Srmacklem			cp = namestr;
2282191783Srmacklem			nfsv4_uidtostr(vap->va_uid, &cp, &siz, p);
2283191783Srmacklem			retnum += nfsm_strtom(nd, cp, siz);
2284191783Srmacklem			if (cp != namestr)
2285191783Srmacklem				free(cp, M_NFSSTRING);
2286191783Srmacklem			break;
2287191783Srmacklem		case NFSATTRBIT_OWNERGROUP:
2288191783Srmacklem			cp = namestr;
2289191783Srmacklem			nfsv4_gidtostr(vap->va_gid, &cp, &siz, p);
2290191783Srmacklem			retnum += nfsm_strtom(nd, cp, siz);
2291191783Srmacklem			if (cp != namestr)
2292191783Srmacklem				free(cp, M_NFSSTRING);
2293191783Srmacklem			break;
2294191783Srmacklem		case NFSATTRBIT_QUOTAHARD:
2295191783Srmacklem			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2296191783Srmacklem				freenum = fs.f_bfree;
2297191783Srmacklem			else
2298191783Srmacklem				freenum = fs.f_bavail;
2299191783Srmacklem#ifdef QUOTA
2300191783Srmacklem			/*
2301191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2302191783Srmacklem			 * equal p_ruid for non-root quota access, so
2303191783Srmacklem			 * we'll just make sure that's the case.
2304191783Srmacklem			 */
2305191783Srmacklem			savuid = p->p_cred->p_ruid;
2306191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2307220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2308191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2309191783Srmacklem			    freenum = min(dqb.dqb_bhardlimit, freenum);
2310191783Srmacklem			p->p_cred->p_ruid = savuid;
2311191783Srmacklem#endif	/* QUOTA */
2312191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2313191783Srmacklem			uquad = (u_int64_t)freenum;
2314191783Srmacklem			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2315191783Srmacklem			txdr_hyper(uquad, tl);
2316191783Srmacklem			retnum += NFSX_HYPER;
2317191783Srmacklem			break;
2318191783Srmacklem		case NFSATTRBIT_QUOTASOFT:
2319191783Srmacklem			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2320191783Srmacklem				freenum = fs.f_bfree;
2321191783Srmacklem			else
2322191783Srmacklem				freenum = fs.f_bavail;
2323191783Srmacklem#ifdef QUOTA
2324191783Srmacklem			/*
2325191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2326191783Srmacklem			 * equal p_ruid for non-root quota access, so
2327191783Srmacklem			 * we'll just make sure that's the case.
2328191783Srmacklem			 */
2329191783Srmacklem			savuid = p->p_cred->p_ruid;
2330191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2331220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2332191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2333191783Srmacklem			    freenum = min(dqb.dqb_bsoftlimit, freenum);
2334191783Srmacklem			p->p_cred->p_ruid = savuid;
2335191783Srmacklem#endif	/* QUOTA */
2336191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2337191783Srmacklem			uquad = (u_int64_t)freenum;
2338191783Srmacklem			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2339191783Srmacklem			txdr_hyper(uquad, tl);
2340191783Srmacklem			retnum += NFSX_HYPER;
2341191783Srmacklem			break;
2342191783Srmacklem		case NFSATTRBIT_QUOTAUSED:
2343191783Srmacklem			freenum = 0;
2344191783Srmacklem#ifdef QUOTA
2345191783Srmacklem			/*
2346191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2347191783Srmacklem			 * equal p_ruid for non-root quota access, so
2348191783Srmacklem			 * we'll just make sure that's the case.
2349191783Srmacklem			 */
2350191783Srmacklem			savuid = p->p_cred->p_ruid;
2351191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2352220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2353191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2354191783Srmacklem			    freenum = dqb.dqb_curblocks;
2355191783Srmacklem			p->p_cred->p_ruid = savuid;
2356191783Srmacklem#endif	/* QUOTA */
2357191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2358191783Srmacklem			uquad = (u_int64_t)freenum;
2359191783Srmacklem			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2360191783Srmacklem			txdr_hyper(uquad, tl);
2361191783Srmacklem			retnum += NFSX_HYPER;
2362191783Srmacklem			break;
2363191783Srmacklem		case NFSATTRBIT_RAWDEV:
2364191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
2365191783Srmacklem			*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
2366191783Srmacklem			*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
2367191783Srmacklem			retnum += NFSX_V4SPECDATA;
2368191783Srmacklem			break;
2369191783Srmacklem		case NFSATTRBIT_SPACEAVAIL:
2370191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2371191783Srmacklem			if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE, 0))
2372191783Srmacklem				uquad = (u_int64_t)fs.f_bfree;
2373191783Srmacklem			else
2374191783Srmacklem				uquad = (u_int64_t)fs.f_bavail;
2375191783Srmacklem			uquad *= fs.f_bsize;
2376191783Srmacklem			txdr_hyper(uquad, tl);
2377191783Srmacklem			retnum += NFSX_HYPER;
2378191783Srmacklem			break;
2379191783Srmacklem		case NFSATTRBIT_SPACEFREE:
2380191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2381191783Srmacklem			uquad = (u_int64_t)fs.f_bfree;
2382191783Srmacklem			uquad *= fs.f_bsize;
2383191783Srmacklem			txdr_hyper(uquad, tl);
2384191783Srmacklem			retnum += NFSX_HYPER;
2385191783Srmacklem			break;
2386191783Srmacklem		case NFSATTRBIT_SPACETOTAL:
2387191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2388191783Srmacklem			uquad = (u_int64_t)fs.f_blocks;
2389191783Srmacklem			uquad *= fs.f_bsize;
2390191783Srmacklem			txdr_hyper(uquad, tl);
2391191783Srmacklem			retnum += NFSX_HYPER;
2392191783Srmacklem			break;
2393191783Srmacklem		case NFSATTRBIT_SPACEUSED:
2394191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2395191783Srmacklem			txdr_hyper(vap->va_bytes, tl);
2396191783Srmacklem			retnum += NFSX_HYPER;
2397191783Srmacklem			break;
2398191783Srmacklem		case NFSATTRBIT_TIMEACCESS:
2399191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2400191783Srmacklem			txdr_nfsv4time(&vap->va_atime, tl);
2401191783Srmacklem			retnum += NFSX_V4TIME;
2402191783Srmacklem			break;
2403191783Srmacklem		case NFSATTRBIT_TIMEACCESSSET:
2404247502Sjhb			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2405191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2406191783Srmacklem				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2407191783Srmacklem				txdr_nfsv4time(&vap->va_atime, tl);
2408191783Srmacklem				retnum += NFSX_V4SETTIME;
2409191783Srmacklem			} else {
2410191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2411191783Srmacklem				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2412191783Srmacklem				retnum += NFSX_UNSIGNED;
2413191783Srmacklem			}
2414191783Srmacklem			break;
2415191783Srmacklem		case NFSATTRBIT_TIMEDELTA:
2416191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2417191783Srmacklem			temptime.tv_sec = 0;
2418191783Srmacklem			temptime.tv_nsec = 1000000000 / hz;
2419191783Srmacklem			txdr_nfsv4time(&temptime, tl);
2420191783Srmacklem			retnum += NFSX_V4TIME;
2421191783Srmacklem			break;
2422191783Srmacklem		case NFSATTRBIT_TIMEMETADATA:
2423191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2424191783Srmacklem			txdr_nfsv4time(&vap->va_ctime, tl);
2425191783Srmacklem			retnum += NFSX_V4TIME;
2426191783Srmacklem			break;
2427191783Srmacklem		case NFSATTRBIT_TIMEMODIFY:
2428191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2429191783Srmacklem			txdr_nfsv4time(&vap->va_mtime, tl);
2430191783Srmacklem			retnum += NFSX_V4TIME;
2431191783Srmacklem			break;
2432191783Srmacklem		case NFSATTRBIT_TIMEMODIFYSET:
2433247502Sjhb			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2434191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2435191783Srmacklem				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2436191783Srmacklem				txdr_nfsv4time(&vap->va_mtime, tl);
2437191783Srmacklem				retnum += NFSX_V4SETTIME;
2438191783Srmacklem			} else {
2439191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2440191783Srmacklem				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2441191783Srmacklem				retnum += NFSX_UNSIGNED;
2442191783Srmacklem			}
2443191783Srmacklem			break;
2444191783Srmacklem		case NFSATTRBIT_MOUNTEDONFILEID:
2445191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2446220645Srmacklem			if (at_root != 0)
2447220645Srmacklem				uquad = mounted_on_fileno;
2448191783Srmacklem			else
2449220645Srmacklem				uquad = (u_int64_t)vap->va_fileid;
2450220645Srmacklem			txdr_hyper(uquad, tl);
2451191783Srmacklem			retnum += NFSX_HYPER;
2452191783Srmacklem			break;
2453191783Srmacklem		default:
2454191783Srmacklem			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
2455191783Srmacklem		};
2456191783Srmacklem	    }
2457191783Srmacklem	}
2458191783Srmacklem	if (naclp != NULL)
2459191783Srmacklem		acl_free(naclp);
2460191783Srmacklem	*retnump = txdr_unsigned(retnum);
2461191783Srmacklem	return (retnum + prefixnum);
2462191783Srmacklem}
2463191783Srmacklem
2464191783Srmacklem/*
2465191783Srmacklem * Put the attribute bits onto an mbuf list.
2466191783Srmacklem * Return the number of bytes of output generated.
2467191783Srmacklem */
2468191783SrmacklemAPPLESTATIC int
2469191783Srmacklemnfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
2470191783Srmacklem{
2471191783Srmacklem	u_int32_t *tl;
2472191783Srmacklem	int cnt, i, bytesize;
2473191783Srmacklem
2474191783Srmacklem	for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
2475191783Srmacklem		if (attrbitp->bits[cnt - 1])
2476191783Srmacklem			break;
2477191783Srmacklem	bytesize = (cnt + 1) * NFSX_UNSIGNED;
2478191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, bytesize);
2479191783Srmacklem	*tl++ = txdr_unsigned(cnt);
2480191783Srmacklem	for (i = 0; i < cnt; i++)
2481191783Srmacklem		*tl++ = txdr_unsigned(attrbitp->bits[i]);
2482191783Srmacklem	return (bytesize);
2483191783Srmacklem}
2484191783Srmacklem
2485191783Srmacklem/*
2486191783Srmacklem * Convert a uid to a string.
2487191783Srmacklem * If the lookup fails, just output the digits.
2488191783Srmacklem * uid - the user id
2489191783Srmacklem * cpp - points to a buffer of size NFSV4_SMALLSTR
2490191783Srmacklem *       (malloc a larger one, as required)
2491191783Srmacklem * retlenp - pointer to length to be returned
2492191783Srmacklem */
2493191783SrmacklemAPPLESTATIC void
2494191783Srmacklemnfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2495191783Srmacklem{
2496191783Srmacklem	int i;
2497191783Srmacklem	struct nfsusrgrp *usrp;
2498191783Srmacklem	u_char *cp = *cpp;
2499191783Srmacklem	uid_t tmp;
2500191783Srmacklem	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2501191783Srmacklem
2502191783Srmacklem	cnt = 0;
2503191783Srmacklemtryagain:
2504191783Srmacklem	NFSLOCKNAMEID();
2505191783Srmacklem	if (nfsrv_dnsname) {
2506191783Srmacklem		/*
2507191783Srmacklem		 * Always map nfsrv_defaultuid to "nobody".
2508191783Srmacklem		 */
2509191783Srmacklem		if (uid == nfsrv_defaultuid) {
2510191783Srmacklem			i = nfsrv_dnsnamelen + 7;
2511191783Srmacklem			if (i > len) {
2512191783Srmacklem				NFSUNLOCKNAMEID();
2513191783Srmacklem				if (len > NFSV4_SMALLSTR)
2514191783Srmacklem					free(cp, M_NFSSTRING);
2515191783Srmacklem				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2516191783Srmacklem				*cpp = cp;
2517191783Srmacklem				len = i;
2518191783Srmacklem				goto tryagain;
2519191783Srmacklem			}
2520191783Srmacklem			*retlenp = i;
2521191783Srmacklem			NFSBCOPY("nobody@", cp, 7);
2522191783Srmacklem			cp += 7;
2523191783Srmacklem			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2524191783Srmacklem			NFSUNLOCKNAMEID();
2525191783Srmacklem			return;
2526191783Srmacklem		}
2527191783Srmacklem		hasampersand = 0;
2528191783Srmacklem		LIST_FOREACH(usrp, NFSUSERHASH(uid), lug_numhash) {
2529191783Srmacklem			if (usrp->lug_uid == uid) {
2530191783Srmacklem				if (usrp->lug_expiry < NFSD_MONOSEC)
2531191783Srmacklem					break;
2532191783Srmacklem				/*
2533191783Srmacklem				 * If the name doesn't already have an '@'
2534191783Srmacklem				 * in it, append @domainname to it.
2535191783Srmacklem				 */
2536191783Srmacklem				for (i = 0; i < usrp->lug_namelen; i++) {
2537191783Srmacklem					if (usrp->lug_name[i] == '@') {
2538191783Srmacklem						hasampersand = 1;
2539191783Srmacklem						break;
2540191783Srmacklem					}
2541191783Srmacklem				}
2542191783Srmacklem				if (hasampersand)
2543191783Srmacklem					i = usrp->lug_namelen;
2544191783Srmacklem				else
2545191783Srmacklem					i = usrp->lug_namelen +
2546191783Srmacklem					    nfsrv_dnsnamelen + 1;
2547191783Srmacklem				if (i > len) {
2548191783Srmacklem					NFSUNLOCKNAMEID();
2549191783Srmacklem					if (len > NFSV4_SMALLSTR)
2550191783Srmacklem						free(cp, M_NFSSTRING);
2551191783Srmacklem					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2552191783Srmacklem					*cpp = cp;
2553191783Srmacklem					len = i;
2554191783Srmacklem					goto tryagain;
2555191783Srmacklem				}
2556191783Srmacklem				*retlenp = i;
2557191783Srmacklem				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2558191783Srmacklem				if (!hasampersand) {
2559191783Srmacklem					cp += usrp->lug_namelen;
2560191783Srmacklem					*cp++ = '@';
2561191783Srmacklem					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2562191783Srmacklem				}
2563191783Srmacklem				TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2564191783Srmacklem				TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2565191783Srmacklem				NFSUNLOCKNAMEID();
2566191783Srmacklem				return;
2567191783Srmacklem			}
2568191783Srmacklem		}
2569191783Srmacklem		NFSUNLOCKNAMEID();
2570191783Srmacklem		cnt++;
2571191783Srmacklem		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2572191783Srmacklem		    NULL, p);
2573191783Srmacklem		if (ret == 0 && cnt < 2)
2574191783Srmacklem			goto tryagain;
2575191783Srmacklem	} else {
2576191783Srmacklem		NFSUNLOCKNAMEID();
2577191783Srmacklem	}
2578191783Srmacklem
2579191783Srmacklem	/*
2580191783Srmacklem	 * No match, just return a string of digits.
2581191783Srmacklem	 */
2582191783Srmacklem	tmp = uid;
2583191783Srmacklem	i = 0;
2584191783Srmacklem	while (tmp || i == 0) {
2585191783Srmacklem		tmp /= 10;
2586191783Srmacklem		i++;
2587191783Srmacklem	}
2588191783Srmacklem	len = (i > len) ? len : i;
2589191783Srmacklem	*retlenp = len;
2590191783Srmacklem	cp += (len - 1);
2591191783Srmacklem	tmp = uid;
2592191783Srmacklem	for (i = 0; i < len; i++) {
2593191783Srmacklem		*cp-- = '0' + (tmp % 10);
2594191783Srmacklem		tmp /= 10;
2595191783Srmacklem	}
2596191783Srmacklem	return;
2597191783Srmacklem}
2598191783Srmacklem
2599191783Srmacklem/*
2600191783Srmacklem * Convert a string to a uid.
2601191783Srmacklem * If no conversion is possible return NFSERR_BADOWNER, otherwise
2602191783Srmacklem * return 0.
2603241194Srmacklem * If this is called from a client side mount using AUTH_SYS and the
2604241194Srmacklem * string is made up entirely of digits, just convert the string to
2605241194Srmacklem * a number.
2606191783Srmacklem */
2607191783SrmacklemAPPLESTATIC int
2608241194Srmacklemnfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
2609241194Srmacklem    NFSPROC_T *p)
2610191783Srmacklem{
2611191783Srmacklem	int i;
2612241194Srmacklem	char *cp, *endstr, *str0;
2613191783Srmacklem	struct nfsusrgrp *usrp;
2614191783Srmacklem	int cnt, ret;
2615224086Szack	int error = 0;
2616241194Srmacklem	uid_t tuid;
2617191783Srmacklem
2618224086Szack	if (len == 0) {
2619224086Szack		error = NFSERR_BADOWNER;
2620224086Szack		goto out;
2621224086Szack	}
2622241194Srmacklem	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2623241194Srmacklem	str0 = str;
2624241194Srmacklem	tuid = (uid_t)strtoul(str0, &endstr, 10);
2625265724Srmacklem	if ((endstr - str0) == len) {
2626265724Srmacklem		/* A numeric string. */
2627265724Srmacklem		if ((nd->nd_flag & ND_KERBV) == 0 &&
2628265724Srmacklem		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2629265724Srmacklem		      nfsd_enable_stringtouid != 0))
2630265724Srmacklem			*uidp = tuid;
2631265724Srmacklem		else
2632265724Srmacklem			error = NFSERR_BADOWNER;
2633241194Srmacklem		goto out;
2634241194Srmacklem	}
2635191783Srmacklem	/*
2636191783Srmacklem	 * Look for an '@'.
2637191783Srmacklem	 */
2638241194Srmacklem	cp = strchr(str0, '@');
2639241194Srmacklem	if (cp != NULL)
2640241194Srmacklem		i = (int)(cp++ - str0);
2641241194Srmacklem	else
2642241194Srmacklem		i = len;
2643191783Srmacklem
2644191783Srmacklem	cnt = 0;
2645191783Srmacklemtryagain:
2646191783Srmacklem	NFSLOCKNAMEID();
2647191783Srmacklem	/*
2648191783Srmacklem	 * If an '@' is found and the domain name matches, search for the name
2649191783Srmacklem	 * with dns stripped off.
2650191783Srmacklem	 * Mixed case alpahbetics will match for the domain name, but all
2651191783Srmacklem	 * upper case will not.
2652191783Srmacklem	 */
2653191783Srmacklem	if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
2654191783Srmacklem	    (len - 1 - i) == nfsrv_dnsnamelen &&
2655191783Srmacklem	    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2656191783Srmacklem		len -= (nfsrv_dnsnamelen + 1);
2657191783Srmacklem		*(cp - 1) = '\0';
2658191783Srmacklem	}
2659191783Srmacklem
2660191783Srmacklem	/*
2661191783Srmacklem	 * Check for the special case of "nobody".
2662191783Srmacklem	 */
2663191783Srmacklem	if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
2664191783Srmacklem		*uidp = nfsrv_defaultuid;
2665191783Srmacklem		NFSUNLOCKNAMEID();
2666224086Szack		error = 0;
2667224086Szack		goto out;
2668191783Srmacklem	}
2669191783Srmacklem
2670191783Srmacklem	LIST_FOREACH(usrp, NFSUSERNAMEHASH(str, len), lug_namehash) {
2671191783Srmacklem		if (usrp->lug_namelen == len &&
2672191783Srmacklem		    !NFSBCMP(usrp->lug_name, str, len)) {
2673191783Srmacklem			if (usrp->lug_expiry < NFSD_MONOSEC)
2674191783Srmacklem				break;
2675191783Srmacklem			*uidp = usrp->lug_uid;
2676191783Srmacklem			TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2677191783Srmacklem			TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2678191783Srmacklem			NFSUNLOCKNAMEID();
2679224086Szack			error = 0;
2680224086Szack			goto out;
2681191783Srmacklem		}
2682191783Srmacklem	}
2683191783Srmacklem	NFSUNLOCKNAMEID();
2684191783Srmacklem	cnt++;
2685191783Srmacklem	ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
2686191783Srmacklem	    str, p);
2687191783Srmacklem	if (ret == 0 && cnt < 2)
2688191783Srmacklem		goto tryagain;
2689224086Szack	error = NFSERR_BADOWNER;
2690224086Szack
2691224086Szackout:
2692224086Szack	NFSEXITCODE(error);
2693224086Szack	return (error);
2694191783Srmacklem}
2695191783Srmacklem
2696191783Srmacklem/*
2697191783Srmacklem * Convert a gid to a string.
2698191783Srmacklem * gid - the group id
2699191783Srmacklem * cpp - points to a buffer of size NFSV4_SMALLSTR
2700191783Srmacklem *       (malloc a larger one, as required)
2701191783Srmacklem * retlenp - pointer to length to be returned
2702191783Srmacklem */
2703191783SrmacklemAPPLESTATIC void
2704191783Srmacklemnfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2705191783Srmacklem{
2706191783Srmacklem	int i;
2707191783Srmacklem	struct nfsusrgrp *usrp;
2708191783Srmacklem	u_char *cp = *cpp;
2709191783Srmacklem	gid_t tmp;
2710191783Srmacklem	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2711191783Srmacklem
2712191783Srmacklem	cnt = 0;
2713191783Srmacklemtryagain:
2714191783Srmacklem	NFSLOCKNAMEID();
2715191783Srmacklem	if (nfsrv_dnsname) {
2716191783Srmacklem		/*
2717191783Srmacklem		 * Always map nfsrv_defaultgid to "nogroup".
2718191783Srmacklem		 */
2719191783Srmacklem		if (gid == nfsrv_defaultgid) {
2720191783Srmacklem			i = nfsrv_dnsnamelen + 8;
2721191783Srmacklem			if (i > len) {
2722191783Srmacklem				NFSUNLOCKNAMEID();
2723191783Srmacklem				if (len > NFSV4_SMALLSTR)
2724191783Srmacklem					free(cp, M_NFSSTRING);
2725191783Srmacklem				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2726191783Srmacklem				*cpp = cp;
2727191783Srmacklem				len = i;
2728191783Srmacklem				goto tryagain;
2729191783Srmacklem			}
2730191783Srmacklem			*retlenp = i;
2731191783Srmacklem			NFSBCOPY("nogroup@", cp, 8);
2732191783Srmacklem			cp += 8;
2733191783Srmacklem			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2734191783Srmacklem			NFSUNLOCKNAMEID();
2735191783Srmacklem			return;
2736191783Srmacklem		}
2737191783Srmacklem		hasampersand = 0;
2738191783Srmacklem		LIST_FOREACH(usrp, NFSGROUPHASH(gid), lug_numhash) {
2739191783Srmacklem			if (usrp->lug_gid == gid) {
2740191783Srmacklem				if (usrp->lug_expiry < NFSD_MONOSEC)
2741191783Srmacklem					break;
2742191783Srmacklem				/*
2743191783Srmacklem				 * If the name doesn't already have an '@'
2744191783Srmacklem				 * in it, append @domainname to it.
2745191783Srmacklem				 */
2746191783Srmacklem				for (i = 0; i < usrp->lug_namelen; i++) {
2747191783Srmacklem					if (usrp->lug_name[i] == '@') {
2748191783Srmacklem						hasampersand = 1;
2749191783Srmacklem						break;
2750191783Srmacklem					}
2751191783Srmacklem				}
2752191783Srmacklem				if (hasampersand)
2753191783Srmacklem					i = usrp->lug_namelen;
2754191783Srmacklem				else
2755191783Srmacklem					i = usrp->lug_namelen +
2756191783Srmacklem					    nfsrv_dnsnamelen + 1;
2757191783Srmacklem				if (i > len) {
2758191783Srmacklem					NFSUNLOCKNAMEID();
2759191783Srmacklem					if (len > NFSV4_SMALLSTR)
2760191783Srmacklem						free(cp, M_NFSSTRING);
2761191783Srmacklem					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2762191783Srmacklem					*cpp = cp;
2763191783Srmacklem					len = i;
2764191783Srmacklem					goto tryagain;
2765191783Srmacklem				}
2766191783Srmacklem				*retlenp = i;
2767191783Srmacklem				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2768191783Srmacklem				if (!hasampersand) {
2769191783Srmacklem					cp += usrp->lug_namelen;
2770191783Srmacklem					*cp++ = '@';
2771191783Srmacklem					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2772191783Srmacklem				}
2773191783Srmacklem				TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2774191783Srmacklem				TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2775191783Srmacklem				NFSUNLOCKNAMEID();
2776191783Srmacklem				return;
2777191783Srmacklem			}
2778191783Srmacklem		}
2779191783Srmacklem		NFSUNLOCKNAMEID();
2780191783Srmacklem		cnt++;
2781191783Srmacklem		ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
2782191783Srmacklem		    NULL, p);
2783191783Srmacklem		if (ret == 0 && cnt < 2)
2784191783Srmacklem			goto tryagain;
2785191783Srmacklem	} else {
2786191783Srmacklem		NFSUNLOCKNAMEID();
2787191783Srmacklem	}
2788191783Srmacklem
2789191783Srmacklem	/*
2790191783Srmacklem	 * No match, just return a string of digits.
2791191783Srmacklem	 */
2792191783Srmacklem	tmp = gid;
2793191783Srmacklem	i = 0;
2794191783Srmacklem	while (tmp || i == 0) {
2795191783Srmacklem		tmp /= 10;
2796191783Srmacklem		i++;
2797191783Srmacklem	}
2798191783Srmacklem	len = (i > len) ? len : i;
2799191783Srmacklem	*retlenp = len;
2800191783Srmacklem	cp += (len - 1);
2801191783Srmacklem	tmp = gid;
2802191783Srmacklem	for (i = 0; i < len; i++) {
2803191783Srmacklem		*cp-- = '0' + (tmp % 10);
2804191783Srmacklem		tmp /= 10;
2805191783Srmacklem	}
2806191783Srmacklem	return;
2807191783Srmacklem}
2808191783Srmacklem
2809191783Srmacklem/*
2810191783Srmacklem * Convert a string to a gid.
2811241194Srmacklem * If no conversion is possible return NFSERR_BADOWNER, otherwise
2812241194Srmacklem * return 0.
2813241194Srmacklem * If this is called from a client side mount using AUTH_SYS and the
2814241194Srmacklem * string is made up entirely of digits, just convert the string to
2815241194Srmacklem * a number.
2816191783Srmacklem */
2817191783SrmacklemAPPLESTATIC int
2818241194Srmacklemnfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
2819241194Srmacklem    NFSPROC_T *p)
2820191783Srmacklem{
2821191783Srmacklem	int i;
2822241194Srmacklem	char *cp, *endstr, *str0;
2823191783Srmacklem	struct nfsusrgrp *usrp;
2824191783Srmacklem	int cnt, ret;
2825224086Szack	int error = 0;
2826241194Srmacklem	gid_t tgid;
2827191783Srmacklem
2828224086Szack	if (len == 0) {
2829224086Szack		error =  NFSERR_BADOWNER;
2830224086Szack		goto out;
2831224086Szack	}
2832241194Srmacklem	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2833241194Srmacklem	str0 = str;
2834241194Srmacklem	tgid = (gid_t)strtoul(str0, &endstr, 10);
2835265724Srmacklem	if ((endstr - str0) == len) {
2836265724Srmacklem		/* A numeric string. */
2837265724Srmacklem		if ((nd->nd_flag & ND_KERBV) == 0 &&
2838265724Srmacklem		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2839265724Srmacklem		      nfsd_enable_stringtouid != 0))
2840265724Srmacklem			*gidp = tgid;
2841265724Srmacklem		else
2842265724Srmacklem			error = NFSERR_BADOWNER;
2843241194Srmacklem		goto out;
2844241194Srmacklem	}
2845191783Srmacklem	/*
2846191783Srmacklem	 * Look for an '@'.
2847191783Srmacklem	 */
2848241194Srmacklem	cp = strchr(str0, '@');
2849241194Srmacklem	if (cp != NULL)
2850241194Srmacklem		i = (int)(cp++ - str0);
2851241194Srmacklem	else
2852241194Srmacklem		i = len;
2853191783Srmacklem
2854191783Srmacklem	cnt = 0;
2855191783Srmacklemtryagain:
2856191783Srmacklem	NFSLOCKNAMEID();
2857191783Srmacklem	/*
2858191783Srmacklem	 * If an '@' is found and the dns name matches, search for the name
2859191783Srmacklem	 * with the dns stripped off.
2860191783Srmacklem	 */
2861191783Srmacklem	if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
2862191783Srmacklem	    (len - 1 - i) == nfsrv_dnsnamelen &&
2863191783Srmacklem	    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2864191783Srmacklem		len -= (nfsrv_dnsnamelen + 1);
2865191783Srmacklem		*(cp - 1) = '\0';
2866191783Srmacklem	}
2867191783Srmacklem
2868191783Srmacklem	/*
2869191783Srmacklem	 * Check for the special case of "nogroup".
2870191783Srmacklem	 */
2871191783Srmacklem	if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
2872191783Srmacklem		*gidp = nfsrv_defaultgid;
2873191783Srmacklem		NFSUNLOCKNAMEID();
2874224086Szack		error = 0;
2875224086Szack		goto out;
2876191783Srmacklem	}
2877191783Srmacklem
2878191783Srmacklem	LIST_FOREACH(usrp, NFSGROUPNAMEHASH(str, len), lug_namehash) {
2879191783Srmacklem		if (usrp->lug_namelen == len &&
2880191783Srmacklem		    !NFSBCMP(usrp->lug_name, str, len)) {
2881191783Srmacklem			if (usrp->lug_expiry < NFSD_MONOSEC)
2882191783Srmacklem				break;
2883191783Srmacklem			*gidp = usrp->lug_gid;
2884191783Srmacklem			TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2885191783Srmacklem			TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2886191783Srmacklem			NFSUNLOCKNAMEID();
2887224086Szack			error = 0;
2888224086Szack			goto out;
2889191783Srmacklem		}
2890191783Srmacklem	}
2891191783Srmacklem	NFSUNLOCKNAMEID();
2892191783Srmacklem	cnt++;
2893191783Srmacklem	ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
2894191783Srmacklem	    str, p);
2895191783Srmacklem	if (ret == 0 && cnt < 2)
2896191783Srmacklem		goto tryagain;
2897224086Szack	error = NFSERR_BADOWNER;
2898224086Szack
2899224086Szackout:
2900224086Szack	NFSEXITCODE(error);
2901224086Szack	return (error);
2902191783Srmacklem}
2903191783Srmacklem
2904191783Srmacklem/*
2905191783Srmacklem * Cmp len chars, allowing mixed case in the first argument to match lower
2906191783Srmacklem * case in the second, but not if the first argument is all upper case.
2907191783Srmacklem * Return 0 for a match, 1 otherwise.
2908191783Srmacklem */
2909191783Srmacklemstatic int
2910191783Srmacklemnfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len)
2911191783Srmacklem{
2912191783Srmacklem	int i;
2913191783Srmacklem	u_char tmp;
2914191783Srmacklem	int fndlower = 0;
2915191783Srmacklem
2916191783Srmacklem	for (i = 0; i < len; i++) {
2917191783Srmacklem		if (*cp >= 'A' && *cp <= 'Z') {
2918191783Srmacklem			tmp = *cp++ + ('a' - 'A');
2919191783Srmacklem		} else {
2920191783Srmacklem			tmp = *cp++;
2921191783Srmacklem			if (tmp >= 'a' && tmp <= 'z')
2922191783Srmacklem				fndlower = 1;
2923191783Srmacklem		}
2924191783Srmacklem		if (tmp != *cp2++)
2925191783Srmacklem			return (1);
2926191783Srmacklem	}
2927191783Srmacklem	if (fndlower)
2928191783Srmacklem		return (0);
2929191783Srmacklem	else
2930191783Srmacklem		return (1);
2931191783Srmacklem}
2932191783Srmacklem
2933191783Srmacklem/*
2934191783Srmacklem * Set the port for the nfsuserd.
2935191783Srmacklem */
2936191783SrmacklemAPPLESTATIC int
2937191783Srmacklemnfsrv_nfsuserdport(u_short port, NFSPROC_T *p)
2938191783Srmacklem{
2939191783Srmacklem	struct nfssockreq *rp;
2940191783Srmacklem	struct sockaddr_in *ad;
2941191783Srmacklem	int error;
2942191783Srmacklem
2943191783Srmacklem	NFSLOCKNAMEID();
2944191783Srmacklem	if (nfsrv_nfsuserd) {
2945191783Srmacklem		NFSUNLOCKNAMEID();
2946224086Szack		error = EPERM;
2947224086Szack		goto out;
2948191783Srmacklem	}
2949191783Srmacklem	nfsrv_nfsuserd = 1;
2950191783Srmacklem	NFSUNLOCKNAMEID();
2951191783Srmacklem	/*
2952191783Srmacklem	 * Set up the socket record and connect.
2953191783Srmacklem	 */
2954191783Srmacklem	rp = &nfsrv_nfsuserdsock;
2955191783Srmacklem	rp->nr_client = NULL;
2956191783Srmacklem	rp->nr_sotype = SOCK_DGRAM;
2957191783Srmacklem	rp->nr_soproto = IPPROTO_UDP;
2958191783Srmacklem	rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
2959191783Srmacklem	rp->nr_cred = NULL;
2960191783Srmacklem	NFSSOCKADDRALLOC(rp->nr_nam);
2961191783Srmacklem	NFSSOCKADDRSIZE(rp->nr_nam, sizeof (struct sockaddr_in));
2962191783Srmacklem	ad = NFSSOCKADDR(rp->nr_nam, struct sockaddr_in *);
2963191783Srmacklem	ad->sin_family = AF_INET;
2964191783Srmacklem	ad->sin_addr.s_addr = htonl((u_int32_t)0x7f000001);	/* 127.0.0.1 */
2965191783Srmacklem	ad->sin_port = port;
2966191783Srmacklem	rp->nr_prog = RPCPROG_NFSUSERD;
2967191783Srmacklem	rp->nr_vers = RPCNFSUSERD_VERS;
2968191783Srmacklem	error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0);
2969191783Srmacklem	if (error) {
2970191783Srmacklem		NFSSOCKADDRFREE(rp->nr_nam);
2971191783Srmacklem		nfsrv_nfsuserd = 0;
2972191783Srmacklem	}
2973224086Szackout:
2974224086Szack	NFSEXITCODE(error);
2975191783Srmacklem	return (error);
2976191783Srmacklem}
2977191783Srmacklem
2978191783Srmacklem/*
2979191783Srmacklem * Delete the nfsuserd port.
2980191783Srmacklem */
2981191783SrmacklemAPPLESTATIC void
2982191783Srmacklemnfsrv_nfsuserddelport(void)
2983191783Srmacklem{
2984191783Srmacklem
2985191783Srmacklem	NFSLOCKNAMEID();
2986191783Srmacklem	if (nfsrv_nfsuserd == 0) {
2987191783Srmacklem		NFSUNLOCKNAMEID();
2988191783Srmacklem		return;
2989191783Srmacklem	}
2990191783Srmacklem	nfsrv_nfsuserd = 0;
2991191783Srmacklem	NFSUNLOCKNAMEID();
2992191783Srmacklem	newnfs_disconnect(&nfsrv_nfsuserdsock);
2993191783Srmacklem	NFSSOCKADDRFREE(nfsrv_nfsuserdsock.nr_nam);
2994191783Srmacklem}
2995191783Srmacklem
2996191783Srmacklem/*
2997191783Srmacklem * Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
2998191783Srmacklem * name<-->id cache.
2999191783Srmacklem * Returns 0 upon success, non-zero otherwise.
3000191783Srmacklem */
3001191783Srmacklemstatic int
3002191783Srmacklemnfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name, NFSPROC_T *p)
3003191783Srmacklem{
3004191783Srmacklem	u_int32_t *tl;
3005191783Srmacklem	struct nfsrv_descript *nd;
3006191783Srmacklem	int len;
3007191783Srmacklem	struct nfsrv_descript nfsd;
3008191783Srmacklem	struct ucred *cred;
3009191783Srmacklem	int error;
3010191783Srmacklem
3011191783Srmacklem	NFSLOCKNAMEID();
3012191783Srmacklem	if (nfsrv_nfsuserd == 0) {
3013191783Srmacklem		NFSUNLOCKNAMEID();
3014224086Szack		error = EPERM;
3015224086Szack		goto out;
3016191783Srmacklem	}
3017191783Srmacklem	NFSUNLOCKNAMEID();
3018191783Srmacklem	nd = &nfsd;
3019191783Srmacklem	cred = newnfs_getcred();
3020191783Srmacklem	nd->nd_flag = ND_GSSINITREPLY;
3021191783Srmacklem	nfsrvd_rephead(nd);
3022191783Srmacklem
3023191783Srmacklem	nd->nd_procnum = procnum;
3024191783Srmacklem	if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
3025191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3026191783Srmacklem		if (procnum == RPCNFSUSERD_GETUID)
3027191783Srmacklem			*tl = txdr_unsigned(uid);
3028191783Srmacklem		else
3029191783Srmacklem			*tl = txdr_unsigned(gid);
3030191783Srmacklem	} else {
3031191783Srmacklem		len = strlen(name);
3032191783Srmacklem		(void) nfsm_strtom(nd, name, len);
3033191783Srmacklem	}
3034191783Srmacklem	error = newnfs_request(nd, NULL, NULL, &nfsrv_nfsuserdsock, NULL, NULL,
3035191783Srmacklem		cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0, NULL);
3036191783Srmacklem	NFSFREECRED(cred);
3037191783Srmacklem	if (!error) {
3038191783Srmacklem		mbuf_freem(nd->nd_mrep);
3039191783Srmacklem		error = nd->nd_repstat;
3040191783Srmacklem	}
3041224086Szackout:
3042224086Szack	NFSEXITCODE(error);
3043191783Srmacklem	return (error);
3044191783Srmacklem}
3045191783Srmacklem
3046191783Srmacklem/*
3047191783Srmacklem * This function is called from the nfssvc(2) system call, to update the
3048191783Srmacklem * kernel user/group name list(s) for the V4 owner and ownergroup attributes.
3049191783Srmacklem */
3050191783SrmacklemAPPLESTATIC int
3051191783Srmacklemnfssvc_idname(struct nfsd_idargs *nidp)
3052191783Srmacklem{
3053191783Srmacklem	struct nfsusrgrp *nusrp, *usrp, *newusrp;
3054191783Srmacklem	struct nfsuserhashhead *hp;
3055191783Srmacklem	int i;
3056191783Srmacklem	int error = 0;
3057191783Srmacklem	u_char *cp;
3058191783Srmacklem
3059191783Srmacklem	if (nidp->nid_flag & NFSID_INITIALIZE) {
3060191783Srmacklem	    cp = (u_char *)malloc(nidp->nid_namelen + 1,
3061191783Srmacklem		M_NFSSTRING, M_WAITOK);
3062191783Srmacklem	    error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
3063191783Srmacklem		nidp->nid_namelen);
3064191783Srmacklem	    NFSLOCKNAMEID();
3065191783Srmacklem	    if (nfsrv_dnsname) {
3066191783Srmacklem		/*
3067191783Srmacklem		 * Free up all the old stuff and reinitialize hash lists.
3068191783Srmacklem		 */
3069191783Srmacklem		TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
3070191783Srmacklem			nfsrv_removeuser(usrp);
3071191783Srmacklem		}
3072191783Srmacklem		free(nfsrv_dnsname, M_NFSSTRING);
3073191783Srmacklem		nfsrv_dnsname = NULL;
3074191783Srmacklem	    }
3075191783Srmacklem	    TAILQ_INIT(&nfsuserlruhead);
3076191783Srmacklem	    for (i = 0; i < NFSUSERHASHSIZE; i++)
3077191783Srmacklem		LIST_INIT(&nfsuserhash[i]);
3078191783Srmacklem	    for (i = 0; i < NFSGROUPHASHSIZE; i++)
3079191783Srmacklem		LIST_INIT(&nfsgrouphash[i]);
3080191783Srmacklem	    for (i = 0; i < NFSUSERHASHSIZE; i++)
3081191783Srmacklem		LIST_INIT(&nfsusernamehash[i]);
3082191783Srmacklem	    for (i = 0; i < NFSGROUPHASHSIZE; i++)
3083191783Srmacklem		LIST_INIT(&nfsgroupnamehash[i]);
3084191783Srmacklem
3085191783Srmacklem	    /*
3086191783Srmacklem	     * Put name in "DNS" string.
3087191783Srmacklem	     */
3088191783Srmacklem	    if (!error) {
3089191783Srmacklem		nfsrv_dnsname = cp;
3090191783Srmacklem		nfsrv_dnsnamelen = nidp->nid_namelen;
3091191783Srmacklem		nfsrv_defaultuid = nidp->nid_uid;
3092191783Srmacklem		nfsrv_defaultgid = nidp->nid_gid;
3093191783Srmacklem		nfsrv_usercnt = 0;
3094191783Srmacklem		nfsrv_usermax = nidp->nid_usermax;
3095191783Srmacklem	    }
3096191783Srmacklem	    NFSUNLOCKNAMEID();
3097191783Srmacklem	    if (error)
3098191783Srmacklem		free(cp, M_NFSSTRING);
3099224086Szack	    goto out;
3100191783Srmacklem	}
3101191783Srmacklem
3102191783Srmacklem	/*
3103191783Srmacklem	 * malloc the new one now, so any potential sleep occurs before
3104191783Srmacklem	 * manipulation of the lists.
3105191783Srmacklem	 */
3106191783Srmacklem	MALLOC(newusrp, struct nfsusrgrp *, sizeof (struct nfsusrgrp) +
3107191783Srmacklem	    nidp->nid_namelen, M_NFSUSERGROUP, M_WAITOK);
3108191783Srmacklem	error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
3109191783Srmacklem	    nidp->nid_namelen);
3110191783Srmacklem	if (error) {
3111191783Srmacklem		free((caddr_t)newusrp, M_NFSUSERGROUP);
3112224086Szack		goto out;
3113191783Srmacklem	}
3114191783Srmacklem	newusrp->lug_namelen = nidp->nid_namelen;
3115191783Srmacklem
3116191783Srmacklem	NFSLOCKNAMEID();
3117191783Srmacklem	/*
3118191783Srmacklem	 * Delete old entries, as required.
3119191783Srmacklem	 */
3120191783Srmacklem	if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
3121191783Srmacklem		hp = NFSUSERHASH(nidp->nid_uid);
3122191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
3123191783Srmacklem			if (usrp->lug_uid == nidp->nid_uid)
3124191783Srmacklem				nfsrv_removeuser(usrp);
3125191783Srmacklem		}
3126191783Srmacklem	}
3127191783Srmacklem	if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
3128191783Srmacklem		hp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3129191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
3130191783Srmacklem			if (usrp->lug_namelen == newusrp->lug_namelen &&
3131191783Srmacklem			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3132191783Srmacklem			    usrp->lug_namelen))
3133191783Srmacklem				nfsrv_removeuser(usrp);
3134191783Srmacklem		}
3135191783Srmacklem	}
3136191783Srmacklem	if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
3137191783Srmacklem		hp = NFSGROUPHASH(nidp->nid_gid);
3138191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
3139191783Srmacklem			if (usrp->lug_gid == nidp->nid_gid)
3140191783Srmacklem				nfsrv_removeuser(usrp);
3141191783Srmacklem		}
3142191783Srmacklem	}
3143191783Srmacklem	if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
3144191783Srmacklem		hp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3145191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
3146191783Srmacklem			if (usrp->lug_namelen == newusrp->lug_namelen &&
3147191783Srmacklem			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3148191783Srmacklem			    usrp->lug_namelen))
3149191783Srmacklem				nfsrv_removeuser(usrp);
3150191783Srmacklem		}
3151191783Srmacklem	}
3152191783Srmacklem	TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
3153191783Srmacklem		if (usrp->lug_expiry < NFSD_MONOSEC)
3154191783Srmacklem			nfsrv_removeuser(usrp);
3155191783Srmacklem	}
3156191783Srmacklem	while (nfsrv_usercnt >= nfsrv_usermax) {
3157191783Srmacklem		usrp = TAILQ_FIRST(&nfsuserlruhead);
3158191783Srmacklem		nfsrv_removeuser(usrp);
3159191783Srmacklem	}
3160191783Srmacklem
3161191783Srmacklem	/*
3162191783Srmacklem	 * Now, we can add the new one.
3163191783Srmacklem	 */
3164191783Srmacklem	if (nidp->nid_usertimeout)
3165191783Srmacklem		newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
3166191783Srmacklem	else
3167191783Srmacklem		newusrp->lug_expiry = NFSD_MONOSEC + 5;
3168191783Srmacklem	if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
3169191783Srmacklem		newusrp->lug_uid = nidp->nid_uid;
3170191783Srmacklem		LIST_INSERT_HEAD(NFSUSERHASH(newusrp->lug_uid), newusrp,
3171191783Srmacklem		    lug_numhash);
3172191783Srmacklem		LIST_INSERT_HEAD(NFSUSERNAMEHASH(newusrp->lug_name,
3173191783Srmacklem		    newusrp->lug_namelen), newusrp, lug_namehash);
3174191783Srmacklem		TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
3175191783Srmacklem		nfsrv_usercnt++;
3176191783Srmacklem	} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
3177191783Srmacklem		newusrp->lug_gid = nidp->nid_gid;
3178191783Srmacklem		LIST_INSERT_HEAD(NFSGROUPHASH(newusrp->lug_gid), newusrp,
3179191783Srmacklem		    lug_numhash);
3180191783Srmacklem		LIST_INSERT_HEAD(NFSGROUPNAMEHASH(newusrp->lug_name,
3181191783Srmacklem		    newusrp->lug_namelen), newusrp, lug_namehash);
3182191783Srmacklem		TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
3183191783Srmacklem		nfsrv_usercnt++;
3184191783Srmacklem	} else
3185191783Srmacklem		FREE((caddr_t)newusrp, M_NFSUSERGROUP);
3186191783Srmacklem	NFSUNLOCKNAMEID();
3187224086Szackout:
3188224086Szack	NFSEXITCODE(error);
3189191783Srmacklem	return (error);
3190191783Srmacklem}
3191191783Srmacklem
3192191783Srmacklem/*
3193191783Srmacklem * Remove a user/group name element.
3194191783Srmacklem */
3195191783Srmacklemstatic void
3196191783Srmacklemnfsrv_removeuser(struct nfsusrgrp *usrp)
3197191783Srmacklem{
3198191783Srmacklem
3199191783Srmacklem	NFSNAMEIDREQUIRED();
3200191783Srmacklem	LIST_REMOVE(usrp, lug_numhash);
3201191783Srmacklem	LIST_REMOVE(usrp, lug_namehash);
3202191783Srmacklem	TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
3203191783Srmacklem	nfsrv_usercnt--;
3204191783Srmacklem	FREE((caddr_t)usrp, M_NFSUSERGROUP);
3205191783Srmacklem}
3206191783Srmacklem
3207191783Srmacklem/*
3208191783Srmacklem * This function scans a byte string and checks for UTF-8 compliance.
3209191783Srmacklem * It returns 0 if it conforms and NFSERR_INVAL if not.
3210191783Srmacklem */
3211191783SrmacklemAPPLESTATIC int
3212191783Srmacklemnfsrv_checkutf8(u_int8_t *cp, int len)
3213191783Srmacklem{
3214191783Srmacklem	u_int32_t val = 0x0;
3215191783Srmacklem	int cnt = 0, gotd = 0, shift = 0;
3216191783Srmacklem	u_int8_t byte;
3217191783Srmacklem	static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
3218224086Szack	int error = 0;
3219191783Srmacklem
3220191783Srmacklem	/*
3221191783Srmacklem	 * Here are what the variables are used for:
3222191783Srmacklem	 * val - the calculated value of a multibyte char, used to check
3223191783Srmacklem	 *       that it was coded with the correct range
3224191783Srmacklem	 * cnt - the number of 10xxxxxx bytes to follow
3225191783Srmacklem	 * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
3226191783Srmacklem	 * shift - lower order bits of range (ie. "val >> shift" should
3227191783Srmacklem	 *       not be 0, in other words, dividing by the lower bound
3228191783Srmacklem	 *       of the range should get a non-zero value)
3229191783Srmacklem	 * byte - used to calculate cnt
3230191783Srmacklem	 */
3231191783Srmacklem	while (len > 0) {
3232191783Srmacklem		if (cnt > 0) {
3233191783Srmacklem			/* This handles the 10xxxxxx bytes */
3234191783Srmacklem			if ((*cp & 0xc0) != 0x80 ||
3235224086Szack			    (gotd && (*cp & 0x20))) {
3236224086Szack				error = NFSERR_INVAL;
3237224086Szack				goto out;
3238224086Szack			}
3239191783Srmacklem			gotd = 0;
3240191783Srmacklem			val <<= 6;
3241191783Srmacklem			val |= (*cp & 0x3f);
3242191783Srmacklem			cnt--;
3243224086Szack			if (cnt == 0 && (val >> shift) == 0x0) {
3244224086Szack				error = NFSERR_INVAL;
3245224086Szack				goto out;
3246224086Szack			}
3247191783Srmacklem		} else if (*cp & 0x80) {
3248191783Srmacklem			/* first byte of multi byte char */
3249191783Srmacklem			byte = *cp;
3250191783Srmacklem			while ((byte & 0x40) && cnt < 6) {
3251191783Srmacklem				cnt++;
3252191783Srmacklem				byte <<= 1;
3253191783Srmacklem			}
3254224086Szack			if (cnt == 0 || cnt == 6) {
3255224086Szack				error = NFSERR_INVAL;
3256224086Szack				goto out;
3257224086Szack			}
3258191783Srmacklem			val = (*cp & (0x3f >> cnt));
3259191783Srmacklem			shift = utf8_shift[cnt - 1];
3260191783Srmacklem			if (cnt == 2 && val == 0xd)
3261191783Srmacklem				/* Check for the 0xd800-0xdfff case */
3262191783Srmacklem				gotd = 1;
3263191783Srmacklem		}
3264191783Srmacklem		cp++;
3265191783Srmacklem		len--;
3266191783Srmacklem	}
3267191783Srmacklem	if (cnt > 0)
3268224086Szack		error = NFSERR_INVAL;
3269224086Szack
3270224086Szackout:
3271224086Szack	NFSEXITCODE(error);
3272224086Szack	return (error);
3273191783Srmacklem}
3274191783Srmacklem
3275191783Srmacklem/*
3276191783Srmacklem * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
3277191783Srmacklem * strings, one with the root path in it and the other with the list of
3278191783Srmacklem * locations. The list is in the same format as is found in nfr_refs.
3279191783Srmacklem * It is a "," separated list of entries, where each of them is of the
3280191783Srmacklem * form <server>:<rootpath>. For example
3281191783Srmacklem * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
3282191783Srmacklem * The nilp argument is set to 1 for the special case of a null fs_root
3283191783Srmacklem * and an empty server list.
3284191783Srmacklem * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
3285191783Srmacklem * number of xdr bytes parsed in sump.
3286191783Srmacklem */
3287191783Srmacklemstatic int
3288191783Srmacklemnfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
3289191783Srmacklem    int *sump, int *nilp)
3290191783Srmacklem{
3291191783Srmacklem	u_int32_t *tl;
3292191783Srmacklem	u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
3293224086Szack	int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
3294191783Srmacklem	struct list {
3295191783Srmacklem		SLIST_ENTRY(list) next;
3296191783Srmacklem		int len;
3297191783Srmacklem		u_char host[1];
3298191783Srmacklem	} *lsp, *nlsp;
3299191783Srmacklem	SLIST_HEAD(, list) head;
3300191783Srmacklem
3301191783Srmacklem	*fsrootp = NULL;
3302191783Srmacklem	*srvp = NULL;
3303191783Srmacklem	*nilp = 0;
3304191783Srmacklem
3305191783Srmacklem	/*
3306191783Srmacklem	 * Get the fs_root path and check for the special case of null path
3307191783Srmacklem	 * and 0 length server list.
3308191783Srmacklem	 */
3309191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3310191783Srmacklem	len = fxdr_unsigned(int, *tl);
3311224086Szack	if (len < 0 || len > 10240) {
3312224086Szack		error = NFSERR_BADXDR;
3313224086Szack		goto nfsmout;
3314224086Szack	}
3315191783Srmacklem	if (len == 0) {
3316191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3317224086Szack		if (*tl != 0) {
3318224086Szack			error = NFSERR_BADXDR;
3319224086Szack			goto nfsmout;
3320224086Szack		}
3321191783Srmacklem		*nilp = 1;
3322191783Srmacklem		*sump = 2 * NFSX_UNSIGNED;
3323224086Szack		error = 0;
3324224086Szack		goto nfsmout;
3325191783Srmacklem	}
3326191783Srmacklem	cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
3327191783Srmacklem	error = nfsrv_mtostr(nd, cp, len);
3328191783Srmacklem	if (!error) {
3329191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3330191783Srmacklem		cnt = fxdr_unsigned(int, *tl);
3331191783Srmacklem		if (cnt <= 0)
3332191783Srmacklem			error = NFSERR_BADXDR;
3333191783Srmacklem	}
3334224086Szack	if (error)
3335224086Szack		goto nfsmout;
3336191783Srmacklem
3337191783Srmacklem	/*
3338191783Srmacklem	 * Now, loop through the location list and make up the srvlist.
3339191783Srmacklem	 */
3340191783Srmacklem	xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3341191783Srmacklem	cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
3342191783Srmacklem	slen = 1024;
3343191783Srmacklem	siz = 0;
3344191783Srmacklem	for (i = 0; i < cnt; i++) {
3345191783Srmacklem		SLIST_INIT(&head);
3346191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3347191783Srmacklem		nsrv = fxdr_unsigned(int, *tl);
3348191783Srmacklem		if (nsrv <= 0) {
3349224086Szack			error = NFSERR_BADXDR;
3350224086Szack			goto nfsmout;
3351191783Srmacklem		}
3352191783Srmacklem
3353191783Srmacklem		/*
3354191783Srmacklem		 * Handle the first server by putting it in the srvstr.
3355191783Srmacklem		 */
3356191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3357191783Srmacklem		len = fxdr_unsigned(int, *tl);
3358191783Srmacklem		if (len <= 0 || len > 1024) {
3359224086Szack			error = NFSERR_BADXDR;
3360224086Szack			goto nfsmout;
3361191783Srmacklem		}
3362191783Srmacklem		nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
3363191783Srmacklem		if (cp3 != cp2) {
3364191783Srmacklem			*cp3++ = ',';
3365191783Srmacklem			siz++;
3366191783Srmacklem		}
3367191783Srmacklem		error = nfsrv_mtostr(nd, cp3, len);
3368224086Szack		if (error)
3369224086Szack			goto nfsmout;
3370191783Srmacklem		cp3 += len;
3371191783Srmacklem		*cp3++ = ':';
3372191783Srmacklem		siz += (len + 1);
3373191783Srmacklem		xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3374191783Srmacklem		for (j = 1; j < nsrv; j++) {
3375191783Srmacklem			/*
3376191783Srmacklem			 * Yuck, put them in an slist and process them later.
3377191783Srmacklem			 */
3378191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3379191783Srmacklem			len = fxdr_unsigned(int, *tl);
3380191783Srmacklem			if (len <= 0 || len > 1024) {
3381224086Szack				error = NFSERR_BADXDR;
3382224086Szack				goto nfsmout;
3383191783Srmacklem			}
3384191783Srmacklem			lsp = (struct list *)malloc(sizeof (struct list)
3385191783Srmacklem			    + len, M_TEMP, M_WAITOK);
3386191783Srmacklem			error = nfsrv_mtostr(nd, lsp->host, len);
3387224086Szack			if (error)
3388224086Szack				goto nfsmout;
3389191783Srmacklem			xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3390191783Srmacklem			lsp->len = len;
3391191783Srmacklem			SLIST_INSERT_HEAD(&head, lsp, next);
3392191783Srmacklem		}
3393191783Srmacklem
3394191783Srmacklem		/*
3395191783Srmacklem		 * Finally, we can get the path.
3396191783Srmacklem		 */
3397191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3398191783Srmacklem		len = fxdr_unsigned(int, *tl);
3399191783Srmacklem		if (len <= 0 || len > 1024) {
3400224086Szack			error = NFSERR_BADXDR;
3401224086Szack			goto nfsmout;
3402191783Srmacklem		}
3403191783Srmacklem		nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
3404191783Srmacklem		error = nfsrv_mtostr(nd, cp3, len);
3405224086Szack		if (error)
3406224086Szack			goto nfsmout;
3407191783Srmacklem		xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3408191783Srmacklem		str = cp3;
3409191783Srmacklem		stringlen = len;
3410191783Srmacklem		cp3 += len;
3411191783Srmacklem		siz += len;
3412191783Srmacklem		SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
3413191783Srmacklem			nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
3414191783Srmacklem			    &cp2, &cp3, &slen);
3415191783Srmacklem			*cp3++ = ',';
3416191783Srmacklem			NFSBCOPY(lsp->host, cp3, lsp->len);
3417191783Srmacklem			cp3 += lsp->len;
3418191783Srmacklem			*cp3++ = ':';
3419191783Srmacklem			NFSBCOPY(str, cp3, stringlen);
3420191783Srmacklem			cp3 += stringlen;
3421191783Srmacklem			*cp3 = '\0';
3422191783Srmacklem			siz += (lsp->len + stringlen + 2);
3423191783Srmacklem			free((caddr_t)lsp, M_TEMP);
3424191783Srmacklem		}
3425191783Srmacklem	}
3426191783Srmacklem	*fsrootp = cp;
3427191783Srmacklem	*srvp = cp2;
3428191783Srmacklem	*sump = xdrsum;
3429224086Szack	NFSEXITCODE2(0, nd);
3430191783Srmacklem	return (0);
3431191783Srmacklemnfsmout:
3432191783Srmacklem	if (cp != NULL)
3433191783Srmacklem		free(cp, M_NFSSTRING);
3434191783Srmacklem	if (cp2 != NULL)
3435191783Srmacklem		free(cp2, M_NFSSTRING);
3436224086Szack	NFSEXITCODE2(error, nd);
3437191783Srmacklem	return (error);
3438191783Srmacklem}
3439191783Srmacklem
3440191783Srmacklem/*
3441191783Srmacklem * Make the malloc'd space large enough. This is a pain, but the xdr
3442191783Srmacklem * doesn't set an upper bound on the side, so...
3443191783Srmacklem */
3444191783Srmacklemstatic void
3445191783Srmacklemnfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
3446191783Srmacklem{
3447191783Srmacklem	u_char *cp;
3448191783Srmacklem	int i;
3449191783Srmacklem
3450191783Srmacklem	if (siz <= *slenp)
3451191783Srmacklem		return;
3452191783Srmacklem	cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
3453191783Srmacklem	NFSBCOPY(*cpp, cp, *slenp);
3454191783Srmacklem	free(*cpp, M_NFSSTRING);
3455191783Srmacklem	i = *cpp2 - *cpp;
3456191783Srmacklem	*cpp = cp;
3457191783Srmacklem	*cpp2 = cp + i;
3458191783Srmacklem	*slenp = siz + 1024;
3459191783Srmacklem}
3460191783Srmacklem
3461191783Srmacklem/*
3462191783Srmacklem * Initialize the reply header data structures.
3463191783Srmacklem */
3464191783SrmacklemAPPLESTATIC void
3465191783Srmacklemnfsrvd_rephead(struct nfsrv_descript *nd)
3466191783Srmacklem{
3467191783Srmacklem	mbuf_t mreq;
3468191783Srmacklem
3469191783Srmacklem	/*
3470191783Srmacklem	 * If this is a big reply, use a cluster.
3471191783Srmacklem	 */
3472191783Srmacklem	if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
3473191783Srmacklem	    nfs_bigreply[nd->nd_procnum]) {
3474191783Srmacklem		NFSMCLGET(mreq, M_WAIT);
3475191783Srmacklem		nd->nd_mreq = mreq;
3476191783Srmacklem		nd->nd_mb = mreq;
3477191783Srmacklem	} else {
3478191783Srmacklem		NFSMGET(mreq);
3479191783Srmacklem		nd->nd_mreq = mreq;
3480191783Srmacklem		nd->nd_mb = mreq;
3481191783Srmacklem	}
3482191783Srmacklem	nd->nd_bpos = NFSMTOD(mreq, caddr_t);
3483191783Srmacklem	mbuf_setlen(mreq, 0);
3484191783Srmacklem
3485191783Srmacklem	if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
3486191783Srmacklem		NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
3487191783Srmacklem}
3488191783Srmacklem
3489191783Srmacklem/*
3490191783Srmacklem * Lock a socket against others.
3491191783Srmacklem * Currently used to serialize connect/disconnect attempts.
3492191783Srmacklem */
3493191783Srmacklemint
3494191783Srmacklemnewnfs_sndlock(int *flagp)
3495191783Srmacklem{
3496191783Srmacklem	struct timespec ts;
3497191783Srmacklem
3498191783Srmacklem	NFSLOCKSOCK();
3499191783Srmacklem	while (*flagp & NFSR_SNDLOCK) {
3500191783Srmacklem		*flagp |= NFSR_WANTSND;
3501191783Srmacklem		ts.tv_sec = 0;
3502191783Srmacklem		ts.tv_nsec = 0;
3503191783Srmacklem		(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
3504191783Srmacklem		    PZERO - 1, "nfsndlck", &ts);
3505191783Srmacklem	}
3506191783Srmacklem	*flagp |= NFSR_SNDLOCK;
3507191783Srmacklem	NFSUNLOCKSOCK();
3508191783Srmacklem	return (0);
3509191783Srmacklem}
3510191783Srmacklem
3511191783Srmacklem/*
3512191783Srmacklem * Unlock the stream socket for others.
3513191783Srmacklem */
3514191783Srmacklemvoid
3515191783Srmacklemnewnfs_sndunlock(int *flagp)
3516191783Srmacklem{
3517191783Srmacklem
3518191783Srmacklem	NFSLOCKSOCK();
3519191783Srmacklem	if ((*flagp & NFSR_SNDLOCK) == 0)
3520191783Srmacklem		panic("nfs sndunlock");
3521191783Srmacklem	*flagp &= ~NFSR_SNDLOCK;
3522191783Srmacklem	if (*flagp & NFSR_WANTSND) {
3523191783Srmacklem		*flagp &= ~NFSR_WANTSND;
3524191783Srmacklem		wakeup((caddr_t)flagp);
3525191783Srmacklem	}
3526191783Srmacklem	NFSUNLOCKSOCK();
3527191783Srmacklem}
3528191783Srmacklem
3529