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 <mntopts.h> 68 #include <netdb.h> 69 #include <pwd.h> 70 #include <signal.h> 71 #include <stdio.h> 72 #include <stdlib.h> 73 #include <string.h> 74 #include <unistd.h> 75 #include <vis.h> 76 #include "pathnames.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_ERR, 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 free_iovec(&iov, &iovlen); 2103 2104 /* 2105 * If there was no public fh, clear any previous one set. 2106 */ 2107 if (has_publicfh == 0) { 2108 LOGDEBUG("clear public fh"); 2109 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); 2110 } 2111 2112 /* Resume the nfsd. If they weren't suspended, this is harmless. */ 2113 (void)nfssvc(NFSSVC_RESUMENFSD, NULL); 2114 LOGDEBUG("eo get_exportlist"); 2115 } 2116 2117 /* 2118 * Insert an export entry in the appropriate list. 2119 */ 2120 static void 2121 insert_exports(struct exportlist *ep, struct exportlisthead *exhp) 2122 { 2123 uint32_t i; 2124 2125 i = EXPHASH(&ep->ex_fs); 2126 LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i); 2127 SLIST_INSERT_HEAD(&exhp[i], ep, entries); 2128 } 2129 2130 /* 2131 * Free up the exports lists passed in as arguments. 2132 */ 2133 static void 2134 free_exports(struct exportlisthead *exhp) 2135 { 2136 struct exportlist *ep, *ep2; 2137 int i; 2138 2139 for (i = 0; i < exphashsize; i++) { 2140 SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) { 2141 SLIST_REMOVE(&exhp[i], ep, exportlist, entries); 2142 free_exp(ep); 2143 } 2144 SLIST_INIT(&exhp[i]); 2145 } 2146 } 2147 2148 /* 2149 * Read the exports file(s) and call get_exportlist_one() for each line. 2150 */ 2151 static void 2152 read_exportfile(int passno) 2153 { 2154 int done, i; 2155 2156 /* 2157 * Read in the exports file and build the list, calling 2158 * nmount() as we go along to push the export rules into the kernel. 2159 */ 2160 done = 0; 2161 for (i = 0; exnames[i] != NULL; i++) { 2162 if (debug) 2163 warnx("reading exports from %s", exnames[i]); 2164 if ((exp_file = fopen(exnames[i], "r")) == NULL) { 2165 syslog(LOG_WARNING, "can't open %s", exnames[i]); 2166 continue; 2167 } 2168 get_exportlist_one(passno); 2169 fclose(exp_file); 2170 done++; 2171 } 2172 if (done == 0) { 2173 syslog(LOG_ERR, "can't open any exports file"); 2174 exit(2); 2175 } 2176 } 2177 2178 /* 2179 * Compare the export lists against the old ones and do nmount() operations 2180 * for any cases that have changed. This avoids doing nmount() for entries 2181 * that have not changed. 2182 * Return 0 upon success, 1 otherwise. 2183 */ 2184 static int 2185 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg) 2186 { 2187 struct exportlist *ep, *oep; 2188 struct grouplist *grp; 2189 struct statfs fs, ofs; 2190 int i, ret; 2191 2192 /* 2193 * Loop through the current list and look for an entry in the old 2194 * list. 2195 * If found, check to see if it the same. 2196 * If it is not the same, delete and re-export. 2197 * Then mark it done on the old list. 2198 * else (not found) 2199 * export it. 2200 * Any entries left in the old list after processing must have their 2201 * exports deleted. 2202 */ 2203 for (i = 0; i < exphashsize; i++) 2204 SLIST_FOREACH(ep, &exphead[i], entries) { 2205 LOGDEBUG("foreach ep=%s", ep->ex_fsdir); 2206 oep = ex_search(&ep->ex_fs, oldexphead); 2207 if (oep != NULL) { 2208 /* 2209 * Check the mount paths are the same. 2210 * If not, return 1 so that the reload of the 2211 * exports will be done in bulk, the 2212 * passno == 0 way. 2213 */ 2214 LOGDEBUG("found old exp"); 2215 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2216 return (1); 2217 LOGDEBUG("same fsdir"); 2218 /* 2219 * Test to see if the entry is the same. 2220 * If not the same delete exports and 2221 * re-export. 2222 */ 2223 if (compare_export(ep, oep) != 0) { 2224 /* 2225 * Clear has_publicfh if if was set 2226 * in the old exports, but only if it 2227 * has not been set during processing of 2228 * the exports for this pass, as 2229 * indicated by has_set_publicfh. 2230 */ 2231 if (has_set_publicfh == 0 && 2232 (oep->ex_flag & EX_PUBLICFH) != 0) 2233 has_publicfh = 0; 2234 2235 /* Delete and re-export. */ 2236 if (statfs(ep->ex_fsdir, &fs) < 0) 2237 return (1); 2238 delete_export(iov, iovlen, &fs, errmsg); 2239 ret = do_export_mount(ep, &fs); 2240 if (ret != 0) 2241 return (ret); 2242 } 2243 oep->ex_flag |= EX_DONE; 2244 LOGDEBUG("exdone"); 2245 } else { 2246 LOGDEBUG("not found so export"); 2247 /* Not found, so do export. */ 2248 if (statfs(ep->ex_fsdir, &fs) < 0) 2249 return (1); 2250 ret = do_export_mount(ep, &fs); 2251 if (ret != 0) 2252 return (ret); 2253 } 2254 } 2255 2256 /* Delete exports not done. */ 2257 for (i = 0; i < exphashsize; i++) 2258 SLIST_FOREACH(oep, &oldexphead[i], entries) { 2259 if ((oep->ex_flag & EX_DONE) == 0) { 2260 LOGDEBUG("not done delete=%s", oep->ex_fsdir); 2261 if (statfs(oep->ex_fsdir, &ofs) >= 0 && 2262 fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) { 2263 LOGDEBUG("do delete"); 2264 /* 2265 * Clear has_publicfh if if was set 2266 * in the old exports, but only if it 2267 * has not been set during processing of 2268 * the exports for this pass, as 2269 * indicated by has_set_publicfh. 2270 */ 2271 if (has_set_publicfh == 0 && 2272 (oep->ex_flag & EX_PUBLICFH) != 0) 2273 has_publicfh = 0; 2274 2275 delete_export(iov, iovlen, &ofs, 2276 errmsg); 2277 } 2278 } 2279 } 2280 2281 /* Do the V4 root exports, as required. */ 2282 grp = NULL; 2283 if (v4root_ep != NULL) 2284 grp = v4root_ep->ex_grphead; 2285 v4root_phase = 2; 2286 while (v4root_ep != NULL && grp != NULL) { 2287 LOGDEBUG("v4root expath=%s", v4root_dirpath); 2288 ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon, 2289 v4root_dirpath, strlen(v4root_dirpath), &fs, 2290 grp->gr_numsecflavors, grp->gr_secflavors); 2291 if (ret != 0) { 2292 v4root_phase = 0; 2293 return (ret); 2294 } 2295 grp = grp->gr_next; 2296 } 2297 v4root_phase = 0; 2298 free_v4rootexp(); 2299 return (0); 2300 } 2301 2302 /* 2303 * Compare old and current exportlist entries for the fsid and return 0 2304 * if they are the same, 1 otherwise. 2305 */ 2306 static int 2307 compare_export(struct exportlist *ep, struct exportlist *oep) 2308 { 2309 struct grouplist *grp, *ogrp; 2310 2311 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2312 return (1); 2313 if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET)) 2314 return (1); 2315 if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) || 2316 (ep->ex_defdir == NULL && oep->ex_defdir != NULL)) 2317 return (1); 2318 if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) != 2319 (oep->ex_defdir->dp_flag & DP_DEFSET)) 2320 return (1); 2321 if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors != 2322 oep->ex_defnumsecflavors || ep->ex_defexflags != 2323 oep->ex_defexflags || compare_cred(&ep->ex_defanon, 2324 &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors, 2325 oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0)) 2326 return (1); 2327 2328 /* Now, check all the groups. */ 2329 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2330 ogrp->gr_flag = 0; 2331 for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) { 2332 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = 2333 ogrp->gr_next) 2334 if ((ogrp->gr_flag & GR_FND) == 0 && 2335 grp->gr_numsecflavors == ogrp->gr_numsecflavors && 2336 grp->gr_exflags == ogrp->gr_exflags && 2337 compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 && 2338 compare_secflavor(grp->gr_secflavors, 2339 ogrp->gr_secflavors, grp->gr_numsecflavors) == 0) 2340 break; 2341 if (ogrp != NULL) 2342 ogrp->gr_flag |= GR_FND; 2343 else 2344 return (1); 2345 } 2346 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2347 if ((ogrp->gr_flag & GR_FND) == 0) 2348 return (1); 2349 return (0); 2350 } 2351 2352 /* 2353 * This algorithm compares two arrays of "n" items. It returns 0 if they are 2354 * the "same" and 1 otherwise. Although suboptimal, it is always safe to 2355 * return 1, which makes compare_nmount_export() reload the exports entry. 2356 * "same" refers to having the same set of values in the two arrays. 2357 * The arrays are in no particular order and duplicates (multiple entries 2358 * in an array with the same value) is allowed. 2359 * The algorithm is inefficient, but the common case of identical arrays is 2360 * handled first and "n" is normally fairly small. 2361 * Since the two functions need the same algorithm but for arrays of 2362 * different types (gid_t vs int), this is done as a macro. 2363 */ 2364 #define COMPARE_ARRAYS(a1, a2, n) \ 2365 do { \ 2366 int fnd, fndarray[(n)], i, j; \ 2367 /* Handle common case of identical arrays. */ \ 2368 for (i = 0; i < (n); i++) \ 2369 if ((a1)[i] != (a2)[i]) \ 2370 break; \ 2371 if (i == (n)) \ 2372 return (0); \ 2373 for (i = 0; i < (n); i++) \ 2374 fndarray[i] = 0; \ 2375 for (i = 0; i < (n); i++) { \ 2376 fnd = 0; \ 2377 for (j = 0; j < (n); j++) { \ 2378 if ((a1)[i] == (a2)[j]) { \ 2379 fndarray[j] = 1; \ 2380 fnd = 1; \ 2381 } \ 2382 } \ 2383 if (fnd == 0) \ 2384 return (1); \ 2385 } \ 2386 for (i = 0; i < (n); i++) \ 2387 if (fndarray[i] == 0) \ 2388 return (1); \ 2389 return (0); \ 2390 } while (0) 2391 2392 /* 2393 * Compare two struct expcred's. Return 0 if the same and 1 otherwise. 2394 */ 2395 static int 2396 compare_cred(struct expcred *cr0, struct expcred *cr1) 2397 { 2398 2399 if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups) 2400 return (1); 2401 2402 COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups); 2403 } 2404 2405 /* 2406 * Compare two lists of security flavors. Return 0 if the same and 1 otherwise. 2407 */ 2408 static int 2409 compare_secflavor(int *sec1, int *sec2, int nsec) 2410 { 2411 2412 COMPARE_ARRAYS(sec1, sec2, nsec); 2413 } 2414 2415 /* 2416 * Delete an exports entry. 2417 */ 2418 static void 2419 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg) 2420 { 2421 struct xvfsconf vfc; 2422 2423 if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { 2424 syslog(LOG_ERR, "getvfsbyname() failed for %s", 2425 fsp->f_fstypename); 2426 return; 2427 } 2428 2429 /* 2430 * We do not need to delete "export" flag from 2431 * filesystems that do not have it set. 2432 */ 2433 if (!(fsp->f_flags & MNT_EXPORTED)) 2434 return; 2435 /* 2436 * Do not delete export for network filesystem by 2437 * passing "export" arg to nmount(). 2438 * It only makes sense to do this for local filesystems. 2439 */ 2440 if (vfc.vfc_flags & VFCF_NETWORK) 2441 return; 2442 2443 iov[1].iov_base = fsp->f_fstypename; 2444 iov[1].iov_len = strlen(fsp->f_fstypename) + 1; 2445 iov[3].iov_base = fsp->f_mntonname; 2446 iov[3].iov_len = strlen(fsp->f_mntonname) + 1; 2447 iov[5].iov_base = fsp->f_mntfromname; 2448 iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; 2449 errmsg[0] = '\0'; 2450 2451 /* 2452 * EXDEV is returned when path exists but is not a 2453 * mount point. May happens if raced with unmount. 2454 */ 2455 if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT && 2456 errno != ENOTSUP && errno != EXDEV) { 2457 syslog(LOG_ERR, 2458 "can't delete exports for %s: %m %s", 2459 fsp->f_mntonname, errmsg); 2460 } 2461 } 2462 2463 /* 2464 * Allocate an export list element 2465 */ 2466 static struct exportlist * 2467 get_exp(void) 2468 { 2469 struct exportlist *ep; 2470 2471 ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); 2472 if (ep == (struct exportlist *)NULL) 2473 out_of_mem(); 2474 return (ep); 2475 } 2476 2477 /* 2478 * Allocate a group list element 2479 */ 2480 static struct grouplist * 2481 get_grp(void) 2482 { 2483 struct grouplist *gp; 2484 2485 gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); 2486 if (gp == (struct grouplist *)NULL) 2487 out_of_mem(); 2488 return (gp); 2489 } 2490 2491 /* 2492 * Clean up upon an error in get_exportlist(). 2493 */ 2494 static void 2495 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) 2496 { 2497 struct grouplist *tgrp; 2498 2499 if (!(opt_flags & OP_QUIET)) { 2500 if (reason != NULL) 2501 syslog(LOG_ERR, "bad exports list line '%s': %s", line, 2502 reason); 2503 else 2504 syslog(LOG_ERR, "bad exports list line '%s'", line); 2505 } 2506 if (ep && (ep->ex_flag & EX_LINKED) == 0) 2507 free_exp(ep); 2508 while (grp) { 2509 tgrp = grp; 2510 grp = grp->gr_next; 2511 free_grp(tgrp); 2512 } 2513 } 2514 2515 /* 2516 * Search the export list for a matching fs. 2517 */ 2518 static struct exportlist * 2519 ex_search(fsid_t *fsid, struct exportlisthead *exhp) 2520 { 2521 struct exportlist *ep; 2522 uint32_t i; 2523 2524 i = EXPHASH(fsid); 2525 SLIST_FOREACH(ep, &exhp[i], entries) { 2526 if (fsidcmp(&ep->ex_fs, fsid) == 0) 2527 return (ep); 2528 } 2529 2530 return (ep); 2531 } 2532 2533 /* 2534 * Add a directory path to the list. 2535 */ 2536 static char * 2537 add_expdir(struct dirlist **dpp, char *cp, int len) 2538 { 2539 struct dirlist *dp; 2540 2541 dp = malloc(sizeof (struct dirlist)); 2542 if (dp == (struct dirlist *)NULL) 2543 out_of_mem(); 2544 dp->dp_left = *dpp; 2545 dp->dp_right = (struct dirlist *)NULL; 2546 dp->dp_flag = 0; 2547 dp->dp_hosts = (struct hostlist *)NULL; 2548 dp->dp_dirp = strndup(cp, len); 2549 if (dp->dp_dirp == NULL) 2550 out_of_mem(); 2551 *dpp = dp; 2552 return (dp->dp_dirp); 2553 } 2554 2555 /* 2556 * Hang the dir list element off the dirpath binary tree as required 2557 * and update the entry for host. 2558 */ 2559 static void 2560 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 2561 int flags, struct expcred *anoncrp, uint64_t exflags) 2562 { 2563 struct hostlist *hp; 2564 struct dirlist *dp2; 2565 2566 if (flags & OP_ALLDIRS) { 2567 if (ep->ex_defdir) 2568 free((caddr_t)dp); 2569 else 2570 ep->ex_defdir = dp; 2571 if (grp == (struct grouplist *)NULL) { 2572 ep->ex_flag |= EX_DEFSET; 2573 ep->ex_defdir->dp_flag |= DP_DEFSET; 2574 /* Save the default security flavors list. */ 2575 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2576 if (ep->ex_numsecflavors > 0) 2577 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2578 sizeof(ep->ex_secflavors)); 2579 cp_cred(&ep->ex_defanon, anoncrp); 2580 ep->ex_defexflags = exflags; 2581 } else while (grp) { 2582 hp = get_ht(); 2583 hp->ht_grp = grp; 2584 hp->ht_next = ep->ex_defdir->dp_hosts; 2585 ep->ex_defdir->dp_hosts = hp; 2586 /* Save the security flavors list for this host set. */ 2587 grp->gr_numsecflavors = ep->ex_numsecflavors; 2588 if (ep->ex_numsecflavors > 0) 2589 memcpy(grp->gr_secflavors, ep->ex_secflavors, 2590 sizeof(ep->ex_secflavors)); 2591 grp = grp->gr_next; 2592 } 2593 } else { 2594 2595 /* 2596 * Loop through the directories adding them to the tree. 2597 */ 2598 while (dp) { 2599 dp2 = dp->dp_left; 2600 add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp, 2601 exflags); 2602 dp = dp2; 2603 } 2604 } 2605 } 2606 2607 /* 2608 * Traverse the binary tree either updating a node that is already there 2609 * for the new directory or adding the new node. 2610 */ 2611 static void 2612 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 2613 int flags, struct exportlist *ep, struct expcred *anoncrp, 2614 uint64_t exflags) 2615 { 2616 struct dirlist *dp; 2617 struct hostlist *hp; 2618 int cmp; 2619 2620 dp = *dpp; 2621 if (dp) { 2622 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 2623 if (cmp > 0) { 2624 add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp, 2625 exflags); 2626 return; 2627 } else if (cmp < 0) { 2628 add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp, 2629 exflags); 2630 return; 2631 } else 2632 free((caddr_t)newdp); 2633 } else { 2634 dp = newdp; 2635 dp->dp_left = (struct dirlist *)NULL; 2636 *dpp = dp; 2637 } 2638 if (grp) { 2639 2640 /* 2641 * Hang all of the host(s) off of the directory point. 2642 */ 2643 do { 2644 hp = get_ht(); 2645 hp->ht_grp = grp; 2646 hp->ht_next = dp->dp_hosts; 2647 dp->dp_hosts = hp; 2648 /* Save the security flavors list for this host set. */ 2649 grp->gr_numsecflavors = ep->ex_numsecflavors; 2650 if (ep->ex_numsecflavors > 0) 2651 memcpy(grp->gr_secflavors, ep->ex_secflavors, 2652 sizeof(ep->ex_secflavors)); 2653 grp = grp->gr_next; 2654 } while (grp); 2655 } else { 2656 ep->ex_flag |= EX_DEFSET; 2657 dp->dp_flag |= DP_DEFSET; 2658 /* Save the default security flavors list. */ 2659 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2660 if (ep->ex_numsecflavors > 0) 2661 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2662 sizeof(ep->ex_secflavors)); 2663 cp_cred(&ep->ex_defanon, anoncrp); 2664 ep->ex_defexflags = exflags; 2665 } 2666 } 2667 2668 /* 2669 * Search for a dirpath on the export point. 2670 */ 2671 static struct dirlist * 2672 dirp_search(struct dirlist *dp, char *dirp) 2673 { 2674 int cmp; 2675 2676 if (dp) { 2677 cmp = strcmp(dp->dp_dirp, dirp); 2678 if (cmp > 0) 2679 return (dirp_search(dp->dp_left, dirp)); 2680 else if (cmp < 0) 2681 return (dirp_search(dp->dp_right, dirp)); 2682 else 2683 return (dp); 2684 } 2685 return (dp); 2686 } 2687 2688 /* 2689 * Scan for a host match in a directory tree. 2690 */ 2691 static int 2692 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, 2693 int *hostsetp, int *numsecflavors, int **secflavorsp) 2694 { 2695 struct hostlist *hp; 2696 struct grouplist *grp; 2697 struct addrinfo *ai; 2698 2699 if (dp) { 2700 if (dp->dp_flag & DP_DEFSET) 2701 *defsetp = dp->dp_flag; 2702 hp = dp->dp_hosts; 2703 while (hp) { 2704 grp = hp->ht_grp; 2705 switch (grp->gr_type) { 2706 case GT_HOST: 2707 ai = grp->gr_ptr.gt_addrinfo; 2708 for (; ai; ai = ai->ai_next) { 2709 if (!sacmp(ai->ai_addr, saddr, NULL)) { 2710 *hostsetp = 2711 (hp->ht_flag | DP_HOSTSET); 2712 if (numsecflavors != NULL) { 2713 *numsecflavors = 2714 grp->gr_numsecflavors; 2715 *secflavorsp = 2716 grp->gr_secflavors; 2717 } 2718 return (1); 2719 } 2720 } 2721 break; 2722 case GT_NET: 2723 if (!sacmp(saddr, (struct sockaddr *) 2724 &grp->gr_ptr.gt_net.nt_net, 2725 (struct sockaddr *) 2726 &grp->gr_ptr.gt_net.nt_mask)) { 2727 *hostsetp = (hp->ht_flag | DP_HOSTSET); 2728 if (numsecflavors != NULL) { 2729 *numsecflavors = 2730 grp->gr_numsecflavors; 2731 *secflavorsp = 2732 grp->gr_secflavors; 2733 } 2734 return (1); 2735 } 2736 break; 2737 } 2738 hp = hp->ht_next; 2739 } 2740 } 2741 return (0); 2742 } 2743 2744 /* 2745 * Scan tree for a host that matches the address. 2746 */ 2747 static int 2748 scan_tree(struct dirlist *dp, struct sockaddr *saddr) 2749 { 2750 int defset, hostset; 2751 2752 if (dp) { 2753 if (scan_tree(dp->dp_left, saddr)) 2754 return (1); 2755 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) 2756 return (1); 2757 if (scan_tree(dp->dp_right, saddr)) 2758 return (1); 2759 } 2760 return (0); 2761 } 2762 2763 /* 2764 * Traverse the dirlist tree and free it up. 2765 */ 2766 static void 2767 free_dir(struct dirlist *dp) 2768 { 2769 2770 if (dp) { 2771 free_dir(dp->dp_left); 2772 free_dir(dp->dp_right); 2773 free_host(dp->dp_hosts); 2774 free(dp->dp_dirp); 2775 free(dp); 2776 } 2777 } 2778 2779 /* 2780 * Parse a colon separated list of security flavors 2781 */ 2782 static int 2783 parsesec(char *seclist, struct exportlist *ep) 2784 { 2785 char *cp, savedc; 2786 int flavor; 2787 2788 ep->ex_numsecflavors = 0; 2789 for (;;) { 2790 cp = strchr(seclist, ':'); 2791 if (cp) { 2792 savedc = *cp; 2793 *cp = '\0'; 2794 } 2795 2796 if (!strcmp(seclist, "sys")) 2797 flavor = AUTH_SYS; 2798 else if (!strcmp(seclist, "krb5")) 2799 flavor = RPCSEC_GSS_KRB5; 2800 else if (!strcmp(seclist, "krb5i")) 2801 flavor = RPCSEC_GSS_KRB5I; 2802 else if (!strcmp(seclist, "krb5p")) 2803 flavor = RPCSEC_GSS_KRB5P; 2804 else { 2805 if (cp) 2806 *cp = savedc; 2807 syslog(LOG_ERR, "bad sec flavor: %s", seclist); 2808 return (1); 2809 } 2810 if (ep->ex_numsecflavors == MAXSECFLAVORS) { 2811 if (cp) 2812 *cp = savedc; 2813 syslog(LOG_ERR, "too many sec flavors: %s", seclist); 2814 return (1); 2815 } 2816 ep->ex_secflavors[ep->ex_numsecflavors] = flavor; 2817 ep->ex_numsecflavors++; 2818 if (cp) { 2819 *cp = savedc; 2820 seclist = cp + 1; 2821 } else { 2822 break; 2823 } 2824 } 2825 return (0); 2826 } 2827 2828 /* 2829 * Parse the option string and update fields. 2830 * Option arguments may either be -<option>=<value> or 2831 * -<option> <value> 2832 */ 2833 static int 2834 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp, 2835 int *has_hostp, uint64_t *exflagsp, struct expcred *cr, char *unvis_dir) 2836 { 2837 char *cpoptarg, *cpoptend; 2838 char *cp, *endcp, *cpopt, savedc, savedc2; 2839 int allflag, usedarg, fnd_equal; 2840 2841 savedc2 = '\0'; 2842 cpopt = *cpp; 2843 cpopt++; 2844 cp = *endcpp; 2845 savedc = *cp; 2846 *cp = '\0'; 2847 while (cpopt && *cpopt) { 2848 allflag = 1; 2849 usedarg = -2; 2850 fnd_equal = 0; 2851 if ((cpoptend = strchr(cpopt, ','))) { 2852 *cpoptend++ = '\0'; 2853 if ((cpoptarg = strchr(cpopt, '='))) { 2854 *cpoptarg++ = '\0'; 2855 fnd_equal = 1; 2856 } 2857 } else { 2858 if ((cpoptarg = strchr(cpopt, '='))) { 2859 *cpoptarg++ = '\0'; 2860 fnd_equal = 1; 2861 } else { 2862 *cp = savedc; 2863 nextfield(&cp, &endcp); 2864 **endcpp = '\0'; 2865 if (endcp > cp && *cp != '-') { 2866 cpoptarg = cp; 2867 savedc2 = *endcp; 2868 *endcp = '\0'; 2869 usedarg = 0; 2870 } 2871 } 2872 } 2873 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 2874 if (fnd_equal == 1) { 2875 syslog(LOG_ERR, "= after op: %s", cpopt); 2876 return (1); 2877 } 2878 *exflagsp |= MNT_EXRDONLY; 2879 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 2880 !(allflag = strcmp(cpopt, "mapall")) || 2881 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 2882 usedarg++; 2883 parsecred(cpoptarg, cr); 2884 if (allflag == 0) { 2885 *exflagsp |= MNT_EXPORTANON; 2886 opt_flags |= OP_MAPALL; 2887 } else 2888 opt_flags |= OP_MAPROOT; 2889 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 2890 !strcmp(cpopt, "m"))) { 2891 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 2892 syslog(LOG_ERR, "bad mask: %s", cpoptarg); 2893 return (1); 2894 } 2895 usedarg++; 2896 opt_flags |= OP_MASK; 2897 } else if (cpoptarg && (!strcmp(cpopt, "network") || 2898 !strcmp(cpopt, "n"))) { 2899 if (strchr(cpoptarg, '/') != NULL) { 2900 if (debug) 2901 fprintf(stderr, "setting OP_MASKLEN\n"); 2902 opt_flags |= OP_MASKLEN; 2903 } 2904 if (grp->gr_type != GT_NULL) { 2905 syslog(LOG_ERR, "network/host conflict"); 2906 return (1); 2907 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 2908 syslog(LOG_ERR, "bad net: %s", cpoptarg); 2909 return (1); 2910 } 2911 grp->gr_type = GT_NET; 2912 *has_hostp = 1; 2913 usedarg++; 2914 opt_flags |= OP_NET; 2915 } else if (!strcmp(cpopt, "alldirs")) { 2916 if (fnd_equal == 1) { 2917 syslog(LOG_ERR, "= after op: %s", cpopt); 2918 return (1); 2919 } 2920 if ((opt_flags & OP_NOTROOT) != 0) { 2921 syslog(LOG_ERR, "%s: path %s not mount point", 2922 cpopt, unvis_dir); 2923 if (alldirs_fail != 0) 2924 return (1); 2925 } 2926 opt_flags |= OP_ALLDIRS; 2927 } else if (!strcmp(cpopt, "public")) { 2928 if (fnd_equal == 1) { 2929 syslog(LOG_ERR, "= after op: %s", cpopt); 2930 return (1); 2931 } 2932 *exflagsp |= MNT_EXPUBLIC; 2933 } else if (!strcmp(cpopt, "webnfs")) { 2934 if (fnd_equal == 1) { 2935 syslog(LOG_ERR, "= after op: %s", cpopt); 2936 return (1); 2937 } 2938 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 2939 opt_flags |= OP_MAPALL; 2940 } else if (cpoptarg && !strcmp(cpopt, "index")) { 2941 ep->ex_indexfile = strdup(cpoptarg); 2942 } else if (!strcmp(cpopt, "quiet")) { 2943 if (fnd_equal == 1) { 2944 syslog(LOG_ERR, "= after op: %s", cpopt); 2945 return (1); 2946 } 2947 opt_flags |= OP_QUIET; 2948 } else if (cpoptarg && !strcmp(cpopt, "sec")) { 2949 if (parsesec(cpoptarg, ep)) 2950 return (1); 2951 opt_flags |= OP_SEC; 2952 usedarg++; 2953 } else if (!strcmp(cpopt, "tls")) { 2954 if (fnd_equal == 1) { 2955 syslog(LOG_ERR, "= after op: %s", cpopt); 2956 return (1); 2957 } 2958 *exflagsp |= MNT_EXTLS; 2959 } else if (!strcmp(cpopt, "tlscert")) { 2960 if (fnd_equal == 1) { 2961 syslog(LOG_ERR, "= after op: %s", cpopt); 2962 return (1); 2963 } 2964 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT); 2965 } else if (!strcmp(cpopt, "tlscertuser")) { 2966 if (fnd_equal == 1) { 2967 syslog(LOG_ERR, "= after op: %s", cpopt); 2968 return (1); 2969 } 2970 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT | 2971 MNT_EXTLSCERTUSER); 2972 } else { 2973 syslog(LOG_ERR, "bad opt %s", cpopt); 2974 return (1); 2975 } 2976 if (usedarg >= 0) { 2977 *endcp = savedc2; 2978 **endcpp = savedc; 2979 if (usedarg > 0) { 2980 *cpp = cp; 2981 *endcpp = endcp; 2982 } 2983 return (0); 2984 } 2985 cpopt = cpoptend; 2986 } 2987 **endcpp = savedc; 2988 return (0); 2989 } 2990 2991 /* 2992 * Translate a character string to the corresponding list of network 2993 * addresses for a hostname. 2994 */ 2995 static int 2996 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp) 2997 { 2998 struct grouplist *checkgrp; 2999 struct addrinfo *ai, *tai, hints; 3000 int ecode; 3001 char host[NI_MAXHOST]; 3002 3003 if (grp->gr_type != GT_NULL) { 3004 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 3005 return (1); 3006 } 3007 memset(&hints, 0, sizeof hints); 3008 hints.ai_flags = AI_CANONNAME; 3009 hints.ai_protocol = IPPROTO_UDP; 3010 ecode = getaddrinfo(cp, NULL, &hints, &ai); 3011 if (ecode != 0) { 3012 syslog(LOG_ERR,"can't get address info for host %s", cp); 3013 return 1; 3014 } 3015 grp->gr_ptr.gt_addrinfo = ai; 3016 while (ai != NULL) { 3017 if (ai->ai_canonname == NULL) { 3018 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 3019 sizeof host, NULL, 0, NI_NUMERICHOST) != 0) 3020 strlcpy(host, "?", sizeof(host)); 3021 ai->ai_canonname = strdup(host); 3022 ai->ai_flags |= AI_CANONNAME; 3023 } 3024 if (debug) 3025 fprintf(stderr, "got host %s\n", ai->ai_canonname); 3026 /* 3027 * Sanity check: make sure we don't already have an entry 3028 * for this host in the grouplist. 3029 */ 3030 for (checkgrp = tgrp; checkgrp != NULL; 3031 checkgrp = checkgrp->gr_next) { 3032 if (checkgrp->gr_type != GT_HOST) 3033 continue; 3034 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 3035 tai = tai->ai_next) { 3036 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 3037 continue; 3038 if (debug) 3039 fprintf(stderr, 3040 "ignoring duplicate host %s\n", 3041 ai->ai_canonname); 3042 grp->gr_type = GT_IGNORE; 3043 return (0); 3044 } 3045 } 3046 ai = ai->ai_next; 3047 } 3048 grp->gr_type = GT_HOST; 3049 return (0); 3050 } 3051 3052 /* 3053 * Free up an exports list component 3054 */ 3055 static void 3056 free_exp(struct exportlist *ep) 3057 { 3058 struct grouplist *grp, *tgrp; 3059 3060 if (ep->ex_defdir) { 3061 free_host(ep->ex_defdir->dp_hosts); 3062 free((caddr_t)ep->ex_defdir); 3063 } 3064 if (ep->ex_fsdir) 3065 free(ep->ex_fsdir); 3066 if (ep->ex_indexfile) 3067 free(ep->ex_indexfile); 3068 free_dir(ep->ex_dirl); 3069 grp = ep->ex_grphead; 3070 while (grp) { 3071 tgrp = grp; 3072 grp = grp->gr_next; 3073 free_grp(tgrp); 3074 } 3075 if (ep->ex_defanon.cr_groups != ep->ex_defanon.cr_smallgrps) 3076 free(ep->ex_defanon.cr_groups); 3077 free((caddr_t)ep); 3078 } 3079 3080 /* 3081 * Free up the v4root exports. 3082 */ 3083 static void 3084 free_v4rootexp(void) 3085 { 3086 3087 if (v4root_ep != NULL) { 3088 free_exp(v4root_ep); 3089 v4root_ep = NULL; 3090 } 3091 } 3092 3093 /* 3094 * Free hosts. 3095 */ 3096 static void 3097 free_host(struct hostlist *hp) 3098 { 3099 struct hostlist *hp2; 3100 3101 while (hp) { 3102 hp2 = hp; 3103 hp = hp->ht_next; 3104 free((caddr_t)hp2); 3105 } 3106 } 3107 3108 static struct hostlist * 3109 get_ht(void) 3110 { 3111 struct hostlist *hp; 3112 3113 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 3114 if (hp == (struct hostlist *)NULL) 3115 out_of_mem(); 3116 hp->ht_next = (struct hostlist *)NULL; 3117 hp->ht_flag = 0; 3118 return (hp); 3119 } 3120 3121 /* 3122 * Out of memory, fatal 3123 */ 3124 static void 3125 out_of_mem(void) 3126 { 3127 3128 syslog(LOG_ERR, "out of memory"); 3129 exit(2); 3130 } 3131 3132 /* 3133 * Call do_mount() from the struct exportlist, for each case needed. 3134 */ 3135 static int 3136 do_export_mount(struct exportlist *ep, struct statfs *fsp) 3137 { 3138 struct grouplist *grp, defgrp; 3139 int ret; 3140 size_t dirlen; 3141 3142 LOGDEBUG("do_export_mount=%s", ep->ex_fsdir); 3143 dirlen = strlen(ep->ex_fsdir); 3144 if ((ep->ex_flag & EX_DEFSET) != 0) { 3145 defgrp.gr_type = GT_DEFAULT; 3146 defgrp.gr_next = NULL; 3147 /* We have an entry for all other hosts/nets. */ 3148 LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags); 3149 ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon, 3150 ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors, 3151 ep->ex_defsecflavors); 3152 if (ret != 0) 3153 return (ret); 3154 } 3155 3156 /* Do a mount for each group. */ 3157 grp = ep->ex_grphead; 3158 while (grp != NULL) { 3159 LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx", 3160 grp->gr_type, (uintmax_t)grp->gr_exflags); 3161 ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon, 3162 ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors, 3163 grp->gr_secflavors); 3164 if (ret != 0) 3165 return (ret); 3166 grp = grp->gr_next; 3167 } 3168 return (0); 3169 } 3170 3171 /* 3172 * Do the nmount() syscall with the update flag to push the export info into 3173 * the kernel. 3174 */ 3175 static int 3176 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags, 3177 struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb, 3178 int numsecflavors, int *secflavors) 3179 { 3180 struct statfs fsb1; 3181 struct addrinfo *ai; 3182 struct export_args *eap; 3183 char errmsg[255]; 3184 char *cp; 3185 int done; 3186 char savedc; 3187 struct iovec *iov; 3188 int i, iovlen; 3189 int ret; 3190 struct nfsex_args nfsea; 3191 3192 eap = &nfsea.export; 3193 3194 cp = NULL; 3195 savedc = '\0'; 3196 iov = NULL; 3197 iovlen = 0; 3198 ret = 0; 3199 3200 bzero(eap, sizeof (struct export_args)); 3201 bzero(errmsg, sizeof(errmsg)); 3202 eap->ex_flags = exflags; 3203 eap->ex_uid = anoncrp->cr_uid; 3204 eap->ex_ngroups = anoncrp->cr_ngroups; 3205 /* 3206 * Use the memory pointed to by 'anoncrp', as it outlives 'eap' which is 3207 * local to this function. 3208 */ 3209 eap->ex_groups = anoncrp->cr_groups; 3210 LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags); 3211 eap->ex_indexfile = ep->ex_indexfile; 3212 if (grp->gr_type == GT_HOST) 3213 ai = grp->gr_ptr.gt_addrinfo; 3214 else 3215 ai = NULL; 3216 eap->ex_numsecflavors = numsecflavors; 3217 LOGDEBUG("do_mount numsec=%d", numsecflavors); 3218 for (i = 0; i < eap->ex_numsecflavors; i++) 3219 eap->ex_secflavors[i] = secflavors[i]; 3220 if (eap->ex_numsecflavors == 0) { 3221 eap->ex_numsecflavors = 1; 3222 eap->ex_secflavors[0] = AUTH_SYS; 3223 } 3224 done = FALSE; 3225 3226 if (v4root_phase == 0) { 3227 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 3228 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 3229 build_iovec(&iov, &iovlen, "from", NULL, 0); 3230 build_iovec(&iov, &iovlen, "update", NULL, 0); 3231 build_iovec(&iov, &iovlen, "export", eap, 3232 sizeof (struct export_args)); 3233 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 3234 } 3235 3236 while (!done) { 3237 switch (grp->gr_type) { 3238 case GT_HOST: 3239 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 3240 goto skip; 3241 eap->ex_addr = ai->ai_addr; 3242 eap->ex_addrlen = ai->ai_addrlen; 3243 eap->ex_masklen = 0; 3244 break; 3245 case GT_NET: 3246 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 3247 have_v6 == 0) 3248 goto skip; 3249 eap->ex_addr = 3250 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 3251 eap->ex_addrlen = 3252 ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len; 3253 eap->ex_mask = 3254 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 3255 eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len; 3256 break; 3257 case GT_DEFAULT: 3258 eap->ex_addr = NULL; 3259 eap->ex_addrlen = 0; 3260 eap->ex_mask = NULL; 3261 eap->ex_masklen = 0; 3262 break; 3263 case GT_IGNORE: 3264 ret = 0; 3265 goto error_exit; 3266 break; 3267 default: 3268 syslog(LOG_ERR, "bad grouptype"); 3269 if (cp) 3270 *cp = savedc; 3271 ret = 1; 3272 goto error_exit; 3273 } 3274 3275 /* 3276 * For V4:, use the nfssvc() syscall, instead of mount(). 3277 */ 3278 if (v4root_phase == 2) { 3279 nfsea.fspec = v4root_dirpath; 3280 if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, 3281 (caddr_t)&nfsea) < 0) { 3282 syslog(LOG_ERR, "Exporting V4: failed"); 3283 ret = 2; 3284 goto error_exit; 3285 } 3286 } else { 3287 /* 3288 * XXX: 3289 * Maybe I should just use the fsb->f_mntonname path 3290 * instead of looping back up the dirp to the mount 3291 * point?? 3292 * Also, needs to know how to export all types of local 3293 * exportable filesystems and not just "ufs". 3294 */ 3295 iov[1].iov_base = fsb->f_fstypename; /* "fstype" */ 3296 iov[1].iov_len = strlen(fsb->f_fstypename) + 1; 3297 iov[3].iov_base = fsb->f_mntonname; /* "fspath" */ 3298 iov[3].iov_len = strlen(fsb->f_mntonname) + 1; 3299 iov[5].iov_base = fsb->f_mntfromname; /* "from" */ 3300 iov[5].iov_len = strlen(fsb->f_mntfromname) + 1; 3301 errmsg[0] = '\0'; 3302 3303 while (nmount(iov, iovlen, fsb->f_flags) < 0) { 3304 if (cp) 3305 *cp-- = savedc; 3306 else 3307 cp = dirp + dirplen - 1; 3308 if (opt_flags & OP_QUIET) { 3309 ret = 1; 3310 goto error_exit; 3311 } 3312 if (errno == EPERM) { 3313 if (debug) 3314 warnx("can't change attributes for %s: %s", 3315 dirp, errmsg); 3316 syslog(LOG_ERR, 3317 "can't change attributes for %s: %s", 3318 dirp, errmsg); 3319 ret = 1; 3320 goto error_exit; 3321 } 3322 if ((opt_flags & OP_ALLDIRS) && 3323 alldirs_fail != 0) { 3324 if (errno == EINVAL) 3325 syslog(LOG_ERR, 3326 "-alldirs requested but %s is not a filesystem mountpoint", 3327 dirp); 3328 else 3329 syslog(LOG_ERR, 3330 "could not remount %s: %m", 3331 dirp); 3332 ret = 1; 3333 goto error_exit; 3334 } 3335 /* back up over the last component */ 3336 while (cp > dirp && *cp == '/') 3337 cp--; 3338 while (cp > dirp && *(cp - 1) != '/') 3339 cp--; 3340 if (cp == dirp) { 3341 if (debug) 3342 warnx("mnt unsucc"); 3343 syslog(LOG_ERR, "can't export %s %s", 3344 dirp, errmsg); 3345 ret = 1; 3346 goto error_exit; 3347 } 3348 savedc = *cp; 3349 *cp = '\0'; 3350 /* 3351 * Check that we're still on the same 3352 * filesystem. 3353 */ 3354 if (statfs(dirp, &fsb1) != 0 || 3355 fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) { 3356 *cp = savedc; 3357 syslog(LOG_ERR, 3358 "can't export %s %s", dirp, 3359 errmsg); 3360 ret = 1; 3361 goto error_exit; 3362 } 3363 } 3364 } 3365 3366 /* 3367 * For the experimental server: 3368 * If this is the public directory, get the file handle 3369 * and load it into the kernel via the nfssvc() syscall. 3370 */ 3371 if ((exflags & MNT_EXPUBLIC) != 0) { 3372 fhandle_t fh; 3373 char *public_name; 3374 3375 if (eap->ex_indexfile != NULL) 3376 public_name = eap->ex_indexfile; 3377 else 3378 public_name = dirp; 3379 if (getfh(public_name, &fh) < 0) 3380 syslog(LOG_ERR, 3381 "Can't get public fh for %s", public_name); 3382 else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) 3383 syslog(LOG_ERR, 3384 "Can't set public fh for %s", public_name); 3385 else { 3386 has_publicfh = 1; 3387 has_set_publicfh = 1; 3388 ep->ex_flag |= EX_PUBLICFH; 3389 } 3390 } 3391 skip: 3392 if (ai != NULL) 3393 ai = ai->ai_next; 3394 if (ai == NULL) 3395 done = TRUE; 3396 } 3397 if (cp) 3398 *cp = savedc; 3399 error_exit: 3400 free_iovec(&iov, &iovlen); 3401 return (ret); 3402 } 3403 3404 /* 3405 * Translate a net address. 3406 * 3407 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 3408 */ 3409 static int 3410 get_net(char *cp, struct netmsk *net, int maskflg) 3411 { 3412 struct netent *np = NULL; 3413 char *name, *p, *prefp; 3414 struct sockaddr_in sin; 3415 struct sockaddr *sa = NULL; 3416 struct addrinfo hints, *ai = NULL; 3417 char netname[NI_MAXHOST]; 3418 long preflen; 3419 3420 p = prefp = NULL; 3421 if ((opt_flags & OP_MASKLEN) && !maskflg) { 3422 p = strchr(cp, '/'); 3423 *p = '\0'; 3424 prefp = p + 1; 3425 } 3426 3427 /* 3428 * Check for a numeric address first. We wish to avoid 3429 * possible DNS lookups in getnetbyname(). 3430 */ 3431 if (isxdigit(*cp) || *cp == ':') { 3432 memset(&hints, 0, sizeof hints); 3433 /* Ensure the mask and the network have the same family. */ 3434 if (maskflg && (opt_flags & OP_NET)) 3435 hints.ai_family = net->nt_net.ss_family; 3436 else if (!maskflg && (opt_flags & OP_HAVEMASK)) 3437 hints.ai_family = net->nt_mask.ss_family; 3438 else 3439 hints.ai_family = AF_UNSPEC; 3440 hints.ai_flags = AI_NUMERICHOST; 3441 if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 3442 sa = ai->ai_addr; 3443 if (sa != NULL && ai->ai_family == AF_INET) { 3444 /* 3445 * The address in `cp' is really a network address, so 3446 * use inet_network() to re-interpret this correctly. 3447 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 3448 */ 3449 bzero(&sin, sizeof sin); 3450 sin.sin_family = AF_INET; 3451 sin.sin_len = sizeof sin; 3452 sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 3453 if (debug) 3454 fprintf(stderr, "get_net: v4 addr %s\n", 3455 inet_ntoa(sin.sin_addr)); 3456 sa = (struct sockaddr *)&sin; 3457 } 3458 } 3459 if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 3460 bzero(&sin, sizeof sin); 3461 sin.sin_family = AF_INET; 3462 sin.sin_len = sizeof sin; 3463 sin.sin_addr = inet_makeaddr(np->n_net, 0); 3464 sa = (struct sockaddr *)&sin; 3465 } 3466 if (sa == NULL) 3467 goto fail; 3468 3469 if (maskflg) { 3470 /* The specified sockaddr is a mask. */ 3471 if (checkmask(sa) != 0) 3472 goto fail; 3473 bcopy(sa, &net->nt_mask, sa->sa_len); 3474 opt_flags |= OP_HAVEMASK; 3475 opt_flags &= ~OP_CLASSMASK; 3476 } else { 3477 /* The specified sockaddr is a network address. */ 3478 bcopy(sa, &net->nt_net, sa->sa_len); 3479 3480 /* Get a network name for the export list. */ 3481 if (np) { 3482 name = np->n_name; 3483 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 3484 NULL, 0, NI_NUMERICHOST) == 0) { 3485 name = netname; 3486 } else { 3487 goto fail; 3488 } 3489 if ((net->nt_name = strdup(name)) == NULL) 3490 out_of_mem(); 3491 3492 /* 3493 * Extract a mask from either a "/<masklen>" suffix, or 3494 * from the class of an IPv4 address. 3495 */ 3496 if (opt_flags & OP_MASKLEN) { 3497 preflen = strtol(prefp, NULL, 10); 3498 if (preflen < 0L || preflen == LONG_MAX) 3499 goto fail; 3500 bcopy(sa, &net->nt_mask, sa->sa_len); 3501 if (makemask(&net->nt_mask, (int)preflen) != 0) 3502 goto fail; 3503 opt_flags |= OP_HAVEMASK; 3504 *p = '/'; 3505 } else if (sa->sa_family == AF_INET && 3506 (opt_flags & OP_MASK) == 0) { 3507 in_addr_t addr; 3508 3509 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 3510 if (IN_CLASSA(addr)) 3511 preflen = 8; 3512 else if (IN_CLASSB(addr)) 3513 preflen = 16; 3514 else if (IN_CLASSC(addr)) 3515 preflen = 24; 3516 else if (IN_CLASSD(addr)) /* XXX Multicast??? */ 3517 preflen = 28; 3518 else 3519 preflen = 32; /* XXX */ 3520 3521 bcopy(sa, &net->nt_mask, sa->sa_len); 3522 makemask(&net->nt_mask, (int)preflen); 3523 opt_flags |= OP_HAVEMASK | OP_CLASSMASK; 3524 } 3525 } 3526 3527 if (ai) 3528 freeaddrinfo(ai); 3529 return 0; 3530 3531 fail: 3532 if (ai) 3533 freeaddrinfo(ai); 3534 return 1; 3535 } 3536 3537 /* 3538 * Parse out the next white space separated field 3539 */ 3540 static void 3541 nextfield(char **cp, char **endcp) 3542 { 3543 char *p; 3544 char quot = 0; 3545 3546 p = *cp; 3547 while (*p == ' ' || *p == '\t') 3548 p++; 3549 *cp = p; 3550 while (*p != '\0') { 3551 if (quot) { 3552 if (*p == quot) 3553 quot = 0; 3554 } else { 3555 if (*p == '\\' && *(p + 1) != '\0') 3556 p++; 3557 else if (*p == '\'' || *p == '"') 3558 quot = *p; 3559 else if (*p == ' ' || *p == '\t') 3560 break; 3561 } 3562 p++; 3563 }; 3564 *endcp = p; 3565 } 3566 3567 /* 3568 * Get an exports file line. Skip over blank lines and handle line 3569 * continuations. 3570 */ 3571 static int 3572 get_line(void) 3573 { 3574 char *p, *cp; 3575 size_t len; 3576 int totlen, cont_line; 3577 3578 /* 3579 * Loop around ignoring blank lines and getting all continuation lines. 3580 */ 3581 p = line; 3582 totlen = 0; 3583 do { 3584 if ((p = fgetln(exp_file, &len)) == NULL) 3585 return (0); 3586 cp = p + len - 1; 3587 cont_line = 0; 3588 while (cp >= p && 3589 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 3590 if (*cp == '\\') 3591 cont_line = 1; 3592 cp--; 3593 len--; 3594 } 3595 if (cont_line) { 3596 *++cp = ' '; 3597 len++; 3598 } 3599 if (linesize < len + totlen + 1) { 3600 linesize = len + totlen + 1; 3601 line = realloc(line, linesize); 3602 if (line == NULL) 3603 out_of_mem(); 3604 } 3605 memcpy(line + totlen, p, len); 3606 totlen += len; 3607 line[totlen] = '\0'; 3608 } while (totlen == 0 || cont_line); 3609 return (1); 3610 } 3611 3612 /* 3613 * Parse a description of a credential. 3614 */ 3615 static void 3616 parsecred(char *names, struct expcred *cr) 3617 { 3618 char *name; 3619 struct passwd *pw; 3620 unsigned long name_ul; 3621 char *end = NULL; 3622 3623 assert(cr->cr_groups == tmp_groups); 3624 3625 /* 3626 * Parse the user and if possible get its password table entry. 3627 * 'cr_uid' is filled when exiting this block. 3628 */ 3629 name = strsep_quote(&names, ":"); 3630 name_ul = strtoul(name, &end, 10); 3631 if (*end != '\0' || end == name) 3632 pw = getpwnam(name); 3633 else 3634 pw = getpwuid((uid_t)name_ul); 3635 if (pw != NULL) { 3636 cr->cr_uid = pw->pw_uid; 3637 } else if (*end != '\0' || end == name) { 3638 syslog(LOG_ERR, "unknown user: %s", name); 3639 cr->cr_uid = UID_NOBODY; 3640 goto nogroup; 3641 } else { 3642 cr->cr_uid = name_ul; 3643 } 3644 3645 /* 3646 * Credentials specified as those of a user (i.e., use its associated 3647 * groups as specified in the password database). 3648 */ 3649 if (names == NULL) { 3650 if (pw == NULL) { 3651 syslog(LOG_ERR, "no passwd entry for user: %s, " 3652 "can't determine groups", name); 3653 goto nogroup; 3654 } 3655 3656 cr->cr_ngroups = tngroups_max; 3657 if (getgrouplist(pw->pw_name, pw->pw_gid, 3658 cr->cr_groups, &cr->cr_ngroups) != 0) { 3659 syslog(LOG_ERR, "too many groups"); 3660 cr->cr_ngroups = tngroups_max; 3661 } 3662 return; 3663 } 3664 3665 /* 3666 * Explicit credentials specified as a colon separated list: 3667 * uid:gid:gid:... 3668 */ 3669 cr->cr_ngroups = 0; 3670 while (names != NULL && *names != '\0') { 3671 const struct group *gr; 3672 gid_t group; 3673 3674 name = strsep_quote(&names, ":"); 3675 name_ul = strtoul(name, &end, 10); 3676 if (*end != '\0' || end == name) { 3677 if ((gr = getgrnam(name)) == NULL) { 3678 syslog(LOG_ERR, "unknown group: %s", name); 3679 continue; 3680 } 3681 group = gr->gr_gid; 3682 } else { 3683 group = name_ul; 3684 } 3685 if (cr->cr_ngroups == tngroups_max) { 3686 syslog(LOG_ERR, "too many groups"); 3687 break; 3688 } 3689 cr->cr_groups[cr->cr_ngroups++] = group; 3690 } 3691 if (cr->cr_ngroups == 0) 3692 goto nogroup; 3693 return; 3694 3695 nogroup: 3696 cr->cr_ngroups = 1; 3697 cr->cr_groups[0] = nogroup(); 3698 } 3699 3700 #define STRSIZ (MNTNAMLEN+MNTPATHLEN+50) 3701 /* 3702 * Routines that maintain the remote mounttab 3703 */ 3704 static void 3705 get_mountlist(void) 3706 { 3707 struct mountlist *mlp; 3708 char *host, *dirp, *cp; 3709 char str[STRSIZ]; 3710 FILE *mlfile; 3711 3712 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 3713 if (errno == ENOENT) 3714 return; 3715 else { 3716 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 3717 return; 3718 } 3719 } 3720 while (fgets(str, STRSIZ, mlfile) != NULL) { 3721 cp = str; 3722 host = strsep(&cp, " \t\n"); 3723 dirp = strsep(&cp, " \t\n"); 3724 if (host == NULL || dirp == NULL) 3725 continue; 3726 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 3727 if (mlp == (struct mountlist *)NULL) 3728 out_of_mem(); 3729 strncpy(mlp->ml_host, host, MNTNAMLEN); 3730 mlp->ml_host[MNTNAMLEN] = '\0'; 3731 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3732 mlp->ml_dirp[MNTPATHLEN] = '\0'; 3733 3734 SLIST_INSERT_HEAD(&mlhead, mlp, next); 3735 } 3736 fclose(mlfile); 3737 } 3738 3739 static void 3740 del_mlist(char *hostp, char *dirp) 3741 { 3742 struct mountlist *mlp, *mlp2; 3743 FILE *mlfile; 3744 int fnd = 0; 3745 3746 SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) { 3747 if (!strcmp(mlp->ml_host, hostp) && 3748 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 3749 fnd = 1; 3750 SLIST_REMOVE(&mlhead, mlp, mountlist, next); 3751 free((caddr_t)mlp); 3752 } 3753 } 3754 if (fnd) { 3755 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 3756 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 3757 return; 3758 } 3759 SLIST_FOREACH(mlp, &mlhead, next) { 3760 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3761 } 3762 fclose(mlfile); 3763 } 3764 } 3765 3766 static void 3767 add_mlist(char *hostp, char *dirp) 3768 { 3769 struct mountlist *mlp; 3770 FILE *mlfile; 3771 3772 SLIST_FOREACH(mlp, &mlhead, next) { 3773 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 3774 return; 3775 } 3776 3777 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 3778 if (mlp == (struct mountlist *)NULL) 3779 out_of_mem(); 3780 strncpy(mlp->ml_host, hostp, MNTNAMLEN); 3781 mlp->ml_host[MNTNAMLEN] = '\0'; 3782 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3783 mlp->ml_dirp[MNTPATHLEN] = '\0'; 3784 SLIST_INSERT_HEAD(&mlhead, mlp, next); 3785 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 3786 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 3787 return; 3788 } 3789 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3790 fclose(mlfile); 3791 } 3792 3793 /* 3794 * Free up a group list. 3795 */ 3796 static void 3797 free_grp(struct grouplist *grp) 3798 { 3799 if (grp->gr_type == GT_HOST) { 3800 if (grp->gr_ptr.gt_addrinfo != NULL) 3801 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 3802 } else if (grp->gr_type == GT_NET) { 3803 if (grp->gr_ptr.gt_net.nt_name) 3804 free(grp->gr_ptr.gt_net.nt_name); 3805 } 3806 if (grp->gr_anon.cr_groups != grp->gr_anon.cr_smallgrps) 3807 free(grp->gr_anon.cr_groups); 3808 free((caddr_t)grp); 3809 } 3810 3811 #ifdef DEBUG 3812 static void 3813 SYSLOG(int pri, const char *fmt, ...) 3814 { 3815 va_list ap; 3816 3817 va_start(ap, fmt); 3818 vfprintf(stderr, fmt, ap); 3819 va_end(ap); 3820 } 3821 #endif /* DEBUG */ 3822 3823 /* 3824 * Check options for consistency. 3825 */ 3826 static int 3827 check_options(struct dirlist *dp) 3828 { 3829 3830 if (v4root_phase == 0 && dp == NULL) 3831 return (1); 3832 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 3833 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 3834 return (1); 3835 } 3836 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 3837 syslog(LOG_ERR, "-mask requires -network"); 3838 return (1); 3839 } 3840 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 3841 syslog(LOG_ERR, "-network requires mask specification"); 3842 return (1); 3843 } 3844 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 3845 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 3846 return (1); 3847 } 3848 if (v4root_phase > 0 && 3849 (opt_flags & 3850 ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) { 3851 syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:"); 3852 return (1); 3853 } 3854 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 3855 syslog(LOG_ERR, "-alldirs has multiple directories"); 3856 return (1); 3857 } 3858 return (0); 3859 } 3860 3861 static int 3862 check_path_component(const char *path, char **err) 3863 { 3864 struct stat sb; 3865 3866 if (lstat(path, &sb)) { 3867 asprintf(err, "%s: lstat() failed: %s.\n", 3868 path, strerror(errno)); 3869 return (0); 3870 } 3871 3872 switch (sb.st_mode & S_IFMT) { 3873 case S_IFDIR: 3874 return (1); 3875 case S_IFLNK: 3876 asprintf(err, "%s: path is a symbolic link.\n", path); 3877 break; 3878 case S_IFREG: 3879 asprintf(err, "%s: path is a file rather than a directory.\n", 3880 path); 3881 break; 3882 default: 3883 asprintf(err, "%s: path is not a directory.\n", path); 3884 } 3885 3886 return (0); 3887 } 3888 3889 /* 3890 * Check each path component for the presence of symbolic links. Return true 3891 */ 3892 static int 3893 check_dirpath(char *dirp, char **err) 3894 { 3895 char *cp; 3896 3897 cp = dirp + 1; 3898 while (*cp) { 3899 if (*cp == '/') { 3900 *cp = '\0'; 3901 3902 if (!check_path_component(dirp, err)) { 3903 *cp = '/'; 3904 return (0); 3905 } 3906 3907 *cp = '/'; 3908 } 3909 cp++; 3910 } 3911 3912 if (!check_path_component(dirp, err)) 3913 return (0); 3914 3915 return (1); 3916 } 3917 3918 /* 3919 * Populate statfs information. Return true on success. 3920 */ 3921 static int 3922 check_statfs(const char *dirp, struct statfs *fsb, char **err) 3923 { 3924 if (statfs(dirp, fsb)) { 3925 asprintf(err, "%s: statfs() failed: %s\n", dirp, 3926 strerror(errno)); 3927 return (0); 3928 } 3929 3930 return (1); 3931 } 3932 3933 /* 3934 * Make a netmask according to the specified prefix length. The ss_family 3935 * and other non-address fields must be initialised before calling this. 3936 */ 3937 static int 3938 makemask(struct sockaddr_storage *ssp, int bitlen) 3939 { 3940 u_char *p; 3941 int bits, i, len; 3942 3943 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 3944 return (-1); 3945 if (bitlen > len * CHAR_BIT) 3946 return (-1); 3947 3948 for (i = 0; i < len; i++) { 3949 bits = MIN(CHAR_BIT, bitlen); 3950 *p++ = (u_char)~0 << (CHAR_BIT - bits); 3951 bitlen -= bits; 3952 } 3953 return 0; 3954 } 3955 3956 /* 3957 * Check that the sockaddr is a valid netmask. Returns 0 if the mask 3958 * is acceptable (i.e. of the form 1...10....0). 3959 */ 3960 static int 3961 checkmask(struct sockaddr *sa) 3962 { 3963 u_char *mask; 3964 int i, len; 3965 3966 if ((mask = sa_rawaddr(sa, &len)) == NULL) 3967 return (-1); 3968 3969 for (i = 0; i < len; i++) 3970 if (mask[i] != 0xff) 3971 break; 3972 if (i < len) { 3973 if (~mask[i] & (u_char)(~mask[i] + 1)) 3974 return (-1); 3975 i++; 3976 } 3977 for (; i < len; i++) 3978 if (mask[i] != 0) 3979 return (-1); 3980 return (0); 3981 } 3982 3983 /* 3984 * Compare two sockaddrs according to a specified mask. Return zero if 3985 * `sa1' matches `sa2' when filtered by the netmask in `samask'. 3986 * If samask is NULL, perform a full comparison. 3987 */ 3988 static int 3989 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 3990 { 3991 unsigned char *p1, *p2, *mask; 3992 int len, i; 3993 3994 if (sa1->sa_family != sa2->sa_family || 3995 (p1 = sa_rawaddr(sa1, &len)) == NULL || 3996 (p2 = sa_rawaddr(sa2, NULL)) == NULL) 3997 return (1); 3998 3999 switch (sa1->sa_family) { 4000 case AF_INET6: 4001 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 4002 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 4003 return (1); 4004 break; 4005 } 4006 4007 /* Simple binary comparison if no mask specified. */ 4008 if (samask == NULL) 4009 return (memcmp(p1, p2, len)); 4010 4011 /* Set up the mask, and do a mask-based comparison. */ 4012 if (sa1->sa_family != samask->sa_family || 4013 (mask = sa_rawaddr(samask, NULL)) == NULL) 4014 return (1); 4015 4016 for (i = 0; i < len; i++) 4017 if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 4018 return (1); 4019 return (0); 4020 } 4021 4022 /* 4023 * Return a pointer to the part of the sockaddr that contains the 4024 * raw address, and set *nbytes to its length in bytes. Returns 4025 * NULL if the address family is unknown. 4026 */ 4027 static void * 4028 sa_rawaddr(struct sockaddr *sa, int *nbytes) { 4029 void *p; 4030 int len; 4031 4032 switch (sa->sa_family) { 4033 case AF_INET: 4034 len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 4035 p = &((struct sockaddr_in *)sa)->sin_addr; 4036 break; 4037 case AF_INET6: 4038 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 4039 p = &((struct sockaddr_in6 *)sa)->sin6_addr; 4040 break; 4041 default: 4042 p = NULL; 4043 len = 0; 4044 } 4045 4046 if (nbytes != NULL) 4047 *nbytes = len; 4048 return (p); 4049 } 4050 4051 static void 4052 huphandler(int sig __unused) 4053 { 4054 4055 got_sighup = 1; 4056 } 4057 4058 static void 4059 terminate(int sig __unused) 4060 { 4061 free(tmp_groups); 4062 pidfile_remove(pfh); 4063 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 4064 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 4065 exit (0); 4066 } 4067 4068 static void 4069 cp_cred(struct expcred *outcr, struct expcred *incr) 4070 { 4071 4072 outcr->cr_uid = incr->cr_uid; 4073 outcr->cr_ngroups = incr->cr_ngroups; 4074 if (outcr->cr_ngroups > SMALLNGROUPS) 4075 outcr->cr_groups = malloc(outcr->cr_ngroups * sizeof(gid_t)); 4076 else 4077 outcr->cr_groups = outcr->cr_smallgrps; 4078 memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups * 4079 sizeof(gid_t)); 4080 } 4081 4082 static gid_t 4083 nogroup() 4084 { 4085 static gid_t nogroup = 0; /* 0 means unset. */ 4086 4087 if (nogroup == 0) { 4088 const struct group *gr = getgrnam("nogroup"); 4089 4090 if (gr != NULL && gr->gr_gid != 0) 4091 nogroup = gr->gr_gid; 4092 else 4093 nogroup = GID_NOGROUP; 4094 } 4095 return (nogroup); 4096 } 4097