1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1992, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * 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/linker.h> 37 #include <sys/module.h> 38 #include <sys/mount.h> 39 #include <sys/socket.h> 40 #include <sys/stat.h> 41 #include <sys/syslog.h> 42 #include <sys/uio.h> 43 44 #include <rpc/rpc.h> 45 #include <rpc/pmap_clnt.h> 46 #include <rpc/pmap_prot.h> 47 #include <rpcsvc/nfs_prot.h> 48 #include <rpcsvc/mount.h> 49 50 #include <fs/nfs/nfsproto.h> 51 #include <fs/nfs/nfsv4_errstr.h> 52 53 #include <arpa/inet.h> 54 #include <net/route.h> 55 #include <net/if.h> 56 57 #include <ctype.h> 58 #include <err.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <mntopts.h> 62 #include <netdb.h> 63 #include <stdbool.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <strings.h> 68 #include <sysexits.h> 69 #include <unistd.h> 70 71 #include "mounttab.h" 72 73 /* Table for af,sotype -> netid conversions. */ 74 static struct nc_protos { 75 const char *netid; 76 int af; 77 int sotype; 78 } nc_protos[] = { 79 {"udp", AF_INET, SOCK_DGRAM}, 80 {"tcp", AF_INET, SOCK_STREAM}, 81 {"udp6", AF_INET6, SOCK_DGRAM}, 82 {"tcp6", AF_INET6, SOCK_STREAM}, 83 {NULL, 0, 0} 84 }; 85 86 struct nfhret { 87 u_long stat; 88 long vers; 89 long auth; 90 long fhsize; 91 u_char nfh[NFS3_FHSIZE]; 92 }; 93 #define BGRND 0x01 94 #define ISBGRND 0x02 95 #define OF_NOINET4 0x04 96 #define OF_NOINET6 0x08 97 #define BGRNDNOW 0x10 98 static int retrycnt = -1; 99 static int opflags = 0; 100 static int nfsproto = IPPROTO_TCP; 101 static int mnttcp_ok = 1; 102 static int noconn = 0; 103 /* The 'portspec' is the server nfs port; NULL means look up via rpcbind. */ 104 static const char *portspec = NULL; 105 static struct sockaddr *addr; 106 static int addrlen = 0; 107 static u_char *fh = NULL; 108 static int fhsize = 0; 109 static int secflavor = -1; 110 static int got_principal = 0; 111 static in_port_t mntproto_port = 0; 112 113 static enum mountmode { 114 ANY, 115 V2, 116 V3, 117 V4 118 } mountmode = ANY; 119 120 /* Return codes for nfs_tryproto. */ 121 enum tryret { 122 TRYRET_SUCCESS, 123 TRYRET_TIMEOUT, /* No response received. */ 124 TRYRET_REMOTEERR, /* Error received from remote server. */ 125 TRYRET_LOCALERR /* Local failure. */ 126 }; 127 128 static int sec_name_to_num(const char *sec); 129 static const char *sec_num_to_name(int num); 130 static int getnfsargs(char **, char **, struct iovec **iov, int *iovlen); 131 /* void set_rpc_maxgrouplist(int); */ 132 static struct netconfig *getnetconf_cached(const char *netid); 133 static const char *netidbytype(int af, int sotype); 134 static void usage(void) __dead2; 135 static int xdr_dir(XDR *, char *); 136 static int xdr_fh(XDR *, struct nfhret *); 137 static enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, 138 char **errstr, struct iovec **iov, int *iovlen); 139 static enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 140 141 int 142 main(int argc, char *argv[]) 143 { 144 int c; 145 struct iovec *iov; 146 int num, iovlen; 147 char *host, *mntname, *p, *spec, *tmp; 148 char mntpath[MAXPATHLEN], errmsg[255]; 149 char hostname[MAXHOSTNAMELEN + 1], gssn[MAXHOSTNAMELEN + 50]; 150 const char *gssname, *nmount_errstr; 151 bool softintr; 152 153 softintr = false; 154 iov = NULL; 155 iovlen = 0; 156 memset(errmsg, 0, sizeof(errmsg)); 157 gssname = NULL; 158 159 while ((c = getopt(argc, argv, 160 "23a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1) 161 switch (c) { 162 case '2': 163 mountmode = V2; 164 break; 165 case '3': 166 mountmode = V3; 167 break; 168 case 'a': 169 printf("-a deprecated, use -o readahead=<value>\n"); 170 build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1); 171 break; 172 case 'b': 173 opflags |= BGRND; 174 break; 175 case 'c': 176 printf("-c deprecated, use -o noconn\n"); 177 build_iovec(&iov, &iovlen, "noconn", NULL, 0); 178 noconn = 1; 179 break; 180 case 'D': 181 printf("-D deprecated, use -o deadthresh=<value>\n"); 182 build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1); 183 break; 184 case 'd': 185 printf("-d deprecated, use -o dumbtimer"); 186 build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0); 187 break; 188 case 'g': 189 printf("-g deprecated, use -o maxgroups"); 190 num = strtol(optarg, &p, 10); 191 if (*p || num <= 0) 192 errx(1, "illegal -g value -- %s", optarg); 193 //set_rpc_maxgrouplist(num); 194 build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1); 195 break; 196 case 'I': 197 printf("-I deprecated, use -o readdirsize=<value>\n"); 198 build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1); 199 break; 200 case 'i': 201 printf("-i deprecated, use -o intr\n"); 202 build_iovec(&iov, &iovlen, "intr", NULL, 0); 203 softintr = true; 204 break; 205 case 'L': 206 printf("-L deprecated, use -o nolockd\n"); 207 build_iovec(&iov, &iovlen, "nolockd", NULL, 0); 208 break; 209 case 'l': 210 printf("-l deprecated, -o rdirplus\n"); 211 build_iovec(&iov, &iovlen, "rdirplus", NULL, 0); 212 break; 213 case 'N': 214 printf("-N deprecated, do not specify -o resvport\n"); 215 break; 216 case 'o': { 217 int pass_flag_to_nmount; 218 char *opt = optarg; 219 while (opt) { 220 char *pval = NULL; 221 char *pnextopt = NULL; 222 const char *val = ""; 223 pass_flag_to_nmount = 1; 224 pnextopt = strchr(opt, ','); 225 if (pnextopt != NULL) { 226 *pnextopt = '\0'; 227 pnextopt++; 228 } 229 pval = strchr(opt, '='); 230 if (pval != NULL) { 231 *pval = '\0'; 232 val = pval + 1; 233 } 234 if (strcmp(opt, "bg") == 0) { 235 opflags |= BGRND; 236 pass_flag_to_nmount=0; 237 } else if (strcmp(opt, "bgnow") == 0) { 238 opflags |= BGRNDNOW; 239 pass_flag_to_nmount=0; 240 } else if (strcmp(opt, "fg") == 0) { 241 /* same as not specifying -o bg */ 242 pass_flag_to_nmount=0; 243 } else if (strcmp(opt, "gssname") == 0) { 244 pass_flag_to_nmount = 0; 245 gssname = val; 246 } else if (strcmp(opt, "mntudp") == 0) { 247 mnttcp_ok = 0; 248 nfsproto = IPPROTO_UDP; 249 } else if (strcmp(opt, "udp") == 0) { 250 nfsproto = IPPROTO_UDP; 251 } else if (strcmp(opt, "tcp") == 0) { 252 nfsproto = IPPROTO_TCP; 253 } else if (strcmp(opt, "noinet4") == 0) { 254 pass_flag_to_nmount=0; 255 opflags |= OF_NOINET4; 256 } else if (strcmp(opt, "noinet6") == 0) { 257 pass_flag_to_nmount=0; 258 opflags |= OF_NOINET6; 259 } else if (strcmp(opt, "noconn") == 0) { 260 noconn = 1; 261 } else if (strcmp(opt, "nfsv2") == 0) { 262 pass_flag_to_nmount=0; 263 mountmode = V2; 264 } else if (strcmp(opt, "nfsv3") == 0) { 265 mountmode = V3; 266 } else if (strcmp(opt, "nfsv4") == 0) { 267 pass_flag_to_nmount=0; 268 mountmode = V4; 269 nfsproto = IPPROTO_TCP; 270 if (portspec == NULL) 271 portspec = "2049"; 272 } else if (strcmp(opt, "port") == 0) { 273 pass_flag_to_nmount=0; 274 asprintf(&tmp, "%d", atoi(val)); 275 if (tmp == NULL) 276 err(1, "asprintf"); 277 portspec = tmp; 278 } else if (strcmp(opt, "principal") == 0) { 279 got_principal = 1; 280 } else if (strcmp(opt, "proto") == 0) { 281 pass_flag_to_nmount=0; 282 if (strcmp(val, "tcp") == 0) { 283 nfsproto = IPPROTO_TCP; 284 opflags |= OF_NOINET6; 285 build_iovec(&iov, &iovlen, 286 "tcp", NULL, 0); 287 } else if (strcmp(val, "udp") == 0) { 288 mnttcp_ok = 0; 289 nfsproto = IPPROTO_UDP; 290 opflags |= OF_NOINET6; 291 build_iovec(&iov, &iovlen, 292 "udp", NULL, 0); 293 } else if (strcmp(val, "tcp6") == 0) { 294 nfsproto = IPPROTO_TCP; 295 opflags |= OF_NOINET4; 296 build_iovec(&iov, &iovlen, 297 "tcp", NULL, 0); 298 } else if (strcmp(val, "udp6") == 0) { 299 mnttcp_ok = 0; 300 nfsproto = IPPROTO_UDP; 301 opflags |= OF_NOINET4; 302 build_iovec(&iov, &iovlen, 303 "udp", NULL, 0); 304 } else { 305 errx(1, 306 "illegal proto value -- %s", 307 val); 308 } 309 } else if (strcmp(opt, "sec") == 0) { 310 /* 311 * Don't add this option to 312 * the iovec yet - we will 313 * negotiate which sec flavor 314 * to use with the remote 315 * mountd. 316 */ 317 pass_flag_to_nmount=0; 318 secflavor = sec_name_to_num(val); 319 if (secflavor < 0) { 320 errx(1, 321 "illegal sec value -- %s", 322 val); 323 } 324 } else if (strcmp(opt, "retrycnt") == 0) { 325 pass_flag_to_nmount=0; 326 num = strtol(val, &p, 10); 327 if (*p || num < 0) 328 errx(1, "illegal retrycnt value -- %s", val); 329 retrycnt = num; 330 } else if (strcmp(opt, "maxgroups") == 0) { 331 num = strtol(val, &p, 10); 332 if (*p || num <= 0) 333 errx(1, "illegal maxgroups value -- %s", val); 334 //set_rpc_maxgrouplist(num); 335 } else if (strcmp(opt, "vers") == 0) { 336 num = strtol(val, &p, 10); 337 if (*p || num <= 0) 338 errx(1, "illegal vers value -- " 339 "%s", val); 340 switch (num) { 341 case 2: 342 mountmode = V2; 343 break; 344 case 3: 345 mountmode = V3; 346 build_iovec(&iov, &iovlen, 347 "nfsv3", NULL, 0); 348 break; 349 case 4: 350 mountmode = V4; 351 nfsproto = IPPROTO_TCP; 352 if (portspec == NULL) 353 portspec = "2049"; 354 break; 355 default: 356 errx(1, "illegal nfs version " 357 "value -- %s", val); 358 } 359 pass_flag_to_nmount=0; 360 } else if (strcmp(opt, "soft") == 0) { 361 softintr = true; 362 } else if (strcmp(opt, "intr") == 0) { 363 softintr = true; 364 } else if (strcmp(opt, "mountport") == 0) { 365 num = strtol(val, &p, 10); 366 if (*p || num <= 0 || num > IPPORT_MAX) 367 errx(1, "illegal port num -- " 368 "%s", val); 369 mntproto_port = num; 370 pass_flag_to_nmount=0; 371 } 372 if (pass_flag_to_nmount) { 373 build_iovec(&iov, &iovlen, opt, 374 __DECONST(void *, val), 375 strlen(val) + 1); 376 } 377 opt = pnextopt; 378 } 379 } 380 break; 381 case 'P': 382 /* obsolete for -o noresvport now default */ 383 printf("-P deprecated, use -o noresvport\n"); 384 build_iovec(&iov, &iovlen, "noresvport", NULL, 0); 385 break; 386 case 'R': 387 printf("-R deprecated, use -o retrycnt=<retrycnt>\n"); 388 num = strtol(optarg, &p, 10); 389 if (*p || num < 0) 390 errx(1, "illegal -R value -- %s", optarg); 391 retrycnt = num; 392 break; 393 case 'r': 394 printf("-r deprecated, use -o rsize=<rsize>\n"); 395 build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1); 396 break; 397 case 's': 398 printf("-s deprecated, use -o soft\n"); 399 build_iovec(&iov, &iovlen, "soft", NULL, 0); 400 softintr = true; 401 break; 402 case 'T': 403 nfsproto = IPPROTO_TCP; 404 printf("-T deprecated, use -o tcp\n"); 405 break; 406 case 't': 407 printf("-t deprecated, use -o timeout=<value>\n"); 408 build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1); 409 break; 410 case 'w': 411 printf("-w deprecated, use -o wsize=<value>\n"); 412 build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1); 413 break; 414 case 'x': 415 printf("-x deprecated, use -o retrans=<value>\n"); 416 build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1); 417 break; 418 case 'U': 419 printf("-U deprecated, use -o mntudp\n"); 420 mnttcp_ok = 0; 421 nfsproto = IPPROTO_UDP; 422 build_iovec(&iov, &iovlen, "mntudp", NULL, 0); 423 break; 424 default: 425 usage(); 426 break; 427 } 428 argc -= optind; 429 argv += optind; 430 431 if ((opflags & (BGRND | BGRNDNOW)) == (BGRND | BGRNDNOW)) 432 errx(1, "Options bg and bgnow are mutually exclusive"); 433 434 if (argc != 2) { 435 usage(); 436 /* NOTREACHED */ 437 } 438 439 /* Warn that NFSv4 mounts only work correctly as hard mounts. */ 440 if (mountmode == V4 && softintr) 441 warnx("Warning, options soft and/or intr cannot be safely used" 442 " for NFSv4. See the BUGS section of mount_nfs(8)"); 443 444 spec = *argv++; 445 mntname = *argv; 446 447 if (retrycnt == -1) 448 /* The default is to keep retrying forever. */ 449 retrycnt = 0; 450 451 if (modfind("nfscl") < 0) { 452 /* Not present in kernel, try loading it */ 453 if (kldload("nfscl") < 0 || 454 modfind("nfscl") < 0) 455 errx(1, "nfscl is not available"); 456 } 457 458 /* 459 * Add the fqdn to the gssname, as required. 460 */ 461 if (gssname != NULL) { 462 if (strchr(gssname, '@') == NULL && 463 gethostname(hostname, MAXHOSTNAMELEN) == 0) { 464 snprintf(gssn, sizeof (gssn), "%s@%s", gssname, 465 hostname); 466 gssname = gssn; 467 } 468 build_iovec(&iov, &iovlen, "gssname", 469 __DECONST(void *, gssname), strlen(gssname) + 1); 470 } 471 472 if (!getnfsargs(&spec, &host, &iov, &iovlen)) 473 exit(1); 474 475 /* resolve the mountpoint with realpath(3) */ 476 if (checkpath(mntname, mntpath) != 0) 477 err(1, "%s", mntpath); 478 479 build_iovec_argf(&iov, &iovlen, "fstype", "nfs"); 480 build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); 481 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 482 483 if (nmount(iov, iovlen, 0)) { 484 nmount_errstr = nfsv4_geterrstr(errno); 485 if (mountmode == V4 && nmount_errstr != NULL) 486 errx(1, "nmount: %s, %s", mntpath, nmount_errstr); 487 else 488 err(1, "nmount: %s%s%s", mntpath, errmsg[0] ? ", " : "", 489 errmsg); 490 } else if (mountmode != V4 && !add_mtab(host, spec)) { 491 /* Add mounted file system to PATH_MOUNTTAB */ 492 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, host, spec); 493 } 494 495 exit(0); 496 } 497 498 static int 499 sec_name_to_num(const char *sec) 500 { 501 if (!strcmp(sec, "krb5")) 502 return (RPCSEC_GSS_KRB5); 503 if (!strcmp(sec, "krb5i")) 504 return (RPCSEC_GSS_KRB5I); 505 if (!strcmp(sec, "krb5p")) 506 return (RPCSEC_GSS_KRB5P); 507 if (!strcmp(sec, "sys")) 508 return (AUTH_SYS); 509 return (-1); 510 } 511 512 static const char * 513 sec_num_to_name(int flavor) 514 { 515 switch (flavor) { 516 case RPCSEC_GSS_KRB5: 517 return ("krb5"); 518 case RPCSEC_GSS_KRB5I: 519 return ("krb5i"); 520 case RPCSEC_GSS_KRB5P: 521 return ("krb5p"); 522 case AUTH_SYS: 523 return ("sys"); 524 } 525 return (NULL); 526 } 527 528 /* 529 * Wait for RTM_IFINFO message with interface that is IFF_UP and with 530 * link on, or until timeout expires. Returns seconds left. 531 */ 532 static time_t 533 rtm_ifinfo_sleep(time_t sec) 534 { 535 char buf[2048] __aligned(__alignof(struct if_msghdr)); 536 fd_set rfds; 537 struct timeval tv, start; 538 ssize_t nread; 539 int n, s; 540 541 s = socket(PF_ROUTE, SOCK_RAW, 0); 542 if (s < 0) 543 err(EX_OSERR, "socket"); 544 (void)gettimeofday(&start, NULL); 545 546 for (tv.tv_sec = sec, tv.tv_usec = 0; 547 tv.tv_sec > 0; 548 (void)gettimeofday(&tv, NULL), 549 tv.tv_sec = sec - (tv.tv_sec - start.tv_sec)) { 550 FD_ZERO(&rfds); 551 FD_SET(s, &rfds); 552 n = select(s + 1, &rfds, NULL, NULL, &tv); 553 if (n == 0) 554 continue; 555 if (n == -1) { 556 if (errno == EINTR) 557 continue; 558 else 559 err(EX_SOFTWARE, "select"); 560 } 561 nread = read(s, buf, 2048); 562 if (nread < 0) 563 err(EX_OSERR, "read"); 564 if ((size_t)nread >= sizeof(struct if_msghdr)) { 565 struct if_msghdr *ifm; 566 567 ifm = (struct if_msghdr *)buf; 568 if (ifm->ifm_version == RTM_VERSION && 569 ifm->ifm_type == RTM_IFINFO && 570 (ifm->ifm_flags & IFF_UP) && 571 ifm->ifm_data.ifi_link_state != LINK_STATE_DOWN) 572 break; 573 } 574 } 575 576 close(s); 577 578 return (tv.tv_sec); 579 } 580 581 static int 582 getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen) 583 { 584 struct addrinfo hints, *ai_nfs, *ai; 585 enum tryret ret; 586 int ecode, speclen, remoteerr, offset, have_bracket = 0; 587 char *hostp, *delimp, *errstr, *spec; 588 size_t len; 589 static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; 590 bool resolved; 591 592 spec = *specp; 593 if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 594 *(delimp + 1) == ':') { 595 hostp = spec + 1; 596 spec = delimp + 2; 597 have_bracket = 1; 598 } else if ((delimp = strrchr(spec, ':')) != NULL) { 599 hostp = spec; 600 spec = delimp + 1; 601 } else if ((delimp = strrchr(spec, '@')) != NULL) { 602 warnx("path@server syntax is deprecated, use server:path"); 603 hostp = delimp + 1; 604 } else { 605 warnx("no <host>:<dirpath> nfs-name"); 606 return (0); 607 } 608 *delimp = '\0'; 609 610 /* 611 * If there has been a trailing slash at mounttime it seems 612 * that some mountd implementations fail to remove the mount 613 * entries from their mountlist while unmounting. 614 */ 615 for (speclen = strlen(spec); 616 speclen > 1 && spec[speclen - 1] == '/'; 617 speclen--) 618 spec[speclen - 1] = '\0'; 619 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 620 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 621 return (0); 622 } 623 /* Make both '@' and ':' notations equal */ 624 if (*hostp != '\0') { 625 len = strlen(hostp); 626 offset = 0; 627 if (have_bracket) 628 nam[offset++] = '['; 629 memmove(nam + offset, hostp, len); 630 if (have_bracket) 631 nam[len + offset++] = ']'; 632 nam[len + offset++] = ':'; 633 memmove(nam + len + offset, spec, speclen); 634 nam[len + speclen + offset] = '\0'; 635 } 636 637 /* 638 * Handle an internet host address. 639 */ 640 memset(&hints, 0, sizeof hints); 641 hints.ai_flags = AI_NUMERICHOST; 642 if (nfsproto == IPPROTO_TCP) 643 hints.ai_socktype = SOCK_STREAM; 644 else if (nfsproto == IPPROTO_UDP) 645 hints.ai_socktype = SOCK_DGRAM; 646 647 resolved = (getaddrinfo(hostp, portspec, &hints, &ai_nfs) == 0); 648 649 if ((opflags & (BGRNDNOW | ISBGRND)) == BGRNDNOW) { 650 warnx("Mount %s:%s, backgrounding", 651 hostp, spec); 652 opflags |= ISBGRND; 653 if (daemon(0, 0) != 0) 654 err(1, "daemon"); 655 } 656 657 ret = TRYRET_LOCALERR; 658 for (;;) { 659 if (!resolved) { 660 hints.ai_flags = AI_CANONNAME; 661 if ((ecode = getaddrinfo(hostp, portspec, &hints, 662 &ai_nfs)) != 0) { 663 if (portspec == NULL) 664 warnx("%s: %s", hostp, 665 gai_strerror(ecode)); 666 else 667 warnx("%s:%s: %s", hostp, portspec, 668 gai_strerror(ecode)); 669 if (ecode == EAI_AGAIN && 670 (opflags & (BGRNDNOW | BGRND))) 671 goto retry; 672 else 673 exit(1); 674 } 675 resolved = true; 676 /* 677 * For a Kerberized nfs mount where the 678 * "principal" argument has not been set, add 679 * it here. 680 */ 681 if (got_principal == 0 && secflavor != AUTH_SYS && 682 ai_nfs->ai_canonname != NULL) { 683 snprintf(pname, sizeof (pname), "nfs@%s", 684 ai_nfs->ai_canonname); 685 build_iovec(iov, iovlen, "principal", pname, 686 strlen(pname) + 1); 687 } 688 } 689 690 /* 691 * Try each entry returned by getaddrinfo(). Note the 692 * occurrence of remote errors by setting `remoteerr'. 693 */ 694 remoteerr = 0; 695 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 696 if ((ai->ai_family == AF_INET6) && 697 (opflags & OF_NOINET6)) 698 continue; 699 if ((ai->ai_family == AF_INET) && 700 (opflags & OF_NOINET4)) 701 continue; 702 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 703 iovlen); 704 if (ret == TRYRET_SUCCESS) 705 break; 706 if (ret != TRYRET_LOCALERR) 707 remoteerr = 1; 708 if ((opflags & ISBGRND) == 0) 709 fprintf(stderr, "%s\n", errstr); 710 } 711 if (ret == TRYRET_SUCCESS) 712 break; 713 714 /* Exit if all errors were local. */ 715 if (!remoteerr) 716 exit(1); 717 retry: 718 /* 719 * If retrycnt == 0, we are to keep retrying forever. 720 * Otherwise decrement it, and exit if it hits zero. 721 */ 722 if (retrycnt != 0 && --retrycnt == 0) 723 exit(1); 724 725 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 726 warnx("Cannot immediately mount %s:%s, backgrounding", 727 hostp, spec); 728 opflags |= ISBGRND; 729 if (daemon(0, 0) != 0) 730 err(1, "daemon"); 731 } 732 /* 733 * If rtm_ifinfo_sleep() returns non-zero, don't count 734 * that as a retry attempt. 735 */ 736 if (rtm_ifinfo_sleep(60) && retrycnt != 0) 737 retrycnt++; 738 } 739 freeaddrinfo(ai_nfs); 740 741 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 742 743 *specp = spec; 744 *hostpp = hostp; 745 return (1); 746 } 747 748 /* 749 * Try to set up the NFS arguments according to the address 750 * family, protocol (and possibly port) specified in `ai'. 751 * 752 * Returns TRYRET_SUCCESS if successful, or: 753 * TRYRET_TIMEOUT The server did not respond. 754 * TRYRET_REMOTEERR The server reported an error. 755 * TRYRET_LOCALERR Local failure. 756 * 757 * In all error cases, *errstr will be set to a statically-allocated string 758 * describing the error. 759 */ 760 static enum tryret 761 nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 762 struct iovec **iov, int *iovlen) 763 { 764 static char errbuf[256]; 765 struct sockaddr_storage nfs_ss; 766 struct netbuf nfs_nb; 767 struct nfhret nfhret; 768 struct timeval try; 769 struct rpc_err rpcerr; 770 CLIENT *clp; 771 struct netconfig *nconf, *nconf_mnt; 772 const char *netid, *netid_mnt, *secname; 773 int doconnect, nfsvers, mntvers, sotype; 774 enum clnt_stat clntstat; 775 enum mountmode trymntmode; 776 777 sotype = 0; 778 trymntmode = mountmode; 779 errbuf[0] = '\0'; 780 *errstr = errbuf; 781 782 if (nfsproto == IPPROTO_TCP) 783 sotype = SOCK_STREAM; 784 else if (nfsproto == IPPROTO_UDP) 785 sotype = SOCK_DGRAM; 786 787 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 788 snprintf(errbuf, sizeof errbuf, 789 "af %d sotype %d not supported", ai->ai_family, sotype); 790 return (TRYRET_LOCALERR); 791 } 792 if ((nconf = getnetconf_cached(netid)) == NULL) { 793 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 794 return (TRYRET_LOCALERR); 795 } 796 /* The RPCPROG_MNT netid may be different. */ 797 if (mnttcp_ok) { 798 netid_mnt = netid; 799 nconf_mnt = nconf; 800 } else { 801 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 802 == NULL) { 803 snprintf(errbuf, sizeof errbuf, 804 "af %d sotype SOCK_DGRAM not supported", 805 ai->ai_family); 806 return (TRYRET_LOCALERR); 807 } 808 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 809 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 810 nc_sperror()); 811 return (TRYRET_LOCALERR); 812 } 813 } 814 815 tryagain: 816 if (trymntmode == V4) { 817 nfsvers = 4; 818 mntvers = 3; /* Workaround for GCC. */ 819 } else if (trymntmode == V2) { 820 nfsvers = 2; 821 mntvers = 1; 822 } else { 823 nfsvers = 3; 824 mntvers = 3; 825 } 826 827 if (portspec != NULL) { 828 /* `ai' contains the complete nfsd sockaddr. */ 829 nfs_nb.buf = ai->ai_addr; 830 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 831 } else { 832 /* Ask the remote rpcbind. */ 833 nfs_nb.buf = &nfs_ss; 834 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 835 836 if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb, 837 hostp)) { 838 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 839 trymntmode == ANY) { 840 trymntmode = V2; 841 goto tryagain; 842 } 843 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 844 netid, hostp, spec, 845 clnt_spcreateerror("RPCPROG_NFS")); 846 return (returncode(rpc_createerr.cf_stat, 847 &rpc_createerr.cf_error)); 848 } 849 } 850 851 /* Check that the server (nfsd) responds on the port we have chosen. */ 852 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers, 853 0, 0); 854 if (clp == NULL) { 855 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 856 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 857 return (returncode(rpc_createerr.cf_stat, 858 &rpc_createerr.cf_error)); 859 } 860 if (sotype == SOCK_DGRAM && noconn == 0) { 861 /* 862 * Use connect(), to match what the kernel does. This 863 * catches cases where the server responds from the 864 * wrong source address. 865 */ 866 doconnect = 1; 867 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 868 clnt_destroy(clp); 869 snprintf(errbuf, sizeof errbuf, 870 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 871 spec); 872 return (TRYRET_LOCALERR); 873 } 874 } 875 876 try.tv_sec = 10; 877 try.tv_usec = 0; 878 clntstat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 879 (xdrproc_t)xdr_void, NULL, try); 880 if (clntstat != RPC_SUCCESS) { 881 if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 882 clnt_destroy(clp); 883 trymntmode = V2; 884 goto tryagain; 885 } 886 clnt_geterr(clp, &rpcerr); 887 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 888 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 889 clnt_destroy(clp); 890 return (returncode(clntstat, &rpcerr)); 891 } 892 clnt_destroy(clp); 893 894 /* 895 * For NFSv4, there is no mount protocol. 896 */ 897 if (trymntmode == V4) { 898 /* 899 * Store the server address in nfsargsp, making 900 * sure to copy any locally allocated structures. 901 */ 902 addrlen = nfs_nb.len; 903 addr = malloc(addrlen); 904 if (addr == NULL) 905 err(1, "malloc"); 906 bcopy(nfs_nb.buf, addr, addrlen); 907 908 build_iovec(iov, iovlen, "addr", addr, addrlen); 909 secname = sec_num_to_name(secflavor); 910 if (secname != NULL) { 911 build_iovec(iov, iovlen, "sec", 912 __DECONST(void *, secname), (size_t)-1); 913 } 914 build_iovec(iov, iovlen, "nfsv4", NULL, 0); 915 build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); 916 917 return (TRYRET_SUCCESS); 918 } 919 920 /* 921 * malloc() and copy the address, so that it can be used for 922 * nfsargs below. 923 */ 924 addrlen = nfs_nb.len; 925 addr = malloc(addrlen); 926 if (addr == NULL) 927 err(1, "malloc"); 928 bcopy(nfs_nb.buf, addr, addrlen); 929 930 /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ 931 try.tv_sec = 10; 932 try.tv_usec = 0; 933 if (mntproto_port != 0) { 934 struct sockaddr *sad; 935 struct sockaddr_in *sin; 936 struct sockaddr_in6 *sin6; 937 938 sad = (struct sockaddr *)nfs_nb.buf; 939 switch (sad->sa_family) { 940 case AF_INET: 941 sin = (struct sockaddr_in *)nfs_nb.buf; 942 sin->sin_port = htons(mntproto_port); 943 break; 944 case AF_INET6: 945 sin6 = (struct sockaddr_in6 *)nfs_nb.buf; 946 sin6->sin6_port = htons(mntproto_port); 947 break; 948 default: 949 snprintf(errbuf, sizeof(errbuf), 950 "Mnt port bad addr family %d\n", sad->sa_family); 951 return (TRYRET_LOCALERR); 952 } 953 clp = clnt_tli_create(RPC_ANYFD, nconf_mnt, &nfs_nb, MOUNTPROG, 954 mntvers, 0, 0); 955 } else { 956 /* Get the Mount protocol port# via rpcbind. */ 957 clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); 958 } 959 if (clp == NULL) { 960 free(addr); 961 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 962 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 963 return (returncode(rpc_createerr.cf_stat, 964 &rpc_createerr.cf_error)); 965 } 966 clp->cl_auth = authsys_create_default(); 967 nfhret.auth = secflavor; 968 nfhret.vers = mntvers; 969 clntstat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, 970 (xdrproc_t)xdr_fh, &nfhret, try); 971 auth_destroy(clp->cl_auth); 972 if (clntstat != RPC_SUCCESS) { 973 free(addr); 974 if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 975 clnt_destroy(clp); 976 trymntmode = V2; 977 goto tryagain; 978 } 979 clnt_geterr(clp, &rpcerr); 980 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 981 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 982 clnt_destroy(clp); 983 return (returncode(clntstat, &rpcerr)); 984 } 985 clnt_destroy(clp); 986 987 if (nfhret.stat != 0) { 988 free(addr); 989 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 990 hostp, spec, strerror(nfhret.stat)); 991 return (TRYRET_REMOTEERR); 992 } 993 994 /* 995 * Store the filehandle and server address in nfsargsp, making 996 * sure to copy any locally allocated structures. 997 */ 998 fhsize = nfhret.fhsize; 999 fh = malloc(fhsize); 1000 if (fh == NULL) 1001 err(1, "malloc"); 1002 bcopy(nfhret.nfh, fh, fhsize); 1003 1004 build_iovec(iov, iovlen, "addr", addr, addrlen); 1005 build_iovec(iov, iovlen, "fh", fh, fhsize); 1006 secname = sec_num_to_name(nfhret.auth); 1007 if (secname) { 1008 build_iovec(iov, iovlen, "sec", 1009 __DECONST(void *, secname), (size_t)-1); 1010 } 1011 if (nfsvers == 3) 1012 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 1013 1014 return (TRYRET_SUCCESS); 1015 } 1016 1017 /* 1018 * Catagorise a RPC return status and error into an `enum tryret' 1019 * return code. 1020 */ 1021 static enum tryret 1022 returncode(enum clnt_stat clntstat, struct rpc_err *rpcerr) 1023 { 1024 1025 switch (clntstat) { 1026 case RPC_TIMEDOUT: 1027 return (TRYRET_TIMEOUT); 1028 case RPC_PMAPFAILURE: 1029 case RPC_PROGNOTREGISTERED: 1030 case RPC_PROGVERSMISMATCH: 1031 /* XXX, these can be local or remote. */ 1032 case RPC_CANTSEND: 1033 case RPC_CANTRECV: 1034 return (TRYRET_REMOTEERR); 1035 case RPC_SYSTEMERROR: 1036 switch (rpcerr->re_errno) { 1037 case ETIMEDOUT: 1038 return (TRYRET_TIMEOUT); 1039 case ENOMEM: 1040 break; 1041 default: 1042 return (TRYRET_REMOTEERR); 1043 } 1044 /* FALLTHROUGH */ 1045 default: 1046 break; 1047 } 1048 return (TRYRET_LOCALERR); 1049 } 1050 1051 /* 1052 * Look up a netid based on an address family and socket type. 1053 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1054 * 1055 * XXX there should be a library function for this. 1056 */ 1057 static const char * 1058 netidbytype(int af, int sotype) 1059 { 1060 struct nc_protos *p; 1061 1062 for (p = nc_protos; p->netid != NULL; p++) { 1063 if (af != p->af || sotype != p->sotype) 1064 continue; 1065 return (p->netid); 1066 } 1067 return (NULL); 1068 } 1069 1070 /* 1071 * Look up a netconfig entry based on a netid, and cache the result so 1072 * that we don't need to remember to call freenetconfigent(). 1073 * 1074 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1075 * work on failure. 1076 */ 1077 static struct netconfig * 1078 getnetconf_cached(const char *netid) 1079 { 1080 static struct nc_entry { 1081 struct netconfig *nconf; 1082 struct nc_entry *next; 1083 } *head; 1084 struct nc_entry *p; 1085 struct netconfig *nconf; 1086 1087 for (p = head; p != NULL; p = p->next) 1088 if (strcmp(netid, p->nconf->nc_netid) == 0) 1089 return (p->nconf); 1090 1091 if ((nconf = getnetconfigent(netid)) == NULL) 1092 return (NULL); 1093 if ((p = malloc(sizeof(*p))) == NULL) 1094 err(1, "malloc"); 1095 p->nconf = nconf; 1096 p->next = head; 1097 head = p; 1098 1099 return (p->nconf); 1100 } 1101 1102 /* 1103 * xdr routines for mount rpc's 1104 */ 1105 static int 1106 xdr_dir(XDR *xdrsp, char *dirp) 1107 { 1108 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 1109 } 1110 1111 static int 1112 xdr_fh(XDR *xdrsp, struct nfhret *np) 1113 { 1114 int i; 1115 long auth, authcnt, authfnd = 0; 1116 1117 if (!xdr_u_long(xdrsp, &np->stat)) 1118 return (0); 1119 if (np->stat) 1120 return (1); 1121 switch (np->vers) { 1122 case 1: 1123 np->fhsize = NFS_FHSIZE; 1124 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE)); 1125 case 3: 1126 if (!xdr_long(xdrsp, &np->fhsize)) 1127 return (0); 1128 if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE) 1129 return (0); 1130 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1131 return (0); 1132 if (!xdr_long(xdrsp, &authcnt)) 1133 return (0); 1134 for (i = 0; i < authcnt; i++) { 1135 if (!xdr_long(xdrsp, &auth)) 1136 return (0); 1137 if (np->auth == -1) { 1138 np->auth = auth; 1139 authfnd++; 1140 } else if (auth == np->auth) { 1141 authfnd++; 1142 } 1143 } 1144 /* 1145 * Some servers, such as DEC's OSF/1 return a nil authenticator 1146 * list to indicate RPCAUTH_UNIX. 1147 */ 1148 if (authcnt == 0 && np->auth == -1) 1149 np->auth = AUTH_SYS; 1150 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1151 np->stat = EAUTH; 1152 return (1); 1153 } 1154 return (0); 1155 } 1156 1157 static void 1158 usage(void) 1159 { 1160 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1161 "usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1162 " [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1163 " [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1164 " rhost:path node"); 1165 exit(1); 1166 } 1167