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