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