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