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