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