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