xref: /xnu-8792.61.2/bsd/nfs/nfs_subs.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
1 /*
2  * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30  * Copyright (c) 1989, 1993
31  *	The Regents of the University of California.  All rights reserved.
32  *
33  * This code is derived from software contributed to Berkeley by
34  * Rick Macklem at The University of Guelph.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *	This product includes software developed by the University of
47  *	California, Berkeley and its contributors.
48  * 4. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  *	@(#)nfs_subs.c	8.8 (Berkeley) 5/22/95
65  * FreeBSD-Id: nfs_subs.c,v 1.47 1997/11/07 08:53:24 phk Exp $
66  */
67 
68 #include <nfs/nfs_conf.h>
69 #if CONFIG_NFS_SERVER
70 
71 /*
72  * These functions support the macros and help fiddle mbuf chains for
73  * the nfs op functions. They do things like create the rpc header and
74  * copy data between mbuf chains and uio lists.
75  */
76 #include <sys/kauth.h>
77 #include <sys/mount_internal.h>
78 #include <sys/vnode_internal.h>
79 #include <sys/kpi_mbuf.h>
80 #include <sys/un.h>
81 #include <sys/domain.h>
82 
83 #include <nfs/rpcv2.h>
84 #include <nfs/nfsproto.h>
85 #include <nfs/nfs.h>
86 #define _NFS_XDR_SUBS_FUNCS_ /* define this to get xdrbuf function definitions */
87 #include <nfs/xdr_subs.h>
88 #include <nfs/nfsm_subs.h>
89 #include <nfs/nfs_gss.h>
90 
91 /*
92  * NFS globals
93  */
94 struct nfsrvstats __attribute__((aligned(8))) nfsrvstats;
95 size_t nfs_mbuf_mhlen = 0, nfs_mbuf_minclsize = 0;
96 
97 /* NFS debugging support */
98 uint32_t nfsrv_debug_ctl;
99 
100 #include <libkern/libkern.h>
101 #include <stdarg.h>
102 
103 void
nfs_printf(unsigned int debug_control,unsigned int facility,unsigned int level,const char * fmt,...)104 nfs_printf(unsigned int debug_control, unsigned int facility, unsigned int level, const char *fmt, ...)
105 {
106 	va_list ap;
107 
108 	if (__NFS_IS_DBG(debug_control, facility, level)) {
109 		va_start(ap, fmt);
110 		vprintf(fmt, ap);
111 		va_end(ap);
112 	}
113 }
114 
115 
116 #define DISPLAYLEN 16
117 
118 static bool
isprint(int ch)119 isprint(int ch)
120 {
121 	return ch >= 0x20 && ch <= 0x7e;
122 }
123 
124 static void
hexdump(void * data,size_t len)125 hexdump(void *data, size_t len)
126 {
127 	size_t i, j;
128 	unsigned char *d = data;
129 	char *p, disbuf[3 * DISPLAYLEN + 1];
130 
131 	for (i = 0; i < len; i += DISPLAYLEN) {
132 		for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p += 3) {
133 			snprintf(p, 4, "%2.2x ", d[i + j]);
134 		}
135 		for (; j < DISPLAYLEN; j++, p += 3) {
136 			snprintf(p, 4, "   ");
137 		}
138 		printf("%s    ", disbuf);
139 		for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p++) {
140 			snprintf(p, 2, "%c", isprint(d[i + j]) ? d[i + j] : '.');
141 		}
142 		printf("%s\n", disbuf);
143 	}
144 }
145 
146 void
nfs_dump_mbuf(const char * func,int lineno,const char * msg,mbuf_t mb)147 nfs_dump_mbuf(const char *func, int lineno, const char *msg, mbuf_t mb)
148 {
149 	mbuf_t m;
150 
151 	printf("%s:%d %s\n", func, lineno, msg);
152 	for (m = mb; m; m = mbuf_next(m)) {
153 		hexdump(mbuf_data(m), mbuf_len(m));
154 	}
155 }
156 
157 /*
158  * functions to convert between NFS and VFS types
159  */
160 nfstype
vtonfs_type(enum vtype vtype,int nfsvers)161 vtonfs_type(enum vtype vtype, int nfsvers)
162 {
163 	switch (vtype) {
164 	case VNON:
165 		return NFNON;
166 	case VREG:
167 		return NFREG;
168 	case VDIR:
169 		return NFDIR;
170 	case VBLK:
171 		return NFBLK;
172 	case VCHR:
173 		return NFCHR;
174 	case VLNK:
175 		return NFLNK;
176 	case VSOCK:
177 		if (nfsvers > NFS_VER2) {
178 			return NFSOCK;
179 		}
180 		return NFNON;
181 	case VFIFO:
182 		if (nfsvers > NFS_VER2) {
183 			return NFFIFO;
184 		}
185 		return NFNON;
186 	case VBAD:
187 	case VSTR:
188 	case VCPLX:
189 	default:
190 		return NFNON;
191 	}
192 }
193 
194 enum vtype
nfstov_type(nfstype nvtype,int nfsvers)195 nfstov_type(nfstype nvtype, int nfsvers)
196 {
197 	switch (nvtype) {
198 	case NFNON:
199 		return VNON;
200 	case NFREG:
201 		return VREG;
202 	case NFDIR:
203 		return VDIR;
204 	case NFBLK:
205 		return VBLK;
206 	case NFCHR:
207 		return VCHR;
208 	case NFLNK:
209 		return VLNK;
210 	case NFSOCK:
211 		if (nfsvers > NFS_VER2) {
212 			return VSOCK;
213 		}
214 		OS_FALLTHROUGH;
215 	case NFFIFO:
216 		if (nfsvers > NFS_VER2) {
217 			return VFIFO;
218 		}
219 		OS_FALLTHROUGH;
220 	case NFATTRDIR:
221 		if (nfsvers > NFS_VER3) {
222 			return VDIR;
223 		}
224 		OS_FALLTHROUGH;
225 	case NFNAMEDATTR:
226 		if (nfsvers > NFS_VER3) {
227 			return VREG;
228 		}
229 		OS_FALLTHROUGH;
230 	default:
231 		return VNON;
232 	}
233 }
234 
235 int
vtonfsv2_mode(enum vtype vtype,mode_t m)236 vtonfsv2_mode(enum vtype vtype, mode_t m)
237 {
238 	switch (vtype) {
239 	case VNON:
240 	case VREG:
241 	case VDIR:
242 	case VBLK:
243 	case VCHR:
244 	case VLNK:
245 	case VSOCK:
246 		return MAKEIMODE(vtype, m);
247 	case VFIFO:
248 		return MAKEIMODE(VCHR, m);
249 	case VBAD:
250 	case VSTR:
251 	case VCPLX:
252 	default:
253 		return MAKEIMODE(VNON, m);
254 	}
255 }
256 
257 /*
258  * Mapping of old NFS Version 2 RPC numbers to generic numbers.
259  */
260 int nfsv3_procid[NFS_NPROCS] = {
261 	NFSPROC_NULL,
262 	NFSPROC_GETATTR,
263 	NFSPROC_SETATTR,
264 	NFSPROC_NOOP,
265 	NFSPROC_LOOKUP,
266 	NFSPROC_READLINK,
267 	NFSPROC_READ,
268 	NFSPROC_NOOP,
269 	NFSPROC_WRITE,
270 	NFSPROC_CREATE,
271 	NFSPROC_REMOVE,
272 	NFSPROC_RENAME,
273 	NFSPROC_LINK,
274 	NFSPROC_SYMLINK,
275 	NFSPROC_MKDIR,
276 	NFSPROC_RMDIR,
277 	NFSPROC_READDIR,
278 	NFSPROC_FSSTAT,
279 	NFSPROC_NOOP,
280 	NFSPROC_NOOP,
281 	NFSPROC_NOOP,
282 	NFSPROC_NOOP,
283 	NFSPROC_NOOP
284 };
285 
286 /*
287  * and the reverse mapping from generic to Version 2 procedure numbers
288  */
289 int nfsv2_procid[NFS_NPROCS] = {
290 	NFSV2PROC_NULL,
291 	NFSV2PROC_GETATTR,
292 	NFSV2PROC_SETATTR,
293 	NFSV2PROC_LOOKUP,
294 	NFSV2PROC_NOOP,
295 	NFSV2PROC_READLINK,
296 	NFSV2PROC_READ,
297 	NFSV2PROC_WRITE,
298 	NFSV2PROC_CREATE,
299 	NFSV2PROC_MKDIR,
300 	NFSV2PROC_SYMLINK,
301 	NFSV2PROC_CREATE,
302 	NFSV2PROC_REMOVE,
303 	NFSV2PROC_RMDIR,
304 	NFSV2PROC_RENAME,
305 	NFSV2PROC_LINK,
306 	NFSV2PROC_READDIR,
307 	NFSV2PROC_NOOP,
308 	NFSV2PROC_STATFS,
309 	NFSV2PROC_NOOP,
310 	NFSV2PROC_NOOP,
311 	NFSV2PROC_NOOP,
312 	NFSV2PROC_NOOP
313 };
314 
315 
316 /*
317  * initialize NFS's cache of mbuf constants
318  */
319 void
nfs_mbuf_init(void)320 nfs_mbuf_init(void)
321 {
322 	struct mbuf_stat ms;
323 
324 	mbuf_stats(&ms);
325 	nfs_mbuf_mhlen = ms.mhlen;
326 	nfs_mbuf_minclsize = ms.minclsize;
327 }
328 
329 static void
nfs_netopt_free(struct nfs_netopt * no)330 nfs_netopt_free(struct nfs_netopt *no)
331 {
332 	if (no->no_addr) {
333 		kfree_data(no->no_addr, no->no_addr->sa_len);
334 	}
335 	if (no->no_mask) {
336 		kfree_data(no->no_mask, no->no_mask->sa_len);
337 	}
338 
339 	kfree_type(struct nfs_netopt, no);
340 }
341 
342 /*
343  * allocate a list of mbufs to hold the given amount of data
344  */
345 int
nfsm_mbuf_get_list(size_t size,mbuf_t * mp,int * mbcnt)346 nfsm_mbuf_get_list(size_t size, mbuf_t *mp, int *mbcnt)
347 {
348 	int error, cnt;
349 	mbuf_t mhead, mlast, m;
350 	size_t len, mlen;
351 
352 	error = cnt = 0;
353 	mhead = mlast = NULL;
354 	len = 0;
355 
356 	while (len < size) {
357 		nfsm_mbuf_getcluster(error, &m, (size - len));
358 		if (error) {
359 			break;
360 		}
361 		if (!mhead) {
362 			mhead = m;
363 		}
364 		if (mlast && ((error = mbuf_setnext(mlast, m)))) {
365 			mbuf_free(m);
366 			break;
367 		}
368 		mlen = mbuf_maxlen(m);
369 		if ((len + mlen) > size) {
370 			mlen = size - len;
371 		}
372 		mbuf_setlen(m, mlen);
373 		len += mlen;
374 		cnt++;
375 		mlast = m;
376 	}
377 
378 	if (!error) {
379 		*mp = mhead;
380 		*mbcnt = cnt;
381 	}
382 	return error;
383 }
384 
385 /*
386  * nfsm_chain_new_mbuf()
387  *
388  * Add a new mbuf to the given chain.
389  */
390 int
nfsm_chain_new_mbuf(struct nfsm_chain * nmc,size_t sizehint)391 nfsm_chain_new_mbuf(struct nfsm_chain *nmc, size_t sizehint)
392 {
393 	mbuf_t mb;
394 	int error = 0;
395 
396 	if (nmc->nmc_flags & NFSM_CHAIN_FLAG_ADD_CLUSTERS) {
397 		sizehint = nfs_mbuf_minclsize;
398 	}
399 
400 	/* allocate a new mbuf */
401 	nfsm_mbuf_getcluster(error, &mb, sizehint);
402 	if (error) {
403 		return error;
404 	}
405 	if (mb == NULL) {
406 		panic("got NULL mbuf?");
407 	}
408 
409 	/* do we have a current mbuf? */
410 	if (nmc->nmc_mcur) {
411 		/* first cap off current mbuf */
412 		mbuf_setlen(nmc->nmc_mcur, nmc->nmc_ptr - (caddr_t)mbuf_data(nmc->nmc_mcur));
413 		/* then append the new mbuf */
414 		error = mbuf_setnext(nmc->nmc_mcur, mb);
415 		if (error) {
416 			mbuf_free(mb);
417 			return error;
418 		}
419 	}
420 
421 	/* set up for using the new mbuf */
422 	nmc->nmc_mcur = mb;
423 	nmc->nmc_ptr = mbuf_data(mb);
424 	nmc->nmc_left = mbuf_trailingspace(mb);
425 
426 	return 0;
427 }
428 
429 /*
430  * nfsm_chain_add_opaque_f()
431  *
432  * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
433  */
434 int
nfsm_chain_add_opaque_f(struct nfsm_chain * nmc,const u_char * buf,size_t len)435 nfsm_chain_add_opaque_f(struct nfsm_chain *nmc, const u_char *buf, size_t len)
436 {
437 	size_t paddedlen, tlen;
438 	int error;
439 
440 	paddedlen = nfsm_rndup(len);
441 
442 	while (paddedlen) {
443 		if (!nmc->nmc_left) {
444 			error = nfsm_chain_new_mbuf(nmc, paddedlen);
445 			if (error) {
446 				return error;
447 			}
448 		}
449 		tlen = MIN(nmc->nmc_left, paddedlen);
450 		if (tlen) {
451 			if (len) {
452 				if (tlen > len) {
453 					tlen = len;
454 				}
455 				bcopy(buf, nmc->nmc_ptr, tlen);
456 			} else {
457 				bzero(nmc->nmc_ptr, tlen);
458 			}
459 			nmc->nmc_ptr += tlen;
460 			nmc->nmc_left -= tlen;
461 			paddedlen -= tlen;
462 			if (len) {
463 				buf += tlen;
464 				len -= tlen;
465 			}
466 		}
467 	}
468 	return 0;
469 }
470 
471 /*
472  * nfsm_chain_add_opaque_nopad_f()
473  *
474  * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
475  * Do not XDR pad.
476  */
477 int
nfsm_chain_add_opaque_nopad_f(struct nfsm_chain * nmc,const u_char * buf,size_t len)478 nfsm_chain_add_opaque_nopad_f(struct nfsm_chain *nmc, const u_char *buf, size_t len)
479 {
480 	size_t tlen;
481 	int error;
482 
483 	while (len > 0) {
484 		if (nmc->nmc_left <= 0) {
485 			error = nfsm_chain_new_mbuf(nmc, len);
486 			if (error) {
487 				return error;
488 			}
489 		}
490 		tlen = MIN(nmc->nmc_left, len);
491 		bcopy(buf, nmc->nmc_ptr, tlen);
492 		nmc->nmc_ptr += tlen;
493 		nmc->nmc_left -= tlen;
494 		len -= tlen;
495 		buf += tlen;
496 	}
497 	return 0;
498 }
499 
500 /*
501  * nfsm_chain_add_uio()
502  *
503  * Add "len" bytes of data from "uio" to the given chain.
504  */
505 int
nfsm_chain_add_uio(struct nfsm_chain * nmc,uio_t uio,size_t len)506 nfsm_chain_add_uio(struct nfsm_chain *nmc, uio_t uio, size_t len)
507 {
508 	size_t paddedlen, tlen;
509 	int error;
510 
511 	paddedlen = nfsm_rndup(len);
512 
513 	while (paddedlen) {
514 		if (!nmc->nmc_left) {
515 			error = nfsm_chain_new_mbuf(nmc, paddedlen);
516 			if (error) {
517 				return error;
518 			}
519 		}
520 		tlen = MIN(nmc->nmc_left, paddedlen);
521 		if (tlen) {
522 			if (len) {
523 				tlen = MIN(INT32_MAX, MIN(tlen, len));
524 				uiomove(nmc->nmc_ptr, (int)tlen, uio);
525 			} else {
526 				bzero(nmc->nmc_ptr, tlen);
527 			}
528 			nmc->nmc_ptr += tlen;
529 			nmc->nmc_left -= tlen;
530 			paddedlen -= tlen;
531 			if (len) {
532 				len -= tlen;
533 			}
534 		}
535 	}
536 	return 0;
537 }
538 
539 /*
540  * Find the length of the NFS mbuf chain
541  * up to the current encoding/decoding offset.
542  */
543 size_t
nfsm_chain_offset(struct nfsm_chain * nmc)544 nfsm_chain_offset(struct nfsm_chain *nmc)
545 {
546 	mbuf_t mb;
547 	size_t len = 0;
548 
549 	for (mb = nmc->nmc_mhead; mb; mb = mbuf_next(mb)) {
550 		if (mb == nmc->nmc_mcur) {
551 			return len + (nmc->nmc_ptr - (caddr_t) mbuf_data(mb));
552 		}
553 		len += mbuf_len(mb);
554 	}
555 
556 	return len;
557 }
558 
559 /*
560  * nfsm_chain_advance()
561  *
562  * Advance an nfsm_chain by "len" bytes.
563  */
564 int
nfsm_chain_advance(struct nfsm_chain * nmc,size_t len)565 nfsm_chain_advance(struct nfsm_chain *nmc, size_t len)
566 {
567 	mbuf_t mb;
568 
569 	while (len) {
570 		if (nmc->nmc_left >= len) {
571 			nmc->nmc_left -= len;
572 			nmc->nmc_ptr += len;
573 			return 0;
574 		}
575 		len -= nmc->nmc_left;
576 		nmc->nmc_mcur = mb = mbuf_next(nmc->nmc_mcur);
577 		if (!mb) {
578 			return EBADRPC;
579 		}
580 		nmc->nmc_ptr = mbuf_data(mb);
581 		nmc->nmc_left = mbuf_len(mb);
582 	}
583 
584 	return 0;
585 }
586 
587 /*
588  * nfsm_chain_reverse()
589  *
590  * Reverse decode offset in an nfsm_chain by "len" bytes.
591  */
592 int
nfsm_chain_reverse(struct nfsm_chain * nmc,size_t len)593 nfsm_chain_reverse(struct nfsm_chain *nmc, size_t len)
594 {
595 	size_t mlen, new_offset;
596 	int error = 0;
597 
598 	mlen = nmc->nmc_ptr - (caddr_t) mbuf_data(nmc->nmc_mcur);
599 	if (len <= mlen) {
600 		nmc->nmc_ptr -= len;
601 		nmc->nmc_left += len;
602 		return 0;
603 	}
604 
605 	new_offset = nfsm_chain_offset(nmc) - len;
606 	nfsm_chain_dissect_init(error, nmc, nmc->nmc_mhead);
607 	if (error) {
608 		return error;
609 	}
610 
611 	return nfsm_chain_advance(nmc, new_offset);
612 }
613 
614 /*
615  * nfsm_chain_get_opaque_pointer_f()
616  *
617  * Return a pointer to the next "len" bytes of contiguous data in
618  * the mbuf chain.  If the next "len" bytes are not contiguous, we
619  * try to manipulate the mbuf chain so that it is.
620  *
621  * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
622  */
623 int
nfsm_chain_get_opaque_pointer_f(struct nfsm_chain * nmc,uint32_t len,u_char ** pptr)624 nfsm_chain_get_opaque_pointer_f(struct nfsm_chain *nmc, uint32_t len, u_char **pptr)
625 {
626 	mbuf_t mbcur, mb;
627 	uint32_t padlen;
628 	size_t mblen, cplen, need, left;
629 	u_char *ptr;
630 	int error = 0;
631 
632 	/* move to next mbuf with data */
633 	while (nmc->nmc_mcur && (nmc->nmc_left == 0)) {
634 		mb = mbuf_next(nmc->nmc_mcur);
635 		nmc->nmc_mcur = mb;
636 		if (!mb) {
637 			break;
638 		}
639 		nmc->nmc_ptr = mbuf_data(mb);
640 		nmc->nmc_left = mbuf_len(mb);
641 	}
642 	/* check if we've run out of data */
643 	if (!nmc->nmc_mcur) {
644 		return EBADRPC;
645 	}
646 
647 	/* do we already have a contiguous buffer? */
648 	if (nmc->nmc_left >= len) {
649 		/* the returned pointer will be the current pointer */
650 		*pptr = (u_char*)nmc->nmc_ptr;
651 		error = nfsm_chain_advance(nmc, nfsm_rndup(len));
652 		return error;
653 	}
654 
655 	padlen = nfsm_rndup(len) - len;
656 
657 	/* we need (len - left) more bytes */
658 	mbcur = nmc->nmc_mcur;
659 	left = nmc->nmc_left;
660 	need = len - left;
661 
662 	if (need > mbuf_trailingspace(mbcur)) {
663 		/*
664 		 * The needed bytes won't fit in the current mbuf so we'll
665 		 * allocate a new mbuf to hold the contiguous range of data.
666 		 */
667 		nfsm_mbuf_getcluster(error, &mb, len);
668 		if (error) {
669 			return error;
670 		}
671 		/* double check that this mbuf can hold all the data */
672 		if (mbuf_maxlen(mb) < len) {
673 			mbuf_free(mb);
674 			return EOVERFLOW;
675 		}
676 
677 		/* the returned pointer will be the new mbuf's data pointer */
678 		*pptr = ptr = mbuf_data(mb);
679 
680 		/* copy "left" bytes to the new mbuf */
681 		bcopy(nmc->nmc_ptr, ptr, left);
682 		ptr += left;
683 		mbuf_setlen(mb, left);
684 
685 		/* insert the new mbuf between the current and next mbufs */
686 		error = mbuf_setnext(mb, mbuf_next(mbcur));
687 		if (!error) {
688 			error = mbuf_setnext(mbcur, mb);
689 		}
690 		if (error) {
691 			mbuf_free(mb);
692 			return error;
693 		}
694 
695 		/* reduce current mbuf's length by "left" */
696 		mbuf_setlen(mbcur, mbuf_len(mbcur) - left);
697 
698 		/*
699 		 * update nmc's state to point at the end of the mbuf
700 		 * where the needed data will be copied to.
701 		 */
702 		nmc->nmc_mcur = mbcur = mb;
703 		nmc->nmc_left = 0;
704 		nmc->nmc_ptr = (caddr_t)ptr;
705 	} else {
706 		/* The rest of the data will fit in this mbuf. */
707 
708 		/* the returned pointer will be the current pointer */
709 		*pptr = (u_char*)nmc->nmc_ptr;
710 
711 		/*
712 		 * update nmc's state to point at the end of the mbuf
713 		 * where the needed data will be copied to.
714 		 */
715 		nmc->nmc_ptr += left;
716 		nmc->nmc_left = 0;
717 	}
718 
719 	/*
720 	 * move the next "need" bytes into the current
721 	 * mbuf from the mbufs that follow
722 	 */
723 
724 	/* extend current mbuf length */
725 	mbuf_setlen(mbcur, mbuf_len(mbcur) + need);
726 
727 	/* mb follows mbufs we're copying/compacting data from */
728 	mb = mbuf_next(mbcur);
729 
730 	while (need && mb) {
731 		/* copy as much as we need/can */
732 		ptr = mbuf_data(mb);
733 		mblen = mbuf_len(mb);
734 		cplen = MIN(mblen, need);
735 		if (cplen) {
736 			bcopy(ptr, nmc->nmc_ptr, cplen);
737 			/*
738 			 * update the mbuf's pointer and length to reflect that
739 			 * the data was shifted to an earlier mbuf in the chain
740 			 */
741 			error = mbuf_setdata(mb, ptr + cplen, mblen - cplen);
742 			if (error) {
743 				mbuf_setlen(mbcur, mbuf_len(mbcur) - need);
744 				return error;
745 			}
746 			/* update pointer/need */
747 			nmc->nmc_ptr += cplen;
748 			need -= cplen;
749 		}
750 		/* if more needed, go to next mbuf */
751 		if (need) {
752 			mb = mbuf_next(mb);
753 		}
754 	}
755 
756 	/* did we run out of data in the mbuf chain? */
757 	if (need) {
758 		mbuf_setlen(mbcur, mbuf_len(mbcur) - need);
759 		return EBADRPC;
760 	}
761 
762 	/*
763 	 * update nmc's state to point after this contiguous data
764 	 *
765 	 * "mb" points to the last mbuf we copied data from so we
766 	 * just set nmc to point at whatever remains in that mbuf.
767 	 */
768 	nmc->nmc_mcur = mb;
769 	nmc->nmc_ptr = mbuf_data(mb);
770 	nmc->nmc_left = mbuf_len(mb);
771 
772 	/* move past any padding */
773 	if (padlen) {
774 		error = nfsm_chain_advance(nmc, padlen);
775 	}
776 
777 	return error;
778 }
779 
780 /*
781  * nfsm_chain_get_opaque_f()
782  *
783  * Read the next "len" bytes in the chain into "buf".
784  * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
785  */
786 int
nfsm_chain_get_opaque_f(struct nfsm_chain * nmc,size_t len,u_char * buf)787 nfsm_chain_get_opaque_f(struct nfsm_chain *nmc, size_t len, u_char *buf)
788 {
789 	size_t cplen, padlen;
790 	int error = 0;
791 
792 	padlen = nfsm_rndup(len) - len;
793 
794 	/* loop through mbufs copying all the data we need */
795 	while (len && nmc->nmc_mcur) {
796 		/* copy as much as we need/can */
797 		cplen = MIN(nmc->nmc_left, len);
798 		if (cplen) {
799 			bcopy(nmc->nmc_ptr, buf, cplen);
800 			nmc->nmc_ptr += cplen;
801 			nmc->nmc_left -= cplen;
802 			buf += cplen;
803 			len -= cplen;
804 		}
805 		/* if more needed, go to next mbuf */
806 		if (len) {
807 			mbuf_t mb = mbuf_next(nmc->nmc_mcur);
808 			nmc->nmc_mcur = mb;
809 			nmc->nmc_ptr = mb ? mbuf_data(mb) : NULL;
810 			nmc->nmc_left = mb ? mbuf_len(mb) : 0;
811 		}
812 	}
813 
814 	/* did we run out of data in the mbuf chain? */
815 	if (len) {
816 		return EBADRPC;
817 	}
818 
819 	if (padlen) {
820 		nfsm_chain_adv(error, nmc, padlen);
821 	}
822 
823 	return error;
824 }
825 
826 /*
827  * nfsm_chain_get_uio()
828  *
829  * Read the next "len" bytes in the chain into the given uio.
830  * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
831  */
832 int
nfsm_chain_get_uio(struct nfsm_chain * nmc,size_t len,uio_t uio)833 nfsm_chain_get_uio(struct nfsm_chain *nmc, size_t len, uio_t uio)
834 {
835 	size_t cplen, padlen;
836 	int error = 0;
837 
838 	padlen = nfsm_rndup(len) - len;
839 
840 	/* loop through mbufs copying all the data we need */
841 	while (len && nmc->nmc_mcur) {
842 		/* copy as much as we need/can */
843 		cplen = MIN(nmc->nmc_left, len);
844 		if (cplen) {
845 			cplen = MIN(cplen, INT32_MAX);
846 			error = uiomove(nmc->nmc_ptr, (int)cplen, uio);
847 			if (error) {
848 				return error;
849 			}
850 			nmc->nmc_ptr += cplen;
851 			nmc->nmc_left -= cplen;
852 			len -= cplen;
853 		}
854 		/* if more needed, go to next mbuf */
855 		if (len) {
856 			mbuf_t mb = mbuf_next(nmc->nmc_mcur);
857 			nmc->nmc_mcur = mb;
858 			nmc->nmc_ptr = mb ? mbuf_data(mb) : NULL;
859 			nmc->nmc_left = mb ? mbuf_len(mb) : 0;
860 		}
861 	}
862 
863 	/* did we run out of data in the mbuf chain? */
864 	if (len) {
865 		return EBADRPC;
866 	}
867 
868 	if (padlen) {
869 		nfsm_chain_adv(error, nmc, padlen);
870 	}
871 
872 	return error;
873 }
874 
875 /*
876  * Schedule a callout thread to run an NFS timer function
877  * interval milliseconds in the future.
878  */
879 void
nfs_interval_timer_start(thread_call_t call,time_t interval)880 nfs_interval_timer_start(thread_call_t call, time_t interval)
881 {
882 	uint64_t deadline;
883 
884 	clock_interval_to_deadline((int)interval, 1000 * 1000, &deadline);
885 	thread_call_enter_delayed(call, deadline);
886 }
887 
888 int nfsrv_cmp_secflavs(struct nfs_sec *, struct nfs_sec *);
889 int nfsrv_hang_addrlist(struct nfs_export *, struct user_nfs_export_args *);
890 int nfsrv_free_netopt(struct radix_node *, void *);
891 int nfsrv_free_addrlist(struct nfs_export *, struct user_nfs_export_args *);
892 struct nfs_export_options *nfsrv_export_lookup(struct nfs_export *, mbuf_t);
893 struct nfs_export *nfsrv_fhtoexport(struct nfs_filehandle *);
894 struct nfs_user_stat_node *nfsrv_get_user_stat_node(struct nfs_active_user_list *, struct sockaddr *, uid_t);
895 void nfsrv_init_user_list(struct nfs_active_user_list *);
896 void nfsrv_free_user_list(struct nfs_active_user_list *);
897 
898 /*
899  * add NFSv3 WCC data to an mbuf chain
900  */
901 int
nfsm_chain_add_wcc_data_f(struct nfsrv_descript * nd,struct nfsm_chain * nmc,int preattrerr,struct vnode_attr * prevap,int postattrerr,struct vnode_attr * postvap)902 nfsm_chain_add_wcc_data_f(
903 	struct nfsrv_descript *nd,
904 	struct nfsm_chain *nmc,
905 	int preattrerr,
906 	struct vnode_attr *prevap,
907 	int postattrerr,
908 	struct vnode_attr *postvap)
909 {
910 	int error = 0;
911 
912 	if (preattrerr) {
913 		nfsm_chain_add_32(error, nmc, FALSE);
914 	} else {
915 		nfsm_chain_add_32(error, nmc, TRUE);
916 		nfsm_chain_add_64(error, nmc, prevap->va_data_size);
917 		nfsm_chain_add_time(error, nmc, NFS_VER3, &prevap->va_modify_time);
918 		nfsm_chain_add_time(error, nmc, NFS_VER3, &prevap->va_change_time);
919 	}
920 	nfsm_chain_add_postop_attr(error, nd, nmc, postattrerr, postvap);
921 
922 	return error;
923 }
924 
925 /*
926  * Extract a lookup path from the given mbufs and store it in
927  * a newly allocated buffer saved in the given nameidata structure.
928  */
929 int
nfsm_chain_get_path_namei(struct nfsm_chain * nmc,uint32_t len,struct nameidata * nip)930 nfsm_chain_get_path_namei(
931 	struct nfsm_chain *nmc,
932 	uint32_t len,
933 	struct nameidata *nip)
934 {
935 	struct componentname *cnp = &nip->ni_cnd;
936 	int error = 0;
937 	char *cp;
938 
939 	if (len > (MAXPATHLEN - 1)) {
940 		return ENAMETOOLONG;
941 	}
942 
943 	/*
944 	 * Get a buffer for the name to be translated, and copy the
945 	 * name into the buffer.
946 	 */
947 	cnp->cn_pnbuf = zalloc(ZV_NAMEI);
948 	cnp->cn_pnlen = MAXPATHLEN;
949 	cnp->cn_flags |= HASBUF;
950 
951 	/* Copy the name from the mbuf list to the string */
952 	cp = cnp->cn_pnbuf;
953 	nfsm_chain_get_opaque(error, nmc, len, cp);
954 	if (error) {
955 		goto out;
956 	}
957 	cnp->cn_pnbuf[len] = '\0';
958 
959 	/* sanity check the string */
960 	if ((strlen(cp) != len) || strchr(cp, '/')) {
961 		error = EACCES;
962 	}
963 out:
964 	if (error) {
965 		if (cnp->cn_pnbuf) {
966 			NFS_ZFREE(ZV_NAMEI, cnp->cn_pnbuf);
967 		}
968 		cnp->cn_flags &= ~HASBUF;
969 	} else {
970 		nip->ni_pathlen = len;
971 	}
972 	return error;
973 }
974 
975 /*
976  * Set up nameidata for a lookup() call and do it.
977  */
978 int
nfsrv_namei(struct nfsrv_descript * nd,vfs_context_t ctx,struct nameidata * nip,struct nfs_filehandle * nfhp,vnode_t * retdirp,struct nfs_export ** nxp,struct nfs_export_options ** nxop)979 nfsrv_namei(
980 	struct nfsrv_descript *nd,
981 	vfs_context_t ctx,
982 	struct nameidata *nip,
983 	struct nfs_filehandle *nfhp,
984 	vnode_t *retdirp,
985 	struct nfs_export **nxp,
986 	struct nfs_export_options **nxop)
987 {
988 	vnode_t dp;
989 	int error;
990 	struct componentname *cnp = &nip->ni_cnd;
991 	uint32_t cnflags;
992 	char *tmppn;
993 
994 	*retdirp = NULL;
995 
996 	/*
997 	 * Extract and set starting directory.
998 	 */
999 	error = nfsrv_fhtovp(nfhp, nd, &dp, nxp, nxop);
1000 	if (error) {
1001 		goto out;
1002 	}
1003 	error = nfsrv_credcheck(nd, ctx, *nxp, *nxop);
1004 	if (error || (vnode_vtype(dp) != VDIR)) {
1005 		vnode_put(dp);
1006 		error = ENOTDIR;
1007 		goto out;
1008 	}
1009 	*retdirp = dp;
1010 
1011 	nip->ni_cnd.cn_context = ctx;
1012 
1013 	if (*nxop && ((*nxop)->nxo_flags & NX_READONLY)) {
1014 		cnp->cn_flags |= RDONLY;
1015 	}
1016 
1017 	cnp->cn_flags |= NOCROSSMOUNT;
1018 	cnp->cn_nameptr = cnp->cn_pnbuf;
1019 	nip->ni_usedvp = nip->ni_startdir = dp;
1020 	nip->ni_rootdir = rootvnode;
1021 
1022 	/*
1023 	 * And call lookup() to do the real work
1024 	 */
1025 	cnflags = nip->ni_cnd.cn_flags; /* store in case we have to restore */
1026 	while ((error = lookup(nip)) == ERECYCLE) {
1027 		nip->ni_cnd.cn_flags = cnflags;
1028 		cnp->cn_nameptr = cnp->cn_pnbuf;
1029 		nip->ni_usedvp = nip->ni_dvp = nip->ni_startdir = dp;
1030 	}
1031 	if (error) {
1032 		goto out;
1033 	}
1034 
1035 	/* Check for encountering a symbolic link */
1036 	if (cnp->cn_flags & ISSYMLINK) {
1037 		if (cnp->cn_flags & (LOCKPARENT | WANTPARENT)) {
1038 			vnode_put(nip->ni_dvp);
1039 		}
1040 		if (nip->ni_vp) {
1041 			vnode_put(nip->ni_vp);
1042 			nip->ni_vp = NULL;
1043 		}
1044 		error = EINVAL;
1045 	}
1046 out:
1047 	if (error) {
1048 		tmppn = cnp->cn_pnbuf;
1049 		cnp->cn_pnbuf = NULL;
1050 		cnp->cn_flags &= ~HASBUF;
1051 		NFS_ZFREE(ZV_NAMEI, tmppn);
1052 	}
1053 	return error;
1054 }
1055 
1056 /*
1057  * A fiddled version of m_adj() that ensures null fill to a 4-byte
1058  * boundary and only trims off the back end
1059  */
1060 void
nfsm_adj(mbuf_t mp,int len,int nul)1061 nfsm_adj(mbuf_t mp, int len, int nul)
1062 {
1063 	mbuf_t m, mnext;
1064 	int count, i;
1065 	long mlen;
1066 	char *cp;
1067 
1068 	/*
1069 	 * Trim from tail.  Scan the mbuf chain,
1070 	 * calculating its length and finding the last mbuf.
1071 	 * If the adjustment only affects this mbuf, then just
1072 	 * adjust and return.  Otherwise, rescan and truncate
1073 	 * after the remaining size.
1074 	 */
1075 	count = 0;
1076 	m = mp;
1077 	for (;;) {
1078 		mlen = mbuf_len(m);
1079 		count += mlen;
1080 		mnext = mbuf_next(m);
1081 		if (mnext == NULL) {
1082 			break;
1083 		}
1084 		m = mnext;
1085 	}
1086 	if (mlen > len) {
1087 		mlen -= len;
1088 		mbuf_setlen(m, mlen);
1089 		if (nul > 0) {
1090 			cp = (caddr_t)mbuf_data(m) + mlen - nul;
1091 			for (i = 0; i < nul; i++) {
1092 				*cp++ = '\0';
1093 			}
1094 		}
1095 		return;
1096 	}
1097 	count -= len;
1098 	if (count < 0) {
1099 		count = 0;
1100 	}
1101 	/*
1102 	 * Correct length for chain is "count".
1103 	 * Find the mbuf with last data, adjust its length,
1104 	 * and toss data from remaining mbufs on chain.
1105 	 */
1106 	for (m = mp; m; m = mbuf_next(m)) {
1107 		mlen = mbuf_len(m);
1108 		if (mlen >= count) {
1109 			mlen = count;
1110 			mbuf_setlen(m, count);
1111 			if (nul > 0) {
1112 				cp = (caddr_t)mbuf_data(m) + mlen - nul;
1113 				for (i = 0; i < nul; i++) {
1114 					*cp++ = '\0';
1115 				}
1116 			}
1117 			break;
1118 		}
1119 		count -= mlen;
1120 	}
1121 	for (m = mbuf_next(m); m; m = mbuf_next(m)) {
1122 		mbuf_setlen(m, 0);
1123 	}
1124 }
1125 
1126 /*
1127  * Trim the header out of the mbuf list and trim off any trailing
1128  * junk so that the mbuf list has only the write data.
1129  */
1130 int
nfsm_chain_trim_data(struct nfsm_chain * nmc,int len,int * mlen)1131 nfsm_chain_trim_data(struct nfsm_chain *nmc, int len, int *mlen)
1132 {
1133 	int cnt = 0;
1134 	long dlen, adjust;
1135 	caddr_t data;
1136 	mbuf_t m;
1137 
1138 	if (mlen) {
1139 		*mlen = 0;
1140 	}
1141 
1142 	/* trim header */
1143 	for (m = nmc->nmc_mhead; m && (m != nmc->nmc_mcur); m = mbuf_next(m)) {
1144 		mbuf_setlen(m, 0);
1145 	}
1146 	if (!m) {
1147 		return EIO;
1148 	}
1149 
1150 	/* trim current mbuf */
1151 	data = mbuf_data(m);
1152 	dlen = mbuf_len(m);
1153 	adjust = nmc->nmc_ptr - data;
1154 	dlen -= adjust;
1155 	if ((dlen > 0) && (adjust > 0)) {
1156 		if (mbuf_setdata(m, nmc->nmc_ptr, dlen)) {
1157 			return EIO;
1158 		}
1159 	} else {
1160 		mbuf_setlen(m, dlen);
1161 	}
1162 
1163 	/* skip next len bytes  */
1164 	for (; m && (cnt < len); m = mbuf_next(m)) {
1165 		dlen = mbuf_len(m);
1166 		cnt += dlen;
1167 		if (cnt > len) {
1168 			/* truncate to end of data */
1169 			mbuf_setlen(m, dlen - (cnt - len));
1170 			if (m == nmc->nmc_mcur) {
1171 				nmc->nmc_left -= (cnt - len);
1172 			}
1173 			cnt = len;
1174 		}
1175 	}
1176 	if (mlen) {
1177 		*mlen = cnt;
1178 	}
1179 
1180 	/* trim any trailing data */
1181 	if (m == nmc->nmc_mcur) {
1182 		nmc->nmc_left = 0;
1183 	}
1184 	for (; m; m = mbuf_next(m)) {
1185 		mbuf_setlen(m, 0);
1186 	}
1187 
1188 	return 0;
1189 }
1190 
1191 int
nfsm_chain_add_fattr(struct nfsrv_descript * nd,struct nfsm_chain * nmc,struct vnode_attr * vap)1192 nfsm_chain_add_fattr(
1193 	struct nfsrv_descript *nd,
1194 	struct nfsm_chain *nmc,
1195 	struct vnode_attr *vap)
1196 {
1197 	int error = 0;
1198 
1199 	// XXX Should we assert here that all fields are supported?
1200 
1201 	nfsm_chain_add_32(error, nmc, vtonfs_type(vap->va_type, nd->nd_vers));
1202 	if (nd->nd_vers == NFS_VER3) {
1203 		nfsm_chain_add_32(error, nmc, vap->va_mode & 07777);
1204 	} else {
1205 		nfsm_chain_add_32(error, nmc, vtonfsv2_mode(vap->va_type, vap->va_mode));
1206 	}
1207 	nfsm_chain_add_32(error, nmc, vap->va_nlink);
1208 	nfsm_chain_add_32(error, nmc, vap->va_uid);
1209 	nfsm_chain_add_32(error, nmc, vap->va_gid);
1210 	if (nd->nd_vers == NFS_VER3) {
1211 		nfsm_chain_add_64(error, nmc, vap->va_data_size);
1212 		nfsm_chain_add_64(error, nmc, vap->va_data_alloc);
1213 		nfsm_chain_add_32(error, nmc, major(vap->va_rdev));
1214 		nfsm_chain_add_32(error, nmc, minor(vap->va_rdev));
1215 		nfsm_chain_add_64(error, nmc, vap->va_fsid);
1216 		nfsm_chain_add_64(error, nmc, vap->va_fileid);
1217 	} else {
1218 		nfsm_chain_add_32(error, nmc, vap->va_data_size);
1219 		nfsm_chain_add_32(error, nmc, NFS_FABLKSIZE);
1220 		if (vap->va_type == VFIFO) {
1221 			nfsm_chain_add_32(error, nmc, 0xffffffff);
1222 		} else {
1223 			nfsm_chain_add_32(error, nmc, vap->va_rdev);
1224 		}
1225 		nfsm_chain_add_32(error, nmc, vap->va_data_alloc / NFS_FABLKSIZE);
1226 		nfsm_chain_add_32(error, nmc, vap->va_fsid);
1227 		nfsm_chain_add_32(error, nmc, vap->va_fileid);
1228 	}
1229 	nfsm_chain_add_time(error, nmc, nd->nd_vers, &vap->va_access_time);
1230 	nfsm_chain_add_time(error, nmc, nd->nd_vers, &vap->va_modify_time);
1231 	nfsm_chain_add_time(error, nmc, nd->nd_vers, &vap->va_change_time);
1232 
1233 	return error;
1234 }
1235 
1236 int
nfsm_chain_get_sattr(struct nfsrv_descript * nd,struct nfsm_chain * nmc,struct vnode_attr * vap)1237 nfsm_chain_get_sattr(
1238 	struct nfsrv_descript *nd,
1239 	struct nfsm_chain *nmc,
1240 	struct vnode_attr *vap)
1241 {
1242 	int error = 0;
1243 	uint32_t val = 0;
1244 	uint64_t val64 = 0;
1245 	struct timespec now;
1246 
1247 	if (nd->nd_vers == NFS_VER2) {
1248 		/*
1249 		 * There is/was a bug in the Sun client that puts 0xffff in the mode
1250 		 * field of sattr when it should put in 0xffffffff.  The u_short
1251 		 * doesn't sign extend.  So check the low order 2 bytes for 0xffff.
1252 		 */
1253 		nfsm_chain_get_32(error, nmc, val);
1254 		if ((val & 0xffff) != 0xffff) {
1255 			VATTR_SET(vap, va_mode, val & 07777);
1256 			/* save the "type" bits for NFSv2 create */
1257 			VATTR_SET(vap, va_type, IFTOVT(val));
1258 			VATTR_CLEAR_ACTIVE(vap, va_type);
1259 		}
1260 		nfsm_chain_get_32(error, nmc, val);
1261 		if (val != (uint32_t)-1) {
1262 			VATTR_SET(vap, va_uid, val);
1263 		}
1264 		nfsm_chain_get_32(error, nmc, val);
1265 		if (val != (uint32_t)-1) {
1266 			VATTR_SET(vap, va_gid, val);
1267 		}
1268 		/* save the "size" bits for NFSv2 create (even if they appear unset) */
1269 		nfsm_chain_get_32(error, nmc, val);
1270 		VATTR_SET(vap, va_data_size, val);
1271 		if (val == (uint32_t)-1) {
1272 			VATTR_CLEAR_ACTIVE(vap, va_data_size);
1273 		}
1274 		nfsm_chain_get_time(error, nmc, NFS_VER2,
1275 		    vap->va_access_time.tv_sec,
1276 		    vap->va_access_time.tv_nsec);
1277 		if (vap->va_access_time.tv_sec != -1) {
1278 			VATTR_SET_ACTIVE(vap, va_access_time);
1279 		}
1280 		nfsm_chain_get_time(error, nmc, NFS_VER2,
1281 		    vap->va_modify_time.tv_sec,
1282 		    vap->va_modify_time.tv_nsec);
1283 		if (vap->va_modify_time.tv_sec != -1) {
1284 			VATTR_SET_ACTIVE(vap, va_modify_time);
1285 		}
1286 		return error;
1287 	}
1288 
1289 	/* NFSv3 */
1290 	nfsm_chain_get_32(error, nmc, val);
1291 	if (val) {
1292 		nfsm_chain_get_32(error, nmc, val);
1293 		VATTR_SET(vap, va_mode, val & 07777);
1294 	}
1295 	nfsm_chain_get_32(error, nmc, val);
1296 	if (val) {
1297 		nfsm_chain_get_32(error, nmc, val);
1298 		VATTR_SET(vap, va_uid, val);
1299 	}
1300 	nfsm_chain_get_32(error, nmc, val);
1301 	if (val) {
1302 		nfsm_chain_get_32(error, nmc, val);
1303 		VATTR_SET(vap, va_gid, val);
1304 	}
1305 	nfsm_chain_get_32(error, nmc, val);
1306 	if (val) {
1307 		nfsm_chain_get_64(error, nmc, val64);
1308 		VATTR_SET(vap, va_data_size, val64);
1309 	}
1310 	nanotime(&now);
1311 	nfsm_chain_get_32(error, nmc, val);
1312 	switch (val) {
1313 	case NFS_TIME_SET_TO_CLIENT:
1314 		nfsm_chain_get_time(error, nmc, nd->nd_vers,
1315 		    vap->va_access_time.tv_sec,
1316 		    vap->va_access_time.tv_nsec);
1317 		VATTR_SET_ACTIVE(vap, va_access_time);
1318 		vap->va_vaflags &= ~VA_UTIMES_NULL;
1319 		break;
1320 	case NFS_TIME_SET_TO_SERVER:
1321 		VATTR_SET(vap, va_access_time, now);
1322 		vap->va_vaflags |= VA_UTIMES_NULL;
1323 		break;
1324 	}
1325 	nfsm_chain_get_32(error, nmc, val);
1326 	switch (val) {
1327 	case NFS_TIME_SET_TO_CLIENT:
1328 		nfsm_chain_get_time(error, nmc, nd->nd_vers,
1329 		    vap->va_modify_time.tv_sec,
1330 		    vap->va_modify_time.tv_nsec);
1331 		VATTR_SET_ACTIVE(vap, va_modify_time);
1332 		vap->va_vaflags &= ~VA_UTIMES_NULL;
1333 		break;
1334 	case NFS_TIME_SET_TO_SERVER:
1335 		VATTR_SET(vap, va_modify_time, now);
1336 		if (!VATTR_IS_ACTIVE(vap, va_access_time)) {
1337 			vap->va_vaflags |= VA_UTIMES_NULL;
1338 		}
1339 		break;
1340 	}
1341 
1342 	return error;
1343 }
1344 
1345 /*
1346  * Compare two security flavor structs
1347  */
1348 int
nfsrv_cmp_secflavs(struct nfs_sec * sf1,struct nfs_sec * sf2)1349 nfsrv_cmp_secflavs(struct nfs_sec *sf1, struct nfs_sec *sf2)
1350 {
1351 	int i;
1352 
1353 	if (sf1->count != sf2->count) {
1354 		return 1;
1355 	}
1356 	for (i = 0; i < sf1->count; i++) {
1357 		if (sf1->flavors[i] != sf2->flavors[i]) {
1358 			return 1;
1359 		}
1360 	}
1361 	return 0;
1362 }
1363 
1364 /*
1365  * Build hash lists of net addresses and hang them off the NFS export.
1366  * Called by nfsrv_export() to set up the lists of export addresses.
1367  */
1368 int
nfsrv_hang_addrlist(struct nfs_export * nx,struct user_nfs_export_args * unxa)1369 nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
1370 {
1371 	struct nfs_export_net_args nxna;
1372 	struct nfs_netopt *no, *rn_no;
1373 	struct radix_node_head *rnh;
1374 	struct radix_node *rn;
1375 	struct sockaddr *saddr, *smask;
1376 	struct domain *dom;
1377 	size_t i, ss_minsize;
1378 	int error;
1379 	unsigned int net;
1380 	user_addr_t uaddr;
1381 	kauth_cred_t cred;
1382 
1383 	uaddr = unxa->nxa_nets;
1384 	ss_minsize = sizeof(((struct sockaddr_storage *)0)->ss_len) + sizeof(((struct sockaddr_storage *)0)->ss_family);
1385 	for (net = 0; net < unxa->nxa_netcount; net++, uaddr += sizeof(nxna)) {
1386 		error = copyin(uaddr, &nxna, sizeof(nxna));
1387 		if (error) {
1388 			return error;
1389 		}
1390 
1391 		if (nxna.nxna_addr.ss_len > sizeof(struct sockaddr_storage) ||
1392 		    (nxna.nxna_addr.ss_len != 0 && nxna.nxna_addr.ss_len < ss_minsize) ||
1393 		    nxna.nxna_mask.ss_len > sizeof(struct sockaddr_storage) ||
1394 		    (nxna.nxna_mask.ss_len != 0 && nxna.nxna_mask.ss_len < ss_minsize) ||
1395 		    nxna.nxna_addr.ss_family > AF_MAX ||
1396 		    nxna.nxna_mask.ss_family > AF_MAX) {
1397 			return EINVAL;
1398 		}
1399 
1400 		if (nxna.nxna_flags & (NX_MAPROOT | NX_MAPALL)) {
1401 			struct posix_cred temp_pcred;
1402 			bzero(&temp_pcred, sizeof(temp_pcred));
1403 			temp_pcred.cr_uid = nxna.nxna_cred.cr_uid;
1404 			temp_pcred.cr_ngroups = nxna.nxna_cred.cr_ngroups;
1405 			for (i = 0; i < (size_t)nxna.nxna_cred.cr_ngroups && i < NGROUPS; i++) {
1406 				temp_pcred.cr_groups[i] = nxna.nxna_cred.cr_groups[i];
1407 			}
1408 			cred = posix_cred_create(&temp_pcred);
1409 			if (!IS_VALID_CRED(cred)) {
1410 				return ENOMEM;
1411 			}
1412 		} else {
1413 			cred = NOCRED;
1414 		}
1415 
1416 		if (nxna.nxna_addr.ss_len == 0) {
1417 			/* No address means this is a default/world export */
1418 			if (nx->nx_flags & NX_DEFAULTEXPORT) {
1419 				if (IS_VALID_CRED(cred)) {
1420 					kauth_cred_unref(&cred);
1421 				}
1422 				return EEXIST;
1423 			}
1424 			nx->nx_flags |= NX_DEFAULTEXPORT;
1425 			nx->nx_defopt.nxo_flags = nxna.nxna_flags;
1426 			nx->nx_defopt.nxo_cred = cred;
1427 			bcopy(&nxna.nxna_sec, &nx->nx_defopt.nxo_sec, sizeof(struct nfs_sec));
1428 			nx->nx_expcnt++;
1429 			continue;
1430 		}
1431 
1432 		no = kalloc_type(struct nfs_netopt, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1433 		no->no_opt.nxo_flags = nxna.nxna_flags;
1434 		no->no_opt.nxo_cred = cred;
1435 		bcopy(&nxna.nxna_sec, &no->no_opt.nxo_sec, sizeof(struct nfs_sec));
1436 
1437 		if (nxna.nxna_addr.ss_len) {
1438 			no->no_addr = kalloc_data(nxna.nxna_addr.ss_len, M_WAITOK);
1439 			bcopy(&nxna.nxna_addr, no->no_addr, nxna.nxna_addr.ss_len);
1440 		}
1441 		saddr = no->no_addr;
1442 
1443 		if (nxna.nxna_mask.ss_len) {
1444 			no->no_mask = kalloc_data(nxna.nxna_mask.ss_len, M_WAITOK);
1445 			bcopy(&nxna.nxna_mask, no->no_mask, nxna.nxna_mask.ss_len);
1446 		}
1447 		smask = no->no_mask;
1448 
1449 		sa_family_t family = saddr->sa_family;
1450 		if ((rnh = nx->nx_rtable[family]) == 0) {
1451 			/*
1452 			 * Seems silly to initialize every AF when most are not
1453 			 * used, do so on demand here
1454 			 */
1455 			TAILQ_FOREACH(dom, &domains, dom_entry) {
1456 				if (dom->dom_family == family && dom->dom_rtattach) {
1457 					dom->dom_rtattach((void **)&nx->nx_rtable[family],
1458 					    dom->dom_rtoffset);
1459 					break;
1460 				}
1461 			}
1462 			if ((rnh = nx->nx_rtable[family]) == 0) {
1463 				if (IS_VALID_CRED(cred)) {
1464 					kauth_cred_unref(&cred);
1465 				}
1466 				nfs_netopt_free(no);
1467 				return ENOBUFS;
1468 			}
1469 		}
1470 		rn = (*rnh->rnh_addaddr)((caddr_t)saddr, (caddr_t)smask, rnh, no->no_rnodes);
1471 		if (rn == 0) {
1472 			/*
1473 			 * One of the reasons that rnh_addaddr may fail is that
1474 			 * the entry already exists. To check for this case, we
1475 			 * look up the entry to see if it is there. If so, we
1476 			 * do not need to make a new entry but do continue.
1477 			 *
1478 			 * XXX should this be rnh_lookup() instead?
1479 			 */
1480 			int matched = 0;
1481 			rn = (*rnh->rnh_matchaddr)((caddr_t)saddr, rnh);
1482 			rn_no = (struct nfs_netopt *)rn;
1483 			if (rn != 0 && (rn->rn_flags & RNF_ROOT) == 0 &&
1484 			    (rn_no->no_opt.nxo_flags == nxna.nxna_flags) &&
1485 			    (!nfsrv_cmp_secflavs(&rn_no->no_opt.nxo_sec, &nxna.nxna_sec))) {
1486 				kauth_cred_t cred2 = rn_no->no_opt.nxo_cred;
1487 				if (cred == cred2) {
1488 					/* creds are same (or both NULL) */
1489 					matched = 1;
1490 				} else if (cred && cred2 && (kauth_cred_getuid(cred) == kauth_cred_getuid(cred2))) {
1491 					/*
1492 					 * Now compare the effective and
1493 					 * supplementary groups...
1494 					 *
1495 					 * Note: This comparison, as written,
1496 					 * does not correctly indicate that
1497 					 * the groups are equivalent, since
1498 					 * other than the first supplementary
1499 					 * group, which is also the effective
1500 					 * group, order on the remaining groups
1501 					 * doesn't matter, and this is an
1502 					 * ordered compare.
1503 					 */
1504 					gid_t groups[NGROUPS];
1505 					gid_t groups2[NGROUPS];
1506 					size_t groupcount = NGROUPS;
1507 					size_t group2count = NGROUPS;
1508 
1509 					if (!kauth_cred_getgroups(cred, groups, &groupcount) &&
1510 					    !kauth_cred_getgroups(cred2, groups2, &group2count) &&
1511 					    groupcount == group2count) {
1512 						for (i = 0; i < group2count; i++) {
1513 							if (groups[i] != groups2[i]) {
1514 								break;
1515 							}
1516 						}
1517 						if (i >= group2count || i >= NGROUPS) {
1518 							matched = 1;
1519 						}
1520 					}
1521 				}
1522 			}
1523 			if (IS_VALID_CRED(cred)) {
1524 				kauth_cred_unref(&cred);
1525 			}
1526 			nfs_netopt_free(no);
1527 			if (matched) {
1528 				continue;
1529 			}
1530 			return EPERM;
1531 		}
1532 		nx->nx_expcnt++;
1533 	}
1534 
1535 	return 0;
1536 }
1537 
1538 /*
1539  * In order to properly track an export's netopt count, we need to pass
1540  * an additional argument to nfsrv_free_netopt() so that it can decrement
1541  * the export's netopt count.
1542  */
1543 struct nfsrv_free_netopt_arg {
1544 	uint32_t *cnt;
1545 	struct radix_node_head *rnh;
1546 };
1547 
1548 int
nfsrv_free_netopt(struct radix_node * rn,void * w)1549 nfsrv_free_netopt(struct radix_node *rn, void *w)
1550 {
1551 	struct nfsrv_free_netopt_arg *fna = (struct nfsrv_free_netopt_arg *)w;
1552 	struct radix_node_head *rnh = fna->rnh;
1553 	uint32_t *cnt = fna->cnt;
1554 	struct nfs_netopt *nno = (struct nfs_netopt *)rn;
1555 
1556 	(*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh);
1557 	if (IS_VALID_CRED(nno->no_opt.nxo_cred)) {
1558 		kauth_cred_unref(&nno->no_opt.nxo_cred);
1559 	}
1560 	nfs_netopt_free(nno);
1561 	*cnt -= 1;
1562 	return 0;
1563 }
1564 
1565 /*
1566  * Free the net address hash lists that are hanging off the mount points.
1567  */
1568 int
nfsrv_free_addrlist(struct nfs_export * nx,struct user_nfs_export_args * unxa)1569 nfsrv_free_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
1570 {
1571 	struct nfs_export_net_args nxna;
1572 	struct radix_node_head *rnh;
1573 	struct radix_node *rn;
1574 	struct nfsrv_free_netopt_arg fna;
1575 	struct nfs_netopt *nno;
1576 	size_t ss_minsize;
1577 	user_addr_t uaddr;
1578 	unsigned int net;
1579 	int i, error;
1580 
1581 	if (!unxa || !unxa->nxa_netcount) {
1582 		/* delete everything */
1583 		for (i = 0; i <= AF_MAX; i++) {
1584 			if ((rnh = nx->nx_rtable[i])) {
1585 				fna.rnh = rnh;
1586 				fna.cnt = &nx->nx_expcnt;
1587 				(*rnh->rnh_walktree)(rnh, nfsrv_free_netopt, (caddr_t)&fna);
1588 				zfree(radix_node_head_zone, rnh);
1589 				nx->nx_rtable[i] = 0;
1590 			}
1591 		}
1592 		return 0;
1593 	}
1594 
1595 	/* delete only the exports specified */
1596 	uaddr = unxa->nxa_nets;
1597 	ss_minsize = sizeof(((struct sockaddr_storage *)0)->ss_len) + sizeof(((struct sockaddr_storage *)0)->ss_family);
1598 	for (net = 0; net < unxa->nxa_netcount; net++, uaddr += sizeof(nxna)) {
1599 		error = copyin(uaddr, &nxna, sizeof(nxna));
1600 		if (error) {
1601 			return error;
1602 		}
1603 
1604 		if (nxna.nxna_addr.ss_len == 0) {
1605 			/* No address means this is a default/world export */
1606 			if (nx->nx_flags & NX_DEFAULTEXPORT) {
1607 				nx->nx_flags &= ~NX_DEFAULTEXPORT;
1608 				if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
1609 					kauth_cred_unref(&nx->nx_defopt.nxo_cred);
1610 				}
1611 				nx->nx_expcnt--;
1612 			}
1613 			continue;
1614 		}
1615 
1616 		if (nxna.nxna_addr.ss_len > sizeof(struct sockaddr_storage) ||
1617 		    (nxna.nxna_addr.ss_len != 0 && nxna.nxna_addr.ss_len < ss_minsize) ||
1618 		    nxna.nxna_addr.ss_family > AF_MAX) {
1619 			printf("nfsrv_free_addrlist: invalid socket address (%u)\n", net);
1620 			continue;
1621 		}
1622 
1623 		if (nxna.nxna_mask.ss_len > sizeof(struct sockaddr_storage) ||
1624 		    (nxna.nxna_mask.ss_len != 0 && nxna.nxna_mask.ss_len < ss_minsize) ||
1625 		    nxna.nxna_mask.ss_family > AF_MAX) {
1626 			printf("nfsrv_free_addrlist: invalid socket mask (%u)\n", net);
1627 			continue;
1628 		}
1629 
1630 		if ((rnh = nx->nx_rtable[nxna.nxna_addr.ss_family]) == 0) {
1631 			/* AF not initialized? */
1632 			if (!(unxa->nxa_flags & NXA_ADD)) {
1633 				printf("nfsrv_free_addrlist: address not found (0)\n");
1634 			}
1635 			continue;
1636 		}
1637 
1638 		rn = (*rnh->rnh_lookup)(&nxna.nxna_addr,
1639 		    nxna.nxna_mask.ss_len ? &nxna.nxna_mask : NULL, rnh);
1640 		if (!rn || (rn->rn_flags & RNF_ROOT)) {
1641 			if (!(unxa->nxa_flags & NXA_ADD)) {
1642 				printf("nfsrv_free_addrlist: address not found (1)\n");
1643 			}
1644 			continue;
1645 		}
1646 
1647 		(*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh);
1648 		nno = (struct nfs_netopt *)rn;
1649 		if (IS_VALID_CRED(nno->no_opt.nxo_cred)) {
1650 			kauth_cred_unref(&nno->no_opt.nxo_cred);
1651 		}
1652 		nfs_netopt_free(nno);
1653 
1654 		nx->nx_expcnt--;
1655 		if (nx->nx_expcnt == ((nx->nx_flags & NX_DEFAULTEXPORT) ? 1 : 0)) {
1656 			/* no more entries in rnh, so free it up */
1657 			zfree(radix_node_head_zone, rnh);
1658 			nx->nx_rtable[nxna.nxna_addr.ss_family] = 0;
1659 		}
1660 	}
1661 
1662 	return 0;
1663 }
1664 
1665 void enablequotas(struct mount *mp, vfs_context_t ctx); // XXX
1666 
1667 #define DATA_VOLUME_MP "/System/Volumes/Data" // PLATFORM_DATA_VOLUME_MOUNT_POINT
1668 
1669 int
nfsrv_export(struct user_nfs_export_args * unxa,vfs_context_t ctx)1670 nfsrv_export(struct user_nfs_export_args *unxa, vfs_context_t ctx)
1671 {
1672 	int error = 0;
1673 	size_t pathlen, nxfs_pathlen;
1674 	struct nfs_exportfs *nxfs, *nxfs2, *nxfs3;
1675 	struct nfs_export *nx, *nx2, *nx3;
1676 	struct nfs_filehandle nfh;
1677 	struct nameidata mnd, xnd;
1678 	vnode_t mvp = NULL, xvp = NULL;
1679 	mount_t mp = NULL;
1680 	char path[MAXPATHLEN], *nxfs_path;
1681 	char fl_pathbuff[MAXPATHLEN];
1682 	size_t fl_pathbuff_len = MAXPATHLEN;
1683 	int expisroot;
1684 	size_t datavol_len = strlen(DATA_VOLUME_MP);
1685 
1686 	if (unxa->nxa_flags == NXA_CHECK) {
1687 		/* just check if the path is an NFS-exportable file system */
1688 		error = copyinstr(unxa->nxa_fspath, path, MAXPATHLEN, &pathlen);
1689 		if (error) {
1690 			return error;
1691 		}
1692 		NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
1693 		    UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
1694 		error = namei(&mnd);
1695 		if (error) {
1696 			return error;
1697 		}
1698 		mvp = mnd.ni_vp;
1699 		mp = vnode_mount(mvp);
1700 		/* make sure it's the root of a file system */
1701 		if (!vnode_isvroot(mvp)) {
1702 			error = EINVAL;
1703 		}
1704 		/* make sure the file system is NFS-exportable */
1705 		if (!error) {
1706 			nfh.nfh_len = NFSV3_MAX_FID_SIZE;
1707 			error = VFS_VPTOFH(mvp, (int*)&nfh.nfh_len, &nfh.nfh_fid[0], NULL);
1708 		}
1709 		if (!error && (nfh.nfh_len > (int)NFSV3_MAX_FID_SIZE)) {
1710 			error = EIO;
1711 		}
1712 		if (!error && !(mp->mnt_vtable->vfc_vfsflags & VFC_VFSREADDIR_EXTENDED)) {
1713 			error = EISDIR;
1714 		}
1715 		vnode_put(mvp);
1716 		nameidone(&mnd);
1717 		return error;
1718 	}
1719 
1720 	/* all other operations: must be super user */
1721 	if ((error = vfs_context_suser(ctx))) {
1722 		return error;
1723 	}
1724 
1725 	if (unxa->nxa_flags & NXA_DELETE_ALL) {
1726 		/* delete all exports on all file systems */
1727 		lck_rw_lock_exclusive(&nfsrv_export_rwlock);
1728 		while ((nxfs = LIST_FIRST(&nfsrv_exports))) {
1729 			mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
1730 			if (mp) {
1731 				vfs_clearflags(mp, MNT_EXPORTED);
1732 				mount_iterdrop(mp);
1733 				mp = NULL;
1734 			}
1735 			/* delete all exports on this file system */
1736 			while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
1737 				LIST_REMOVE(nx, nx_next);
1738 				LIST_REMOVE(nx, nx_hash);
1739 				/* delete all netopts for this export */
1740 				nfsrv_free_addrlist(nx, NULL);
1741 				nx->nx_flags &= ~NX_DEFAULTEXPORT;
1742 				if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
1743 					kauth_cred_unref(&nx->nx_defopt.nxo_cred);
1744 				}
1745 				/* free active user list for this export */
1746 				nfsrv_free_user_list(&nx->nx_user_list);
1747 				kfree_data_addr(nx->nx_path);
1748 				kfree_type(struct nfs_export, nx);
1749 			}
1750 			LIST_REMOVE(nxfs, nxfs_next);
1751 			kfree_data_addr(nxfs->nxfs_path);
1752 			kfree_type(struct nfs_exportfs, nxfs);
1753 		}
1754 		if (nfsrv_export_hashtbl) {
1755 			/* all exports deleted, clean up export hash table */
1756 			hashdestroy(nfsrv_export_hashtbl, M_TEMP, nfsrv_export_hash);
1757 			nfsrv_export_hash = 0;
1758 			nfsrv_export_hashtbl = NULL;
1759 		}
1760 		lck_rw_done(&nfsrv_export_rwlock);
1761 		return 0;
1762 	}
1763 
1764 	error = copyinstr(unxa->nxa_fspath, path, MAXPATHLEN, &pathlen);
1765 	if (error) {
1766 		return error;
1767 	}
1768 
1769 	lck_rw_lock_exclusive(&nfsrv_export_rwlock);
1770 
1771 	/* init export hash table if not already */
1772 	if (!nfsrv_export_hashtbl) {
1773 		if (nfsrv_export_hash_size <= 0) {
1774 			nfsrv_export_hash_size = NFSRVEXPHASHSZ;
1775 		}
1776 		nfsrv_export_hashtbl = hashinit(nfsrv_export_hash_size, M_TEMP, &nfsrv_export_hash);
1777 	}
1778 
1779 	// first check if we've already got an exportfs with the given ID
1780 	LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
1781 		if (nxfs->nxfs_id == unxa->nxa_fsid) {
1782 			break;
1783 		}
1784 	}
1785 	if (nxfs) {
1786 		/* verify exported FS path matches given path */
1787 		if (strncmp(path, nxfs->nxfs_path, MAXPATHLEN) &&
1788 		    (strncmp(path, DATA_VOLUME_MP, datavol_len) || strncmp(path + datavol_len, nxfs->nxfs_path, MAXPATHLEN - datavol_len))) {
1789 			error = EEXIST;
1790 			goto unlock_out;
1791 		}
1792 		if ((unxa->nxa_flags & (NXA_ADD | NXA_OFFLINE)) == NXA_ADD) {
1793 			/* find exported FS root vnode */
1794 			NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
1795 			    UIO_SYSSPACE, CAST_USER_ADDR_T(nxfs->nxfs_path), ctx);
1796 			error = namei(&mnd);
1797 			if (error) {
1798 				goto unlock_out;
1799 			}
1800 			mvp = mnd.ni_vp;
1801 			/* make sure it's (still) the root of a file system */
1802 			if (!vnode_isvroot(mvp)) {
1803 				error = EINVAL;
1804 				goto out;
1805 			}
1806 			/* if adding, verify that the mount is still what we expect */
1807 			mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
1808 			if (!mp) {
1809 				/* check for firmlink-free path */
1810 				if (vn_getpath_ext(mvp, NULLVP, fl_pathbuff, &fl_pathbuff_len, VN_GETPATH_NO_FIRMLINK) == 0 &&
1811 				    fl_pathbuff_len > 0 &&
1812 				    !strncmp(nxfs->nxfs_path, fl_pathbuff, MAXPATHLEN)) {
1813 					mp = vfs_getvfs_by_mntonname(vfs_statfs(vnode_mount(mvp))->f_mntonname);
1814 				}
1815 			}
1816 			if (mp) {
1817 				mount_ref(mp, 0);
1818 				mount_iterdrop(mp);
1819 			}
1820 			/* sanity check: this should be same mount */
1821 			if (mp != vnode_mount(mvp)) {
1822 				error = EINVAL;
1823 				goto out;
1824 			}
1825 		}
1826 	} else {
1827 		/* no current exported file system with that ID */
1828 		if (!(unxa->nxa_flags & NXA_ADD)) {
1829 			error = ENOENT;
1830 			goto unlock_out;
1831 		}
1832 
1833 		/* find exported FS root vnode */
1834 		NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
1835 		    UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
1836 		error = namei(&mnd);
1837 		if (error) {
1838 			if (!(unxa->nxa_flags & NXA_OFFLINE)) {
1839 				goto unlock_out;
1840 			}
1841 		} else {
1842 			mvp = mnd.ni_vp;
1843 			/* make sure it's the root of a file system */
1844 			if (!vnode_isvroot(mvp)) {
1845 				/* bail if not marked offline */
1846 				if (!(unxa->nxa_flags & NXA_OFFLINE)) {
1847 					error = EINVAL;
1848 					goto out;
1849 				}
1850 				vnode_put(mvp);
1851 				nameidone(&mnd);
1852 				mvp = NULL;
1853 			} else {
1854 				mp = vnode_mount(mvp);
1855 				mount_ref(mp, 0);
1856 
1857 				/* make sure the file system is NFS-exportable */
1858 				nfh.nfh_len = NFSV3_MAX_FID_SIZE;
1859 				error = VFS_VPTOFH(mvp, (int*)&nfh.nfh_len, &nfh.nfh_fid[0], NULL);
1860 				if (!error && (nfh.nfh_len > (int)NFSV3_MAX_FID_SIZE)) {
1861 					error = EIO;
1862 				}
1863 				if (!error && !(mp->mnt_vtable->vfc_vfsflags & VFC_VFSREADDIR_EXTENDED)) {
1864 					error = EISDIR;
1865 				}
1866 				if (error) {
1867 					goto out;
1868 				}
1869 			}
1870 		}
1871 
1872 		/* add an exportfs for it */
1873 		nxfs = kalloc_type(struct nfs_exportfs, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1874 		nxfs->nxfs_id = unxa->nxa_fsid;
1875 		if (mp) {
1876 			nxfs_path = mp->mnt_vfsstat.f_mntonname;
1877 			nxfs_pathlen = sizeof(mp->mnt_vfsstat.f_mntonname);
1878 		} else {
1879 			nxfs_path = path;
1880 			nxfs_pathlen = pathlen;
1881 		}
1882 		nxfs->nxfs_path = kalloc_data(nxfs_pathlen, Z_WAITOK);
1883 		if (!nxfs->nxfs_path) {
1884 			kfree_type(struct nfs_exportfs, nxfs);
1885 			error = ENOMEM;
1886 			goto out;
1887 		}
1888 		bcopy(nxfs_path, nxfs->nxfs_path, nxfs_pathlen);
1889 		/* insert into list in reverse-sorted order */
1890 		nxfs3 = NULL;
1891 		LIST_FOREACH(nxfs2, &nfsrv_exports, nxfs_next) {
1892 			if (strncmp(nxfs->nxfs_path, nxfs2->nxfs_path, MAXPATHLEN) > 0) {
1893 				break;
1894 			}
1895 			nxfs3 = nxfs2;
1896 		}
1897 		if (nxfs2) {
1898 			LIST_INSERT_BEFORE(nxfs2, nxfs, nxfs_next);
1899 		} else if (nxfs3) {
1900 			LIST_INSERT_AFTER(nxfs3, nxfs, nxfs_next);
1901 		} else {
1902 			LIST_INSERT_HEAD(&nfsrv_exports, nxfs, nxfs_next);
1903 		}
1904 
1905 		/* make sure any quotas are enabled before we export the file system */
1906 		if (mp) {
1907 			enablequotas(mp, ctx);
1908 		}
1909 	}
1910 
1911 	if (unxa->nxa_exppath) {
1912 		error = copyinstr(unxa->nxa_exppath, path, MAXPATHLEN, &pathlen);
1913 		if (error) {
1914 			goto out;
1915 		}
1916 		LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
1917 			if (nx->nx_id == unxa->nxa_expid) {
1918 				break;
1919 			}
1920 		}
1921 		if (nx) {
1922 			/* verify exported FS path matches given path */
1923 			if (strncmp(path, nx->nx_path, MAXPATHLEN)) {
1924 				error = EEXIST;
1925 				goto out;
1926 			}
1927 		} else {
1928 			/* no current export with that ID */
1929 			if (!(unxa->nxa_flags & NXA_ADD)) {
1930 				error = ENOENT;
1931 				goto out;
1932 			}
1933 			/* add an export for it */
1934 			nx = kalloc_type(struct nfs_export, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1935 			nx->nx_id = unxa->nxa_expid;
1936 			nx->nx_fs = nxfs;
1937 			microtime(&nx->nx_exptime);
1938 			nx->nx_path = kalloc_data(pathlen, Z_WAITOK);
1939 			if (!nx->nx_path) {
1940 				error = ENOMEM;
1941 				kfree_type(struct nfs_export, nx);
1942 				nx = NULL;
1943 				goto out1;
1944 			}
1945 			bcopy(path, nx->nx_path, pathlen);
1946 			/* initialize the active user list */
1947 			nfsrv_init_user_list(&nx->nx_user_list);
1948 			/* insert into list in reverse-sorted order */
1949 			nx3 = NULL;
1950 			LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
1951 				if (strncmp(nx->nx_path, nx2->nx_path, MAXPATHLEN) > 0) {
1952 					break;
1953 				}
1954 				nx3 = nx2;
1955 			}
1956 			if (nx2) {
1957 				LIST_INSERT_BEFORE(nx2, nx, nx_next);
1958 			} else if (nx3) {
1959 				LIST_INSERT_AFTER(nx3, nx, nx_next);
1960 			} else {
1961 				LIST_INSERT_HEAD(&nxfs->nxfs_exports, nx, nx_next);
1962 			}
1963 			/* insert into hash */
1964 			LIST_INSERT_HEAD(NFSRVEXPHASH(nxfs->nxfs_id, nx->nx_id), nx, nx_hash);
1965 
1966 			/*
1967 			 * We don't allow/support nested exports.  Check if the new entry
1968 			 * nests with the entries before and after or if there's an
1969 			 * entry for the file system root and subdirs.
1970 			 */
1971 			error = 0;
1972 			if ((nx3 && !strncmp(nx3->nx_path, nx->nx_path, pathlen - 1) &&
1973 			    (nx3->nx_path[pathlen - 1] == '/')) ||
1974 			    (nx2 && !strncmp(nx2->nx_path, nx->nx_path, strlen(nx2->nx_path)) &&
1975 			    (nx->nx_path[strlen(nx2->nx_path)] == '/'))) {
1976 				error = EINVAL;
1977 			}
1978 			if (!error) {
1979 				/* check export conflict with fs root export and vice versa */
1980 				expisroot = !nx->nx_path[0] ||
1981 				    ((nx->nx_path[0] == '.') && !nx->nx_path[1]);
1982 				LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
1983 					if (expisroot) {
1984 						if (nx2 != nx) {
1985 							break;
1986 						}
1987 					} else if (!nx2->nx_path[0]) {
1988 						break;
1989 					} else if ((nx2->nx_path[0] == '.') && !nx2->nx_path[1]) {
1990 						break;
1991 					}
1992 				}
1993 				if (nx2) {
1994 					error = EINVAL;
1995 				}
1996 			}
1997 			if (error) {
1998 				/*
1999 				 * Don't actually return an error because mountd is
2000 				 * probably about to delete the conflicting export.
2001 				 * This can happen when a new export momentarily conflicts
2002 				 * with an old export while the transition is being made.
2003 				 * Theoretically, mountd could be written to avoid this
2004 				 * transient situation - but it would greatly increase the
2005 				 * complexity of mountd for very little overall benefit.
2006 				 */
2007 				printf("nfsrv_export: warning: nested exports: %s/%s\n",
2008 				    nxfs->nxfs_path, nx->nx_path);
2009 				error = 0;
2010 			}
2011 			nx->nx_fh.nfh_xh.nxh_flags = NXHF_INVALIDFH;
2012 		}
2013 		/* make sure file handle is set up */
2014 		if ((nx->nx_fh.nfh_xh.nxh_version != htonl(NFS_FH_VERSION)) ||
2015 		    (nx->nx_fh.nfh_xh.nxh_flags & NXHF_INVALIDFH)) {
2016 			/* try to set up export root file handle */
2017 			nx->nx_fh.nfh_xh.nxh_version = htonl(NFS_FH_VERSION);
2018 			nx->nx_fh.nfh_xh.nxh_fsid = htonl(nx->nx_fs->nxfs_id);
2019 			nx->nx_fh.nfh_xh.nxh_expid = htonl(nx->nx_id);
2020 			nx->nx_fh.nfh_xh.nxh_flags = 0;
2021 			nx->nx_fh.nfh_xh.nxh_reserved = 0;
2022 			nx->nx_fh.nfh_fhp = (u_char*)&nx->nx_fh.nfh_xh;
2023 			bzero(&nx->nx_fh.nfh_fid[0], NFSV2_MAX_FID_SIZE);
2024 			if (mvp) {
2025 				/* find export root vnode */
2026 				if (!nx->nx_path[0] || ((nx->nx_path[0] == '.') && !nx->nx_path[1])) {
2027 					/* exporting file system's root directory */
2028 					xvp = mvp;
2029 					vnode_get(xvp);
2030 				} else {
2031 					NDINIT(&xnd, LOOKUP, OP_LOOKUP, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
2032 					xnd.ni_pathlen = (uint32_t)pathlen - 1; // pathlen max value is equal to MAXPATHLEN
2033 					xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf = path;
2034 					xnd.ni_startdir = mvp;
2035 					xnd.ni_usedvp   = mvp;
2036 					xnd.ni_rootdir = rootvnode;
2037 					while ((error = lookup(&xnd)) == ERECYCLE) {
2038 						xnd.ni_cnd.cn_flags = LOCKLEAF;
2039 						xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf;
2040 						xnd.ni_usedvp = xnd.ni_dvp = xnd.ni_startdir = mvp;
2041 					}
2042 					if (error) {
2043 						goto out1;
2044 					}
2045 					xvp = xnd.ni_vp;
2046 				}
2047 
2048 				if (vnode_vtype(xvp) != VDIR) {
2049 					error = EINVAL;
2050 					vnode_put(xvp);
2051 					goto out1;
2052 				}
2053 
2054 				/* grab file handle */
2055 				nx->nx_fh.nfh_len = NFSV3_MAX_FID_SIZE;
2056 				error = VFS_VPTOFH(xvp, (int*)&nx->nx_fh.nfh_len, &nx->nx_fh.nfh_fid[0], NULL);
2057 				if (!error && (nx->nx_fh.nfh_len > (int)NFSV3_MAX_FID_SIZE)) {
2058 					error = EIO;
2059 				} else {
2060 					nx->nx_fh.nfh_xh.nxh_fidlen = nx->nx_fh.nfh_len;
2061 					nx->nx_fh.nfh_len += sizeof(nx->nx_fh.nfh_xh);
2062 				}
2063 
2064 				vnode_put(xvp);
2065 				if (error) {
2066 					goto out1;
2067 				}
2068 			} else {
2069 				nx->nx_fh.nfh_xh.nxh_flags = NXHF_INVALIDFH;
2070 				nx->nx_fh.nfh_xh.nxh_fidlen = 0;
2071 				nx->nx_fh.nfh_len = sizeof(nx->nx_fh.nfh_xh);
2072 			}
2073 		}
2074 	} else {
2075 		nx = NULL;
2076 	}
2077 
2078 	/* perform the export changes */
2079 	if (unxa->nxa_flags & NXA_DELETE) {
2080 		if (!nx) {
2081 			/* delete all exports on this file system */
2082 			while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
2083 				LIST_REMOVE(nx, nx_next);
2084 				LIST_REMOVE(nx, nx_hash);
2085 				/* delete all netopts for this export */
2086 				nfsrv_free_addrlist(nx, NULL);
2087 				nx->nx_flags &= ~NX_DEFAULTEXPORT;
2088 				if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
2089 					kauth_cred_unref(&nx->nx_defopt.nxo_cred);
2090 				}
2091 				/* delete active user list for this export */
2092 				nfsrv_free_user_list(&nx->nx_user_list);
2093 				kfree_data_addr(nx->nx_path);
2094 				kfree_type(struct nfs_export, nx);
2095 			}
2096 			goto out1;
2097 		} else if (!unxa->nxa_netcount) {
2098 			/* delete all netopts for this export */
2099 			nfsrv_free_addrlist(nx, NULL);
2100 			nx->nx_flags &= ~NX_DEFAULTEXPORT;
2101 			if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
2102 				kauth_cred_unref(&nx->nx_defopt.nxo_cred);
2103 			}
2104 		} else {
2105 			/* delete only the netopts for the given addresses */
2106 			error = nfsrv_free_addrlist(nx, unxa);
2107 			if (error) {
2108 				goto out1;
2109 			}
2110 		}
2111 	}
2112 	if (unxa->nxa_flags & NXA_ADD) {
2113 		/*
2114 		 * If going offline set the export time so that when
2115 		 * coming back on line we will present a new write verifier
2116 		 * to the client.
2117 		 */
2118 		if (unxa->nxa_flags & NXA_OFFLINE) {
2119 			microtime(&nx->nx_exptime);
2120 		}
2121 
2122 		error = nfsrv_hang_addrlist(nx, unxa);
2123 		if (!error && mp) {
2124 			vfs_setflags(mp, MNT_EXPORTED);
2125 		}
2126 	}
2127 
2128 out1:
2129 	if (nx && !nx->nx_expcnt) {
2130 		/* export has no export options */
2131 		LIST_REMOVE(nx, nx_next);
2132 		LIST_REMOVE(nx, nx_hash);
2133 		/* delete active user list for this export */
2134 		nfsrv_free_user_list(&nx->nx_user_list);
2135 		kfree_data_addr(nx->nx_path);
2136 		kfree_type(struct nfs_export, nx);
2137 	}
2138 	if (LIST_EMPTY(&nxfs->nxfs_exports)) {
2139 		/* exported file system has no more exports */
2140 		LIST_REMOVE(nxfs, nxfs_next);
2141 		kfree_data_addr(nxfs->nxfs_path);
2142 		kfree_type(struct nfs_exportfs, nxfs);
2143 		if (mp) {
2144 			vfs_clearflags(mp, MNT_EXPORTED);
2145 		}
2146 	}
2147 
2148 out:
2149 	if (mvp) {
2150 		vnode_put(mvp);
2151 		nameidone(&mnd);
2152 	}
2153 unlock_out:
2154 	if (mp) {
2155 		mount_drop(mp, 0);
2156 	}
2157 	lck_rw_done(&nfsrv_export_rwlock);
2158 	return error;
2159 }
2160 
2161 /*
2162  * Check if there is a least one export that will allow this address.
2163  *
2164  * Return 0, if there is an export that will allow this address,
2165  * else return EACCES
2166  */
2167 int
nfsrv_check_exports_allow_address(mbuf_t nam)2168 nfsrv_check_exports_allow_address(mbuf_t nam)
2169 {
2170 	struct nfs_exportfs             *nxfs;
2171 	struct nfs_export               *nx;
2172 	struct nfs_export_options       *nxo = NULL;
2173 
2174 	if (nam == NULL) {
2175 		return EACCES;
2176 	}
2177 
2178 	lck_rw_lock_shared(&nfsrv_export_rwlock);
2179 	LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
2180 		LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
2181 			/* A little optimizing by checking for the default first */
2182 			if (nx->nx_flags & NX_DEFAULTEXPORT) {
2183 				nxo = &nx->nx_defopt;
2184 			}
2185 			if (nxo || (nxo = nfsrv_export_lookup(nx, nam))) {
2186 				goto found;
2187 			}
2188 		}
2189 	}
2190 found:
2191 	lck_rw_done(&nfsrv_export_rwlock);
2192 
2193 	return nxo ? 0 : EACCES;
2194 }
2195 
2196 struct nfs_export_options *
nfsrv_export_lookup(struct nfs_export * nx,mbuf_t nam)2197 nfsrv_export_lookup(struct nfs_export *nx, mbuf_t nam)
2198 {
2199 	struct nfs_export_options *nxo = NULL;
2200 	struct nfs_netopt *no = NULL;
2201 	struct radix_node_head *rnh;
2202 	struct sockaddr *saddr;
2203 
2204 	/* Lookup in the export list first. */
2205 	if (nam != NULL) {
2206 		saddr = mbuf_data(nam);
2207 		if (saddr->sa_family > AF_MAX) {
2208 			/* Bogus sockaddr?  Don't match anything. */
2209 			return NULL;
2210 		}
2211 		rnh = nx->nx_rtable[saddr->sa_family];
2212 		if (rnh != NULL) {
2213 			no = (struct nfs_netopt *)
2214 			    (*rnh->rnh_matchaddr)((caddr_t)saddr, rnh);
2215 			if (no && no->no_rnodes->rn_flags & RNF_ROOT) {
2216 				no = NULL;
2217 			}
2218 			if (no) {
2219 				nxo = &no->no_opt;
2220 			}
2221 		}
2222 	}
2223 	/* If no address match, use the default if it exists. */
2224 	if ((nxo == NULL) && (nx->nx_flags & NX_DEFAULTEXPORT)) {
2225 		nxo = &nx->nx_defopt;
2226 	}
2227 	return nxo;
2228 }
2229 
2230 /* find an export for the given handle */
2231 struct nfs_export *
nfsrv_fhtoexport(struct nfs_filehandle * nfhp)2232 nfsrv_fhtoexport(struct nfs_filehandle *nfhp)
2233 {
2234 	struct nfs_exphandle *nxh = (struct nfs_exphandle*)nfhp->nfh_fhp;
2235 	struct nfs_export *nx;
2236 	uint32_t fsid, expid;
2237 
2238 	if (!nfsrv_export_hashtbl) {
2239 		return NULL;
2240 	}
2241 	fsid = ntohl(nxh->nxh_fsid);
2242 	expid = ntohl(nxh->nxh_expid);
2243 	nx = NFSRVEXPHASH(fsid, expid)->lh_first;
2244 	for (; nx; nx = LIST_NEXT(nx, nx_hash)) {
2245 		if (nx->nx_fs->nxfs_id != fsid) {
2246 			continue;
2247 		}
2248 		if (nx->nx_id != expid) {
2249 			continue;
2250 		}
2251 		break;
2252 	}
2253 	return nx;
2254 }
2255 
2256 struct nfsrv_getvfs_by_mntonname_callback_args {
2257 	const char      *path;          /* IN */
2258 	mount_t         mp;             /* OUT */
2259 };
2260 
2261 static int
nfsrv_getvfs_by_mntonname_callback(mount_t mp,void * v)2262 nfsrv_getvfs_by_mntonname_callback(mount_t mp, void *v)
2263 {
2264 	struct nfsrv_getvfs_by_mntonname_callback_args * const args = v;
2265 	char real_mntonname[MAXPATHLEN];
2266 	size_t pathbuflen = MAXPATHLEN;
2267 	vnode_t rvp;
2268 	int error;
2269 
2270 	error = VFS_ROOT(mp, &rvp, vfs_context_current());
2271 	if (error) {
2272 		goto out;
2273 	}
2274 	error = vn_getpath_ext(rvp, NULLVP, real_mntonname, &pathbuflen,
2275 	    VN_GETPATH_FSENTER | VN_GETPATH_NO_FIRMLINK);
2276 	vnode_put(rvp);
2277 	if (error) {
2278 		goto out;
2279 	}
2280 	if (strcmp(args->path, real_mntonname) == 0) {
2281 		error = vfs_busy(mp, LK_NOWAIT);
2282 		if (error == 0) {
2283 			args->mp = mp;
2284 		}
2285 		return VFS_RETURNED_DONE;
2286 	}
2287 out:
2288 	return VFS_RETURNED;
2289 }
2290 
2291 static mount_t
nfsrv_getvfs_by_mntonname(char * path)2292 nfsrv_getvfs_by_mntonname(char *path)
2293 {
2294 	struct nfsrv_getvfs_by_mntonname_callback_args args = {
2295 		.path = path,
2296 		.mp = NULL,
2297 	};
2298 	mount_t mp;
2299 	int error;
2300 
2301 	mp = vfs_getvfs_by_mntonname(path);
2302 	if (mp) {
2303 		error = vfs_busy(mp, LK_NOWAIT);
2304 		mount_iterdrop(mp);
2305 		if (error) {
2306 			mp = NULL;
2307 		}
2308 	} else if (vfs_iterate(0, nfsrv_getvfs_by_mntonname_callback,
2309 	    &args) == 0) {
2310 		mp = args.mp;
2311 	}
2312 	return mp;
2313 }
2314 
2315 /*
2316  * nfsrv_fhtovp() - convert FH to vnode and export info
2317  */
2318 int
nfsrv_fhtovp(struct nfs_filehandle * nfhp,struct nfsrv_descript * nd,vnode_t * vpp,struct nfs_export ** nxp,struct nfs_export_options ** nxop)2319 nfsrv_fhtovp(
2320 	struct nfs_filehandle *nfhp,
2321 	struct nfsrv_descript *nd,
2322 	vnode_t *vpp,
2323 	struct nfs_export **nxp,
2324 	struct nfs_export_options **nxop)
2325 {
2326 	struct nfs_exphandle *nxh = (struct nfs_exphandle*)nfhp->nfh_fhp;
2327 	struct nfs_export_options *nxo;
2328 	u_char *fidp;
2329 	int error;
2330 	struct mount *mp;
2331 	mbuf_t nam = NULL;
2332 	uint32_t v;
2333 	int i, valid;
2334 
2335 	*vpp = NULL;
2336 	*nxp = NULL;
2337 	*nxop = NULL;
2338 
2339 	if (nd != NULL) {
2340 		nam = nd->nd_nam;
2341 	}
2342 
2343 	v = ntohl(nxh->nxh_version);
2344 	if (v != NFS_FH_VERSION) {
2345 		/* file handle format not supported */
2346 		return ESTALE;
2347 	}
2348 	if (nfhp->nfh_len > NFSV3_MAX_FH_SIZE) {
2349 		return EBADRPC;
2350 	}
2351 	if (nfhp->nfh_len < (int)sizeof(struct nfs_exphandle)) {
2352 		return ESTALE;
2353 	}
2354 	v = ntohs(nxh->nxh_flags);
2355 	if (v & NXHF_INVALIDFH) {
2356 		return ESTALE;
2357 	}
2358 
2359 	*nxp = nfsrv_fhtoexport(nfhp);
2360 	if (!*nxp) {
2361 		return ESTALE;
2362 	}
2363 
2364 	/* Get the export option structure for this <export, client> tuple. */
2365 	*nxop = nxo = nfsrv_export_lookup(*nxp, nam);
2366 	if (nam && (*nxop == NULL)) {
2367 		return EACCES;
2368 	}
2369 
2370 	if (nd != NULL) {
2371 		/* Validate the security flavor of the request */
2372 		for (i = 0, valid = 0; i < nxo->nxo_sec.count; i++) {
2373 			if (nd->nd_sec == nxo->nxo_sec.flavors[i]) {
2374 				valid = 1;
2375 				break;
2376 			}
2377 		}
2378 		if (!valid) {
2379 			/*
2380 			 * RFC 2623 section 2.3.2 recommends no authentication
2381 			 * requirement for certain NFS procedures used for mounting.
2382 			 * This allows an unauthenticated superuser on the client
2383 			 * to do mounts for the benefit of authenticated users.
2384 			 */
2385 			if (nd->nd_vers == NFS_VER2) {
2386 				if (nd->nd_procnum == NFSV2PROC_GETATTR ||
2387 				    nd->nd_procnum == NFSV2PROC_STATFS) {
2388 					valid = 1;
2389 				}
2390 			}
2391 			if (nd->nd_vers == NFS_VER3) {
2392 				if (nd->nd_procnum == NFSPROC_FSINFO) {
2393 					valid = 1;
2394 				}
2395 			}
2396 
2397 			if (!valid) {
2398 				return NFSERR_AUTHERR | AUTH_REJECTCRED;
2399 			}
2400 		}
2401 	}
2402 
2403 	if (nxo && (nxo->nxo_flags & NX_OFFLINE)) {
2404 		return (nd == NULL || nd->nd_vers == NFS_VER2) ? ESTALE : NFSERR_TRYLATER;
2405 	}
2406 
2407 	/* find mount structure */
2408 	mp = nfsrv_getvfs_by_mntonname((*nxp)->nx_fs->nxfs_path);
2409 	if (!mp) {
2410 		/*
2411 		 * We have an export, but no mount?
2412 		 * Perhaps the export just hasn't been marked offline yet.
2413 		 */
2414 		return (nd == NULL || nd->nd_vers == NFS_VER2) ? ESTALE : NFSERR_TRYLATER;
2415 	}
2416 
2417 	fidp = nfhp->nfh_fhp + sizeof(*nxh);
2418 	error = VFS_FHTOVP(mp, nxh->nxh_fidlen, fidp, vpp, NULL);
2419 	vfs_unbusy(mp);
2420 	if (error) {
2421 		return error;
2422 	}
2423 	/* vnode pointer should be good at this point or ... */
2424 	if (*vpp == NULL) {
2425 		return ESTALE;
2426 	}
2427 	return 0;
2428 }
2429 
2430 /*
2431  * nfsrv_credcheck() - check/map credentials according
2432  * to given export options.
2433  */
2434 int
nfsrv_credcheck(struct nfsrv_descript * nd,vfs_context_t ctx,__unused struct nfs_export * nx,struct nfs_export_options * nxo)2435 nfsrv_credcheck(
2436 	struct nfsrv_descript *nd,
2437 	vfs_context_t ctx,
2438 	__unused struct nfs_export *nx,
2439 	struct nfs_export_options *nxo)
2440 {
2441 	if (nxo && nxo->nxo_cred) {
2442 		if ((nxo->nxo_flags & NX_MAPALL) ||
2443 		    ((nxo->nxo_flags & NX_MAPROOT) && !suser(nd->nd_cr, NULL))) {
2444 			kauth_cred_ref(nxo->nxo_cred);
2445 			kauth_cred_unref(&nd->nd_cr);
2446 			nd->nd_cr = nxo->nxo_cred;
2447 		}
2448 	}
2449 	ctx->vc_ucred = nd->nd_cr;
2450 	return 0;
2451 }
2452 
2453 /*
2454  * nfsrv_vptofh() - convert vnode to file handle for given export
2455  *
2456  * If the caller is passing in a vnode for a ".." directory entry,
2457  * they can pass a directory NFS file handle (dnfhp) which will be
2458  * checked against the root export file handle.  If it matches, we
2459  * refuse to provide the file handle for the out-of-export directory.
2460  */
2461 int
nfsrv_vptofh(struct nfs_export * nx,int nfsvers,struct nfs_filehandle * dnfhp,vnode_t vp,vfs_context_t ctx,struct nfs_filehandle * nfhp)2462 nfsrv_vptofh(
2463 	struct nfs_export *nx,
2464 	int nfsvers,
2465 	struct nfs_filehandle *dnfhp,
2466 	vnode_t vp,
2467 	vfs_context_t ctx,
2468 	struct nfs_filehandle *nfhp)
2469 {
2470 	int error;
2471 	uint32_t maxfidsize;
2472 
2473 	nfhp->nfh_fhp = (u_char*)&nfhp->nfh_xh;
2474 	nfhp->nfh_xh.nxh_version = htonl(NFS_FH_VERSION);
2475 	nfhp->nfh_xh.nxh_fsid = htonl(nx->nx_fs->nxfs_id);
2476 	nfhp->nfh_xh.nxh_expid = htonl(nx->nx_id);
2477 	nfhp->nfh_xh.nxh_flags = 0;
2478 	nfhp->nfh_xh.nxh_reserved = 0;
2479 
2480 	if (nfsvers == NFS_VER2) {
2481 		bzero(&nfhp->nfh_fid[0], NFSV2_MAX_FID_SIZE);
2482 	}
2483 
2484 	/* if directory FH matches export root, return invalid FH */
2485 	if (dnfhp && nfsrv_fhmatch(dnfhp, &nx->nx_fh)) {
2486 		if (nfsvers == NFS_VER2) {
2487 			nfhp->nfh_len = NFSX_V2FH;
2488 		} else {
2489 			nfhp->nfh_len = sizeof(nfhp->nfh_xh);
2490 		}
2491 		nfhp->nfh_xh.nxh_fidlen = 0;
2492 		nfhp->nfh_xh.nxh_flags = htons(NXHF_INVALIDFH);
2493 		return 0;
2494 	}
2495 
2496 	if (nfsvers == NFS_VER2) {
2497 		maxfidsize = NFSV2_MAX_FID_SIZE;
2498 	} else {
2499 		maxfidsize = NFSV3_MAX_FID_SIZE;
2500 	}
2501 	nfhp->nfh_len = maxfidsize;
2502 
2503 	error = VFS_VPTOFH(vp, (int*)&nfhp->nfh_len, &nfhp->nfh_fid[0], ctx);
2504 	if (error) {
2505 		return error;
2506 	}
2507 	if (nfhp->nfh_len > maxfidsize) {
2508 		return EOVERFLOW;
2509 	}
2510 	nfhp->nfh_xh.nxh_fidlen = nfhp->nfh_len;
2511 	nfhp->nfh_len += sizeof(nfhp->nfh_xh);
2512 	if ((nfsvers == NFS_VER2) && (nfhp->nfh_len < NFSX_V2FH)) {
2513 		nfhp->nfh_len = NFSX_V2FH;
2514 	}
2515 
2516 	return 0;
2517 }
2518 
2519 /*
2520  * Compare two file handles to see it they're the same.
2521  * Note that we don't use nfh_len because that may include
2522  * padding in an NFSv2 file handle.
2523  */
2524 int
nfsrv_fhmatch(struct nfs_filehandle * fh1,struct nfs_filehandle * fh2)2525 nfsrv_fhmatch(struct nfs_filehandle *fh1, struct nfs_filehandle *fh2)
2526 {
2527 	struct nfs_exphandle *nxh1, *nxh2;
2528 	int len1, len2;
2529 
2530 	nxh1 = (struct nfs_exphandle *)fh1->nfh_fhp;
2531 	nxh2 = (struct nfs_exphandle *)fh2->nfh_fhp;
2532 	len1 = sizeof(fh1->nfh_xh) + nxh1->nxh_fidlen;
2533 	len2 = sizeof(fh2->nfh_xh) + nxh2->nxh_fidlen;
2534 	if (len1 != len2) {
2535 		return 0;
2536 	}
2537 	if (bcmp(nxh1, nxh2, len1)) {
2538 		return 0;
2539 	}
2540 	return 1;
2541 }
2542 
2543 /*
2544  * Functions for dealing with active user lists
2545  */
2546 
2547 /*
2548  * Search the hash table for a user node with a matching IP address and uid field.
2549  * If found, the node's tm_last timestamp is updated and the node is returned.
2550  *
2551  * If not found, a new node is allocated (or reclaimed via LRU), initialized, and returned.
2552  * Returns NULL if a new node could not be allocated OR saddr length exceeds sizeof(unode->sock).
2553  *
2554  * The list's user_mutex lock MUST be held.
2555  */
2556 struct nfs_user_stat_node *
nfsrv_get_user_stat_node(struct nfs_active_user_list * list,struct sockaddr * saddr,uid_t uid)2557 nfsrv_get_user_stat_node(struct nfs_active_user_list *list, struct sockaddr *saddr, uid_t uid)
2558 {
2559 	struct nfs_user_stat_node               *unode;
2560 	struct timeval                          now;
2561 	struct nfs_user_stat_hashtbl_head       *head;
2562 
2563 	/* seach the hash table */
2564 	head = NFS_USER_STAT_HASH(list->user_hashtbl, uid);
2565 	LIST_FOREACH(unode, head, hash_link) {
2566 		if ((uid == unode->uid) && (nfs_sockaddr_cmp(saddr, (struct sockaddr*)&unode->sock) == 0)) {
2567 			/* found matching node */
2568 			break;
2569 		}
2570 	}
2571 
2572 	if (unode) {
2573 		/* found node in the hash table, now update lru position */
2574 		TAILQ_REMOVE(&list->user_lru, unode, lru_link);
2575 		TAILQ_INSERT_TAIL(&list->user_lru, unode, lru_link);
2576 
2577 		/* update time stamp */
2578 		microtime(&now);
2579 		unode->tm_last = (uint32_t)now.tv_sec;
2580 		return unode;
2581 	}
2582 
2583 	if (saddr->sa_len > sizeof(((struct nfs_user_stat_node *)0)->sock)) {
2584 		/* saddr length exceeds maximum value */
2585 		return NULL;
2586 	}
2587 
2588 	if (list->node_count < nfsrv_user_stat_max_nodes) {
2589 		/* Allocate a new node */
2590 		unode = kalloc_type(struct nfs_user_stat_node,
2591 		    Z_WAITOK | Z_ZERO | Z_NOFAIL);
2592 
2593 		/* increment node count */
2594 		OSAddAtomic(1, &nfsrv_user_stat_node_count);
2595 		list->node_count++;
2596 	} else {
2597 		/* reuse the oldest node in the lru list */
2598 		unode = TAILQ_FIRST(&list->user_lru);
2599 
2600 		if (!unode) {
2601 			return NULL;
2602 		}
2603 
2604 		/* Remove the node */
2605 		TAILQ_REMOVE(&list->user_lru, unode, lru_link);
2606 		LIST_REMOVE(unode, hash_link);
2607 	}
2608 
2609 	/* Initialize the node */
2610 	unode->uid = uid;
2611 	bcopy(saddr, &unode->sock, MIN(saddr->sa_len, sizeof(unode->sock)));
2612 	microtime(&now);
2613 	unode->ops = 0;
2614 	unode->bytes_read = 0;
2615 	unode->bytes_written = 0;
2616 	unode->tm_start = (uint32_t)now.tv_sec;
2617 	unode->tm_last = (uint32_t)now.tv_sec;
2618 
2619 	/* insert the node  */
2620 	TAILQ_INSERT_TAIL(&list->user_lru, unode, lru_link);
2621 	LIST_INSERT_HEAD(head, unode, hash_link);
2622 
2623 	return unode;
2624 }
2625 
2626 void
nfsrv_update_user_stat(struct nfs_export * nx,struct nfsrv_descript * nd,uid_t uid,u_int ops,u_int rd_bytes,u_int wr_bytes)2627 nfsrv_update_user_stat(struct nfs_export *nx, struct nfsrv_descript *nd, uid_t uid, u_int ops, u_int rd_bytes, u_int wr_bytes)
2628 {
2629 	struct nfs_user_stat_node       *unode;
2630 	struct nfs_active_user_list     *ulist;
2631 	struct sockaddr                 *saddr;
2632 
2633 	if ((!nfsrv_user_stat_enabled) || (!nx) || (!nd) || (!nd->nd_nam)) {
2634 		return;
2635 	}
2636 
2637 	saddr = (struct sockaddr *)mbuf_data(nd->nd_nam);
2638 
2639 	/* check address family before going any further */
2640 	if ((saddr->sa_family != AF_INET) && (saddr->sa_family != AF_INET6)) {
2641 		return;
2642 	}
2643 
2644 	ulist = &nx->nx_user_list;
2645 
2646 	/* lock the active user list */
2647 	lck_mtx_lock(&ulist->user_mutex);
2648 
2649 	/* get the user node */
2650 	unode = nfsrv_get_user_stat_node(ulist, saddr, uid);
2651 
2652 	if (!unode) {
2653 		lck_mtx_unlock(&ulist->user_mutex);
2654 		return;
2655 	}
2656 
2657 	/* update counters */
2658 	unode->ops += ops;
2659 	unode->bytes_read += rd_bytes;
2660 	unode->bytes_written += wr_bytes;
2661 
2662 	/* done */
2663 	lck_mtx_unlock(&ulist->user_mutex);
2664 }
2665 
2666 /* initialize an active user list */
2667 void
nfsrv_init_user_list(struct nfs_active_user_list * ulist)2668 nfsrv_init_user_list(struct nfs_active_user_list *ulist)
2669 {
2670 	uint i;
2671 
2672 	/* initialize the lru */
2673 	TAILQ_INIT(&ulist->user_lru);
2674 
2675 	/* initialize the hash table */
2676 	for (i = 0; i < NFS_USER_STAT_HASH_SIZE; i++) {
2677 		LIST_INIT(&ulist->user_hashtbl[i]);
2678 	}
2679 	ulist->node_count = 0;
2680 
2681 	lck_mtx_init(&ulist->user_mutex, &nfsrv_active_user_mutex_group, LCK_ATTR_NULL);
2682 }
2683 
2684 /* Free all nodes in an active user list */
2685 void
nfsrv_free_user_list(struct nfs_active_user_list * ulist)2686 nfsrv_free_user_list(struct nfs_active_user_list *ulist)
2687 {
2688 	struct nfs_user_stat_node *unode;
2689 
2690 	if (!ulist) {
2691 		return;
2692 	}
2693 
2694 	while ((unode = TAILQ_FIRST(&ulist->user_lru))) {
2695 		/* Remove node and free */
2696 		TAILQ_REMOVE(&ulist->user_lru, unode, lru_link);
2697 		LIST_REMOVE(unode, hash_link);
2698 		kfree_type(struct nfs_user_stat_node, unode);
2699 
2700 		/* decrement node count */
2701 		OSAddAtomic(-1, &nfsrv_user_stat_node_count);
2702 	}
2703 	ulist->node_count = 0;
2704 
2705 	lck_mtx_destroy(&ulist->user_mutex, &nfsrv_active_user_mutex_group);
2706 }
2707 
2708 /* Reclaim old expired user nodes from active user lists. */
2709 void
nfsrv_active_user_list_reclaim(void)2710 nfsrv_active_user_list_reclaim(void)
2711 {
2712 	struct nfs_exportfs                     *nxfs;
2713 	struct nfs_export                       *nx;
2714 	struct nfs_active_user_list             *ulist;
2715 	struct nfs_user_stat_hashtbl_head       oldlist;
2716 	struct nfs_user_stat_node               *unode, *unode_next;
2717 	struct timeval                          now;
2718 	long                                    tstale;
2719 
2720 	LIST_INIT(&oldlist);
2721 
2722 	lck_rw_lock_shared(&nfsrv_export_rwlock);
2723 	microtime(&now);
2724 	tstale = now.tv_sec - nfsrv_user_stat_max_idle_sec;
2725 	LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
2726 		LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
2727 			/* Scan through all user nodes of this export */
2728 			ulist = &nx->nx_user_list;
2729 			lck_mtx_lock(&ulist->user_mutex);
2730 			for (unode = TAILQ_FIRST(&ulist->user_lru); unode; unode = unode_next) {
2731 				unode_next = TAILQ_NEXT(unode, lru_link);
2732 
2733 				/* check if this node has expired */
2734 				if (unode->tm_last >= tstale) {
2735 					break;
2736 				}
2737 
2738 				/* Remove node from the active user list */
2739 				TAILQ_REMOVE(&ulist->user_lru, unode, lru_link);
2740 				LIST_REMOVE(unode, hash_link);
2741 
2742 				/* Add node to temp list */
2743 				LIST_INSERT_HEAD(&oldlist, unode, hash_link);
2744 
2745 				/* decrement node count */
2746 				OSAddAtomic(-1, &nfsrv_user_stat_node_count);
2747 				ulist->node_count--;
2748 			}
2749 			/* can unlock this export's list now */
2750 			lck_mtx_unlock(&ulist->user_mutex);
2751 		}
2752 	}
2753 	lck_rw_done(&nfsrv_export_rwlock);
2754 
2755 	/* Free expired nodes */
2756 	while ((unode = LIST_FIRST(&oldlist))) {
2757 		LIST_REMOVE(unode, hash_link);
2758 		kfree_type(struct nfs_user_stat_node, unode);
2759 	}
2760 }
2761 
2762 /*
2763  * Maps errno values to nfs error numbers.
2764  * Use NFSERR_IO as the catch all for ones not specifically defined in
2765  * RFC 1094.
2766  */
2767 static u_char nfsrv_v2errmap[] = {
2768 	NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2769 	NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2770 	NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
2771 	NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
2772 	NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2773 	NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
2774 	NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2775 	NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2776 	NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2777 	NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2778 	NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2779 	NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2780 	NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
2781 	NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
2782 };
2783 
2784 /*
2785  * Maps errno values to nfs error numbers.
2786  * Although it is not obvious whether or not NFS clients really care if
2787  * a returned error value is in the specified list for the procedure, the
2788  * safest thing to do is filter them appropriately. For Version 2, the
2789  * X/Open XNFS document is the only specification that defines error values
2790  * for each RPC (The RFC simply lists all possible error values for all RPCs),
2791  * so I have decided to not do this for Version 2.
2792  * The first entry is the default error return and the rest are the valid
2793  * errors for that RPC in increasing numeric order.
2794  */
2795 static short nfsv3err_null[] = {
2796 	0,
2797 	0,
2798 };
2799 
2800 static short nfsv3err_getattr[] = {
2801 	NFSERR_IO,
2802 	NFSERR_IO,
2803 	NFSERR_STALE,
2804 	NFSERR_BADHANDLE,
2805 	NFSERR_SERVERFAULT,
2806 	NFSERR_TRYLATER,
2807 	0,
2808 };
2809 
2810 static short nfsv3err_setattr[] = {
2811 	NFSERR_IO,
2812 	NFSERR_PERM,
2813 	NFSERR_IO,
2814 	NFSERR_ACCES,
2815 	NFSERR_INVAL,
2816 	NFSERR_NOSPC,
2817 	NFSERR_ROFS,
2818 	NFSERR_DQUOT,
2819 	NFSERR_STALE,
2820 	NFSERR_BADHANDLE,
2821 	NFSERR_NOT_SYNC,
2822 	NFSERR_SERVERFAULT,
2823 	NFSERR_TRYLATER,
2824 	0,
2825 };
2826 
2827 static short nfsv3err_lookup[] = {
2828 	NFSERR_IO,
2829 	NFSERR_NOENT,
2830 	NFSERR_IO,
2831 	NFSERR_ACCES,
2832 	NFSERR_NOTDIR,
2833 	NFSERR_NAMETOL,
2834 	NFSERR_STALE,
2835 	NFSERR_BADHANDLE,
2836 	NFSERR_SERVERFAULT,
2837 	NFSERR_TRYLATER,
2838 	0,
2839 };
2840 
2841 static short nfsv3err_access[] = {
2842 	NFSERR_IO,
2843 	NFSERR_IO,
2844 	NFSERR_STALE,
2845 	NFSERR_BADHANDLE,
2846 	NFSERR_SERVERFAULT,
2847 	NFSERR_TRYLATER,
2848 	0,
2849 };
2850 
2851 static short nfsv3err_readlink[] = {
2852 	NFSERR_IO,
2853 	NFSERR_IO,
2854 	NFSERR_ACCES,
2855 	NFSERR_INVAL,
2856 	NFSERR_STALE,
2857 	NFSERR_BADHANDLE,
2858 	NFSERR_NOTSUPP,
2859 	NFSERR_SERVERFAULT,
2860 	NFSERR_TRYLATER,
2861 	0,
2862 };
2863 
2864 static short nfsv3err_read[] = {
2865 	NFSERR_IO,
2866 	NFSERR_IO,
2867 	NFSERR_NXIO,
2868 	NFSERR_ACCES,
2869 	NFSERR_INVAL,
2870 	NFSERR_STALE,
2871 	NFSERR_BADHANDLE,
2872 	NFSERR_SERVERFAULT,
2873 	NFSERR_TRYLATER,
2874 	0,
2875 };
2876 
2877 static short nfsv3err_write[] = {
2878 	NFSERR_IO,
2879 	NFSERR_IO,
2880 	NFSERR_ACCES,
2881 	NFSERR_INVAL,
2882 	NFSERR_FBIG,
2883 	NFSERR_NOSPC,
2884 	NFSERR_ROFS,
2885 	NFSERR_DQUOT,
2886 	NFSERR_STALE,
2887 	NFSERR_BADHANDLE,
2888 	NFSERR_SERVERFAULT,
2889 	NFSERR_TRYLATER,
2890 	0,
2891 };
2892 
2893 static short nfsv3err_create[] = {
2894 	NFSERR_IO,
2895 	NFSERR_IO,
2896 	NFSERR_ACCES,
2897 	NFSERR_EXIST,
2898 	NFSERR_NOTDIR,
2899 	NFSERR_NOSPC,
2900 	NFSERR_ROFS,
2901 	NFSERR_NAMETOL,
2902 	NFSERR_DQUOT,
2903 	NFSERR_STALE,
2904 	NFSERR_BADHANDLE,
2905 	NFSERR_NOTSUPP,
2906 	NFSERR_SERVERFAULT,
2907 	NFSERR_TRYLATER,
2908 	0,
2909 };
2910 
2911 static short nfsv3err_mkdir[] = {
2912 	NFSERR_IO,
2913 	NFSERR_IO,
2914 	NFSERR_ACCES,
2915 	NFSERR_EXIST,
2916 	NFSERR_NOTDIR,
2917 	NFSERR_NOSPC,
2918 	NFSERR_ROFS,
2919 	NFSERR_NAMETOL,
2920 	NFSERR_DQUOT,
2921 	NFSERR_STALE,
2922 	NFSERR_BADHANDLE,
2923 	NFSERR_NOTSUPP,
2924 	NFSERR_SERVERFAULT,
2925 	NFSERR_TRYLATER,
2926 	0,
2927 };
2928 
2929 static short nfsv3err_symlink[] = {
2930 	NFSERR_IO,
2931 	NFSERR_IO,
2932 	NFSERR_ACCES,
2933 	NFSERR_EXIST,
2934 	NFSERR_NOTDIR,
2935 	NFSERR_NOSPC,
2936 	NFSERR_ROFS,
2937 	NFSERR_NAMETOL,
2938 	NFSERR_DQUOT,
2939 	NFSERR_STALE,
2940 	NFSERR_BADHANDLE,
2941 	NFSERR_NOTSUPP,
2942 	NFSERR_SERVERFAULT,
2943 	NFSERR_TRYLATER,
2944 	0,
2945 };
2946 
2947 static short nfsv3err_mknod[] = {
2948 	NFSERR_IO,
2949 	NFSERR_IO,
2950 	NFSERR_ACCES,
2951 	NFSERR_EXIST,
2952 	NFSERR_NOTDIR,
2953 	NFSERR_NOSPC,
2954 	NFSERR_ROFS,
2955 	NFSERR_NAMETOL,
2956 	NFSERR_DQUOT,
2957 	NFSERR_STALE,
2958 	NFSERR_BADHANDLE,
2959 	NFSERR_NOTSUPP,
2960 	NFSERR_SERVERFAULT,
2961 	NFSERR_BADTYPE,
2962 	NFSERR_TRYLATER,
2963 	0,
2964 };
2965 
2966 static short nfsv3err_remove[] = {
2967 	NFSERR_IO,
2968 	NFSERR_NOENT,
2969 	NFSERR_IO,
2970 	NFSERR_ACCES,
2971 	NFSERR_NOTDIR,
2972 	NFSERR_ROFS,
2973 	NFSERR_NAMETOL,
2974 	NFSERR_STALE,
2975 	NFSERR_BADHANDLE,
2976 	NFSERR_SERVERFAULT,
2977 	NFSERR_TRYLATER,
2978 	0,
2979 };
2980 
2981 static short nfsv3err_rmdir[] = {
2982 	NFSERR_IO,
2983 	NFSERR_NOENT,
2984 	NFSERR_IO,
2985 	NFSERR_ACCES,
2986 	NFSERR_EXIST,
2987 	NFSERR_NOTDIR,
2988 	NFSERR_INVAL,
2989 	NFSERR_ROFS,
2990 	NFSERR_NAMETOL,
2991 	NFSERR_NOTEMPTY,
2992 	NFSERR_STALE,
2993 	NFSERR_BADHANDLE,
2994 	NFSERR_NOTSUPP,
2995 	NFSERR_SERVERFAULT,
2996 	NFSERR_TRYLATER,
2997 	0,
2998 };
2999 
3000 static short nfsv3err_rename[] = {
3001 	NFSERR_IO,
3002 	NFSERR_NOENT,
3003 	NFSERR_IO,
3004 	NFSERR_ACCES,
3005 	NFSERR_EXIST,
3006 	NFSERR_XDEV,
3007 	NFSERR_NOTDIR,
3008 	NFSERR_ISDIR,
3009 	NFSERR_INVAL,
3010 	NFSERR_NOSPC,
3011 	NFSERR_ROFS,
3012 	NFSERR_MLINK,
3013 	NFSERR_NAMETOL,
3014 	NFSERR_NOTEMPTY,
3015 	NFSERR_DQUOT,
3016 	NFSERR_STALE,
3017 	NFSERR_BADHANDLE,
3018 	NFSERR_NOTSUPP,
3019 	NFSERR_SERVERFAULT,
3020 	NFSERR_TRYLATER,
3021 	0,
3022 };
3023 
3024 static short nfsv3err_link[] = {
3025 	NFSERR_IO,
3026 	NFSERR_IO,
3027 	NFSERR_ACCES,
3028 	NFSERR_EXIST,
3029 	NFSERR_XDEV,
3030 	NFSERR_NOTDIR,
3031 	NFSERR_INVAL,
3032 	NFSERR_NOSPC,
3033 	NFSERR_ROFS,
3034 	NFSERR_MLINK,
3035 	NFSERR_NAMETOL,
3036 	NFSERR_DQUOT,
3037 	NFSERR_STALE,
3038 	NFSERR_BADHANDLE,
3039 	NFSERR_NOTSUPP,
3040 	NFSERR_SERVERFAULT,
3041 	NFSERR_TRYLATER,
3042 	0,
3043 };
3044 
3045 static short nfsv3err_readdir[] = {
3046 	NFSERR_IO,
3047 	NFSERR_IO,
3048 	NFSERR_ACCES,
3049 	NFSERR_NOTDIR,
3050 	NFSERR_STALE,
3051 	NFSERR_BADHANDLE,
3052 	NFSERR_BAD_COOKIE,
3053 	NFSERR_TOOSMALL,
3054 	NFSERR_SERVERFAULT,
3055 	NFSERR_TRYLATER,
3056 	0,
3057 };
3058 
3059 static short nfsv3err_readdirplus[] = {
3060 	NFSERR_IO,
3061 	NFSERR_IO,
3062 	NFSERR_ACCES,
3063 	NFSERR_NOTDIR,
3064 	NFSERR_STALE,
3065 	NFSERR_BADHANDLE,
3066 	NFSERR_BAD_COOKIE,
3067 	NFSERR_NOTSUPP,
3068 	NFSERR_TOOSMALL,
3069 	NFSERR_SERVERFAULT,
3070 	NFSERR_TRYLATER,
3071 	0,
3072 };
3073 
3074 static short nfsv3err_fsstat[] = {
3075 	NFSERR_IO,
3076 	NFSERR_IO,
3077 	NFSERR_STALE,
3078 	NFSERR_BADHANDLE,
3079 	NFSERR_SERVERFAULT,
3080 	NFSERR_TRYLATER,
3081 	0,
3082 };
3083 
3084 static short nfsv3err_fsinfo[] = {
3085 	NFSERR_STALE,
3086 	NFSERR_STALE,
3087 	NFSERR_BADHANDLE,
3088 	NFSERR_SERVERFAULT,
3089 	NFSERR_TRYLATER,
3090 	0,
3091 };
3092 
3093 static short nfsv3err_pathconf[] = {
3094 	NFSERR_STALE,
3095 	NFSERR_STALE,
3096 	NFSERR_BADHANDLE,
3097 	NFSERR_SERVERFAULT,
3098 	NFSERR_TRYLATER,
3099 	0,
3100 };
3101 
3102 static short nfsv3err_commit[] = {
3103 	NFSERR_IO,
3104 	NFSERR_IO,
3105 	NFSERR_STALE,
3106 	NFSERR_BADHANDLE,
3107 	NFSERR_SERVERFAULT,
3108 	NFSERR_TRYLATER,
3109 	0,
3110 };
3111 
3112 static short *nfsrv_v3errmap[] = {
3113 	nfsv3err_null,
3114 	nfsv3err_getattr,
3115 	nfsv3err_setattr,
3116 	nfsv3err_lookup,
3117 	nfsv3err_access,
3118 	nfsv3err_readlink,
3119 	nfsv3err_read,
3120 	nfsv3err_write,
3121 	nfsv3err_create,
3122 	nfsv3err_mkdir,
3123 	nfsv3err_symlink,
3124 	nfsv3err_mknod,
3125 	nfsv3err_remove,
3126 	nfsv3err_rmdir,
3127 	nfsv3err_rename,
3128 	nfsv3err_link,
3129 	nfsv3err_readdir,
3130 	nfsv3err_readdirplus,
3131 	nfsv3err_fsstat,
3132 	nfsv3err_fsinfo,
3133 	nfsv3err_pathconf,
3134 	nfsv3err_commit,
3135 };
3136 
3137 /*
3138  * Map errnos to NFS error numbers. For Version 3 also filter out error
3139  * numbers not specified for the associated procedure.
3140  */
3141 int
nfsrv_errmap(struct nfsrv_descript * nd,int err)3142 nfsrv_errmap(struct nfsrv_descript *nd, int err)
3143 {
3144 	short *defaulterrp, *errp;
3145 
3146 	if (nd->nd_vers == NFS_VER2) {
3147 		if (err <= (int)sizeof(nfsrv_v2errmap)) {
3148 			return (int)nfsrv_v2errmap[err - 1];
3149 		}
3150 		return NFSERR_IO;
3151 	}
3152 	/* NFSv3 */
3153 	if (nd->nd_procnum > NFSPROC_COMMIT) {
3154 		return err & 0xffff;
3155 	}
3156 	errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
3157 	while (*++errp) {
3158 		if (*errp == err) {
3159 			return err;
3160 		} else if (*errp > err) {
3161 			break;
3162 		}
3163 	}
3164 	return (int)*defaulterrp;
3165 }
3166 
3167 #endif /* CONFIG_NFS_SERVER */
3168