mount_nfs.c revision 80086
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 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38static const char copyright[] = 39"@(#) Copyright (c) 1992, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif /* not lint */ 42 43#ifndef lint 44#if 0 45static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 46#endif 47static const char rcsid[] = 48 "$FreeBSD: head/sbin/mount_nfs/mount_nfs.c 80086 2001-07-21 15:38:37Z iedowse $"; 49#endif /* not lint */ 50 51#include <sys/param.h> 52#include <sys/mount.h> 53#include <sys/socket.h> 54#include <sys/stat.h> 55#include <sys/syslog.h> 56 57#include <rpc/rpc.h> 58#include <rpc/pmap_clnt.h> 59#include <rpc/pmap_prot.h> 60 61#ifdef NFSKERB 62#include <kerberosIV/des.h> 63#include <kerberosIV/krb.h> 64#endif 65 66#include <nfs/rpcv2.h> 67#include <nfs/nfsproto.h> 68#include <nfs/nfs.h> 69#include <nfs/nqnfs.h> 70 71#include <arpa/inet.h> 72 73#include <ctype.h> 74#include <err.h> 75#include <errno.h> 76#include <fcntl.h> 77#include <netdb.h> 78#include <stdio.h> 79#include <stdlib.h> 80#include <strings.h> 81#include <sysexits.h> 82#include <unistd.h> 83 84#include "mntopts.h" 85#include "mounttab.h" 86 87#define ALTF_BG 0x1 88#define ALTF_NOCONN 0x2 89#define ALTF_DUMBTIMR 0x4 90#define ALTF_INTR 0x8 91#define ALTF_KERB 0x10 92#define ALTF_NFSV3 0x20 93#define ALTF_RDIRPLUS 0x40 94#define ALTF_MNTUDP 0x80 95#define ALTF_RESVPORT 0x100 96#define ALTF_SEQPACKET 0x200 97#define ALTF_NQNFS 0x400 98#define ALTF_SOFT 0x800 99#define ALTF_TCP 0x1000 100#define ALTF_PORT 0x2000 101#define ALTF_NFSV2 0x4000 102#define ALTF_ACREGMIN 0x8000 103#define ALTF_ACREGMAX 0x10000 104#define ALTF_ACDIRMIN 0x20000 105#define ALTF_ACDIRMAX 0x40000 106 107struct mntopt mopts[] = { 108 MOPT_STDOPTS, 109 MOPT_FORCE, 110 MOPT_UPDATE, 111 MOPT_ASYNC, 112 { "bg", 0, ALTF_BG, 1 }, 113 { "conn", 1, ALTF_NOCONN, 1 }, 114 { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, 115 { "intr", 0, ALTF_INTR, 1 }, 116#ifdef NFSKERB 117 { "kerb", 0, ALTF_KERB, 1 }, 118#endif 119 { "nfsv3", 0, ALTF_NFSV3, 1 }, 120 { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, 121 { "mntudp", 0, ALTF_MNTUDP, 1 }, 122 { "resvport", 0, ALTF_RESVPORT, 1 }, 123 { "nqnfs", 0, ALTF_NQNFS, 1 }, 124 { "soft", 0, ALTF_SOFT, 1 }, 125 { "tcp", 0, ALTF_TCP, 1 }, 126 { "port=", 0, ALTF_PORT, 1 }, 127 { "nfsv2", 0, ALTF_NFSV2, 1 }, 128 { "acregmin=", 0, ALTF_ACREGMIN, 1 }, 129 { "acregmax=", 0, ALTF_ACREGMAX, 1 }, 130 { "acdirmin=", 0, ALTF_ACDIRMIN, 1 }, 131 { "acdirmax=", 0, ALTF_ACDIRMAX, 1 }, 132 { NULL } 133}; 134 135struct nfs_args nfsdefargs = { 136 NFS_ARGSVERSION, 137 (struct sockaddr *)0, 138 sizeof (struct sockaddr_in), 139 SOCK_DGRAM, 140 0, 141 (u_char *)0, 142 0, 143 NFSMNT_RESVPORT, 144 NFS_WSIZE, 145 NFS_RSIZE, 146 NFS_READDIRSIZE, 147 10, 148 NFS_RETRANS, 149 NFS_MAXGRPS, 150 NFS_DEFRAHEAD, 151 NQ_DEFLEASE, 152 NQ_DEADTHRESH, 153 (char *)0, 154 /* args version 4 */ 155 NFS_MINATTRTIMO, 156 NFS_MAXATTRTIMO, 157 NFS_MINDIRATTRTIMO, 158 NFS_MAXDIRATTRTIMO, 159}; 160 161/* Table for af,sotype -> netid conversions. */ 162struct nc_protos { 163 char *netid; 164 int af; 165 int sotype; 166} nc_protos[] = { 167 {"udp", AF_INET, SOCK_DGRAM}, 168 {"tcp", AF_INET, SOCK_STREAM}, 169 {"udp6", AF_INET6, SOCK_DGRAM}, 170 {"tcp6", AF_INET6, SOCK_STREAM}, 171 {NULL} 172}; 173 174struct nfhret { 175 u_long stat; 176 long vers; 177 long auth; 178 long fhsize; 179 u_char nfh[NFSX_V3FHMAX]; 180}; 181#define BGRND 1 182#define ISBGRND 2 183int retrycnt = -1; 184int opflags = 0; 185int nfsproto = IPPROTO_UDP; 186int mnttcp_ok = 1; 187char *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 188enum mountmode { 189 ANY, 190 V2, 191 V3 192} mountmode = ANY; 193 194#ifdef NFSKERB 195char inst[INST_SZ]; 196char realm[REALM_SZ]; 197struct { 198 u_long kind; 199 KTEXT_ST kt; 200} ktick; 201struct nfsrpc_nickverf kverf; 202struct nfsrpc_fullblock kin, kout; 203NFSKERBKEY_T kivec; 204CREDENTIALS kcr; 205struct timeval ktv; 206NFSKERBKEYSCHED_T kerb_keysched; 207#endif 208 209/* Return codes for nfs_tryproto. */ 210enum tryret { 211 TRYRET_SUCCESS, 212 TRYRET_TIMEOUT, /* No response received. */ 213 TRYRET_REMOTEERR, /* Error received from remote server. */ 214 TRYRET_LOCALERR /* Local failure. */ 215}; 216 217int getnfsargs __P((char *, struct nfs_args *)); 218/* void set_rpc_maxgrouplist __P((int)); */ 219struct netconfig *getnetconf_cached(const char *netid); 220char *netidbytype(int af, int sotype); 221void usage __P((void)) __dead2; 222int xdr_dir __P((XDR *, char *)); 223int xdr_fh __P((XDR *, struct nfhret *)); 224enum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, 225 char *hostp, char *spec, char **errstr); 226enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 227 228/* 229 * Used to set mount flags with getmntopts. Call with dir=TRUE to 230 * initialize altflags from the current mount flags. Call with 231 * dir=FALSE to update mount flags with the new value of altflags after 232 * the call to getmntopts. 233 */ 234static void 235set_flags(int* altflags, int* nfsflags, int dir) 236{ 237#define F2(af, nf) \ 238 if (dir) { \ 239 if (*nfsflags & NFSMNT_##nf) \ 240 *altflags |= ALTF_##af; \ 241 else \ 242 *altflags &= ~ALTF_##af; \ 243 } else { \ 244 if (*altflags & ALTF_##af) \ 245 *nfsflags |= NFSMNT_##nf; \ 246 else \ 247 *nfsflags &= ~NFSMNT_##nf; \ 248 } 249#define F(f) F2(f,f) 250 251 F(NOCONN); 252 F(DUMBTIMR); 253 F2(INTR, INT); 254#ifdef NFSKERB 255 F(KERB); 256#endif 257 F(RDIRPLUS); 258 F(RESVPORT); 259 F(NQNFS); 260 F(SOFT); 261 262#undef F 263#undef F2 264} 265 266int 267main(argc, argv) 268 int argc; 269 char *argv[]; 270{ 271 register int c; 272 register struct nfs_args *nfsargsp; 273 struct nfs_args nfsargs; 274 struct nfsd_cargs ncd; 275 int mntflags, altflags, nfssvc_flag, num; 276 char *name, *p, *spec; 277 char mntpath[MAXPATHLEN]; 278 struct vfsconf vfc; 279 int error = 0; 280#ifdef NFSKERB 281 uid_t last_ruid; 282 283 last_ruid = -1; 284 (void)strcpy(realm, KRB_REALM); 285 if (sizeof (struct nfsrpc_nickverf) != RPCX_NICKVERF || 286 sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK || 287 ((char *)&ktick.kt) - ((char *)&ktick) != NFSX_UNSIGNED || 288 ((char *)ktick.kt.dat) - ((char *)&ktick) != 2 * NFSX_UNSIGNED) 289 fprintf(stderr, "Yikes! NFSKERB structs not packed!!\n"); 290#endif /* NFSKERB */ 291 292 mntflags = 0; 293 altflags = 0; 294 nfsargs = nfsdefargs; 295 nfsargsp = &nfsargs; 296 while ((c = getopt(argc, argv, 297 "23a:bcdD:g:I:iKL:lm:No:PpqR:r:sTt:w:x:U")) != -1) 298 switch (c) { 299 case '2': 300 mountmode = V2; 301 break; 302 case '3': 303 mountmode = V3; 304 break; 305 case 'a': 306 num = strtol(optarg, &p, 10); 307 if (*p || num < 0) 308 errx(1, "illegal -a value -- %s", optarg); 309 nfsargsp->readahead = num; 310 nfsargsp->flags |= NFSMNT_READAHEAD; 311 break; 312 case 'b': 313 opflags |= BGRND; 314 break; 315 case 'c': 316 nfsargsp->flags |= NFSMNT_NOCONN; 317 break; 318 case 'D': 319 num = strtol(optarg, &p, 10); 320 if (*p || num <= 0) 321 errx(1, "illegal -D value -- %s", optarg); 322 nfsargsp->deadthresh = num; 323 nfsargsp->flags |= NFSMNT_DEADTHRESH; 324 break; 325 case 'd': 326 nfsargsp->flags |= NFSMNT_DUMBTIMR; 327 break; 328#if 0 /* XXXX */ 329 case 'g': 330 num = strtol(optarg, &p, 10); 331 if (*p || num <= 0) 332 errx(1, "illegal -g value -- %s", optarg); 333 set_rpc_maxgrouplist(num); 334 nfsargsp->maxgrouplist = num; 335 nfsargsp->flags |= NFSMNT_MAXGRPS; 336 break; 337#endif 338 case 'I': 339 num = strtol(optarg, &p, 10); 340 if (*p || num <= 0) 341 errx(1, "illegal -I value -- %s", optarg); 342 nfsargsp->readdirsize = num; 343 nfsargsp->flags |= NFSMNT_READDIRSIZE; 344 break; 345 case 'i': 346 nfsargsp->flags |= NFSMNT_INT; 347 break; 348#ifdef NFSKERB 349 case 'K': 350 nfsargsp->flags |= NFSMNT_KERB; 351 break; 352#endif 353 case 'L': 354 num = strtol(optarg, &p, 10); 355 if (*p || num < 2) 356 errx(1, "illegal -L value -- %s", optarg); 357 nfsargsp->leaseterm = num; 358 nfsargsp->flags |= NFSMNT_LEASETERM; 359 break; 360 case 'l': 361 nfsargsp->flags |= NFSMNT_RDIRPLUS; 362 break; 363#ifdef NFSKERB 364 case 'm': 365 (void)strncpy(realm, optarg, REALM_SZ - 1); 366 realm[REALM_SZ - 1] = '\0'; 367 break; 368#endif 369 case 'N': 370 nfsargsp->flags &= ~NFSMNT_RESVPORT; 371 break; 372 case 'o': 373 altflags = 0; 374 set_flags(&altflags, &nfsargsp->flags, TRUE); 375 if (mountmode == V2) 376 altflags |= ALTF_NFSV2; 377 else if (mountmode == V3) 378 altflags |= ALTF_NFSV3; 379 getmntopts(optarg, mopts, &mntflags, &altflags); 380 set_flags(&altflags, &nfsargsp->flags, FALSE); 381 /* 382 * Handle altflags which don't map directly to 383 * mount flags. 384 */ 385 if(altflags & ALTF_BG) 386 opflags |= BGRND; 387 if(altflags & ALTF_MNTUDP) 388 mnttcp_ok = 0; 389 if(altflags & ALTF_TCP) { 390 nfsargsp->sotype = SOCK_STREAM; 391 nfsproto = IPPROTO_TCP; 392 } 393 if(altflags & ALTF_PORT) { 394 /* 395 * XXX Converting from a string to an int 396 * and back again is silly, and we should 397 * allow /etc/services names. 398 */ 399 asprintf(&portspec, "%d", 400 atoi(strstr(optarg, "port=") + 5)); 401 if (portspec == NULL) 402 err(1, "asprintf"); 403 } 404 mountmode = ANY; 405 if(altflags & ALTF_NFSV2) 406 mountmode = V2; 407 if(altflags & ALTF_NFSV3) 408 mountmode = V3; 409 if(altflags & ALTF_ACREGMIN) 410 nfsargsp->acregmin = atoi(strstr(optarg, 411 "acregmin=") + 9); 412 if(altflags & ALTF_ACREGMAX) 413 nfsargsp->acregmax = atoi(strstr(optarg, 414 "acregmax=") + 9); 415 if(altflags & ALTF_ACDIRMIN) 416 nfsargsp->acdirmin = atoi(strstr(optarg, 417 "acdirmin=") + 9); 418 if(altflags & ALTF_ACDIRMAX) 419 nfsargsp->acdirmax = atoi(strstr(optarg, 420 "acdirmax=") + 9); 421 break; 422 case 'P': 423 /* obsolete for NFSMNT_RESVPORT, now default */ 424 break; 425 case 'q': 426 mountmode = V3; 427 nfsargsp->flags |= NFSMNT_NQNFS; 428 break; 429 case 'R': 430 num = strtol(optarg, &p, 10); 431 if (*p || num < 0) 432 errx(1, "illegal -R value -- %s", optarg); 433 retrycnt = num; 434 break; 435 case 'r': 436 num = strtol(optarg, &p, 10); 437 if (*p || num <= 0) 438 errx(1, "illegal -r value -- %s", optarg); 439 nfsargsp->rsize = num; 440 nfsargsp->flags |= NFSMNT_RSIZE; 441 break; 442 case 's': 443 nfsargsp->flags |= NFSMNT_SOFT; 444 break; 445 case 'T': 446 nfsargsp->sotype = SOCK_STREAM; 447 nfsproto = IPPROTO_TCP; 448 break; 449 case 't': 450 num = strtol(optarg, &p, 10); 451 if (*p || num <= 0) 452 errx(1, "illegal -t value -- %s", optarg); 453 nfsargsp->timeo = num; 454 nfsargsp->flags |= NFSMNT_TIMEO; 455 break; 456 case 'w': 457 num = strtol(optarg, &p, 10); 458 if (*p || num <= 0) 459 errx(1, "illegal -w value -- %s", optarg); 460 nfsargsp->wsize = num; 461 nfsargsp->flags |= NFSMNT_WSIZE; 462 break; 463 case 'x': 464 num = strtol(optarg, &p, 10); 465 if (*p || num <= 0) 466 errx(1, "illegal -x value -- %s", optarg); 467 nfsargsp->retrans = num; 468 nfsargsp->flags |= NFSMNT_RETRANS; 469 break; 470 case 'U': 471 mnttcp_ok = 0; 472 break; 473 default: 474 usage(); 475 break; 476 } 477 argc -= optind; 478 argv += optind; 479 480 if (argc != 2) { 481 usage(); 482 /* NOTREACHED */ 483 } 484 485 spec = *argv++; 486 name = *argv; 487 488 if (retrycnt == -1) 489 /* The default is to keep retrying forever. */ 490 retrycnt = 0; 491 if (!getnfsargs(spec, nfsargsp)) 492 exit(1); 493 494 /* resolve the mountpoint with realpath(3) */ 495 (void)checkpath(name, mntpath); 496 497 error = getvfsbyname("nfs", &vfc); 498 if (error && vfsisloadable("nfs")) { 499 if(vfsload("nfs")) 500 err(EX_OSERR, "vfsload(nfs)"); 501 endvfsent(); /* clear cache */ 502 error = getvfsbyname("nfs", &vfc); 503 } 504 if (error) 505 errx(EX_OSERR, "nfs filesystem is not available"); 506 507 if (mount(vfc.vfc_name, mntpath, mntflags, nfsargsp)) 508 err(1, "%s", mntpath); 509 if (nfsargsp->flags & (NFSMNT_NQNFS | NFSMNT_KERB)) { 510 if ((opflags & ISBGRND) == 0) { 511 if (daemon(0, 0) != 0) 512 err(1, "daemon"); 513 } 514 openlog("mount_nfs", LOG_PID, LOG_DAEMON); 515 nfssvc_flag = NFSSVC_MNTD; 516 ncd.ncd_dirp = mntpath; 517 while (nfssvc(nfssvc_flag, (caddr_t)&ncd) < 0) { 518 if (errno != ENEEDAUTH) { 519 syslog(LOG_ERR, "nfssvc err %m"); 520 continue; 521 } 522 nfssvc_flag = 523 NFSSVC_MNTD | NFSSVC_GOTAUTH | NFSSVC_AUTHINFAIL; 524#ifdef NFSKERB 525 /* 526 * Set up as ncd_authuid for the kerberos call. 527 * Must set ruid to ncd_authuid and reset the 528 * ticket name iff ncd_authuid is not the same 529 * as last time, so that the right ticket file 530 * is found. 531 * Get the Kerberos credential structure so that 532 * we have the session key and get a ticket for 533 * this uid. 534 * For more info see the IETF Draft "Authentication 535 * in ONC RPC". 536 */ 537 if (ncd.ncd_authuid != last_ruid) { 538 char buf[512]; 539 (void)sprintf(buf, "%s%d", 540 TKT_ROOT, ncd.ncd_authuid); 541 krb_set_tkt_string(buf); 542 last_ruid = ncd.ncd_authuid; 543 } 544 setreuid(ncd.ncd_authuid, 0); 545 kret = krb_get_cred(NFS_KERBSRV, inst, realm, &kcr); 546 if (kret == RET_NOTKT) { 547 kret = get_ad_tkt(NFS_KERBSRV, inst, realm, 548 DEFAULT_TKT_LIFE); 549 if (kret == KSUCCESS) 550 kret = krb_get_cred(NFS_KERBSRV, inst, realm, 551 &kcr); 552 } 553 if (kret == KSUCCESS) 554 kret = krb_mk_req(&ktick.kt, NFS_KERBSRV, inst, 555 realm, 0); 556 557 /* 558 * Fill in the AKN_FULLNAME authenticator and verifier. 559 * Along with the Kerberos ticket, we need to build 560 * the timestamp verifier and encrypt it in CBC mode. 561 */ 562 if (kret == KSUCCESS && 563 ktick.kt.length <= (RPCAUTH_MAXSIZ-3*NFSX_UNSIGNED) 564 && gettimeofday(&ktv, (struct timezone *)0) == 0) { 565 ncd.ncd_authtype = RPCAUTH_KERB4; 566 ncd.ncd_authstr = (u_char *)&ktick; 567 ncd.ncd_authlen = nfsm_rndup(ktick.kt.length) + 568 3 * NFSX_UNSIGNED; 569 ncd.ncd_verfstr = (u_char *)&kverf; 570 ncd.ncd_verflen = sizeof (kverf); 571 memmove(ncd.ncd_key, kcr.session, 572 sizeof (kcr.session)); 573 kin.t1 = htonl(ktv.tv_sec); 574 kin.t2 = htonl(ktv.tv_usec); 575 kin.w1 = htonl(NFS_KERBTTL); 576 kin.w2 = htonl(NFS_KERBTTL - 1); 577 bzero((caddr_t)kivec, sizeof (kivec)); 578 579 /* 580 * Encrypt kin in CBC mode using the session 581 * key in kcr. 582 */ 583 XXX 584 585 /* 586 * Finally, fill the timestamp verifier into the 587 * authenticator and verifier. 588 */ 589 ktick.kind = htonl(RPCAKN_FULLNAME); 590 kverf.kind = htonl(RPCAKN_FULLNAME); 591 NFS_KERBW1(ktick.kt) = kout.w1; 592 ktick.kt.length = htonl(ktick.kt.length); 593 kverf.verf.t1 = kout.t1; 594 kverf.verf.t2 = kout.t2; 595 kverf.verf.w2 = kout.w2; 596 nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH; 597 } 598 setreuid(0, 0); 599#endif /* NFSKERB */ 600 } 601 } 602 exit(0); 603} 604 605int 606getnfsargs(spec, nfsargsp) 607 char *spec; 608 struct nfs_args *nfsargsp; 609{ 610 struct addrinfo hints, *ai_nfs, *ai; 611#ifdef NFSKERB 612 char host[NI_MAXHOST], serv[NI_MAXSERV]; 613#endif 614 enum tryret ret; 615 int ecode, speclen, remoteerr; 616 char *hostp, *delimp, *errstr; 617#ifdef NFSKERB 618 char *cp; 619#endif 620 size_t len; 621 static char nam[MNAMELEN + 1]; 622 623 if ((delimp = strrchr(spec, ':')) != NULL) { 624 hostp = spec; 625 spec = delimp + 1; 626 } else if ((delimp = strrchr(spec, '@')) != NULL) { 627 warnx("path@server syntax is deprecated, use server:path"); 628 hostp = delimp + 1; 629 } else { 630 warnx("no <host>:<dirpath> nfs-name"); 631 return (0); 632 } 633 *delimp = '\0'; 634 635 /* 636 * If there has been a trailing slash at mounttime it seems 637 * that some mountd implementations fail to remove the mount 638 * entries from their mountlist while unmounting. 639 */ 640 for (speclen = strlen(spec); 641 speclen > 1 && spec[speclen - 1] == '/'; 642 speclen--) 643 spec[speclen - 1] = '\0'; 644 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 645 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 646 return (0); 647 } 648 /* Make both '@' and ':' notations equal */ 649 if (*hostp != '\0') { 650 len = strlen(hostp); 651 memmove(nam, hostp, len); 652 nam[len] = ':'; 653 memmove(nam + len + 1, spec, speclen); 654 nam[len + speclen + 1] = '\0'; 655 } 656 657 /* 658 * Handle an internet host address and reverse resolve it if 659 * doing Kerberos. 660 */ 661 memset(&hints, 0, sizeof hints); 662 hints.ai_flags = AI_NUMERICHOST; 663 hints.ai_socktype = nfsargsp->sotype; 664 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) == 0) { 665#ifdef NFSKERB 666 if ((nfsargsp->flags & NFSMNT_KERB)) { 667 hints.ai_flags = 0; 668 if (getnameinfo(ai_nfs->ai_addr, ai_nfs->ai_addrlen, 669 host, sizeof host, serv, sizeof serv, 0) != 0) { 670 warnx("can't reverse resolve net address"); 671 return (0); 672 } 673 hostp = host; 674 } 675#endif /* NFSKERB */ 676 } else { 677 hints.ai_flags = 0; 678 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 679 != 0) { 680 if (portspec == NULL) 681 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 682 else 683 errx(1, "%s:%s: %s", hostp, portspec, 684 gai_strerror(ecode)); 685 return (0); 686 } 687 } 688#ifdef NFSKERB 689 if (nfsargsp->flags & NFSMNT_KERB) { 690 strncpy(inst, hp->h_name, INST_SZ); 691 inst[INST_SZ - 1] = '\0'; 692 if (cp = strchr(inst, '.')) 693 *cp = '\0'; 694 } 695#endif /* NFSKERB */ 696 697 ret = TRYRET_LOCALERR; 698 for (;;) { 699 /* 700 * Try each entry returned by getaddrinfo(). Note the 701 * occurence of remote errors by setting `remoteerr'. 702 */ 703 remoteerr = 0; 704 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 705 ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr); 706 if (ret == TRYRET_SUCCESS) 707 break; 708 if (ret != TRYRET_LOCALERR) 709 remoteerr = 1; 710 if ((opflags & ISBGRND) == 0) 711 fprintf(stderr, "%s\n", errstr); 712 } 713 if (ret == TRYRET_SUCCESS) 714 break; 715 716 /* Exit if all errors were local. */ 717 if (!remoteerr) 718 exit(1); 719 720 /* 721 * If retrycnt == 0, we are to keep retrying forever. 722 * Otherwise decrement it, and exit if it hits zero. 723 */ 724 if (retrycnt != 0 && --retrycnt == 0) 725 exit(1); 726 727 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 728 warnx("Cannot immediately mount %s:%s, backgrounding", 729 hostp, spec); 730 opflags |= ISBGRND; 731 if (daemon(0, 0) != 0) 732 err(1, "daemon"); 733 } 734 sleep(60); 735 } 736 freeaddrinfo(ai_nfs); 737 nfsargsp->hostname = nam; 738 /* Add mounted filesystem to PATH_MOUNTTAB */ 739 if (!add_mtab(hostp, spec)) 740 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 741 return (1); 742} 743 744/* 745 * Try to set up the NFS arguments according to the address 746 * family, protocol (and possibly port) specified in `ai'. 747 * 748 * Returns TRYRET_SUCCESS if successful, or: 749 * TRYRET_TIMEOUT The server did not respond. 750 * TRYRET_REMOTEERR The server reported an error. 751 * TRYRET_LOCALERR Local failure. 752 * 753 * In all error cases, *errstr will be set to a statically-allocated string 754 * describing the error. 755 */ 756enum tryret 757nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp, 758 char *spec, char **errstr) 759{ 760 static char errbuf[256]; 761 struct sockaddr_storage nfs_ss; 762 struct netbuf nfs_nb; 763 struct nfhret nfhret; 764 struct timeval try; 765 struct rpc_err rpcerr; 766 CLIENT *clp; 767 struct netconfig *nconf, *nconf_mnt; 768 char *netid, *netid_mnt; 769 int doconnect, nfsvers, mntvers; 770 enum clnt_stat stat; 771 enum mountmode trymntmode; 772 773 trymntmode = mountmode; 774 errbuf[0] = '\0'; 775 *errstr = errbuf; 776 777 if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) { 778 snprintf(errbuf, sizeof errbuf, 779 "af %d sotype %d not supported", ai->ai_family, 780 nfsargsp->sotype); 781 return (TRYRET_LOCALERR); 782 } 783 if ((nconf = getnetconf_cached(netid)) == NULL) { 784 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 785 return (TRYRET_LOCALERR); 786 } 787 /* The RPCPROG_MNT netid may be different. */ 788 if (mnttcp_ok) { 789 netid_mnt = netid; 790 nconf_mnt = nconf; 791 } else { 792 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 793 == NULL) { 794 snprintf(errbuf, sizeof errbuf, 795 "af %d sotype SOCK_DGRAM not supported", 796 ai->ai_family); 797 return (TRYRET_LOCALERR); 798 } 799 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 800 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 801 nc_sperror()); 802 return (TRYRET_LOCALERR); 803 } 804 } 805 806tryagain: 807 if (trymntmode == V2) { 808 nfsvers = 2; 809 mntvers = 1; 810 } else { 811 nfsvers = 3; 812 mntvers = 3; 813 } 814 815 if (portspec != NULL) { 816 /* `ai' contains the complete nfsd sockaddr. */ 817 nfs_nb.buf = ai->ai_addr; 818 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 819 } else { 820 /* Ask the remote rpcbind. */ 821 nfs_nb.buf = &nfs_ss; 822 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 823 824 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 825 hostp)) { 826 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 827 trymntmode == ANY) { 828 trymntmode = V2; 829 goto tryagain; 830 } 831 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 832 netid, hostp, spec, 833 clnt_spcreateerror("RPCPROG_NFS")); 834 return (returncode(rpc_createerr.cf_stat, 835 &rpc_createerr.cf_error)); 836 } 837 } 838 839 /* Check that the server (nfsd) responds on the port we have chosen. */ 840 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers, 841 0, 0); 842 if (clp == NULL) { 843 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 844 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 845 return (returncode(rpc_createerr.cf_stat, 846 &rpc_createerr.cf_error)); 847 } 848 if (nfsargsp->sotype == SOCK_DGRAM) { 849 /* 850 * Use connect(), to match what the kernel does. This 851 * catches cases where the server responds from the 852 * wrong source address. 853 */ 854 doconnect = 1; 855 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 856 clnt_destroy(clp); 857 snprintf(errbuf, sizeof errbuf, 858 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 859 spec); 860 return (TRYRET_LOCALERR); 861 } 862 } 863 864 try.tv_sec = 10; 865 try.tv_usec = 0; 866 stat = clnt_call(clp, NFSPROC_NULL, xdr_void, NULL, xdr_void, NULL, 867 try); 868 if (stat != RPC_SUCCESS) { 869 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 870 clnt_destroy(clp); 871 trymntmode = V2; 872 goto tryagain; 873 } 874 clnt_geterr(clp, &rpcerr); 875 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 876 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 877 clnt_destroy(clp); 878 return (returncode(stat, &rpcerr)); 879 } 880 clnt_destroy(clp); 881 882 /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ 883 try.tv_sec = 10; 884 try.tv_usec = 0; 885 clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt); 886 if (clp == NULL) { 887 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 888 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 889 return (returncode(rpc_createerr.cf_stat, 890 &rpc_createerr.cf_error)); 891 } 892 clp->cl_auth = authsys_create_default(); 893 if (nfsargsp->flags & NFSMNT_KERB) 894 nfhret.auth = RPCAUTH_KERB4; 895 else 896 nfhret.auth = RPCAUTH_UNIX; 897 nfhret.vers = mntvers; 898 stat = clnt_call(clp, RPCMNT_MOUNT, xdr_dir, spec, xdr_fh, &nfhret, 899 try); 900 auth_destroy(clp->cl_auth); 901 if (stat != RPC_SUCCESS) { 902 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 903 clnt_destroy(clp); 904 trymntmode = V2; 905 goto tryagain; 906 } 907 clnt_geterr(clp, &rpcerr); 908 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 909 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 910 clnt_destroy(clp); 911 return (returncode(stat, &rpcerr)); 912 } 913 clnt_destroy(clp); 914 915 if (nfhret.stat != 0) { 916 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 917 hostp, spec, strerror(nfhret.stat)); 918 return (TRYRET_REMOTEERR); 919 } 920 921 /* 922 * Store the filehandle and server address in nfsargsp, making 923 * sure to copy any locally allocated structures. 924 */ 925 nfsargsp->addrlen = nfs_nb.len; 926 nfsargsp->addr = malloc(nfsargsp->addrlen); 927 nfsargsp->fhsize = nfhret.fhsize; 928 nfsargsp->fh = malloc(nfsargsp->fhsize); 929 if (nfsargsp->addr == NULL || nfsargsp->fh == NULL) 930 err(1, "malloc"); 931 bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen); 932 bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize); 933 934 if (nfsvers == 3) 935 nfsargsp->flags |= NFSMNT_NFSV3; 936 else 937 nfsargsp->flags &= ~NFSMNT_NFSV3; 938 939 return (TRYRET_SUCCESS); 940} 941 942 943/* 944 * Catagorise a RPC return status and error into an `enum tryret' 945 * return code. 946 */ 947enum tryret 948returncode(enum clnt_stat stat, struct rpc_err *rpcerr) 949{ 950 switch (stat) { 951 case RPC_TIMEDOUT: 952 return (TRYRET_TIMEOUT); 953 case RPC_PMAPFAILURE: 954 case RPC_PROGNOTREGISTERED: 955 case RPC_PROGVERSMISMATCH: 956 /* XXX, these can be local or remote. */ 957 case RPC_CANTSEND: 958 case RPC_CANTRECV: 959 return (TRYRET_REMOTEERR); 960 case RPC_SYSTEMERROR: 961 switch (rpcerr->re_errno) { 962 case ETIMEDOUT: 963 return (TRYRET_TIMEOUT); 964 case ENOMEM: 965 break; 966 default: 967 return (TRYRET_REMOTEERR); 968 } 969 /* FALLTHROUGH */ 970 default: 971 break; 972 } 973 return (TRYRET_LOCALERR); 974} 975 976/* 977 * Look up a netid based on an address family and socket type. 978 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 979 * 980 * XXX there should be a library function for this. 981 */ 982char * 983netidbytype(int af, int sotype) { 984 struct nc_protos *p; 985 986 for (p = nc_protos; p->netid != NULL; p++) { 987 if (af != p->af || sotype != p->sotype) 988 continue; 989 return (p->netid); 990 } 991 return (NULL); 992} 993 994/* 995 * Look up a netconfig entry based on a netid, and cache the result so 996 * that we don't need to remember to call freenetconfigent(). 997 * 998 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 999 * work on failure. 1000 */ 1001struct netconfig * 1002getnetconf_cached(const char *netid) { 1003 static struct nc_entry { 1004 struct netconfig *nconf; 1005 struct nc_entry *next; 1006 } *head; 1007 struct nc_entry *p; 1008 struct netconfig *nconf; 1009 1010 for (p = head; p != NULL; p = p->next) 1011 if (strcmp(netid, p->nconf->nc_netid) == 0) 1012 return (p->nconf); 1013 1014 if ((nconf = getnetconfigent(netid)) == NULL) 1015 return (NULL); 1016 if ((p = malloc(sizeof(*p))) == NULL) 1017 err(1, "malloc"); 1018 p->nconf = nconf; 1019 p->next = head; 1020 head = p; 1021 1022 return (p->nconf); 1023} 1024 1025/* 1026 * xdr routines for mount rpc's 1027 */ 1028int 1029xdr_dir(xdrsp, dirp) 1030 XDR *xdrsp; 1031 char *dirp; 1032{ 1033 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 1034} 1035 1036int 1037xdr_fh(xdrsp, np) 1038 XDR *xdrsp; 1039 register struct nfhret *np; 1040{ 1041 register int i; 1042 long auth, authcnt, authfnd = 0; 1043 1044 if (!xdr_u_long(xdrsp, &np->stat)) 1045 return (0); 1046 if (np->stat) 1047 return (1); 1048 switch (np->vers) { 1049 case 1: 1050 np->fhsize = NFSX_V2FH; 1051 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); 1052 case 3: 1053 if (!xdr_long(xdrsp, &np->fhsize)) 1054 return (0); 1055 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) 1056 return (0); 1057 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1058 return (0); 1059 if (!xdr_long(xdrsp, &authcnt)) 1060 return (0); 1061 for (i = 0; i < authcnt; i++) { 1062 if (!xdr_long(xdrsp, &auth)) 1063 return (0); 1064 if (auth == np->auth) 1065 authfnd++; 1066 } 1067 /* 1068 * Some servers, such as DEC's OSF/1 return a nil authenticator 1069 * list to indicate RPCAUTH_UNIX. 1070 */ 1071 if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX)) 1072 np->stat = EAUTH; 1073 return (1); 1074 }; 1075 return (0); 1076} 1077 1078void 1079usage() 1080{ 1081 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1082"usage: mount_nfs [-23KNPTUbcdilqs] [-D deadthresh] [-I readdirsize]", 1083" [-L leaseterm] [-R retrycnt] [-a maxreadahead]", 1084" [-g maxgroups] [-m realm] [-o options] [-r readsize]", 1085" [-t timeout] [-w writesize] [-x retrans] rhost:path node"); 1086 exit(1); 1087} 1088