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