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