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