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