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