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