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