1/* $NetBSD: nfs_export.c,v 1.63 2021/06/04 10:44:58 hannken Exp $ */ 2 3/*- 4 * Copyright (c) 1997, 1998, 2004, 2005, 2008, 2019 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * This code is derived from software contributed to The NetBSD Foundation 11 * by Charles M. Hannum. 12 * This code is derived from software contributed to The NetBSD Foundation 13 * by Julio M. Merino Vidal. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37/* 38 * Copyright (c) 1989, 1993 39 * The Regents of the University of California. All rights reserved. 40 * (c) UNIX System Laboratories, Inc. 41 * All or some portions of this file are derived from material licensed 42 * to the University of California by American Telephone and Telegraph 43 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 44 * the permission of UNIX System Laboratories, Inc. 45 * 46 * Redistribution and use in source and binary forms, with or without 47 * modification, are permitted provided that the following conditions 48 * are met: 49 * 1. Redistributions of source code must retain the above copyright 50 * notice, this list of conditions and the following disclaimer. 51 * 2. Redistributions in binary form must reproduce the above copyright 52 * notice, this list of conditions and the following disclaimer in the 53 * documentation and/or other materials provided with the distribution. 54 * 3. Neither the name of the University nor the names of its contributors 55 * may be used to endorse or promote products derived from this software 56 * without specific prior written permission. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 * 70 * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94 71 */ 72 73/* 74 * VFS exports list management. 75 * 76 * Lock order: vfs_busy -> mnt_updating -> netexport_lock. 77 */ 78 79#include <sys/cdefs.h> 80__KERNEL_RCSID(0, "$NetBSD: nfs_export.c,v 1.63 2021/06/04 10:44:58 hannken Exp $"); 81 82#include <sys/param.h> 83#include <sys/systm.h> 84#include <sys/kernel.h> 85#include <sys/queue.h> 86#include <sys/proc.h> 87#include <sys/mount.h> 88#include <sys/vnode.h> 89#include <sys/namei.h> 90#include <sys/errno.h> 91#include <sys/malloc.h> 92#include <sys/domain.h> 93#include <sys/mbuf.h> 94#include <sys/dirent.h> 95#include <sys/socket.h> /* XXX for AF_MAX */ 96#include <sys/kauth.h> 97 98#include <net/radix.h> 99 100#include <netinet/in.h> 101 102#include <nfs/rpcv2.h> 103#include <nfs/nfsproto.h> 104#include <nfs/nfs.h> 105#include <nfs/nfs_var.h> 106 107/* 108 * Network address lookup element. 109 */ 110struct netcred { 111 struct radix_node netc_rnodes[2]; 112 int netc_refcnt; 113 int netc_exflags; 114 kauth_cred_t netc_anon; 115}; 116 117/* 118 * Network export information. 119 */ 120struct netexport { 121 TAILQ_ENTRY(netexport) ne_list; 122 struct mount *ne_mount; 123 struct netcred ne_defexported; /* Default export */ 124 struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */ 125}; 126TAILQ_HEAD(, netexport) netexport_list = 127 TAILQ_HEAD_INITIALIZER(netexport_list); 128 129/* Publicly exported file system. */ 130struct nfs_public nfs_pub; 131 132/* 133 * Local prototypes. 134 */ 135static int init_exports(struct mount *, struct netexport **); 136static int hang_addrlist(struct mount *, struct netexport *, 137 const struct export_args *); 138static int sacheck(struct sockaddr *); 139static int free_netcred(struct radix_node *, void *); 140static int export(struct netexport *, const struct export_args *); 141static int setpublicfs(struct mount *, struct netexport *, 142 const struct export_args *); 143static struct netcred *netcred_lookup(struct netexport *, struct mbuf *); 144static struct netexport *netexport_lookup(const struct mount *); 145static struct netexport *netexport_lookup_byfsid(const fsid_t *); 146static void netexport_clear(struct netexport *); 147static void netexport_insert(struct netexport *); 148static void netexport_remove(struct netexport *); 149static void netexport_wrlock(void); 150static void netexport_wrunlock(void); 151static int nfs_export_update_30(struct mount *mp, const char *path, void *); 152 153static krwlock_t netexport_lock; 154 155/* 156 * PUBLIC INTERFACE 157 */ 158 159/* 160 * Declare and initialize the file system export hooks. 161 */ 162static void netexport_unmount(struct mount *); 163 164struct vfs_hooks nfs_export_hooks = { 165 { NULL, NULL }, 166 .vh_unmount = netexport_unmount, 167 .vh_reexport = nfs_export_update_30, 168}; 169 170/* 171 * VFS unmount hook for NFS exports. 172 * 173 * Releases NFS exports list resources if the given mount point has some. 174 * As allocation happens lazily, it may be that it doesn't have this 175 * information, although it theoretically should. 176 */ 177static void 178netexport_unmount(struct mount *mp) 179{ 180 struct netexport *ne; 181 182 KASSERT(mp != NULL); 183 184 netexport_wrlock(); 185 ne = netexport_lookup(mp); 186 if (ne == NULL) { 187 netexport_wrunlock(); 188 return; 189 } 190 netexport_clear(ne); 191 netexport_remove(ne); 192 netexport_wrunlock(); 193 kmem_free(ne, sizeof(*ne)); 194} 195 196void 197netexport_init(void) 198{ 199 200 rw_init(&netexport_lock); 201} 202 203void 204netexport_fini(void) 205{ 206 struct netexport *ne; 207 struct mount *mp; 208 int error; 209 210 while (!TAILQ_EMPTY(&netexport_list)) { 211 netexport_wrlock(); 212 ne = TAILQ_FIRST(&netexport_list); 213 mp = ne->ne_mount; 214 error = vfs_busy(mp); 215 netexport_wrunlock(); 216 if (error != 0) { 217 kpause("nfsfini", false, hz, NULL); 218 continue; 219 } 220 mutex_enter(mp->mnt_updating); /* mnt_flag */ 221 netexport_unmount(mp); 222 mutex_exit(mp->mnt_updating); /* mnt_flag */ 223 vfs_unbusy(mp); 224 } 225 rw_destroy(&netexport_lock); 226} 227 228 229/* 230 * Atomically set the NFS exports list of the given file system, replacing 231 * it with a new list of entries. 232 * 233 * Returns zero on success or an appropriate error code otherwise. 234 * 235 * Helper function for the nfssvc(2) system call (NFSSVC_SETEXPORTSLIST 236 * and NFSSVC_REPLACEEXPORTSLIST command). 237 */ 238int 239mountd_set_exports_list(const struct mountd_exports_list *mel, struct lwp *l, 240 struct mount *nmp, int cmd) 241{ 242 int error; 243 size_t i; 244 struct mount *mp; 245 struct netexport *ne; 246 struct pathbuf *pb; 247 struct nameidata nd; 248 struct vnode *vp; 249 size_t fid_size; 250 251 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_NFS, 252 KAUTH_REQ_NETWORK_NFS_EXPORT, NULL, NULL, NULL) != 0) 253 return EPERM; 254 255 /* Look up the file system path. */ 256 error = pathbuf_copyin(mel->mel_path, &pb); 257 if (error) { 258 return error; 259 } 260 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pb); 261 error = namei(&nd); 262 if (error != 0) { 263 pathbuf_destroy(pb); 264 return error; 265 } 266 vp = nd.ni_vp; 267 mp = vp->v_mount; 268 KASSERT(nmp == NULL || nmp == mp); 269 pathbuf_destroy(pb); 270 271 /* 272 * Make sure the file system can do vptofh. If the file system 273 * knows the handle's size, just trust it's able to do the 274 * actual translation also (otherwise we should check fhtovp 275 * also, and that's getting a wee bit ridiculous). 276 */ 277 fid_size = 0; 278 if ((error = VFS_VPTOFH(vp, NULL, &fid_size)) != E2BIG) { 279 vput(vp); 280 return EOPNOTSUPP; 281 } 282 283 /* Mark the file system busy. */ 284 error = vfs_busy(mp); 285 vput(vp); 286 if (error != 0) 287 return error; 288 if (nmp == NULL) 289 mutex_enter(mp->mnt_updating); /* mnt_flag */ 290 netexport_wrlock(); 291 ne = netexport_lookup(mp); 292 if (ne == NULL) { 293 error = init_exports(mp, &ne); 294 if (error != 0) { 295 goto out; 296 } 297 } 298 299 KASSERT(ne != NULL); 300 KASSERT(ne->ne_mount == mp); 301 302 if (cmd == NFSSVC_SETEXPORTSLIST) { 303 if (mel->mel_nexports == 0) 304 netexport_clear(ne); 305 else if (mel->mel_nexports == 1) 306 error = export(ne, &mel->mel_exports[0]); 307 else { 308 printf("%s: Cannot set more than one " 309 "entry at once (unimplemented)\n", __func__); 310 error = EOPNOTSUPP; 311 } 312 } else if (cmd == NFSSVC_REPLACEEXPORTSLIST) { 313 netexport_clear(ne); 314 for (i = 0; error == 0 && i < mel->mel_nexports; i++) 315 error = export(ne, &mel->mel_exports[i]); 316 } else { 317 printf("%s: Command %#x not implemented\n", __func__, cmd); 318 error = EOPNOTSUPP; 319 } 320 321out: 322 netexport_wrunlock(); 323 if (nmp == NULL) 324 mutex_exit(mp->mnt_updating); /* mnt_flag */ 325 vfs_unbusy(mp); 326 return error; 327} 328 329static void 330netexport_insert(struct netexport *ne) 331{ 332 333 TAILQ_INSERT_HEAD(&netexport_list, ne, ne_list); 334} 335 336static void 337netexport_remove(struct netexport *ne) 338{ 339 340 TAILQ_REMOVE(&netexport_list, ne, ne_list); 341} 342 343static struct netexport * 344netexport_lookup(const struct mount *mp) 345{ 346 struct netexport *ne; 347 348 TAILQ_FOREACH(ne, &netexport_list, ne_list) { 349 if (ne->ne_mount == mp) { 350 goto done; 351 } 352 } 353 ne = NULL; 354done: 355 return ne; 356} 357 358static struct netexport * 359netexport_lookup_byfsid(const fsid_t *fsid) 360{ 361 struct netexport *ne; 362 363 TAILQ_FOREACH(ne, &netexport_list, ne_list) { 364 const struct mount *mp = ne->ne_mount; 365 366 if (mp->mnt_stat.f_fsidx.__fsid_val[0] == fsid->__fsid_val[0] && 367 mp->mnt_stat.f_fsidx.__fsid_val[1] == fsid->__fsid_val[1]) { 368 goto done; 369 } 370 } 371 ne = NULL; 372done: 373 374 return ne; 375} 376 377/* 378 * Check if the file system specified by the 'mp' mount structure is 379 * exported to a client with 'anon' anonymous credentials. The 'mb' 380 * argument is an mbuf containing the network address of the client. 381 * The return parameters for the export flags for the client are returned 382 * in the address specified by 'wh'. 383 * 384 * This function is used exclusively by the NFS server. It is generally 385 * invoked before VFS_FHTOVP to validate that a client has access to the 386 * file system. 387 */ 388 389int 390netexport_check(const fsid_t *fsid, struct mbuf *mb, struct mount **mpp, 391 int *wh, kauth_cred_t *anon) 392{ 393 struct netexport *ne; 394 struct netcred *np; 395 396 ne = netexport_lookup_byfsid(fsid); 397 if (ne == NULL) { 398 return EACCES; 399 } 400 np = netcred_lookup(ne, mb); 401 if (np == NULL) { 402 return EACCES; 403 } 404 405 *mpp = ne->ne_mount; 406 *wh = np->netc_exflags; 407 *anon = np->netc_anon; 408 409 return 0; 410} 411 412/* 413 * Handles legacy export requests. In this case, the export information 414 * is hardcoded in a specific place of the mount arguments structure (given 415 * in data); the request for an update is given through the fspec field 416 * (also in a known location), which must be a null pointer. 417 * 418 * Returns EJUSTRETURN if the given command was not a export request. 419 * Otherwise, returns 0 on success or an appropriate error code otherwise. 420 */ 421static int 422nfs_export_update_30(struct mount *mp, const char *path, void *data) 423{ 424 struct mountd_exports_list mel; 425 struct mnt_export_args30 *args; 426 427 args = data; 428 mel.mel_path = path; 429 430 if (args->fspec != NULL) 431 return EJUSTRETURN; 432 433 if (args->eargs.ex_flags & 0x00020000) { 434 /* Request to delete exports. The mask above holds the 435 * value that used to be in MNT_DELEXPORT. */ 436 mel.mel_nexports = 0; 437 } else { 438 /* 439 * The following code assumes export_args has not 440 * changed since export_args30, so check that. 441 */ 442 __CTASSERT(sizeof(args->eargs) == sizeof(*mel.mel_exports)); 443 444 mel.mel_nexports = 1; 445 mel.mel_exports = (void *)&args->eargs; 446 } 447 448 return mountd_set_exports_list(&mel, curlwp, mp, NFSSVC_SETEXPORTSLIST); 449} 450 451/* 452 * INTERNAL FUNCTIONS 453 */ 454 455/* 456 * Initializes NFS exports for the mountpoint given in 'mp'. 457 * If successful, returns 0 and sets *nep to the address of the new 458 * netexport item; otherwise returns an appropriate error code 459 * and *nep remains unmodified. 460 */ 461static int 462init_exports(struct mount *mp, struct netexport **nep) 463{ 464 int error; 465 struct export_args ea; 466 struct netexport *ne; 467 468 KASSERT(mp != NULL); 469 470 /* Ensure that we do not already have this mount point. */ 471 KASSERT(netexport_lookup(mp) == NULL); 472 473 ne = kmem_zalloc(sizeof(*ne), KM_SLEEP); 474 ne->ne_mount = mp; 475 476 /* Set the default export entry. Handled internally by export upon 477 * first call. */ 478 memset(&ea, 0, sizeof(ea)); 479 ea.ex_root = -2; 480 if (mp->mnt_flag & MNT_RDONLY) 481 ea.ex_flags |= MNT_EXRDONLY; 482 error = export(ne, &ea); 483 if (error != 0) { 484 kmem_free(ne, sizeof(*ne)); 485 } else { 486 netexport_insert(ne); 487 *nep = ne; 488 } 489 490 return error; 491} 492 493/* 494 * Build hash lists of net addresses and hang them off the mount point. 495 * Called by export() to set up a new entry in the lists of export 496 * addresses. 497 */ 498static int 499hang_addrlist(struct mount *mp, struct netexport *nep, 500 const struct export_args *argp) 501{ 502 int error, i; 503 struct netcred *np, *enp; 504 struct radix_node_head *rnh; 505 struct sockaddr *saddr, *smask; 506 struct domain *dom; 507 508 smask = NULL; 509 510 if (argp->ex_addrlen == 0) { 511 if (mp->mnt_flag & MNT_DEFEXPORTED) 512 return EPERM; 513 np = &nep->ne_defexported; 514 KASSERT(np->netc_anon == NULL); 515 np->netc_anon = kauth_cred_alloc(); 516 np->netc_exflags = argp->ex_flags; 517 kauth_uucred_to_cred(np->netc_anon, &argp->ex_anon); 518 mp->mnt_flag |= MNT_DEFEXPORTED; 519 return 0; 520 } 521 522 if (argp->ex_addrlen > MLEN || argp->ex_masklen > MLEN) 523 return EINVAL; 524 525 i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen; 526 np = malloc(i, M_NETADDR, M_WAITOK | M_ZERO); 527 np->netc_anon = kauth_cred_alloc(); 528 saddr = (struct sockaddr *)(np + 1); 529 error = copyin(argp->ex_addr, saddr, argp->ex_addrlen); 530 if (error) 531 goto out; 532 if (saddr->sa_len > argp->ex_addrlen) 533 saddr->sa_len = argp->ex_addrlen; 534 if (sacheck(saddr) == -1) { 535 error = EINVAL; 536 goto out; 537 } 538 if (argp->ex_masklen) { 539 smask = (struct sockaddr *)((char *)saddr + argp->ex_addrlen); 540 error = copyin(argp->ex_mask, smask, argp->ex_masklen); 541 if (error) 542 goto out; 543 if (smask->sa_len > argp->ex_masklen) 544 smask->sa_len = argp->ex_masklen; 545 if (smask->sa_family != saddr->sa_family) { 546 error = EINVAL; 547 goto out; 548 } 549 if (sacheck(smask) == -1) { 550 error = EINVAL; 551 goto out; 552 } 553 } 554 i = saddr->sa_family; 555 if ((rnh = nep->ne_rtable[i]) == 0) { 556 /* 557 * Seems silly to initialize every AF when most are not 558 * used, do so on demand here. 559 */ 560 DOMAIN_FOREACH(dom) { 561 if (dom->dom_family == i && dom->dom_rtattach) { 562 rn_inithead((void **)&nep->ne_rtable[i], 563 dom->dom_rtoffset); 564 break; 565 } 566 } 567 if ((rnh = nep->ne_rtable[i]) == 0) { 568 error = ENOBUFS; 569 goto out; 570 } 571 } 572 573 enp = (struct netcred *)(*rnh->rnh_addaddr)(saddr, smask, rnh, 574 np->netc_rnodes); 575 if (enp != np) { 576 if (enp == NULL) { 577 enp = (struct netcred *)(*rnh->rnh_lookup)(saddr, 578 smask, rnh); 579 if (enp == NULL) { 580 error = EPERM; 581 goto out; 582 } 583 } else 584 enp->netc_refcnt++; 585 586 goto check; 587 } else 588 enp->netc_refcnt = 1; 589 590 np->netc_exflags = argp->ex_flags; 591 kauth_uucred_to_cred(np->netc_anon, &argp->ex_anon); 592 return 0; 593check: 594 if (enp->netc_exflags != argp->ex_flags || 595 kauth_cred_uucmp(enp->netc_anon, &argp->ex_anon) != 0) 596 error = EPERM; 597 else 598 error = 0; 599out: 600 KASSERT(np->netc_anon != NULL); 601 kauth_cred_free(np->netc_anon); 602 free(np, M_NETADDR); 603 return error; 604} 605 606/* 607 * Ensure that the address stored in 'sa' is valid. 608 * Returns zero on success, otherwise -1. 609 */ 610static int 611sacheck(struct sockaddr *sa) 612{ 613 614 switch (sa->sa_family) { 615 case AF_INET: { 616 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 617 char *p = (char *)sin->sin_zero; 618 size_t i; 619 620 if (sin->sin_len != sizeof(*sin)) 621 return -1; 622 if (sin->sin_port != 0) 623 return -1; 624 for (i = 0; i < sizeof(sin->sin_zero); i++) 625 if (*p++ != '\0') 626 return -1; 627 return 0; 628 } 629 case AF_INET6: { 630 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 631 632 if (sin6->sin6_len != sizeof(*sin6)) 633 return -1; 634 if (sin6->sin6_port != 0) 635 return -1; 636 return 0; 637 } 638 default: 639 return -1; 640 } 641} 642 643/* 644 * Free the netcred object pointed to by the 'rn' radix node. 645 * 'w' holds a pointer to the radix tree head. 646 */ 647static int 648free_netcred(struct radix_node *rn, void *w) 649{ 650 struct radix_node_head *rnh = (struct radix_node_head *)w; 651 struct netcred *np = (struct netcred *)(void *)rn; 652 653 (*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh); 654 if (--(np->netc_refcnt) <= 0) { 655 KASSERT(np->netc_anon != NULL); 656 kauth_cred_free(np->netc_anon); 657 free(np, M_NETADDR); 658 } 659 return 0; 660} 661 662/* 663 * Clears the exports list for a given file system. 664 */ 665static void 666netexport_clear(struct netexport *ne) 667{ 668 struct radix_node_head *rnh; 669 struct mount *mp = ne->ne_mount; 670 int i; 671 672 if (mp->mnt_flag & MNT_EXPUBLIC) { 673 setpublicfs(NULL, NULL, NULL); 674 mp->mnt_flag &= ~MNT_EXPUBLIC; 675 } 676 677 for (i = 0; i <= AF_MAX; i++) { 678 if ((rnh = ne->ne_rtable[i]) != NULL) { 679 rn_walktree(rnh, free_netcred, rnh); 680 free(rnh, M_RTABLE); 681 ne->ne_rtable[i] = NULL; 682 } 683 } 684 685 if ((mp->mnt_flag & MNT_DEFEXPORTED) != 0) { 686 struct netcred *np = &ne->ne_defexported; 687 688 KASSERT(np->netc_anon != NULL); 689 kauth_cred_free(np->netc_anon); 690 np->netc_anon = NULL; 691 } else { 692 KASSERT(ne->ne_defexported.netc_anon == NULL); 693 } 694 695 mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED); 696} 697 698/* 699 * Add a new export entry (described by an export_args structure) to the 700 * given file system. 701 */ 702static int 703export(struct netexport *nep, const struct export_args *argp) 704{ 705 struct mount *mp = nep->ne_mount; 706 int error; 707 708 if (argp->ex_flags & MNT_EXPORTED) { 709 if (argp->ex_flags & MNT_EXPUBLIC) { 710 if ((error = setpublicfs(mp, nep, argp)) != 0) 711 return error; 712 mp->mnt_flag |= MNT_EXPUBLIC; 713 } 714 if ((error = hang_addrlist(mp, nep, argp)) != 0) 715 return error; 716 mp->mnt_flag |= MNT_EXPORTED; 717 } 718 return 0; 719} 720 721/* 722 * Set the publicly exported filesystem (WebNFS). Currently, only 723 * one public filesystem is possible in the spec (RFC 2054 and 2055) 724 */ 725static int 726setpublicfs(struct mount *mp, struct netexport *nep, 727 const struct export_args *argp) 728{ 729 char *cp; 730 int error; 731 struct vnode *rvp; 732 size_t fhsize; 733 734 /* 735 * mp == NULL --> invalidate the current info; the FS is 736 * no longer exported. May be called from either export 737 * or unmount, so check if it hasn't already been done. 738 */ 739 if (mp == NULL) { 740 if (nfs_pub.np_valid) { 741 nfs_pub.np_valid = 0; 742 if (nfs_pub.np_handle != NULL) { 743 free(nfs_pub.np_handle, M_TEMP); 744 nfs_pub.np_handle = NULL; 745 } 746 if (nfs_pub.np_index != NULL) { 747 free(nfs_pub.np_index, M_TEMP); 748 nfs_pub.np_index = NULL; 749 } 750 } 751 return 0; 752 } 753 754 /* 755 * Only one allowed at a time. 756 */ 757 if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount) 758 return EBUSY; 759 760 /* 761 * Get real filehandle for root of exported FS. 762 */ 763 if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp))) 764 return error; 765 766 fhsize = 0; 767 error = vfs_composefh(rvp, NULL, &fhsize); 768 if (error != E2BIG) 769 return error; 770 nfs_pub.np_handle = malloc(fhsize, M_TEMP, M_NOWAIT); 771 if (nfs_pub.np_handle == NULL) 772 error = ENOMEM; 773 else 774 error = vfs_composefh(rvp, nfs_pub.np_handle, &fhsize); 775 if (error) 776 return error; 777 778 vput(rvp); 779 780 /* 781 * If an indexfile was specified, pull it in. 782 */ 783 if (argp->ex_indexfile != NULL) { 784 nfs_pub.np_index = malloc(NFS_MAXNAMLEN + 1, M_TEMP, M_WAITOK); 785 error = copyinstr(argp->ex_indexfile, nfs_pub.np_index, 786 NFS_MAXNAMLEN, (size_t *)0); 787 if (!error) { 788 /* 789 * Check for illegal filenames. 790 */ 791 for (cp = nfs_pub.np_index; *cp; cp++) { 792 if (*cp == '/') { 793 error = EINVAL; 794 break; 795 } 796 } 797 } 798 if (error) { 799 free(nfs_pub.np_index, M_TEMP); 800 return error; 801 } 802 } 803 804 nfs_pub.np_mount = mp; 805 nfs_pub.np_valid = 1; 806 return 0; 807} 808 809/* 810 * Look up an export entry in the exports list that matches the address 811 * stored in 'nam'. If no entry is found, the default one is used instead 812 * (if available). 813 */ 814static struct netcred * 815netcred_lookup(struct netexport *ne, struct mbuf *nam) 816{ 817 struct netcred *np; 818 struct radix_node_head *rnh; 819 struct sockaddr *saddr; 820 821 if ((ne->ne_mount->mnt_flag & MNT_EXPORTED) == 0) { 822 return NULL; 823 } 824 825 /* 826 * Look in the export list first. 827 */ 828 np = NULL; 829 if (nam != NULL) { 830 saddr = mtod(nam, struct sockaddr *); 831 rnh = ne->ne_rtable[saddr->sa_family]; 832 if (rnh != NULL) { 833 np = (struct netcred *) 834 (*rnh->rnh_matchaddr)((void *)saddr, 835 rnh); 836 if (np && np->netc_rnodes->rn_flags & RNF_ROOT) 837 np = NULL; 838 } 839 } 840 /* 841 * If no address match, use the default if it exists. 842 */ 843 if (np == NULL && ne->ne_mount->mnt_flag & MNT_DEFEXPORTED) 844 np = &ne->ne_defexported; 845 846 return np; 847} 848 849void 850netexport_rdlock(void) 851{ 852 853 rw_enter(&netexport_lock, RW_READER); 854} 855 856void 857netexport_rdunlock(void) 858{ 859 860 rw_exit(&netexport_lock); 861} 862 863static void 864netexport_wrlock(void) 865{ 866 867 rw_enter(&netexport_lock, RW_WRITER); 868} 869 870static void 871netexport_wrunlock(void) 872{ 873 874 rw_exit(&netexport_lock); 875} 876 877bool 878netexport_hasexports(void) 879{ 880 881 return nfs_pub.np_valid || !TAILQ_EMPTY(&netexport_list); 882} 883