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 86 #include <arpa/inet.h> 87 88 #include <ctype.h> 89 #include <err.h> 90 #include <errno.h> 91 #include <fcntl.h> 92 #include <netdb.h> 93 #include <stdio.h> 94 #include <stdlib.h> 95 #include <string.h> 96 #include <strings.h> 97 #include <sysexits.h> 98 #include <unistd.h> 99 100 #include "mntopts.h" 101 #include "mounttab.h" 102 103 /* Table for af,sotype -> netid conversions. */ 104 struct nc_protos { 105 const char *netid; 106 int af; 107 int sotype; 108 } nc_protos[] = { 109 {"udp", AF_INET, SOCK_DGRAM}, 110 {"tcp", AF_INET, SOCK_STREAM}, 111 {"udp6", AF_INET6, SOCK_DGRAM}, 112 {"tcp6", AF_INET6, SOCK_STREAM}, 113 {NULL, 0, 0} 114 }; 115 116 struct nfhret { 117 u_long stat; 118 long vers; 119 long auth; 120 long fhsize; 121 u_char nfh[NFSX_V3FHMAX]; 122 }; 123 #define BGRND 1 124 #define ISBGRND 2 125 #define OF_NOINET4 4 126 #define OF_NOINET6 8 127 int retrycnt = -1; 128 int opflags = 0; 129 int nfsproto = IPPROTO_UDP; 130 int mnttcp_ok = 1; 131 int noconn = 0; 132 char *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 133 struct sockaddr *addr; 134 int addrlen = 0; 135 u_char *fh = NULL; 136 int fhsize = 0; 137 int secflavor = -1; 138 139 enum mountmode { 140 ANY, 141 V2, 142 V3, 143 V4 144 } mountmode = ANY; 145 146 /* Return codes for nfs_tryproto. */ 147 enum tryret { 148 TRYRET_SUCCESS, 149 TRYRET_TIMEOUT, /* No response received. */ 150 TRYRET_REMOTEERR, /* Error received from remote server. */ 151 TRYRET_LOCALERR /* Local failure. */ 152 }; 153 154 int fallback_mount(struct iovec *iov, int iovlen, int mntflags); 155 int sec_name_to_num(char *sec); 156 char *sec_num_to_name(int num); 157 int getnfsargs(char *, struct iovec **iov, int *iovlen); 158 int getnfs4args(char *, struct iovec **iov, int *iovlen); 159 /* void set_rpc_maxgrouplist(int); */ 160 struct netconfig *getnetconf_cached(const char *netid); 161 const char *netidbytype(int af, int sotype); 162 void usage(void) __dead2; 163 int xdr_dir(XDR *, char *); 164 int xdr_fh(XDR *, struct nfhret *); 165 enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, 166 char **errstr, struct iovec **iov, int *iovlen); 167 enum tryret nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec, 168 char **errstr); 169 enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 170 extern int getosreldate(void); 171 172 int 173 main(int argc, char *argv[]) 174 { 175 int c; 176 struct iovec *iov; 177 int mntflags, num, iovlen; 178 int osversion; 179 char *name, *p, *spec, *fstype; 180 char mntpath[MAXPATHLEN], errmsg[255]; 181 182 mntflags = 0; 183 iov = NULL; 184 iovlen = 0; 185 memset(errmsg, 0, sizeof(errmsg)); 186 187 fstype = strrchr(argv[0], '_'); 188 if (fstype == NULL) 189 errx(EX_USAGE, "argv[0] must end in _fstype"); 190 191 ++fstype; 192 193 if (strcmp(fstype, "nfs4") == 0) { 194 nfsproto = IPPROTO_TCP; 195 portspec = "2049"; 196 build_iovec(&iov, &iovlen, "tcp", NULL, 0); 197 mountmode = V4; 198 } 199 200 while ((c = getopt(argc, argv, 201 "234a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1) 202 switch (c) { 203 case '2': 204 mountmode = V2; 205 break; 206 case '3': 207 mountmode = V3; 208 break; 209 case '4': 210 mountmode = V4; 211 fstype = "nfs4"; 212 break; 213 case 'a': 214 printf("-a deprecated, use -o readhead=<value>\n"); 215 build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1); 216 break; 217 case 'b': 218 opflags |= BGRND; 219 break; 220 case 'c': 221 printf("-c deprecated, use -o noconn\n"); 222 build_iovec(&iov, &iovlen, "noconn", NULL, 0); 223 noconn = 1; 224 break; 225 case 'D': 226 printf("-D deprecated, use -o deadthresh=<value>\n"); 227 build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1); 228 break; 229 case 'd': 230 printf("-d deprecated, use -o dumbtimer"); 231 build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0); 232 break; 233 case 'g': 234 printf("-g deprecated, use -o maxgroups"); 235 num = strtol(optarg, &p, 10); 236 if (*p || num <= 0) 237 errx(1, "illegal -g value -- %s", optarg); 238 //set_rpc_maxgrouplist(num); 239 build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1); 240 break; 241 case 'I': 242 printf("-I deprecated, use -o readdirsize=<value>\n"); 243 build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1); 244 break; 245 case 'i': 246 printf("-i deprecated, use -o intr\n"); 247 build_iovec(&iov, &iovlen, "intr", NULL, 0); 248 break; 249 case 'L': 250 printf("-i deprecated, use -o nolockd\n"); 251 build_iovec(&iov, &iovlen, "nolockd", NULL, 0); 252 break; 253 case 'l': 254 printf("-l deprecated, -o rdirplus\n"); 255 build_iovec(&iov, &iovlen, "rdirplus", NULL, 0); 256 break; 257 case 'N': 258 printf("-N deprecated, do not specify -o resvport\n"); 259 break; 260 case 'o': { 261 int pass_flag_to_nmount; 262 char *opt = optarg; 263 while (opt) { 264 char *pval = NULL; 265 char *pnextopt = NULL; 266 char *val = ""; 267 pass_flag_to_nmount = 1; 268 pval = strchr(opt, '='); 269 pnextopt = strchr(opt, ','); 270 if (pval != NULL) { 271 *pval = '\0'; 272 val = pval + 1; 273 } 274 if (pnextopt) { 275 *pnextopt = '\0'; 276 pnextopt++; 277 } 278 if (strcmp(opt, "bg") == 0) { 279 opflags |= BGRND; 280 pass_flag_to_nmount=0; 281 } else if (strcmp(opt, "fg") == 0) { 282 /* same as not specifying -o bg */ 283 pass_flag_to_nmount=0; 284 } else if (strcmp(opt, "mntudp") == 0) { 285 mnttcp_ok = 0; 286 nfsproto = IPPROTO_UDP; 287 } else if (strcmp(opt, "udp") == 0) { 288 nfsproto = IPPROTO_UDP; 289 } else if (strcmp(opt, "tcp") == 0) { 290 nfsproto = IPPROTO_TCP; 291 } else if (strcmp(opt, "noinet4") == 0) { 292 pass_flag_to_nmount=0; 293 opflags |= OF_NOINET4; 294 } else if (strcmp(opt, "noinet6") == 0) { 295 pass_flag_to_nmount=0; 296 opflags |= OF_NOINET6; 297 } else if (strcmp(opt, "noconn") == 0) { 298 noconn = 1; 299 } else if (strcmp(opt, "nfsv2") == 0) { 300 pass_flag_to_nmount=0; 301 mountmode = V2; 302 } else if (strcmp(opt, "nfsv3") == 0) { 303 mountmode = V3; 304 } else if (strcmp(opt, "nfsv4") == 0) { 305 pass_flag_to_nmount=0; 306 mountmode = V4; 307 fstype = "nfs4"; 308 } else if (strcmp(opt, "port") == 0) { 309 pass_flag_to_nmount=0; 310 asprintf(&portspec, "%d", 311 atoi(val)); 312 if (portspec == NULL) 313 err(1, "asprintf"); 314 } else if (strcmp(opt, "sec") == 0) { 315 /* 316 * Don't add this option to 317 * the iovec yet - we will 318 * negotiate which sec flavor 319 * to use with the remote 320 * mountd. 321 */ 322 pass_flag_to_nmount=0; 323 secflavor = sec_name_to_num(val); 324 if (secflavor < 0) { 325 errx(1, 326 "illegal sec value -- %s", 327 val); 328 } 329 } else if (strcmp(opt, "retrycnt") == 0) { 330 pass_flag_to_nmount=0; 331 num = strtol(val, &p, 10); 332 if (*p || num < 0) 333 errx(1, "illegal retrycnt value -- %s", val); 334 retrycnt = num; 335 } else if (strcmp(opt, "maxgroups") == 0) { 336 num = strtol(val, &p, 10); 337 if (*p || num <= 0) 338 errx(1, "illegal maxgroups value -- %s", val); 339 //set_rpc_maxgrouplist(num); 340 } 341 if (pass_flag_to_nmount) 342 build_iovec(&iov, &iovlen, opt, val, 343 strlen(val) + 1); 344 opt = pnextopt; 345 } 346 } 347 break; 348 case 'P': 349 /* obsolete for -o noresvport now default */ 350 printf("-P deprecated, use -o noresvport\n"); 351 build_iovec(&iov, &iovlen, "noresvport", NULL, 0); 352 break; 353 case 'R': 354 printf("-R deprecated, use -o retrycnt=<retrycnt>\n"); 355 num = strtol(optarg, &p, 10); 356 if (*p || num < 0) 357 errx(1, "illegal -R value -- %s", optarg); 358 retrycnt = num; 359 break; 360 case 'r': 361 printf("-r deprecated, use -o rsize=<rsize>\n"); 362 build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1); 363 break; 364 case 's': 365 printf("-s deprecated, use -o soft\n"); 366 build_iovec(&iov, &iovlen, "soft", NULL, 0); 367 break; 368 case 'T': 369 nfsproto = IPPROTO_TCP; 370 printf("-T deprecated, use -o tcp\n"); 371 break; 372 case 't': 373 printf("-t deprecated, use -o timeout=<value>\n"); 374 build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1); 375 break; 376 case 'w': 377 printf("-w deprecated, use -o wsize=<value>\n"); 378 build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1); 379 break; 380 case 'x': 381 printf("-x deprecated, use -o retrans=<value>\n"); 382 build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1); 383 break; 384 case 'U': 385 printf("-U deprecated, use -o mntudp\n"); 386 mnttcp_ok = 0; 387 nfsproto = IPPROTO_UDP; 388 build_iovec(&iov, &iovlen, "mntudp", NULL, 0); 389 break; 390 default: 391 usage(); 392 break; 393 } 394 argc -= optind; 395 argv += optind; 396 397 if (argc != 2) { 398 usage(); 399 /* NOTREACHED */ 400 } 401 402 spec = *argv++; 403 name = *argv; 404 405 if (retrycnt == -1) 406 /* The default is to keep retrying forever. */ 407 retrycnt = 0; 408 409 if (mountmode == V4) { 410 if (!getnfs4args(spec, &iov, &iovlen)) 411 exit(1); 412 } else { 413 if (!getnfsargs(spec, &iov, &iovlen)) 414 exit(1); 415 } 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 >= 800048) { 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 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 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 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 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; 701 char *hostp, *delimp, *errstr; 702 size_t len; 703 static char nam[MNAMELEN + 1]; 704 705 if ((delimp = strrchr(spec, ':')) != NULL) { 706 hostp = spec; 707 spec = delimp + 1; 708 } else if ((delimp = strrchr(spec, '@')) != NULL) { 709 warnx("path@server syntax is deprecated, use server:path"); 710 hostp = delimp + 1; 711 } else { 712 warnx("no <host>:<dirpath> nfs-name"); 713 return (0); 714 } 715 *delimp = '\0'; 716 717 /* 718 * If there has been a trailing slash at mounttime it seems 719 * that some mountd implementations fail to remove the mount 720 * entries from their mountlist while unmounting. 721 */ 722 for (speclen = strlen(spec); 723 speclen > 1 && spec[speclen - 1] == '/'; 724 speclen--) 725 spec[speclen - 1] = '\0'; 726 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 727 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 728 return (0); 729 } 730 /* Make both '@' and ':' notations equal */ 731 if (*hostp != '\0') { 732 len = strlen(hostp); 733 memmove(nam, hostp, len); 734 nam[len] = ':'; 735 memmove(nam + len + 1, spec, speclen); 736 nam[len + speclen + 1] = '\0'; 737 } 738 739 /* 740 * Handle an internet host address. 741 */ 742 memset(&hints, 0, sizeof hints); 743 hints.ai_flags = AI_NUMERICHOST; 744 if (nfsproto == IPPROTO_TCP) 745 hints.ai_socktype = SOCK_STREAM; 746 else if (nfsproto == IPPROTO_UDP) 747 hints.ai_socktype = SOCK_DGRAM; 748 749 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 750 hints.ai_flags = 0; 751 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 752 != 0) { 753 if (portspec == NULL) 754 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 755 else 756 errx(1, "%s:%s: %s", hostp, portspec, 757 gai_strerror(ecode)); 758 return (0); 759 } 760 } 761 762 ret = TRYRET_LOCALERR; 763 for (;;) { 764 /* 765 * Try each entry returned by getaddrinfo(). Note the 766 * occurence of remote errors by setting `remoteerr'. 767 */ 768 remoteerr = 0; 769 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 770 if ((ai->ai_family == AF_INET6) && 771 (opflags & OF_NOINET6)) 772 continue; 773 if ((ai->ai_family == AF_INET) && 774 (opflags & OF_NOINET4)) 775 continue; 776 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 777 iovlen); 778 if (ret == TRYRET_SUCCESS) 779 break; 780 if (ret != TRYRET_LOCALERR) 781 remoteerr = 1; 782 if ((opflags & ISBGRND) == 0) 783 fprintf(stderr, "%s\n", errstr); 784 } 785 if (ret == TRYRET_SUCCESS) 786 break; 787 788 /* Exit if all errors were local. */ 789 if (!remoteerr) 790 exit(1); 791 792 /* 793 * If retrycnt == 0, we are to keep retrying forever. 794 * Otherwise decrement it, and exit if it hits zero. 795 */ 796 if (retrycnt != 0 && --retrycnt == 0) 797 exit(1); 798 799 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 800 warnx("Cannot immediately mount %s:%s, backgrounding", 801 hostp, spec); 802 opflags |= ISBGRND; 803 if (daemon(0, 0) != 0) 804 err(1, "daemon"); 805 } 806 sleep(60); 807 } 808 freeaddrinfo(ai_nfs); 809 810 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 811 /* Add mounted file system to PATH_MOUNTTAB */ 812 if (!add_mtab(hostp, spec)) 813 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 814 return (1); 815 } 816 817 818 int 819 getnfs4args(char *spec, struct iovec **iov, int *iovlen) 820 { 821 struct addrinfo hints, *ai_nfs, *ai; 822 enum tryret ret; 823 int ecode, speclen, remoteerr, sotype; 824 char *hostp, *delimp, *errstr; 825 size_t len; 826 static char nam[MNAMELEN + 1]; 827 828 if (nfsproto == IPPROTO_TCP) 829 sotype = SOCK_STREAM; 830 else if (nfsproto == IPPROTO_UDP) 831 sotype = SOCK_DGRAM; 832 833 834 if ((delimp = strrchr(spec, ':')) != NULL) { 835 hostp = spec; 836 spec = delimp + 1; 837 } else if ((delimp = strrchr(spec, '@')) != NULL) { 838 warnx("path@server syntax is deprecated, use server:path"); 839 hostp = delimp + 1; 840 } else { 841 warnx("no <host>:<dirpath> nfs-name"); 842 return (0); 843 } 844 *delimp = '\0'; 845 846 /* 847 * If there has been a trailing slash at mounttime it seems 848 * that some mountd implementations fail to remove the mount 849 * entries from their mountlist while unmounting. 850 */ 851 for (speclen = strlen(spec); 852 speclen > 1 && spec[speclen - 1] == '/'; 853 speclen--) 854 spec[speclen - 1] = '\0'; 855 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 856 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 857 return (0); 858 } 859 /* Make both '@' and ':' notations equal */ 860 if (*hostp != '\0') { 861 len = strlen(hostp); 862 memmove(nam, hostp, len); 863 nam[len] = ':'; 864 memmove(nam + len + 1, spec, speclen); 865 nam[len + speclen + 1] = '\0'; 866 } 867 868 /* 869 * Handle an internet host address. 870 */ 871 memset(&hints, 0, sizeof hints); 872 hints.ai_flags = AI_NUMERICHOST; 873 hints.ai_socktype = sotype; 874 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 875 hints.ai_flags = 0; 876 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 877 != 0) { 878 if (portspec == NULL) 879 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 880 else 881 errx(1, "%s:%s: %s", hostp, portspec, 882 gai_strerror(ecode)); 883 return (0); 884 } 885 } 886 887 ret = TRYRET_LOCALERR; 888 for (;;) { 889 /* 890 * Try each entry returned by getaddrinfo(). Note the 891 * occurence of remote errors by setting `remoteerr'. 892 */ 893 remoteerr = 0; 894 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 895 if ((ai->ai_family == AF_INET6) && 896 (opflags & OF_NOINET6)) 897 continue; 898 if ((ai->ai_family == AF_INET) && 899 (opflags & OF_NOINET4)) 900 continue; 901 ret = nfs4_tryproto(ai, hostp, spec, &errstr); 902 if (ret == TRYRET_SUCCESS) 903 break; 904 if (ret != TRYRET_LOCALERR) 905 remoteerr = 1; 906 if ((opflags & ISBGRND) == 0) 907 fprintf(stderr, "%s\n", errstr); 908 } 909 if (ret == TRYRET_SUCCESS) 910 break; 911 912 /* Exit if all errors were local. */ 913 if (!remoteerr) 914 exit(1); 915 916 /* 917 * If retrycnt == 0, we are to keep retrying forever. 918 * Otherwise decrement it, and exit if it hits zero. 919 */ 920 if (retrycnt != 0 && --retrycnt == 0) 921 exit(1); 922 923 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 924 warnx("Cannot immediately mount %s:%s, backgrounding", 925 hostp, spec); 926 opflags |= ISBGRND; 927 if (daemon(0, 0) != 0) 928 err(1, "daemon"); 929 } 930 sleep(60); 931 } 932 freeaddrinfo(ai_nfs); 933 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 934 /* Add mounted file system to PATH_MOUNTTAB */ 935 if (!add_mtab(hostp, spec)) 936 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 937 return (1); 938 } 939 940 /* 941 * Try to set up the NFS arguments according to the address 942 * family, protocol (and possibly port) specified in `ai'. 943 * 944 * Returns TRYRET_SUCCESS if successful, or: 945 * TRYRET_TIMEOUT The server did not respond. 946 * TRYRET_REMOTEERR The server reported an error. 947 * TRYRET_LOCALERR Local failure. 948 * 949 * In all error cases, *errstr will be set to a statically-allocated string 950 * describing the error. 951 */ 952 enum tryret 953 nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 954 struct iovec **iov, int *iovlen) 955 { 956 static char errbuf[256]; 957 struct sockaddr_storage nfs_ss; 958 struct netbuf nfs_nb; 959 struct nfhret nfhret; 960 struct timeval try; 961 struct rpc_err rpcerr; 962 CLIENT *clp; 963 struct netconfig *nconf, *nconf_mnt; 964 const char *netid, *netid_mnt; 965 char *secname; 966 int doconnect, nfsvers, mntvers, sotype; 967 enum clnt_stat stat; 968 enum mountmode trymntmode; 969 970 trymntmode = mountmode; 971 errbuf[0] = '\0'; 972 *errstr = errbuf; 973 974 if (nfsproto == IPPROTO_TCP) 975 sotype = SOCK_STREAM; 976 else if (nfsproto == IPPROTO_UDP) 977 sotype = SOCK_DGRAM; 978 979 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 980 snprintf(errbuf, sizeof errbuf, 981 "af %d sotype %d not supported", ai->ai_family, sotype); 982 return (TRYRET_LOCALERR); 983 } 984 if ((nconf = getnetconf_cached(netid)) == NULL) { 985 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 986 return (TRYRET_LOCALERR); 987 } 988 /* The RPCPROG_MNT netid may be different. */ 989 if (mnttcp_ok) { 990 netid_mnt = netid; 991 nconf_mnt = nconf; 992 } else { 993 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 994 == NULL) { 995 snprintf(errbuf, sizeof errbuf, 996 "af %d sotype SOCK_DGRAM not supported", 997 ai->ai_family); 998 return (TRYRET_LOCALERR); 999 } 1000 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 1001 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 1002 nc_sperror()); 1003 return (TRYRET_LOCALERR); 1004 } 1005 } 1006 1007 tryagain: 1008 if (trymntmode == V2) { 1009 nfsvers = 2; 1010 mntvers = 1; 1011 } else { 1012 nfsvers = 3; 1013 mntvers = 3; 1014 } 1015 1016 if (portspec != NULL) { 1017 /* `ai' contains the complete nfsd sockaddr. */ 1018 nfs_nb.buf = ai->ai_addr; 1019 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 1020 } else { 1021 /* Ask the remote rpcbind. */ 1022 nfs_nb.buf = &nfs_ss; 1023 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 1024 1025 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 1026 hostp)) { 1027 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 1028 trymntmode == ANY) { 1029 trymntmode = V2; 1030 goto tryagain; 1031 } 1032 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 1033 netid, hostp, spec, 1034 clnt_spcreateerror("RPCPROG_NFS")); 1035 return (returncode(rpc_createerr.cf_stat, 1036 &rpc_createerr.cf_error)); 1037 } 1038 } 1039 1040 /* Check that the server (nfsd) responds on the port we have chosen. */ 1041 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers, 1042 0, 0); 1043 if (clp == NULL) { 1044 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 1045 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 1046 return (returncode(rpc_createerr.cf_stat, 1047 &rpc_createerr.cf_error)); 1048 } 1049 if (sotype == SOCK_DGRAM && noconn == 0) { 1050 /* 1051 * Use connect(), to match what the kernel does. This 1052 * catches cases where the server responds from the 1053 * wrong source address. 1054 */ 1055 doconnect = 1; 1056 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 1057 clnt_destroy(clp); 1058 snprintf(errbuf, sizeof errbuf, 1059 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 1060 spec); 1061 return (TRYRET_LOCALERR); 1062 } 1063 } 1064 1065 try.tv_sec = 10; 1066 try.tv_usec = 0; 1067 stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 1068 (xdrproc_t)xdr_void, NULL, 1069 try); 1070 if (stat != RPC_SUCCESS) { 1071 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 1072 clnt_destroy(clp); 1073 trymntmode = V2; 1074 goto tryagain; 1075 } 1076 clnt_geterr(clp, &rpcerr); 1077 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 1078 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 1079 clnt_destroy(clp); 1080 return (returncode(stat, &rpcerr)); 1081 } 1082 clnt_destroy(clp); 1083 1084 /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ 1085 try.tv_sec = 10; 1086 try.tv_usec = 0; 1087 clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt); 1088 if (clp == NULL) { 1089 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1090 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 1091 return (returncode(rpc_createerr.cf_stat, 1092 &rpc_createerr.cf_error)); 1093 } 1094 clp->cl_auth = authsys_create_default(); 1095 nfhret.auth = secflavor; 1096 nfhret.vers = mntvers; 1097 stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec, 1098 (xdrproc_t)xdr_fh, &nfhret, 1099 try); 1100 auth_destroy(clp->cl_auth); 1101 if (stat != RPC_SUCCESS) { 1102 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 1103 clnt_destroy(clp); 1104 trymntmode = V2; 1105 goto tryagain; 1106 } 1107 clnt_geterr(clp, &rpcerr); 1108 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1109 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 1110 clnt_destroy(clp); 1111 return (returncode(stat, &rpcerr)); 1112 } 1113 clnt_destroy(clp); 1114 1115 if (nfhret.stat != 0) { 1116 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1117 hostp, spec, strerror(nfhret.stat)); 1118 return (TRYRET_REMOTEERR); 1119 } 1120 1121 /* 1122 * Store the filehandle and server address in nfsargsp, making 1123 * sure to copy any locally allocated structures. 1124 */ 1125 addrlen = nfs_nb.len; 1126 addr = malloc(addrlen); 1127 fhsize = nfhret.fhsize; 1128 fh = malloc(fhsize); 1129 if (addr == NULL || fh == NULL) 1130 err(1, "malloc"); 1131 bcopy(nfs_nb.buf, addr, addrlen); 1132 bcopy(nfhret.nfh, fh, fhsize); 1133 1134 build_iovec(iov, iovlen, "addr", addr, addrlen); 1135 build_iovec(iov, iovlen, "fh", fh, fhsize); 1136 secname = sec_num_to_name(nfhret.auth); 1137 if (secname) 1138 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1139 if (nfsvers == 3) 1140 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 1141 1142 return (TRYRET_SUCCESS); 1143 } 1144 1145 1146 /* 1147 * Try to set up the NFS arguments according to the address 1148 * family, protocol (and possibly port) specified in `ai'. 1149 * 1150 * Returns TRYRET_SUCCESS if successful, or: 1151 * TRYRET_TIMEOUT The server did not respond. 1152 * TRYRET_REMOTEERR The server reported an error. 1153 * TRYRET_LOCALERR Local failure. 1154 * 1155 * In all error cases, *errstr will be set to a statically-allocated string 1156 * describing the error. 1157 */ 1158 enum tryret 1159 nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr) 1160 { 1161 static char errbuf[256]; 1162 struct sockaddr_storage nfs_ss; 1163 struct netbuf nfs_nb; 1164 struct netconfig *nconf; 1165 const char *netid; 1166 int nfsvers, sotype; 1167 1168 errbuf[0] = '\0'; 1169 *errstr = errbuf; 1170 1171 if (nfsproto == IPPROTO_TCP) 1172 sotype = SOCK_STREAM; 1173 else if (nfsproto == IPPROTO_UDP) 1174 sotype = SOCK_DGRAM; 1175 1176 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 1177 snprintf(errbuf, sizeof errbuf, 1178 "af %d sotype %d not supported", ai->ai_family, sotype); 1179 return (TRYRET_LOCALERR); 1180 } 1181 if ((nconf = getnetconf_cached(netid)) == NULL) { 1182 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 1183 return (TRYRET_LOCALERR); 1184 } 1185 1186 nfsvers = 4; 1187 1188 if (portspec != NULL && atoi(portspec) != 0) { 1189 /* `ai' contains the complete nfsd sockaddr. */ 1190 nfs_nb.buf = ai->ai_addr; 1191 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 1192 } else { 1193 /* Ask the remote rpcbind. */ 1194 nfs_nb.buf = &nfs_ss; 1195 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 1196 1197 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 1198 hostp)) { 1199 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 1200 netid, hostp, spec, 1201 clnt_spcreateerror("RPCPROG_NFS")); 1202 return (returncode(rpc_createerr.cf_stat, 1203 &rpc_createerr.cf_error)); 1204 } 1205 } 1206 1207 /* 1208 * Store the filehandle and server address in nfsargsp, making 1209 * sure to copy any locally allocated structures. 1210 */ 1211 addrlen = nfs_nb.len; 1212 addr = malloc(addrlen); 1213 1214 if (addr == NULL) 1215 err(1, "malloc"); 1216 bcopy(nfs_nb.buf, addr, addrlen); 1217 1218 return (TRYRET_SUCCESS); 1219 } 1220 1221 /* 1222 * Catagorise a RPC return status and error into an `enum tryret' 1223 * return code. 1224 */ 1225 enum tryret 1226 returncode(enum clnt_stat stat, struct rpc_err *rpcerr) 1227 { 1228 switch (stat) { 1229 case RPC_TIMEDOUT: 1230 return (TRYRET_TIMEOUT); 1231 case RPC_PMAPFAILURE: 1232 case RPC_PROGNOTREGISTERED: 1233 case RPC_PROGVERSMISMATCH: 1234 /* XXX, these can be local or remote. */ 1235 case RPC_CANTSEND: 1236 case RPC_CANTRECV: 1237 return (TRYRET_REMOTEERR); 1238 case RPC_SYSTEMERROR: 1239 switch (rpcerr->re_errno) { 1240 case ETIMEDOUT: 1241 return (TRYRET_TIMEOUT); 1242 case ENOMEM: 1243 break; 1244 default: 1245 return (TRYRET_REMOTEERR); 1246 } 1247 /* FALLTHROUGH */ 1248 default: 1249 break; 1250 } 1251 return (TRYRET_LOCALERR); 1252 } 1253 1254 /* 1255 * Look up a netid based on an address family and socket type. 1256 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1257 * 1258 * XXX there should be a library function for this. 1259 */ 1260 const char * 1261 netidbytype(int af, int sotype) 1262 { 1263 struct nc_protos *p; 1264 1265 for (p = nc_protos; p->netid != NULL; p++) { 1266 if (af != p->af || sotype != p->sotype) 1267 continue; 1268 return (p->netid); 1269 } 1270 return (NULL); 1271 } 1272 1273 /* 1274 * Look up a netconfig entry based on a netid, and cache the result so 1275 * that we don't need to remember to call freenetconfigent(). 1276 * 1277 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1278 * work on failure. 1279 */ 1280 struct netconfig * 1281 getnetconf_cached(const char *netid) 1282 { 1283 static struct nc_entry { 1284 struct netconfig *nconf; 1285 struct nc_entry *next; 1286 } *head; 1287 struct nc_entry *p; 1288 struct netconfig *nconf; 1289 1290 for (p = head; p != NULL; p = p->next) 1291 if (strcmp(netid, p->nconf->nc_netid) == 0) 1292 return (p->nconf); 1293 1294 if ((nconf = getnetconfigent(netid)) == NULL) 1295 return (NULL); 1296 if ((p = malloc(sizeof(*p))) == NULL) 1297 err(1, "malloc"); 1298 p->nconf = nconf; 1299 p->next = head; 1300 head = p; 1301 1302 return (p->nconf); 1303 } 1304 1305 /* 1306 * xdr routines for mount rpc's 1307 */ 1308 int 1309 xdr_dir(XDR *xdrsp, char *dirp) 1310 { 1311 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 1312 } 1313 1314 int 1315 xdr_fh(XDR *xdrsp, struct nfhret *np) 1316 { 1317 int i; 1318 long auth, authcnt, authfnd = 0; 1319 1320 if (!xdr_u_long(xdrsp, &np->stat)) 1321 return (0); 1322 if (np->stat) 1323 return (1); 1324 switch (np->vers) { 1325 case 1: 1326 np->fhsize = NFSX_V2FH; 1327 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); 1328 case 3: 1329 if (!xdr_long(xdrsp, &np->fhsize)) 1330 return (0); 1331 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) 1332 return (0); 1333 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1334 return (0); 1335 if (!xdr_long(xdrsp, &authcnt)) 1336 return (0); 1337 for (i = 0; i < authcnt; i++) { 1338 if (!xdr_long(xdrsp, &auth)) 1339 return (0); 1340 if (np->auth == -1) { 1341 np->auth = auth; 1342 authfnd++; 1343 } else if (auth == np->auth) { 1344 authfnd++; 1345 } 1346 } 1347 /* 1348 * Some servers, such as DEC's OSF/1 return a nil authenticator 1349 * list to indicate RPCAUTH_UNIX. 1350 */ 1351 if (authcnt == 0 && np->auth == -1) 1352 np->auth = AUTH_SYS; 1353 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1354 np->stat = EAUTH; 1355 return (1); 1356 }; 1357 return (0); 1358 } 1359 1360 void 1361 usage() 1362 { 1363 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1364 "usage: mount_nfs [-234bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1365 " [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1366 " [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1367 " rhost:path node"); 1368 exit(1); 1369 } 1370