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