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 **, 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 *host, *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, &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