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