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 * @(#) Copyright (c) 1992, 1993, 1994 The Regents of the University of California. All rights reserved. 33 * @(#)mount_nfs.c 8.11 (Berkeley) 5/4/95 34 * $FreeBSD: src/sbin/mount_nfs/mount_nfs.c,v 1.36.2.6 2003/05/13 14:45:40 trhodes Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/mount.h> 39 #include <sys/socket.h> 40 #include <sys/stat.h> 41 #include <sys/syslog.h> 42 43 #include <rpc/rpc.h> 44 #include <rpc/pmap_clnt.h> 45 #include <rpc/pmap_prot.h> 46 47 #include <vfs/nfs/rpcv2.h> 48 #include <vfs/nfs/nfsproto.h> 49 #include <vfs/nfs/nfs.h> 50 51 #include <arpa/inet.h> 52 53 #include <ctype.h> 54 #include <err.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <mntopts.h> 58 #include <netdb.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <sysexits.h> 63 #include <unistd.h> 64 65 #include "mounttab.h" 66 67 #define ALTF_BG 0x1 68 #define ALTF_NOCONN 0x2 69 #define ALTF_DUMBTIMR 0x4 70 #define ALTF_INTR 0x8 71 /* 0x10 was for ALTF_KERB */ 72 #define ALTF_NFSV3 0x20 73 #define ALTF_RDIRPLUS 0x40 74 #define ALTF_CACHE 0x80 75 #define ALTF_RESVPORT 0x100 76 #define ALTF_SEQPACKET 0x200 77 #define ALTF_UNUSED400 0x400 78 #define ALTF_SOFT 0x800 79 #define ALTF_TCP 0x1000 80 #define ALTF_PORT 0x2000 81 #define ALTF_NFSV2 0x4000 82 #define ALTF_ACREGMIN 0x8000 83 #define ALTF_ACREGMAX 0x10000 84 #define ALTF_ACDIRMIN 0x20000 85 #define ALTF_ACDIRMAX 0x40000 86 #define ALTF_RETRYCNT 0x80000 87 88 struct mntopt mopts[] = { 89 MOPT_STDOPTS, 90 MOPT_FORCE, 91 MOPT_UPDATE, 92 MOPT_ASYNC, 93 { "bg", 0, ALTF_BG, 1 }, 94 { "conn", 1, ALTF_NOCONN, 1 }, 95 { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, 96 { "intr", 0, ALTF_INTR, 1 }, 97 { "nfsv3", 0, ALTF_NFSV3, 1 }, 98 { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, 99 { "mntudp", 1, ALTF_TCP, 1 }, 100 { "resvport", 0, ALTF_RESVPORT, 1 }, 101 { "soft", 0, ALTF_SOFT, 1 }, 102 { "tcp", 0, ALTF_TCP, 1 }, 103 { "udp", 1, ALTF_TCP, 1 }, 104 { "port=", 0, ALTF_PORT, 1 }, 105 { "nfsv2", 0, ALTF_NFSV2, 1 }, 106 { "acregmin=", 0, ALTF_ACREGMIN, 1 }, 107 { "acregmax=", 0, ALTF_ACREGMAX, 1 }, 108 { "acdirmin=", 0, ALTF_ACDIRMIN, 1 }, 109 { "acdirmax=", 0, ALTF_ACDIRMAX, 1 }, 110 { "cache", 0, ALTF_CACHE, 1 }, 111 { "retrycnt=", 0, ALTF_RETRYCNT, 1 }, 112 MOPT_NULL 113 }; 114 115 struct nfs_args nfsdefargs = { 116 NFS_ARGSVERSION, 117 NULL, 118 sizeof (struct sockaddr_in), 119 0, 120 0, 121 NULL, 122 0, 123 NFSMNT_RESVPORT, 124 NFS_WSIZE, 125 NFS_RSIZE, 126 NFS_READDIRSIZE, 127 10, 128 NFS_RETRANS, 129 NFS_MAXGRPS, 130 NFS_DEFRAHEAD, 131 0, 132 NFS_DEADTHRESH, 133 NULL, 134 /* args version 4 */ 135 NFS_MINATTRTIMO, 136 NFS_MAXATTRTIMO, 137 NFS_MINDIRATTRTIMO, 138 NFS_MAXDIRATTRTIMO, 139 }; 140 141 /* Table for af,sotype -> netid conversions. */ 142 struct nc_protos { 143 const char *netid; 144 int af; 145 int sotype; 146 } nc_protos[] = { 147 {"udp", AF_INET, SOCK_DGRAM}, 148 {"tcp", AF_INET, SOCK_STREAM}, 149 {"udp6", AF_INET6, SOCK_DGRAM}, 150 {"tcp6", AF_INET6, SOCK_STREAM}, 151 {NULL, 0, 0} 152 }; 153 154 struct nfhret { 155 u_long stat; 156 long vers; 157 long auth; 158 long fhsize; 159 u_char nfh[NFSX_V3FHMAX]; 160 }; 161 #define BGRND 1 162 #define ISBGRND 2 163 int retrycnt = -1; 164 int opflags = 0; 165 int nfsproto; 166 char *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 167 enum mountmode { 168 ANY, 169 V2, 170 V3 171 } mountmode = ANY; 172 173 /* Return codes for nfs_tryproto. */ 174 enum tryret { 175 TRYRET_SUCCESS, 176 TRYRET_TIMEOUT, /* No response received. */ 177 TRYRET_REMOTEERR, /* Error received from remote server. */ 178 TRYRET_LOCALERR /* Local failure. */ 179 }; 180 181 #if 0 182 void set_rpc_maxgrouplist(int); 183 #endif 184 static struct netconfig *getnetconf_cached(const char *); 185 static const char *netidbytype(int, int); 186 static int getnfsargs(char *, struct nfs_args *); 187 static void usage(void) __dead2; 188 static int xdr_dir(XDR *, char *); 189 static int xdr_fh(XDR *, struct nfhret *); 190 static enum tryret 191 nfs_tryproto(struct nfs_args *, struct addrinfo *, 192 char *, char *, char **); 193 static enum tryret 194 returncode(enum clnt_stat, struct rpc_err *); 195 196 /* 197 * Used to set mount flags with getmntopts. Call with dir=TRUE to 198 * initialize altflags from the current mount flags. Call with 199 * dir=FALSE to update mount flags with the new value of altflags after 200 * the call to getmntopts. 201 */ 202 static void 203 set_flags(int* altflags, int* nfsflags, int dir) 204 { 205 #define F2(af, nf) \ 206 if (dir) { \ 207 if (*nfsflags & NFSMNT_##nf) \ 208 *altflags |= ALTF_##af; \ 209 else \ 210 *altflags &= ~ALTF_##af; \ 211 } else { \ 212 if (*altflags & ALTF_##af) \ 213 *nfsflags |= NFSMNT_##nf; \ 214 else \ 215 *nfsflags &= ~NFSMNT_##nf; \ 216 } 217 #define F(f) F2(f,f) 218 219 F(NOCONN); 220 F(DUMBTIMR); 221 F2(INTR, INT); 222 F(RDIRPLUS); 223 F(RESVPORT); 224 F(SOFT); 225 F(ACREGMIN); 226 F(ACREGMAX); 227 F(ACDIRMIN); 228 F(ACDIRMAX); 229 #ifdef NFSMNT_CACHE 230 F(CACHE); 231 #endif 232 F(RETRYCNT); 233 234 #undef F 235 #undef F2 236 } 237 238 int 239 main(int argc, char **argv) 240 { 241 int c; 242 struct nfs_args *nfsargsp; 243 struct nfs_args nfsargs; 244 int mntflags, altflags, num; 245 char *name, *p, *spec; 246 char mntpath[MAXPATHLEN]; 247 struct vfsconf vfc; 248 int error = 0; 249 250 mntflags = 0; 251 altflags = ALTF_TCP | ALTF_RDIRPLUS; 252 nfsargs = nfsdefargs; 253 nfsargsp = &nfsargs; 254 if (altflags & ALTF_TCP) { 255 nfsproto = IPPROTO_TCP; 256 nfsargsp->sotype = SOCK_STREAM; 257 } else { 258 nfsproto = IPPROTO_UDP; 259 nfsargsp->sotype = SOCK_DGRAM; 260 } 261 while ((c = getopt(argc, argv, 262 "23a:bcdD:g:I:ilNo:PR:r:sTt:w:x:U")) != -1) 263 switch (c) { 264 case '2': 265 mountmode = V2; 266 break; 267 case '3': 268 mountmode = V3; 269 break; 270 case 'a': 271 num = strtol(optarg, &p, 10); 272 if (*p || num < 0) 273 errx(1, "illegal -a value -- %s", optarg); 274 nfsargsp->readahead = num; 275 nfsargsp->flags |= NFSMNT_READAHEAD; 276 break; 277 case 'b': 278 opflags |= BGRND; 279 break; 280 case 'c': 281 nfsargsp->flags |= NFSMNT_NOCONN; 282 break; 283 case 'D': 284 num = strtol(optarg, &p, 10); 285 if (*p || num <= 0) 286 errx(1, "illegal -D value -- %s", optarg); 287 nfsargsp->deadthresh = num; 288 nfsargsp->flags |= NFSMNT_DEADTHRESH; 289 break; 290 case 'd': 291 nfsargsp->flags |= NFSMNT_DUMBTIMR; 292 break; 293 #if 0 /* XXXX */ 294 case 'g': 295 num = strtol(optarg, &p, 10); 296 if (*p || num <= 0) 297 errx(1, "illegal -g value -- %s", optarg); 298 set_rpc_maxgrouplist(num); 299 nfsargsp->maxgrouplist = num; 300 nfsargsp->flags |= NFSMNT_MAXGRPS; 301 break; 302 #endif 303 case 'I': 304 num = strtol(optarg, &p, 10); 305 if (*p || num <= 0) 306 errx(1, "illegal -I value -- %s", optarg); 307 nfsargsp->readdirsize = num; 308 nfsargsp->flags |= NFSMNT_READDIRSIZE; 309 break; 310 case 'i': 311 nfsargsp->flags |= NFSMNT_INT; 312 break; 313 case 'l': 314 nfsargsp->flags |= NFSMNT_RDIRPLUS; 315 break; 316 case 'N': 317 nfsargsp->flags &= ~NFSMNT_RESVPORT; 318 break; 319 case 'o': 320 set_flags(&altflags, &nfsargsp->flags, TRUE); 321 altflags |= ALTF_RDIRPLUS; 322 if (nfsproto == IPPROTO_TCP) 323 altflags |= ALTF_TCP; 324 if (mountmode == V2) 325 altflags |= ALTF_NFSV2; 326 else if (mountmode == V3) 327 altflags |= ALTF_NFSV3; 328 getmntopts(optarg, mopts, &mntflags, &altflags); 329 set_flags(&altflags, &nfsargsp->flags, FALSE); 330 /* 331 * Handle altflags which don't map directly to 332 * mount flags. 333 */ 334 if(altflags & ALTF_BG) 335 opflags |= BGRND; 336 if (altflags & ALTF_TCP) { 337 nfsproto = IPPROTO_TCP; 338 nfsargsp->sotype = SOCK_STREAM; 339 } else { 340 nfsproto = IPPROTO_UDP; 341 nfsargsp->sotype = SOCK_DGRAM; 342 } 343 if(altflags & ALTF_PORT) { 344 /* 345 * XXX Converting from a string to an int 346 * and back again is silly, and we should 347 * allow /etc/services names. 348 */ 349 asprintf(&portspec, "%d", 350 atoi(strstr(optarg, "port=") + 5)); 351 if (portspec == NULL) 352 err(1, "asprintf"); 353 } 354 mountmode = ANY; 355 if(altflags & ALTF_NFSV2) 356 mountmode = V2; 357 if(altflags & ALTF_NFSV3) 358 mountmode = V3; 359 if(altflags & ALTF_ACREGMIN) 360 nfsargsp->acregmin = atoi(strstr(optarg, 361 "acregmin=") + 9); 362 if(altflags & ALTF_ACREGMAX) 363 nfsargsp->acregmax = atoi(strstr(optarg, 364 "acregmax=") + 9); 365 if(altflags & ALTF_ACDIRMIN) 366 nfsargsp->acdirmin = atoi(strstr(optarg, 367 "acdirmin=") + 9); 368 if(altflags & ALTF_ACDIRMAX) 369 nfsargsp->acdirmax = atoi(strstr(optarg, 370 "acdirmax=") + 9); 371 if(altflags & ALTF_RETRYCNT) { 372 num = atoi(strstr(optarg, "retrycnt=") + 9); 373 if (num < 0) 374 errx(1, "illegal -o retrycnt value -- %s", optarg); 375 retrycnt = num; 376 } 377 break; 378 case 'P': 379 /* obsolete for NFSMNT_RESVPORT, now default */ 380 break; 381 case 'R': 382 num = strtol(optarg, &p, 10); 383 if (*p || num < 0) 384 errx(1, "illegal -R value -- %s", optarg); 385 retrycnt = num; 386 break; 387 case 'r': 388 num = strtol(optarg, &p, 10); 389 if (*p || num <= 0) 390 errx(1, "illegal -r value -- %s", optarg); 391 nfsargsp->rsize = num; 392 nfsargsp->flags |= NFSMNT_RSIZE; 393 break; 394 case 's': 395 nfsargsp->flags |= NFSMNT_SOFT; 396 break; 397 case 'T': 398 nfsargsp->sotype = SOCK_STREAM; 399 nfsproto = IPPROTO_TCP; 400 altflags |= ALTF_TCP; 401 break; 402 case 't': 403 num = strtol(optarg, &p, 10); 404 if (*p || num <= 0) 405 errx(1, "illegal -t value -- %s", optarg); 406 nfsargsp->timeo = num; 407 nfsargsp->flags |= NFSMNT_TIMEO; 408 break; 409 case 'w': 410 num = strtol(optarg, &p, 10); 411 if (*p || num <= 0) 412 errx(1, "illegal -w value -- %s", optarg); 413 nfsargsp->wsize = num; 414 nfsargsp->flags |= NFSMNT_WSIZE; 415 break; 416 case 'x': 417 num = strtol(optarg, &p, 10); 418 if (*p || num <= 0) 419 errx(1, "illegal -x value -- %s", optarg); 420 nfsargsp->retrans = num; 421 nfsargsp->flags |= NFSMNT_RETRANS; 422 break; 423 case 'U': 424 nfsargsp->sotype = SOCK_DGRAM; 425 nfsproto = IPPROTO_UDP; 426 altflags &= ~ALTF_TCP; 427 break; 428 default: 429 usage(); 430 break; 431 } 432 argc -= optind; 433 argv += optind; 434 435 if (argc != 2) { 436 usage(); 437 /* NOTREACHED */ 438 } 439 440 spec = *argv++; 441 name = *argv; 442 443 if (retrycnt == -1) 444 /* The default is to keep retrying forever. */ 445 retrycnt = 0; 446 if (!getnfsargs(spec, nfsargsp)) 447 exit(1); 448 449 /* resolve the mountpoint with realpath(3) */ 450 checkpath(name, mntpath); 451 452 error = getvfsbyname("nfs", &vfc); 453 if (error && vfsisloadable("nfs")) { 454 if(vfsload("nfs")) 455 err(EX_OSERR, "vfsload(nfs)"); 456 endvfsent(); /* clear cache */ 457 error = getvfsbyname("nfs", &vfc); 458 } 459 if (error) 460 errx(EX_OSERR, "nfs filesystem is not available"); 461 462 if (mount(vfc.vfc_name, mntpath, mntflags, nfsargsp)) 463 err(1, "%s", mntpath); 464 exit(0); 465 } 466 467 static int 468 getnfsargs(char *spec, struct nfs_args *nfsargsp) 469 { 470 struct addrinfo hints, *ai_nfs, *ai; 471 enum tryret ret; 472 int ecode, speclen, remoteerr; 473 char *hostp, *delimp, *errstr; 474 size_t len; 475 static char nam[MNAMELEN + 1]; 476 477 if ((delimp = strrchr(spec, ':')) != NULL) { 478 hostp = spec; 479 spec = delimp + 1; 480 } else if ((delimp = strrchr(spec, '@')) != NULL) { 481 warnx("path@server syntax is deprecated, use server:path"); 482 hostp = delimp + 1; 483 } else { 484 warnx("no <host>:<dirpath> nfs-name"); 485 return (0); 486 } 487 *delimp = '\0'; 488 489 /* 490 * If there has been a trailing slash at mounttime it seems 491 * that some mountd implementations fail to remove the mount 492 * entries from their mountlist while unmounting. 493 */ 494 for (speclen = strlen(spec); 495 speclen > 1 && spec[speclen - 1] == '/'; 496 speclen--) 497 spec[speclen - 1] = '\0'; 498 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 499 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 500 return (0); 501 } 502 /* Make both '@' and ':' notations equal */ 503 if (*hostp != '\0') { 504 len = strlen(hostp); 505 memmove(nam, hostp, len); 506 nam[len] = ':'; 507 memmove(nam + len + 1, spec, speclen); 508 nam[len + speclen + 1] = '\0'; 509 } 510 511 /* 512 * Handle an internet host address. 513 */ 514 memset(&hints, 0, sizeof hints); 515 hints.ai_flags = AI_NUMERICHOST; 516 hints.ai_socktype = nfsargsp->sotype; 517 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 518 hints.ai_flags = 0; 519 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 520 != 0) { 521 if (portspec == NULL) 522 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 523 else 524 errx(1, "%s:%s: %s", hostp, portspec, 525 gai_strerror(ecode)); 526 return (0); 527 } 528 } 529 530 ret = TRYRET_LOCALERR; 531 for (;;) { 532 /* 533 * Try each entry returned by getaddrinfo(). Note the 534 * occurence of remote errors by setting `remoteerr'. 535 */ 536 remoteerr = 0; 537 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 538 ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr); 539 if (ret == TRYRET_SUCCESS) 540 break; 541 if (ret != TRYRET_LOCALERR) 542 remoteerr = 1; 543 if ((opflags & ISBGRND) == 0) 544 fprintf(stderr, "%s\n", errstr); 545 } 546 if (ret == TRYRET_SUCCESS) 547 break; 548 549 /* Exit if all errors were local. */ 550 if (!remoteerr) 551 exit(1); 552 553 /* 554 * If retrycnt == 0, we are to keep retrying forever. 555 * Otherwise decrement it, and exit if it hits zero. 556 */ 557 if (retrycnt != 0 && --retrycnt == 0) 558 exit(1); 559 560 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 561 warnx("Cannot immediately mount %s:%s, backgrounding", 562 hostp, spec); 563 opflags |= ISBGRND; 564 if (daemon(0, 0) != 0) 565 err(1, "daemon"); 566 } 567 sleep(60); 568 } 569 freeaddrinfo(ai_nfs); 570 nfsargsp->hostname = nam; 571 /* Add mounted filesystem to PATH_MOUNTTAB */ 572 if (!add_mtab(hostp, spec)) 573 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 574 return (1); 575 } 576 577 /* 578 * Try to set up the NFS arguments according to the address 579 * family, protocol (and possibly port) specified in `ai'. 580 * 581 * Returns TRYRET_SUCCESS if successful, or: 582 * TRYRET_TIMEOUT The server did not respond. 583 * TRYRET_REMOTEERR The server reported an error. 584 * TRYRET_LOCALERR Local failure. 585 * 586 * In all error cases, *errstr will be set to a statically-allocated string 587 * describing the error. 588 */ 589 static enum tryret 590 nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp, 591 char *spec, char **errstr) 592 { 593 static char errbuf[256]; 594 struct sockaddr_storage nfs_ss; 595 struct netbuf nfs_nb; 596 struct nfhret nfhret; 597 struct timeval try; 598 struct rpc_err rpcerr; 599 CLIENT *clp; 600 struct netconfig *nconf, *nconf_mnt; 601 const char *netid, *netid_mnt; 602 int doconnect, nfsvers, mntvers; 603 enum clnt_stat status; 604 enum mountmode trymntmode; 605 606 trymntmode = mountmode; 607 errbuf[0] = '\0'; 608 *errstr = errbuf; 609 610 if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) { 611 snprintf(errbuf, sizeof errbuf, 612 "af %d sotype %d not supported", ai->ai_family, 613 nfsargsp->sotype); 614 return (TRYRET_LOCALERR); 615 } 616 if ((nconf = getnetconf_cached(netid)) == NULL) { 617 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 618 return (TRYRET_LOCALERR); 619 } 620 621 netid_mnt = netid; 622 nconf_mnt = nconf; 623 624 tryagain: 625 if (trymntmode == V2) { 626 nfsvers = 2; 627 mntvers = 1; 628 } else { 629 nfsvers = 3; 630 mntvers = 3; 631 } 632 633 if (portspec != NULL) { 634 /* `ai' contains the complete nfsd sockaddr. */ 635 nfs_nb.buf = ai->ai_addr; 636 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 637 } else { 638 /* Ask the remote rpcbind. */ 639 nfs_nb.buf = &nfs_ss; 640 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 641 642 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 643 hostp)) { 644 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 645 trymntmode == ANY) { 646 trymntmode = V2; 647 goto tryagain; 648 } 649 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 650 netid, hostp, spec, 651 clnt_spcreateerror("RPCPROG_NFS")); 652 return (returncode(rpc_createerr.cf_stat, 653 &rpc_createerr.cf_error)); 654 } 655 } 656 657 /* Check that the server (nfsd) responds on the port we have chosen. */ 658 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers, 659 0, 0); 660 if (clp == NULL) { 661 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 662 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 663 return (returncode(rpc_createerr.cf_stat, 664 &rpc_createerr.cf_error)); 665 } 666 if (nfsargsp->sotype == SOCK_DGRAM && 667 !(nfsargsp->flags & NFSMNT_NOCONN)) { 668 /* 669 * Use connect(), to match what the kernel does. This 670 * catches cases where the server responds from the 671 * wrong source address. 672 */ 673 doconnect = 1; 674 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 675 clnt_destroy(clp); 676 snprintf(errbuf, sizeof errbuf, 677 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 678 spec); 679 return (TRYRET_LOCALERR); 680 } 681 } 682 683 try.tv_sec = 10; 684 try.tv_usec = 0; 685 status = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 686 (xdrproc_t)xdr_void, NULL, try); 687 if (status != RPC_SUCCESS) { 688 if (status == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 689 clnt_destroy(clp); 690 trymntmode = V2; 691 goto tryagain; 692 } 693 clnt_geterr(clp, &rpcerr); 694 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 695 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 696 clnt_destroy(clp); 697 return (returncode(status, &rpcerr)); 698 } 699 clnt_destroy(clp); 700 701 /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ 702 try.tv_sec = 10; 703 try.tv_usec = 0; 704 clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt); 705 if (clp == NULL) { 706 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 707 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 708 return (returncode(rpc_createerr.cf_stat, 709 &rpc_createerr.cf_error)); 710 } 711 clp->cl_auth = authsys_create_default(); 712 nfhret.auth = RPCAUTH_UNIX; 713 nfhret.vers = mntvers; 714 status = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec, 715 (xdrproc_t)xdr_fh, &nfhret, try); 716 auth_destroy(clp->cl_auth); 717 if (status != RPC_SUCCESS) { 718 if (status == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 719 clnt_destroy(clp); 720 trymntmode = V2; 721 goto tryagain; 722 } 723 clnt_geterr(clp, &rpcerr); 724 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 725 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 726 clnt_destroy(clp); 727 return (returncode(status, &rpcerr)); 728 } 729 clnt_destroy(clp); 730 731 if (nfhret.stat != 0) { 732 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 733 hostp, spec, strerror(nfhret.stat)); 734 return (TRYRET_REMOTEERR); 735 } 736 737 /* 738 * Store the filehandle and server address in nfsargsp, making 739 * sure to copy any locally allocated structures. 740 */ 741 nfsargsp->addrlen = nfs_nb.len; 742 nfsargsp->addr = malloc(nfsargsp->addrlen); 743 nfsargsp->fhsize = nfhret.fhsize; 744 nfsargsp->fh = malloc(nfsargsp->fhsize); 745 if (nfsargsp->addr == NULL || nfsargsp->fh == NULL) 746 err(1, "malloc"); 747 bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen); 748 bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize); 749 750 if (nfsvers == 3) 751 nfsargsp->flags |= NFSMNT_NFSV3; 752 else 753 nfsargsp->flags &= ~NFSMNT_NFSV3; 754 755 return (TRYRET_SUCCESS); 756 } 757 758 759 /* 760 * Catagorise a RPC return status and error into an `enum tryret' 761 * return code. 762 */ 763 static enum tryret 764 returncode(enum clnt_stat status, struct rpc_err *rpcerr) 765 { 766 switch (status) { 767 case RPC_TIMEDOUT: 768 return (TRYRET_TIMEOUT); 769 case RPC_PMAPFAILURE: 770 case RPC_PROGNOTREGISTERED: 771 case RPC_PROGVERSMISMATCH: 772 /* XXX, these can be local or remote. */ 773 case RPC_CANTSEND: 774 case RPC_CANTRECV: 775 return (TRYRET_REMOTEERR); 776 case RPC_SYSTEMERROR: 777 switch (rpcerr->re_errno) { 778 case ETIMEDOUT: 779 return (TRYRET_TIMEOUT); 780 case ENOMEM: 781 break; 782 default: 783 return (TRYRET_REMOTEERR); 784 } 785 /* FALLTHROUGH */ 786 default: 787 break; 788 } 789 return (TRYRET_LOCALERR); 790 } 791 792 /* 793 * Look up a netid based on an address family and socket type. 794 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 795 * 796 * XXX there should be a library function for this. 797 */ 798 static const char * 799 netidbytype(int af, int sotype) 800 { 801 struct nc_protos *p; 802 803 for (p = nc_protos; p->netid != NULL; p++) { 804 if (af != p->af || sotype != p->sotype) 805 continue; 806 return (p->netid); 807 } 808 return (NULL); 809 } 810 811 /* 812 * Look up a netconfig entry based on a netid, and cache the result so 813 * that we don't need to remember to call freenetconfigent(). 814 * 815 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 816 * work on failure. 817 */ 818 static struct netconfig * 819 getnetconf_cached(const char *netid) 820 { 821 static struct nc_entry { 822 struct netconfig *nconf; 823 struct nc_entry *next; 824 } *head; 825 struct nc_entry *p; 826 struct netconfig *nconf; 827 828 for (p = head; p != NULL; p = p->next) 829 if (strcmp(netid, p->nconf->nc_netid) == 0) 830 return (p->nconf); 831 832 if ((nconf = getnetconfigent(netid)) == NULL) 833 return (NULL); 834 if ((p = malloc(sizeof(*p))) == NULL) 835 err(1, "malloc"); 836 p->nconf = nconf; 837 p->next = head; 838 head = p; 839 840 return (p->nconf); 841 } 842 843 /* 844 * xdr routines for mount rpc's 845 */ 846 static int 847 xdr_dir(XDR *xdrsp, char *dirp) 848 { 849 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 850 } 851 852 static int 853 xdr_fh(XDR *xdrsp, struct nfhret *np) 854 { 855 int i; 856 long auth, authcnt, authfnd = 0; 857 858 if (!xdr_u_long(xdrsp, &np->stat)) 859 return (0); 860 if (np->stat) 861 return (1); 862 switch (np->vers) { 863 case 1: 864 np->fhsize = NFSX_V2FH; 865 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); 866 case 3: 867 if (!xdr_long(xdrsp, &np->fhsize)) 868 return (0); 869 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) 870 return (0); 871 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 872 return (0); 873 if (!xdr_long(xdrsp, &authcnt)) 874 return (0); 875 for (i = 0; i < authcnt; i++) { 876 if (!xdr_long(xdrsp, &auth)) 877 return (0); 878 if (auth == np->auth) 879 authfnd++; 880 } 881 /* 882 * Some servers, such as DEC's OSF/1 return a nil authenticator 883 * list to indicate RPCAUTH_UNIX. 884 */ 885 if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX)) 886 np->stat = EAUTH; 887 return (1); 888 } 889 return (0); 890 } 891 892 static void 893 usage(void) 894 { 895 fprintf(stderr, "%s\n%s\n%s\n%s\n", 896 "usage: mount_nfs [-23NPTUbcdils] [-D deadthresh] [-I readdirsize]", 897 " [-R retrycnt] [-a maxreadahead]", 898 " [-g maxgroups] [-o options] [-r readsize]", 899 " [-t timeout] [-w writesize] [-x retrans] rhost:path node"); 900 exit(1); 901 } 902