1 /* 2 * copyright (c) 2003 3 * the regents of the university of michigan 4 * all rights reserved 5 * 6 * permission is granted to use, copy, create derivative works and redistribute 7 * this software and such derivative works for any purpose, so long as the name 8 * of the university of michigan is not used in any advertising or publicity 9 * pertaining to the use or distribution of this software without specific, 10 * written prior authorization. if the above copyright notice or any other 11 * identification of the university of michigan is included in any copy of any 12 * portion of this software, then the disclaimer below must also be included. 13 * 14 * this software is provided as is, without representation from the university 15 * of michigan as to its fitness for any purpose, and without warranty by the 16 * university of michigan of any kind, either express or implied, including 17 * without limitation the implied warranties of merchantability and fitness for 18 * a particular purpose. the regents of the university of michigan shall not be 19 * liable for any damages, including special, indirect, incidental, or 20 * consequential damages, with respect to any claim arising out of or in 21 * connection with the use of the software, even if it has been or is hereafter 22 * advised of the possibility of such damages. 23 */ 24 25 /* 26 * Copyright (c) 1992, 1993, 1994 27 * The Regents of the University of California. All rights reserved. 28 * 29 * This code is derived from software contributed to Berkeley by 30 * Rick Macklem at The University of Guelph. 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 4. Neither the name of the University nor the names of its contributors 41 * may be used to endorse or promote products derived from this software 42 * without specific prior written permission. 43 * 44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54 * SUCH DAMAGE. 55 */ 56 57 #if 0 58 #ifndef lint 59 static const char copyright[] = 60 "@(#) Copyright (c) 1992, 1993, 1994\n\ 61 The Regents of the University of California. All rights reserved.\n"; 62 #endif /* not lint */ 63 64 #ifndef lint 65 static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 66 #endif /* not lint */ 67 #endif 68 #include <sys/cdefs.h> 69 __FBSDID("$FreeBSD$"); 70 71 #include <sys/param.h> 72 #include <sys/mount.h> 73 #include <sys/socket.h> 74 #include <sys/stat.h> 75 #include <sys/syslog.h> 76 #include <sys/uio.h> 77 78 #include <rpc/rpc.h> 79 #include <rpc/pmap_clnt.h> 80 #include <rpc/pmap_prot.h> 81 82 #include <nfs/rpcv2.h> 83 #include <nfs/nfsproto.h> 84 #include <nfsclient/nfs.h> 85 #include <nfsclient/nfsargs.h> 86 87 #include <arpa/inet.h> 88 89 #include <ctype.h> 90 #include <err.h> 91 #include <errno.h> 92 #include <fcntl.h> 93 #include <netdb.h> 94 #include <stdio.h> 95 #include <stdlib.h> 96 #include <string.h> 97 #include <strings.h> 98 #include <sysexits.h> 99 #include <unistd.h> 100 101 #include "mntopts.h" 102 #include "mounttab.h" 103 104 #define ALTF_BG 0x1 105 #define ALTF_NOCONN 0x2 106 #define ALTF_DUMBTIMR 0x4 107 #define ALTF_INTR 0x8 108 #define ALTF_NFSV3 0x20 109 #define ALTF_RDIRPLUS 0x40 110 #define ALTF_MNTUDP 0x80 111 #define ALTF_RESVPORT 0x100 112 #define ALTF_SEQPACKET 0x200 113 #define ALTF_SOFT 0x800 114 #define ALTF_TCP 0x1000 115 #define ALTF_PORT 0x2000 116 #define ALTF_NFSV2 0x4000 117 #define ALTF_ACREGMIN 0x8000 118 #define ALTF_ACREGMAX 0x10000 119 #define ALTF_ACDIRMIN 0x20000 120 #define ALTF_ACDIRMAX 0x40000 121 #define ALTF_NOLOCKD 0x80000 122 #define ALTF_NOINET4 0x100000 123 #define ALTF_NOINET6 0x200000 124 125 struct mntopt mopts[] = { 126 MOPT_STDOPTS, 127 MOPT_FORCE, 128 MOPT_UPDATE, 129 MOPT_ASYNC, 130 { "bg", 0, ALTF_BG, 1 }, 131 { "conn", 1, ALTF_NOCONN, 1 }, 132 { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, 133 { "intr", 0, ALTF_INTR, 1 }, 134 { "nfsv3", 0, ALTF_NFSV3, 1 }, 135 { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, 136 { "mntudp", 0, ALTF_MNTUDP, 1 }, 137 { "resvport", 0, ALTF_RESVPORT, 1 }, 138 { "soft", 0, ALTF_SOFT, 1 }, 139 { "tcp", 0, ALTF_TCP, 1 }, 140 { "port=", 0, ALTF_PORT, 1 }, 141 { "nfsv2", 0, ALTF_NFSV2, 1 }, 142 { "acregmin=", 0, ALTF_ACREGMIN, 1 }, 143 { "acregmax=", 0, ALTF_ACREGMAX, 1 }, 144 { "acdirmin=", 0, ALTF_ACDIRMIN, 1 }, 145 { "acdirmax=", 0, ALTF_ACDIRMAX, 1 }, 146 { "lockd", 1, ALTF_NOLOCKD, 1 }, 147 { "inet4", 1, ALTF_NOINET4, 1 }, 148 { "inet6", 1, ALTF_NOINET6, 1 }, 149 MOPT_END 150 }; 151 152 struct nfs_args nfsdefargs = { 153 NFS_ARGSVERSION, 154 NULL, 155 sizeof (struct sockaddr_in), 156 SOCK_DGRAM, 157 0, 158 NULL, 159 0, 160 NFSMNT_RESVPORT, 161 NFS_WSIZE, 162 NFS_RSIZE, 163 NFS_READDIRSIZE, 164 10, 165 NFS_RETRANS, 166 NFS_MAXGRPS, 167 NFS_DEFRAHEAD, 168 0, /* was: NQ_DEFLEASE */ 169 NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 170 NULL, 171 /* args version 4 */ 172 NFS_MINATTRTIMO, 173 NFS_MAXATTRTIMO, 174 NFS_MINDIRATTRTIMO, 175 NFS_MAXDIRATTRTIMO, 176 }; 177 178 /* Table for af,sotype -> netid conversions. */ 179 struct nc_protos { 180 char *netid; 181 int af; 182 int sotype; 183 } nc_protos[] = { 184 {"udp", AF_INET, SOCK_DGRAM}, 185 {"tcp", AF_INET, SOCK_STREAM}, 186 {"udp6", AF_INET6, SOCK_DGRAM}, 187 {"tcp6", AF_INET6, SOCK_STREAM}, 188 {NULL, 0, 0} 189 }; 190 191 struct nfhret { 192 u_long stat; 193 long vers; 194 long auth; 195 long fhsize; 196 u_char nfh[NFSX_V3FHMAX]; 197 }; 198 #define BGRND 1 199 #define ISBGRND 2 200 #define OF_NOINET4 4 201 #define OF_NOINET6 8 202 int retrycnt = -1; 203 int opflags = 0; 204 int nfsproto = IPPROTO_UDP; 205 int mnttcp_ok = 1; 206 char *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 207 enum mountmode { 208 ANY, 209 V2, 210 V3, 211 V4 212 } mountmode = ANY; 213 214 /* Return codes for nfs_tryproto. */ 215 enum tryret { 216 TRYRET_SUCCESS, 217 TRYRET_TIMEOUT, /* No response received. */ 218 TRYRET_REMOTEERR, /* Error received from remote server. */ 219 TRYRET_LOCALERR /* Local failure. */ 220 }; 221 222 int getnfsargs(char *, struct nfs_args *); 223 int getnfs4args(char *, struct nfs_args *); 224 /* void set_rpc_maxgrouplist(int); */ 225 struct netconfig *getnetconf_cached(const char *netid); 226 char *netidbytype(int af, int sotype); 227 void usage(void) __dead2; 228 int xdr_dir(XDR *, char *); 229 int xdr_fh(XDR *, struct nfhret *); 230 enum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, 231 char *hostp, char *spec, char **errstr); 232 enum tryret nfs4_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, 233 char *hostp, char *spec, char **errstr); 234 enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 235 236 /* 237 * Used to set mount flags with getmntopts. Call with dir=TRUE to 238 * initialize altflags from the current mount flags. Call with 239 * dir=FALSE to update mount flags with the new value of altflags after 240 * the call to getmntopts. 241 */ 242 static void 243 set_flags(int* altflags, int* nfsflags, int dir) 244 { 245 #define F2(af, nf) \ 246 if (dir) { \ 247 if (*nfsflags & NFSMNT_##nf) \ 248 *altflags |= ALTF_##af; \ 249 else \ 250 *altflags &= ~ALTF_##af; \ 251 } else { \ 252 if (*altflags & ALTF_##af) \ 253 *nfsflags |= NFSMNT_##nf; \ 254 else \ 255 *nfsflags &= ~NFSMNT_##nf; \ 256 } 257 #define F(f) F2(f,f) 258 259 F(NOCONN); 260 F(DUMBTIMR); 261 F2(INTR, INT); 262 F(RDIRPLUS); 263 F(RESVPORT); 264 F(SOFT); 265 F(ACREGMIN); 266 F(ACREGMAX); 267 F(ACDIRMIN); 268 F(ACDIRMAX); 269 F(NOLOCKD); 270 271 #undef F 272 #undef F2 273 } 274 275 int 276 main(int argc, char *argv[]) 277 { 278 int c; 279 struct nfs_args *nfsargsp; 280 struct nfs_args nfsargs; 281 struct iovec *iov; 282 int mntflags, altflags, num; 283 int iovlen; 284 char *name, *p, *spec, *fstype; 285 char mntpath[MAXPATHLEN], errmsg[255]; 286 287 mntflags = 0; 288 altflags = 0; 289 nfsargs = nfsdefargs; 290 nfsargsp = &nfsargs; 291 iov = NULL; 292 iovlen = 0; 293 memset(errmsg, 0, sizeof(errmsg)); 294 295 fstype = strrchr(argv[0], '_'); 296 if (fstype == NULL) 297 errx(EX_USAGE, "argv[0] must end in _fstype"); 298 299 ++fstype; 300 301 if (strcmp(fstype, "nfs4") == 0) { 302 nfsproto = IPPROTO_TCP; 303 portspec = "2049"; 304 nfsdefargs.sotype = SOCK_STREAM; 305 mountmode = V4; 306 } 307 308 while ((c = getopt(argc, argv, 309 "234a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1) 310 switch (c) { 311 case '2': 312 mountmode = V2; 313 break; 314 case '3': 315 mountmode = V3; 316 break; 317 case '4': 318 mountmode = V4; 319 fstype = "nfs4"; 320 break; 321 case 'a': 322 num = strtol(optarg, &p, 10); 323 if (*p || num < 0) 324 errx(1, "illegal -a value -- %s", optarg); 325 nfsargsp->readahead = num; 326 nfsargsp->flags |= NFSMNT_READAHEAD; 327 break; 328 case 'b': 329 opflags |= BGRND; 330 break; 331 case 'c': 332 nfsargsp->flags |= NFSMNT_NOCONN; 333 break; 334 case 'D': 335 num = strtol(optarg, &p, 10); 336 if (*p || num <= 0) 337 errx(1, "illegal -D value -- %s", optarg); 338 nfsargsp->deadthresh = num; 339 nfsargsp->flags |= NFSMNT_DEADTHRESH; 340 break; 341 case 'd': 342 nfsargsp->flags |= NFSMNT_DUMBTIMR; 343 break; 344 #if 0 /* XXXX */ 345 case 'g': 346 num = strtol(optarg, &p, 10); 347 if (*p || num <= 0) 348 errx(1, "illegal -g value -- %s", optarg); 349 set_rpc_maxgrouplist(num); 350 nfsargsp->maxgrouplist = num; 351 nfsargsp->flags |= NFSMNT_MAXGRPS; 352 break; 353 #endif 354 case 'I': 355 num = strtol(optarg, &p, 10); 356 if (*p || num <= 0) 357 errx(1, "illegal -I value -- %s", optarg); 358 nfsargsp->readdirsize = num; 359 nfsargsp->flags |= NFSMNT_READDIRSIZE; 360 break; 361 case 'i': 362 nfsargsp->flags |= NFSMNT_INT; 363 break; 364 case 'L': 365 nfsargsp->flags |= NFSMNT_NOLOCKD; 366 break; 367 case 'l': 368 nfsargsp->flags |= NFSMNT_RDIRPLUS; 369 break; 370 case 'N': 371 nfsargsp->flags &= ~NFSMNT_RESVPORT; 372 break; 373 case 'o': 374 altflags = 0; 375 set_flags(&altflags, &nfsargsp->flags, TRUE); 376 if (mountmode == V2) 377 altflags |= ALTF_NFSV2; 378 else if (mountmode == V3) 379 altflags |= ALTF_NFSV3; 380 getmntopts(optarg, mopts, &mntflags, &altflags); 381 set_flags(&altflags, &nfsargsp->flags, FALSE); 382 /* 383 * Handle altflags which don't map directly to 384 * mount flags. 385 */ 386 if (altflags & ALTF_BG) 387 opflags |= BGRND; 388 if (altflags & ALTF_NOINET4) 389 opflags |= OF_NOINET4; 390 if (altflags & ALTF_NOINET6) 391 opflags |= OF_NOINET6; 392 if (altflags & ALTF_MNTUDP) 393 mnttcp_ok = 0; 394 if (altflags & ALTF_TCP) { 395 nfsargsp->sotype = SOCK_STREAM; 396 nfsproto = IPPROTO_TCP; 397 } 398 if (altflags & ALTF_PORT) { 399 /* 400 * XXX Converting from a string to an int 401 * and back again is silly, and we should 402 * allow /etc/services names. 403 */ 404 p = strstr(optarg, "port="); 405 if (p) { 406 asprintf(&portspec, "%d", 407 atoi(p + 5)); 408 if (portspec == NULL) 409 err(1, "asprintf"); 410 } 411 } 412 mountmode = ANY; 413 if (altflags & ALTF_NFSV2) 414 mountmode = V2; 415 if (altflags & ALTF_NFSV3) 416 mountmode = V3; 417 if (altflags & ALTF_ACREGMIN) { 418 p = strstr(optarg, "acregmin="); 419 if (p) 420 nfsargsp->acregmin = atoi(p + 9); 421 } 422 if (altflags & ALTF_ACREGMAX) { 423 p = strstr(optarg, "acregmax="); 424 if (p) 425 nfsargsp->acregmax = atoi(p + 9); 426 } 427 if (altflags & ALTF_ACDIRMIN) { 428 p = strstr(optarg, "acdirmin="); 429 if (p) 430 nfsargsp->acdirmin = atoi(p + 9); 431 } 432 if (altflags & ALTF_ACDIRMAX) { 433 p = strstr(optarg, "acdirmax="); 434 if (p) 435 nfsargsp->acdirmax = atoi(p + 9); 436 } 437 break; 438 case 'P': 439 /* obsolete for NFSMNT_RESVPORT, now default */ 440 break; 441 case 'R': 442 num = strtol(optarg, &p, 10); 443 if (*p || num < 0) 444 errx(1, "illegal -R value -- %s", optarg); 445 retrycnt = num; 446 break; 447 case 'r': 448 num = strtol(optarg, &p, 10); 449 if (*p || num <= 0) 450 errx(1, "illegal -r value -- %s", optarg); 451 nfsargsp->rsize = num; 452 nfsargsp->flags |= NFSMNT_RSIZE; 453 break; 454 case 's': 455 nfsargsp->flags |= NFSMNT_SOFT; 456 break; 457 case 'T': 458 nfsargsp->sotype = SOCK_STREAM; 459 nfsproto = IPPROTO_TCP; 460 break; 461 case 't': 462 num = strtol(optarg, &p, 10); 463 if (*p || num <= 0) 464 errx(1, "illegal -t value -- %s", optarg); 465 nfsargsp->timeo = num; 466 nfsargsp->flags |= NFSMNT_TIMEO; 467 break; 468 case 'w': 469 num = strtol(optarg, &p, 10); 470 if (*p || num <= 0) 471 errx(1, "illegal -w value -- %s", optarg); 472 nfsargsp->wsize = num; 473 nfsargsp->flags |= NFSMNT_WSIZE; 474 break; 475 case 'x': 476 num = strtol(optarg, &p, 10); 477 if (*p || num <= 0) 478 errx(1, "illegal -x value -- %s", optarg); 479 nfsargsp->retrans = num; 480 nfsargsp->flags |= NFSMNT_RETRANS; 481 break; 482 case 'U': 483 mnttcp_ok = 0; 484 nfsargsp->sotype = SOCK_DGRAM; 485 nfsproto = IPPROTO_UDP; 486 break; 487 default: 488 usage(); 489 break; 490 } 491 argc -= optind; 492 argv += optind; 493 494 if (argc != 2) { 495 usage(); 496 /* NOTREACHED */ 497 } 498 499 spec = *argv++; 500 name = *argv; 501 502 if (retrycnt == -1) 503 /* The default is to keep retrying forever. */ 504 retrycnt = 0; 505 506 if (mountmode == V4) { 507 if (!getnfs4args(spec, nfsargsp)) 508 exit(1); 509 } else { 510 if (!getnfsargs(spec, nfsargsp)) 511 exit(1); 512 } 513 514 /* resolve the mountpoint with realpath(3) */ 515 (void)checkpath(name, mntpath); 516 517 build_iovec(&iov, &iovlen, "nfs_args", nfsargsp, sizeof(*nfsargsp)); 518 build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); 519 build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); 520 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 521 522 if (nmount(iov, iovlen, mntflags)) 523 err(1, "%s, %s", mntpath, errmsg); 524 525 exit(0); 526 } 527 528 int 529 getnfsargs(char *spec, struct nfs_args *nfsargsp) 530 { 531 struct addrinfo hints, *ai_nfs, *ai; 532 enum tryret ret; 533 int ecode, speclen, remoteerr; 534 char *hostp, *delimp, *errstr; 535 size_t len; 536 static char nam[MNAMELEN + 1]; 537 538 if ((delimp = strrchr(spec, ':')) != NULL) { 539 hostp = spec; 540 spec = delimp + 1; 541 } else if ((delimp = strrchr(spec, '@')) != NULL) { 542 warnx("path@server syntax is deprecated, use server:path"); 543 hostp = delimp + 1; 544 } else { 545 warnx("no <host>:<dirpath> nfs-name"); 546 return (0); 547 } 548 *delimp = '\0'; 549 550 /* 551 * If there has been a trailing slash at mounttime it seems 552 * that some mountd implementations fail to remove the mount 553 * entries from their mountlist while unmounting. 554 */ 555 for (speclen = strlen(spec); 556 speclen > 1 && spec[speclen - 1] == '/'; 557 speclen--) 558 spec[speclen - 1] = '\0'; 559 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 560 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 561 return (0); 562 } 563 /* Make both '@' and ':' notations equal */ 564 if (*hostp != '\0') { 565 len = strlen(hostp); 566 memmove(nam, hostp, len); 567 nam[len] = ':'; 568 memmove(nam + len + 1, spec, speclen); 569 nam[len + speclen + 1] = '\0'; 570 } 571 572 /* 573 * Handle an internet host address. 574 */ 575 memset(&hints, 0, sizeof hints); 576 hints.ai_flags = AI_NUMERICHOST; 577 hints.ai_socktype = nfsargsp->sotype; 578 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 579 hints.ai_flags = 0; 580 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 581 != 0) { 582 if (portspec == NULL) 583 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 584 else 585 errx(1, "%s:%s: %s", hostp, portspec, 586 gai_strerror(ecode)); 587 return (0); 588 } 589 } 590 591 ret = TRYRET_LOCALERR; 592 for (;;) { 593 /* 594 * Try each entry returned by getaddrinfo(). Note the 595 * occurence of remote errors by setting `remoteerr'. 596 */ 597 remoteerr = 0; 598 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 599 if ((ai->ai_family == AF_INET6) && 600 (opflags & OF_NOINET6)) 601 continue; 602 if ((ai->ai_family == AF_INET) && 603 (opflags & OF_NOINET4)) 604 continue; 605 ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr); 606 if (ret == TRYRET_SUCCESS) 607 break; 608 if (ret != TRYRET_LOCALERR) 609 remoteerr = 1; 610 if ((opflags & ISBGRND) == 0) 611 fprintf(stderr, "%s\n", errstr); 612 } 613 if (ret == TRYRET_SUCCESS) 614 break; 615 616 /* Exit if all errors were local. */ 617 if (!remoteerr) 618 exit(1); 619 620 /* 621 * If retrycnt == 0, we are to keep retrying forever. 622 * Otherwise decrement it, and exit if it hits zero. 623 */ 624 if (retrycnt != 0 && --retrycnt == 0) 625 exit(1); 626 627 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 628 warnx("Cannot immediately mount %s:%s, backgrounding", 629 hostp, spec); 630 opflags |= ISBGRND; 631 if (daemon(0, 0) != 0) 632 err(1, "daemon"); 633 } 634 sleep(60); 635 } 636 freeaddrinfo(ai_nfs); 637 nfsargsp->hostname = nam; 638 /* Add mounted file system to PATH_MOUNTTAB */ 639 if (!add_mtab(hostp, spec)) 640 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 641 return (1); 642 } 643 644 645 int 646 getnfs4args(char *spec, struct nfs_args *nfsargsp) 647 { 648 struct addrinfo hints, *ai_nfs, *ai; 649 enum tryret ret; 650 int ecode, speclen, remoteerr; 651 char *hostp, *delimp, *errstr; 652 size_t len; 653 static char nam[MNAMELEN + 1]; 654 655 if ((delimp = strrchr(spec, ':')) != NULL) { 656 hostp = spec; 657 spec = delimp + 1; 658 } else if ((delimp = strrchr(spec, '@')) != NULL) { 659 warnx("path@server syntax is deprecated, use server:path"); 660 hostp = delimp + 1; 661 } else { 662 warnx("no <host>:<dirpath> nfs-name"); 663 return (0); 664 } 665 *delimp = '\0'; 666 667 /* 668 * If there has been a trailing slash at mounttime it seems 669 * that some mountd implementations fail to remove the mount 670 * entries from their mountlist while unmounting. 671 */ 672 for (speclen = strlen(spec); 673 speclen > 1 && spec[speclen - 1] == '/'; 674 speclen--) 675 spec[speclen - 1] = '\0'; 676 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 677 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 678 return (0); 679 } 680 /* Make both '@' and ':' notations equal */ 681 if (*hostp != '\0') { 682 len = strlen(hostp); 683 memmove(nam, hostp, len); 684 nam[len] = ':'; 685 memmove(nam + len + 1, spec, speclen); 686 nam[len + speclen + 1] = '\0'; 687 } 688 689 /* 690 * Handle an internet host address. 691 */ 692 memset(&hints, 0, sizeof hints); 693 hints.ai_flags = AI_NUMERICHOST; 694 hints.ai_socktype = nfsargsp->sotype; 695 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 696 hints.ai_flags = 0; 697 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 698 != 0) { 699 if (portspec == NULL) 700 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 701 else 702 errx(1, "%s:%s: %s", hostp, portspec, 703 gai_strerror(ecode)); 704 return (0); 705 } 706 } 707 708 ret = TRYRET_LOCALERR; 709 for (;;) { 710 /* 711 * Try each entry returned by getaddrinfo(). Note the 712 * occurence of remote errors by setting `remoteerr'. 713 */ 714 remoteerr = 0; 715 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 716 if ((ai->ai_family == AF_INET6) && 717 (opflags & OF_NOINET6)) 718 continue; 719 if ((ai->ai_family == AF_INET) && 720 (opflags & OF_NOINET4)) 721 continue; 722 ret = nfs4_tryproto(nfsargsp, ai, hostp, spec, &errstr); 723 if (ret == TRYRET_SUCCESS) 724 break; 725 if (ret != TRYRET_LOCALERR) 726 remoteerr = 1; 727 if ((opflags & ISBGRND) == 0) 728 fprintf(stderr, "%s\n", errstr); 729 } 730 if (ret == TRYRET_SUCCESS) 731 break; 732 733 /* Exit if all errors were local. */ 734 if (!remoteerr) 735 exit(1); 736 737 /* 738 * If retrycnt == 0, we are to keep retrying forever. 739 * Otherwise decrement it, and exit if it hits zero. 740 */ 741 if (retrycnt != 0 && --retrycnt == 0) 742 exit(1); 743 744 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 745 warnx("Cannot immediately mount %s:%s, backgrounding", 746 hostp, spec); 747 opflags |= ISBGRND; 748 if (daemon(0, 0) != 0) 749 err(1, "daemon"); 750 } 751 sleep(60); 752 } 753 freeaddrinfo(ai_nfs); 754 nfsargsp->hostname = nam; 755 /* Add mounted file system to PATH_MOUNTTAB */ 756 if (!add_mtab(hostp, spec)) 757 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 758 return (1); 759 } 760 761 /* 762 * Try to set up the NFS arguments according to the address 763 * family, protocol (and possibly port) specified in `ai'. 764 * 765 * Returns TRYRET_SUCCESS if successful, or: 766 * TRYRET_TIMEOUT The server did not respond. 767 * TRYRET_REMOTEERR The server reported an error. 768 * TRYRET_LOCALERR Local failure. 769 * 770 * In all error cases, *errstr will be set to a statically-allocated string 771 * describing the error. 772 */ 773 enum tryret 774 nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp, 775 char *spec, char **errstr) 776 { 777 static char errbuf[256]; 778 struct sockaddr_storage nfs_ss; 779 struct netbuf nfs_nb; 780 struct nfhret nfhret; 781 struct timeval try; 782 struct rpc_err rpcerr; 783 CLIENT *clp; 784 struct netconfig *nconf, *nconf_mnt; 785 char *netid, *netid_mnt; 786 int doconnect, nfsvers, mntvers; 787 enum clnt_stat stat; 788 enum mountmode trymntmode; 789 790 trymntmode = mountmode; 791 errbuf[0] = '\0'; 792 *errstr = errbuf; 793 794 if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) { 795 snprintf(errbuf, sizeof errbuf, 796 "af %d sotype %d not supported", ai->ai_family, 797 nfsargsp->sotype); 798 return (TRYRET_LOCALERR); 799 } 800 if ((nconf = getnetconf_cached(netid)) == NULL) { 801 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 802 return (TRYRET_LOCALERR); 803 } 804 /* The RPCPROG_MNT netid may be different. */ 805 if (mnttcp_ok) { 806 netid_mnt = netid; 807 nconf_mnt = nconf; 808 } else { 809 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 810 == NULL) { 811 snprintf(errbuf, sizeof errbuf, 812 "af %d sotype SOCK_DGRAM not supported", 813 ai->ai_family); 814 return (TRYRET_LOCALERR); 815 } 816 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 817 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 818 nc_sperror()); 819 return (TRYRET_LOCALERR); 820 } 821 } 822 823 tryagain: 824 if (trymntmode == V2) { 825 nfsvers = 2; 826 mntvers = 1; 827 } else { 828 nfsvers = 3; 829 mntvers = 3; 830 } 831 832 if (portspec != NULL) { 833 /* `ai' contains the complete nfsd sockaddr. */ 834 nfs_nb.buf = ai->ai_addr; 835 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 836 } else { 837 /* Ask the remote rpcbind. */ 838 nfs_nb.buf = &nfs_ss; 839 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 840 841 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 842 hostp)) { 843 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 844 trymntmode == ANY) { 845 trymntmode = V2; 846 goto tryagain; 847 } 848 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 849 netid, hostp, spec, 850 clnt_spcreateerror("RPCPROG_NFS")); 851 return (returncode(rpc_createerr.cf_stat, 852 &rpc_createerr.cf_error)); 853 } 854 } 855 856 /* Check that the server (nfsd) responds on the port we have chosen. */ 857 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers, 858 0, 0); 859 if (clp == NULL) { 860 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 861 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 862 return (returncode(rpc_createerr.cf_stat, 863 &rpc_createerr.cf_error)); 864 } 865 if (nfsargsp->sotype == SOCK_DGRAM && 866 !(nfsargsp->flags & NFSMNT_NOCONN)) { 867 /* 868 * Use connect(), to match what the kernel does. This 869 * catches cases where the server responds from the 870 * wrong source address. 871 */ 872 doconnect = 1; 873 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 874 clnt_destroy(clp); 875 snprintf(errbuf, sizeof errbuf, 876 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 877 spec); 878 return (TRYRET_LOCALERR); 879 } 880 } 881 882 try.tv_sec = 10; 883 try.tv_usec = 0; 884 stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 885 (xdrproc_t)xdr_void, NULL, 886 try); 887 if (stat != RPC_SUCCESS) { 888 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 889 clnt_destroy(clp); 890 trymntmode = V2; 891 goto tryagain; 892 } 893 clnt_geterr(clp, &rpcerr); 894 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 895 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 896 clnt_destroy(clp); 897 return (returncode(stat, &rpcerr)); 898 } 899 clnt_destroy(clp); 900 901 /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ 902 try.tv_sec = 10; 903 try.tv_usec = 0; 904 clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt); 905 if (clp == NULL) { 906 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 907 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 908 return (returncode(rpc_createerr.cf_stat, 909 &rpc_createerr.cf_error)); 910 } 911 clp->cl_auth = authsys_create_default(); 912 nfhret.auth = RPCAUTH_UNIX; 913 nfhret.vers = mntvers; 914 stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec, 915 (xdrproc_t)xdr_fh, &nfhret, 916 try); 917 auth_destroy(clp->cl_auth); 918 if (stat != RPC_SUCCESS) { 919 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 920 clnt_destroy(clp); 921 trymntmode = V2; 922 goto tryagain; 923 } 924 clnt_geterr(clp, &rpcerr); 925 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 926 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 927 clnt_destroy(clp); 928 return (returncode(stat, &rpcerr)); 929 } 930 clnt_destroy(clp); 931 932 if (nfhret.stat != 0) { 933 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 934 hostp, spec, strerror(nfhret.stat)); 935 return (TRYRET_REMOTEERR); 936 } 937 938 /* 939 * Store the filehandle and server address in nfsargsp, making 940 * sure to copy any locally allocated structures. 941 */ 942 nfsargsp->addrlen = nfs_nb.len; 943 nfsargsp->addr = malloc(nfsargsp->addrlen); 944 nfsargsp->fhsize = nfhret.fhsize; 945 nfsargsp->fh = malloc(nfsargsp->fhsize); 946 if (nfsargsp->addr == NULL || nfsargsp->fh == NULL) 947 err(1, "malloc"); 948 bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen); 949 bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize); 950 951 if (nfsvers == 3) 952 nfsargsp->flags |= NFSMNT_NFSV3; 953 else 954 nfsargsp->flags &= ~NFSMNT_NFSV3; 955 956 return (TRYRET_SUCCESS); 957 } 958 959 960 /* 961 * Try to set up the NFS arguments according to the address 962 * family, protocol (and possibly port) specified in `ai'. 963 * 964 * Returns TRYRET_SUCCESS if successful, or: 965 * TRYRET_TIMEOUT The server did not respond. 966 * TRYRET_REMOTEERR The server reported an error. 967 * TRYRET_LOCALERR Local failure. 968 * 969 * In all error cases, *errstr will be set to a statically-allocated string 970 * describing the error. 971 */ 972 enum tryret 973 nfs4_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp, 974 char *spec, char **errstr) 975 { 976 static char errbuf[256]; 977 struct sockaddr_storage nfs_ss; 978 struct netbuf nfs_nb; 979 struct netconfig *nconf; 980 char *netid; 981 int nfsvers; 982 983 errbuf[0] = '\0'; 984 *errstr = errbuf; 985 986 if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) { 987 snprintf(errbuf, sizeof errbuf, 988 "af %d sotype %d not supported", ai->ai_family, 989 nfsargsp->sotype); 990 return (TRYRET_LOCALERR); 991 } 992 if ((nconf = getnetconf_cached(netid)) == NULL) { 993 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 994 return (TRYRET_LOCALERR); 995 } 996 997 nfsvers = 4; 998 999 if (portspec != NULL && atoi(portspec) != 0) { 1000 /* `ai' contains the complete nfsd sockaddr. */ 1001 nfs_nb.buf = ai->ai_addr; 1002 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 1003 } else { 1004 /* Ask the remote rpcbind. */ 1005 nfs_nb.buf = &nfs_ss; 1006 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 1007 1008 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 1009 hostp)) { 1010 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 1011 netid, hostp, spec, 1012 clnt_spcreateerror("RPCPROG_NFS")); 1013 return (returncode(rpc_createerr.cf_stat, 1014 &rpc_createerr.cf_error)); 1015 } 1016 } 1017 1018 /* 1019 * Store the filehandle and server address in nfsargsp, making 1020 * sure to copy any locally allocated structures. 1021 */ 1022 nfsargsp->addrlen = nfs_nb.len; 1023 nfsargsp->addr = malloc(nfsargsp->addrlen); 1024 1025 if (nfsargsp->addr == NULL) 1026 err(1, "malloc"); 1027 bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen); 1028 1029 /* XXX hack */ 1030 nfsargsp->flags |= (NFSMNT_NFSV3 | NFSMNT_NFSV4); 1031 1032 return (TRYRET_SUCCESS); 1033 } 1034 1035 /* 1036 * Catagorise a RPC return status and error into an `enum tryret' 1037 * return code. 1038 */ 1039 enum tryret 1040 returncode(enum clnt_stat stat, struct rpc_err *rpcerr) 1041 { 1042 switch (stat) { 1043 case RPC_TIMEDOUT: 1044 return (TRYRET_TIMEOUT); 1045 case RPC_PMAPFAILURE: 1046 case RPC_PROGNOTREGISTERED: 1047 case RPC_PROGVERSMISMATCH: 1048 /* XXX, these can be local or remote. */ 1049 case RPC_CANTSEND: 1050 case RPC_CANTRECV: 1051 return (TRYRET_REMOTEERR); 1052 case RPC_SYSTEMERROR: 1053 switch (rpcerr->re_errno) { 1054 case ETIMEDOUT: 1055 return (TRYRET_TIMEOUT); 1056 case ENOMEM: 1057 break; 1058 default: 1059 return (TRYRET_REMOTEERR); 1060 } 1061 /* FALLTHROUGH */ 1062 default: 1063 break; 1064 } 1065 return (TRYRET_LOCALERR); 1066 } 1067 1068 /* 1069 * Look up a netid based on an address family and socket type. 1070 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1071 * 1072 * XXX there should be a library function for this. 1073 */ 1074 char * 1075 netidbytype(int af, int sotype) 1076 { 1077 struct nc_protos *p; 1078 1079 for (p = nc_protos; p->netid != NULL; p++) { 1080 if (af != p->af || sotype != p->sotype) 1081 continue; 1082 return (p->netid); 1083 } 1084 return (NULL); 1085 } 1086 1087 /* 1088 * Look up a netconfig entry based on a netid, and cache the result so 1089 * that we don't need to remember to call freenetconfigent(). 1090 * 1091 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1092 * work on failure. 1093 */ 1094 struct netconfig * 1095 getnetconf_cached(const char *netid) 1096 { 1097 static struct nc_entry { 1098 struct netconfig *nconf; 1099 struct nc_entry *next; 1100 } *head; 1101 struct nc_entry *p; 1102 struct netconfig *nconf; 1103 1104 for (p = head; p != NULL; p = p->next) 1105 if (strcmp(netid, p->nconf->nc_netid) == 0) 1106 return (p->nconf); 1107 1108 if ((nconf = getnetconfigent(netid)) == NULL) 1109 return (NULL); 1110 if ((p = malloc(sizeof(*p))) == NULL) 1111 err(1, "malloc"); 1112 p->nconf = nconf; 1113 p->next = head; 1114 head = p; 1115 1116 return (p->nconf); 1117 } 1118 1119 /* 1120 * xdr routines for mount rpc's 1121 */ 1122 int 1123 xdr_dir(XDR *xdrsp, char *dirp) 1124 { 1125 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 1126 } 1127 1128 int 1129 xdr_fh(XDR *xdrsp, struct nfhret *np) 1130 { 1131 int i; 1132 long auth, authcnt, authfnd = 0; 1133 1134 if (!xdr_u_long(xdrsp, &np->stat)) 1135 return (0); 1136 if (np->stat) 1137 return (1); 1138 switch (np->vers) { 1139 case 1: 1140 np->fhsize = NFSX_V2FH; 1141 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); 1142 case 3: 1143 if (!xdr_long(xdrsp, &np->fhsize)) 1144 return (0); 1145 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) 1146 return (0); 1147 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1148 return (0); 1149 if (!xdr_long(xdrsp, &authcnt)) 1150 return (0); 1151 for (i = 0; i < authcnt; i++) { 1152 if (!xdr_long(xdrsp, &auth)) 1153 return (0); 1154 if (auth == np->auth) 1155 authfnd++; 1156 } 1157 /* 1158 * Some servers, such as DEC's OSF/1 return a nil authenticator 1159 * list to indicate RPCAUTH_UNIX. 1160 */ 1161 if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX)) 1162 np->stat = EAUTH; 1163 return (1); 1164 }; 1165 return (0); 1166 } 1167 1168 void 1169 usage() 1170 { 1171 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1172 "usage: mount_nfs [-234bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1173 " [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1174 " [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1175 " rhost:path node"); 1176 exit(1); 1177 } 1178