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, "nmount: %s%s%s", mntpath, errmsg[0] ? ", " : "", 480 errmsg); 481 482 exit(0); 483 } 484 485 static int 486 sec_name_to_num(const char *sec) 487 { 488 if (!strcmp(sec, "krb5")) 489 return (RPCSEC_GSS_KRB5); 490 if (!strcmp(sec, "krb5i")) 491 return (RPCSEC_GSS_KRB5I); 492 if (!strcmp(sec, "krb5p")) 493 return (RPCSEC_GSS_KRB5P); 494 if (!strcmp(sec, "sys")) 495 return (AUTH_SYS); 496 return (-1); 497 } 498 499 static const char * 500 sec_num_to_name(int flavor) 501 { 502 switch (flavor) { 503 case RPCSEC_GSS_KRB5: 504 return ("krb5"); 505 case RPCSEC_GSS_KRB5I: 506 return ("krb5i"); 507 case RPCSEC_GSS_KRB5P: 508 return ("krb5p"); 509 case AUTH_SYS: 510 return ("sys"); 511 } 512 return (NULL); 513 } 514 515 static int 516 getnfsargs(char *spec, struct iovec **iov, int *iovlen) 517 { 518 struct addrinfo hints, *ai_nfs, *ai; 519 enum tryret ret; 520 int ecode, speclen, remoteerr, offset, have_bracket = 0; 521 char *hostp, *delimp, *errstr; 522 size_t len; 523 static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; 524 525 if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 526 *(delimp + 1) == ':') { 527 hostp = spec + 1; 528 spec = delimp + 2; 529 have_bracket = 1; 530 } else if ((delimp = strrchr(spec, ':')) != NULL) { 531 hostp = spec; 532 spec = delimp + 1; 533 } else if ((delimp = strrchr(spec, '@')) != NULL) { 534 warnx("path@server syntax is deprecated, use server:path"); 535 hostp = delimp + 1; 536 } else { 537 warnx("no <host>:<dirpath> nfs-name"); 538 return (0); 539 } 540 *delimp = '\0'; 541 542 /* 543 * If there has been a trailing slash at mounttime it seems 544 * that some mountd implementations fail to remove the mount 545 * entries from their mountlist while unmounting. 546 */ 547 for (speclen = strlen(spec); 548 speclen > 1 && spec[speclen - 1] == '/'; 549 speclen--) 550 spec[speclen - 1] = '\0'; 551 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 552 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 553 return (0); 554 } 555 /* Make both '@' and ':' notations equal */ 556 if (*hostp != '\0') { 557 len = strlen(hostp); 558 offset = 0; 559 if (have_bracket) 560 nam[offset++] = '['; 561 memmove(nam + offset, hostp, len); 562 if (have_bracket) 563 nam[len + offset++] = ']'; 564 nam[len + offset++] = ':'; 565 memmove(nam + len + offset, spec, speclen); 566 nam[len + speclen + offset] = '\0'; 567 } 568 569 /* 570 * Handle an internet host address. 571 */ 572 memset(&hints, 0, sizeof hints); 573 hints.ai_flags = AI_NUMERICHOST; 574 if (nfsproto == IPPROTO_TCP) 575 hints.ai_socktype = SOCK_STREAM; 576 else if (nfsproto == IPPROTO_UDP) 577 hints.ai_socktype = SOCK_DGRAM; 578 579 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 580 hints.ai_flags = AI_CANONNAME; 581 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 582 != 0) { 583 if (portspec == NULL) 584 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 585 else 586 errx(1, "%s:%s: %s", hostp, portspec, 587 gai_strerror(ecode)); 588 return (0); 589 } 590 591 /* 592 * For a Kerberized nfs mount where the "principal" 593 * argument has not been set, add it here. 594 */ 595 if (got_principal == 0 && secflavor != AUTH_SYS && 596 ai_nfs->ai_canonname != NULL) { 597 snprintf(pname, sizeof (pname), "nfs@%s", 598 ai_nfs->ai_canonname); 599 build_iovec(iov, iovlen, "principal", pname, 600 strlen(pname) + 1); 601 } 602 } 603 604 ret = TRYRET_LOCALERR; 605 for (;;) { 606 /* 607 * Try each entry returned by getaddrinfo(). Note the 608 * occurrence of remote errors by setting `remoteerr'. 609 */ 610 remoteerr = 0; 611 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 612 if ((ai->ai_family == AF_INET6) && 613 (opflags & OF_NOINET6)) 614 continue; 615 if ((ai->ai_family == AF_INET) && 616 (opflags & OF_NOINET4)) 617 continue; 618 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 619 iovlen); 620 if (ret == TRYRET_SUCCESS) 621 break; 622 if (ret != TRYRET_LOCALERR) 623 remoteerr = 1; 624 if ((opflags & ISBGRND) == 0) 625 fprintf(stderr, "%s\n", errstr); 626 } 627 if (ret == TRYRET_SUCCESS) 628 break; 629 630 /* Exit if all errors were local. */ 631 if (!remoteerr) 632 exit(1); 633 634 /* 635 * If retrycnt == 0, we are to keep retrying forever. 636 * Otherwise decrement it, and exit if it hits zero. 637 */ 638 if (retrycnt != 0 && --retrycnt == 0) 639 exit(1); 640 641 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 642 warnx("Cannot immediately mount %s:%s, backgrounding", 643 hostp, spec); 644 opflags |= ISBGRND; 645 if (daemon(0, 0) != 0) 646 err(1, "daemon"); 647 } 648 sleep(60); 649 } 650 freeaddrinfo(ai_nfs); 651 652 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 653 /* Add mounted file system to PATH_MOUNTTAB */ 654 if (!add_mtab(hostp, spec)) 655 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 656 return (1); 657 } 658 659 /* 660 * Try to set up the NFS arguments according to the address 661 * family, protocol (and possibly port) specified in `ai'. 662 * 663 * Returns TRYRET_SUCCESS if successful, or: 664 * TRYRET_TIMEOUT The server did not respond. 665 * TRYRET_REMOTEERR The server reported an error. 666 * TRYRET_LOCALERR Local failure. 667 * 668 * In all error cases, *errstr will be set to a statically-allocated string 669 * describing the error. 670 */ 671 static enum tryret 672 nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 673 struct iovec **iov, int *iovlen) 674 { 675 static char errbuf[256]; 676 struct sockaddr_storage nfs_ss; 677 struct netbuf nfs_nb; 678 struct nfhret nfhret; 679 struct timeval try; 680 struct rpc_err rpcerr; 681 CLIENT *clp; 682 struct netconfig *nconf, *nconf_mnt; 683 const char *netid, *netid_mnt, *secname; 684 int doconnect, nfsvers, mntvers, sotype; 685 enum clnt_stat clntstat; 686 enum mountmode trymntmode; 687 688 sotype = 0; 689 trymntmode = mountmode; 690 errbuf[0] = '\0'; 691 *errstr = errbuf; 692 693 if (nfsproto == IPPROTO_TCP) 694 sotype = SOCK_STREAM; 695 else if (nfsproto == IPPROTO_UDP) 696 sotype = SOCK_DGRAM; 697 698 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 699 snprintf(errbuf, sizeof errbuf, 700 "af %d sotype %d not supported", ai->ai_family, sotype); 701 return (TRYRET_LOCALERR); 702 } 703 if ((nconf = getnetconf_cached(netid)) == NULL) { 704 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 705 return (TRYRET_LOCALERR); 706 } 707 /* The RPCPROG_MNT netid may be different. */ 708 if (mnttcp_ok) { 709 netid_mnt = netid; 710 nconf_mnt = nconf; 711 } else { 712 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 713 == NULL) { 714 snprintf(errbuf, sizeof errbuf, 715 "af %d sotype SOCK_DGRAM not supported", 716 ai->ai_family); 717 return (TRYRET_LOCALERR); 718 } 719 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 720 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 721 nc_sperror()); 722 return (TRYRET_LOCALERR); 723 } 724 } 725 726 tryagain: 727 if (trymntmode == V4) { 728 nfsvers = 4; 729 mntvers = 3; /* Workaround for GCC. */ 730 } else if (trymntmode == V2) { 731 nfsvers = 2; 732 mntvers = 1; 733 } else { 734 nfsvers = 3; 735 mntvers = 3; 736 } 737 738 if (portspec != NULL) { 739 /* `ai' contains the complete nfsd sockaddr. */ 740 nfs_nb.buf = ai->ai_addr; 741 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 742 } else { 743 /* Ask the remote rpcbind. */ 744 nfs_nb.buf = &nfs_ss; 745 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 746 747 if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb, 748 hostp)) { 749 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 750 trymntmode == ANY) { 751 trymntmode = V2; 752 goto tryagain; 753 } 754 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 755 netid, hostp, spec, 756 clnt_spcreateerror("RPCPROG_NFS")); 757 return (returncode(rpc_createerr.cf_stat, 758 &rpc_createerr.cf_error)); 759 } 760 } 761 762 /* Check that the server (nfsd) responds on the port we have chosen. */ 763 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers, 764 0, 0); 765 if (clp == NULL) { 766 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 767 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 768 return (returncode(rpc_createerr.cf_stat, 769 &rpc_createerr.cf_error)); 770 } 771 if (sotype == SOCK_DGRAM && noconn == 0) { 772 /* 773 * Use connect(), to match what the kernel does. This 774 * catches cases where the server responds from the 775 * wrong source address. 776 */ 777 doconnect = 1; 778 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 779 clnt_destroy(clp); 780 snprintf(errbuf, sizeof errbuf, 781 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 782 spec); 783 return (TRYRET_LOCALERR); 784 } 785 } 786 787 try.tv_sec = 10; 788 try.tv_usec = 0; 789 clntstat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 790 (xdrproc_t)xdr_void, NULL, try); 791 if (clntstat != RPC_SUCCESS) { 792 if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 793 clnt_destroy(clp); 794 trymntmode = V2; 795 goto tryagain; 796 } 797 clnt_geterr(clp, &rpcerr); 798 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 799 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 800 clnt_destroy(clp); 801 return (returncode(clntstat, &rpcerr)); 802 } 803 clnt_destroy(clp); 804 805 /* 806 * For NFSv4, there is no mount protocol. 807 */ 808 if (trymntmode == V4) { 809 /* 810 * Store the server address in nfsargsp, making 811 * sure to copy any locally allocated structures. 812 */ 813 addrlen = nfs_nb.len; 814 addr = malloc(addrlen); 815 if (addr == NULL) 816 err(1, "malloc"); 817 bcopy(nfs_nb.buf, addr, addrlen); 818 819 build_iovec(iov, iovlen, "addr", addr, addrlen); 820 secname = sec_num_to_name(secflavor); 821 if (secname != NULL) { 822 build_iovec(iov, iovlen, "sec", 823 __DECONST(void *, secname), (size_t)-1); 824 } 825 build_iovec(iov, iovlen, "nfsv4", NULL, 0); 826 build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); 827 828 return (TRYRET_SUCCESS); 829 } 830 831 /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ 832 try.tv_sec = 10; 833 try.tv_usec = 0; 834 clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); 835 if (clp == NULL) { 836 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 837 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 838 return (returncode(rpc_createerr.cf_stat, 839 &rpc_createerr.cf_error)); 840 } 841 clp->cl_auth = authsys_create_default(); 842 nfhret.auth = secflavor; 843 nfhret.vers = mntvers; 844 clntstat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, 845 (xdrproc_t)xdr_fh, &nfhret, 846 try); 847 auth_destroy(clp->cl_auth); 848 if (clntstat != RPC_SUCCESS) { 849 if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 850 clnt_destroy(clp); 851 trymntmode = V2; 852 goto tryagain; 853 } 854 clnt_geterr(clp, &rpcerr); 855 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 856 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 857 clnt_destroy(clp); 858 return (returncode(clntstat, &rpcerr)); 859 } 860 clnt_destroy(clp); 861 862 if (nfhret.stat != 0) { 863 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 864 hostp, spec, strerror(nfhret.stat)); 865 return (TRYRET_REMOTEERR); 866 } 867 868 /* 869 * Store the filehandle and server address in nfsargsp, making 870 * sure to copy any locally allocated structures. 871 */ 872 addrlen = nfs_nb.len; 873 addr = malloc(addrlen); 874 fhsize = nfhret.fhsize; 875 fh = malloc(fhsize); 876 if (addr == NULL || fh == NULL) 877 err(1, "malloc"); 878 bcopy(nfs_nb.buf, addr, addrlen); 879 bcopy(nfhret.nfh, fh, fhsize); 880 881 build_iovec(iov, iovlen, "addr", addr, addrlen); 882 build_iovec(iov, iovlen, "fh", fh, fhsize); 883 secname = sec_num_to_name(nfhret.auth); 884 if (secname) { 885 build_iovec(iov, iovlen, "sec", 886 __DECONST(void *, secname), (size_t)-1); 887 } 888 if (nfsvers == 3) 889 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 890 891 return (TRYRET_SUCCESS); 892 } 893 894 /* 895 * Catagorise a RPC return status and error into an `enum tryret' 896 * return code. 897 */ 898 static enum tryret 899 returncode(enum clnt_stat clntstat, struct rpc_err *rpcerr) 900 { 901 902 switch (clntstat) { 903 case RPC_TIMEDOUT: 904 return (TRYRET_TIMEOUT); 905 case RPC_PMAPFAILURE: 906 case RPC_PROGNOTREGISTERED: 907 case RPC_PROGVERSMISMATCH: 908 /* XXX, these can be local or remote. */ 909 case RPC_CANTSEND: 910 case RPC_CANTRECV: 911 return (TRYRET_REMOTEERR); 912 case RPC_SYSTEMERROR: 913 switch (rpcerr->re_errno) { 914 case ETIMEDOUT: 915 return (TRYRET_TIMEOUT); 916 case ENOMEM: 917 break; 918 default: 919 return (TRYRET_REMOTEERR); 920 } 921 /* FALLTHROUGH */ 922 default: 923 break; 924 } 925 return (TRYRET_LOCALERR); 926 } 927 928 /* 929 * Look up a netid based on an address family and socket type. 930 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 931 * 932 * XXX there should be a library function for this. 933 */ 934 static const char * 935 netidbytype(int af, int sotype) 936 { 937 struct nc_protos *p; 938 939 for (p = nc_protos; p->netid != NULL; p++) { 940 if (af != p->af || sotype != p->sotype) 941 continue; 942 return (p->netid); 943 } 944 return (NULL); 945 } 946 947 /* 948 * Look up a netconfig entry based on a netid, and cache the result so 949 * that we don't need to remember to call freenetconfigent(). 950 * 951 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 952 * work on failure. 953 */ 954 static struct netconfig * 955 getnetconf_cached(const char *netid) 956 { 957 static struct nc_entry { 958 struct netconfig *nconf; 959 struct nc_entry *next; 960 } *head; 961 struct nc_entry *p; 962 struct netconfig *nconf; 963 964 for (p = head; p != NULL; p = p->next) 965 if (strcmp(netid, p->nconf->nc_netid) == 0) 966 return (p->nconf); 967 968 if ((nconf = getnetconfigent(netid)) == NULL) 969 return (NULL); 970 if ((p = malloc(sizeof(*p))) == NULL) 971 err(1, "malloc"); 972 p->nconf = nconf; 973 p->next = head; 974 head = p; 975 976 return (p->nconf); 977 } 978 979 /* 980 * xdr routines for mount rpc's 981 */ 982 static int 983 xdr_dir(XDR *xdrsp, char *dirp) 984 { 985 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 986 } 987 988 static int 989 xdr_fh(XDR *xdrsp, struct nfhret *np) 990 { 991 int i; 992 long auth, authcnt, authfnd = 0; 993 994 if (!xdr_u_long(xdrsp, &np->stat)) 995 return (0); 996 if (np->stat) 997 return (1); 998 switch (np->vers) { 999 case 1: 1000 np->fhsize = NFS_FHSIZE; 1001 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE)); 1002 case 3: 1003 if (!xdr_long(xdrsp, &np->fhsize)) 1004 return (0); 1005 if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE) 1006 return (0); 1007 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1008 return (0); 1009 if (!xdr_long(xdrsp, &authcnt)) 1010 return (0); 1011 for (i = 0; i < authcnt; i++) { 1012 if (!xdr_long(xdrsp, &auth)) 1013 return (0); 1014 if (np->auth == -1) { 1015 np->auth = auth; 1016 authfnd++; 1017 } else if (auth == np->auth) { 1018 authfnd++; 1019 } 1020 } 1021 /* 1022 * Some servers, such as DEC's OSF/1 return a nil authenticator 1023 * list to indicate RPCAUTH_UNIX. 1024 */ 1025 if (authcnt == 0 && np->auth == -1) 1026 np->auth = AUTH_SYS; 1027 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1028 np->stat = EAUTH; 1029 return (1); 1030 } 1031 return (0); 1032 } 1033 1034 static void 1035 usage(void) 1036 { 1037 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1038 "usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1039 " [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1040 " [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1041 " rhost:path node"); 1042 exit(1); 1043 } 1044