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 <netdb.h> 62 #include <stdbool.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <strings.h> 67 #include <sysexits.h> 68 #include <unistd.h> 69 70 #include "mntopts.h" 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 591 spec = *specp; 592 if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 593 *(delimp + 1) == ':') { 594 hostp = spec + 1; 595 spec = delimp + 2; 596 have_bracket = 1; 597 } else if ((delimp = strrchr(spec, ':')) != NULL) { 598 hostp = spec; 599 spec = delimp + 1; 600 } else if ((delimp = strrchr(spec, '@')) != NULL) { 601 warnx("path@server syntax is deprecated, use server:path"); 602 hostp = delimp + 1; 603 } else { 604 warnx("no <host>:<dirpath> nfs-name"); 605 return (0); 606 } 607 *delimp = '\0'; 608 609 /* 610 * If there has been a trailing slash at mounttime it seems 611 * that some mountd implementations fail to remove the mount 612 * entries from their mountlist while unmounting. 613 */ 614 for (speclen = strlen(spec); 615 speclen > 1 && spec[speclen - 1] == '/'; 616 speclen--) 617 spec[speclen - 1] = '\0'; 618 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 619 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 620 return (0); 621 } 622 /* Make both '@' and ':' notations equal */ 623 if (*hostp != '\0') { 624 len = strlen(hostp); 625 offset = 0; 626 if (have_bracket) 627 nam[offset++] = '['; 628 memmove(nam + offset, hostp, len); 629 if (have_bracket) 630 nam[len + offset++] = ']'; 631 nam[len + offset++] = ':'; 632 memmove(nam + len + offset, spec, speclen); 633 nam[len + speclen + offset] = '\0'; 634 } 635 636 /* 637 * Handle an internet host address. 638 */ 639 memset(&hints, 0, sizeof hints); 640 hints.ai_flags = AI_NUMERICHOST; 641 if (nfsproto == IPPROTO_TCP) 642 hints.ai_socktype = SOCK_STREAM; 643 else if (nfsproto == IPPROTO_UDP) 644 hints.ai_socktype = SOCK_DGRAM; 645 646 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 647 hints.ai_flags = AI_CANONNAME; 648 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 649 != 0) { 650 if (portspec == NULL) 651 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 652 else 653 errx(1, "%s:%s: %s", hostp, portspec, 654 gai_strerror(ecode)); 655 return (0); 656 } 657 658 /* 659 * For a Kerberized nfs mount where the "principal" 660 * argument has not been set, add it here. 661 */ 662 if (got_principal == 0 && secflavor != AUTH_SYS && 663 ai_nfs->ai_canonname != NULL) { 664 snprintf(pname, sizeof (pname), "nfs@%s", 665 ai_nfs->ai_canonname); 666 build_iovec(iov, iovlen, "principal", pname, 667 strlen(pname) + 1); 668 } 669 } 670 671 if ((opflags & (BGRNDNOW | ISBGRND)) == BGRNDNOW) { 672 warnx("Mount %s:%s, backgrounding", 673 hostp, spec); 674 opflags |= ISBGRND; 675 if (daemon(0, 0) != 0) 676 err(1, "daemon"); 677 } 678 679 ret = TRYRET_LOCALERR; 680 for (;;) { 681 /* 682 * Try each entry returned by getaddrinfo(). Note the 683 * occurrence of remote errors by setting `remoteerr'. 684 */ 685 remoteerr = 0; 686 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 687 if ((ai->ai_family == AF_INET6) && 688 (opflags & OF_NOINET6)) 689 continue; 690 if ((ai->ai_family == AF_INET) && 691 (opflags & OF_NOINET4)) 692 continue; 693 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 694 iovlen); 695 if (ret == TRYRET_SUCCESS) 696 break; 697 if (ret != TRYRET_LOCALERR) 698 remoteerr = 1; 699 if ((opflags & ISBGRND) == 0) 700 fprintf(stderr, "%s\n", errstr); 701 } 702 if (ret == TRYRET_SUCCESS) 703 break; 704 705 /* Exit if all errors were local. */ 706 if (!remoteerr) 707 exit(1); 708 709 /* 710 * If retrycnt == 0, we are to keep retrying forever. 711 * Otherwise decrement it, and exit if it hits zero. 712 */ 713 if (retrycnt != 0 && --retrycnt == 0) 714 exit(1); 715 716 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 717 warnx("Cannot immediately mount %s:%s, backgrounding", 718 hostp, spec); 719 opflags |= ISBGRND; 720 if (daemon(0, 0) != 0) 721 err(1, "daemon"); 722 } 723 /* 724 * If rtm_ifinfo_sleep() returns non-zero, don't count 725 * that as a retry attempt. 726 */ 727 if (rtm_ifinfo_sleep(60) && retrycnt != 0) 728 retrycnt++; 729 } 730 freeaddrinfo(ai_nfs); 731 732 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 733 734 *specp = spec; 735 *hostpp = hostp; 736 return (1); 737 } 738 739 /* 740 * Try to set up the NFS arguments according to the address 741 * family, protocol (and possibly port) specified in `ai'. 742 * 743 * Returns TRYRET_SUCCESS if successful, or: 744 * TRYRET_TIMEOUT The server did not respond. 745 * TRYRET_REMOTEERR The server reported an error. 746 * TRYRET_LOCALERR Local failure. 747 * 748 * In all error cases, *errstr will be set to a statically-allocated string 749 * describing the error. 750 */ 751 static enum tryret 752 nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 753 struct iovec **iov, int *iovlen) 754 { 755 static char errbuf[256]; 756 struct sockaddr_storage nfs_ss; 757 struct netbuf nfs_nb; 758 struct nfhret nfhret; 759 struct timeval try; 760 struct rpc_err rpcerr; 761 CLIENT *clp; 762 struct netconfig *nconf, *nconf_mnt; 763 const char *netid, *netid_mnt, *secname; 764 int doconnect, nfsvers, mntvers, sotype; 765 enum clnt_stat clntstat; 766 enum mountmode trymntmode; 767 768 sotype = 0; 769 trymntmode = mountmode; 770 errbuf[0] = '\0'; 771 *errstr = errbuf; 772 773 if (nfsproto == IPPROTO_TCP) 774 sotype = SOCK_STREAM; 775 else if (nfsproto == IPPROTO_UDP) 776 sotype = SOCK_DGRAM; 777 778 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 779 snprintf(errbuf, sizeof errbuf, 780 "af %d sotype %d not supported", ai->ai_family, sotype); 781 return (TRYRET_LOCALERR); 782 } 783 if ((nconf = getnetconf_cached(netid)) == NULL) { 784 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 785 return (TRYRET_LOCALERR); 786 } 787 /* The RPCPROG_MNT netid may be different. */ 788 if (mnttcp_ok) { 789 netid_mnt = netid; 790 nconf_mnt = nconf; 791 } else { 792 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 793 == NULL) { 794 snprintf(errbuf, sizeof errbuf, 795 "af %d sotype SOCK_DGRAM not supported", 796 ai->ai_family); 797 return (TRYRET_LOCALERR); 798 } 799 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 800 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 801 nc_sperror()); 802 return (TRYRET_LOCALERR); 803 } 804 } 805 806 tryagain: 807 if (trymntmode == V4) { 808 nfsvers = 4; 809 mntvers = 3; /* Workaround for GCC. */ 810 } else if (trymntmode == V2) { 811 nfsvers = 2; 812 mntvers = 1; 813 } else { 814 nfsvers = 3; 815 mntvers = 3; 816 } 817 818 if (portspec != NULL) { 819 /* `ai' contains the complete nfsd sockaddr. */ 820 nfs_nb.buf = ai->ai_addr; 821 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 822 } else { 823 /* Ask the remote rpcbind. */ 824 nfs_nb.buf = &nfs_ss; 825 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 826 827 if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb, 828 hostp)) { 829 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 830 trymntmode == ANY) { 831 trymntmode = V2; 832 goto tryagain; 833 } 834 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 835 netid, hostp, spec, 836 clnt_spcreateerror("RPCPROG_NFS")); 837 return (returncode(rpc_createerr.cf_stat, 838 &rpc_createerr.cf_error)); 839 } 840 } 841 842 /* Check that the server (nfsd) responds on the port we have chosen. */ 843 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers, 844 0, 0); 845 if (clp == NULL) { 846 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 847 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 848 return (returncode(rpc_createerr.cf_stat, 849 &rpc_createerr.cf_error)); 850 } 851 if (sotype == SOCK_DGRAM && noconn == 0) { 852 /* 853 * Use connect(), to match what the kernel does. This 854 * catches cases where the server responds from the 855 * wrong source address. 856 */ 857 doconnect = 1; 858 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 859 clnt_destroy(clp); 860 snprintf(errbuf, sizeof errbuf, 861 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 862 spec); 863 return (TRYRET_LOCALERR); 864 } 865 } 866 867 try.tv_sec = 10; 868 try.tv_usec = 0; 869 clntstat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 870 (xdrproc_t)xdr_void, NULL, try); 871 if (clntstat != RPC_SUCCESS) { 872 if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 873 clnt_destroy(clp); 874 trymntmode = V2; 875 goto tryagain; 876 } 877 clnt_geterr(clp, &rpcerr); 878 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 879 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 880 clnt_destroy(clp); 881 return (returncode(clntstat, &rpcerr)); 882 } 883 clnt_destroy(clp); 884 885 /* 886 * For NFSv4, there is no mount protocol. 887 */ 888 if (trymntmode == V4) { 889 /* 890 * Store the server address in nfsargsp, making 891 * sure to copy any locally allocated structures. 892 */ 893 addrlen = nfs_nb.len; 894 addr = malloc(addrlen); 895 if (addr == NULL) 896 err(1, "malloc"); 897 bcopy(nfs_nb.buf, addr, addrlen); 898 899 build_iovec(iov, iovlen, "addr", addr, addrlen); 900 secname = sec_num_to_name(secflavor); 901 if (secname != NULL) { 902 build_iovec(iov, iovlen, "sec", 903 __DECONST(void *, secname), (size_t)-1); 904 } 905 build_iovec(iov, iovlen, "nfsv4", NULL, 0); 906 build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); 907 908 return (TRYRET_SUCCESS); 909 } 910 911 /* 912 * malloc() and copy the address, so that it can be used for 913 * nfsargs below. 914 */ 915 addrlen = nfs_nb.len; 916 addr = malloc(addrlen); 917 if (addr == NULL) 918 err(1, "malloc"); 919 bcopy(nfs_nb.buf, addr, addrlen); 920 921 /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ 922 try.tv_sec = 10; 923 try.tv_usec = 0; 924 if (mntproto_port != 0) { 925 struct sockaddr *sad; 926 struct sockaddr_in *sin; 927 struct sockaddr_in6 *sin6; 928 929 sad = (struct sockaddr *)nfs_nb.buf; 930 switch (sad->sa_family) { 931 case AF_INET: 932 sin = (struct sockaddr_in *)nfs_nb.buf; 933 sin->sin_port = htons(mntproto_port); 934 break; 935 case AF_INET6: 936 sin6 = (struct sockaddr_in6 *)nfs_nb.buf; 937 sin6->sin6_port = htons(mntproto_port); 938 break; 939 default: 940 snprintf(errbuf, sizeof(errbuf), 941 "Mnt port bad addr family %d\n", sad->sa_family); 942 return (TRYRET_LOCALERR); 943 } 944 clp = clnt_tli_create(RPC_ANYFD, nconf_mnt, &nfs_nb, MOUNTPROG, 945 mntvers, 0, 0); 946 } else { 947 /* Get the Mount protocol port# via rpcbind. */ 948 clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); 949 } 950 if (clp == NULL) { 951 free(addr); 952 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 953 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 954 return (returncode(rpc_createerr.cf_stat, 955 &rpc_createerr.cf_error)); 956 } 957 clp->cl_auth = authsys_create_default(); 958 nfhret.auth = secflavor; 959 nfhret.vers = mntvers; 960 clntstat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, 961 (xdrproc_t)xdr_fh, &nfhret, try); 962 auth_destroy(clp->cl_auth); 963 if (clntstat != RPC_SUCCESS) { 964 free(addr); 965 if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 966 clnt_destroy(clp); 967 trymntmode = V2; 968 goto tryagain; 969 } 970 clnt_geterr(clp, &rpcerr); 971 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 972 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 973 clnt_destroy(clp); 974 return (returncode(clntstat, &rpcerr)); 975 } 976 clnt_destroy(clp); 977 978 if (nfhret.stat != 0) { 979 free(addr); 980 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 981 hostp, spec, strerror(nfhret.stat)); 982 return (TRYRET_REMOTEERR); 983 } 984 985 /* 986 * Store the filehandle and server address in nfsargsp, making 987 * sure to copy any locally allocated structures. 988 */ 989 fhsize = nfhret.fhsize; 990 fh = malloc(fhsize); 991 if (fh == NULL) 992 err(1, "malloc"); 993 bcopy(nfhret.nfh, fh, fhsize); 994 995 build_iovec(iov, iovlen, "addr", addr, addrlen); 996 build_iovec(iov, iovlen, "fh", fh, fhsize); 997 secname = sec_num_to_name(nfhret.auth); 998 if (secname) { 999 build_iovec(iov, iovlen, "sec", 1000 __DECONST(void *, secname), (size_t)-1); 1001 } 1002 if (nfsvers == 3) 1003 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 1004 1005 return (TRYRET_SUCCESS); 1006 } 1007 1008 /* 1009 * Catagorise a RPC return status and error into an `enum tryret' 1010 * return code. 1011 */ 1012 static enum tryret 1013 returncode(enum clnt_stat clntstat, struct rpc_err *rpcerr) 1014 { 1015 1016 switch (clntstat) { 1017 case RPC_TIMEDOUT: 1018 return (TRYRET_TIMEOUT); 1019 case RPC_PMAPFAILURE: 1020 case RPC_PROGNOTREGISTERED: 1021 case RPC_PROGVERSMISMATCH: 1022 /* XXX, these can be local or remote. */ 1023 case RPC_CANTSEND: 1024 case RPC_CANTRECV: 1025 return (TRYRET_REMOTEERR); 1026 case RPC_SYSTEMERROR: 1027 switch (rpcerr->re_errno) { 1028 case ETIMEDOUT: 1029 return (TRYRET_TIMEOUT); 1030 case ENOMEM: 1031 break; 1032 default: 1033 return (TRYRET_REMOTEERR); 1034 } 1035 /* FALLTHROUGH */ 1036 default: 1037 break; 1038 } 1039 return (TRYRET_LOCALERR); 1040 } 1041 1042 /* 1043 * Look up a netid based on an address family and socket type. 1044 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1045 * 1046 * XXX there should be a library function for this. 1047 */ 1048 static const char * 1049 netidbytype(int af, int sotype) 1050 { 1051 struct nc_protos *p; 1052 1053 for (p = nc_protos; p->netid != NULL; p++) { 1054 if (af != p->af || sotype != p->sotype) 1055 continue; 1056 return (p->netid); 1057 } 1058 return (NULL); 1059 } 1060 1061 /* 1062 * Look up a netconfig entry based on a netid, and cache the result so 1063 * that we don't need to remember to call freenetconfigent(). 1064 * 1065 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1066 * work on failure. 1067 */ 1068 static struct netconfig * 1069 getnetconf_cached(const char *netid) 1070 { 1071 static struct nc_entry { 1072 struct netconfig *nconf; 1073 struct nc_entry *next; 1074 } *head; 1075 struct nc_entry *p; 1076 struct netconfig *nconf; 1077 1078 for (p = head; p != NULL; p = p->next) 1079 if (strcmp(netid, p->nconf->nc_netid) == 0) 1080 return (p->nconf); 1081 1082 if ((nconf = getnetconfigent(netid)) == NULL) 1083 return (NULL); 1084 if ((p = malloc(sizeof(*p))) == NULL) 1085 err(1, "malloc"); 1086 p->nconf = nconf; 1087 p->next = head; 1088 head = p; 1089 1090 return (p->nconf); 1091 } 1092 1093 /* 1094 * xdr routines for mount rpc's 1095 */ 1096 static int 1097 xdr_dir(XDR *xdrsp, char *dirp) 1098 { 1099 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 1100 } 1101 1102 static int 1103 xdr_fh(XDR *xdrsp, struct nfhret *np) 1104 { 1105 int i; 1106 long auth, authcnt, authfnd = 0; 1107 1108 if (!xdr_u_long(xdrsp, &np->stat)) 1109 return (0); 1110 if (np->stat) 1111 return (1); 1112 switch (np->vers) { 1113 case 1: 1114 np->fhsize = NFS_FHSIZE; 1115 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE)); 1116 case 3: 1117 if (!xdr_long(xdrsp, &np->fhsize)) 1118 return (0); 1119 if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE) 1120 return (0); 1121 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1122 return (0); 1123 if (!xdr_long(xdrsp, &authcnt)) 1124 return (0); 1125 for (i = 0; i < authcnt; i++) { 1126 if (!xdr_long(xdrsp, &auth)) 1127 return (0); 1128 if (np->auth == -1) { 1129 np->auth = auth; 1130 authfnd++; 1131 } else if (auth == np->auth) { 1132 authfnd++; 1133 } 1134 } 1135 /* 1136 * Some servers, such as DEC's OSF/1 return a nil authenticator 1137 * list to indicate RPCAUTH_UNIX. 1138 */ 1139 if (authcnt == 0 && np->auth == -1) 1140 np->auth = AUTH_SYS; 1141 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1142 np->stat = EAUTH; 1143 return (1); 1144 } 1145 return (0); 1146 } 1147 1148 static void 1149 usage(void) 1150 { 1151 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1152 "usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1153 " [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1154 " [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1155 " rhost:path node"); 1156 exit(1); 1157 } 1158