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