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