mount_nfs.c revision 92806
1148330Snetchild/* 2148330Snetchild * Copyright (c) 1992, 1993, 1994 3148330Snetchild * The Regents of the University of California. All rights reserved. 4148330Snetchild * 5148330Snetchild * This code is derived from software contributed to Berkeley by 6148330Snetchild * Rick Macklem at The University of Guelph. 7148330Snetchild * 8148330Snetchild * Redistribution and use in source and binary forms, with or without 9148330Snetchild * modification, are permitted provided that the following conditions 10148330Snetchild * are met: 11148330Snetchild * 1. Redistributions of source code must retain the above copyright 12148330Snetchild * notice, this list of conditions and the following disclaimer. 13148330Snetchild * 2. Redistributions in binary form must reproduce the above copyright 14148543Snetchild * notice, this list of conditions and the following disclaimer in the 15148543Snetchild * documentation and/or other materials provided with the distribution. 16215669Snetchild * 3. All advertising materials mentioning features or use of this software 17215669Snetchild * must display the following acknowledgement: 18215669Snetchild * This product includes software developed by the University of 19215669Snetchild * California, Berkeley and its contributors. 20215669Snetchild * 4. Neither the name of the University nor the names of its contributors 21215669Snetchild * may be used to endorse or promote products derived from this software 22215669Snetchild * without specific prior written permission. 23215669Snetchild * 24215669Snetchild * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25299007Spfg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26216179Suqs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27216179Suqs * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28216179Suqs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29216179Suqs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30216179Suqs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31216179Suqs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32216179Suqs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33216179Suqs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34216179Suqs * SUCH DAMAGE. 35216179Suqs */ 36216179Suqs 37216179Suqs#ifndef lint 38216179Suqsstatic const char copyright[] = 39216179Suqs"@(#) Copyright (c) 1992, 1993, 1994\n\ 40148330Snetchild The Regents of the University of California. All rights reserved.\n"; 41363496Sdim#endif /* not lint */ 42363496Sdim 43363496Sdim#ifndef lint 44363496Sdim#if 0 45363496Sdimstatic char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 46363496Sdim#endif 47363496Sdimstatic const char rcsid[] = 48363496Sdim "$FreeBSD: head/sbin/mount_nfs/mount_nfs.c 92806 2002-03-20 17:55:10Z obrien $"; 49363496Sdim#endif /* not lint */ 50363496Sdim 51363496Sdim#include <sys/param.h> 52363496Sdim#include <sys/mount.h> 53363496Sdim#include <sys/socket.h> 54363496Sdim#include <sys/stat.h> 55363496Sdim#include <sys/syslog.h> 56363496Sdim 57363496Sdim#include <rpc/rpc.h> 58363496Sdim#include <rpc/pmap_clnt.h> 59363496Sdim#include <rpc/pmap_prot.h> 60363496Sdim 61363496Sdim#include <nfs/rpcv2.h> 62363496Sdim#include <nfs/nfsproto.h> 63363496Sdim#include <nfsclient/nfs.h> 64363496Sdim#include <nfsclient/nfsargs.h> 65363496Sdim 66363496Sdim#include <arpa/inet.h> 67363496Sdim 68363496Sdim#include <ctype.h> 69363496Sdim#include <err.h> 70363496Sdim#include <errno.h> 71363496Sdim#include <fcntl.h> 72363496Sdim#include <netdb.h> 73363496Sdim#include <stdio.h> 74363496Sdim#include <stdlib.h> 75363496Sdim#include <strings.h> 76363496Sdim#include <sysexits.h> 77363496Sdim#include <unistd.h> 78363496Sdim 79363496Sdim#include "mntopts.h" 80363496Sdim#include "mounttab.h" 81363496Sdim 82363496Sdim#define ALTF_BG 0x1 83363496Sdim#define ALTF_NOCONN 0x2 84363496Sdim#define ALTF_DUMBTIMR 0x4 85363496Sdim#define ALTF_INTR 0x8 86363496Sdim#define ALTF_NFSV3 0x20 87363496Sdim#define ALTF_RDIRPLUS 0x40 88363496Sdim#define ALTF_MNTUDP 0x80 89363496Sdim#define ALTF_RESVPORT 0x100 90363496Sdim#define ALTF_SEQPACKET 0x200 91363496Sdim#define ALTF_SOFT 0x800 92363496Sdim#define ALTF_TCP 0x1000 93363496Sdim#define ALTF_PORT 0x2000 94363496Sdim#define ALTF_NFSV2 0x4000 95363496Sdim#define ALTF_ACREGMIN 0x8000 96363496Sdim#define ALTF_ACREGMAX 0x10000 97363496Sdim#define ALTF_ACDIRMIN 0x20000 98363496Sdim#define ALTF_ACDIRMAX 0x40000 99363496Sdim#define ALTF_NOLOCKD 0x80000 100363496Sdim 101363496Sdimstruct mntopt mopts[] = { 102363496Sdim MOPT_STDOPTS, 103363496Sdim MOPT_FORCE, 104363496Sdim MOPT_UPDATE, 105363496Sdim MOPT_ASYNC, 106363496Sdim { "bg", 0, ALTF_BG, 1 }, 107363496Sdim { "conn", 1, ALTF_NOCONN, 1 }, 108363496Sdim { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, 109363496Sdim { "intr", 0, ALTF_INTR, 1 }, 110363496Sdim { "nfsv3", 0, ALTF_NFSV3, 1 }, 111363496Sdim { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, 112363496Sdim { "mntudp", 0, ALTF_MNTUDP, 1 }, 113363496Sdim { "resvport", 0, ALTF_RESVPORT, 1 }, 114363496Sdim { "soft", 0, ALTF_SOFT, 1 }, 115363496Sdim { "tcp", 0, ALTF_TCP, 1 }, 116363496Sdim { "port=", 0, ALTF_PORT, 1 }, 117363496Sdim { "nfsv2", 0, ALTF_NFSV2, 1 }, 118363496Sdim { "acregmin=", 0, ALTF_ACREGMIN, 1 }, 119363496Sdim { "acregmax=", 0, ALTF_ACREGMAX, 1 }, 120363496Sdim { "acdirmin=", 0, ALTF_ACDIRMIN, 1 }, 121363496Sdim { "acdirmax=", 0, ALTF_ACDIRMAX, 1 }, 122363496Sdim { "lockd", 1, ALTF_NOLOCKD, 1 }, 123363496Sdim { NULL } 124363496Sdim}; 125363496Sdim 126363496Sdimstruct nfs_args nfsdefargs = { 127363496Sdim NFS_ARGSVERSION, 128363496Sdim (struct sockaddr *)0, 129363496Sdim sizeof (struct sockaddr_in), 130363496Sdim SOCK_DGRAM, 131363496Sdim 0, 132363496Sdim (u_char *)0, 133363496Sdim 0, 134363496Sdim NFSMNT_RESVPORT, 135363496Sdim NFS_WSIZE, 136363496Sdim NFS_RSIZE, 137363496Sdim NFS_READDIRSIZE, 138363496Sdim 10, 139363496Sdim NFS_RETRANS, 140363496Sdim NFS_MAXGRPS, 141363496Sdim NFS_DEFRAHEAD, 142363496Sdim 0, /* was: NQ_DEFLEASE */ 143363496Sdim NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 144363496Sdim (char *)0, 145363496Sdim /* args version 4 */ 146363496Sdim NFS_MINATTRTIMO, 147363496Sdim NFS_MAXATTRTIMO, 148363496Sdim NFS_MINDIRATTRTIMO, 149363496Sdim NFS_MAXDIRATTRTIMO, 150363496Sdim}; 151363496Sdim 152363496Sdim/* Table for af,sotype -> netid conversions. */ 153363496Sdimstruct nc_protos { 154363496Sdim char *netid; 155363496Sdim int af; 156363496Sdim int sotype; 157363496Sdim} nc_protos[] = { 158363496Sdim {"udp", AF_INET, SOCK_DGRAM}, 159363496Sdim {"tcp", AF_INET, SOCK_STREAM}, 160363496Sdim {"udp6", AF_INET6, SOCK_DGRAM}, 161363496Sdim {"tcp6", AF_INET6, SOCK_STREAM}, 162363496Sdim {NULL} 163363496Sdim}; 164363496Sdim 165363496Sdimstruct nfhret { 166363496Sdim u_long stat; 167363496Sdim long vers; 168363496Sdim long auth; 169363496Sdim long fhsize; 170363496Sdim u_char nfh[NFSX_V3FHMAX]; 171363496Sdim}; 172363496Sdim#define BGRND 1 173363496Sdim#define ISBGRND 2 174363496Sdimint retrycnt = -1; 175363496Sdimint opflags = 0; 176363496Sdimint nfsproto = IPPROTO_UDP; 177363496Sdimint mnttcp_ok = 1; 178363496Sdimchar *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 179363496Sdimenum mountmode { 180363496Sdim ANY, 181363496Sdim V2, 182363496Sdim V3 183363496Sdim} mountmode = ANY; 184363496Sdim 185363496Sdim/* Return codes for nfs_tryproto. */ 186363496Sdimenum tryret { 187363496Sdim TRYRET_SUCCESS, 188363496Sdim TRYRET_TIMEOUT, /* No response received. */ 189363496Sdim TRYRET_REMOTEERR, /* Error received from remote server. */ 190363496Sdim TRYRET_LOCALERR /* Local failure. */ 191363496Sdim}; 192363496Sdim 193363496Sdimint getnfsargs __P((char *, struct nfs_args *)); 194363496Sdim/* void set_rpc_maxgrouplist __P((int)); */ 195363496Sdimstruct netconfig *getnetconf_cached(const char *netid); 196363496Sdimchar *netidbytype(int af, int sotype); 197363496Sdimvoid usage __P((void)) __dead2; 198363496Sdimint xdr_dir __P((XDR *, char *)); 199363496Sdimint xdr_fh __P((XDR *, struct nfhret *)); 200363496Sdimenum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, 201363496Sdim char *hostp, char *spec, char **errstr); 202363496Sdimenum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 203363496Sdim 204363496Sdim/* 205363496Sdim * Used to set mount flags with getmntopts. Call with dir=TRUE to 206363496Sdim * initialize altflags from the current mount flags. Call with 207363496Sdim * dir=FALSE to update mount flags with the new value of altflags after 208363496Sdim * the call to getmntopts. 209363496Sdim */ 210363496Sdimstatic void 211363496Sdimset_flags(int* altflags, int* nfsflags, int dir) 212363496Sdim{ 213363496Sdim#define F2(af, nf) \ 214363496Sdim if (dir) { \ 215363496Sdim if (*nfsflags & NFSMNT_##nf) \ 216363496Sdim *altflags |= ALTF_##af; \ 217363496Sdim else \ 218363496Sdim *altflags &= ~ALTF_##af; \ 219363496Sdim } else { \ 220363496Sdim if (*altflags & ALTF_##af) \ 221363496Sdim *nfsflags |= NFSMNT_##nf; \ 222363496Sdim else \ 223363496Sdim *nfsflags &= ~NFSMNT_##nf; \ 224363496Sdim } 225363496Sdim#define F(f) F2(f,f) 226363496Sdim 227363496Sdim F(NOCONN); 228363496Sdim F(DUMBTIMR); 229363496Sdim F2(INTR, INT); 230363496Sdim F(RDIRPLUS); 231363496Sdim F(RESVPORT); 232363496Sdim F(SOFT); 233363496Sdim F(NOLOCKD); 234363496Sdim 235363496Sdim#undef F 236363496Sdim#undef F2 237363496Sdim} 238363496Sdim 239363496Sdimint 240363496Sdimmain(argc, argv) 241363496Sdim int argc; 242363496Sdim char *argv[]; 243363496Sdim{ 244363496Sdim int c; 245363496Sdim struct nfs_args *nfsargsp; 246363496Sdim struct nfs_args nfsargs; 247363496Sdim int mntflags, altflags, nfssvc_flag, num; 248363496Sdim char *name, *p, *spec; 249363496Sdim char mntpath[MAXPATHLEN]; 250363496Sdim struct vfsconf vfc; 251363496Sdim int error = 0; 252363496Sdim 253363496Sdim mntflags = 0; 254363496Sdim altflags = 0; 255363496Sdim nfsargs = nfsdefargs; 256363496Sdim nfsargsp = &nfsargs; 257363496Sdim while ((c = getopt(argc, argv, 258363496Sdim "23a:bcdD:g:I:iLl:No:PpR:r:sTt:w:x:U")) != -1) 259363496Sdim switch (c) { 260363496Sdim case '2': 261363496Sdim mountmode = V2; 262363496Sdim break; 263363496Sdim case '3': 264363496Sdim mountmode = V3; 265363496Sdim break; 266363496Sdim case 'a': 267363496Sdim num = strtol(optarg, &p, 10); 268363496Sdim if (*p || num < 0) 269363496Sdim errx(1, "illegal -a value -- %s", optarg); 270363496Sdim nfsargsp->readahead = num; 271363496Sdim nfsargsp->flags |= NFSMNT_READAHEAD; 272363496Sdim break; 273363496Sdim case 'b': 274363496Sdim opflags |= BGRND; 275363496Sdim break; 276363496Sdim case 'c': 277363496Sdim nfsargsp->flags |= NFSMNT_NOCONN; 278363496Sdim break; 279363496Sdim case 'D': 280363496Sdim num = strtol(optarg, &p, 10); 281363496Sdim if (*p || num <= 0) 282363496Sdim errx(1, "illegal -D value -- %s", optarg); 283363496Sdim nfsargsp->deadthresh = num; 284363496Sdim nfsargsp->flags |= NFSMNT_DEADTHRESH; 285363496Sdim break; 286363496Sdim case 'd': 287363496Sdim nfsargsp->flags |= NFSMNT_DUMBTIMR; 288363496Sdim break; 289363496Sdim#if 0 /* XXXX */ 290363496Sdim case 'g': 291363496Sdim num = strtol(optarg, &p, 10); 292360785Sdim if (*p || num <= 0) 293360784Sdim errx(1, "illegal -g value -- %s", optarg); 294360784Sdim set_rpc_maxgrouplist(num); 295360784Sdim nfsargsp->maxgrouplist = num; 296360784Sdim nfsargsp->flags |= NFSMNT_MAXGRPS; 297360784Sdim break; 298360784Sdim#endif 299360784Sdim case 'I': 300360784Sdim num = strtol(optarg, &p, 10); 301360784Sdim if (*p || num <= 0) 302360784Sdim errx(1, "illegal -I value -- %s", optarg); 303360784Sdim nfsargsp->readdirsize = num; 304360784Sdim nfsargsp->flags |= NFSMNT_READDIRSIZE; 305360784Sdim break; 306360784Sdim case 'i': 307360784Sdim nfsargsp->flags |= NFSMNT_INT; 308360784Sdim break; 309360784Sdim case 'L': 310360784Sdim nfsargsp->flags |= NFSMNT_NOLOCKD; 311360784Sdim break; 312360784Sdim case 'l': 313360784Sdim nfsargsp->flags |= NFSMNT_RDIRPLUS; 314360784Sdim break; 315360784Sdim case 'N': 316360784Sdim nfsargsp->flags &= ~NFSMNT_RESVPORT; 317360784Sdim break; 318360784Sdim case 'o': 319360784Sdim altflags = 0; 320360784Sdim set_flags(&altflags, &nfsargsp->flags, TRUE); 321360784Sdim if (mountmode == V2) 322360784Sdim altflags |= ALTF_NFSV2; 323360784Sdim else if (mountmode == V3) 324360784Sdim altflags |= ALTF_NFSV3; 325360784Sdim getmntopts(optarg, mopts, &mntflags, &altflags); 326360784Sdim set_flags(&altflags, &nfsargsp->flags, FALSE); 327360784Sdim /* 328360784Sdim * Handle altflags which don't map directly to 329360784Sdim * mount flags. 330360784Sdim */ 331360784Sdim if(altflags & ALTF_BG) 332360784Sdim opflags |= BGRND; 333360784Sdim if(altflags & ALTF_MNTUDP) 334360784Sdim mnttcp_ok = 0; 335360784Sdim if(altflags & ALTF_TCP) { 336360784Sdim nfsargsp->sotype = SOCK_STREAM; 337360784Sdim nfsproto = IPPROTO_TCP; 338360784Sdim } 339360784Sdim if(altflags & ALTF_PORT) { 340360784Sdim /* 341360784Sdim * XXX Converting from a string to an int 342360784Sdim * and back again is silly, and we should 343360784Sdim * allow /etc/services names. 344360784Sdim */ 345360784Sdim asprintf(&portspec, "%d", 346360784Sdim atoi(strstr(optarg, "port=") + 5)); 347360784Sdim if (portspec == NULL) 348360784Sdim err(1, "asprintf"); 349360784Sdim } 350360784Sdim mountmode = ANY; 351360784Sdim if(altflags & ALTF_NFSV2) 352360784Sdim mountmode = V2; 353360784Sdim if(altflags & ALTF_NFSV3) 354360784Sdim mountmode = V3; 355360784Sdim if(altflags & ALTF_ACREGMIN) 356360784Sdim nfsargsp->acregmin = atoi(strstr(optarg, 357360784Sdim "acregmin=") + 9); 358360784Sdim if(altflags & ALTF_ACREGMAX) 359360784Sdim nfsargsp->acregmax = atoi(strstr(optarg, 360360784Sdim "acregmax=") + 9); 361360784Sdim if(altflags & ALTF_ACDIRMIN) 362360784Sdim nfsargsp->acdirmin = atoi(strstr(optarg, 363360784Sdim "acdirmin=") + 9); 364360784Sdim if(altflags & ALTF_ACDIRMAX) 365360784Sdim nfsargsp->acdirmax = atoi(strstr(optarg, 366360784Sdim "acdirmax=") + 9); 367360784Sdim break; 368360784Sdim case 'P': 369360784Sdim /* obsolete for NFSMNT_RESVPORT, now default */ 370360784Sdim break; 371360784Sdim case 'R': 372360784Sdim num = strtol(optarg, &p, 10); 373360784Sdim if (*p || num < 0) 374360784Sdim errx(1, "illegal -R value -- %s", optarg); 375360784Sdim retrycnt = num; 376360784Sdim break; 377360784Sdim case 'r': 378360784Sdim num = strtol(optarg, &p, 10); 379360784Sdim if (*p || num <= 0) 380360784Sdim errx(1, "illegal -r value -- %s", optarg); 381360784Sdim nfsargsp->rsize = num; 382360784Sdim nfsargsp->flags |= NFSMNT_RSIZE; 383360784Sdim break; 384360784Sdim case 's': 385360784Sdim nfsargsp->flags |= NFSMNT_SOFT; 386360784Sdim break; 387360784Sdim case 'T': 388360784Sdim nfsargsp->sotype = SOCK_STREAM; 389360784Sdim nfsproto = IPPROTO_TCP; 390360784Sdim break; 391360784Sdim case 't': 392360784Sdim num = strtol(optarg, &p, 10); 393360784Sdim if (*p || num <= 0) 394360784Sdim errx(1, "illegal -t value -- %s", optarg); 395360784Sdim nfsargsp->timeo = num; 396360784Sdim nfsargsp->flags |= NFSMNT_TIMEO; 397360784Sdim break; 398360784Sdim case 'w': 399360784Sdim num = strtol(optarg, &p, 10); 400360784Sdim if (*p || num <= 0) 401360784Sdim errx(1, "illegal -w value -- %s", optarg); 402360784Sdim nfsargsp->wsize = num; 403360784Sdim nfsargsp->flags |= NFSMNT_WSIZE; 404360784Sdim break; 405360784Sdim case 'x': 406360784Sdim num = strtol(optarg, &p, 10); 407360784Sdim if (*p || num <= 0) 408360784Sdim errx(1, "illegal -x value -- %s", optarg); 409360784Sdim nfsargsp->retrans = num; 410360784Sdim nfsargsp->flags |= NFSMNT_RETRANS; 411360784Sdim break; 412360784Sdim case 'U': 413360784Sdim mnttcp_ok = 0; 414360784Sdim break; 415360784Sdim default: 416360784Sdim usage(); 417360784Sdim break; 418360784Sdim } 419360784Sdim argc -= optind; 420360784Sdim argv += optind; 421360784Sdim 422360784Sdim if (argc != 2) { 423360784Sdim usage(); 424360784Sdim /* NOTREACHED */ 425360784Sdim } 426360784Sdim 427360784Sdim spec = *argv++; 428360784Sdim name = *argv; 429360784Sdim 430360784Sdim if (retrycnt == -1) 431360784Sdim /* The default is to keep retrying forever. */ 432360784Sdim retrycnt = 0; 433360784Sdim if (!getnfsargs(spec, nfsargsp)) 434360784Sdim exit(1); 435360784Sdim 436360784Sdim /* resolve the mountpoint with realpath(3) */ 437360784Sdim (void)checkpath(name, mntpath); 438360784Sdim 439360784Sdim error = getvfsbyname("nfs", &vfc); 440360784Sdim if (error && vfsisloadable("nfs")) { 441360784Sdim if(vfsload("nfs")) 442360784Sdim err(EX_OSERR, "vfsload(nfs)"); 443360784Sdim endvfsent(); /* clear cache */ 444360784Sdim error = getvfsbyname("nfs", &vfc); 445360784Sdim } 446360784Sdim if (error) 447360784Sdim errx(EX_OSERR, "nfs filesystem is not available"); 448360784Sdim 449360784Sdim if (mount(vfc.vfc_name, mntpath, mntflags, nfsargsp)) 450360784Sdim err(1, "%s", mntpath); 451360784Sdim 452360784Sdim exit(0); 453360784Sdim} 454360784Sdim 455360784Sdimint 456360784Sdimgetnfsargs(spec, nfsargsp) 457360784Sdim char *spec; 458360784Sdim struct nfs_args *nfsargsp; 459360784Sdim{ 460360784Sdim struct addrinfo hints, *ai_nfs, *ai; 461360784Sdim enum tryret ret; 462360784Sdim int ecode, speclen, remoteerr; 463360784Sdim char *hostp, *delimp, *errstr; 464360784Sdim size_t len; 465360784Sdim static char nam[MNAMELEN + 1]; 466360784Sdim 467360784Sdim if ((delimp = strrchr(spec, ':')) != NULL) { 468360784Sdim hostp = spec; 469360784Sdim spec = delimp + 1; 470360784Sdim } else if ((delimp = strrchr(spec, '@')) != NULL) { 471360784Sdim warnx("path@server syntax is deprecated, use server:path"); 472360784Sdim hostp = delimp + 1; 473360784Sdim } else { 474360784Sdim warnx("no <host>:<dirpath> nfs-name"); 475360784Sdim return (0); 476360784Sdim } 477360784Sdim *delimp = '\0'; 478360784Sdim 479360784Sdim /* 480360784Sdim * If there has been a trailing slash at mounttime it seems 481360784Sdim * that some mountd implementations fail to remove the mount 482360784Sdim * entries from their mountlist while unmounting. 483360784Sdim */ 484360784Sdim for (speclen = strlen(spec); 485360784Sdim speclen > 1 && spec[speclen - 1] == '/'; 486360784Sdim speclen--) 487360784Sdim spec[speclen - 1] = '\0'; 488360784Sdim if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 489360784Sdim warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 490360784Sdim return (0); 491360784Sdim } 492360784Sdim /* Make both '@' and ':' notations equal */ 493360784Sdim if (*hostp != '\0') { 494360784Sdim len = strlen(hostp); 495360784Sdim memmove(nam, hostp, len); 496360784Sdim nam[len] = ':'; 497360784Sdim memmove(nam + len + 1, spec, speclen); 498360784Sdim nam[len + speclen + 1] = '\0'; 499360784Sdim } 500360784Sdim 501360784Sdim /* 502360784Sdim * Handle an internet host address. 503360784Sdim */ 504360784Sdim memset(&hints, 0, sizeof hints); 505360784Sdim hints.ai_flags = AI_NUMERICHOST; 506360784Sdim hints.ai_socktype = nfsargsp->sotype; 507360784Sdim if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 508360784Sdim hints.ai_flags = 0; 509360784Sdim if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 510360784Sdim != 0) { 511360784Sdim if (portspec == NULL) 512360784Sdim errx(1, "%s: %s", hostp, gai_strerror(ecode)); 513360784Sdim else 514360784Sdim errx(1, "%s:%s: %s", hostp, portspec, 515360784Sdim gai_strerror(ecode)); 516360784Sdim return (0); 517360784Sdim } 518360784Sdim } 519360784Sdim 520360784Sdim ret = TRYRET_LOCALERR; 521360784Sdim for (;;) { 522360784Sdim /* 523360784Sdim * Try each entry returned by getaddrinfo(). Note the 524360784Sdim * occurence of remote errors by setting `remoteerr'. 525360784Sdim */ 526360784Sdim remoteerr = 0; 527360784Sdim for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 528360784Sdim ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr); 529360661Sdim if (ret == TRYRET_SUCCESS) 530360661Sdim break; 531360661Sdim if (ret != TRYRET_LOCALERR) 532360661Sdim remoteerr = 1; 533360661Sdim if ((opflags & ISBGRND) == 0) 534360661Sdim fprintf(stderr, "%s\n", errstr); 535360661Sdim } 536360661Sdim if (ret == TRYRET_SUCCESS) 537360661Sdim break; 538360661Sdim 539360661Sdim /* Exit if all errors were local. */ 540360661Sdim if (!remoteerr) 541360661Sdim exit(1); 542360661Sdim 543360661Sdim /* 544360661Sdim * If retrycnt == 0, we are to keep retrying forever. 545360661Sdim * Otherwise decrement it, and exit if it hits zero. 546360661Sdim */ 547360661Sdim if (retrycnt != 0 && --retrycnt == 0) 548360661Sdim exit(1); 549360661Sdim 550360661Sdim if ((opflags & (BGRND | ISBGRND)) == BGRND) { 551360661Sdim warnx("Cannot immediately mount %s:%s, backgrounding", 552360661Sdim hostp, spec); 553360661Sdim opflags |= ISBGRND; 554360661Sdim if (daemon(0, 0) != 0) 555360661Sdim err(1, "daemon"); 556360661Sdim } 557360661Sdim sleep(60); 558360661Sdim } 559360661Sdim freeaddrinfo(ai_nfs); 560360661Sdim nfsargsp->hostname = nam; 561360661Sdim /* Add mounted filesystem to PATH_MOUNTTAB */ 562360661Sdim if (!add_mtab(hostp, spec)) 563360661Sdim warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 564360661Sdim return (1); 565360661Sdim} 566360661Sdim 567360661Sdim/* 568360661Sdim * Try to set up the NFS arguments according to the address 569360661Sdim * family, protocol (and possibly port) specified in `ai'. 570360661Sdim * 571360661Sdim * Returns TRYRET_SUCCESS if successful, or: 572360661Sdim * TRYRET_TIMEOUT The server did not respond. 573360661Sdim * TRYRET_REMOTEERR The server reported an error. 574360661Sdim * TRYRET_LOCALERR Local failure. 575360661Sdim * 576360661Sdim * In all error cases, *errstr will be set to a statically-allocated string 577360661Sdim * describing the error. 578360661Sdim */ 579360661Sdimenum tryret 580360661Sdimnfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp, 581360661Sdim char *spec, char **errstr) 582360661Sdim{ 583360661Sdim static char errbuf[256]; 584360661Sdim struct sockaddr_storage nfs_ss; 585360661Sdim struct netbuf nfs_nb; 586360661Sdim struct nfhret nfhret; 587360661Sdim struct timeval try; 588360661Sdim struct rpc_err rpcerr; 589360661Sdim CLIENT *clp; 590360661Sdim struct netconfig *nconf, *nconf_mnt; 591360661Sdim char *netid, *netid_mnt; 592360661Sdim int doconnect, nfsvers, mntvers; 593360661Sdim enum clnt_stat stat; 594360661Sdim enum mountmode trymntmode; 595360661Sdim 596360661Sdim trymntmode = mountmode; 597360661Sdim errbuf[0] = '\0'; 598360661Sdim *errstr = errbuf; 599360661Sdim 600360661Sdim if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) { 601360661Sdim snprintf(errbuf, sizeof errbuf, 602360661Sdim "af %d sotype %d not supported", ai->ai_family, 603360661Sdim nfsargsp->sotype); 604360661Sdim return (TRYRET_LOCALERR); 605360661Sdim } 606360661Sdim if ((nconf = getnetconf_cached(netid)) == NULL) { 607360661Sdim snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 608360661Sdim return (TRYRET_LOCALERR); 609360661Sdim } 610360661Sdim /* The RPCPROG_MNT netid may be different. */ 611360661Sdim if (mnttcp_ok) { 612360661Sdim netid_mnt = netid; 613360661Sdim nconf_mnt = nconf; 614360661Sdim } else { 615360661Sdim if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 616360661Sdim == NULL) { 617360661Sdim snprintf(errbuf, sizeof errbuf, 618360661Sdim "af %d sotype SOCK_DGRAM not supported", 619360661Sdim ai->ai_family); 620360661Sdim return (TRYRET_LOCALERR); 621360661Sdim } 622360661Sdim if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 623360661Sdim snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 624360661Sdim nc_sperror()); 625360661Sdim return (TRYRET_LOCALERR); 626360661Sdim } 627360661Sdim } 628360661Sdim 629360661Sdimtryagain: 630360661Sdim if (trymntmode == V2) { 631360661Sdim nfsvers = 2; 632360661Sdim mntvers = 1; 633360661Sdim } else { 634360661Sdim nfsvers = 3; 635360661Sdim mntvers = 3; 636360661Sdim } 637360661Sdim 638360661Sdim if (portspec != NULL) { 639360661Sdim /* `ai' contains the complete nfsd sockaddr. */ 640360661Sdim nfs_nb.buf = ai->ai_addr; 641360661Sdim nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 642360661Sdim } else { 643360661Sdim /* Ask the remote rpcbind. */ 644360661Sdim nfs_nb.buf = &nfs_ss; 645360661Sdim nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 646360661Sdim 647360661Sdim if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 648360661Sdim hostp)) { 649360661Sdim if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 650360661Sdim trymntmode == ANY) { 651360661Sdim trymntmode = V2; 652360661Sdim goto tryagain; 653360661Sdim } 654360661Sdim snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 655360661Sdim netid, hostp, spec, 656360661Sdim clnt_spcreateerror("RPCPROG_NFS")); 657360661Sdim return (returncode(rpc_createerr.cf_stat, 658360661Sdim &rpc_createerr.cf_error)); 659360661Sdim } 660360661Sdim } 661360661Sdim 662360661Sdim /* Check that the server (nfsd) responds on the port we have chosen. */ 663360661Sdim clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers, 664360661Sdim 0, 0); 665360661Sdim if (clp == NULL) { 666360661Sdim snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 667360661Sdim hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 668360661Sdim return (returncode(rpc_createerr.cf_stat, 669360661Sdim &rpc_createerr.cf_error)); 670360661Sdim } 671360661Sdim if (nfsargsp->sotype == SOCK_DGRAM) { 672360661Sdim /* 673360661Sdim * Use connect(), to match what the kernel does. This 674360661Sdim * catches cases where the server responds from the 675360661Sdim * wrong source address. 676360661Sdim */ 677360661Sdim doconnect = 1; 678360661Sdim if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 679360661Sdim clnt_destroy(clp); 680360661Sdim snprintf(errbuf, sizeof errbuf, 681360661Sdim "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 682360661Sdim spec); 683360661Sdim return (TRYRET_LOCALERR); 684360661Sdim } 685360661Sdim } 686360661Sdim 687360661Sdim try.tv_sec = 10; 688360661Sdim try.tv_usec = 0; 689360661Sdim stat = clnt_call(clp, NFSPROC_NULL, xdr_void, NULL, xdr_void, NULL, 690360661Sdim try); 691360661Sdim if (stat != RPC_SUCCESS) { 692360661Sdim if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 693360661Sdim clnt_destroy(clp); 694360661Sdim trymntmode = V2; 695360661Sdim goto tryagain; 696360661Sdim } 697360661Sdim clnt_geterr(clp, &rpcerr); 698360661Sdim snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 699360661Sdim hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 700360661Sdim clnt_destroy(clp); 701360661Sdim return (returncode(stat, &rpcerr)); 702360661Sdim } 703360661Sdim clnt_destroy(clp); 704360661Sdim 705360661Sdim /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ 706360661Sdim try.tv_sec = 10; 707360661Sdim try.tv_usec = 0; 708360661Sdim clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt); 709360661Sdim if (clp == NULL) { 710360661Sdim snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 711360661Sdim hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 712360661Sdim return (returncode(rpc_createerr.cf_stat, 713360661Sdim &rpc_createerr.cf_error)); 714360661Sdim } 715360661Sdim clp->cl_auth = authsys_create_default(); 716360661Sdim nfhret.auth = RPCAUTH_UNIX; 717360661Sdim nfhret.vers = mntvers; 718360661Sdim stat = clnt_call(clp, RPCMNT_MOUNT, xdr_dir, spec, xdr_fh, &nfhret, 719360661Sdim try); 720360661Sdim auth_destroy(clp->cl_auth); 721360661Sdim if (stat != RPC_SUCCESS) { 722360661Sdim if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 723360661Sdim clnt_destroy(clp); 724360661Sdim trymntmode = V2; 725360661Sdim goto tryagain; 726360661Sdim } 727360661Sdim clnt_geterr(clp, &rpcerr); 728360661Sdim snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 729360661Sdim hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 730360661Sdim clnt_destroy(clp); 731360661Sdim return (returncode(stat, &rpcerr)); 732360661Sdim } 733360661Sdim clnt_destroy(clp); 734360661Sdim 735360661Sdim if (nfhret.stat != 0) { 736360661Sdim snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 737360661Sdim hostp, spec, strerror(nfhret.stat)); 738360661Sdim return (TRYRET_REMOTEERR); 739360661Sdim } 740360661Sdim 741360661Sdim /* 742360661Sdim * Store the filehandle and server address in nfsargsp, making 743360661Sdim * sure to copy any locally allocated structures. 744360661Sdim */ 745360661Sdim nfsargsp->addrlen = nfs_nb.len; 746360661Sdim nfsargsp->addr = malloc(nfsargsp->addrlen); 747360661Sdim nfsargsp->fhsize = nfhret.fhsize; 748360661Sdim nfsargsp->fh = malloc(nfsargsp->fhsize); 749360661Sdim if (nfsargsp->addr == NULL || nfsargsp->fh == NULL) 750360661Sdim err(1, "malloc"); 751360661Sdim bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen); 752360661Sdim bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize); 753360661Sdim 754360661Sdim if (nfsvers == 3) 755360661Sdim nfsargsp->flags |= NFSMNT_NFSV3; 756360661Sdim else 757360661Sdim nfsargsp->flags &= ~NFSMNT_NFSV3; 758360661Sdim 759360661Sdim return (TRYRET_SUCCESS); 760360661Sdim} 761360661Sdim 762360661Sdim 763360661Sdim/* 764360661Sdim * Catagorise a RPC return status and error into an `enum tryret' 765360661Sdim * return code. 766360658Sdim */ 767360658Sdimenum tryret 768360658Sdimreturncode(enum clnt_stat stat, struct rpc_err *rpcerr) 769360658Sdim{ 770360658Sdim switch (stat) { 771360658Sdim case RPC_TIMEDOUT: 772360658Sdim return (TRYRET_TIMEOUT); 773360658Sdim case RPC_PMAPFAILURE: 774360658Sdim case RPC_PROGNOTREGISTERED: 775360658Sdim case RPC_PROGVERSMISMATCH: 776360658Sdim /* XXX, these can be local or remote. */ 777360658Sdim case RPC_CANTSEND: 778360658Sdim case RPC_CANTRECV: 779360658Sdim return (TRYRET_REMOTEERR); 780360658Sdim case RPC_SYSTEMERROR: 781360658Sdim switch (rpcerr->re_errno) { 782360658Sdim case ETIMEDOUT: 783360658Sdim return (TRYRET_TIMEOUT); 784360658Sdim case ENOMEM: 785360658Sdim break; 786360658Sdim default: 787360658Sdim return (TRYRET_REMOTEERR); 788360658Sdim } 789360658Sdim /* FALLTHROUGH */ 790360658Sdim default: 791360658Sdim break; 792360658Sdim } 793360658Sdim return (TRYRET_LOCALERR); 794360658Sdim} 795360658Sdim 796360658Sdim/* 797360658Sdim * Look up a netid based on an address family and socket type. 798360658Sdim * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 799360658Sdim * 800360658Sdim * XXX there should be a library function for this. 801360658Sdim */ 802360658Sdimchar * 803360658Sdimnetidbytype(int af, int sotype) { 804360658Sdim struct nc_protos *p; 805360658Sdim 806360658Sdim for (p = nc_protos; p->netid != NULL; p++) { 807360658Sdim if (af != p->af || sotype != p->sotype) 808360658Sdim continue; 809360658Sdim return (p->netid); 810360658Sdim } 811360658Sdim return (NULL); 812360658Sdim} 813360658Sdim 814360658Sdim/* 815360658Sdim * Look up a netconfig entry based on a netid, and cache the result so 816360658Sdim * that we don't need to remember to call freenetconfigent(). 817360658Sdim * 818360658Sdim * Otherwise it behaves just like getnetconfigent(), so nc_*error() 819360658Sdim * work on failure. 820360658Sdim */ 821360658Sdimstruct netconfig * 822360658Sdimgetnetconf_cached(const char *netid) { 823360658Sdim static struct nc_entry { 824360658Sdim struct netconfig *nconf; 825360658Sdim struct nc_entry *next; 826360658Sdim } *head; 827360658Sdim struct nc_entry *p; 828360658Sdim struct netconfig *nconf; 829360658Sdim 830360658Sdim for (p = head; p != NULL; p = p->next) 831360658Sdim if (strcmp(netid, p->nconf->nc_netid) == 0) 832360658Sdim return (p->nconf); 833360658Sdim 834360658Sdim if ((nconf = getnetconfigent(netid)) == NULL) 835360658Sdim return (NULL); 836360658Sdim if ((p = malloc(sizeof(*p))) == NULL) 837360658Sdim err(1, "malloc"); 838360658Sdim p->nconf = nconf; 839360658Sdim p->next = head; 840360658Sdim head = p; 841360658Sdim 842360658Sdim return (p->nconf); 843360658Sdim} 844360658Sdim 845360658Sdim/* 846360658Sdim * xdr routines for mount rpc's 847360658Sdim */ 848360658Sdimint 849360658Sdimxdr_dir(xdrsp, dirp) 850360658Sdim XDR *xdrsp; 851360658Sdim char *dirp; 852360658Sdim{ 853360658Sdim return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 854360658Sdim} 855360658Sdim 856360658Sdimint 857360658Sdimxdr_fh(xdrsp, np) 858360658Sdim XDR *xdrsp; 859360658Sdim struct nfhret *np; 860360658Sdim{ 861360658Sdim int i; 862360658Sdim long auth, authcnt, authfnd = 0; 863360658Sdim 864360658Sdim if (!xdr_u_long(xdrsp, &np->stat)) 865360658Sdim return (0); 866360658Sdim if (np->stat) 867360658Sdim return (1); 868360658Sdim switch (np->vers) { 869360658Sdim case 1: 870360658Sdim np->fhsize = NFSX_V2FH; 871360658Sdim return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); 872360658Sdim case 3: 873360658Sdim if (!xdr_long(xdrsp, &np->fhsize)) 874360658Sdim return (0); 875360658Sdim if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) 876360658Sdim return (0); 877360658Sdim if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 878360658Sdim return (0); 879360658Sdim if (!xdr_long(xdrsp, &authcnt)) 880360658Sdim return (0); 881360658Sdim for (i = 0; i < authcnt; i++) { 882360658Sdim if (!xdr_long(xdrsp, &auth)) 883360658Sdim return (0); 884360658Sdim if (auth == np->auth) 885360658Sdim authfnd++; 886360658Sdim } 887360658Sdim /* 888360658Sdim * Some servers, such as DEC's OSF/1 return a nil authenticator 889360658Sdim * list to indicate RPCAUTH_UNIX. 890360658Sdim */ 891360658Sdim if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX)) 892360658Sdim np->stat = EAUTH; 893360658Sdim return (1); 894360658Sdim }; 895360658Sdim return (0); 896360658Sdim} 897360658Sdim 898360658Sdimvoid 899360658Sdimusage() 900360658Sdim{ 901360658Sdim (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 902360658Sdim"usage: mount_nfs [-23KNPTUbcdilqs] [-D deadthresh] [-I readdirsize]", 903360658Sdim" [-R retrycnt] [-a maxreadahead]", 904360658Sdim" [-g maxgroups] [-m realm] [-o options] [-r readsize]", 905360658Sdim" [-t timeout] [-w writesize] [-x retrans] rhost:path node"); 906360658Sdim exit(1); 907360658Sdim} 908360658Sdim