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