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