mount_nfs.c revision 103318
11556Srgrimes/* 21556Srgrimes * Copyright (c) 1992, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Rick Macklem at The University of Guelph. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 3536049Scharnier */ 3636049Scharnier 3736049Scharnier#ifndef lint 381556Srgrimesstatic const char copyright[] = 3999110Sobrien"@(#) Copyright (c) 1992, 1993, 1994\n\ 4099110Sobrien The Regents of the University of California. All rights reserved.\n"; 411556Srgrimes#endif /* not lint */ 421556Srgrimes 431556Srgrimes#ifndef lint 441556Srgrimes#if 0 4576286Skrisstatic char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 4676286Skris#endif 4776286Skrisstatic const char rcsid[] = 4876286Skris "$FreeBSD: head/sbin/mount_nfs/mount_nfs.c 103318 2002-09-14 15:14:29Z mux $"; 4976286Skris#endif /* not lint */ 501556Srgrimes 51104548Stjr#include <sys/param.h> 5276286Skris#include <sys/mount.h> 531556Srgrimes#include <sys/socket.h> 5476286Skris#include <sys/stat.h> 551556Srgrimes#include <sys/syslog.h> 561556Srgrimes 5776351Skris#include <rpc/rpc.h> 581556Srgrimes#include <rpc/pmap_clnt.h> 591556Srgrimes#include <rpc/pmap_prot.h> 601556Srgrimes 611556Srgrimes#include <nfs/rpcv2.h> 621556Srgrimes#include <nfs/nfsproto.h> 631556Srgrimes#include <nfsclient/nfs.h> 641556Srgrimes#include <nfsclient/nfsargs.h> 651556Srgrimes 661556Srgrimes#include <arpa/inet.h> 671556Srgrimes 68114583Smarkm#include <ctype.h> 69114583Smarkm#include <err.h> 70114583Smarkm#include <errno.h> 71114583Smarkm#include <fcntl.h> 721556Srgrimes#include <netdb.h> 731556Srgrimes#include <stdio.h> 741556Srgrimes#include <stdlib.h> 751556Srgrimes#include <strings.h> 761556Srgrimes#include <sysexits.h> 771556Srgrimes#include <unistd.h> 781556Srgrimes 791556Srgrimes#include "mntopts.h" 801556Srgrimes#include "mounttab.h" 811556Srgrimes 821556Srgrimes#define ALTF_BG 0x1 83114583Smarkm#define ALTF_NOCONN 0x2 8476286Skris#define ALTF_DUMBTIMR 0x4 8576286Skris#define ALTF_INTR 0x8 861556Srgrimes#define ALTF_NFSV3 0x20 8790110Simp#define ALTF_RDIRPLUS 0x40 881556Srgrimes#define ALTF_MNTUDP 0x80 8990110Simp#define ALTF_RESVPORT 0x100 901556Srgrimes#define ALTF_SEQPACKET 0x200 911556Srgrimes#define ALTF_SOFT 0x800 921556Srgrimes#define ALTF_TCP 0x1000 931556Srgrimes#define ALTF_PORT 0x2000 941556Srgrimes#define ALTF_NFSV2 0x4000 951556Srgrimes#define ALTF_ACREGMIN 0x8000 961556Srgrimes#define ALTF_ACREGMAX 0x10000 971556Srgrimes#define ALTF_ACDIRMIN 0x20000 981556Srgrimes#define ALTF_ACDIRMAX 0x40000 991556Srgrimes#define ALTF_NOLOCKD 0x80000 1001556Srgrimes 101114583Smarkmstruct mntopt mopts[] = { 1021556Srgrimes MOPT_STDOPTS, 10376019Skris MOPT_FORCE, 1041556Srgrimes MOPT_UPDATE, 1051556Srgrimes MOPT_ASYNC, 1061556Srgrimes { "bg", 0, ALTF_BG, 1 }, 1071556Srgrimes { "conn", 1, ALTF_NOCONN, 1 }, 1081556Srgrimes { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, 1091556Srgrimes { "intr", 0, ALTF_INTR, 1 }, 1101556Srgrimes { "nfsv3", 0, ALTF_NFSV3, 1 }, 1111556Srgrimes { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, 1121556Srgrimes { "mntudp", 0, ALTF_MNTUDP, 1 }, 1131556Srgrimes { "resvport", 0, ALTF_RESVPORT, 1 }, 1141556Srgrimes { "soft", 0, ALTF_SOFT, 1 }, 1151556Srgrimes { "tcp", 0, ALTF_TCP, 1 }, 1161556Srgrimes { "port=", 0, ALTF_PORT, 1 }, 1171556Srgrimes { "nfsv2", 0, ALTF_NFSV2, 1 }, 1181556Srgrimes { "acregmin=", 0, ALTF_ACREGMIN, 1 }, 1191556Srgrimes { "acregmax=", 0, ALTF_ACREGMAX, 1 }, 120114583Smarkm { "acdirmin=", 0, ALTF_ACDIRMIN, 1 }, 1211556Srgrimes { "acdirmax=", 0, ALTF_ACDIRMAX, 1 }, 12276017Skris { "lockd", 1, ALTF_NOLOCKD, 1 }, 12376286Skris { NULL } 12476286Skris}; 1251556Srgrimes 1261556Srgrimesstruct nfs_args nfsdefargs = { 1271556Srgrimes NFS_ARGSVERSION, 1281556Srgrimes NULL, 129114583Smarkm sizeof (struct sockaddr_in), 1301556Srgrimes SOCK_DGRAM, 13176017Skris 0, 1321556Srgrimes NULL, 1331556Srgrimes 0, 13476286Skris NFSMNT_RESVPORT, 13576286Skris NFS_WSIZE, 1361556Srgrimes NFS_RSIZE, 1371556Srgrimes NFS_READDIRSIZE, 1381556Srgrimes 10, 1391556Srgrimes NFS_RETRANS, 140114583Smarkm NFS_MAXGRPS, 1411556Srgrimes NFS_DEFRAHEAD, 14276017Skris 0, /* was: NQ_DEFLEASE */ 1431556Srgrimes NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 1441556Srgrimes NULL, 1451556Srgrimes /* args version 4 */ 1461556Srgrimes NFS_MINATTRTIMO, 1471556Srgrimes NFS_MAXATTRTIMO, 1481556Srgrimes NFS_MINDIRATTRTIMO, 149114583Smarkm NFS_MAXDIRATTRTIMO, 1501556Srgrimes}; 1511556Srgrimes 1521556Srgrimes/* Table for af,sotype -> netid conversions. */ 1531556Srgrimesstruct nc_protos { 1541556Srgrimes char *netid; 1551556Srgrimes int af; 15676351Skris int sotype; 157131085Sobrien} nc_protos[] = { 15876351Skris {"udp", AF_INET, SOCK_DGRAM}, 159131085Sobrien {"tcp", AF_INET, SOCK_STREAM}, 160131085Sobrien {"udp6", AF_INET6, SOCK_DGRAM}, 1611556Srgrimes {"tcp6", AF_INET6, SOCK_STREAM}, 1621556Srgrimes {NULL} 1631556Srgrimes}; 1641556Srgrimes 16576017Skrisstruct nfhret { 1661556Srgrimes u_long stat; 1671556Srgrimes long vers; 1681556Srgrimes long auth; 1691556Srgrimes long fhsize; 1701556Srgrimes u_char nfh[NFSX_V3FHMAX]; 1711556Srgrimes}; 17276017Skris#define BGRND 1 1731556Srgrimes#define ISBGRND 2 1741556Srgrimesint retrycnt = -1; 1751556Srgrimesint opflags = 0; 1761556Srgrimesint nfsproto = IPPROTO_UDP; 1771556Srgrimesint mnttcp_ok = 1; 1781556Srgrimeschar *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 1791556Srgrimesenum mountmode { 1801556Srgrimes ANY, 1811556Srgrimes V2, 1821556Srgrimes V3 1831556Srgrimes} mountmode = ANY; 1841556Srgrimes 1851556Srgrimes/* Return codes for nfs_tryproto. */ 1861556Srgrimesenum tryret { 1871556Srgrimes TRYRET_SUCCESS, 1881556Srgrimes TRYRET_TIMEOUT, /* No response received. */ 1891556Srgrimes TRYRET_REMOTEERR, /* Error received from remote server. */ 1901556Srgrimes TRYRET_LOCALERR /* Local failure. */ 1911556Srgrimes}; 1921556Srgrimes 1931556Srgrimesint getnfsargs(char *, struct nfs_args *); 1941556Srgrimes/* void set_rpc_maxgrouplist(int); */ 1951556Srgrimesstruct netconfig *getnetconf_cached(const char *netid); 1961556Srgrimeschar *netidbytype(int af, int sotype); 1971556Srgrimesvoid usage(void) __dead2; 1981556Srgrimesint xdr_dir(XDR *, char *); 1991556Srgrimesint xdr_fh(XDR *, struct nfhret *); 2001556Srgrimesenum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, 2011556Srgrimes char *hostp, char *spec, char **errstr); 2021556Srgrimesenum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 2031556Srgrimes 2041556Srgrimes/* 2051556Srgrimes * Used to set mount flags with getmntopts. Call with dir=TRUE to 2061556Srgrimes * initialize altflags from the current mount flags. Call with 2071556Srgrimes * dir=FALSE to update mount flags with the new value of altflags after 2081556Srgrimes * the call to getmntopts. 2091556Srgrimes */ 2101556Srgrimesstatic void 2111556Srgrimesset_flags(int* altflags, int* nfsflags, int dir) 2121556Srgrimes{ 2131556Srgrimes#define F2(af, nf) \ 2141556Srgrimes if (dir) { \ 2151556Srgrimes if (*nfsflags & NFSMNT_##nf) \ 2161556Srgrimes *altflags |= ALTF_##af; \ 2171556Srgrimes else \ 2181556Srgrimes *altflags &= ~ALTF_##af; \ 2191556Srgrimes } else { \ 2201556Srgrimes if (*altflags & ALTF_##af) \ 2211556Srgrimes *nfsflags |= NFSMNT_##nf; \ 2221556Srgrimes else \ 2231556Srgrimes *nfsflags &= ~NFSMNT_##nf; \ 2241556Srgrimes } 2251556Srgrimes#define F(f) F2(f,f) 2261556Srgrimes 2271556Srgrimes F(NOCONN); 2281556Srgrimes F(DUMBTIMR); 2291556Srgrimes F2(INTR, INT); 2301556Srgrimes F(RDIRPLUS); 2311556Srgrimes F(RESVPORT); 2321556Srgrimes F(SOFT); 2331556Srgrimes F(ACREGMIN); 2341556Srgrimes F(ACREGMAX); 2351556Srgrimes F(ACDIRMIN); 2361556Srgrimes F(ACDIRMAX); 2371556Srgrimes F(NOLOCKD); 2381556Srgrimes 2391556Srgrimes#undef F 2401556Srgrimes#undef F2 2411556Srgrimes} 2421556Srgrimes 2431556Srgrimesint 2441556Srgrimesmain(argc, argv) 2451556Srgrimes int argc; 2461556Srgrimes char *argv[]; 2471556Srgrimes{ 2481556Srgrimes int c; 2491556Srgrimes struct nfs_args *nfsargsp; 2501556Srgrimes struct nfs_args nfsargs; 2511556Srgrimes int mntflags, altflags, nfssvc_flag, num; 2521556Srgrimes char *name, *p, *spec; 2531556Srgrimes char mntpath[MAXPATHLEN]; 2541556Srgrimes 2551556Srgrimes mntflags = 0; 2561556Srgrimes altflags = 0; 2571556Srgrimes nfsargs = nfsdefargs; 2581556Srgrimes nfsargsp = &nfsargs; 2591556Srgrimes while ((c = getopt(argc, argv, 2601556Srgrimes "23a:bcdD:g:I:iLl:No:PpR:r:sTt:w:x:U")) != -1) 2611556Srgrimes switch (c) { 2621556Srgrimes case '2': 2631556Srgrimes mountmode = V2; 2641556Srgrimes break; 2651556Srgrimes case '3': 2661556Srgrimes mountmode = V3; 2671556Srgrimes break; 2681556Srgrimes case 'a': 2691556Srgrimes num = strtol(optarg, &p, 10); 2701556Srgrimes if (*p || num < 0) 2711556Srgrimes errx(1, "illegal -a value -- %s", optarg); 2721556Srgrimes nfsargsp->readahead = num; 2731556Srgrimes nfsargsp->flags |= NFSMNT_READAHEAD; 27446684Skris break; 2751556Srgrimes case 'b': 2761556Srgrimes opflags |= BGRND; 2771556Srgrimes break; 2781556Srgrimes case 'c': 2791556Srgrimes nfsargsp->flags |= NFSMNT_NOCONN; 2801556Srgrimes break; 2811556Srgrimes case 'D': 2821556Srgrimes num = strtol(optarg, &p, 10); 2831556Srgrimes if (*p || num <= 0) 2841556Srgrimes errx(1, "illegal -D value -- %s", optarg); 2851556Srgrimes nfsargsp->deadthresh = num; 2861556Srgrimes nfsargsp->flags |= NFSMNT_DEADTHRESH; 2871556Srgrimes break; 2888855Srgrimes case 'd': 2891556Srgrimes nfsargsp->flags |= NFSMNT_DUMBTIMR; 2901556Srgrimes break; 2911556Srgrimes#if 0 /* XXXX */ 2921556Srgrimes case 'g': 2931556Srgrimes num = strtol(optarg, &p, 10); 2941556Srgrimes if (*p || num <= 0) 2951556Srgrimes errx(1, "illegal -g value -- %s", optarg); 2961556Srgrimes set_rpc_maxgrouplist(num); 2971556Srgrimes nfsargsp->maxgrouplist = num; 2981556Srgrimes nfsargsp->flags |= NFSMNT_MAXGRPS; 2991556Srgrimes break; 3001556Srgrimes#endif 3011556Srgrimes case 'I': 3021556Srgrimes num = strtol(optarg, &p, 10); 3031556Srgrimes if (*p || num <= 0) 304143447Sobrien errx(1, "illegal -I value -- %s", optarg); 3051556Srgrimes nfsargsp->readdirsize = num; 3061556Srgrimes nfsargsp->flags |= NFSMNT_READDIRSIZE; 3071556Srgrimes break; 3081556Srgrimes case 'i': 3091556Srgrimes nfsargsp->flags |= NFSMNT_INT; 3101556Srgrimes break; 3111556Srgrimes case 'L': 3121556Srgrimes nfsargsp->flags |= NFSMNT_NOLOCKD; 3131556Srgrimes break; 3141556Srgrimes case 'l': 3151556Srgrimes nfsargsp->flags |= NFSMNT_RDIRPLUS; 3161556Srgrimes break; 3171556Srgrimes case 'N': 3181556Srgrimes nfsargsp->flags &= ~NFSMNT_RESVPORT; 31976351Skris break; 32076351Skris case 'o': 3211556Srgrimes altflags = 0; 3221556Srgrimes set_flags(&altflags, &nfsargsp->flags, TRUE); 32376351Skris if (mountmode == V2) 3241556Srgrimes altflags |= ALTF_NFSV2; 3251556Srgrimes else if (mountmode == V3) 3261556Srgrimes altflags |= ALTF_NFSV3; 3271556Srgrimes getmntopts(optarg, mopts, &mntflags, &altflags); 3281556Srgrimes set_flags(&altflags, &nfsargsp->flags, FALSE); 3291556Srgrimes /* 3301556Srgrimes * Handle altflags which don't map directly to 3311556Srgrimes * mount flags. 3321556Srgrimes */ 3331556Srgrimes if (altflags & ALTF_BG) 3341556Srgrimes opflags |= BGRND; 3351556Srgrimes if (altflags & ALTF_MNTUDP) 33676286Skris mnttcp_ok = 0; 33776286Skris if (altflags & ALTF_TCP) { 33876286Skris nfsargsp->sotype = SOCK_STREAM; 33976286Skris nfsproto = IPPROTO_TCP; 340143447Sobrien } 34176286Skris if (altflags & ALTF_PORT) { 34276286Skris /* 3431556Srgrimes * XXX Converting from a string to an int 3441556Srgrimes * and back again is silly, and we should 345143447Sobrien * allow /etc/services names. 346143447Sobrien */ 347143447Sobrien p = strstr(optarg, "port="); 348143447Sobrien if (p) { 3491556Srgrimes asprintf(&portspec, "%d", 35076351Skris atoi(p + 5)); 3511556Srgrimes if (portspec == NULL) 35276351Skris err(1, "asprintf"); 3531556Srgrimes } 3541556Srgrimes } 3551556Srgrimes mountmode = ANY; 3561556Srgrimes if (altflags & ALTF_NFSV2) 3571556Srgrimes mountmode = V2; 3581556Srgrimes if (altflags & ALTF_NFSV3) 3591556Srgrimes mountmode = V3; 3601556Srgrimes if (altflags & ALTF_ACREGMIN) { 3611556Srgrimes p = strstr(optarg, "acregmin="); 3621556Srgrimes if (p) 3631556Srgrimes nfsargsp->acregmin = atoi(p + 9); 3641556Srgrimes } 3651556Srgrimes if (altflags & ALTF_ACREGMAX) { 3661556Srgrimes p = strstr(optarg, "acregmax="); 3671556Srgrimes if (p) 3681556Srgrimes nfsargsp->acregmax = atoi(p + 9); 3691556Srgrimes } 3701556Srgrimes if (altflags & ALTF_ACDIRMIN) { 3711556Srgrimes p = strstr(optarg, "acdirmin="); 3721556Srgrimes if (p) 3731556Srgrimes nfsargsp->acdirmin = atoi(p + 9); 3741556Srgrimes } 3751556Srgrimes if (altflags & ALTF_ACDIRMAX) { 3761556Srgrimes p = strstr(optarg, "acdirmax="); 3771556Srgrimes if (p) 37876351Skris nfsargsp->acdirmax = atoi(p + 9); 3791556Srgrimes } 3801556Srgrimes break; 3811556Srgrimes case 'P': 3821556Srgrimes /* obsolete for NFSMNT_RESVPORT, now default */ 3831556Srgrimes break; 3841556Srgrimes case 'R': 3851556Srgrimes num = strtol(optarg, &p, 10); 3861556Srgrimes if (*p || num < 0) 3871556Srgrimes errx(1, "illegal -R value -- %s", optarg); 3881556Srgrimes retrycnt = num; 38976351Skris break; 390104548Stjr case 'r': 3911556Srgrimes num = strtol(optarg, &p, 10); 392104548Stjr if (*p || num <= 0) 393104548Stjr errx(1, "illegal -r value -- %s", optarg); 3941556Srgrimes nfsargsp->rsize = num; 39576351Skris nfsargsp->flags |= NFSMNT_RSIZE; 3961556Srgrimes break; 3971556Srgrimes case 's': 3981556Srgrimes nfsargsp->flags |= NFSMNT_SOFT; 3991556Srgrimes break; 40076351Skris case 'T': 401104548Stjr nfsargsp->sotype = SOCK_STREAM; 402104548Stjr nfsproto = IPPROTO_TCP; 40376351Skris break; 40476351Skris case 't': 4051556Srgrimes num = strtol(optarg, &p, 10); 40676019Skris if (*p || num <= 0) 407104548Stjr errx(1, "illegal -t value -- %s", optarg); 4081556Srgrimes nfsargsp->timeo = num; 409104548Stjr nfsargsp->flags |= NFSMNT_TIMEO; 410104548Stjr break; 411104548Stjr case 'w': 4121556Srgrimes num = strtol(optarg, &p, 10); 41376351Skris if (*p || num <= 0) 4141556Srgrimes errx(1, "illegal -w value -- %s", optarg); 4151556Srgrimes nfsargsp->wsize = num; 4161556Srgrimes nfsargsp->flags |= NFSMNT_WSIZE; 4171556Srgrimes break; 4181556Srgrimes case 'x': 4191556Srgrimes num = strtol(optarg, &p, 10); 4201556Srgrimes if (*p || num <= 0) 4211556Srgrimes errx(1, "illegal -x value -- %s", optarg); 4221556Srgrimes nfsargsp->retrans = num; 4231556Srgrimes nfsargsp->flags |= NFSMNT_RETRANS; 4241556Srgrimes break; 4251556Srgrimes case 'U': 4261556Srgrimes mnttcp_ok = 0; 42790113Simp break; 4281556Srgrimes default: 4291556Srgrimes usage(); 4301556Srgrimes break; 4311556Srgrimes } 4321556Srgrimes argc -= optind; 4331556Srgrimes argv += optind; 4341556Srgrimes 4351556Srgrimes if (argc != 2) { 4361556Srgrimes usage(); 4371556Srgrimes /* NOTREACHED */ 4381556Srgrimes } 4391556Srgrimes 4401556Srgrimes spec = *argv++; 4411556Srgrimes name = *argv; 4421556Srgrimes 4431556Srgrimes if (retrycnt == -1) 4441556Srgrimes /* The default is to keep retrying forever. */ 4451556Srgrimes retrycnt = 0; 4461556Srgrimes if (!getnfsargs(spec, nfsargsp)) 4471556Srgrimes exit(1); 4481556Srgrimes 4491556Srgrimes /* resolve the mountpoint with realpath(3) */ 4501556Srgrimes (void)checkpath(name, mntpath); 4511556Srgrimes 4521556Srgrimes if (mount("nfs", mntpath, mntflags, nfsargsp)) 4531556Srgrimes err(1, "%s", mntpath); 4541556Srgrimes 4551556Srgrimes exit(0); 4561556Srgrimes} 4571556Srgrimes 4581556Srgrimesint 4591556Srgrimesgetnfsargs(spec, nfsargsp) 4601556Srgrimes char *spec; 4611556Srgrimes struct nfs_args *nfsargsp; 4621556Srgrimes{ 4631556Srgrimes struct addrinfo hints, *ai_nfs, *ai; 4641556Srgrimes enum tryret ret; 4651556Srgrimes int ecode, speclen, remoteerr; 4668855Srgrimes char *hostp, *delimp, *errstr; 4678855Srgrimes size_t len; 4681556Srgrimes static char nam[MNAMELEN + 1]; 4691556Srgrimes 4701556Srgrimes if ((delimp = strrchr(spec, ':')) != NULL) { 4711556Srgrimes hostp = spec; 4721556Srgrimes spec = delimp + 1; 4731556Srgrimes } else if ((delimp = strrchr(spec, '@')) != NULL) { 4741556Srgrimes warnx("path@server syntax is deprecated, use server:path"); 4751556Srgrimes hostp = delimp + 1; 4761556Srgrimes } else { 4771556Srgrimes warnx("no <host>:<dirpath> nfs-name"); 4781556Srgrimes return (0); 47976017Skris } 4801556Srgrimes *delimp = '\0'; 4811556Srgrimes 4821556Srgrimes /* 4831556Srgrimes * If there has been a trailing slash at mounttime it seems 4841556Srgrimes * that some mountd implementations fail to remove the mount 4851556Srgrimes * entries from their mountlist while unmounting. 4861556Srgrimes */ 4871556Srgrimes for (speclen = strlen(spec); 4888855Srgrimes speclen > 1 && spec[speclen - 1] == '/'; 4891556Srgrimes speclen--) 4901556Srgrimes spec[speclen - 1] = '\0'; 4911556Srgrimes if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 4921556Srgrimes warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 4931556Srgrimes return (0); 4941556Srgrimes } 4951556Srgrimes /* Make both '@' and ':' notations equal */ 4961556Srgrimes if (*hostp != '\0') { 4971556Srgrimes len = strlen(hostp); 49876017Skris memmove(nam, hostp, len); 4991556Srgrimes nam[len] = ':'; 5001556Srgrimes memmove(nam + len + 1, spec, speclen); 5011556Srgrimes nam[len + speclen + 1] = '\0'; 5021556Srgrimes } 5031556Srgrimes 50476017Skris /* 5051556Srgrimes * Handle an internet host address. 5061556Srgrimes */ 5071556Srgrimes memset(&hints, 0, sizeof hints); 5081556Srgrimes hints.ai_flags = AI_NUMERICHOST; 5091556Srgrimes hints.ai_socktype = nfsargsp->sotype; 5101556Srgrimes if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 5111556Srgrimes hints.ai_flags = 0; 5121556Srgrimes if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 5131556Srgrimes != 0) { 5141556Srgrimes if (portspec == NULL) 5151556Srgrimes errx(1, "%s: %s", hostp, gai_strerror(ecode)); 5161556Srgrimes else 5171556Srgrimes errx(1, "%s:%s: %s", hostp, portspec, 5181556Srgrimes gai_strerror(ecode)); 51990113Simp return (0); 5201556Srgrimes } 52190113Simp } 5221556Srgrimes 5231556Srgrimes ret = TRYRET_LOCALERR; 5241556Srgrimes for (;;) { 5251556Srgrimes /* 5261556Srgrimes * Try each entry returned by getaddrinfo(). Note the 5271556Srgrimes * occurence of remote errors by setting `remoteerr'. 5281556Srgrimes */ 5291556Srgrimes remoteerr = 0; 5301556Srgrimes for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 5311556Srgrimes ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr); 5321556Srgrimes if (ret == TRYRET_SUCCESS) 5331556Srgrimes break; 5341556Srgrimes if (ret != TRYRET_LOCALERR) 5351556Srgrimes remoteerr = 1; 5361556Srgrimes if ((opflags & ISBGRND) == 0) 5371556Srgrimes fprintf(stderr, "%s\n", errstr); 5381556Srgrimes } 5391556Srgrimes if (ret == TRYRET_SUCCESS) 5401556Srgrimes break; 5411556Srgrimes 5421556Srgrimes /* Exit if all errors were local. */ 5431556Srgrimes if (!remoteerr) 5441556Srgrimes exit(1); 5451556Srgrimes 5461556Srgrimes /* 5471556Srgrimes * If retrycnt == 0, we are to keep retrying forever. 5481556Srgrimes * Otherwise decrement it, and exit if it hits zero. 5491556Srgrimes */ 5501556Srgrimes if (retrycnt != 0 && --retrycnt == 0) 5511556Srgrimes exit(1); 5521556Srgrimes 5531556Srgrimes if ((opflags & (BGRND | ISBGRND)) == BGRND) { 5541556Srgrimes warnx("Cannot immediately mount %s:%s, backgrounding", 5551556Srgrimes hostp, spec); 5561556Srgrimes opflags |= ISBGRND; 5571556Srgrimes if (daemon(0, 0) != 0) 5581556Srgrimes err(1, "daemon"); 5591556Srgrimes } 5601556Srgrimes sleep(60); 5611556Srgrimes } 5621556Srgrimes freeaddrinfo(ai_nfs); 5631556Srgrimes nfsargsp->hostname = nam; 5641556Srgrimes /* Add mounted file system to PATH_MOUNTTAB */ 5651556Srgrimes if (!add_mtab(hostp, spec)) 5661556Srgrimes warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 5671556Srgrimes return (1); 5681556Srgrimes} 5691556Srgrimes 5701556Srgrimes/* 5711556Srgrimes * Try to set up the NFS arguments according to the address 5721556Srgrimes * family, protocol (and possibly port) specified in `ai'. 5731556Srgrimes * 5741556Srgrimes * Returns TRYRET_SUCCESS if successful, or: 5751556Srgrimes * TRYRET_TIMEOUT The server did not respond. 5761556Srgrimes * TRYRET_REMOTEERR The server reported an error. 5771556Srgrimes * TRYRET_LOCALERR Local failure. 5781556Srgrimes * 5791556Srgrimes * In all error cases, *errstr will be set to a statically-allocated string 5801556Srgrimes * describing the error. 5811556Srgrimes */ 5821556Srgrimesenum tryret 5831556Srgrimesnfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp, 58476017Skris char *spec, char **errstr) 5851556Srgrimes{ 58676017Skris static char errbuf[256]; 5871556Srgrimes struct sockaddr_storage nfs_ss; 5888855Srgrimes struct netbuf nfs_nb; 5891556Srgrimes struct nfhret nfhret; 5901556Srgrimes struct timeval try; 5911556Srgrimes struct rpc_err rpcerr; 5921556Srgrimes CLIENT *clp; 5931556Srgrimes struct netconfig *nconf, *nconf_mnt; 5941556Srgrimes char *netid, *netid_mnt; 5951556Srgrimes int doconnect, nfsvers, mntvers; 5961556Srgrimes enum clnt_stat stat; 5971556Srgrimes enum mountmode trymntmode; 5981556Srgrimes 5991556Srgrimes trymntmode = mountmode; 6001556Srgrimes errbuf[0] = '\0'; 6011556Srgrimes *errstr = errbuf; 60290113Simp 6031556Srgrimes if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) { 60490113Simp snprintf(errbuf, sizeof errbuf, 6051556Srgrimes "af %d sotype %d not supported", ai->ai_family, 6061556Srgrimes nfsargsp->sotype); 6071556Srgrimes return (TRYRET_LOCALERR); 6081556Srgrimes } 6091556Srgrimes if ((nconf = getnetconf_cached(netid)) == NULL) { 6101556Srgrimes snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 6111556Srgrimes return (TRYRET_LOCALERR); 6121556Srgrimes } 6131556Srgrimes /* The RPCPROG_MNT netid may be different. */ 6141556Srgrimes if (mnttcp_ok) { 6151556Srgrimes netid_mnt = netid; 6161556Srgrimes nconf_mnt = nconf; 6171556Srgrimes } else { 6181556Srgrimes if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 6191556Srgrimes == NULL) { 6201556Srgrimes snprintf(errbuf, sizeof errbuf, 6211556Srgrimes "af %d sotype SOCK_DGRAM not supported", 6221556Srgrimes ai->ai_family); 6231556Srgrimes return (TRYRET_LOCALERR); 6241556Srgrimes } 6251556Srgrimes if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 6261556Srgrimes snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 6271556Srgrimes nc_sperror()); 6281556Srgrimes return (TRYRET_LOCALERR); 6291556Srgrimes } 6301556Srgrimes } 6311556Srgrimes 6321556Srgrimestryagain: 6331556Srgrimes if (trymntmode == V2) { 6341556Srgrimes nfsvers = 2; 6351556Srgrimes mntvers = 1; 6361556Srgrimes } else { 6371556Srgrimes nfsvers = 3; 6381556Srgrimes mntvers = 3; 6391556Srgrimes } 6401556Srgrimes 6411556Srgrimes if (portspec != NULL) { 6421556Srgrimes /* `ai' contains the complete nfsd sockaddr. */ 6431556Srgrimes nfs_nb.buf = ai->ai_addr; 6441556Srgrimes nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 6451556Srgrimes } else { 6461556Srgrimes /* Ask the remote rpcbind. */ 6471556Srgrimes nfs_nb.buf = &nfs_ss; 6481556Srgrimes nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 6491556Srgrimes 6501556Srgrimes if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, 6511556Srgrimes hostp)) { 6521556Srgrimes if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 6531556Srgrimes trymntmode == ANY) { 6541556Srgrimes trymntmode = V2; 6551556Srgrimes goto tryagain; 6561556Srgrimes } 6571556Srgrimes snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 65876017Skris netid, hostp, spec, 6591556Srgrimes clnt_spcreateerror("RPCPROG_NFS")); 6601556Srgrimes return (returncode(rpc_createerr.cf_stat, 6611556Srgrimes &rpc_createerr.cf_error)); 6621556Srgrimes } 6631556Srgrimes } 6641556Srgrimes 6651556Srgrimes /* Check that the server (nfsd) responds on the port we have chosen. */ 6661556Srgrimes clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers, 6671556Srgrimes 0, 0); 6681556Srgrimes if (clp == NULL) { 6691556Srgrimes snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 6701556Srgrimes hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 6711556Srgrimes return (returncode(rpc_createerr.cf_stat, 6721556Srgrimes &rpc_createerr.cf_error)); 6731556Srgrimes } 6741556Srgrimes if (nfsargsp->sotype == SOCK_DGRAM && 6751556Srgrimes !(nfsargsp->flags & NFSMNT_NOCONN)) { 6761556Srgrimes /* 6771556Srgrimes * Use connect(), to match what the kernel does. This 6781556Srgrimes * catches cases where the server responds from the 6791556Srgrimes * wrong source address. 68046684Skris */ 6811556Srgrimes doconnect = 1; 6821556Srgrimes if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 6831556Srgrimes clnt_destroy(clp); 6841556Srgrimes snprintf(errbuf, sizeof errbuf, 6851556Srgrimes "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 6861556Srgrimes spec); 6871556Srgrimes return (TRYRET_LOCALERR); 6881556Srgrimes } 6891556Srgrimes } 6901556Srgrimes 6911556Srgrimes try.tv_sec = 10; 6921556Srgrimes try.tv_usec = 0; 6931556Srgrimes stat = clnt_call(clp, NFSPROC_NULL, xdr_void, NULL, xdr_void, NULL, 6941556Srgrimes try); 6951556Srgrimes if (stat != RPC_SUCCESS) { 69676017Skris if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 6971556Srgrimes clnt_destroy(clp); 6981556Srgrimes trymntmode = V2; 6998855Srgrimes goto tryagain; 7008855Srgrimes } 70176017Skris clnt_geterr(clp, &rpcerr); 7021556Srgrimes snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 70376017Skris hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 7041556Srgrimes clnt_destroy(clp); 70576017Skris return (returncode(stat, &rpcerr)); 7061556Srgrimes } 70776017Skris clnt_destroy(clp); 7081556Srgrimes 7091556Srgrimes /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ 7101556Srgrimes try.tv_sec = 10; 7111556Srgrimes try.tv_usec = 0; 7121556Srgrimes clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt); 7131556Srgrimes if (clp == NULL) { 7141556Srgrimes snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 7151556Srgrimes hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 7161556Srgrimes return (returncode(rpc_createerr.cf_stat, 7171556Srgrimes &rpc_createerr.cf_error)); 7181556Srgrimes } 7191556Srgrimes clp->cl_auth = authsys_create_default(); 7201556Srgrimes nfhret.auth = RPCAUTH_UNIX; 7211556Srgrimes nfhret.vers = mntvers; 7221556Srgrimes stat = clnt_call(clp, RPCMNT_MOUNT, xdr_dir, spec, xdr_fh, &nfhret, 7231556Srgrimes try); 7241556Srgrimes auth_destroy(clp->cl_auth); 72576019Skris if (stat != RPC_SUCCESS) { 7261556Srgrimes if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 7271556Srgrimes clnt_destroy(clp); 7281556Srgrimes trymntmode = V2; 729108533Sschweikh goto tryagain; 730108533Sschweikh } 7311556Srgrimes clnt_geterr(clp, &rpcerr); 7321556Srgrimes snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 7331556Srgrimes hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 7341556Srgrimes clnt_destroy(clp); 7351556Srgrimes return (returncode(stat, &rpcerr)); 73676017Skris } 7371556Srgrimes clnt_destroy(clp); 7381556Srgrimes 7391556Srgrimes if (nfhret.stat != 0) { 7401556Srgrimes snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 7411556Srgrimes hostp, spec, strerror(nfhret.stat)); 7421556Srgrimes return (TRYRET_REMOTEERR); 7431556Srgrimes } 7441556Srgrimes 7451556Srgrimes /* 7461556Srgrimes * Store the filehandle and server address in nfsargsp, making 7471556Srgrimes * sure to copy any locally allocated structures. 74846684Skris */ 7491556Srgrimes nfsargsp->addrlen = nfs_nb.len; 7501556Srgrimes nfsargsp->addr = malloc(nfsargsp->addrlen); 7511556Srgrimes nfsargsp->fhsize = nfhret.fhsize; 7521556Srgrimes nfsargsp->fh = malloc(nfsargsp->fhsize); 7531556Srgrimes if (nfsargsp->addr == NULL || nfsargsp->fh == NULL) 7541556Srgrimes err(1, "malloc"); 7551556Srgrimes bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen); 7561556Srgrimes bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize); 7571556Srgrimes 7581556Srgrimes if (nfsvers == 3) 7591556Srgrimes nfsargsp->flags |= NFSMNT_NFSV3; 7601556Srgrimes else 7611556Srgrimes nfsargsp->flags &= ~NFSMNT_NFSV3; 7621556Srgrimes 7631556Srgrimes return (TRYRET_SUCCESS); 7641556Srgrimes} 7651556Srgrimes 7661556Srgrimes 7671556Srgrimes/* 7681556Srgrimes * Catagorise a RPC return status and error into an `enum tryret' 7691556Srgrimes * return code. 7701556Srgrimes */ 7711556Srgrimesenum tryret 7721556Srgrimesreturncode(enum clnt_stat stat, struct rpc_err *rpcerr) 7731556Srgrimes{ 7741556Srgrimes switch (stat) { 7758855Srgrimes case RPC_TIMEDOUT: 7761556Srgrimes return (TRYRET_TIMEOUT); 7771556Srgrimes case RPC_PMAPFAILURE: 7781556Srgrimes case RPC_PROGNOTREGISTERED: 7791556Srgrimes case RPC_PROGVERSMISMATCH: 7801556Srgrimes /* XXX, these can be local or remote. */ 7811556Srgrimes case RPC_CANTSEND: 7821556Srgrimes case RPC_CANTRECV: 7831556Srgrimes return (TRYRET_REMOTEERR); 7841556Srgrimes case RPC_SYSTEMERROR: 7851556Srgrimes switch (rpcerr->re_errno) { 7861556Srgrimes case ETIMEDOUT: 7871556Srgrimes return (TRYRET_TIMEOUT); 78876017Skris case ENOMEM: 7891556Srgrimes break; 7901556Srgrimes default: 79176017Skris return (TRYRET_REMOTEERR); 7921556Srgrimes } 7931556Srgrimes /* FALLTHROUGH */ 7941556Srgrimes default: 7951556Srgrimes break; 7961556Srgrimes } 7971556Srgrimes return (TRYRET_LOCALERR); 7981556Srgrimes} 7991556Srgrimes 8001556Srgrimes/* 8011556Srgrimes * Look up a netid based on an address family and socket type. 8021556Srgrimes * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 8031556Srgrimes * 8041556Srgrimes * XXX there should be a library function for this. 8051556Srgrimes */ 8061556Srgrimeschar * 8071556Srgrimesnetidbytype(int af, int sotype) { 8081556Srgrimes struct nc_protos *p; 8091556Srgrimes 8101556Srgrimes for (p = nc_protos; p->netid != NULL; p++) { 8111556Srgrimes if (af != p->af || sotype != p->sotype) 8121556Srgrimes continue; 8131556Srgrimes return (p->netid); 8141556Srgrimes } 8151556Srgrimes return (NULL); 8161556Srgrimes} 8171556Srgrimes 8181556Srgrimes/* 8191556Srgrimes * Look up a netconfig entry based on a netid, and cache the result so 8201556Srgrimes * that we don't need to remember to call freenetconfigent(). 8211556Srgrimes * 8221556Srgrimes * Otherwise it behaves just like getnetconfigent(), so nc_*error() 8231556Srgrimes * work on failure. 8241556Srgrimes */ 8251556Srgrimesstruct netconfig * 8261556Srgrimesgetnetconf_cached(const char *netid) { 8278855Srgrimes static struct nc_entry { 8281556Srgrimes struct netconfig *nconf; 8291556Srgrimes struct nc_entry *next; 8301556Srgrimes } *head; 8311556Srgrimes struct nc_entry *p; 8321556Srgrimes struct netconfig *nconf; 8331556Srgrimes 8348855Srgrimes for (p = head; p != NULL; p = p->next) 8351556Srgrimes if (strcmp(netid, p->nconf->nc_netid) == 0) 8361556Srgrimes return (p->nconf); 8371556Srgrimes 8381556Srgrimes if ((nconf = getnetconfigent(netid)) == NULL) 8391556Srgrimes return (NULL); 8401556Srgrimes if ((p = malloc(sizeof(*p))) == NULL) 8411556Srgrimes err(1, "malloc"); 8421556Srgrimes p->nconf = nconf; 8431556Srgrimes p->next = head; 8441556Srgrimes head = p; 8451556Srgrimes 8461556Srgrimes return (p->nconf); 8471556Srgrimes} 84876017Skris 8491556Srgrimes/* 8501556Srgrimes * xdr routines for mount rpc's 8511556Srgrimes */ 8521556Srgrimesint 8531556Srgrimesxdr_dir(xdrsp, dirp) 8541556Srgrimes XDR *xdrsp; 8551556Srgrimes char *dirp; 8561556Srgrimes{ 8571556Srgrimes return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 8581556Srgrimes} 8591556Srgrimes 8601556Srgrimesint 8611556Srgrimesxdr_fh(xdrsp, np) 8621556Srgrimes XDR *xdrsp; 8631556Srgrimes struct nfhret *np; 8641556Srgrimes{ 8651556Srgrimes int i; 8661556Srgrimes long auth, authcnt, authfnd = 0; 8671556Srgrimes 86876019Skris if (!xdr_u_long(xdrsp, &np->stat)) 86990113Simp return (0); 8701556Srgrimes if (np->stat) 8711556Srgrimes return (1); 8721556Srgrimes switch (np->vers) { 8731556Srgrimes case 1: 8741556Srgrimes np->fhsize = NFSX_V2FH; 8751556Srgrimes return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); 8761556Srgrimes case 3: 8771556Srgrimes if (!xdr_long(xdrsp, &np->fhsize)) 8781556Srgrimes return (0); 8798855Srgrimes if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) 8801556Srgrimes return (0); 8811556Srgrimes if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 8821556Srgrimes return (0); 8831556Srgrimes if (!xdr_long(xdrsp, &authcnt)) 88476017Skris return (0); 8851556Srgrimes for (i = 0; i < authcnt; i++) { 8861556Srgrimes if (!xdr_long(xdrsp, &auth)) 8871556Srgrimes return (0); 8881556Srgrimes if (auth == np->auth) 8891556Srgrimes authfnd++; 8901556Srgrimes } 8911556Srgrimes /* 8921556Srgrimes * Some servers, such as DEC's OSF/1 return a nil authenticator 8931556Srgrimes * list to indicate RPCAUTH_UNIX. 8941556Srgrimes */ 8951556Srgrimes if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX)) 8961556Srgrimes np->stat = EAUTH; 8971556Srgrimes return (1); 8981556Srgrimes }; 8991556Srgrimes return (0); 9001556Srgrimes} 9011556Srgrimes 90276017Skrisvoid 9031556Srgrimesusage() 9041556Srgrimes{ 9051556Srgrimes (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 9061556Srgrimes"usage: mount_nfs [-23KNPTUbcdilqs] [-D deadthresh] [-I readdirsize]", 9071556Srgrimes" [-R retrycnt] [-a maxreadahead]", 9081556Srgrimes" [-g maxgroups] [-m realm] [-o options] [-r readsize]", 9091556Srgrimes" [-t timeout] [-w writesize] [-x retrans] rhost:path node"); 9101556Srgrimes exit(1); 9111556Srgrimes} 9121556Srgrimes