1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Herb Hasler and Rick Macklem at The University of Guelph. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/conf.h> 37 #include <sys/fcntl.h> 38 #include <sys/fnv_hash.h> 39 #include <sys/linker.h> 40 #include <sys/module.h> 41 #include <sys/mount.h> 42 #include <sys/queue.h> 43 #include <sys/stat.h> 44 #include <sys/sysctl.h> 45 #include <sys/syslog.h> 46 47 #include <rpc/rpc.h> 48 #include <rpc/rpc_com.h> 49 #include <rpc/pmap_clnt.h> 50 #include <rpc/pmap_prot.h> 51 #include <rpcsvc/mount.h> 52 #include <nfs/nfsproto.h> 53 #include <nfs/nfssvc.h> 54 #include <nfsserver/nfs.h> 55 56 #include <fs/nfs/nfsport.h> 57 58 #include <arpa/inet.h> 59 60 #include <assert.h> 61 #include <ctype.h> 62 #include <err.h> 63 #include <errno.h> 64 #include <grp.h> 65 #include <libutil.h> 66 #include <limits.h> 67 #include <netdb.h> 68 #include <pwd.h> 69 #include <signal.h> 70 #include <stdio.h> 71 #include <stdlib.h> 72 #include <string.h> 73 #include <unistd.h> 74 #include <vis.h> 75 #include "pathnames.h" 76 #include "mntopts.h" 77 78 #ifdef DEBUG 79 #include <stdarg.h> 80 #endif 81 82 /* 83 * Structures for keeping the mount list and export list 84 */ 85 struct mountlist { 86 char ml_host[MNTNAMLEN+1]; 87 char ml_dirp[MNTPATHLEN+1]; 88 89 SLIST_ENTRY(mountlist) next; 90 }; 91 92 struct dirlist { 93 struct dirlist *dp_left; 94 struct dirlist *dp_right; 95 int dp_flag; 96 struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 97 char *dp_dirp; 98 }; 99 /* dp_flag bits */ 100 #define DP_DEFSET 0x1 101 #define DP_HOSTSET 0x2 102 103 /* 104 * maproot/mapall credentials. 105 * cr_smallgrps can be used for a group list up to SMALLNGROUPS in size. 106 * Larger group lists are malloc'd/free'd. 107 */ 108 #define SMALLNGROUPS 32 109 struct expcred { 110 uid_t cr_uid; 111 int cr_ngroups; 112 gid_t cr_smallgrps[SMALLNGROUPS]; 113 gid_t *cr_groups; 114 }; 115 116 struct exportlist { 117 struct dirlist *ex_dirl; 118 struct dirlist *ex_defdir; 119 struct grouplist *ex_grphead; 120 int ex_flag; 121 fsid_t ex_fs; 122 char *ex_fsdir; 123 char *ex_indexfile; 124 struct expcred ex_defanon; 125 uint64_t ex_defexflags; 126 int ex_numsecflavors; 127 int ex_secflavors[MAXSECFLAVORS]; 128 int ex_defnumsecflavors; 129 int ex_defsecflavors[MAXSECFLAVORS]; 130 131 SLIST_ENTRY(exportlist) entries; 132 }; 133 /* ex_flag bits */ 134 #define EX_LINKED 0x01 135 #define EX_DONE 0x02 136 #define EX_DEFSET 0x04 137 #define EX_PUBLICFH 0x08 138 #define EX_ADMINWARN 0x10 139 140 SLIST_HEAD(exportlisthead, exportlist); 141 142 struct netmsk { 143 struct sockaddr_storage nt_net; 144 struct sockaddr_storage nt_mask; 145 char *nt_name; 146 }; 147 148 union grouptypes { 149 struct addrinfo *gt_addrinfo; 150 struct netmsk gt_net; 151 }; 152 153 struct grouplist { 154 int gr_type; 155 union grouptypes gr_ptr; 156 struct grouplist *gr_next; 157 struct expcred gr_anon; 158 uint64_t gr_exflags; 159 int gr_flag; 160 int gr_numsecflavors; 161 int gr_secflavors[MAXSECFLAVORS]; 162 }; 163 /* Group types */ 164 #define GT_NULL 0x0 165 #define GT_HOST 0x1 166 #define GT_NET 0x2 167 #define GT_DEFAULT 0x3 168 #define GT_IGNORE 0x5 169 170 /* Group flags */ 171 #define GR_FND 0x1 172 173 struct hostlist { 174 int ht_flag; /* Uses DP_xx bits */ 175 struct grouplist *ht_grp; 176 struct hostlist *ht_next; 177 }; 178 179 struct fhreturn { 180 int fhr_flag; 181 int fhr_vers; 182 nfsfh_t fhr_fh; 183 int fhr_numsecflavors; 184 int *fhr_secflavors; 185 }; 186 187 #define GETPORT_MAXTRY 20 /* Max tries to get a port # */ 188 189 /* 190 * How long to delay a reload of exports when there are RPC request(s) 191 * to process, in usec. Must be less than 1second. 192 */ 193 #define RELOADDELAY 250000 194 195 /* Global defs */ 196 static char *add_expdir(struct dirlist **, char *, int); 197 static void add_dlist(struct dirlist **, struct dirlist *, 198 struct grouplist *, int, struct exportlist *, 199 struct expcred *, uint64_t); 200 static void add_mlist(char *, char *); 201 static int check_path_component(const char *, char **); 202 static int check_dirpath(char *, char **); 203 static int check_statfs(const char *, struct statfs *, char **); 204 static int check_options(struct dirlist *); 205 static int checkmask(struct sockaddr *sa); 206 static int chk_host(struct dirlist *, struct sockaddr *, int *, int *, 207 int *, int **); 208 static char *strsep_quote(char **stringp, const char *delim); 209 static int create_service(struct netconfig *nconf); 210 static void complete_service(struct netconfig *nconf, char *port_str); 211 static void clearout_service(void); 212 static void del_mlist(char *hostp, char *dirp); 213 static struct dirlist *dirp_search(struct dirlist *, char *); 214 static int do_export_mount(struct exportlist *, struct statfs *); 215 static int do_mount(struct exportlist *, struct grouplist *, uint64_t, 216 struct expcred *, char *, int, struct statfs *, int, int *); 217 static int do_opt(char **, char **, struct exportlist *, 218 struct grouplist *, int *, uint64_t *, struct expcred *, 219 char *); 220 static struct exportlist *ex_search(fsid_t *, struct exportlisthead *); 221 static struct exportlist *get_exp(void); 222 static void free_dir(struct dirlist *); 223 static void free_exp(struct exportlist *); 224 static void free_grp(struct grouplist *); 225 static void free_host(struct hostlist *); 226 static void free_v4rootexp(void); 227 static void get_exportlist_one(int); 228 static void get_exportlist(int); 229 static void insert_exports(struct exportlist *, struct exportlisthead *); 230 static void free_exports(struct exportlisthead *); 231 static void read_exportfile(int); 232 static int compare_nmount_exportlist(struct iovec *, int, char *); 233 static int compare_export(struct exportlist *, struct exportlist *); 234 static int compare_cred(struct expcred *, struct expcred *); 235 static int compare_secflavor(int *, int *, int); 236 static void delete_export(struct iovec *, int, struct statfs *, char *); 237 static int get_host(char *, struct grouplist *, struct grouplist *); 238 static struct hostlist *get_ht(void); 239 static int get_line(void); 240 static void get_mountlist(void); 241 static int get_net(char *, struct netmsk *, int); 242 static void getexp_err(struct exportlist *, struct grouplist *, const char *); 243 static struct grouplist *get_grp(void); 244 static void hang_dirp(struct dirlist *, struct grouplist *, 245 struct exportlist *, int, struct expcred *, uint64_t); 246 static void huphandler(int sig); 247 static int makemask(struct sockaddr_storage *ssp, int bitlen); 248 static void mntsrv(struct svc_req *, SVCXPRT *); 249 static void nextfield(char **, char **); 250 static void out_of_mem(void) __dead2; 251 static void parsecred(char *, struct expcred *); 252 static int parsesec(char *, struct exportlist *); 253 static int put_exlist(struct dirlist *, XDR *, struct dirlist *, 254 int *, int); 255 static void *sa_rawaddr(struct sockaddr *sa, int *nbytes); 256 static int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, 257 struct sockaddr *samask); 258 static int scan_tree(struct dirlist *, struct sockaddr *); 259 static void usage(void); 260 static int xdr_dir(XDR *, char *); 261 static int xdr_explist(XDR *, caddr_t); 262 static int xdr_explist_brief(XDR *, caddr_t); 263 static int xdr_explist_common(XDR *, caddr_t, int); 264 static int xdr_fhs(XDR *, caddr_t); 265 static int xdr_mlist(XDR *, caddr_t); 266 static void terminate(int); 267 static void cp_cred(struct expcred *, struct expcred *); 268 269 static gid_t nogroup(); 270 271 #define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize) 272 static struct exportlisthead *exphead = NULL; 273 static struct exportlisthead *oldexphead = NULL; 274 static int exphashsize = 0; 275 static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead); 276 static char *exnames_default[2] = { _PATH_EXPORTS, NULL }; 277 static char **exnames; 278 static char **hosts = NULL; 279 static int force_v2 = 0; 280 static int warn_admin = 1; 281 static int resvport_only = 1; 282 static int nhosts = 0; 283 static int dir_only = 1; 284 static int dolog = 0; 285 static _Atomic(int) got_sighup = 0; 286 static int xcreated = 0; 287 288 static char *svcport_str = NULL; 289 static int mallocd_svcport = 0; 290 static int *sock_fd; 291 static int sock_fdcnt; 292 static int sock_fdpos; 293 static int suspend_nfsd = 0; 294 static int nofork = 0; 295 static int skiplocalhost = 0; 296 static int alldirs_fail = 0; 297 298 static int opt_flags; 299 static int have_v6 = 1; 300 301 static int v4root_phase = 0; 302 static char v4root_dirpath[PATH_MAX + 1]; 303 static struct exportlist *v4root_ep = NULL; 304 static int has_publicfh = 0; 305 static int has_set_publicfh = 0; 306 307 static struct pidfh *pfh = NULL; 308 309 /* Temporary storage for credentials' groups. */ 310 static long tngroups_max; 311 static gid_t *tmp_groups = NULL; 312 313 /* Bits for opt_flags above */ 314 #define OP_MAPROOT 0x01 315 #define OP_MAPALL 0x02 316 /* 0x4 free */ 317 #define OP_MASK 0x08 318 #define OP_NET 0x10 319 #define OP_ALLDIRS 0x40 320 #define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ 321 #define OP_QUIET 0x100 322 #define OP_MASKLEN 0x200 323 #define OP_SEC 0x400 324 #define OP_CLASSMASK 0x800 /* mask not specified, is Class A/B/C default */ 325 #define OP_NOTROOT 0x1000 /* Mark the the mount path is not a fs root */ 326 327 #ifdef DEBUG 328 static int debug = 1; 329 static void SYSLOG(int, const char *, ...) __printflike(2, 3); 330 #define syslog SYSLOG 331 #else 332 static int debug = 0; 333 #endif 334 335 /* 336 * The LOGDEBUG() syslog() calls are always compiled into the daemon. 337 * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty. 338 * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG. 339 */ 340 static int logdebug = 0; 341 #define LOGDEBUG(format, ...) \ 342 (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0) 343 344 /* 345 * Similar to strsep(), but it allows for quoted strings 346 * and escaped characters. 347 * 348 * It returns the string (or NULL, if *stringp is NULL), 349 * which is a de-quoted version of the string if necessary. 350 * 351 * It modifies *stringp in place. 352 */ 353 static char * 354 strsep_quote(char **stringp, const char *delim) 355 { 356 char *srcptr, *dstptr, *retval; 357 char quot = 0; 358 359 if (stringp == NULL || *stringp == NULL) 360 return (NULL); 361 362 srcptr = dstptr = retval = *stringp; 363 364 while (*srcptr) { 365 /* 366 * We're looking for several edge cases here. 367 * First: if we're in quote state (quot != 0), 368 * then we ignore the delim characters, but otherwise 369 * process as normal, unless it is the quote character. 370 * Second: if the current character is a backslash, 371 * we take the next character as-is, without checking 372 * for delim, quote, or backslash. Exception: if the 373 * next character is a NUL, that's the end of the string. 374 * Third: if the character is a quote character, we toggle 375 * quote state. 376 * Otherwise: check the current character for NUL, or 377 * being in delim, and end the string if either is true. 378 */ 379 if (*srcptr == '\\') { 380 srcptr++; 381 /* 382 * The edge case here is if the next character 383 * is NUL, we want to stop processing. But if 384 * it's not NUL, then we simply want to copy it. 385 */ 386 if (*srcptr) { 387 *dstptr++ = *srcptr++; 388 } 389 continue; 390 } 391 if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) { 392 quot = *srcptr++; 393 continue; 394 } 395 if (quot && *srcptr == quot) { 396 /* End of the quoted part */ 397 quot = 0; 398 srcptr++; 399 continue; 400 } 401 if (!quot && strchr(delim, *srcptr)) 402 break; 403 *dstptr++ = *srcptr++; 404 } 405 406 *stringp = (*srcptr == '\0') ? NULL : srcptr + 1; 407 *dstptr = 0; /* Terminate the string */ 408 return (retval); 409 } 410 411 /* 412 * Mountd server for NFS mount protocol as described in: 413 * NFS: Network File System Protocol Specification, RFC1094, Appendix A 414 * The optional arguments are the exports file name 415 * default: _PATH_EXPORTS 416 * and "-n" to allow nonroot mount. 417 */ 418 int 419 main(int argc, char **argv) 420 { 421 fd_set readfds; 422 struct netconfig *nconf; 423 char *endptr, **hosts_bak; 424 void *nc_handle; 425 pid_t otherpid; 426 in_port_t svcport; 427 int c, k, s; 428 int maxrec = RPC_MAXDATASIZE; 429 int attempt_cnt, port_len, port_pos, ret; 430 char **port_list; 431 uint64_t curtime, nexttime; 432 struct timeval tv; 433 struct timespec tp; 434 sigset_t sig_mask, sighup_mask; 435 int enable_rpcbind; 436 437 enable_rpcbind = 1; 438 /* Check that another mountd isn't already running. */ 439 pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); 440 if (pfh == NULL) { 441 if (errno == EEXIST) 442 errx(1, "mountd already running, pid: %d.", otherpid); 443 warn("cannot open or create pidfile"); 444 } 445 446 openlog("mountd", LOG_PID, LOG_DAEMON); 447 448 /* How many groups do we support? */ 449 tngroups_max = sysconf(_SC_NGROUPS_MAX); 450 if (tngroups_max == -1) 451 tngroups_max = NGROUPS_MAX; 452 /* Add space for the effective GID. */ 453 ++tngroups_max; 454 tmp_groups = malloc(tngroups_max); 455 if (tmp_groups == NULL) 456 out_of_mem(); 457 458 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 459 if (s < 0) 460 have_v6 = 0; 461 else 462 close(s); 463 464 while ((c = getopt(argc, argv, "2Aadeh:lNnp:RrSs")) != -1) 465 switch (c) { 466 case '2': 467 force_v2 = 1; 468 break; 469 case 'A': 470 warn_admin = 0; 471 break; 472 case 'a': 473 alldirs_fail = 1; 474 break; 475 case 'e': 476 /* now a no-op, since this is the default */ 477 break; 478 case 'n': 479 resvport_only = 0; 480 break; 481 case 'R': 482 /* Do not support Mount protocol */ 483 enable_rpcbind = 0; 484 break; 485 case 'r': 486 dir_only = 0; 487 break; 488 case 'd': 489 debug = debug ? 0 : 1; 490 break; 491 case 'l': 492 dolog = 1; 493 break; 494 case 'p': 495 endptr = NULL; 496 svcport = (in_port_t)strtoul(optarg, &endptr, 10); 497 if (endptr == NULL || *endptr != '\0' || 498 svcport == 0 || svcport >= IPPORT_MAX) 499 usage(); 500 svcport_str = strdup(optarg); 501 break; 502 case 'h': 503 ++nhosts; 504 hosts_bak = hosts; 505 hosts_bak = realloc(hosts, nhosts * sizeof(char *)); 506 if (hosts_bak == NULL) { 507 if (hosts != NULL) { 508 for (k = 0; k < nhosts; k++) 509 free(hosts[k]); 510 free(hosts); 511 out_of_mem(); 512 } 513 } 514 hosts = hosts_bak; 515 hosts[nhosts - 1] = strdup(optarg); 516 if (hosts[nhosts - 1] == NULL) { 517 for (k = 0; k < (nhosts - 1); k++) 518 free(hosts[k]); 519 free(hosts); 520 out_of_mem(); 521 } 522 break; 523 case 'S': 524 suspend_nfsd = 1; 525 break; 526 case 'N': 527 nofork = 1; 528 break; 529 case 's': 530 skiplocalhost = 1; 531 break; 532 default: 533 usage(); 534 } 535 if (enable_rpcbind == 0) { 536 if (svcport_str != NULL) { 537 warnx("-p option not compatible with -R, ignored"); 538 free(svcport_str); 539 svcport_str = NULL; 540 } 541 if (nhosts > 0) { 542 warnx("-h option not compatible with -R, ignored"); 543 for (k = 0; k < nhosts; k++) 544 free(hosts[k]); 545 free(hosts); 546 hosts = NULL; 547 nhosts = 0; 548 } 549 } 550 551 if (nhosts == 0 && skiplocalhost != 0) 552 warnx("-s without -h, ignored"); 553 554 if (modfind("nfsd") < 0) { 555 /* Not present in kernel, try loading it */ 556 if (kldload("nfsd") < 0 || modfind("nfsd") < 0) 557 errx(1, "NFS server is not available"); 558 } 559 560 argc -= optind; 561 argv += optind; 562 if (argc > 0) 563 exnames = argv; 564 else 565 exnames = exnames_default; 566 if (debug) 567 warnx("getting export list"); 568 get_exportlist(0); 569 if (debug) 570 warnx("getting mount list"); 571 get_mountlist(); 572 if (debug) 573 warnx("here we go"); 574 if (debug == 0 && nofork == 0) { 575 daemon(0, 0); 576 signal(SIGINT, SIG_IGN); 577 signal(SIGQUIT, SIG_IGN); 578 } 579 signal(SIGHUP, huphandler); 580 signal(SIGTERM, terminate); 581 signal(SIGPIPE, SIG_IGN); 582 583 pidfile_write(pfh); 584 585 if (enable_rpcbind != 0) { 586 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 587 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 588 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 589 590 if (!resvport_only) { 591 if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL, 592 &resvport_only, sizeof(resvport_only)) != 0 && 593 errno != ENOENT) { 594 syslog(LOG_ERR, "sysctl: %m"); 595 exit(1); 596 } 597 } 598 599 /* 600 * If no hosts were specified, add a wildcard entry to bind to 601 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added 602 * to the list. 603 */ 604 if (nhosts == 0) { 605 hosts = malloc(sizeof(char *)); 606 if (hosts == NULL) 607 out_of_mem(); 608 hosts[0] = "*"; 609 nhosts = 1; 610 } else if (skiplocalhost == 0) { 611 hosts_bak = hosts; 612 if (have_v6) { 613 hosts_bak = realloc(hosts, (nhosts + 2) * 614 sizeof(char *)); 615 if (hosts_bak == NULL) { 616 for (k = 0; k < nhosts; k++) 617 free(hosts[k]); 618 free(hosts); 619 out_of_mem(); 620 } else 621 hosts = hosts_bak; 622 nhosts += 2; 623 hosts[nhosts - 2] = "::1"; 624 } else { 625 hosts_bak = realloc(hosts, (nhosts + 1) * 626 sizeof(char *)); 627 if (hosts_bak == NULL) { 628 for (k = 0; k < nhosts; k++) 629 free(hosts[k]); 630 free(hosts); 631 out_of_mem(); 632 } else { 633 nhosts += 1; 634 hosts = hosts_bak; 635 } 636 } 637 638 hosts[nhosts - 1] = "127.0.0.1"; 639 } 640 } 641 642 attempt_cnt = 1; 643 sock_fdcnt = 0; 644 sock_fd = NULL; 645 port_list = NULL; 646 port_len = 0; 647 if (enable_rpcbind != 0) { 648 nc_handle = setnetconfig(); 649 while ((nconf = getnetconfig(nc_handle))) { 650 if (nconf->nc_flag & NC_VISIBLE) { 651 if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 652 "inet6") == 0) { 653 /* DO NOTHING */ 654 } else { 655 ret = create_service(nconf); 656 if (ret == 1) 657 /* Ignore this call */ 658 continue; 659 if (ret < 0) { 660 /* 661 * Failed to bind port, so close 662 * off all sockets created and 663 * try again if the port# was 664 * dynamically assigned via 665 * bind(2). 666 */ 667 clearout_service(); 668 if (mallocd_svcport != 0 && 669 attempt_cnt < 670 GETPORT_MAXTRY) { 671 free(svcport_str); 672 svcport_str = NULL; 673 mallocd_svcport = 0; 674 } else { 675 errno = EADDRINUSE; 676 syslog(LOG_ERR, 677 "bindresvport_sa:" 678 " %m"); 679 exit(1); 680 } 681 682 /* 683 * Start over at the first 684 * service. 685 */ 686 free(sock_fd); 687 sock_fdcnt = 0; 688 sock_fd = NULL; 689 nc_handle = setnetconfig(); 690 attempt_cnt++; 691 } else if (mallocd_svcport != 0 && 692 attempt_cnt == GETPORT_MAXTRY) { 693 /* 694 * For the last attempt, allow 695 * different port #s for each 696 * nconf by saving the 697 * svcport_str setting it back 698 * to NULL. 699 */ 700 port_list = realloc(port_list, 701 (port_len + 1) * 702 sizeof(char *)); 703 if (port_list == NULL) 704 out_of_mem(); 705 port_list[port_len++] = 706 svcport_str; 707 svcport_str = NULL; 708 mallocd_svcport = 0; 709 } 710 } 711 } 712 } 713 714 /* 715 * Successfully bound the ports, so call complete_service() to 716 * do the rest of the setup on the service(s). 717 */ 718 sock_fdpos = 0; 719 port_pos = 0; 720 nc_handle = setnetconfig(); 721 while ((nconf = getnetconfig(nc_handle))) { 722 if (nconf->nc_flag & NC_VISIBLE) { 723 if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 724 "inet6") == 0) { 725 /* DO NOTHING */ 726 } else if (port_list != NULL) { 727 if (port_pos >= port_len) { 728 syslog(LOG_ERR, "too many" 729 " port#s"); 730 exit(1); 731 } 732 complete_service(nconf, 733 port_list[port_pos++]); 734 } else 735 complete_service(nconf, svcport_str); 736 } 737 } 738 endnetconfig(nc_handle); 739 free(sock_fd); 740 if (port_list != NULL) { 741 for (port_pos = 0; port_pos < port_len; port_pos++) 742 free(port_list[port_pos]); 743 free(port_list); 744 } 745 746 if (xcreated == 0) { 747 syslog(LOG_ERR, "could not create any services"); 748 exit(1); 749 } 750 } 751 752 /* Expand svc_run() here so that we can call get_exportlist(). */ 753 curtime = nexttime = 0; 754 sigemptyset(&sighup_mask); 755 sigaddset(&sighup_mask, SIGHUP); 756 for (;;) { 757 clock_gettime(CLOCK_MONOTONIC, &tp); 758 curtime = tp.tv_sec; 759 curtime = curtime * 1000000 + tp.tv_nsec / 1000; 760 sigprocmask(SIG_BLOCK, &sighup_mask, &sig_mask); 761 if (got_sighup && curtime >= nexttime) { 762 got_sighup = 0; 763 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 764 get_exportlist(1); 765 clock_gettime(CLOCK_MONOTONIC, &tp); 766 nexttime = tp.tv_sec; 767 nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 + 768 RELOADDELAY; 769 } else 770 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 771 772 /* 773 * If a reload is pending, poll for received request(s), 774 * otherwise set a RELOADDELAY timeout, since a SIGHUP 775 * could be processed between the got_sighup test and 776 * the select() system call. 777 */ 778 tv.tv_sec = 0; 779 if (got_sighup) 780 tv.tv_usec = 0; 781 else 782 tv.tv_usec = RELOADDELAY; 783 if (enable_rpcbind != 0) { 784 readfds = svc_fdset; 785 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, 786 &tv)) { 787 case -1: 788 if (errno == EINTR) { 789 /* Allow a reload now. */ 790 nexttime = 0; 791 continue; 792 } 793 syslog(LOG_ERR, "mountd died: select: %m"); 794 exit(1); 795 case 0: 796 /* Allow a reload now. */ 797 nexttime = 0; 798 continue; 799 default: 800 svc_getreqset(&readfds); 801 } 802 } else { 803 /* Simply wait for a signal. */ 804 sigsuspend(&sig_mask); 805 } 806 } 807 } 808 809 /* 810 * This routine creates and binds sockets on the appropriate 811 * addresses. It gets called one time for each transport. 812 * It returns 0 upon success, 1 for ignore the call and -1 to indicate 813 * bind failed with EADDRINUSE. 814 * Any file descriptors that have been created are stored in sock_fd and 815 * the total count of them is maintained in sock_fdcnt. 816 */ 817 static int 818 create_service(struct netconfig *nconf) 819 { 820 struct addrinfo hints, *res = NULL; 821 struct sockaddr_in *sin; 822 struct sockaddr_in6 *sin6; 823 struct __rpc_sockinfo si; 824 int aicode; 825 int fd; 826 int nhostsbak; 827 int one = 1; 828 int r; 829 u_int32_t host_addr[4]; /* IPv4 or IPv6 */ 830 int mallocd_res; 831 832 if ((nconf->nc_semantics != NC_TPI_CLTS) && 833 (nconf->nc_semantics != NC_TPI_COTS) && 834 (nconf->nc_semantics != NC_TPI_COTS_ORD)) 835 return (1); /* not my type */ 836 837 /* 838 * XXX - using RPC library internal functions. 839 */ 840 if (!__rpc_nconf2sockinfo(nconf, &si)) { 841 syslog(LOG_ERR, "cannot get information for %s", 842 nconf->nc_netid); 843 return (1); 844 } 845 846 /* Get mountd's address on this transport */ 847 memset(&hints, 0, sizeof hints); 848 hints.ai_family = si.si_af; 849 hints.ai_socktype = si.si_socktype; 850 hints.ai_protocol = si.si_proto; 851 852 /* 853 * Bind to specific IPs if asked to 854 */ 855 nhostsbak = nhosts; 856 while (nhostsbak > 0) { 857 --nhostsbak; 858 sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int)); 859 if (sock_fd == NULL) 860 out_of_mem(); 861 sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */ 862 mallocd_res = 0; 863 864 hints.ai_flags = AI_PASSIVE; 865 866 /* 867 * XXX - using RPC library internal functions. 868 */ 869 if ((fd = __rpc_nconf2fd(nconf)) < 0) { 870 int non_fatal = 0; 871 if (errno == EAFNOSUPPORT && 872 nconf->nc_semantics != NC_TPI_CLTS) 873 non_fatal = 1; 874 875 syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 876 "cannot create socket for %s", nconf->nc_netid); 877 if (non_fatal != 0) 878 continue; 879 exit(1); 880 } 881 882 switch (hints.ai_family) { 883 case AF_INET: 884 if (inet_pton(AF_INET, hosts[nhostsbak], 885 host_addr) == 1) { 886 hints.ai_flags |= AI_NUMERICHOST; 887 } else { 888 /* 889 * Skip if we have an AF_INET6 address. 890 */ 891 if (inet_pton(AF_INET6, hosts[nhostsbak], 892 host_addr) == 1) { 893 close(fd); 894 continue; 895 } 896 } 897 break; 898 case AF_INET6: 899 if (inet_pton(AF_INET6, hosts[nhostsbak], 900 host_addr) == 1) { 901 hints.ai_flags |= AI_NUMERICHOST; 902 } else { 903 /* 904 * Skip if we have an AF_INET address. 905 */ 906 if (inet_pton(AF_INET, hosts[nhostsbak], 907 host_addr) == 1) { 908 close(fd); 909 continue; 910 } 911 } 912 913 /* 914 * We're doing host-based access checks here, so don't 915 * allow v4-in-v6 to confuse things. The kernel will 916 * disable it by default on NFS sockets too. 917 */ 918 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, 919 sizeof one) < 0) { 920 syslog(LOG_ERR, 921 "can't disable v4-in-v6 on IPv6 socket"); 922 exit(1); 923 } 924 break; 925 default: 926 break; 927 } 928 929 /* 930 * If no hosts were specified, just bind to INADDR_ANY 931 */ 932 if (strcmp("*", hosts[nhostsbak]) == 0) { 933 if (svcport_str == NULL) { 934 res = malloc(sizeof(struct addrinfo)); 935 if (res == NULL) 936 out_of_mem(); 937 mallocd_res = 1; 938 res->ai_flags = hints.ai_flags; 939 res->ai_family = hints.ai_family; 940 res->ai_protocol = hints.ai_protocol; 941 switch (res->ai_family) { 942 case AF_INET: 943 sin = malloc(sizeof(struct sockaddr_in)); 944 if (sin == NULL) 945 out_of_mem(); 946 sin->sin_family = AF_INET; 947 sin->sin_port = htons(0); 948 sin->sin_addr.s_addr = htonl(INADDR_ANY); 949 res->ai_addr = (struct sockaddr*) sin; 950 res->ai_addrlen = (socklen_t) 951 sizeof(struct sockaddr_in); 952 break; 953 case AF_INET6: 954 sin6 = malloc(sizeof(struct sockaddr_in6)); 955 if (sin6 == NULL) 956 out_of_mem(); 957 sin6->sin6_family = AF_INET6; 958 sin6->sin6_port = htons(0); 959 sin6->sin6_addr = in6addr_any; 960 res->ai_addr = (struct sockaddr*) sin6; 961 res->ai_addrlen = (socklen_t) 962 sizeof(struct sockaddr_in6); 963 break; 964 default: 965 syslog(LOG_ERR, "bad addr fam %d", 966 res->ai_family); 967 exit(1); 968 } 969 } else { 970 if ((aicode = getaddrinfo(NULL, svcport_str, 971 &hints, &res)) != 0) { 972 syslog(LOG_ERR, 973 "cannot get local address for %s: %s", 974 nconf->nc_netid, 975 gai_strerror(aicode)); 976 close(fd); 977 continue; 978 } 979 } 980 } else { 981 if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, 982 &hints, &res)) != 0) { 983 syslog(LOG_ERR, 984 "cannot get local address for %s: %s", 985 nconf->nc_netid, gai_strerror(aicode)); 986 close(fd); 987 continue; 988 } 989 } 990 991 /* Store the fd. */ 992 sock_fd[sock_fdcnt - 1] = fd; 993 994 /* Now, attempt the bind. */ 995 r = bindresvport_sa(fd, res->ai_addr); 996 if (r != 0) { 997 if (errno == EADDRINUSE && mallocd_svcport != 0) { 998 if (mallocd_res != 0) { 999 free(res->ai_addr); 1000 free(res); 1001 } else 1002 freeaddrinfo(res); 1003 return (-1); 1004 } 1005 syslog(LOG_ERR, "bindresvport_sa: %m"); 1006 exit(1); 1007 } 1008 1009 if (svcport_str == NULL) { 1010 svcport_str = malloc(NI_MAXSERV * sizeof(char)); 1011 if (svcport_str == NULL) 1012 out_of_mem(); 1013 mallocd_svcport = 1; 1014 1015 if (getnameinfo(res->ai_addr, 1016 res->ai_addr->sa_len, NULL, NI_MAXHOST, 1017 svcport_str, NI_MAXSERV * sizeof(char), 1018 NI_NUMERICHOST | NI_NUMERICSERV)) 1019 errx(1, "Cannot get port number"); 1020 } 1021 if (mallocd_res != 0) { 1022 free(res->ai_addr); 1023 free(res); 1024 } else 1025 freeaddrinfo(res); 1026 res = NULL; 1027 } 1028 return (0); 1029 } 1030 1031 /* 1032 * Called after all the create_service() calls have succeeded, to complete 1033 * the setup and registration. 1034 */ 1035 static void 1036 complete_service(struct netconfig *nconf, char *port_str) 1037 { 1038 struct addrinfo hints, *res = NULL; 1039 struct __rpc_sockinfo si; 1040 struct netbuf servaddr; 1041 SVCXPRT *transp = NULL; 1042 int aicode, fd, nhostsbak; 1043 int registered = 0; 1044 1045 if ((nconf->nc_semantics != NC_TPI_CLTS) && 1046 (nconf->nc_semantics != NC_TPI_COTS) && 1047 (nconf->nc_semantics != NC_TPI_COTS_ORD)) 1048 return; /* not my type */ 1049 1050 /* 1051 * XXX - using RPC library internal functions. 1052 */ 1053 if (!__rpc_nconf2sockinfo(nconf, &si)) { 1054 syslog(LOG_ERR, "cannot get information for %s", 1055 nconf->nc_netid); 1056 return; 1057 } 1058 1059 nhostsbak = nhosts; 1060 while (nhostsbak > 0) { 1061 --nhostsbak; 1062 if (sock_fdpos >= sock_fdcnt) { 1063 /* Should never happen. */ 1064 syslog(LOG_ERR, "Ran out of socket fd's"); 1065 return; 1066 } 1067 fd = sock_fd[sock_fdpos++]; 1068 if (fd < 0) 1069 continue; 1070 1071 /* 1072 * Using -1 tells listen(2) to use 1073 * kern.ipc.soacceptqueue for the backlog. 1074 */ 1075 if (nconf->nc_semantics != NC_TPI_CLTS) 1076 listen(fd, -1); 1077 1078 if (nconf->nc_semantics == NC_TPI_CLTS ) 1079 transp = svc_dg_create(fd, 0, 0); 1080 else 1081 transp = svc_vc_create(fd, RPC_MAXDATASIZE, 1082 RPC_MAXDATASIZE); 1083 1084 if (transp != (SVCXPRT *) NULL) { 1085 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv, 1086 NULL)) 1087 syslog(LOG_ERR, 1088 "can't register %s MOUNTVERS service", 1089 nconf->nc_netid); 1090 if (!force_v2) { 1091 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3, 1092 mntsrv, NULL)) 1093 syslog(LOG_ERR, 1094 "can't register %s MOUNTVERS3 service", 1095 nconf->nc_netid); 1096 } 1097 } else 1098 syslog(LOG_WARNING, "can't create %s services", 1099 nconf->nc_netid); 1100 1101 if (registered == 0) { 1102 registered = 1; 1103 memset(&hints, 0, sizeof hints); 1104 hints.ai_flags = AI_PASSIVE; 1105 hints.ai_family = si.si_af; 1106 hints.ai_socktype = si.si_socktype; 1107 hints.ai_protocol = si.si_proto; 1108 1109 if ((aicode = getaddrinfo(NULL, port_str, &hints, 1110 &res)) != 0) { 1111 syslog(LOG_ERR, "cannot get local address: %s", 1112 gai_strerror(aicode)); 1113 exit(1); 1114 } 1115 1116 servaddr.buf = malloc(res->ai_addrlen); 1117 memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); 1118 servaddr.len = res->ai_addrlen; 1119 1120 rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr); 1121 rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr); 1122 1123 xcreated++; 1124 freeaddrinfo(res); 1125 } 1126 } /* end while */ 1127 } 1128 1129 /* 1130 * Clear out sockets after a failure to bind one of them, so that the 1131 * cycle of socket creation/binding can start anew. 1132 */ 1133 static void 1134 clearout_service(void) 1135 { 1136 int i; 1137 1138 for (i = 0; i < sock_fdcnt; i++) { 1139 if (sock_fd[i] >= 0) { 1140 shutdown(sock_fd[i], SHUT_RDWR); 1141 close(sock_fd[i]); 1142 } 1143 } 1144 } 1145 1146 static void 1147 usage(void) 1148 { 1149 fprintf(stderr, 1150 "usage: mountd [-2] [-d] [-e] [-l] [-N] [-n] [-p <port>] [-r] [-S] " 1151 "[-s] [-h <bindip>] [export_file ...]\n"); 1152 exit(1); 1153 } 1154 1155 /* 1156 * The mount rpc service 1157 */ 1158 void 1159 mntsrv(struct svc_req *rqstp, SVCXPRT *transp) 1160 { 1161 struct exportlist *ep; 1162 struct dirlist *dp; 1163 struct fhreturn fhr; 1164 struct stat stb; 1165 struct statfs fsb; 1166 char host[NI_MAXHOST], numerichost[NI_MAXHOST]; 1167 int lookup_failed = 1; 1168 struct sockaddr *saddr; 1169 u_short sport; 1170 char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN]; 1171 int defset, hostset; 1172 long bad = 0; 1173 sigset_t sighup_mask; 1174 int numsecflavors, *secflavorsp; 1175 1176 sigemptyset(&sighup_mask); 1177 sigaddset(&sighup_mask, SIGHUP); 1178 saddr = svc_getrpccaller(transp)->buf; 1179 switch (saddr->sa_family) { 1180 case AF_INET6: 1181 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 1182 break; 1183 case AF_INET: 1184 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 1185 break; 1186 default: 1187 syslog(LOG_ERR, "request from unknown address family"); 1188 return; 1189 } 1190 switch (rqstp->rq_proc) { 1191 case MOUNTPROC_MNT: 1192 case MOUNTPROC_UMNT: 1193 case MOUNTPROC_UMNTALL: 1194 lookup_failed = getnameinfo(saddr, saddr->sa_len, host, 1195 sizeof host, NULL, 0, 0); 1196 } 1197 getnameinfo(saddr, saddr->sa_len, numerichost, 1198 sizeof numerichost, NULL, 0, NI_NUMERICHOST); 1199 switch (rqstp->rq_proc) { 1200 case NULLPROC: 1201 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) 1202 syslog(LOG_ERR, "can't send reply"); 1203 return; 1204 case MOUNTPROC_MNT: 1205 if (sport >= IPPORT_RESERVED && resvport_only) { 1206 syslog(LOG_NOTICE, 1207 "mount request from %s from unprivileged port", 1208 numerichost); 1209 svcerr_weakauth(transp); 1210 return; 1211 } 1212 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 1213 syslog(LOG_NOTICE, "undecodable mount request from %s", 1214 numerichost); 1215 svcerr_decode(transp); 1216 return; 1217 } 1218 1219 /* 1220 * Get the real pathname and make sure it is a directory 1221 * or a regular file if the -r option was specified 1222 * and it exists. 1223 */ 1224 if (realpath(rpcpath, dirpath) == NULL || 1225 stat(dirpath, &stb) < 0 || 1226 statfs(dirpath, &fsb) < 0) { 1227 chdir("/"); /* Just in case realpath doesn't */ 1228 syslog(LOG_NOTICE, 1229 "mount request from %s for non existent path %s", 1230 numerichost, dirpath); 1231 if (debug) 1232 warnx("stat failed on %s", dirpath); 1233 bad = ENOENT; /* We will send error reply later */ 1234 } 1235 if (!bad && 1236 !S_ISDIR(stb.st_mode) && 1237 (dir_only || !S_ISREG(stb.st_mode))) { 1238 syslog(LOG_NOTICE, 1239 "mount request from %s for non-directory path %s", 1240 numerichost, dirpath); 1241 if (debug) 1242 warnx("mounting non-directory %s", dirpath); 1243 bad = ENOTDIR; /* We will send error reply later */ 1244 } 1245 1246 /* Check in the exports list */ 1247 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1248 if (bad) 1249 ep = NULL; 1250 else 1251 ep = ex_search(&fsb.f_fsid, exphead); 1252 hostset = defset = 0; 1253 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, 1254 &numsecflavors, &secflavorsp) || 1255 ((dp = dirp_search(ep->ex_dirl, dirpath)) && 1256 chk_host(dp, saddr, &defset, &hostset, &numsecflavors, 1257 &secflavorsp)) || 1258 (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 1259 scan_tree(ep->ex_dirl, saddr) == 0))) { 1260 if (bad) { 1261 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 1262 (caddr_t)&bad)) 1263 syslog(LOG_ERR, "can't send reply"); 1264 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1265 return; 1266 } 1267 if (hostset & DP_HOSTSET) { 1268 fhr.fhr_flag = hostset; 1269 fhr.fhr_numsecflavors = numsecflavors; 1270 fhr.fhr_secflavors = secflavorsp; 1271 } else { 1272 fhr.fhr_flag = defset; 1273 fhr.fhr_numsecflavors = ep->ex_defnumsecflavors; 1274 fhr.fhr_secflavors = ep->ex_defsecflavors; 1275 } 1276 fhr.fhr_vers = rqstp->rq_vers; 1277 /* Get the file handle */ 1278 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 1279 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 1280 bad = errno; 1281 syslog(LOG_ERR, "can't get fh for %s", dirpath); 1282 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 1283 (caddr_t)&bad)) 1284 syslog(LOG_ERR, "can't send reply"); 1285 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1286 return; 1287 } 1288 if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, 1289 (caddr_t)&fhr)) 1290 syslog(LOG_ERR, "can't send reply"); 1291 if (!lookup_failed) 1292 add_mlist(host, dirpath); 1293 else 1294 add_mlist(numerichost, dirpath); 1295 if (debug) 1296 warnx("mount successful"); 1297 if (dolog) 1298 syslog(LOG_NOTICE, 1299 "mount request succeeded from %s for %s", 1300 numerichost, dirpath); 1301 } else { 1302 if (!bad) 1303 bad = EACCES; 1304 syslog(LOG_NOTICE, 1305 "mount request denied from %s for %s", 1306 numerichost, dirpath); 1307 } 1308 1309 if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long, 1310 (caddr_t)&bad)) 1311 syslog(LOG_ERR, "can't send reply"); 1312 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1313 return; 1314 case MOUNTPROC_DUMP: 1315 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) 1316 syslog(LOG_ERR, "can't send reply"); 1317 else if (dolog) 1318 syslog(LOG_NOTICE, 1319 "dump request succeeded from %s", 1320 numerichost); 1321 return; 1322 case MOUNTPROC_UMNT: 1323 if (sport >= IPPORT_RESERVED && resvport_only) { 1324 syslog(LOG_NOTICE, 1325 "umount request from %s from unprivileged port", 1326 numerichost); 1327 svcerr_weakauth(transp); 1328 return; 1329 } 1330 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 1331 syslog(LOG_NOTICE, "undecodable umount request from %s", 1332 numerichost); 1333 svcerr_decode(transp); 1334 return; 1335 } 1336 if (realpath(rpcpath, dirpath) == NULL) { 1337 syslog(LOG_NOTICE, "umount request from %s " 1338 "for non existent path %s", 1339 numerichost, dirpath); 1340 } 1341 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 1342 syslog(LOG_ERR, "can't send reply"); 1343 if (!lookup_failed) 1344 del_mlist(host, dirpath); 1345 del_mlist(numerichost, dirpath); 1346 if (dolog) 1347 syslog(LOG_NOTICE, 1348 "umount request succeeded from %s for %s", 1349 numerichost, dirpath); 1350 return; 1351 case MOUNTPROC_UMNTALL: 1352 if (sport >= IPPORT_RESERVED && resvport_only) { 1353 syslog(LOG_NOTICE, 1354 "umountall request from %s from unprivileged port", 1355 numerichost); 1356 svcerr_weakauth(transp); 1357 return; 1358 } 1359 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 1360 syslog(LOG_ERR, "can't send reply"); 1361 if (!lookup_failed) 1362 del_mlist(host, NULL); 1363 del_mlist(numerichost, NULL); 1364 if (dolog) 1365 syslog(LOG_NOTICE, 1366 "umountall request succeeded from %s", 1367 numerichost); 1368 return; 1369 case MOUNTPROC_EXPORT: 1370 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) 1371 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief, 1372 (caddr_t)NULL)) 1373 syslog(LOG_ERR, "can't send reply"); 1374 if (dolog) 1375 syslog(LOG_NOTICE, 1376 "export request succeeded from %s", 1377 numerichost); 1378 return; 1379 default: 1380 svcerr_noproc(transp); 1381 return; 1382 } 1383 } 1384 1385 /* 1386 * Xdr conversion for a dirpath string 1387 */ 1388 static int 1389 xdr_dir(XDR *xdrsp, char *dirp) 1390 { 1391 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 1392 } 1393 1394 /* 1395 * Xdr routine to generate file handle reply 1396 */ 1397 static int 1398 xdr_fhs(XDR *xdrsp, caddr_t cp) 1399 { 1400 struct fhreturn *fhrp = (struct fhreturn *)cp; 1401 u_long ok = 0, len, auth; 1402 int i; 1403 1404 if (!xdr_long(xdrsp, &ok)) 1405 return (0); 1406 switch (fhrp->fhr_vers) { 1407 case 1: 1408 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 1409 case 3: 1410 len = NFSX_V3FH; 1411 if (!xdr_long(xdrsp, &len)) 1412 return (0); 1413 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 1414 return (0); 1415 if (fhrp->fhr_numsecflavors) { 1416 if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors)) 1417 return (0); 1418 for (i = 0; i < fhrp->fhr_numsecflavors; i++) 1419 if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i])) 1420 return (0); 1421 return (1); 1422 } else { 1423 auth = AUTH_SYS; 1424 len = 1; 1425 if (!xdr_long(xdrsp, &len)) 1426 return (0); 1427 return (xdr_long(xdrsp, &auth)); 1428 } 1429 } 1430 return (0); 1431 } 1432 1433 static int 1434 xdr_mlist(XDR *xdrsp, caddr_t cp __unused) 1435 { 1436 struct mountlist *mlp; 1437 int true = 1; 1438 int false = 0; 1439 char *strp; 1440 1441 SLIST_FOREACH(mlp, &mlhead, next) { 1442 if (!xdr_bool(xdrsp, &true)) 1443 return (0); 1444 strp = &mlp->ml_host[0]; 1445 if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) 1446 return (0); 1447 strp = &mlp->ml_dirp[0]; 1448 if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1449 return (0); 1450 } 1451 if (!xdr_bool(xdrsp, &false)) 1452 return (0); 1453 return (1); 1454 } 1455 1456 /* 1457 * Xdr conversion for export list 1458 */ 1459 static int 1460 xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief) 1461 { 1462 struct exportlist *ep; 1463 int false = 0; 1464 int putdef; 1465 sigset_t sighup_mask; 1466 int i; 1467 1468 sigemptyset(&sighup_mask); 1469 sigaddset(&sighup_mask, SIGHUP); 1470 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1471 1472 for (i = 0; i < exphashsize; i++) 1473 SLIST_FOREACH(ep, &exphead[i], entries) { 1474 putdef = 0; 1475 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, 1476 &putdef, brief)) 1477 goto errout; 1478 if (ep->ex_defdir && putdef == 0 && 1479 put_exlist(ep->ex_defdir, xdrsp, NULL, 1480 &putdef, brief)) 1481 goto errout; 1482 } 1483 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1484 if (!xdr_bool(xdrsp, &false)) 1485 return (0); 1486 return (1); 1487 errout: 1488 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1489 return (0); 1490 } 1491 1492 /* 1493 * Called from xdr_explist() to traverse the tree and export the 1494 * directory paths. 1495 */ 1496 static int 1497 put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp, 1498 int brief) 1499 { 1500 struct grouplist *grp; 1501 struct hostlist *hp; 1502 int true = 1; 1503 int false = 0; 1504 int gotalldir = 0; 1505 char *strp; 1506 1507 if (dp) { 1508 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) 1509 return (1); 1510 if (!xdr_bool(xdrsp, &true)) 1511 return (1); 1512 strp = dp->dp_dirp; 1513 if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1514 return (1); 1515 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 1516 gotalldir = 1; 1517 *putdefp = 1; 1518 } 1519 if (brief) { 1520 if (!xdr_bool(xdrsp, &true)) 1521 return (1); 1522 strp = "(...)"; 1523 if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1524 return (1); 1525 } else if ((dp->dp_flag & DP_DEFSET) == 0 && 1526 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 1527 hp = dp->dp_hosts; 1528 while (hp) { 1529 grp = hp->ht_grp; 1530 if (grp->gr_type == GT_HOST) { 1531 if (!xdr_bool(xdrsp, &true)) 1532 return (1); 1533 strp = grp->gr_ptr.gt_addrinfo->ai_canonname; 1534 if (!xdr_string(xdrsp, &strp, 1535 MNTNAMLEN)) 1536 return (1); 1537 } else if (grp->gr_type == GT_NET) { 1538 if (!xdr_bool(xdrsp, &true)) 1539 return (1); 1540 strp = grp->gr_ptr.gt_net.nt_name; 1541 if (!xdr_string(xdrsp, &strp, 1542 MNTNAMLEN)) 1543 return (1); 1544 } 1545 hp = hp->ht_next; 1546 if (gotalldir && hp == (struct hostlist *)NULL) { 1547 hp = adp->dp_hosts; 1548 gotalldir = 0; 1549 } 1550 } 1551 } 1552 if (!xdr_bool(xdrsp, &false)) 1553 return (1); 1554 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) 1555 return (1); 1556 } 1557 return (0); 1558 } 1559 1560 static int 1561 xdr_explist(XDR *xdrsp, caddr_t cp) 1562 { 1563 1564 return xdr_explist_common(xdrsp, cp, 0); 1565 } 1566 1567 static int 1568 xdr_explist_brief(XDR *xdrsp, caddr_t cp) 1569 { 1570 1571 return xdr_explist_common(xdrsp, cp, 1); 1572 } 1573 1574 static char *line; 1575 static size_t linesize; 1576 static FILE *exp_file; 1577 1578 /* 1579 * Get the export list from one, currently open file 1580 */ 1581 static void 1582 get_exportlist_one(int passno) 1583 { 1584 struct exportlist *ep; 1585 struct grouplist *grp, *tgrp, *savgrp; 1586 struct dirlist *dirhead; 1587 struct statfs fsb; 1588 struct expcred anon; 1589 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 1590 char *err_msg = NULL; 1591 int len, has_host, got_nondir, dirplen, netgrp; 1592 uint64_t exflags; 1593 char unvis_dir[PATH_MAX + 1]; 1594 int unvis_len; 1595 1596 v4root_phase = 0; 1597 dirhead = (struct dirlist *)NULL; 1598 unvis_dir[0] = '\0'; 1599 fsb.f_mntonname[0] = '\0'; 1600 1601 while (get_line()) { 1602 if (debug) 1603 warnx("got line %s", line); 1604 cp = line; 1605 nextfield(&cp, &endcp); 1606 if (*cp == '#') 1607 goto nextline; 1608 1609 /* 1610 * Set defaults. 1611 */ 1612 has_host = FALSE; 1613 anon.cr_uid = UID_NOBODY; 1614 anon.cr_ngroups = 1; 1615 anon.cr_groups = tmp_groups; 1616 anon.cr_groups[0] = nogroup(); 1617 exflags = MNT_EXPORTED; 1618 got_nondir = 0; 1619 opt_flags = 0; 1620 ep = (struct exportlist *)NULL; 1621 dirp = NULL; 1622 1623 /* 1624 * Handle the V4 root dir. 1625 */ 1626 if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') { 1627 /* 1628 * V4: just indicates that it is the v4 root point, 1629 * so skip over that and set v4root_phase. 1630 */ 1631 if (v4root_phase > 0) { 1632 syslog(LOG_ERR, "V4:duplicate line, ignored"); 1633 goto nextline; 1634 } 1635 v4root_phase = 1; 1636 cp += 3; 1637 nextfield(&cp, &endcp); 1638 } 1639 1640 /* 1641 * Create new exports list entry 1642 */ 1643 len = endcp-cp; 1644 tgrp = grp = get_grp(); 1645 while (len > 0) { 1646 if (len > MNTNAMLEN) { 1647 getexp_err(ep, tgrp, "mountpoint too long"); 1648 goto nextline; 1649 } 1650 if (*cp == '-') { 1651 if (ep == (struct exportlist *)NULL) { 1652 getexp_err(ep, tgrp, 1653 "flag before export path definition"); 1654 goto nextline; 1655 } 1656 if (debug) 1657 warnx("doing opt %s", cp); 1658 got_nondir = 1; 1659 if (do_opt(&cp, &endcp, ep, grp, &has_host, 1660 &exflags, &anon, unvis_dir)) { 1661 getexp_err(ep, tgrp, NULL); 1662 goto nextline; 1663 } 1664 } else if (*cp == '/') { 1665 savedc = *endcp; 1666 *endcp = '\0'; 1667 unvis_len = strnunvis(unvis_dir, sizeof(unvis_dir), 1668 cp); 1669 if (unvis_len <= 0) { 1670 getexp_err(ep, tgrp, "Cannot strunvis " 1671 "decode dir"); 1672 goto nextline; 1673 } 1674 if (v4root_phase > 1) { 1675 if (dirp != NULL) { 1676 getexp_err(ep, tgrp, "Multiple V4 dirs"); 1677 goto nextline; 1678 } 1679 } 1680 if (check_dirpath(unvis_dir, &err_msg) && 1681 check_statfs(unvis_dir, &fsb, &err_msg)) { 1682 if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0) 1683 syslog(LOG_ERR, "Warning: exporting of " 1684 "automounted fs %s not supported", 1685 unvis_dir); 1686 if (got_nondir) { 1687 getexp_err(ep, tgrp, "dirs must be first"); 1688 goto nextline; 1689 } 1690 if (v4root_phase == 1) { 1691 if (dirp != NULL) { 1692 getexp_err(ep, tgrp, "Multiple V4 dirs"); 1693 goto nextline; 1694 } 1695 if (strlen(v4root_dirpath) == 0) { 1696 strlcpy(v4root_dirpath, unvis_dir, 1697 sizeof (v4root_dirpath)); 1698 } else if (strcmp(v4root_dirpath, unvis_dir) 1699 != 0) { 1700 syslog(LOG_ERR, 1701 "different V4 dirpath %s", 1702 unvis_dir); 1703 getexp_err(ep, tgrp, NULL); 1704 goto nextline; 1705 } 1706 dirp = unvis_dir; 1707 v4root_phase = 2; 1708 got_nondir = 1; 1709 ep = get_exp(); 1710 } else { 1711 if (ep) { 1712 if (fsidcmp(&ep->ex_fs, &fsb.f_fsid) 1713 != 0) { 1714 getexp_err(ep, tgrp, 1715 "fsid mismatch"); 1716 goto nextline; 1717 } 1718 } else { 1719 /* 1720 * See if this directory is already 1721 * in the list. 1722 */ 1723 ep = ex_search(&fsb.f_fsid, exphead); 1724 if (ep == (struct exportlist *)NULL) { 1725 ep = get_exp(); 1726 ep->ex_fs = fsb.f_fsid; 1727 ep->ex_fsdir = strdup(fsb.f_mntonname); 1728 if (ep->ex_fsdir == NULL) 1729 out_of_mem(); 1730 if (debug) 1731 warnx( 1732 "making new ep fs=0x%x,0x%x", 1733 fsb.f_fsid.val[0], 1734 fsb.f_fsid.val[1]); 1735 } else if (debug) 1736 warnx("found ep fs=0x%x,0x%x", 1737 fsb.f_fsid.val[0], 1738 fsb.f_fsid.val[1]); 1739 } 1740 1741 if (strcmp(unvis_dir, fsb.f_mntonname) != 1742 0) 1743 opt_flags |= OP_NOTROOT; 1744 1745 /* 1746 * Add dirpath to export mount point. 1747 */ 1748 dirp = add_expdir(&dirhead, unvis_dir, 1749 unvis_len); 1750 dirplen = unvis_len; 1751 } 1752 } else { 1753 if (err_msg != NULL) { 1754 getexp_err(ep, tgrp, err_msg); 1755 free(err_msg); 1756 err_msg = NULL; 1757 } else { 1758 getexp_err(ep, tgrp, 1759 "symbolic link in export path or " 1760 "statfs failed"); 1761 } 1762 goto nextline; 1763 } 1764 *endcp = savedc; 1765 } else { 1766 savedc = *endcp; 1767 *endcp = '\0'; 1768 got_nondir = 1; 1769 if (ep == (struct exportlist *)NULL) { 1770 getexp_err(ep, tgrp, 1771 "host(s) before export path definition"); 1772 goto nextline; 1773 } 1774 1775 /* 1776 * Get the host or netgroup. 1777 */ 1778 setnetgrent(cp); 1779 netgrp = getnetgrent(&hst, &usr, &dom); 1780 do { 1781 if (has_host) { 1782 grp->gr_next = get_grp(); 1783 grp = grp->gr_next; 1784 } 1785 if (netgrp) { 1786 if (hst == 0) { 1787 syslog(LOG_ERR, 1788 "null hostname in netgroup %s, skipping", cp); 1789 grp->gr_type = GT_IGNORE; 1790 } else if (get_host(hst, grp, tgrp)) { 1791 syslog(LOG_ERR, 1792 "bad host %s in netgroup %s, skipping", hst, cp); 1793 grp->gr_type = GT_IGNORE; 1794 } 1795 } else if (get_host(cp, grp, tgrp)) { 1796 syslog(LOG_ERR, "bad host %s, skipping", cp); 1797 grp->gr_type = GT_IGNORE; 1798 } 1799 has_host = TRUE; 1800 } while (netgrp && getnetgrent(&hst, &usr, &dom)); 1801 endnetgrent(); 1802 *endcp = savedc; 1803 } 1804 cp = endcp; 1805 nextfield(&cp, &endcp); 1806 len = endcp - cp; 1807 } 1808 if (opt_flags & OP_CLASSMASK) 1809 syslog(LOG_WARNING, 1810 "WARNING: No mask specified for %s, " 1811 "using out-of-date default", 1812 (&grp->gr_ptr.gt_net)->nt_name); 1813 if ((opt_flags & OP_NOTROOT) != 0 && warn_admin != 0 && 1814 (ep->ex_flag & EX_ADMINWARN) == 0 && unvis_dir[0] != '\0' && 1815 fsb.f_mntonname[0] != '\0') { 1816 if (debug) 1817 warnx("exporting %s exports entire %s file " 1818 "system", unvis_dir, fsb.f_mntonname); 1819 syslog(LOG_ERR, "Warning: exporting %s exports " 1820 "entire %s file system", unvis_dir, 1821 fsb.f_mntonname); 1822 ep->ex_flag |= EX_ADMINWARN; 1823 } 1824 if (check_options(dirhead)) { 1825 getexp_err(ep, tgrp, NULL); 1826 goto nextline; 1827 } 1828 if (!has_host) { 1829 grp->gr_type = GT_DEFAULT; 1830 if (debug) 1831 warnx("adding a default entry"); 1832 1833 /* 1834 * Don't allow a network export coincide with a list of 1835 * host(s) on the same line. 1836 */ 1837 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1838 getexp_err(ep, tgrp, "network/host conflict"); 1839 goto nextline; 1840 1841 /* 1842 * If an export list was specified on this line, make sure 1843 * that we have at least one valid entry, otherwise skip it. 1844 */ 1845 } else { 1846 grp = tgrp; 1847 while (grp && grp->gr_type == GT_IGNORE) 1848 grp = grp->gr_next; 1849 if (! grp) { 1850 getexp_err(ep, tgrp, "no valid entries"); 1851 goto nextline; 1852 } 1853 } 1854 1855 if (v4root_phase == 1) { 1856 getexp_err(ep, tgrp, "V4:root, no dirp, ignored"); 1857 goto nextline; 1858 } 1859 1860 /* 1861 * Loop through hosts, pushing the exports into the kernel. 1862 * After loop, tgrp points to the start of the list and 1863 * grp points to the last entry in the list. 1864 * Do not do the do_mount() for passno == 1, since the 1865 * second pass will do it, as required. 1866 */ 1867 grp = tgrp; 1868 do { 1869 grp->gr_exflags = exflags; 1870 cp_cred(&grp->gr_anon, &anon); 1871 if (v4root_phase == 2 && passno == 0) 1872 LOGDEBUG("do_mount v4root"); 1873 if (passno == 0 && do_mount(ep, grp, exflags, &anon, 1874 dirp, dirplen, &fsb, ep->ex_numsecflavors, 1875 ep->ex_secflavors)) { 1876 getexp_err(ep, tgrp, NULL); 1877 goto nextline; 1878 } 1879 } while (grp->gr_next && (grp = grp->gr_next)); 1880 1881 /* 1882 * For V4: don't enter in mount lists. 1883 */ 1884 if (v4root_phase > 0 && v4root_phase <= 2) { 1885 /* 1886 * These structures are used for the reload, 1887 * so save them for that case. Otherwise, just 1888 * free them up now. 1889 */ 1890 if (passno == 1 && ep != NULL) { 1891 savgrp = tgrp; 1892 while (tgrp != NULL) { 1893 /* 1894 * Save the security flavors and exflags 1895 * for this host set in the groups. 1896 */ 1897 tgrp->gr_numsecflavors = 1898 ep->ex_numsecflavors; 1899 if (ep->ex_numsecflavors > 0) 1900 memcpy(tgrp->gr_secflavors, 1901 ep->ex_secflavors, 1902 sizeof(ep->ex_secflavors)); 1903 tgrp = tgrp->gr_next; 1904 } 1905 if (v4root_ep == NULL) { 1906 v4root_ep = ep; 1907 ep = NULL; /* Don't free below. */ 1908 } 1909 grp->gr_next = v4root_ep->ex_grphead; 1910 v4root_ep->ex_grphead = savgrp; 1911 } 1912 if (ep != NULL) 1913 free_exp(ep); 1914 while (tgrp != NULL) { 1915 grp = tgrp; 1916 tgrp = tgrp->gr_next; 1917 free_grp(grp); 1918 } 1919 goto nextline; 1920 } 1921 1922 /* 1923 * Success. Update the data structures. 1924 */ 1925 if (has_host) { 1926 hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags); 1927 grp->gr_next = ep->ex_grphead; 1928 ep->ex_grphead = tgrp; 1929 } else { 1930 hang_dirp(dirhead, (struct grouplist *)NULL, ep, 1931 opt_flags, &anon, exflags); 1932 free_grp(grp); 1933 } 1934 dirhead = (struct dirlist *)NULL; 1935 if ((ep->ex_flag & EX_LINKED) == 0) { 1936 insert_exports(ep, exphead); 1937 1938 ep->ex_flag |= EX_LINKED; 1939 } 1940 nextline: 1941 v4root_phase = 0; 1942 if (dirhead) { 1943 free_dir(dirhead); 1944 dirhead = (struct dirlist *)NULL; 1945 } 1946 } 1947 } 1948 1949 /* 1950 * Get the export list from all specified files 1951 */ 1952 static void 1953 get_exportlist(int passno) 1954 { 1955 struct export_args export; 1956 struct iovec *iov; 1957 struct statfs *mntbufp; 1958 char errmsg[255]; 1959 int error, i, nfs_maxvers, num; 1960 int iovlen; 1961 struct nfsex_args eargs; 1962 FILE *debug_file; 1963 size_t nfs_maxvers_size; 1964 1965 if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) { 1966 fclose(debug_file); 1967 logdebug = 1; 1968 } else 1969 logdebug = 0; 1970 LOGDEBUG("passno=%d", passno); 1971 v4root_dirpath[0] = '\0'; 1972 free_v4rootexp(); 1973 if (passno == 1) { 1974 /* 1975 * Save the current lists as old ones, so that the new lists 1976 * can be compared with the old ones in the 2nd pass. 1977 */ 1978 for (i = 0; i < exphashsize; i++) { 1979 SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]); 1980 SLIST_INIT(&exphead[i]); 1981 } 1982 1983 /* Note that the public fh has not yet been set. */ 1984 has_set_publicfh = 0; 1985 1986 /* Read the export file(s) and process them */ 1987 read_exportfile(passno); 1988 } else { 1989 /* 1990 * Just make the old lists empty. 1991 * exphashsize == 0 for the first call, before oldexphead 1992 * has been initialized-->loop won't be executed. 1993 */ 1994 for (i = 0; i < exphashsize; i++) 1995 SLIST_INIT(&oldexphead[i]); 1996 } 1997 1998 bzero(&export, sizeof(export)); 1999 export.ex_flags = MNT_DELEXPORT; 2000 iov = NULL; 2001 iovlen = 0; 2002 bzero(errmsg, sizeof(errmsg)); 2003 2004 if (suspend_nfsd != 0) 2005 (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); 2006 /* 2007 * Delete the old V4 root dir. 2008 */ 2009 bzero(&eargs, sizeof (eargs)); 2010 eargs.export.ex_flags = MNT_DELEXPORT; 2011 if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, (caddr_t)&eargs) < 0 && 2012 errno != ENOENT) 2013 syslog(LOG_ERR, "Can't delete exports for V4:"); 2014 2015 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 2016 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 2017 build_iovec(&iov, &iovlen, "from", NULL, 0); 2018 build_iovec(&iov, &iovlen, "update", NULL, 0); 2019 build_iovec(&iov, &iovlen, "export", &export, 2020 sizeof(export)); 2021 build_iovec(&iov, &iovlen, "errmsg", errmsg, 2022 sizeof(errmsg)); 2023 2024 /* 2025 * For passno == 1, compare the old and new lists updating the kernel 2026 * exports for any cases that have changed. 2027 * This call is doing the second pass through the lists. 2028 * If it fails, fall back on the bulk reload. 2029 */ 2030 if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) == 2031 0) { 2032 LOGDEBUG("compareok"); 2033 /* Free up the old lists. */ 2034 free_exports(oldexphead); 2035 } else { 2036 LOGDEBUG("doing passno=0"); 2037 /* 2038 * Clear flag that notes if a public fh has been exported. 2039 * It is set by do_mount() if MNT_EXPUBLIC is set for the entry. 2040 */ 2041 has_publicfh = 0; 2042 2043 /* exphead == NULL if not yet allocated (first call). */ 2044 if (exphead != NULL) { 2045 /* 2046 * First, get rid of the old lists. 2047 */ 2048 free_exports(exphead); 2049 free_exports(oldexphead); 2050 } 2051 2052 /* 2053 * And delete exports that are in the kernel for all local 2054 * filesystems. 2055 * XXX: Should know how to handle all local exportable 2056 * filesystems. 2057 */ 2058 num = getmntinfo(&mntbufp, MNT_NOWAIT); 2059 2060 /* Allocate hash tables, for first call. */ 2061 if (exphead == NULL) { 2062 /* Target an average linked list length of 10. */ 2063 exphashsize = num / 10; 2064 if (exphashsize < 1) 2065 exphashsize = 1; 2066 else if (exphashsize > 100000) 2067 exphashsize = 100000; 2068 exphead = malloc(exphashsize * sizeof(*exphead)); 2069 oldexphead = malloc(exphashsize * sizeof(*oldexphead)); 2070 if (exphead == NULL || oldexphead == NULL) 2071 errx(1, "Can't malloc hash tables"); 2072 2073 for (i = 0; i < exphashsize; i++) { 2074 SLIST_INIT(&exphead[i]); 2075 SLIST_INIT(&oldexphead[i]); 2076 } 2077 } 2078 2079 for (i = 0; i < num; i++) 2080 delete_export(iov, iovlen, &mntbufp[i], errmsg); 2081 2082 2083 /* Read the export file(s) and process them */ 2084 read_exportfile(0); 2085 } 2086 2087 if (strlen(v4root_dirpath) == 0) { 2088 /* Check to see if a V4: line is needed. */ 2089 nfs_maxvers_size = sizeof(nfs_maxvers); 2090 error = sysctlbyname("vfs.nfsd.server_max_nfsvers", 2091 &nfs_maxvers, &nfs_maxvers_size, NULL, 0); 2092 if (error != 0 || nfs_maxvers < NFS_VER2 || nfs_maxvers > 2093 NFS_VER4) { 2094 syslog(LOG_ERR, "sysctlbyname(vfs.nfsd." 2095 "server_max_nfsvers) failed, defaulting to NFSv3"); 2096 nfs_maxvers = NFS_VER3; 2097 } 2098 if (nfs_maxvers == NFS_VER4) 2099 syslog(LOG_ERR, "NFSv4 requires at least one V4: line"); 2100 } 2101 2102 if (iov != NULL) { 2103 /* Free strings allocated by strdup() in getmntopts.c */ 2104 free(iov[0].iov_base); /* fstype */ 2105 free(iov[2].iov_base); /* fspath */ 2106 free(iov[4].iov_base); /* from */ 2107 free(iov[6].iov_base); /* update */ 2108 free(iov[8].iov_base); /* export */ 2109 free(iov[10].iov_base); /* errmsg */ 2110 2111 /* free iov, allocated by realloc() */ 2112 free(iov); 2113 iovlen = 0; 2114 } 2115 2116 /* 2117 * If there was no public fh, clear any previous one set. 2118 */ 2119 if (has_publicfh == 0) { 2120 LOGDEBUG("clear public fh"); 2121 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); 2122 } 2123 2124 /* Resume the nfsd. If they weren't suspended, this is harmless. */ 2125 (void)nfssvc(NFSSVC_RESUMENFSD, NULL); 2126 LOGDEBUG("eo get_exportlist"); 2127 } 2128 2129 /* 2130 * Insert an export entry in the appropriate list. 2131 */ 2132 static void 2133 insert_exports(struct exportlist *ep, struct exportlisthead *exhp) 2134 { 2135 uint32_t i; 2136 2137 i = EXPHASH(&ep->ex_fs); 2138 LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i); 2139 SLIST_INSERT_HEAD(&exhp[i], ep, entries); 2140 } 2141 2142 /* 2143 * Free up the exports lists passed in as arguments. 2144 */ 2145 static void 2146 free_exports(struct exportlisthead *exhp) 2147 { 2148 struct exportlist *ep, *ep2; 2149 int i; 2150 2151 for (i = 0; i < exphashsize; i++) { 2152 SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) { 2153 SLIST_REMOVE(&exhp[i], ep, exportlist, entries); 2154 free_exp(ep); 2155 } 2156 SLIST_INIT(&exhp[i]); 2157 } 2158 } 2159 2160 /* 2161 * Read the exports file(s) and call get_exportlist_one() for each line. 2162 */ 2163 static void 2164 read_exportfile(int passno) 2165 { 2166 int done, i; 2167 2168 /* 2169 * Read in the exports file and build the list, calling 2170 * nmount() as we go along to push the export rules into the kernel. 2171 */ 2172 done = 0; 2173 for (i = 0; exnames[i] != NULL; i++) { 2174 if (debug) 2175 warnx("reading exports from %s", exnames[i]); 2176 if ((exp_file = fopen(exnames[i], "r")) == NULL) { 2177 syslog(LOG_WARNING, "can't open %s", exnames[i]); 2178 continue; 2179 } 2180 get_exportlist_one(passno); 2181 fclose(exp_file); 2182 done++; 2183 } 2184 if (done == 0) { 2185 syslog(LOG_ERR, "can't open any exports file"); 2186 exit(2); 2187 } 2188 } 2189 2190 /* 2191 * Compare the export lists against the old ones and do nmount() operations 2192 * for any cases that have changed. This avoids doing nmount() for entries 2193 * that have not changed. 2194 * Return 0 upon success, 1 otherwise. 2195 */ 2196 static int 2197 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg) 2198 { 2199 struct exportlist *ep, *oep; 2200 struct grouplist *grp; 2201 struct statfs fs, ofs; 2202 int i, ret; 2203 2204 /* 2205 * Loop through the current list and look for an entry in the old 2206 * list. 2207 * If found, check to see if it the same. 2208 * If it is not the same, delete and re-export. 2209 * Then mark it done on the old list. 2210 * else (not found) 2211 * export it. 2212 * Any entries left in the old list after processing must have their 2213 * exports deleted. 2214 */ 2215 for (i = 0; i < exphashsize; i++) 2216 SLIST_FOREACH(ep, &exphead[i], entries) { 2217 LOGDEBUG("foreach ep=%s", ep->ex_fsdir); 2218 oep = ex_search(&ep->ex_fs, oldexphead); 2219 if (oep != NULL) { 2220 /* 2221 * Check the mount paths are the same. 2222 * If not, return 1 so that the reload of the 2223 * exports will be done in bulk, the 2224 * passno == 0 way. 2225 */ 2226 LOGDEBUG("found old exp"); 2227 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2228 return (1); 2229 LOGDEBUG("same fsdir"); 2230 /* 2231 * Test to see if the entry is the same. 2232 * If not the same delete exports and 2233 * re-export. 2234 */ 2235 if (compare_export(ep, oep) != 0) { 2236 /* 2237 * Clear has_publicfh if if was set 2238 * in the old exports, but only if it 2239 * has not been set during processing of 2240 * the exports for this pass, as 2241 * indicated by has_set_publicfh. 2242 */ 2243 if (has_set_publicfh == 0 && 2244 (oep->ex_flag & EX_PUBLICFH) != 0) 2245 has_publicfh = 0; 2246 2247 /* Delete and re-export. */ 2248 if (statfs(ep->ex_fsdir, &fs) < 0) 2249 return (1); 2250 delete_export(iov, iovlen, &fs, errmsg); 2251 ret = do_export_mount(ep, &fs); 2252 if (ret != 0) 2253 return (ret); 2254 } 2255 oep->ex_flag |= EX_DONE; 2256 LOGDEBUG("exdone"); 2257 } else { 2258 LOGDEBUG("not found so export"); 2259 /* Not found, so do export. */ 2260 if (statfs(ep->ex_fsdir, &fs) < 0) 2261 return (1); 2262 ret = do_export_mount(ep, &fs); 2263 if (ret != 0) 2264 return (ret); 2265 } 2266 } 2267 2268 /* Delete exports not done. */ 2269 for (i = 0; i < exphashsize; i++) 2270 SLIST_FOREACH(oep, &oldexphead[i], entries) { 2271 if ((oep->ex_flag & EX_DONE) == 0) { 2272 LOGDEBUG("not done delete=%s", oep->ex_fsdir); 2273 if (statfs(oep->ex_fsdir, &ofs) >= 0 && 2274 fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) { 2275 LOGDEBUG("do delete"); 2276 /* 2277 * Clear has_publicfh if if was set 2278 * in the old exports, but only if it 2279 * has not been set during processing of 2280 * the exports for this pass, as 2281 * indicated by has_set_publicfh. 2282 */ 2283 if (has_set_publicfh == 0 && 2284 (oep->ex_flag & EX_PUBLICFH) != 0) 2285 has_publicfh = 0; 2286 2287 delete_export(iov, iovlen, &ofs, 2288 errmsg); 2289 } 2290 } 2291 } 2292 2293 /* Do the V4 root exports, as required. */ 2294 grp = NULL; 2295 if (v4root_ep != NULL) 2296 grp = v4root_ep->ex_grphead; 2297 v4root_phase = 2; 2298 while (v4root_ep != NULL && grp != NULL) { 2299 LOGDEBUG("v4root expath=%s", v4root_dirpath); 2300 ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon, 2301 v4root_dirpath, strlen(v4root_dirpath), &fs, 2302 grp->gr_numsecflavors, grp->gr_secflavors); 2303 if (ret != 0) { 2304 v4root_phase = 0; 2305 return (ret); 2306 } 2307 grp = grp->gr_next; 2308 } 2309 v4root_phase = 0; 2310 free_v4rootexp(); 2311 return (0); 2312 } 2313 2314 /* 2315 * Compare old and current exportlist entries for the fsid and return 0 2316 * if they are the same, 1 otherwise. 2317 */ 2318 static int 2319 compare_export(struct exportlist *ep, struct exportlist *oep) 2320 { 2321 struct grouplist *grp, *ogrp; 2322 2323 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2324 return (1); 2325 if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET)) 2326 return (1); 2327 if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) || 2328 (ep->ex_defdir == NULL && oep->ex_defdir != NULL)) 2329 return (1); 2330 if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) != 2331 (oep->ex_defdir->dp_flag & DP_DEFSET)) 2332 return (1); 2333 if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors != 2334 oep->ex_defnumsecflavors || ep->ex_defexflags != 2335 oep->ex_defexflags || compare_cred(&ep->ex_defanon, 2336 &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors, 2337 oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0)) 2338 return (1); 2339 2340 /* Now, check all the groups. */ 2341 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2342 ogrp->gr_flag = 0; 2343 for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) { 2344 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = 2345 ogrp->gr_next) 2346 if ((ogrp->gr_flag & GR_FND) == 0 && 2347 grp->gr_numsecflavors == ogrp->gr_numsecflavors && 2348 grp->gr_exflags == ogrp->gr_exflags && 2349 compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 && 2350 compare_secflavor(grp->gr_secflavors, 2351 ogrp->gr_secflavors, grp->gr_numsecflavors) == 0) 2352 break; 2353 if (ogrp != NULL) 2354 ogrp->gr_flag |= GR_FND; 2355 else 2356 return (1); 2357 } 2358 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2359 if ((ogrp->gr_flag & GR_FND) == 0) 2360 return (1); 2361 return (0); 2362 } 2363 2364 /* 2365 * This algorithm compares two arrays of "n" items. It returns 0 if they are 2366 * the "same" and 1 otherwise. Although suboptimal, it is always safe to 2367 * return 1, which makes compare_nmount_export() reload the exports entry. 2368 * "same" refers to having the same set of values in the two arrays. 2369 * The arrays are in no particular order and duplicates (multiple entries 2370 * in an array with the same value) is allowed. 2371 * The algorithm is inefficient, but the common case of identical arrays is 2372 * handled first and "n" is normally fairly small. 2373 * Since the two functions need the same algorithm but for arrays of 2374 * different types (gid_t vs int), this is done as a macro. 2375 */ 2376 #define COMPARE_ARRAYS(a1, a2, n) \ 2377 do { \ 2378 int fnd, fndarray[(n)], i, j; \ 2379 /* Handle common case of identical arrays. */ \ 2380 for (i = 0; i < (n); i++) \ 2381 if ((a1)[i] != (a2)[i]) \ 2382 break; \ 2383 if (i == (n)) \ 2384 return (0); \ 2385 for (i = 0; i < (n); i++) \ 2386 fndarray[i] = 0; \ 2387 for (i = 0; i < (n); i++) { \ 2388 fnd = 0; \ 2389 for (j = 0; j < (n); j++) { \ 2390 if ((a1)[i] == (a2)[j]) { \ 2391 fndarray[j] = 1; \ 2392 fnd = 1; \ 2393 } \ 2394 } \ 2395 if (fnd == 0) \ 2396 return (1); \ 2397 } \ 2398 for (i = 0; i < (n); i++) \ 2399 if (fndarray[i] == 0) \ 2400 return (1); \ 2401 return (0); \ 2402 } while (0) 2403 2404 /* 2405 * Compare two struct expcred's. Return 0 if the same and 1 otherwise. 2406 */ 2407 static int 2408 compare_cred(struct expcred *cr0, struct expcred *cr1) 2409 { 2410 2411 if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups) 2412 return (1); 2413 2414 COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups); 2415 } 2416 2417 /* 2418 * Compare two lists of security flavors. Return 0 if the same and 1 otherwise. 2419 */ 2420 static int 2421 compare_secflavor(int *sec1, int *sec2, int nsec) 2422 { 2423 2424 COMPARE_ARRAYS(sec1, sec2, nsec); 2425 } 2426 2427 /* 2428 * Delete an exports entry. 2429 */ 2430 static void 2431 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg) 2432 { 2433 struct xvfsconf vfc; 2434 2435 if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { 2436 syslog(LOG_ERR, "getvfsbyname() failed for %s", 2437 fsp->f_fstypename); 2438 return; 2439 } 2440 2441 /* 2442 * We do not need to delete "export" flag from 2443 * filesystems that do not have it set. 2444 */ 2445 if (!(fsp->f_flags & MNT_EXPORTED)) 2446 return; 2447 /* 2448 * Do not delete export for network filesystem by 2449 * passing "export" arg to nmount(). 2450 * It only makes sense to do this for local filesystems. 2451 */ 2452 if (vfc.vfc_flags & VFCF_NETWORK) 2453 return; 2454 2455 iov[1].iov_base = fsp->f_fstypename; 2456 iov[1].iov_len = strlen(fsp->f_fstypename) + 1; 2457 iov[3].iov_base = fsp->f_mntonname; 2458 iov[3].iov_len = strlen(fsp->f_mntonname) + 1; 2459 iov[5].iov_base = fsp->f_mntfromname; 2460 iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; 2461 errmsg[0] = '\0'; 2462 2463 /* 2464 * EXDEV is returned when path exists but is not a 2465 * mount point. May happens if raced with unmount. 2466 */ 2467 if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT && 2468 errno != ENOTSUP && errno != EXDEV) { 2469 syslog(LOG_ERR, 2470 "can't delete exports for %s: %m %s", 2471 fsp->f_mntonname, errmsg); 2472 } 2473 } 2474 2475 /* 2476 * Allocate an export list element 2477 */ 2478 static struct exportlist * 2479 get_exp(void) 2480 { 2481 struct exportlist *ep; 2482 2483 ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); 2484 if (ep == (struct exportlist *)NULL) 2485 out_of_mem(); 2486 return (ep); 2487 } 2488 2489 /* 2490 * Allocate a group list element 2491 */ 2492 static struct grouplist * 2493 get_grp(void) 2494 { 2495 struct grouplist *gp; 2496 2497 gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); 2498 if (gp == (struct grouplist *)NULL) 2499 out_of_mem(); 2500 return (gp); 2501 } 2502 2503 /* 2504 * Clean up upon an error in get_exportlist(). 2505 */ 2506 static void 2507 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) 2508 { 2509 struct grouplist *tgrp; 2510 2511 if (!(opt_flags & OP_QUIET)) { 2512 if (reason != NULL) 2513 syslog(LOG_ERR, "bad exports list line '%s': %s", line, 2514 reason); 2515 else 2516 syslog(LOG_ERR, "bad exports list line '%s'", line); 2517 } 2518 if (ep && (ep->ex_flag & EX_LINKED) == 0) 2519 free_exp(ep); 2520 while (grp) { 2521 tgrp = grp; 2522 grp = grp->gr_next; 2523 free_grp(tgrp); 2524 } 2525 } 2526 2527 /* 2528 * Search the export list for a matching fs. 2529 */ 2530 static struct exportlist * 2531 ex_search(fsid_t *fsid, struct exportlisthead *exhp) 2532 { 2533 struct exportlist *ep; 2534 uint32_t i; 2535 2536 i = EXPHASH(fsid); 2537 SLIST_FOREACH(ep, &exhp[i], entries) { 2538 if (fsidcmp(&ep->ex_fs, fsid) == 0) 2539 return (ep); 2540 } 2541 2542 return (ep); 2543 } 2544 2545 /* 2546 * Add a directory path to the list. 2547 */ 2548 static char * 2549 add_expdir(struct dirlist **dpp, char *cp, int len) 2550 { 2551 struct dirlist *dp; 2552 2553 dp = malloc(sizeof (struct dirlist)); 2554 if (dp == (struct dirlist *)NULL) 2555 out_of_mem(); 2556 dp->dp_left = *dpp; 2557 dp->dp_right = (struct dirlist *)NULL; 2558 dp->dp_flag = 0; 2559 dp->dp_hosts = (struct hostlist *)NULL; 2560 dp->dp_dirp = strndup(cp, len); 2561 if (dp->dp_dirp == NULL) 2562 out_of_mem(); 2563 *dpp = dp; 2564 return (dp->dp_dirp); 2565 } 2566 2567 /* 2568 * Hang the dir list element off the dirpath binary tree as required 2569 * and update the entry for host. 2570 */ 2571 static void 2572 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 2573 int flags, struct expcred *anoncrp, uint64_t exflags) 2574 { 2575 struct hostlist *hp; 2576 struct dirlist *dp2; 2577 2578 if (flags & OP_ALLDIRS) { 2579 if (ep->ex_defdir) 2580 free((caddr_t)dp); 2581 else 2582 ep->ex_defdir = dp; 2583 if (grp == (struct grouplist *)NULL) { 2584 ep->ex_flag |= EX_DEFSET; 2585 ep->ex_defdir->dp_flag |= DP_DEFSET; 2586 /* Save the default security flavors list. */ 2587 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2588 if (ep->ex_numsecflavors > 0) 2589 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2590 sizeof(ep->ex_secflavors)); 2591 cp_cred(&ep->ex_defanon, anoncrp); 2592 ep->ex_defexflags = exflags; 2593 } else while (grp) { 2594 hp = get_ht(); 2595 hp->ht_grp = grp; 2596 hp->ht_next = ep->ex_defdir->dp_hosts; 2597 ep->ex_defdir->dp_hosts = hp; 2598 /* Save the security flavors list for this host set. */ 2599 grp->gr_numsecflavors = ep->ex_numsecflavors; 2600 if (ep->ex_numsecflavors > 0) 2601 memcpy(grp->gr_secflavors, ep->ex_secflavors, 2602 sizeof(ep->ex_secflavors)); 2603 grp = grp->gr_next; 2604 } 2605 } else { 2606 2607 /* 2608 * Loop through the directories adding them to the tree. 2609 */ 2610 while (dp) { 2611 dp2 = dp->dp_left; 2612 add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp, 2613 exflags); 2614 dp = dp2; 2615 } 2616 } 2617 } 2618 2619 /* 2620 * Traverse the binary tree either updating a node that is already there 2621 * for the new directory or adding the new node. 2622 */ 2623 static void 2624 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 2625 int flags, struct exportlist *ep, struct expcred *anoncrp, 2626 uint64_t exflags) 2627 { 2628 struct dirlist *dp; 2629 struct hostlist *hp; 2630 int cmp; 2631 2632 dp = *dpp; 2633 if (dp) { 2634 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 2635 if (cmp > 0) { 2636 add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp, 2637 exflags); 2638 return; 2639 } else if (cmp < 0) { 2640 add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp, 2641 exflags); 2642 return; 2643 } else 2644 free((caddr_t)newdp); 2645 } else { 2646 dp = newdp; 2647 dp->dp_left = (struct dirlist *)NULL; 2648 *dpp = dp; 2649 } 2650 if (grp) { 2651 2652 /* 2653 * Hang all of the host(s) off of the directory point. 2654 */ 2655 do { 2656 hp = get_ht(); 2657 hp->ht_grp = grp; 2658 hp->ht_next = dp->dp_hosts; 2659 dp->dp_hosts = hp; 2660 /* Save the security flavors list for this host set. */ 2661 grp->gr_numsecflavors = ep->ex_numsecflavors; 2662 if (ep->ex_numsecflavors > 0) 2663 memcpy(grp->gr_secflavors, ep->ex_secflavors, 2664 sizeof(ep->ex_secflavors)); 2665 grp = grp->gr_next; 2666 } while (grp); 2667 } else { 2668 ep->ex_flag |= EX_DEFSET; 2669 dp->dp_flag |= DP_DEFSET; 2670 /* Save the default security flavors list. */ 2671 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2672 if (ep->ex_numsecflavors > 0) 2673 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2674 sizeof(ep->ex_secflavors)); 2675 cp_cred(&ep->ex_defanon, anoncrp); 2676 ep->ex_defexflags = exflags; 2677 } 2678 } 2679 2680 /* 2681 * Search for a dirpath on the export point. 2682 */ 2683 static struct dirlist * 2684 dirp_search(struct dirlist *dp, char *dirp) 2685 { 2686 int cmp; 2687 2688 if (dp) { 2689 cmp = strcmp(dp->dp_dirp, dirp); 2690 if (cmp > 0) 2691 return (dirp_search(dp->dp_left, dirp)); 2692 else if (cmp < 0) 2693 return (dirp_search(dp->dp_right, dirp)); 2694 else 2695 return (dp); 2696 } 2697 return (dp); 2698 } 2699 2700 /* 2701 * Scan for a host match in a directory tree. 2702 */ 2703 static int 2704 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, 2705 int *hostsetp, int *numsecflavors, int **secflavorsp) 2706 { 2707 struct hostlist *hp; 2708 struct grouplist *grp; 2709 struct addrinfo *ai; 2710 2711 if (dp) { 2712 if (dp->dp_flag & DP_DEFSET) 2713 *defsetp = dp->dp_flag; 2714 hp = dp->dp_hosts; 2715 while (hp) { 2716 grp = hp->ht_grp; 2717 switch (grp->gr_type) { 2718 case GT_HOST: 2719 ai = grp->gr_ptr.gt_addrinfo; 2720 for (; ai; ai = ai->ai_next) { 2721 if (!sacmp(ai->ai_addr, saddr, NULL)) { 2722 *hostsetp = 2723 (hp->ht_flag | DP_HOSTSET); 2724 if (numsecflavors != NULL) { 2725 *numsecflavors = 2726 grp->gr_numsecflavors; 2727 *secflavorsp = 2728 grp->gr_secflavors; 2729 } 2730 return (1); 2731 } 2732 } 2733 break; 2734 case GT_NET: 2735 if (!sacmp(saddr, (struct sockaddr *) 2736 &grp->gr_ptr.gt_net.nt_net, 2737 (struct sockaddr *) 2738 &grp->gr_ptr.gt_net.nt_mask)) { 2739 *hostsetp = (hp->ht_flag | DP_HOSTSET); 2740 if (numsecflavors != NULL) { 2741 *numsecflavors = 2742 grp->gr_numsecflavors; 2743 *secflavorsp = 2744 grp->gr_secflavors; 2745 } 2746 return (1); 2747 } 2748 break; 2749 } 2750 hp = hp->ht_next; 2751 } 2752 } 2753 return (0); 2754 } 2755 2756 /* 2757 * Scan tree for a host that matches the address. 2758 */ 2759 static int 2760 scan_tree(struct dirlist *dp, struct sockaddr *saddr) 2761 { 2762 int defset, hostset; 2763 2764 if (dp) { 2765 if (scan_tree(dp->dp_left, saddr)) 2766 return (1); 2767 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) 2768 return (1); 2769 if (scan_tree(dp->dp_right, saddr)) 2770 return (1); 2771 } 2772 return (0); 2773 } 2774 2775 /* 2776 * Traverse the dirlist tree and free it up. 2777 */ 2778 static void 2779 free_dir(struct dirlist *dp) 2780 { 2781 2782 if (dp) { 2783 free_dir(dp->dp_left); 2784 free_dir(dp->dp_right); 2785 free_host(dp->dp_hosts); 2786 free(dp->dp_dirp); 2787 free(dp); 2788 } 2789 } 2790 2791 /* 2792 * Parse a colon separated list of security flavors 2793 */ 2794 static int 2795 parsesec(char *seclist, struct exportlist *ep) 2796 { 2797 char *cp, savedc; 2798 int flavor; 2799 2800 ep->ex_numsecflavors = 0; 2801 for (;;) { 2802 cp = strchr(seclist, ':'); 2803 if (cp) { 2804 savedc = *cp; 2805 *cp = '\0'; 2806 } 2807 2808 if (!strcmp(seclist, "sys")) 2809 flavor = AUTH_SYS; 2810 else if (!strcmp(seclist, "krb5")) 2811 flavor = RPCSEC_GSS_KRB5; 2812 else if (!strcmp(seclist, "krb5i")) 2813 flavor = RPCSEC_GSS_KRB5I; 2814 else if (!strcmp(seclist, "krb5p")) 2815 flavor = RPCSEC_GSS_KRB5P; 2816 else { 2817 if (cp) 2818 *cp = savedc; 2819 syslog(LOG_ERR, "bad sec flavor: %s", seclist); 2820 return (1); 2821 } 2822 if (ep->ex_numsecflavors == MAXSECFLAVORS) { 2823 if (cp) 2824 *cp = savedc; 2825 syslog(LOG_ERR, "too many sec flavors: %s", seclist); 2826 return (1); 2827 } 2828 ep->ex_secflavors[ep->ex_numsecflavors] = flavor; 2829 ep->ex_numsecflavors++; 2830 if (cp) { 2831 *cp = savedc; 2832 seclist = cp + 1; 2833 } else { 2834 break; 2835 } 2836 } 2837 return (0); 2838 } 2839 2840 /* 2841 * Parse the option string and update fields. 2842 * Option arguments may either be -<option>=<value> or 2843 * -<option> <value> 2844 */ 2845 static int 2846 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp, 2847 int *has_hostp, uint64_t *exflagsp, struct expcred *cr, char *unvis_dir) 2848 { 2849 char *cpoptarg, *cpoptend; 2850 char *cp, *endcp, *cpopt, savedc, savedc2; 2851 int allflag, usedarg, fnd_equal; 2852 2853 savedc2 = '\0'; 2854 cpopt = *cpp; 2855 cpopt++; 2856 cp = *endcpp; 2857 savedc = *cp; 2858 *cp = '\0'; 2859 while (cpopt && *cpopt) { 2860 allflag = 1; 2861 usedarg = -2; 2862 fnd_equal = 0; 2863 if ((cpoptend = strchr(cpopt, ','))) { 2864 *cpoptend++ = '\0'; 2865 if ((cpoptarg = strchr(cpopt, '='))) { 2866 *cpoptarg++ = '\0'; 2867 fnd_equal = 1; 2868 } 2869 } else { 2870 if ((cpoptarg = strchr(cpopt, '='))) { 2871 *cpoptarg++ = '\0'; 2872 fnd_equal = 1; 2873 } else { 2874 *cp = savedc; 2875 nextfield(&cp, &endcp); 2876 **endcpp = '\0'; 2877 if (endcp > cp && *cp != '-') { 2878 cpoptarg = cp; 2879 savedc2 = *endcp; 2880 *endcp = '\0'; 2881 usedarg = 0; 2882 } 2883 } 2884 } 2885 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 2886 if (fnd_equal == 1) { 2887 syslog(LOG_ERR, "= after op: %s", cpopt); 2888 return (1); 2889 } 2890 *exflagsp |= MNT_EXRDONLY; 2891 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 2892 !(allflag = strcmp(cpopt, "mapall")) || 2893 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 2894 usedarg++; 2895 parsecred(cpoptarg, cr); 2896 if (allflag == 0) { 2897 *exflagsp |= MNT_EXPORTANON; 2898 opt_flags |= OP_MAPALL; 2899 } else 2900 opt_flags |= OP_MAPROOT; 2901 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 2902 !strcmp(cpopt, "m"))) { 2903 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 2904 syslog(LOG_ERR, "bad mask: %s", cpoptarg); 2905 return (1); 2906 } 2907 usedarg++; 2908 opt_flags |= OP_MASK; 2909 } else if (cpoptarg && (!strcmp(cpopt, "network") || 2910 !strcmp(cpopt, "n"))) { 2911 if (strchr(cpoptarg, '/') != NULL) { 2912 if (debug) 2913 fprintf(stderr, "setting OP_MASKLEN\n"); 2914 opt_flags |= OP_MASKLEN; 2915 } 2916 if (grp->gr_type != GT_NULL) { 2917 syslog(LOG_ERR, "network/host conflict"); 2918 return (1); 2919 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 2920 syslog(LOG_ERR, "bad net: %s", cpoptarg); 2921 return (1); 2922 } 2923 grp->gr_type = GT_NET; 2924 *has_hostp = 1; 2925 usedarg++; 2926 opt_flags |= OP_NET; 2927 } else if (!strcmp(cpopt, "alldirs")) { 2928 if (fnd_equal == 1) { 2929 syslog(LOG_ERR, "= after op: %s", cpopt); 2930 return (1); 2931 } 2932 if ((opt_flags & OP_NOTROOT) != 0) { 2933 syslog(LOG_ERR, "%s: path %s not mount point", 2934 cpopt, unvis_dir); 2935 if (alldirs_fail != 0) 2936 return (1); 2937 } 2938 opt_flags |= OP_ALLDIRS; 2939 } else if (!strcmp(cpopt, "public")) { 2940 if (fnd_equal == 1) { 2941 syslog(LOG_ERR, "= after op: %s", cpopt); 2942 return (1); 2943 } 2944 *exflagsp |= MNT_EXPUBLIC; 2945 } else if (!strcmp(cpopt, "webnfs")) { 2946 if (fnd_equal == 1) { 2947 syslog(LOG_ERR, "= after op: %s", cpopt); 2948 return (1); 2949 } 2950 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 2951 opt_flags |= OP_MAPALL; 2952 } else if (cpoptarg && !strcmp(cpopt, "index")) { 2953 ep->ex_indexfile = strdup(cpoptarg); 2954 } else if (!strcmp(cpopt, "quiet")) { 2955 if (fnd_equal == 1) { 2956 syslog(LOG_ERR, "= after op: %s", cpopt); 2957 return (1); 2958 } 2959 opt_flags |= OP_QUIET; 2960 } else if (cpoptarg && !strcmp(cpopt, "sec")) { 2961 if (parsesec(cpoptarg, ep)) 2962 return (1); 2963 opt_flags |= OP_SEC; 2964 usedarg++; 2965 } else if (!strcmp(cpopt, "tls")) { 2966 if (fnd_equal == 1) { 2967 syslog(LOG_ERR, "= after op: %s", cpopt); 2968 return (1); 2969 } 2970 *exflagsp |= MNT_EXTLS; 2971 } else if (!strcmp(cpopt, "tlscert")) { 2972 if (fnd_equal == 1) { 2973 syslog(LOG_ERR, "= after op: %s", cpopt); 2974 return (1); 2975 } 2976 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT); 2977 } else if (!strcmp(cpopt, "tlscertuser")) { 2978 if (fnd_equal == 1) { 2979 syslog(LOG_ERR, "= after op: %s", cpopt); 2980 return (1); 2981 } 2982 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT | 2983 MNT_EXTLSCERTUSER); 2984 } else { 2985 syslog(LOG_ERR, "bad opt %s", cpopt); 2986 return (1); 2987 } 2988 if (usedarg >= 0) { 2989 *endcp = savedc2; 2990 **endcpp = savedc; 2991 if (usedarg > 0) { 2992 *cpp = cp; 2993 *endcpp = endcp; 2994 } 2995 return (0); 2996 } 2997 cpopt = cpoptend; 2998 } 2999 **endcpp = savedc; 3000 return (0); 3001 } 3002 3003 /* 3004 * Translate a character string to the corresponding list of network 3005 * addresses for a hostname. 3006 */ 3007 static int 3008 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp) 3009 { 3010 struct grouplist *checkgrp; 3011 struct addrinfo *ai, *tai, hints; 3012 int ecode; 3013 char host[NI_MAXHOST]; 3014 3015 if (grp->gr_type != GT_NULL) { 3016 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 3017 return (1); 3018 } 3019 memset(&hints, 0, sizeof hints); 3020 hints.ai_flags = AI_CANONNAME; 3021 hints.ai_protocol = IPPROTO_UDP; 3022 ecode = getaddrinfo(cp, NULL, &hints, &ai); 3023 if (ecode != 0) { 3024 syslog(LOG_ERR,"can't get address info for host %s", cp); 3025 return 1; 3026 } 3027 grp->gr_ptr.gt_addrinfo = ai; 3028 while (ai != NULL) { 3029 if (ai->ai_canonname == NULL) { 3030 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 3031 sizeof host, NULL, 0, NI_NUMERICHOST) != 0) 3032 strlcpy(host, "?", sizeof(host)); 3033 ai->ai_canonname = strdup(host); 3034 ai->ai_flags |= AI_CANONNAME; 3035 } 3036 if (debug) 3037 fprintf(stderr, "got host %s\n", ai->ai_canonname); 3038 /* 3039 * Sanity check: make sure we don't already have an entry 3040 * for this host in the grouplist. 3041 */ 3042 for (checkgrp = tgrp; checkgrp != NULL; 3043 checkgrp = checkgrp->gr_next) { 3044 if (checkgrp->gr_type != GT_HOST) 3045 continue; 3046 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 3047 tai = tai->ai_next) { 3048 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 3049 continue; 3050 if (debug) 3051 fprintf(stderr, 3052 "ignoring duplicate host %s\n", 3053 ai->ai_canonname); 3054 grp->gr_type = GT_IGNORE; 3055 return (0); 3056 } 3057 } 3058 ai = ai->ai_next; 3059 } 3060 grp->gr_type = GT_HOST; 3061 return (0); 3062 } 3063 3064 /* 3065 * Free up an exports list component 3066 */ 3067 static void 3068 free_exp(struct exportlist *ep) 3069 { 3070 struct grouplist *grp, *tgrp; 3071 3072 if (ep->ex_defdir) { 3073 free_host(ep->ex_defdir->dp_hosts); 3074 free((caddr_t)ep->ex_defdir); 3075 } 3076 if (ep->ex_fsdir) 3077 free(ep->ex_fsdir); 3078 if (ep->ex_indexfile) 3079 free(ep->ex_indexfile); 3080 free_dir(ep->ex_dirl); 3081 grp = ep->ex_grphead; 3082 while (grp) { 3083 tgrp = grp; 3084 grp = grp->gr_next; 3085 free_grp(tgrp); 3086 } 3087 if (ep->ex_defanon.cr_groups != ep->ex_defanon.cr_smallgrps) 3088 free(ep->ex_defanon.cr_groups); 3089 free((caddr_t)ep); 3090 } 3091 3092 /* 3093 * Free up the v4root exports. 3094 */ 3095 static void 3096 free_v4rootexp(void) 3097 { 3098 3099 if (v4root_ep != NULL) { 3100 free_exp(v4root_ep); 3101 v4root_ep = NULL; 3102 } 3103 } 3104 3105 /* 3106 * Free hosts. 3107 */ 3108 static void 3109 free_host(struct hostlist *hp) 3110 { 3111 struct hostlist *hp2; 3112 3113 while (hp) { 3114 hp2 = hp; 3115 hp = hp->ht_next; 3116 free((caddr_t)hp2); 3117 } 3118 } 3119 3120 static struct hostlist * 3121 get_ht(void) 3122 { 3123 struct hostlist *hp; 3124 3125 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 3126 if (hp == (struct hostlist *)NULL) 3127 out_of_mem(); 3128 hp->ht_next = (struct hostlist *)NULL; 3129 hp->ht_flag = 0; 3130 return (hp); 3131 } 3132 3133 /* 3134 * Out of memory, fatal 3135 */ 3136 static void 3137 out_of_mem(void) 3138 { 3139 3140 syslog(LOG_ERR, "out of memory"); 3141 exit(2); 3142 } 3143 3144 /* 3145 * Call do_mount() from the struct exportlist, for each case needed. 3146 */ 3147 static int 3148 do_export_mount(struct exportlist *ep, struct statfs *fsp) 3149 { 3150 struct grouplist *grp, defgrp; 3151 int ret; 3152 size_t dirlen; 3153 3154 LOGDEBUG("do_export_mount=%s", ep->ex_fsdir); 3155 dirlen = strlen(ep->ex_fsdir); 3156 if ((ep->ex_flag & EX_DEFSET) != 0) { 3157 defgrp.gr_type = GT_DEFAULT; 3158 defgrp.gr_next = NULL; 3159 /* We have an entry for all other hosts/nets. */ 3160 LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags); 3161 ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon, 3162 ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors, 3163 ep->ex_defsecflavors); 3164 if (ret != 0) 3165 return (ret); 3166 } 3167 3168 /* Do a mount for each group. */ 3169 grp = ep->ex_grphead; 3170 while (grp != NULL) { 3171 LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx", 3172 grp->gr_type, (uintmax_t)grp->gr_exflags); 3173 ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon, 3174 ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors, 3175 grp->gr_secflavors); 3176 if (ret != 0) 3177 return (ret); 3178 grp = grp->gr_next; 3179 } 3180 return (0); 3181 } 3182 3183 /* 3184 * Do the nmount() syscall with the update flag to push the export info into 3185 * the kernel. 3186 */ 3187 static int 3188 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags, 3189 struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb, 3190 int numsecflavors, int *secflavors) 3191 { 3192 struct statfs fsb1; 3193 struct addrinfo *ai; 3194 struct export_args *eap; 3195 char errmsg[255]; 3196 char *cp; 3197 int done; 3198 char savedc; 3199 struct iovec *iov; 3200 int i, iovlen; 3201 int ret; 3202 struct nfsex_args nfsea; 3203 3204 eap = &nfsea.export; 3205 3206 cp = NULL; 3207 savedc = '\0'; 3208 iov = NULL; 3209 iovlen = 0; 3210 ret = 0; 3211 3212 bzero(eap, sizeof (struct export_args)); 3213 bzero(errmsg, sizeof(errmsg)); 3214 eap->ex_flags = exflags; 3215 eap->ex_uid = anoncrp->cr_uid; 3216 eap->ex_ngroups = anoncrp->cr_ngroups; 3217 /* 3218 * Use the memory pointed to by 'anoncrp', as it outlives 'eap' which is 3219 * local to this function. 3220 */ 3221 eap->ex_groups = anoncrp->cr_groups; 3222 LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags); 3223 eap->ex_indexfile = ep->ex_indexfile; 3224 if (grp->gr_type == GT_HOST) 3225 ai = grp->gr_ptr.gt_addrinfo; 3226 else 3227 ai = NULL; 3228 eap->ex_numsecflavors = numsecflavors; 3229 LOGDEBUG("do_mount numsec=%d", numsecflavors); 3230 for (i = 0; i < eap->ex_numsecflavors; i++) 3231 eap->ex_secflavors[i] = secflavors[i]; 3232 if (eap->ex_numsecflavors == 0) { 3233 eap->ex_numsecflavors = 1; 3234 eap->ex_secflavors[0] = AUTH_SYS; 3235 } 3236 done = FALSE; 3237 3238 if (v4root_phase == 0) { 3239 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 3240 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 3241 build_iovec(&iov, &iovlen, "from", NULL, 0); 3242 build_iovec(&iov, &iovlen, "update", NULL, 0); 3243 build_iovec(&iov, &iovlen, "export", eap, 3244 sizeof (struct export_args)); 3245 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 3246 } 3247 3248 while (!done) { 3249 switch (grp->gr_type) { 3250 case GT_HOST: 3251 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 3252 goto skip; 3253 eap->ex_addr = ai->ai_addr; 3254 eap->ex_addrlen = ai->ai_addrlen; 3255 eap->ex_masklen = 0; 3256 break; 3257 case GT_NET: 3258 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 3259 have_v6 == 0) 3260 goto skip; 3261 eap->ex_addr = 3262 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 3263 eap->ex_addrlen = 3264 ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len; 3265 eap->ex_mask = 3266 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 3267 eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len; 3268 break; 3269 case GT_DEFAULT: 3270 eap->ex_addr = NULL; 3271 eap->ex_addrlen = 0; 3272 eap->ex_mask = NULL; 3273 eap->ex_masklen = 0; 3274 break; 3275 case GT_IGNORE: 3276 ret = 0; 3277 goto error_exit; 3278 break; 3279 default: 3280 syslog(LOG_ERR, "bad grouptype"); 3281 if (cp) 3282 *cp = savedc; 3283 ret = 1; 3284 goto error_exit; 3285 } 3286 3287 /* 3288 * For V4:, use the nfssvc() syscall, instead of mount(). 3289 */ 3290 if (v4root_phase == 2) { 3291 nfsea.fspec = v4root_dirpath; 3292 if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, 3293 (caddr_t)&nfsea) < 0) { 3294 syslog(LOG_ERR, "Exporting V4: failed"); 3295 ret = 2; 3296 goto error_exit; 3297 } 3298 } else { 3299 /* 3300 * XXX: 3301 * Maybe I should just use the fsb->f_mntonname path 3302 * instead of looping back up the dirp to the mount 3303 * point?? 3304 * Also, needs to know how to export all types of local 3305 * exportable filesystems and not just "ufs". 3306 */ 3307 iov[1].iov_base = fsb->f_fstypename; /* "fstype" */ 3308 iov[1].iov_len = strlen(fsb->f_fstypename) + 1; 3309 iov[3].iov_base = fsb->f_mntonname; /* "fspath" */ 3310 iov[3].iov_len = strlen(fsb->f_mntonname) + 1; 3311 iov[5].iov_base = fsb->f_mntfromname; /* "from" */ 3312 iov[5].iov_len = strlen(fsb->f_mntfromname) + 1; 3313 errmsg[0] = '\0'; 3314 3315 while (nmount(iov, iovlen, fsb->f_flags) < 0) { 3316 if (cp) 3317 *cp-- = savedc; 3318 else 3319 cp = dirp + dirplen - 1; 3320 if (opt_flags & OP_QUIET) { 3321 ret = 1; 3322 goto error_exit; 3323 } 3324 if (errno == EPERM) { 3325 if (debug) 3326 warnx("can't change attributes for %s: %s", 3327 dirp, errmsg); 3328 syslog(LOG_ERR, 3329 "can't change attributes for %s: %s", 3330 dirp, errmsg); 3331 ret = 1; 3332 goto error_exit; 3333 } 3334 if ((opt_flags & OP_ALLDIRS) && 3335 alldirs_fail != 0) { 3336 if (errno == EINVAL) 3337 syslog(LOG_ERR, 3338 "-alldirs requested but %s is not a filesystem mountpoint", 3339 dirp); 3340 else 3341 syslog(LOG_ERR, 3342 "could not remount %s: %m", 3343 dirp); 3344 ret = 1; 3345 goto error_exit; 3346 } 3347 /* back up over the last component */ 3348 while (cp > dirp && *cp == '/') 3349 cp--; 3350 while (cp > dirp && *(cp - 1) != '/') 3351 cp--; 3352 if (cp == dirp) { 3353 if (debug) 3354 warnx("mnt unsucc"); 3355 syslog(LOG_ERR, "can't export %s %s", 3356 dirp, errmsg); 3357 ret = 1; 3358 goto error_exit; 3359 } 3360 savedc = *cp; 3361 *cp = '\0'; 3362 /* 3363 * Check that we're still on the same 3364 * filesystem. 3365 */ 3366 if (statfs(dirp, &fsb1) != 0 || 3367 fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) { 3368 *cp = savedc; 3369 syslog(LOG_ERR, 3370 "can't export %s %s", dirp, 3371 errmsg); 3372 ret = 1; 3373 goto error_exit; 3374 } 3375 } 3376 } 3377 3378 /* 3379 * For the experimental server: 3380 * If this is the public directory, get the file handle 3381 * and load it into the kernel via the nfssvc() syscall. 3382 */ 3383 if ((exflags & MNT_EXPUBLIC) != 0) { 3384 fhandle_t fh; 3385 char *public_name; 3386 3387 if (eap->ex_indexfile != NULL) 3388 public_name = eap->ex_indexfile; 3389 else 3390 public_name = dirp; 3391 if (getfh(public_name, &fh) < 0) 3392 syslog(LOG_ERR, 3393 "Can't get public fh for %s", public_name); 3394 else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) 3395 syslog(LOG_ERR, 3396 "Can't set public fh for %s", public_name); 3397 else { 3398 has_publicfh = 1; 3399 has_set_publicfh = 1; 3400 ep->ex_flag |= EX_PUBLICFH; 3401 } 3402 } 3403 skip: 3404 if (ai != NULL) 3405 ai = ai->ai_next; 3406 if (ai == NULL) 3407 done = TRUE; 3408 } 3409 if (cp) 3410 *cp = savedc; 3411 error_exit: 3412 /* free strings allocated by strdup() in getmntopts.c */ 3413 if (iov != NULL) { 3414 free(iov[0].iov_base); /* fstype */ 3415 free(iov[2].iov_base); /* fspath */ 3416 free(iov[4].iov_base); /* from */ 3417 free(iov[6].iov_base); /* update */ 3418 free(iov[8].iov_base); /* export */ 3419 free(iov[10].iov_base); /* errmsg */ 3420 3421 /* free iov, allocated by realloc() */ 3422 free(iov); 3423 } 3424 return (ret); 3425 } 3426 3427 /* 3428 * Translate a net address. 3429 * 3430 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 3431 */ 3432 static int 3433 get_net(char *cp, struct netmsk *net, int maskflg) 3434 { 3435 struct netent *np = NULL; 3436 char *name, *p, *prefp; 3437 struct sockaddr_in sin; 3438 struct sockaddr *sa = NULL; 3439 struct addrinfo hints, *ai = NULL; 3440 char netname[NI_MAXHOST]; 3441 long preflen; 3442 3443 p = prefp = NULL; 3444 if ((opt_flags & OP_MASKLEN) && !maskflg) { 3445 p = strchr(cp, '/'); 3446 *p = '\0'; 3447 prefp = p + 1; 3448 } 3449 3450 /* 3451 * Check for a numeric address first. We wish to avoid 3452 * possible DNS lookups in getnetbyname(). 3453 */ 3454 if (isxdigit(*cp) || *cp == ':') { 3455 memset(&hints, 0, sizeof hints); 3456 /* Ensure the mask and the network have the same family. */ 3457 if (maskflg && (opt_flags & OP_NET)) 3458 hints.ai_family = net->nt_net.ss_family; 3459 else if (!maskflg && (opt_flags & OP_HAVEMASK)) 3460 hints.ai_family = net->nt_mask.ss_family; 3461 else 3462 hints.ai_family = AF_UNSPEC; 3463 hints.ai_flags = AI_NUMERICHOST; 3464 if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 3465 sa = ai->ai_addr; 3466 if (sa != NULL && ai->ai_family == AF_INET) { 3467 /* 3468 * The address in `cp' is really a network address, so 3469 * use inet_network() to re-interpret this correctly. 3470 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 3471 */ 3472 bzero(&sin, sizeof sin); 3473 sin.sin_family = AF_INET; 3474 sin.sin_len = sizeof sin; 3475 sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 3476 if (debug) 3477 fprintf(stderr, "get_net: v4 addr %s\n", 3478 inet_ntoa(sin.sin_addr)); 3479 sa = (struct sockaddr *)&sin; 3480 } 3481 } 3482 if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 3483 bzero(&sin, sizeof sin); 3484 sin.sin_family = AF_INET; 3485 sin.sin_len = sizeof sin; 3486 sin.sin_addr = inet_makeaddr(np->n_net, 0); 3487 sa = (struct sockaddr *)&sin; 3488 } 3489 if (sa == NULL) 3490 goto fail; 3491 3492 if (maskflg) { 3493 /* The specified sockaddr is a mask. */ 3494 if (checkmask(sa) != 0) 3495 goto fail; 3496 bcopy(sa, &net->nt_mask, sa->sa_len); 3497 opt_flags |= OP_HAVEMASK; 3498 opt_flags &= ~OP_CLASSMASK; 3499 } else { 3500 /* The specified sockaddr is a network address. */ 3501 bcopy(sa, &net->nt_net, sa->sa_len); 3502 3503 /* Get a network name for the export list. */ 3504 if (np) { 3505 name = np->n_name; 3506 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 3507 NULL, 0, NI_NUMERICHOST) == 0) { 3508 name = netname; 3509 } else { 3510 goto fail; 3511 } 3512 if ((net->nt_name = strdup(name)) == NULL) 3513 out_of_mem(); 3514 3515 /* 3516 * Extract a mask from either a "/<masklen>" suffix, or 3517 * from the class of an IPv4 address. 3518 */ 3519 if (opt_flags & OP_MASKLEN) { 3520 preflen = strtol(prefp, NULL, 10); 3521 if (preflen < 0L || preflen == LONG_MAX) 3522 goto fail; 3523 bcopy(sa, &net->nt_mask, sa->sa_len); 3524 if (makemask(&net->nt_mask, (int)preflen) != 0) 3525 goto fail; 3526 opt_flags |= OP_HAVEMASK; 3527 *p = '/'; 3528 } else if (sa->sa_family == AF_INET && 3529 (opt_flags & OP_MASK) == 0) { 3530 in_addr_t addr; 3531 3532 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 3533 if (IN_CLASSA(addr)) 3534 preflen = 8; 3535 else if (IN_CLASSB(addr)) 3536 preflen = 16; 3537 else if (IN_CLASSC(addr)) 3538 preflen = 24; 3539 else if (IN_CLASSD(addr)) /* XXX Multicast??? */ 3540 preflen = 28; 3541 else 3542 preflen = 32; /* XXX */ 3543 3544 bcopy(sa, &net->nt_mask, sa->sa_len); 3545 makemask(&net->nt_mask, (int)preflen); 3546 opt_flags |= OP_HAVEMASK | OP_CLASSMASK; 3547 } 3548 } 3549 3550 if (ai) 3551 freeaddrinfo(ai); 3552 return 0; 3553 3554 fail: 3555 if (ai) 3556 freeaddrinfo(ai); 3557 return 1; 3558 } 3559 3560 /* 3561 * Parse out the next white space separated field 3562 */ 3563 static void 3564 nextfield(char **cp, char **endcp) 3565 { 3566 char *p; 3567 char quot = 0; 3568 3569 p = *cp; 3570 while (*p == ' ' || *p == '\t') 3571 p++; 3572 *cp = p; 3573 while (*p != '\0') { 3574 if (quot) { 3575 if (*p == quot) 3576 quot = 0; 3577 } else { 3578 if (*p == '\\' && *(p + 1) != '\0') 3579 p++; 3580 else if (*p == '\'' || *p == '"') 3581 quot = *p; 3582 else if (*p == ' ' || *p == '\t') 3583 break; 3584 } 3585 p++; 3586 }; 3587 *endcp = p; 3588 } 3589 3590 /* 3591 * Get an exports file line. Skip over blank lines and handle line 3592 * continuations. 3593 */ 3594 static int 3595 get_line(void) 3596 { 3597 char *p, *cp; 3598 size_t len; 3599 int totlen, cont_line; 3600 3601 /* 3602 * Loop around ignoring blank lines and getting all continuation lines. 3603 */ 3604 p = line; 3605 totlen = 0; 3606 do { 3607 if ((p = fgetln(exp_file, &len)) == NULL) 3608 return (0); 3609 cp = p + len - 1; 3610 cont_line = 0; 3611 while (cp >= p && 3612 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 3613 if (*cp == '\\') 3614 cont_line = 1; 3615 cp--; 3616 len--; 3617 } 3618 if (cont_line) { 3619 *++cp = ' '; 3620 len++; 3621 } 3622 if (linesize < len + totlen + 1) { 3623 linesize = len + totlen + 1; 3624 line = realloc(line, linesize); 3625 if (line == NULL) 3626 out_of_mem(); 3627 } 3628 memcpy(line + totlen, p, len); 3629 totlen += len; 3630 line[totlen] = '\0'; 3631 } while (totlen == 0 || cont_line); 3632 return (1); 3633 } 3634 3635 /* 3636 * Parse a description of a credential. 3637 */ 3638 static void 3639 parsecred(char *names, struct expcred *cr) 3640 { 3641 char *name; 3642 struct passwd *pw; 3643 unsigned long name_ul; 3644 char *end = NULL; 3645 3646 assert(cr->cr_groups == tmp_groups); 3647 3648 /* 3649 * Parse the user and if possible get its password table entry. 3650 * 'cr_uid' is filled when exiting this block. 3651 */ 3652 name = strsep_quote(&names, ":"); 3653 name_ul = strtoul(name, &end, 10); 3654 if (*end != '\0' || end == name) 3655 pw = getpwnam(name); 3656 else 3657 pw = getpwuid((uid_t)name_ul); 3658 if (pw != NULL) { 3659 cr->cr_uid = pw->pw_uid; 3660 } else if (*end != '\0' || end == name) { 3661 syslog(LOG_ERR, "unknown user: %s", name); 3662 cr->cr_uid = UID_NOBODY; 3663 goto nogroup; 3664 } else { 3665 cr->cr_uid = name_ul; 3666 } 3667 3668 /* 3669 * Credentials specified as those of a user (i.e., use its associated 3670 * groups as specified in the password database). 3671 */ 3672 if (names == NULL) { 3673 if (pw == NULL) { 3674 syslog(LOG_ERR, "no passwd entry for user: %s, " 3675 "can't determine groups", name); 3676 goto nogroup; 3677 } 3678 3679 cr->cr_ngroups = tngroups_max; 3680 if (getgrouplist(pw->pw_name, pw->pw_gid, 3681 cr->cr_groups, &cr->cr_ngroups) != 0) { 3682 syslog(LOG_ERR, "too many groups"); 3683 cr->cr_ngroups = tngroups_max; 3684 } 3685 return; 3686 } 3687 3688 /* 3689 * Explicit credentials specified as a colon separated list: 3690 * uid:gid:gid:... 3691 */ 3692 cr->cr_ngroups = 0; 3693 while (names != NULL && *names != '\0') { 3694 const struct group *gr; 3695 gid_t group; 3696 3697 name = strsep_quote(&names, ":"); 3698 name_ul = strtoul(name, &end, 10); 3699 if (*end != '\0' || end == name) { 3700 if ((gr = getgrnam(name)) == NULL) { 3701 syslog(LOG_ERR, "unknown group: %s", name); 3702 continue; 3703 } 3704 group = gr->gr_gid; 3705 } else { 3706 group = name_ul; 3707 } 3708 if (cr->cr_ngroups == tngroups_max) { 3709 syslog(LOG_ERR, "too many groups"); 3710 break; 3711 } 3712 cr->cr_groups[cr->cr_ngroups++] = group; 3713 } 3714 if (cr->cr_ngroups == 0) 3715 goto nogroup; 3716 return; 3717 3718 nogroup: 3719 cr->cr_ngroups = 1; 3720 cr->cr_groups[0] = nogroup(); 3721 } 3722 3723 #define STRSIZ (MNTNAMLEN+MNTPATHLEN+50) 3724 /* 3725 * Routines that maintain the remote mounttab 3726 */ 3727 static void 3728 get_mountlist(void) 3729 { 3730 struct mountlist *mlp; 3731 char *host, *dirp, *cp; 3732 char str[STRSIZ]; 3733 FILE *mlfile; 3734 3735 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 3736 if (errno == ENOENT) 3737 return; 3738 else { 3739 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 3740 return; 3741 } 3742 } 3743 while (fgets(str, STRSIZ, mlfile) != NULL) { 3744 cp = str; 3745 host = strsep(&cp, " \t\n"); 3746 dirp = strsep(&cp, " \t\n"); 3747 if (host == NULL || dirp == NULL) 3748 continue; 3749 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 3750 if (mlp == (struct mountlist *)NULL) 3751 out_of_mem(); 3752 strncpy(mlp->ml_host, host, MNTNAMLEN); 3753 mlp->ml_host[MNTNAMLEN] = '\0'; 3754 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3755 mlp->ml_dirp[MNTPATHLEN] = '\0'; 3756 3757 SLIST_INSERT_HEAD(&mlhead, mlp, next); 3758 } 3759 fclose(mlfile); 3760 } 3761 3762 static void 3763 del_mlist(char *hostp, char *dirp) 3764 { 3765 struct mountlist *mlp, *mlp2; 3766 FILE *mlfile; 3767 int fnd = 0; 3768 3769 SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) { 3770 if (!strcmp(mlp->ml_host, hostp) && 3771 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 3772 fnd = 1; 3773 SLIST_REMOVE(&mlhead, mlp, mountlist, next); 3774 free((caddr_t)mlp); 3775 } 3776 } 3777 if (fnd) { 3778 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 3779 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 3780 return; 3781 } 3782 SLIST_FOREACH(mlp, &mlhead, next) { 3783 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3784 } 3785 fclose(mlfile); 3786 } 3787 } 3788 3789 static void 3790 add_mlist(char *hostp, char *dirp) 3791 { 3792 struct mountlist *mlp; 3793 FILE *mlfile; 3794 3795 SLIST_FOREACH(mlp, &mlhead, next) { 3796 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 3797 return; 3798 } 3799 3800 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 3801 if (mlp == (struct mountlist *)NULL) 3802 out_of_mem(); 3803 strncpy(mlp->ml_host, hostp, MNTNAMLEN); 3804 mlp->ml_host[MNTNAMLEN] = '\0'; 3805 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3806 mlp->ml_dirp[MNTPATHLEN] = '\0'; 3807 SLIST_INSERT_HEAD(&mlhead, mlp, next); 3808 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 3809 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 3810 return; 3811 } 3812 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3813 fclose(mlfile); 3814 } 3815 3816 /* 3817 * Free up a group list. 3818 */ 3819 static void 3820 free_grp(struct grouplist *grp) 3821 { 3822 if (grp->gr_type == GT_HOST) { 3823 if (grp->gr_ptr.gt_addrinfo != NULL) 3824 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 3825 } else if (grp->gr_type == GT_NET) { 3826 if (grp->gr_ptr.gt_net.nt_name) 3827 free(grp->gr_ptr.gt_net.nt_name); 3828 } 3829 if (grp->gr_anon.cr_groups != grp->gr_anon.cr_smallgrps) 3830 free(grp->gr_anon.cr_groups); 3831 free((caddr_t)grp); 3832 } 3833 3834 #ifdef DEBUG 3835 static void 3836 SYSLOG(int pri, const char *fmt, ...) 3837 { 3838 va_list ap; 3839 3840 va_start(ap, fmt); 3841 vfprintf(stderr, fmt, ap); 3842 va_end(ap); 3843 } 3844 #endif /* DEBUG */ 3845 3846 /* 3847 * Check options for consistency. 3848 */ 3849 static int 3850 check_options(struct dirlist *dp) 3851 { 3852 3853 if (v4root_phase == 0 && dp == NULL) 3854 return (1); 3855 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 3856 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 3857 return (1); 3858 } 3859 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 3860 syslog(LOG_ERR, "-mask requires -network"); 3861 return (1); 3862 } 3863 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 3864 syslog(LOG_ERR, "-network requires mask specification"); 3865 return (1); 3866 } 3867 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 3868 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 3869 return (1); 3870 } 3871 if (v4root_phase > 0 && 3872 (opt_flags & 3873 ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) { 3874 syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:"); 3875 return (1); 3876 } 3877 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 3878 syslog(LOG_ERR, "-alldirs has multiple directories"); 3879 return (1); 3880 } 3881 return (0); 3882 } 3883 3884 static int 3885 check_path_component(const char *path, char **err) 3886 { 3887 struct stat sb; 3888 3889 if (lstat(path, &sb)) { 3890 asprintf(err, "%s: lstat() failed: %s.\n", 3891 path, strerror(errno)); 3892 return (0); 3893 } 3894 3895 switch (sb.st_mode & S_IFMT) { 3896 case S_IFDIR: 3897 return (1); 3898 case S_IFLNK: 3899 asprintf(err, "%s: path is a symbolic link.\n", path); 3900 break; 3901 case S_IFREG: 3902 asprintf(err, "%s: path is a file rather than a directory.\n", 3903 path); 3904 break; 3905 default: 3906 asprintf(err, "%s: path is not a directory.\n", path); 3907 } 3908 3909 return (0); 3910 } 3911 3912 /* 3913 * Check each path component for the presence of symbolic links. Return true 3914 */ 3915 static int 3916 check_dirpath(char *dirp, char **err) 3917 { 3918 char *cp; 3919 3920 cp = dirp + 1; 3921 while (*cp) { 3922 if (*cp == '/') { 3923 *cp = '\0'; 3924 3925 if (!check_path_component(dirp, err)) { 3926 *cp = '/'; 3927 return (0); 3928 } 3929 3930 *cp = '/'; 3931 } 3932 cp++; 3933 } 3934 3935 if (!check_path_component(dirp, err)) 3936 return (0); 3937 3938 return (1); 3939 } 3940 3941 /* 3942 * Populate statfs information. Return true on success. 3943 */ 3944 static int 3945 check_statfs(const char *dirp, struct statfs *fsb, char **err) 3946 { 3947 if (statfs(dirp, fsb)) { 3948 asprintf(err, "%s: statfs() failed: %s\n", dirp, 3949 strerror(errno)); 3950 return (0); 3951 } 3952 3953 return (1); 3954 } 3955 3956 /* 3957 * Make a netmask according to the specified prefix length. The ss_family 3958 * and other non-address fields must be initialised before calling this. 3959 */ 3960 static int 3961 makemask(struct sockaddr_storage *ssp, int bitlen) 3962 { 3963 u_char *p; 3964 int bits, i, len; 3965 3966 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 3967 return (-1); 3968 if (bitlen > len * CHAR_BIT) 3969 return (-1); 3970 3971 for (i = 0; i < len; i++) { 3972 bits = MIN(CHAR_BIT, bitlen); 3973 *p++ = (u_char)~0 << (CHAR_BIT - bits); 3974 bitlen -= bits; 3975 } 3976 return 0; 3977 } 3978 3979 /* 3980 * Check that the sockaddr is a valid netmask. Returns 0 if the mask 3981 * is acceptable (i.e. of the form 1...10....0). 3982 */ 3983 static int 3984 checkmask(struct sockaddr *sa) 3985 { 3986 u_char *mask; 3987 int i, len; 3988 3989 if ((mask = sa_rawaddr(sa, &len)) == NULL) 3990 return (-1); 3991 3992 for (i = 0; i < len; i++) 3993 if (mask[i] != 0xff) 3994 break; 3995 if (i < len) { 3996 if (~mask[i] & (u_char)(~mask[i] + 1)) 3997 return (-1); 3998 i++; 3999 } 4000 for (; i < len; i++) 4001 if (mask[i] != 0) 4002 return (-1); 4003 return (0); 4004 } 4005 4006 /* 4007 * Compare two sockaddrs according to a specified mask. Return zero if 4008 * `sa1' matches `sa2' when filtered by the netmask in `samask'. 4009 * If samask is NULL, perform a full comparison. 4010 */ 4011 static int 4012 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 4013 { 4014 unsigned char *p1, *p2, *mask; 4015 int len, i; 4016 4017 if (sa1->sa_family != sa2->sa_family || 4018 (p1 = sa_rawaddr(sa1, &len)) == NULL || 4019 (p2 = sa_rawaddr(sa2, NULL)) == NULL) 4020 return (1); 4021 4022 switch (sa1->sa_family) { 4023 case AF_INET6: 4024 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 4025 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 4026 return (1); 4027 break; 4028 } 4029 4030 /* Simple binary comparison if no mask specified. */ 4031 if (samask == NULL) 4032 return (memcmp(p1, p2, len)); 4033 4034 /* Set up the mask, and do a mask-based comparison. */ 4035 if (sa1->sa_family != samask->sa_family || 4036 (mask = sa_rawaddr(samask, NULL)) == NULL) 4037 return (1); 4038 4039 for (i = 0; i < len; i++) 4040 if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 4041 return (1); 4042 return (0); 4043 } 4044 4045 /* 4046 * Return a pointer to the part of the sockaddr that contains the 4047 * raw address, and set *nbytes to its length in bytes. Returns 4048 * NULL if the address family is unknown. 4049 */ 4050 static void * 4051 sa_rawaddr(struct sockaddr *sa, int *nbytes) { 4052 void *p; 4053 int len; 4054 4055 switch (sa->sa_family) { 4056 case AF_INET: 4057 len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 4058 p = &((struct sockaddr_in *)sa)->sin_addr; 4059 break; 4060 case AF_INET6: 4061 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 4062 p = &((struct sockaddr_in6 *)sa)->sin6_addr; 4063 break; 4064 default: 4065 p = NULL; 4066 len = 0; 4067 } 4068 4069 if (nbytes != NULL) 4070 *nbytes = len; 4071 return (p); 4072 } 4073 4074 static void 4075 huphandler(int sig __unused) 4076 { 4077 4078 got_sighup = 1; 4079 } 4080 4081 static void 4082 terminate(int sig __unused) 4083 { 4084 free(tmp_groups); 4085 pidfile_remove(pfh); 4086 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 4087 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 4088 exit (0); 4089 } 4090 4091 static void 4092 cp_cred(struct expcred *outcr, struct expcred *incr) 4093 { 4094 4095 outcr->cr_uid = incr->cr_uid; 4096 outcr->cr_ngroups = incr->cr_ngroups; 4097 if (outcr->cr_ngroups > SMALLNGROUPS) 4098 outcr->cr_groups = malloc(outcr->cr_ngroups * sizeof(gid_t)); 4099 else 4100 outcr->cr_groups = outcr->cr_smallgrps; 4101 memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups * 4102 sizeof(gid_t)); 4103 } 4104 4105 static gid_t 4106 nogroup() 4107 { 4108 static gid_t nogroup = 0; /* 0 means unset. */ 4109 4110 if (nogroup == 0) { 4111 const struct group *gr = getgrnam("nogroup"); 4112 4113 if (gr != NULL && gr->gr_gid != 0) 4114 nogroup = gr->gr_gid; 4115 else 4116 nogroup = GID_NOGROUP; 4117 } 4118 return (nogroup); 4119 } 4120