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