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