mount_nfs.c revision 184588
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 184588 2008-11-03 10:38:00Z dfr $"); 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; 137int secflavor = -1; 138 139enum mountmode { 140 ANY, 141 V2, 142 V3, 143 V4 144} mountmode = ANY; 145 146/* Return codes for nfs_tryproto. */ 147enum 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 154int fallback_mount(struct iovec *iov, int iovlen, int mntflags); 155int sec_name_to_num(char *sec); 156char *sec_num_to_name(int num); 157int getnfsargs(char *, struct iovec **iov, int *iovlen); 158int getnfs4args(char *, struct iovec **iov, int *iovlen); 159/* void set_rpc_maxgrouplist(int); */ 160struct netconfig *getnetconf_cached(const char *netid); 161const char *netidbytype(int af, int sotype); 162void usage(void) __dead2; 163int xdr_dir(XDR *, char *); 164int xdr_fh(XDR *, struct nfhret *); 165enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, 166 char **errstr, struct iovec **iov, int *iovlen); 167enum tryret nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec, 168 char **errstr); 169enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 170extern int getosreldate(void); 171 172int 173main(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 443static int 444findopt(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 461static void 462copyopt(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 472int 473fallback_mount(struct iovec *iov, int iovlen, int mntflags) 474{ 475 struct nfs_args args = { 476 .version = NFS_ARGSVERSION, 477 .addr = NULL, 478 .addrlen = sizeof (struct sockaddr_in), 479 .sotype = SOCK_STREAM, 480 .proto = 0, 481 .fh = NULL, 482 .fhsize = 0, 483 .flags = NFSMNT_RESVPORT, 484 .wsize = NFS_WSIZE, 485 .rsize = NFS_RSIZE, 486 .readdirsize = NFS_READDIRSIZE, 487 .timeo = 10, 488 .retrans = NFS_RETRANS, 489 .maxgrouplist = NFS_MAXGRPS, 490 .readahead = NFS_DEFRAHEAD, 491 .wcommitsize = 0, /* was: NQ_DEFLEASE */ 492 .deadthresh = NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 493 .hostname = NULL, 494 /* args version 4 */ 495 .acregmin = NFS_MINATTRTIMO, 496 .acregmax = NFS_MAXATTRTIMO, 497 .acdirmin = NFS_MINDIRATTRTIMO, 498 .acdirmax = NFS_MAXDIRATTRTIMO, 499 }; 500 int ret; 501 char *opt; 502 struct iovec *newiov; 503 int newiovlen; 504 505 if (findopt(iov, iovlen, "dumbtimer", NULL, NULL) == 0) 506 args.flags |= NFSMNT_DUMBTIMR; 507 if (findopt(iov, iovlen, "noconn", NULL, NULL) == 0) 508 args.flags |= NFSMNT_NOCONN; 509 if (findopt(iov, iovlen, "conn", NULL, NULL) == 0) 510 args.flags |= NFSMNT_NOCONN; 511 if (findopt(iov, iovlen, "nolockd", NULL, NULL) == 0) 512 args.flags |= NFSMNT_NOLOCKD; 513 if (findopt(iov, iovlen, "lockd", NULL, NULL) == 0) 514 args.flags &= ~NFSMNT_NOLOCKD; 515 if (findopt(iov, iovlen, "intr", NULL, NULL) == 0) 516 args.flags |= NFSMNT_INT; 517 if (findopt(iov, iovlen, "rdirplus", NULL, NULL) == 0) 518 args.flags |= NFSMNT_RDIRPLUS; 519 if (findopt(iov, iovlen, "resvport", NULL, NULL) == 0) 520 args.flags |= NFSMNT_RESVPORT; 521 if (findopt(iov, iovlen, "noresvport", NULL, NULL) == 0) 522 args.flags &= ~NFSMNT_RESVPORT; 523 if (findopt(iov, iovlen, "soft", NULL, NULL) == 0) 524 args.flags |= NFSMNT_SOFT; 525 if (findopt(iov, iovlen, "hard", NULL, NULL) == 0) 526 args.flags &= ~NFSMNT_SOFT; 527 if (findopt(iov, iovlen, "mntudp", NULL, NULL) == 0) 528 args.sotype = SOCK_DGRAM; 529 if (findopt(iov, iovlen, "udp", NULL, NULL) == 0) 530 args.sotype = SOCK_DGRAM; 531 if (findopt(iov, iovlen, "tcp", NULL, NULL) == 0) 532 args.sotype = SOCK_STREAM; 533 if (findopt(iov, iovlen, "nfsv3", NULL, NULL) == 0) 534 args.flags |= NFSMNT_NFSV3; 535 if (findopt(iov, iovlen, "readdirsize", &opt, NULL) == 0) { 536 if (opt == NULL) { 537 errx(1, "illegal readdirsize"); 538 } 539 ret = sscanf(opt, "%d", &args.readdirsize); 540 if (ret != 1 || args.readdirsize <= 0) { 541 errx(1, "illegal readdirsize: %s", opt); 542 } 543 args.flags |= NFSMNT_READDIRSIZE; 544 } 545 if (findopt(iov, iovlen, "readahead", &opt, NULL) == 0) { 546 if (opt == NULL) { 547 errx(1, "illegal readahead"); 548 } 549 ret = sscanf(opt, "%d", &args.readahead); 550 if (ret != 1 || args.readahead <= 0) { 551 errx(1, "illegal readahead: %s", opt); 552 } 553 args.flags |= NFSMNT_READAHEAD; 554 } 555 if (findopt(iov, iovlen, "wsize", &opt, NULL) == 0) { 556 if (opt == NULL) { 557 errx(1, "illegal wsize"); 558 } 559 ret = sscanf(opt, "%d", &args.wsize); 560 if (ret != 1 || args.wsize <= 0) { 561 errx(1, "illegal wsize: %s", opt); 562 } 563 args.flags |= NFSMNT_WSIZE; 564 } 565 if (findopt(iov, iovlen, "rsize", &opt, NULL) == 0) { 566 if (opt == NULL) { 567 errx(1, "illegal rsize"); 568 } 569 ret = sscanf(opt, "%d", &args.rsize); 570 if (ret != 1 || args.rsize <= 0) { 571 errx(1, "illegal wsize: %s", opt); 572 } 573 args.flags |= NFSMNT_RSIZE; 574 } 575 if (findopt(iov, iovlen, "retrans", &opt, NULL) == 0) { 576 if (opt == NULL) { 577 errx(1, "illegal retrans"); 578 } 579 ret = sscanf(opt, "%d", &args.retrans); 580 if (ret != 1 || args.retrans <= 0) { 581 errx(1, "illegal retrans: %s", opt); 582 } 583 args.flags |= NFSMNT_RETRANS; 584 } 585 if (findopt(iov, iovlen, "acregmin", &opt, NULL) == 0) { 586 ret = sscanf(opt, "%d", &args.acregmin); 587 if (ret != 1 || args.acregmin <= 0) { 588 errx(1, "illegal acregmin: %s", opt); 589 } 590 } 591 if (findopt(iov, iovlen, "acregmax", &opt, NULL) == 0) { 592 ret = sscanf(opt, "%d", &args.acregmax); 593 if (ret != 1 || args.acregmax <= 0) { 594 errx(1, "illegal acregmax: %s", opt); 595 } 596 } 597 if (findopt(iov, iovlen, "acdirmin", &opt, NULL) == 0) { 598 ret = sscanf(opt, "%d", &args.acdirmin); 599 if (ret != 1 || args.acdirmin <= 0) { 600 errx(1, "illegal acdirmin: %s", opt); 601 } 602 } 603 if (findopt(iov, iovlen, "acdirmax", &opt, NULL) == 0) { 604 ret = sscanf(opt, "%d", &args.acdirmax); 605 if (ret != 1 || args.acdirmax <= 0) { 606 errx(1, "illegal acdirmax: %s", opt); 607 } 608 } 609 if (findopt(iov, iovlen, "deadthresh", &opt, NULL) == 0) { 610 ret = sscanf(opt, "%d", &args.deadthresh); 611 if (ret != 1 || args.deadthresh <= 0) { 612 errx(1, "illegal deadthresh: %s", opt); 613 } 614 args.flags |= NFSMNT_DEADTHRESH; 615 } 616 if (findopt(iov, iovlen, "timeout", &opt, NULL) == 0) { 617 ret = sscanf(opt, "%d", &args.timeo); 618 if (ret != 1 || args.timeo <= 0) { 619 errx(1, "illegal timeout: %s", opt); 620 } 621 args.flags |= NFSMNT_TIMEO; 622 } 623 if (findopt(iov, iovlen, "maxgroups", &opt, NULL) == 0) { 624 ret = sscanf(opt, "%d", &args.maxgrouplist); 625 if (ret != 1 || args.timeo <= 0) { 626 errx(1, "illegal maxgroups: %s", opt); 627 } 628 args.flags |= NFSMNT_MAXGRPS; 629 } 630 if (findopt(iov, iovlen, "addr", &opt, 631 &args.addrlen) == 0) { 632 args.addr = (struct sockaddr *) opt; 633 } 634 if (findopt(iov, iovlen, "fh", &opt, &args.fhsize) == 0) { 635 args.fh = opt; 636 } 637 if (findopt(iov, iovlen, "hostname", &args.hostname, 638 NULL) == 0) { 639 } 640 if (args.hostname == NULL) { 641 errx(1, "Invalid hostname"); 642 } 643 644 newiov = NULL; 645 newiovlen = 0; 646 647 build_iovec(&newiov, &newiovlen, "nfs_args", &args, sizeof(args)); 648 copyopt(&newiov, &newiovlen, iov, iovlen, "fstype"); 649 copyopt(&newiov, &newiovlen, iov, iovlen, "fspath"); 650 copyopt(&newiov, &newiovlen, iov, iovlen, "errmsg"); 651 652 return nmount(newiov, newiovlen, mntflags); 653} 654 655int 656sec_name_to_num(char *sec) 657{ 658 if (!strcmp(sec, "krb5")) 659 return (RPCSEC_GSS_KRB5); 660 if (!strcmp(sec, "krb5i")) 661 return (RPCSEC_GSS_KRB5I); 662 if (!strcmp(sec, "krb5p")) 663 return (RPCSEC_GSS_KRB5P); 664 if (!strcmp(sec, "sys")) 665 return (AUTH_SYS); 666 return (-1); 667} 668 669char * 670sec_num_to_name(int flavor) 671{ 672 switch (flavor) { 673 case RPCSEC_GSS_KRB5: 674 return ("krb5"); 675 case RPCSEC_GSS_KRB5I: 676 return ("krb5i"); 677 case RPCSEC_GSS_KRB5P: 678 return ("krb5p"); 679 case AUTH_SYS: 680 return ("sys"); 681 } 682 return (NULL); 683} 684 685int 686getnfsargs(char *spec, struct iovec **iov, int *iovlen) 687{ 688 struct addrinfo hints, *ai_nfs, *ai; 689 enum tryret ret; 690 int ecode, speclen, remoteerr; 691 char *hostp, *delimp, *errstr; 692 size_t len; 693 static char nam[MNAMELEN + 1]; 694 695 if ((delimp = strrchr(spec, ':')) != NULL) { 696 hostp = spec; 697 spec = delimp + 1; 698 } else if ((delimp = strrchr(spec, '@')) != NULL) { 699 warnx("path@server syntax is deprecated, use server:path"); 700 hostp = delimp + 1; 701 } else { 702 warnx("no <host>:<dirpath> nfs-name"); 703 return (0); 704 } 705 *delimp = '\0'; 706 707 /* 708 * If there has been a trailing slash at mounttime it seems 709 * that some mountd implementations fail to remove the mount 710 * entries from their mountlist while unmounting. 711 */ 712 for (speclen = strlen(spec); 713 speclen > 1 && spec[speclen - 1] == '/'; 714 speclen--) 715 spec[speclen - 1] = '\0'; 716 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 717 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 718 return (0); 719 } 720 /* Make both '@' and ':' notations equal */ 721 if (*hostp != '\0') { 722 len = strlen(hostp); 723 memmove(nam, hostp, len); 724 nam[len] = ':'; 725 memmove(nam + len + 1, spec, speclen); 726 nam[len + speclen + 1] = '\0'; 727 } 728 729 /* 730 * Handle an internet host address. 731 */ 732 memset(&hints, 0, sizeof hints); 733 hints.ai_flags = AI_NUMERICHOST; 734 if (nfsproto == IPPROTO_TCP) 735 hints.ai_socktype = SOCK_STREAM; 736 else if (nfsproto == IPPROTO_UDP) 737 hints.ai_socktype = SOCK_DGRAM; 738 739 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 740 hints.ai_flags = 0; 741 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 742 != 0) { 743 if (portspec == NULL) 744 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 745 else 746 errx(1, "%s:%s: %s", hostp, portspec, 747 gai_strerror(ecode)); 748 return (0); 749 } 750 } 751 752 ret = TRYRET_LOCALERR; 753 for (;;) { 754 /* 755 * Try each entry returned by getaddrinfo(). Note the 756 * occurence of remote errors by setting `remoteerr'. 757 */ 758 remoteerr = 0; 759 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 760 if ((ai->ai_family == AF_INET6) && 761 (opflags & OF_NOINET6)) 762 continue; 763 if ((ai->ai_family == AF_INET) && 764 (opflags & OF_NOINET4)) 765 continue; 766 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 767 iovlen); 768 if (ret == TRYRET_SUCCESS) 769 break; 770 if (ret != TRYRET_LOCALERR) 771 remoteerr = 1; 772 if ((opflags & ISBGRND) == 0) 773 fprintf(stderr, "%s\n", errstr); 774 } 775 if (ret == TRYRET_SUCCESS) 776 break; 777 778 /* Exit if all errors were local. */ 779 if (!remoteerr) 780 exit(1); 781 782 /* 783 * If retrycnt == 0, we are to keep retrying forever. 784 * Otherwise decrement it, and exit if it hits zero. 785 */ 786 if (retrycnt != 0 && --retrycnt == 0) 787 exit(1); 788 789 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 790 warnx("Cannot immediately mount %s:%s, backgrounding", 791 hostp, spec); 792 opflags |= ISBGRND; 793 if (daemon(0, 0) != 0) 794 err(1, "daemon"); 795 } 796 sleep(60); 797 } 798 freeaddrinfo(ai_nfs); 799 800 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 801 /* Add mounted file system to PATH_MOUNTTAB */ 802 if (!add_mtab(hostp, spec)) 803 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 804 return (1); 805} 806 807 808int 809getnfs4args(char *spec, struct iovec **iov, int *iovlen) 810{ 811 struct addrinfo hints, *ai_nfs, *ai; 812 enum tryret ret; 813 int ecode, speclen, remoteerr, sotype; 814 char *hostp, *delimp, *errstr; 815 size_t len; 816 static char nam[MNAMELEN + 1]; 817 818 if (nfsproto == IPPROTO_TCP) 819 sotype = SOCK_STREAM; 820 else if (nfsproto == IPPROTO_UDP) 821 sotype = SOCK_DGRAM; 822 823 824 if ((delimp = strrchr(spec, ':')) != NULL) { 825 hostp = spec; 826 spec = delimp + 1; 827 } else if ((delimp = strrchr(spec, '@')) != NULL) { 828 warnx("path@server syntax is deprecated, use server:path"); 829 hostp = delimp + 1; 830 } else { 831 warnx("no <host>:<dirpath> nfs-name"); 832 return (0); 833 } 834 *delimp = '\0'; 835 836 /* 837 * If there has been a trailing slash at mounttime it seems 838 * that some mountd implementations fail to remove the mount 839 * entries from their mountlist while unmounting. 840 */ 841 for (speclen = strlen(spec); 842 speclen > 1 && spec[speclen - 1] == '/'; 843 speclen--) 844 spec[speclen - 1] = '\0'; 845 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 846 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 847 return (0); 848 } 849 /* Make both '@' and ':' notations equal */ 850 if (*hostp != '\0') { 851 len = strlen(hostp); 852 memmove(nam, hostp, len); 853 nam[len] = ':'; 854 memmove(nam + len + 1, spec, speclen); 855 nam[len + speclen + 1] = '\0'; 856 } 857 858 /* 859 * Handle an internet host address. 860 */ 861 memset(&hints, 0, sizeof hints); 862 hints.ai_flags = AI_NUMERICHOST; 863 hints.ai_socktype = sotype; 864 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 865 hints.ai_flags = 0; 866 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 867 != 0) { 868 if (portspec == NULL) 869 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 870 else 871 errx(1, "%s:%s: %s", hostp, portspec, 872 gai_strerror(ecode)); 873 return (0); 874 } 875 } 876 877 ret = TRYRET_LOCALERR; 878 for (;;) { 879 /* 880 * Try each entry returned by getaddrinfo(). Note the 881 * occurence of remote errors by setting `remoteerr'. 882 */ 883 remoteerr = 0; 884 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 885 if ((ai->ai_family == AF_INET6) && 886 (opflags & OF_NOINET6)) 887 continue; 888 if ((ai->ai_family == AF_INET) && 889 (opflags & OF_NOINET4)) 890 continue; 891 ret = nfs4_tryproto(ai, hostp, spec, &errstr); 892 if (ret == TRYRET_SUCCESS) 893 break; 894 if (ret != TRYRET_LOCALERR) 895 remoteerr = 1; 896 if ((opflags & ISBGRND) == 0) 897 fprintf(stderr, "%s\n", errstr); 898 } 899 if (ret == TRYRET_SUCCESS) 900 break; 901 902 /* Exit if all errors were local. */ 903 if (!remoteerr) 904 exit(1); 905 906 /* 907 * If retrycnt == 0, we are to keep retrying forever. 908 * Otherwise decrement it, and exit if it hits zero. 909 */ 910 if (retrycnt != 0 && --retrycnt == 0) 911 exit(1); 912 913 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 914 warnx("Cannot immediately mount %s:%s, backgrounding", 915 hostp, spec); 916 opflags |= ISBGRND; 917 if (daemon(0, 0) != 0) 918 err(1, "daemon"); 919 } 920 sleep(60); 921 } 922 freeaddrinfo(ai_nfs); 923 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 924 /* Add mounted file system to PATH_MOUNTTAB */ 925 if (!add_mtab(hostp, spec)) 926 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 927 return (1); 928} 929 930/* 931 * Try to set up the NFS arguments according to the address 932 * family, protocol (and possibly port) specified in `ai'. 933 * 934 * Returns TRYRET_SUCCESS if successful, or: 935 * TRYRET_TIMEOUT The server did not respond. 936 * TRYRET_REMOTEERR The server reported an error. 937 * TRYRET_LOCALERR Local failure. 938 * 939 * In all error cases, *errstr will be set to a statically-allocated string 940 * describing the error. 941 */ 942enum tryret 943nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 944 struct iovec **iov, int *iovlen) 945{ 946 static char errbuf[256]; 947 struct sockaddr_storage nfs_ss; 948 struct netbuf nfs_nb; 949 struct nfhret nfhret; 950 struct timeval try; 951 struct rpc_err rpcerr; 952 CLIENT *clp; 953 struct netconfig *nconf, *nconf_mnt; 954 const char *netid, *netid_mnt; 955 char *secname; 956 int doconnect, nfsvers, mntvers, sotype; 957 enum clnt_stat stat; 958 enum mountmode trymntmode; 959 960 trymntmode = mountmode; 961 errbuf[0] = '\0'; 962 *errstr = errbuf; 963 964 if (nfsproto == IPPROTO_TCP) 965 sotype = SOCK_STREAM; 966 else if (nfsproto == IPPROTO_UDP) 967 sotype = SOCK_DGRAM; 968 969 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 970 snprintf(errbuf, sizeof errbuf, 971 "af %d sotype %d not supported", ai->ai_family, sotype); 972 return (TRYRET_LOCALERR); 973 } 974 if ((nconf = getnetconf_cached(netid)) == NULL) { 975 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 976 return (TRYRET_LOCALERR); 977 } 978 /* The RPCPROG_MNT netid may be different. */ 979 if (mnttcp_ok) { 980 netid_mnt = netid; 981 nconf_mnt = nconf; 982 } else { 983 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 984 == NULL) { 985 snprintf(errbuf, sizeof errbuf, 986 "af %d sotype SOCK_DGRAM not supported", 987 ai->ai_family); 988 return (TRYRET_LOCALERR); 989 } 990 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 991 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 992 nc_sperror()); 993 return (TRYRET_LOCALERR); 994 } 995 } 996 997tryagain: 998 if (trymntmode == V2) { 999 nfsvers = 2; 1000 mntvers = 1; 1001 } else { 1002 nfsvers = 3; 1003 mntvers = 3; 1004 } 1005 1006 if (portspec != NULL) { 1007 /* `ai' contains the complete nfsd sockaddr. */ 1008 nfs_nb.buf = ai->ai_addr; 1009 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 1010 } else { 1011 /* Ask the remote rpcbind. */ 1012 nfs_nb.buf = &nfs_ss; 1013 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 1014 1015 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 1016 hostp)) { 1017 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 1018 trymntmode == ANY) { 1019 trymntmode = V2; 1020 goto tryagain; 1021 } 1022 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 1023 netid, hostp, spec, 1024 clnt_spcreateerror("RPCPROG_NFS")); 1025 return (returncode(rpc_createerr.cf_stat, 1026 &rpc_createerr.cf_error)); 1027 } 1028 } 1029 1030 /* Check that the server (nfsd) responds on the port we have chosen. */ 1031 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers, 1032 0, 0); 1033 if (clp == NULL) { 1034 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 1035 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 1036 return (returncode(rpc_createerr.cf_stat, 1037 &rpc_createerr.cf_error)); 1038 } 1039 if (sotype == SOCK_DGRAM && noconn == 0) { 1040 /* 1041 * Use connect(), to match what the kernel does. This 1042 * catches cases where the server responds from the 1043 * wrong source address. 1044 */ 1045 doconnect = 1; 1046 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 1047 clnt_destroy(clp); 1048 snprintf(errbuf, sizeof errbuf, 1049 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 1050 spec); 1051 return (TRYRET_LOCALERR); 1052 } 1053 } 1054 1055 try.tv_sec = 10; 1056 try.tv_usec = 0; 1057 stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 1058 (xdrproc_t)xdr_void, NULL, 1059 try); 1060 if (stat != RPC_SUCCESS) { 1061 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 1062 clnt_destroy(clp); 1063 trymntmode = V2; 1064 goto tryagain; 1065 } 1066 clnt_geterr(clp, &rpcerr); 1067 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 1068 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 1069 clnt_destroy(clp); 1070 return (returncode(stat, &rpcerr)); 1071 } 1072 clnt_destroy(clp); 1073 1074 /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ 1075 try.tv_sec = 10; 1076 try.tv_usec = 0; 1077 clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt); 1078 if (clp == NULL) { 1079 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1080 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 1081 return (returncode(rpc_createerr.cf_stat, 1082 &rpc_createerr.cf_error)); 1083 } 1084 clp->cl_auth = authsys_create_default(); 1085 nfhret.auth = secflavor; 1086 nfhret.vers = mntvers; 1087 stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec, 1088 (xdrproc_t)xdr_fh, &nfhret, 1089 try); 1090 auth_destroy(clp->cl_auth); 1091 if (stat != RPC_SUCCESS) { 1092 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 1093 clnt_destroy(clp); 1094 trymntmode = V2; 1095 goto tryagain; 1096 } 1097 clnt_geterr(clp, &rpcerr); 1098 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1099 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 1100 clnt_destroy(clp); 1101 return (returncode(stat, &rpcerr)); 1102 } 1103 clnt_destroy(clp); 1104 1105 if (nfhret.stat != 0) { 1106 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1107 hostp, spec, strerror(nfhret.stat)); 1108 return (TRYRET_REMOTEERR); 1109 } 1110 1111 /* 1112 * Store the filehandle and server address in nfsargsp, making 1113 * sure to copy any locally allocated structures. 1114 */ 1115 addrlen = nfs_nb.len; 1116 addr = malloc(addrlen); 1117 fhsize = nfhret.fhsize; 1118 fh = malloc(fhsize); 1119 if (addr == NULL || fh == NULL) 1120 err(1, "malloc"); 1121 bcopy(nfs_nb.buf, addr, addrlen); 1122 bcopy(nfhret.nfh, fh, fhsize); 1123 1124 build_iovec(iov, iovlen, "addr", addr, addrlen); 1125 build_iovec(iov, iovlen, "fh", fh, fhsize); 1126 secname = sec_num_to_name(nfhret.auth); 1127 if (secname) 1128 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1129 if (nfsvers == 3) 1130 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 1131 1132 return (TRYRET_SUCCESS); 1133} 1134 1135 1136/* 1137 * Try to set up the NFS arguments according to the address 1138 * family, protocol (and possibly port) specified in `ai'. 1139 * 1140 * Returns TRYRET_SUCCESS if successful, or: 1141 * TRYRET_TIMEOUT The server did not respond. 1142 * TRYRET_REMOTEERR The server reported an error. 1143 * TRYRET_LOCALERR Local failure. 1144 * 1145 * In all error cases, *errstr will be set to a statically-allocated string 1146 * describing the error. 1147 */ 1148enum tryret 1149nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr) 1150{ 1151 static char errbuf[256]; 1152 struct sockaddr_storage nfs_ss; 1153 struct netbuf nfs_nb; 1154 struct netconfig *nconf; 1155 const char *netid; 1156 int nfsvers, sotype; 1157 1158 errbuf[0] = '\0'; 1159 *errstr = errbuf; 1160 1161 if (nfsproto == IPPROTO_TCP) 1162 sotype = SOCK_STREAM; 1163 else if (nfsproto == IPPROTO_UDP) 1164 sotype = SOCK_DGRAM; 1165 1166 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 1167 snprintf(errbuf, sizeof errbuf, 1168 "af %d sotype %d not supported", ai->ai_family, sotype); 1169 return (TRYRET_LOCALERR); 1170 } 1171 if ((nconf = getnetconf_cached(netid)) == NULL) { 1172 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 1173 return (TRYRET_LOCALERR); 1174 } 1175 1176 nfsvers = 4; 1177 1178 if (portspec != NULL && atoi(portspec) != 0) { 1179 /* `ai' contains the complete nfsd sockaddr. */ 1180 nfs_nb.buf = ai->ai_addr; 1181 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 1182 } else { 1183 /* Ask the remote rpcbind. */ 1184 nfs_nb.buf = &nfs_ss; 1185 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 1186 1187 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 1188 hostp)) { 1189 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 1190 netid, hostp, spec, 1191 clnt_spcreateerror("RPCPROG_NFS")); 1192 return (returncode(rpc_createerr.cf_stat, 1193 &rpc_createerr.cf_error)); 1194 } 1195 } 1196 1197 /* 1198 * Store the filehandle and server address in nfsargsp, making 1199 * sure to copy any locally allocated structures. 1200 */ 1201 addrlen = nfs_nb.len; 1202 addr = malloc(addrlen); 1203 1204 if (addr == NULL) 1205 err(1, "malloc"); 1206 bcopy(nfs_nb.buf, addr, addrlen); 1207 1208 return (TRYRET_SUCCESS); 1209} 1210 1211/* 1212 * Catagorise a RPC return status and error into an `enum tryret' 1213 * return code. 1214 */ 1215enum tryret 1216returncode(enum clnt_stat stat, struct rpc_err *rpcerr) 1217{ 1218 switch (stat) { 1219 case RPC_TIMEDOUT: 1220 return (TRYRET_TIMEOUT); 1221 case RPC_PMAPFAILURE: 1222 case RPC_PROGNOTREGISTERED: 1223 case RPC_PROGVERSMISMATCH: 1224 /* XXX, these can be local or remote. */ 1225 case RPC_CANTSEND: 1226 case RPC_CANTRECV: 1227 return (TRYRET_REMOTEERR); 1228 case RPC_SYSTEMERROR: 1229 switch (rpcerr->re_errno) { 1230 case ETIMEDOUT: 1231 return (TRYRET_TIMEOUT); 1232 case ENOMEM: 1233 break; 1234 default: 1235 return (TRYRET_REMOTEERR); 1236 } 1237 /* FALLTHROUGH */ 1238 default: 1239 break; 1240 } 1241 return (TRYRET_LOCALERR); 1242} 1243 1244/* 1245 * Look up a netid based on an address family and socket type. 1246 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1247 * 1248 * XXX there should be a library function for this. 1249 */ 1250const char * 1251netidbytype(int af, int sotype) 1252{ 1253 struct nc_protos *p; 1254 1255 for (p = nc_protos; p->netid != NULL; p++) { 1256 if (af != p->af || sotype != p->sotype) 1257 continue; 1258 return (p->netid); 1259 } 1260 return (NULL); 1261} 1262 1263/* 1264 * Look up a netconfig entry based on a netid, and cache the result so 1265 * that we don't need to remember to call freenetconfigent(). 1266 * 1267 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1268 * work on failure. 1269 */ 1270struct netconfig * 1271getnetconf_cached(const char *netid) 1272{ 1273 static struct nc_entry { 1274 struct netconfig *nconf; 1275 struct nc_entry *next; 1276 } *head; 1277 struct nc_entry *p; 1278 struct netconfig *nconf; 1279 1280 for (p = head; p != NULL; p = p->next) 1281 if (strcmp(netid, p->nconf->nc_netid) == 0) 1282 return (p->nconf); 1283 1284 if ((nconf = getnetconfigent(netid)) == NULL) 1285 return (NULL); 1286 if ((p = malloc(sizeof(*p))) == NULL) 1287 err(1, "malloc"); 1288 p->nconf = nconf; 1289 p->next = head; 1290 head = p; 1291 1292 return (p->nconf); 1293} 1294 1295/* 1296 * xdr routines for mount rpc's 1297 */ 1298int 1299xdr_dir(XDR *xdrsp, char *dirp) 1300{ 1301 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 1302} 1303 1304int 1305xdr_fh(XDR *xdrsp, struct nfhret *np) 1306{ 1307 int i; 1308 long auth, authcnt, authfnd = 0; 1309 1310 if (!xdr_u_long(xdrsp, &np->stat)) 1311 return (0); 1312 if (np->stat) 1313 return (1); 1314 switch (np->vers) { 1315 case 1: 1316 np->fhsize = NFSX_V2FH; 1317 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); 1318 case 3: 1319 if (!xdr_long(xdrsp, &np->fhsize)) 1320 return (0); 1321 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) 1322 return (0); 1323 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1324 return (0); 1325 if (!xdr_long(xdrsp, &authcnt)) 1326 return (0); 1327 for (i = 0; i < authcnt; i++) { 1328 if (!xdr_long(xdrsp, &auth)) 1329 return (0); 1330 if (np->auth == -1) { 1331 np->auth = auth; 1332 authfnd++; 1333 } else if (auth == np->auth) { 1334 authfnd++; 1335 } 1336 } 1337 /* 1338 * Some servers, such as DEC's OSF/1 return a nil authenticator 1339 * list to indicate RPCAUTH_UNIX. 1340 */ 1341 if (authcnt == 0 && np->auth == -1) 1342 np->auth = AUTH_SYS; 1343 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1344 np->stat = EAUTH; 1345 return (1); 1346 }; 1347 return (0); 1348} 1349 1350void 1351usage() 1352{ 1353 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1354"usage: mount_nfs [-234bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1355" [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1356" [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1357" rhost:path node"); 1358 exit(1); 1359} 1360