11558Srgrimes/* 21558Srgrimes * Copyright (c) 1992, 1993, 1994 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * This code is derived from software contributed to Berkeley by 61558Srgrimes * Rick Macklem at The University of Guelph. 71558Srgrimes * 81558Srgrimes * Redistribution and use in source and binary forms, with or without 91558Srgrimes * modification, are permitted provided that the following conditions 101558Srgrimes * are met: 111558Srgrimes * 1. Redistributions of source code must retain the above copyright 121558Srgrimes * notice, this list of conditions and the following disclaimer. 131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141558Srgrimes * notice, this list of conditions and the following disclaimer in the 151558Srgrimes * documentation and/or other materials provided with the distribution. 161558Srgrimes * 4. Neither the name of the University nor the names of its contributors 171558Srgrimes * may be used to endorse or promote products derived from this software 181558Srgrimes * without specific prior written permission. 191558Srgrimes * 201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301558Srgrimes * SUCH DAMAGE. 311558Srgrimes */ 321558Srgrimes 33114589Sobrien#if 0 341558Srgrimes#ifndef lint 3537427Scharnierstatic const char copyright[] = 361558Srgrimes"@(#) Copyright (c) 1992, 1993, 1994\n\ 371558Srgrimes The Regents of the University of California. All rights reserved.\n"; 381558Srgrimes#endif /* not lint */ 391558Srgrimes 401558Srgrimes#ifndef lint 4123680Speterstatic char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 42114589Sobrien#endif /* not lint */ 4337427Scharnier#endif 44114589Sobrien#include <sys/cdefs.h> 45114589Sobrien__FBSDID("$FreeBSD$"); 461558Srgrimes 471558Srgrimes#include <sys/param.h> 48192930Srmacklem#include <sys/linker.h> 49192930Srmacklem#include <sys/module.h> 501558Srgrimes#include <sys/mount.h> 5174462Salfred#include <sys/socket.h> 521558Srgrimes#include <sys/stat.h> 531558Srgrimes#include <sys/syslog.h> 54164457Srodrigc#include <sys/uio.h> 551558Srgrimes 561558Srgrimes#include <rpc/rpc.h> 571558Srgrimes#include <rpc/pmap_clnt.h> 581558Srgrimes#include <rpc/pmap_prot.h> 59194880Sdfr#include <rpcsvc/nfs_prot.h> 60194880Sdfr#include <rpcsvc/mount.h> 611558Srgrimes 6283653Speter#include <nfsclient/nfs.h> 631558Srgrimes 641558Srgrimes#include <arpa/inet.h> 651558Srgrimes 661558Srgrimes#include <ctype.h> 671558Srgrimes#include <err.h> 681558Srgrimes#include <errno.h> 6974462Salfred#include <fcntl.h> 701558Srgrimes#include <netdb.h> 711558Srgrimes#include <stdio.h> 721558Srgrimes#include <stdlib.h> 73112570Smdodd#include <string.h> 741558Srgrimes#include <strings.h> 7515770Swollman#include <sysexits.h> 761558Srgrimes#include <unistd.h> 771558Srgrimes 781558Srgrimes#include "mntopts.h" 7953550Sdillon#include "mounttab.h" 801558Srgrimes 8176530Siedowse/* Table for af,sotype -> netid conversions. */ 8276530Siedowsestruct nc_protos { 83183008Srodrigc const char *netid; 8476530Siedowse int af; 8576530Siedowse int sotype; 8676530Siedowse} nc_protos[] = { 8776530Siedowse {"udp", AF_INET, SOCK_DGRAM}, 8876530Siedowse {"tcp", AF_INET, SOCK_STREAM}, 8976530Siedowse {"udp6", AF_INET6, SOCK_DGRAM}, 9076530Siedowse {"tcp6", AF_INET6, SOCK_STREAM}, 91164458Srodrigc {NULL, 0, 0} 9276530Siedowse}; 9376530Siedowse 941558Srgrimesstruct nfhret { 959336Sdfr u_long stat; 969336Sdfr long vers; 979336Sdfr long auth; 989336Sdfr long fhsize; 99194880Sdfr u_char nfh[NFS3_FHSIZE]; 1001558Srgrimes}; 1011558Srgrimes#define BGRND 1 1021558Srgrimes#define ISBGRND 2 103112580Smdodd#define OF_NOINET4 4 104112580Smdodd#define OF_NOINET6 8 10580006Siedowseint retrycnt = -1; 1061558Srgrimesint opflags = 0; 107197298Srmacklemint nfsproto = IPPROTO_TCP; 1089336Sdfrint mnttcp_ok = 1; 109183008Srodrigcint noconn = 0; 11075394Siedowsechar *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 111183008Srodrigcstruct sockaddr *addr; 112183008Srodrigcint addrlen = 0; 113183008Srodrigcu_char *fh = NULL; 114183008Srodrigcint fhsize = 0; 115184588Sdfrint secflavor = -1; 116192930Srmacklemint got_principal = 0; 117183008Srodrigc 11875394Siedowseenum mountmode { 11925004Sdfr ANY, 12025004Sdfr V2, 121166183Srodrigc V3, 122192930Srmacklem V4 12325004Sdfr} mountmode = ANY; 1241558Srgrimes 12575394Siedowse/* Return codes for nfs_tryproto. */ 12675394Siedowseenum tryret { 12775394Siedowse TRYRET_SUCCESS, 12875394Siedowse TRYRET_TIMEOUT, /* No response received. */ 12975394Siedowse TRYRET_REMOTEERR, /* Error received from remote server. */ 13075394Siedowse TRYRET_LOCALERR /* Local failure. */ 13175394Siedowse}; 13275394Siedowse 133247856Sjkimstatic int fallback_mount(struct iovec *iov, int iovlen); 134203461Sdelphijstatic int sec_name_to_num(char *sec); 135203461Sdelphijstatic char *sec_num_to_name(int num); 136203461Sdelphijstatic int getnfsargs(char *, struct iovec **iov, int *iovlen); 13792882Simp/* void set_rpc_maxgrouplist(int); */ 138203461Sdelphijstatic struct netconfig *getnetconf_cached(const char *netid); 139203461Sdelphijstatic const char *netidbytype(int af, int sotype); 140203461Sdelphijstatic void usage(void) __dead2; 141203461Sdelphijstatic int xdr_dir(XDR *, char *); 142203461Sdelphijstatic int xdr_fh(XDR *, struct nfhret *); 143203461Sdelphijstatic enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, 144183008Srodrigc char **errstr, struct iovec **iov, int *iovlen); 145203461Sdelphijstatic enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 1461558Srgrimes 1471558Srgrimesint 148156925Simpmain(int argc, char *argv[]) 1491558Srgrimes{ 15092806Sobrien int c; 151164457Srodrigc struct iovec *iov; 152247856Sjkim int num, iovlen; 153183182Srodrigc int osversion; 154176377Syar char *name, *p, *spec, *fstype; 155164733Srodrigc char mntpath[MAXPATHLEN], errmsg[255]; 156192930Srmacklem char hostname[MAXHOSTNAMELEN + 1], *gssname, gssn[MAXHOSTNAMELEN + 50]; 1571558Srgrimes 158164457Srodrigc iov = NULL; 159164457Srodrigc iovlen = 0; 160164733Srodrigc memset(errmsg, 0, sizeof(errmsg)); 161192930Srmacklem gssname = NULL; 162164457Srodrigc 163164732Srodrigc fstype = strrchr(argv[0], '_'); 164164732Srodrigc if (fstype == NULL) 165164732Srodrigc errx(EX_USAGE, "argv[0] must end in _fstype"); 166164732Srodrigc 167164732Srodrigc ++fstype; 168164732Srodrigc 1691558Srgrimes while ((c = getopt(argc, argv, 170192578Srwatson "23a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1) 1711558Srgrimes switch (c) { 17225004Sdfr case '2': 17325004Sdfr mountmode = V2; 17425004Sdfr break; 1759336Sdfr case '3': 17625004Sdfr mountmode = V3; 1779336Sdfr break; 1781558Srgrimes case 'a': 179214419Sjh printf("-a deprecated, use -o readahead=<value>\n"); 180183008Srodrigc build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1); 1811558Srgrimes break; 1821558Srgrimes case 'b': 1831558Srgrimes opflags |= BGRND; 1841558Srgrimes break; 1851558Srgrimes case 'c': 186183008Srodrigc printf("-c deprecated, use -o noconn\n"); 187183008Srodrigc build_iovec(&iov, &iovlen, "noconn", NULL, 0); 188183008Srodrigc noconn = 1; 1891558Srgrimes break; 1901558Srgrimes case 'D': 191183008Srodrigc printf("-D deprecated, use -o deadthresh=<value>\n"); 192183008Srodrigc build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1); 1931558Srgrimes break; 1941558Srgrimes case 'd': 195183008Srodrigc printf("-d deprecated, use -o dumbtimer"); 196183008Srodrigc build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0); 1971558Srgrimes break; 1981558Srgrimes case 'g': 199183008Srodrigc printf("-g deprecated, use -o maxgroups"); 2001558Srgrimes num = strtol(optarg, &p, 10); 2011558Srgrimes if (*p || num <= 0) 2021558Srgrimes errx(1, "illegal -g value -- %s", optarg); 203183008Srodrigc //set_rpc_maxgrouplist(num); 204183008Srodrigc build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1); 2051558Srgrimes break; 2069336Sdfr case 'I': 207183008Srodrigc printf("-I deprecated, use -o readdirsize=<value>\n"); 208183008Srodrigc build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1); 2099336Sdfr break; 2101558Srgrimes case 'i': 211183008Srodrigc printf("-i deprecated, use -o intr\n"); 212183008Srodrigc build_iovec(&iov, &iovlen, "intr", NULL, 0); 2131558Srgrimes break; 21486284Salfred case 'L': 215216725Ssimon printf("-L deprecated, use -o nolockd\n"); 216183008Srodrigc build_iovec(&iov, &iovlen, "nolockd", NULL, 0); 21786284Salfred break; 2181558Srgrimes case 'l': 219183008Srodrigc printf("-l deprecated, -o rdirplus\n"); 220183008Srodrigc build_iovec(&iov, &iovlen, "rdirplus", NULL, 0); 2211558Srgrimes break; 22230580Sjoerg case 'N': 223183008Srodrigc printf("-N deprecated, do not specify -o resvport\n"); 22430580Sjoerg break; 225183008Srodrigc case 'o': { 226183008Srodrigc int pass_flag_to_nmount; 227183008Srodrigc char *opt = optarg; 228183008Srodrigc while (opt) { 229183008Srodrigc char *pval = NULL; 230183008Srodrigc char *pnextopt = NULL; 231183008Srodrigc char *val = ""; 232183008Srodrigc pass_flag_to_nmount = 1; 233198491Sjh pnextopt = strchr(opt, ','); 234198491Sjh if (pnextopt != NULL) { 235198491Sjh *pnextopt = '\0'; 236198491Sjh pnextopt++; 237198491Sjh } 238183008Srodrigc pval = strchr(opt, '='); 239183008Srodrigc if (pval != NULL) { 240183008Srodrigc *pval = '\0'; 241183008Srodrigc val = pval + 1; 242183008Srodrigc } 243183008Srodrigc if (strcmp(opt, "bg") == 0) { 244183008Srodrigc opflags |= BGRND; 245183008Srodrigc pass_flag_to_nmount=0; 246183008Srodrigc } else if (strcmp(opt, "fg") == 0) { 247183008Srodrigc /* same as not specifying -o bg */ 248183008Srodrigc pass_flag_to_nmount=0; 249192930Srmacklem } else if (strcmp(opt, "gssname") == 0) { 250192930Srmacklem pass_flag_to_nmount = 0; 251192930Srmacklem gssname = val; 252183008Srodrigc } else if (strcmp(opt, "mntudp") == 0) { 253183008Srodrigc mnttcp_ok = 0; 254183008Srodrigc nfsproto = IPPROTO_UDP; 255183008Srodrigc } else if (strcmp(opt, "udp") == 0) { 256183008Srodrigc nfsproto = IPPROTO_UDP; 257183008Srodrigc } else if (strcmp(opt, "tcp") == 0) { 258183008Srodrigc nfsproto = IPPROTO_TCP; 259183008Srodrigc } else if (strcmp(opt, "noinet4") == 0) { 260183008Srodrigc pass_flag_to_nmount=0; 261183008Srodrigc opflags |= OF_NOINET4; 262183008Srodrigc } else if (strcmp(opt, "noinet6") == 0) { 263183008Srodrigc pass_flag_to_nmount=0; 264183008Srodrigc opflags |= OF_NOINET6; 265183008Srodrigc } else if (strcmp(opt, "noconn") == 0) { 266183008Srodrigc noconn = 1; 267183008Srodrigc } else if (strcmp(opt, "nfsv2") == 0) { 268183008Srodrigc pass_flag_to_nmount=0; 269183008Srodrigc mountmode = V2; 270183008Srodrigc } else if (strcmp(opt, "nfsv3") == 0) { 271183008Srodrigc mountmode = V3; 272192930Srmacklem } else if (strcmp(opt, "nfsv4") == 0) { 273192930Srmacklem pass_flag_to_nmount=0; 274192930Srmacklem mountmode = V4; 275221124Srmacklem fstype = "nfs"; 276192930Srmacklem nfsproto = IPPROTO_TCP; 277192930Srmacklem if (portspec == NULL) 278192930Srmacklem portspec = "2049"; 279183008Srodrigc } else if (strcmp(opt, "port") == 0) { 280183008Srodrigc pass_flag_to_nmount=0; 281103039Speter asprintf(&portspec, "%d", 282183008Srodrigc atoi(val)); 283103039Speter if (portspec == NULL) 284103039Speter err(1, "asprintf"); 285192930Srmacklem } else if (strcmp(opt, "principal") == 0) { 286192930Srmacklem got_principal = 1; 287184588Sdfr } else if (strcmp(opt, "sec") == 0) { 288184588Sdfr /* 289184588Sdfr * Don't add this option to 290184588Sdfr * the iovec yet - we will 291184588Sdfr * negotiate which sec flavor 292184588Sdfr * to use with the remote 293184588Sdfr * mountd. 294184588Sdfr */ 295184588Sdfr pass_flag_to_nmount=0; 296184588Sdfr secflavor = sec_name_to_num(val); 297184588Sdfr if (secflavor < 0) { 298184588Sdfr errx(1, 299184588Sdfr "illegal sec value -- %s", 300184588Sdfr val); 301184588Sdfr } 302183008Srodrigc } else if (strcmp(opt, "retrycnt") == 0) { 303183008Srodrigc pass_flag_to_nmount=0; 304183008Srodrigc num = strtol(val, &p, 10); 305183008Srodrigc if (*p || num < 0) 306183008Srodrigc errx(1, "illegal retrycnt value -- %s", val); 307183008Srodrigc retrycnt = num; 308183008Srodrigc } else if (strcmp(opt, "maxgroups") == 0) { 309183008Srodrigc num = strtol(val, &p, 10); 310183008Srodrigc if (*p || num <= 0) 311183008Srodrigc errx(1, "illegal maxgroups value -- %s", val); 312183008Srodrigc //set_rpc_maxgrouplist(num); 313270043Sbz } else if (strcmp(opt, "vers") == 0) { 314270043Sbz num = strtol(val, &p, 10); 315270043Sbz if (*p || num <= 0) 316270043Sbz errx(1, "illegal vers value -- " 317270043Sbz "%s", val); 318270043Sbz switch (num) { 319270043Sbz case 2: 320270043Sbz mountmode = V2; 321270043Sbz break; 322270043Sbz case 3: 323270043Sbz mountmode = V3; 324270043Sbz build_iovec(&iov, &iovlen, 325270043Sbz "nfsv3", NULL, 0); 326270043Sbz break; 327270043Sbz case 4: 328270043Sbz mountmode = V4; 329270043Sbz fstype = "nfs"; 330270043Sbz nfsproto = IPPROTO_TCP; 331270043Sbz if (portspec == NULL) 332270043Sbz portspec = "2049"; 333270043Sbz break; 334270043Sbz default: 335270043Sbz errx(1, "illegal nfs version " 336270043Sbz "value -- %s", val); 337270043Sbz } 338270043Sbz pass_flag_to_nmount=0; 339103039Speter } 340183008Srodrigc if (pass_flag_to_nmount) 341183008Srodrigc build_iovec(&iov, &iovlen, opt, val, 342183008Srodrigc strlen(val) + 1); 343183008Srodrigc opt = pnextopt; 34475394Siedowse } 345103039Speter } 3461558Srgrimes break; 3471558Srgrimes case 'P': 348183008Srodrigc /* obsolete for -o noresvport now default */ 349183008Srodrigc printf("-P deprecated, use -o noresvport\n"); 350183008Srodrigc build_iovec(&iov, &iovlen, "noresvport", NULL, 0); 3511558Srgrimes break; 3521558Srgrimes case 'R': 353183008Srodrigc printf("-R deprecated, use -o retrycnt=<retrycnt>\n"); 3541558Srgrimes num = strtol(optarg, &p, 10); 35580006Siedowse if (*p || num < 0) 3561558Srgrimes errx(1, "illegal -R value -- %s", optarg); 3571558Srgrimes retrycnt = num; 3581558Srgrimes break; 3591558Srgrimes case 'r': 360183008Srodrigc printf("-r deprecated, use -o rsize=<rsize>\n"); 361183008Srodrigc build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1); 3621558Srgrimes break; 3631558Srgrimes case 's': 364183008Srodrigc printf("-s deprecated, use -o soft\n"); 365183008Srodrigc build_iovec(&iov, &iovlen, "soft", NULL, 0); 3661558Srgrimes break; 3671558Srgrimes case 'T': 3689336Sdfr nfsproto = IPPROTO_TCP; 369183008Srodrigc printf("-T deprecated, use -o tcp\n"); 3701558Srgrimes break; 3711558Srgrimes case 't': 372183008Srodrigc printf("-t deprecated, use -o timeout=<value>\n"); 373183008Srodrigc build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1); 3741558Srgrimes break; 3751558Srgrimes case 'w': 376183008Srodrigc printf("-w deprecated, use -o wsize=<value>\n"); 377183008Srodrigc build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1); 3781558Srgrimes break; 3791558Srgrimes case 'x': 380183008Srodrigc printf("-x deprecated, use -o retrans=<value>\n"); 381183008Srodrigc build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1); 3821558Srgrimes break; 3839336Sdfr case 'U': 384183008Srodrigc printf("-U deprecated, use -o mntudp\n"); 3859336Sdfr mnttcp_ok = 0; 386166183Srodrigc nfsproto = IPPROTO_UDP; 387183008Srodrigc build_iovec(&iov, &iovlen, "mntudp", NULL, 0); 3889336Sdfr break; 3891558Srgrimes default: 3901558Srgrimes usage(); 3911558Srgrimes break; 3921558Srgrimes } 3931558Srgrimes argc -= optind; 3941558Srgrimes argv += optind; 3951558Srgrimes 39623680Speter if (argc != 2) { 3975966Sdg usage(); 39823680Speter /* NOTREACHED */ 39923680Speter } 4001558Srgrimes 4011558Srgrimes spec = *argv++; 4021558Srgrimes name = *argv; 4031558Srgrimes 40480006Siedowse if (retrycnt == -1) 40580086Siedowse /* The default is to keep retrying forever. */ 40680086Siedowse retrycnt = 0; 4072999Swollman 408192930Srmacklem /* 409221124Srmacklem * If the fstye is "oldnfs", run the old NFS client unless the 410221124Srmacklem * "nfsv4" option was specified. 411192930Srmacklem */ 412221124Srmacklem if (strcmp(fstype, "nfs") == 0) { 413192930Srmacklem if (modfind("nfscl") < 0) { 414192930Srmacklem /* Not present in kernel, try loading it */ 415192930Srmacklem if (kldload("nfscl") < 0 || 416192930Srmacklem modfind("nfscl") < 0) 417192930Srmacklem errx(1, "nfscl is not available"); 418192930Srmacklem } 419192930Srmacklem } 420192930Srmacklem 421192930Srmacklem /* 422192930Srmacklem * Add the fqdn to the gssname, as required. 423192930Srmacklem */ 424192930Srmacklem if (gssname != NULL) { 425192930Srmacklem if (strchr(gssname, '@') == NULL && 426192930Srmacklem gethostname(hostname, MAXHOSTNAMELEN) == 0) { 427192930Srmacklem snprintf(gssn, sizeof (gssn), "%s@%s", gssname, 428192930Srmacklem hostname); 429192930Srmacklem gssname = gssn; 430192930Srmacklem } 431192930Srmacklem build_iovec(&iov, &iovlen, "gssname", gssname, 432192930Srmacklem strlen(gssname) + 1); 433192930Srmacklem } 434192930Srmacklem 435192578Srwatson if (!getnfsargs(spec, &iov, &iovlen)) 436192578Srwatson exit(1); 437166183Srodrigc 43852055Sphk /* resolve the mountpoint with realpath(3) */ 439230226Sjh if (checkpath(name, mntpath) != 0) 440230226Sjh err(1, "%s", mntpath); 44152055Sphk 442164732Srodrigc build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); 443164457Srodrigc build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); 444164733Srodrigc build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 445164457Srodrigc 446183182Srodrigc /* 447183182Srodrigc * XXX: 448183182Srodrigc * Backwards compatibility routines for older kernels. 449183182Srodrigc * Remove this and fallback_mount() code when we do not need to support 450183182Srodrigc * NFS mounts against older kernels which still need 451183182Srodrigc * struct nfs_args to be passed in via nmount(). 452183182Srodrigc */ 453183182Srodrigc osversion = getosreldate(); 454193191Srodrigc if (osversion >= 702100) { 455247856Sjkim if (nmount(iov, iovlen, 0)) 456183008Srodrigc err(1, "%s, %s", mntpath, errmsg); 457183182Srodrigc } else { 458247856Sjkim if (fallback_mount(iov, iovlen)) 459183182Srodrigc err(1, "%s, %s", mntpath, errmsg); 460183008Srodrigc } 4619336Sdfr 4621558Srgrimes exit(0); 4631558Srgrimes} 4641558Srgrimes 465183008Srodrigcstatic int 466183008Srodrigcfindopt(struct iovec *iov, int iovlen, const char *name, 467183008Srodrigc char **valuep, int *lenp) 468183008Srodrigc{ 469183008Srodrigc int i; 470183008Srodrigc 471183008Srodrigc for (i = 0; i < iovlen/2; i++, iov += 2) { 472183008Srodrigc if (strcmp(name, iov[0].iov_base) == 0) { 473183008Srodrigc if (valuep) 474183008Srodrigc *valuep = iov[1].iov_base; 475183008Srodrigc if (lenp) 476183008Srodrigc *lenp = iov[1].iov_len; 477183008Srodrigc return (0); 478183008Srodrigc } 479183008Srodrigc } 480183008Srodrigc return (ENOENT); 481183008Srodrigc} 482183008Srodrigc 483183008Srodrigcstatic void 484183008Srodrigccopyopt(struct iovec **newiov, int *newiovlen, 485183008Srodrigc struct iovec *iov, int iovlen, const char *name) 486183008Srodrigc{ 487183008Srodrigc char *value; 488183008Srodrigc int len; 489183008Srodrigc 490183008Srodrigc if (findopt(iov, iovlen, name, &value, &len) == 0) 491183008Srodrigc build_iovec(newiov, newiovlen, name, value, len); 492183008Srodrigc} 493183008Srodrigc 494188217Srodrigc/* 495188217Srodrigc * XXX: This function is provided for backwards 496188217Srodrigc * compatibility with older kernels which did not support 497188217Srodrigc * passing NFS mount options to nmount() as individual 498188217Srodrigc * parameters. It should be eventually be removed. 499188217Srodrigc */ 500203461Sdelphijstatic int 501247856Sjkimfallback_mount(struct iovec *iov, int iovlen) 5021558Srgrimes{ 503183008Srodrigc struct nfs_args args = { 504183008Srodrigc .version = NFS_ARGSVERSION, 505183008Srodrigc .addr = NULL, 506183008Srodrigc .addrlen = sizeof (struct sockaddr_in), 507183008Srodrigc .sotype = SOCK_STREAM, 508183008Srodrigc .proto = 0, 509183008Srodrigc .fh = NULL, 510183008Srodrigc .fhsize = 0, 511183008Srodrigc .flags = NFSMNT_RESVPORT, 512183008Srodrigc .wsize = NFS_WSIZE, 513183008Srodrigc .rsize = NFS_RSIZE, 514183008Srodrigc .readdirsize = NFS_READDIRSIZE, 515183008Srodrigc .timeo = 10, 516183008Srodrigc .retrans = NFS_RETRANS, 517183008Srodrigc .maxgrouplist = NFS_MAXGRPS, 518183008Srodrigc .readahead = NFS_DEFRAHEAD, 519183008Srodrigc .wcommitsize = 0, /* was: NQ_DEFLEASE */ 520183008Srodrigc .deadthresh = NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 521183008Srodrigc .hostname = NULL, 522183008Srodrigc /* args version 4 */ 523183008Srodrigc .acregmin = NFS_MINATTRTIMO, 524183008Srodrigc .acregmax = NFS_MAXATTRTIMO, 525183008Srodrigc .acdirmin = NFS_MINDIRATTRTIMO, 526183008Srodrigc .acdirmax = NFS_MAXDIRATTRTIMO, 527183008Srodrigc }; 528183008Srodrigc int ret; 529183008Srodrigc char *opt; 530183008Srodrigc struct iovec *newiov; 531183008Srodrigc int newiovlen; 532183008Srodrigc 533183008Srodrigc if (findopt(iov, iovlen, "dumbtimer", NULL, NULL) == 0) 534183008Srodrigc args.flags |= NFSMNT_DUMBTIMR; 535183008Srodrigc if (findopt(iov, iovlen, "noconn", NULL, NULL) == 0) 536183008Srodrigc args.flags |= NFSMNT_NOCONN; 537183008Srodrigc if (findopt(iov, iovlen, "conn", NULL, NULL) == 0) 538183008Srodrigc args.flags |= NFSMNT_NOCONN; 539183008Srodrigc if (findopt(iov, iovlen, "nolockd", NULL, NULL) == 0) 540183008Srodrigc args.flags |= NFSMNT_NOLOCKD; 541183008Srodrigc if (findopt(iov, iovlen, "lockd", NULL, NULL) == 0) 542183008Srodrigc args.flags &= ~NFSMNT_NOLOCKD; 543183008Srodrigc if (findopt(iov, iovlen, "intr", NULL, NULL) == 0) 544183008Srodrigc args.flags |= NFSMNT_INT; 545183008Srodrigc if (findopt(iov, iovlen, "rdirplus", NULL, NULL) == 0) 546183008Srodrigc args.flags |= NFSMNT_RDIRPLUS; 547183008Srodrigc if (findopt(iov, iovlen, "resvport", NULL, NULL) == 0) 548183008Srodrigc args.flags |= NFSMNT_RESVPORT; 549183008Srodrigc if (findopt(iov, iovlen, "noresvport", NULL, NULL) == 0) 550183008Srodrigc args.flags &= ~NFSMNT_RESVPORT; 551183008Srodrigc if (findopt(iov, iovlen, "soft", NULL, NULL) == 0) 552183008Srodrigc args.flags |= NFSMNT_SOFT; 553183008Srodrigc if (findopt(iov, iovlen, "hard", NULL, NULL) == 0) 554183008Srodrigc args.flags &= ~NFSMNT_SOFT; 555183008Srodrigc if (findopt(iov, iovlen, "mntudp", NULL, NULL) == 0) 556183008Srodrigc args.sotype = SOCK_DGRAM; 557183008Srodrigc if (findopt(iov, iovlen, "udp", NULL, NULL) == 0) 558183008Srodrigc args.sotype = SOCK_DGRAM; 559183008Srodrigc if (findopt(iov, iovlen, "tcp", NULL, NULL) == 0) 560183008Srodrigc args.sotype = SOCK_STREAM; 561183008Srodrigc if (findopt(iov, iovlen, "nfsv3", NULL, NULL) == 0) 562183008Srodrigc args.flags |= NFSMNT_NFSV3; 563183008Srodrigc if (findopt(iov, iovlen, "readdirsize", &opt, NULL) == 0) { 564183008Srodrigc if (opt == NULL) { 565183008Srodrigc errx(1, "illegal readdirsize"); 566183008Srodrigc } 567183008Srodrigc ret = sscanf(opt, "%d", &args.readdirsize); 568183008Srodrigc if (ret != 1 || args.readdirsize <= 0) { 569183008Srodrigc errx(1, "illegal readdirsize: %s", opt); 570183008Srodrigc } 571183008Srodrigc args.flags |= NFSMNT_READDIRSIZE; 572183008Srodrigc } 573183008Srodrigc if (findopt(iov, iovlen, "readahead", &opt, NULL) == 0) { 574183008Srodrigc if (opt == NULL) { 575183008Srodrigc errx(1, "illegal readahead"); 576183008Srodrigc } 577183008Srodrigc ret = sscanf(opt, "%d", &args.readahead); 578183008Srodrigc if (ret != 1 || args.readahead <= 0) { 579183008Srodrigc errx(1, "illegal readahead: %s", opt); 580183008Srodrigc } 581183008Srodrigc args.flags |= NFSMNT_READAHEAD; 582183008Srodrigc } 583183008Srodrigc if (findopt(iov, iovlen, "wsize", &opt, NULL) == 0) { 584183008Srodrigc if (opt == NULL) { 585183008Srodrigc errx(1, "illegal wsize"); 586183008Srodrigc } 587183008Srodrigc ret = sscanf(opt, "%d", &args.wsize); 588183008Srodrigc if (ret != 1 || args.wsize <= 0) { 589183008Srodrigc errx(1, "illegal wsize: %s", opt); 590183008Srodrigc } 591183008Srodrigc args.flags |= NFSMNT_WSIZE; 592183008Srodrigc } 593183008Srodrigc if (findopt(iov, iovlen, "rsize", &opt, NULL) == 0) { 594183008Srodrigc if (opt == NULL) { 595183008Srodrigc errx(1, "illegal rsize"); 596183008Srodrigc } 597183008Srodrigc ret = sscanf(opt, "%d", &args.rsize); 598183008Srodrigc if (ret != 1 || args.rsize <= 0) { 599183008Srodrigc errx(1, "illegal wsize: %s", opt); 600183008Srodrigc } 601183008Srodrigc args.flags |= NFSMNT_RSIZE; 602183008Srodrigc } 603183008Srodrigc if (findopt(iov, iovlen, "retrans", &opt, NULL) == 0) { 604183008Srodrigc if (opt == NULL) { 605183008Srodrigc errx(1, "illegal retrans"); 606183008Srodrigc } 607183008Srodrigc ret = sscanf(opt, "%d", &args.retrans); 608183008Srodrigc if (ret != 1 || args.retrans <= 0) { 609183008Srodrigc errx(1, "illegal retrans: %s", opt); 610183008Srodrigc } 611183008Srodrigc args.flags |= NFSMNT_RETRANS; 612183008Srodrigc } 613183008Srodrigc if (findopt(iov, iovlen, "acregmin", &opt, NULL) == 0) { 614183008Srodrigc ret = sscanf(opt, "%d", &args.acregmin); 615187812Srodrigc if (ret != 1 || args.acregmin < 0) { 616183008Srodrigc errx(1, "illegal acregmin: %s", opt); 617183008Srodrigc } 618188217Srodrigc args.flags |= NFSMNT_ACREGMIN; 619183008Srodrigc } 620183008Srodrigc if (findopt(iov, iovlen, "acregmax", &opt, NULL) == 0) { 621183008Srodrigc ret = sscanf(opt, "%d", &args.acregmax); 622187812Srodrigc if (ret != 1 || args.acregmax < 0) { 623183008Srodrigc errx(1, "illegal acregmax: %s", opt); 624183008Srodrigc } 625188217Srodrigc args.flags |= NFSMNT_ACREGMAX; 626183008Srodrigc } 627183008Srodrigc if (findopt(iov, iovlen, "acdirmin", &opt, NULL) == 0) { 628183008Srodrigc ret = sscanf(opt, "%d", &args.acdirmin); 629187812Srodrigc if (ret != 1 || args.acdirmin < 0) { 630183008Srodrigc errx(1, "illegal acdirmin: %s", opt); 631183008Srodrigc } 632188217Srodrigc args.flags |= NFSMNT_ACDIRMIN; 633183008Srodrigc } 634183008Srodrigc if (findopt(iov, iovlen, "acdirmax", &opt, NULL) == 0) { 635183008Srodrigc ret = sscanf(opt, "%d", &args.acdirmax); 636187812Srodrigc if (ret != 1 || args.acdirmax < 0) { 637183008Srodrigc errx(1, "illegal acdirmax: %s", opt); 638183008Srodrigc } 639188218Srodrigc args.flags |= NFSMNT_ACDIRMAX; 640183008Srodrigc } 641227507Sjhb if (findopt(iov, iovlen, "wcommitsize", &opt, NULL) == 0) { 642227507Sjhb ret = sscanf(opt, "%d", &args.wcommitsize); 643227507Sjhb if (ret != 1 || args.wcommitsize < 0) { 644227507Sjhb errx(1, "illegal wcommitsize: %s", opt); 645227507Sjhb } 646227507Sjhb args.flags |= NFSMNT_WCOMMITSIZE; 647227507Sjhb } 648183008Srodrigc if (findopt(iov, iovlen, "deadthresh", &opt, NULL) == 0) { 649183008Srodrigc ret = sscanf(opt, "%d", &args.deadthresh); 650183008Srodrigc if (ret != 1 || args.deadthresh <= 0) { 651183008Srodrigc errx(1, "illegal deadthresh: %s", opt); 652183008Srodrigc } 653183008Srodrigc args.flags |= NFSMNT_DEADTHRESH; 654183008Srodrigc } 655183008Srodrigc if (findopt(iov, iovlen, "timeout", &opt, NULL) == 0) { 656183008Srodrigc ret = sscanf(opt, "%d", &args.timeo); 657183008Srodrigc if (ret != 1 || args.timeo <= 0) { 658183008Srodrigc errx(1, "illegal timeout: %s", opt); 659183008Srodrigc } 660183008Srodrigc args.flags |= NFSMNT_TIMEO; 661183008Srodrigc } 662183008Srodrigc if (findopt(iov, iovlen, "maxgroups", &opt, NULL) == 0) { 663183008Srodrigc ret = sscanf(opt, "%d", &args.maxgrouplist); 664183008Srodrigc if (ret != 1 || args.timeo <= 0) { 665183008Srodrigc errx(1, "illegal maxgroups: %s", opt); 666183008Srodrigc } 667183008Srodrigc args.flags |= NFSMNT_MAXGRPS; 668183008Srodrigc } 669183008Srodrigc if (findopt(iov, iovlen, "addr", &opt, 670183008Srodrigc &args.addrlen) == 0) { 671183008Srodrigc args.addr = (struct sockaddr *) opt; 672183008Srodrigc } 673183008Srodrigc if (findopt(iov, iovlen, "fh", &opt, &args.fhsize) == 0) { 674183008Srodrigc args.fh = opt; 675183008Srodrigc } 676183008Srodrigc if (findopt(iov, iovlen, "hostname", &args.hostname, 677183008Srodrigc NULL) == 0) { 678183008Srodrigc } 679183008Srodrigc if (args.hostname == NULL) { 680183008Srodrigc errx(1, "Invalid hostname"); 681183008Srodrigc } 682183008Srodrigc 683183008Srodrigc newiov = NULL; 684183008Srodrigc newiovlen = 0; 685183008Srodrigc 686183008Srodrigc build_iovec(&newiov, &newiovlen, "nfs_args", &args, sizeof(args)); 687183008Srodrigc copyopt(&newiov, &newiovlen, iov, iovlen, "fstype"); 688183008Srodrigc copyopt(&newiov, &newiovlen, iov, iovlen, "fspath"); 689183008Srodrigc copyopt(&newiov, &newiovlen, iov, iovlen, "errmsg"); 690183008Srodrigc 691247856Sjkim return nmount(newiov, newiovlen, 0); 692183008Srodrigc} 693183008Srodrigc 694203461Sdelphijstatic int 695184588Sdfrsec_name_to_num(char *sec) 696184588Sdfr{ 697184588Sdfr if (!strcmp(sec, "krb5")) 698184588Sdfr return (RPCSEC_GSS_KRB5); 699184588Sdfr if (!strcmp(sec, "krb5i")) 700184588Sdfr return (RPCSEC_GSS_KRB5I); 701184588Sdfr if (!strcmp(sec, "krb5p")) 702184588Sdfr return (RPCSEC_GSS_KRB5P); 703184588Sdfr if (!strcmp(sec, "sys")) 704184588Sdfr return (AUTH_SYS); 705184588Sdfr return (-1); 706184588Sdfr} 707184588Sdfr 708203461Sdelphijstatic char * 709184588Sdfrsec_num_to_name(int flavor) 710184588Sdfr{ 711184588Sdfr switch (flavor) { 712184588Sdfr case RPCSEC_GSS_KRB5: 713184588Sdfr return ("krb5"); 714184588Sdfr case RPCSEC_GSS_KRB5I: 715184588Sdfr return ("krb5i"); 716184588Sdfr case RPCSEC_GSS_KRB5P: 717184588Sdfr return ("krb5p"); 718184588Sdfr case AUTH_SYS: 719184588Sdfr return ("sys"); 720184588Sdfr } 721184588Sdfr return (NULL); 722184588Sdfr} 723184588Sdfr 724203461Sdelphijstatic int 725183008Srodrigcgetnfsargs(char *spec, struct iovec **iov, int *iovlen) 726183008Srodrigc{ 72774462Salfred struct addrinfo hints, *ai_nfs, *ai; 72875394Siedowse enum tryret ret; 729203490Sume int ecode, speclen, remoteerr, offset, have_bracket = 0; 73075394Siedowse char *hostp, *delimp, *errstr; 73152679Sgreen size_t len; 732192930Srmacklem static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; 7331558Srgrimes 734203490Sume if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 735203490Sume *(delimp + 1) == ':') { 736203490Sume hostp = spec + 1; 737203490Sume spec = delimp + 2; 738203490Sume have_bracket = 1; 739203490Sume } else if ((delimp = strrchr(spec, ':')) != NULL) { 7401558Srgrimes hostp = spec; 7411558Srgrimes spec = delimp + 1; 74252055Sphk } else if ((delimp = strrchr(spec, '@')) != NULL) { 74352055Sphk warnx("path@server syntax is deprecated, use server:path"); 74452055Sphk hostp = delimp + 1; 7451558Srgrimes } else { 74652055Sphk warnx("no <host>:<dirpath> nfs-name"); 7471558Srgrimes return (0); 7481558Srgrimes } 7491558Srgrimes *delimp = '\0'; 75052055Sphk 7511558Srgrimes /* 75252055Sphk * If there has been a trailing slash at mounttime it seems 75352055Sphk * that some mountd implementations fail to remove the mount 75452055Sphk * entries from their mountlist while unmounting. 75552055Sphk */ 75652679Sgreen for (speclen = strlen(spec); 75752679Sgreen speclen > 1 && spec[speclen - 1] == '/'; 75852679Sgreen speclen--) 75952055Sphk spec[speclen - 1] = '\0'; 76052055Sphk if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 76152055Sphk warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 76252055Sphk return (0); 76352055Sphk } 76452055Sphk /* Make both '@' and ':' notations equal */ 76552679Sgreen if (*hostp != '\0') { 76652679Sgreen len = strlen(hostp); 767203490Sume offset = 0; 768203490Sume if (have_bracket) 769203490Sume nam[offset++] = '['; 770203490Sume memmove(nam + offset, hostp, len); 771203490Sume if (have_bracket) 772203490Sume nam[len + offset++] = ']'; 773203490Sume nam[len + offset++] = ':'; 774203490Sume memmove(nam + len + offset, spec, speclen); 775203490Sume nam[len + speclen + offset] = '\0'; 77652679Sgreen } 7771558Srgrimes 7781558Srgrimes /* 77983653Speter * Handle an internet host address. 7801558Srgrimes */ 78174462Salfred memset(&hints, 0, sizeof hints); 78274462Salfred hints.ai_flags = AI_NUMERICHOST; 783183008Srodrigc if (nfsproto == IPPROTO_TCP) 784183008Srodrigc hints.ai_socktype = SOCK_STREAM; 785183008Srodrigc else if (nfsproto == IPPROTO_UDP) 786183008Srodrigc hints.ai_socktype = SOCK_DGRAM; 787183008Srodrigc 78883653Speter if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 789192930Srmacklem hints.ai_flags = AI_CANONNAME; 79075394Siedowse if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 79175394Siedowse != 0) { 79275394Siedowse if (portspec == NULL) 79375394Siedowse errx(1, "%s: %s", hostp, gai_strerror(ecode)); 79475394Siedowse else 79575394Siedowse errx(1, "%s:%s: %s", hostp, portspec, 79675394Siedowse gai_strerror(ecode)); 7971558Srgrimes return (0); 7981558Srgrimes } 799192930Srmacklem 800192930Srmacklem /* 801192930Srmacklem * For a Kerberized nfs mount where the "principal" 802192930Srmacklem * argument has not been set, add it here. 803192930Srmacklem */ 804192930Srmacklem if (got_principal == 0 && secflavor >= 0 && 805192930Srmacklem secflavor != AUTH_SYS && ai_nfs->ai_canonname != NULL) { 806192930Srmacklem snprintf(pname, sizeof (pname), "nfs@%s", 807192930Srmacklem ai_nfs->ai_canonname); 808192930Srmacklem build_iovec(iov, iovlen, "principal", pname, 809192930Srmacklem strlen(pname) + 1); 810192930Srmacklem } 81174462Salfred } 8121558Srgrimes 81375394Siedowse ret = TRYRET_LOCALERR; 81480006Siedowse for (;;) { 81575394Siedowse /* 81675394Siedowse * Try each entry returned by getaddrinfo(). Note the 817229778Suqs * occurrence of remote errors by setting `remoteerr'. 81875394Siedowse */ 81975394Siedowse remoteerr = 0; 82075394Siedowse for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 821112580Smdodd if ((ai->ai_family == AF_INET6) && 822112580Smdodd (opflags & OF_NOINET6)) 823112580Smdodd continue; 824112580Smdodd if ((ai->ai_family == AF_INET) && 825112580Smdodd (opflags & OF_NOINET4)) 826112580Smdodd continue; 827183008Srodrigc ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 828183008Srodrigc iovlen); 82975394Siedowse if (ret == TRYRET_SUCCESS) 83075394Siedowse break; 83175394Siedowse if (ret != TRYRET_LOCALERR) 83275394Siedowse remoteerr = 1; 83375394Siedowse if ((opflags & ISBGRND) == 0) 83475394Siedowse fprintf(stderr, "%s\n", errstr); 83575394Siedowse } 83675394Siedowse if (ret == TRYRET_SUCCESS) 83775394Siedowse break; 83874462Salfred 83980006Siedowse /* Exit if all errors were local. */ 84080006Siedowse if (!remoteerr) 84180006Siedowse exit(1); 84280006Siedowse 84374462Salfred /* 84480006Siedowse * If retrycnt == 0, we are to keep retrying forever. 84580006Siedowse * Otherwise decrement it, and exit if it hits zero. 84674462Salfred */ 84780006Siedowse if (retrycnt != 0 && --retrycnt == 0) 84875394Siedowse exit(1); 84975394Siedowse 85075394Siedowse if ((opflags & (BGRND | ISBGRND)) == BGRND) { 85175394Siedowse warnx("Cannot immediately mount %s:%s, backgrounding", 85275394Siedowse hostp, spec); 85375394Siedowse opflags |= ISBGRND; 85475394Siedowse if (daemon(0, 0) != 0) 85575394Siedowse err(1, "daemon"); 85674462Salfred } 85775394Siedowse sleep(60); 85875394Siedowse } 85975394Siedowse freeaddrinfo(ai_nfs); 860183008Srodrigc 861183008Srodrigc build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 862102231Strhodes /* Add mounted file system to PATH_MOUNTTAB */ 86375394Siedowse if (!add_mtab(hostp, spec)) 86475394Siedowse warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 86575394Siedowse return (1); 86675394Siedowse} 86774462Salfred 86875394Siedowse/* 86975394Siedowse * Try to set up the NFS arguments according to the address 87075394Siedowse * family, protocol (and possibly port) specified in `ai'. 87175394Siedowse * 87275394Siedowse * Returns TRYRET_SUCCESS if successful, or: 87375394Siedowse * TRYRET_TIMEOUT The server did not respond. 87475394Siedowse * TRYRET_REMOTEERR The server reported an error. 87575394Siedowse * TRYRET_LOCALERR Local failure. 87675394Siedowse * 87775394Siedowse * In all error cases, *errstr will be set to a statically-allocated string 87875394Siedowse * describing the error. 87975394Siedowse */ 880203461Sdelphijstatic enum tryret 881183008Srodrigcnfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 882183008Srodrigc struct iovec **iov, int *iovlen) 88375394Siedowse{ 88475394Siedowse static char errbuf[256]; 88575394Siedowse struct sockaddr_storage nfs_ss; 88675394Siedowse struct netbuf nfs_nb; 88775394Siedowse struct nfhret nfhret; 88875394Siedowse struct timeval try; 88975394Siedowse struct rpc_err rpcerr; 89075394Siedowse CLIENT *clp; 89175394Siedowse struct netconfig *nconf, *nconf_mnt; 892183008Srodrigc const char *netid, *netid_mnt; 893184588Sdfr char *secname; 894183008Srodrigc int doconnect, nfsvers, mntvers, sotype; 89575394Siedowse enum clnt_stat stat; 89675394Siedowse enum mountmode trymntmode; 89774462Salfred 898212195Skevlo sotype = 0; 89975394Siedowse trymntmode = mountmode; 90075394Siedowse errbuf[0] = '\0'; 90175394Siedowse *errstr = errbuf; 90274462Salfred 903183008Srodrigc if (nfsproto == IPPROTO_TCP) 904183008Srodrigc sotype = SOCK_STREAM; 905183008Srodrigc else if (nfsproto == IPPROTO_UDP) 906183008Srodrigc sotype = SOCK_DGRAM; 907183008Srodrigc 908183008Srodrigc if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 90976530Siedowse snprintf(errbuf, sizeof errbuf, 910183008Srodrigc "af %d sotype %d not supported", ai->ai_family, sotype); 91176530Siedowse return (TRYRET_LOCALERR); 91276530Siedowse } 91376530Siedowse if ((nconf = getnetconf_cached(netid)) == NULL) { 91475394Siedowse snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 91575394Siedowse return (TRYRET_LOCALERR); 91675394Siedowse } 91775394Siedowse /* The RPCPROG_MNT netid may be different. */ 91875394Siedowse if (mnttcp_ok) { 91975401Siedowse netid_mnt = netid; 92075394Siedowse nconf_mnt = nconf; 92175394Siedowse } else { 92276530Siedowse if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 92376530Siedowse == NULL) { 92476530Siedowse snprintf(errbuf, sizeof errbuf, 92576530Siedowse "af %d sotype SOCK_DGRAM not supported", 92676530Siedowse ai->ai_family); 92776530Siedowse return (TRYRET_LOCALERR); 92876530Siedowse } 92976530Siedowse if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 93075401Siedowse snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 93175394Siedowse nc_sperror()); 93275394Siedowse return (TRYRET_LOCALERR); 93375046Sache } 93475394Siedowse } 93575401Siedowse 93675394Siedowsetryagain: 937192930Srmacklem if (trymntmode == V4) { 938192930Srmacklem nfsvers = 4; 939192930Srmacklem } else if (trymntmode == V2) { 94075394Siedowse nfsvers = 2; 94175394Siedowse mntvers = 1; 94275394Siedowse } else { 94375394Siedowse nfsvers = 3; 94475394Siedowse mntvers = 3; 94575394Siedowse } 94675046Sache 94775394Siedowse if (portspec != NULL) { 94875394Siedowse /* `ai' contains the complete nfsd sockaddr. */ 94975394Siedowse nfs_nb.buf = ai->ai_addr; 95075394Siedowse nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 95175394Siedowse } else { 95275394Siedowse /* Ask the remote rpcbind. */ 95375394Siedowse nfs_nb.buf = &nfs_ss; 95475394Siedowse nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 95575394Siedowse 956194880Sdfr if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb, 95775394Siedowse hostp)) { 95875394Siedowse if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 95975394Siedowse trymntmode == ANY) { 96075394Siedowse trymntmode = V2; 96175394Siedowse goto tryagain; 9621558Srgrimes } 96375394Siedowse snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 96475394Siedowse netid, hostp, spec, 96575394Siedowse clnt_spcreateerror("RPCPROG_NFS")); 96675394Siedowse return (returncode(rpc_createerr.cf_stat, 96775394Siedowse &rpc_createerr.cf_error)); 96875046Sache } 96975394Siedowse } 97075394Siedowse 97175394Siedowse /* Check that the server (nfsd) responds on the port we have chosen. */ 972194880Sdfr clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers, 97375394Siedowse 0, 0); 97475394Siedowse if (clp == NULL) { 97575394Siedowse snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 97675394Siedowse hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 97775394Siedowse return (returncode(rpc_createerr.cf_stat, 97875394Siedowse &rpc_createerr.cf_error)); 97975394Siedowse } 980183008Srodrigc if (sotype == SOCK_DGRAM && noconn == 0) { 98178679Siedowse /* 98278679Siedowse * Use connect(), to match what the kernel does. This 98378679Siedowse * catches cases where the server responds from the 98478679Siedowse * wrong source address. 98578679Siedowse */ 98678679Siedowse doconnect = 1; 98778679Siedowse if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 98878679Siedowse clnt_destroy(clp); 98978679Siedowse snprintf(errbuf, sizeof errbuf, 99078679Siedowse "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 99178679Siedowse spec); 99278679Siedowse return (TRYRET_LOCALERR); 99378679Siedowse } 99478679Siedowse } 99578679Siedowse 99675394Siedowse try.tv_sec = 10; 99775394Siedowse try.tv_usec = 0; 998112570Smdodd stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 999192930Srmacklem (xdrproc_t)xdr_void, NULL, try); 100075394Siedowse if (stat != RPC_SUCCESS) { 100175394Siedowse if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 100275394Siedowse clnt_destroy(clp); 100375394Siedowse trymntmode = V2; 100475394Siedowse goto tryagain; 10051558Srgrimes } 100675394Siedowse clnt_geterr(clp, &rpcerr); 100775394Siedowse snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 100875394Siedowse hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 100975394Siedowse clnt_destroy(clp); 101075394Siedowse return (returncode(stat, &rpcerr)); 10111558Srgrimes } 101275394Siedowse clnt_destroy(clp); 101375394Siedowse 1014192930Srmacklem /* 1015192930Srmacklem * For NFSv4, there is no mount protocol. 1016192930Srmacklem */ 1017192930Srmacklem if (trymntmode == V4) { 1018192930Srmacklem /* 1019192930Srmacklem * Store the server address in nfsargsp, making 1020192930Srmacklem * sure to copy any locally allocated structures. 1021192930Srmacklem */ 1022192930Srmacklem addrlen = nfs_nb.len; 1023192930Srmacklem addr = malloc(addrlen); 1024192930Srmacklem if (addr == NULL) 1025192930Srmacklem err(1, "malloc"); 1026192930Srmacklem bcopy(nfs_nb.buf, addr, addrlen); 1027192930Srmacklem 1028192930Srmacklem build_iovec(iov, iovlen, "addr", addr, addrlen); 1029192930Srmacklem secname = sec_num_to_name(secflavor); 1030192930Srmacklem if (secname != NULL) 1031192930Srmacklem build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1032192930Srmacklem build_iovec(iov, iovlen, "nfsv4", NULL, 0); 1033192930Srmacklem build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); 1034192930Srmacklem 1035192930Srmacklem return (TRYRET_SUCCESS); 1036192930Srmacklem } 1037192930Srmacklem 1038194880Sdfr /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ 103975394Siedowse try.tv_sec = 10; 104075394Siedowse try.tv_usec = 0; 1041194880Sdfr clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); 104275394Siedowse if (clp == NULL) { 104375401Siedowse snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 104475394Siedowse hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 104575394Siedowse return (returncode(rpc_createerr.cf_stat, 104675394Siedowse &rpc_createerr.cf_error)); 10471558Srgrimes } 104875394Siedowse clp->cl_auth = authsys_create_default(); 1049184588Sdfr nfhret.auth = secflavor; 105075394Siedowse nfhret.vers = mntvers; 1051194880Sdfr stat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, 1052112570Smdodd (xdrproc_t)xdr_fh, &nfhret, 105375394Siedowse try); 105475394Siedowse auth_destroy(clp->cl_auth); 105575394Siedowse if (stat != RPC_SUCCESS) { 105675394Siedowse if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 105775394Siedowse clnt_destroy(clp); 105875394Siedowse trymntmode = V2; 105975394Siedowse goto tryagain; 106075394Siedowse } 106175394Siedowse clnt_geterr(clp, &rpcerr); 106275401Siedowse snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 106375394Siedowse hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 106475394Siedowse clnt_destroy(clp); 106575394Siedowse return (returncode(stat, &rpcerr)); 10661558Srgrimes } 106775394Siedowse clnt_destroy(clp); 106875394Siedowse 106975394Siedowse if (nfhret.stat != 0) { 107075401Siedowse snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 107175394Siedowse hostp, spec, strerror(nfhret.stat)); 107275394Siedowse return (TRYRET_REMOTEERR); 107375394Siedowse } 107475394Siedowse 107575394Siedowse /* 107675394Siedowse * Store the filehandle and server address in nfsargsp, making 107775394Siedowse * sure to copy any locally allocated structures. 107875394Siedowse */ 1079183008Srodrigc addrlen = nfs_nb.len; 1080183008Srodrigc addr = malloc(addrlen); 1081183008Srodrigc fhsize = nfhret.fhsize; 1082183008Srodrigc fh = malloc(fhsize); 1083183008Srodrigc if (addr == NULL || fh == NULL) 108475394Siedowse err(1, "malloc"); 1085183008Srodrigc bcopy(nfs_nb.buf, addr, addrlen); 1086183008Srodrigc bcopy(nfhret.nfh, fh, fhsize); 108775394Siedowse 1088183008Srodrigc build_iovec(iov, iovlen, "addr", addr, addrlen); 1089183008Srodrigc build_iovec(iov, iovlen, "fh", fh, fhsize); 1090184588Sdfr secname = sec_num_to_name(nfhret.auth); 1091184588Sdfr if (secname) 1092184588Sdfr build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 109375394Siedowse if (nfsvers == 3) 1094183008Srodrigc build_iovec(iov, iovlen, "nfsv3", NULL, 0); 109575394Siedowse 109675394Siedowse return (TRYRET_SUCCESS); 10971558Srgrimes} 10981558Srgrimes 10991558Srgrimes/* 110075394Siedowse * Catagorise a RPC return status and error into an `enum tryret' 110175394Siedowse * return code. 110275394Siedowse */ 1103203461Sdelphijstatic enum tryret 110475394Siedowsereturncode(enum clnt_stat stat, struct rpc_err *rpcerr) 110575394Siedowse{ 110675394Siedowse switch (stat) { 110775394Siedowse case RPC_TIMEDOUT: 110875394Siedowse return (TRYRET_TIMEOUT); 110975394Siedowse case RPC_PMAPFAILURE: 111075394Siedowse case RPC_PROGNOTREGISTERED: 111175394Siedowse case RPC_PROGVERSMISMATCH: 111278679Siedowse /* XXX, these can be local or remote. */ 111378679Siedowse case RPC_CANTSEND: 111478679Siedowse case RPC_CANTRECV: 111575394Siedowse return (TRYRET_REMOTEERR); 111675394Siedowse case RPC_SYSTEMERROR: 111775394Siedowse switch (rpcerr->re_errno) { 111875394Siedowse case ETIMEDOUT: 111975394Siedowse return (TRYRET_TIMEOUT); 112075394Siedowse case ENOMEM: 112175394Siedowse break; 112275394Siedowse default: 112375394Siedowse return (TRYRET_REMOTEERR); 112475394Siedowse } 112575394Siedowse /* FALLTHROUGH */ 112675394Siedowse default: 112775394Siedowse break; 112875394Siedowse } 112975394Siedowse return (TRYRET_LOCALERR); 113075394Siedowse} 113175394Siedowse 113275394Siedowse/* 113376530Siedowse * Look up a netid based on an address family and socket type. 113476530Siedowse * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 113576530Siedowse * 113676530Siedowse * XXX there should be a library function for this. 113776530Siedowse */ 1138203461Sdelphijstatic const char * 1139156925Simpnetidbytype(int af, int sotype) 1140156925Simp{ 114176530Siedowse struct nc_protos *p; 114276530Siedowse 114376530Siedowse for (p = nc_protos; p->netid != NULL; p++) { 114476530Siedowse if (af != p->af || sotype != p->sotype) 114576530Siedowse continue; 114676530Siedowse return (p->netid); 114776530Siedowse } 114876530Siedowse return (NULL); 114976530Siedowse} 115076530Siedowse 115176530Siedowse/* 115276530Siedowse * Look up a netconfig entry based on a netid, and cache the result so 115376530Siedowse * that we don't need to remember to call freenetconfigent(). 115476530Siedowse * 115576530Siedowse * Otherwise it behaves just like getnetconfigent(), so nc_*error() 115676530Siedowse * work on failure. 115776530Siedowse */ 1158203461Sdelphijstatic struct netconfig * 1159156925Simpgetnetconf_cached(const char *netid) 1160156925Simp{ 116176530Siedowse static struct nc_entry { 116276530Siedowse struct netconfig *nconf; 116376530Siedowse struct nc_entry *next; 116476530Siedowse } *head; 116576530Siedowse struct nc_entry *p; 116676530Siedowse struct netconfig *nconf; 116776530Siedowse 116876530Siedowse for (p = head; p != NULL; p = p->next) 116976530Siedowse if (strcmp(netid, p->nconf->nc_netid) == 0) 117076530Siedowse return (p->nconf); 117176530Siedowse 117276530Siedowse if ((nconf = getnetconfigent(netid)) == NULL) 117376530Siedowse return (NULL); 117476530Siedowse if ((p = malloc(sizeof(*p))) == NULL) 117576530Siedowse err(1, "malloc"); 117676530Siedowse p->nconf = nconf; 117776530Siedowse p->next = head; 117876530Siedowse head = p; 117976530Siedowse 118076530Siedowse return (p->nconf); 118176530Siedowse} 118276530Siedowse 118376530Siedowse/* 11841558Srgrimes * xdr routines for mount rpc's 11851558Srgrimes */ 1186203461Sdelphijstatic int 1187156925Simpxdr_dir(XDR *xdrsp, char *dirp) 11881558Srgrimes{ 1189194880Sdfr return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 11901558Srgrimes} 11911558Srgrimes 1192203461Sdelphijstatic int 1193156925Simpxdr_fh(XDR *xdrsp, struct nfhret *np) 11941558Srgrimes{ 119592806Sobrien int i; 11969336Sdfr long auth, authcnt, authfnd = 0; 11979336Sdfr 11989336Sdfr if (!xdr_u_long(xdrsp, &np->stat)) 11991558Srgrimes return (0); 12001558Srgrimes if (np->stat) 12011558Srgrimes return (1); 12029336Sdfr switch (np->vers) { 12039336Sdfr case 1: 1204194880Sdfr np->fhsize = NFS_FHSIZE; 1205194880Sdfr return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE)); 12069336Sdfr case 3: 12079336Sdfr if (!xdr_long(xdrsp, &np->fhsize)) 12089336Sdfr return (0); 1209194880Sdfr if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE) 12109336Sdfr return (0); 12119336Sdfr if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 12129336Sdfr return (0); 12139336Sdfr if (!xdr_long(xdrsp, &authcnt)) 12149336Sdfr return (0); 12159336Sdfr for (i = 0; i < authcnt; i++) { 12169336Sdfr if (!xdr_long(xdrsp, &auth)) 12179336Sdfr return (0); 1218183008Srodrigc if (np->auth == -1) { 1219183008Srodrigc np->auth = auth; 12209336Sdfr authfnd++; 1221183008Srodrigc } else if (auth == np->auth) { 1222183008Srodrigc authfnd++; 1223183008Srodrigc } 12249336Sdfr } 12259336Sdfr /* 12269336Sdfr * Some servers, such as DEC's OSF/1 return a nil authenticator 12279336Sdfr * list to indicate RPCAUTH_UNIX. 12289336Sdfr */ 1229183008Srodrigc if (authcnt == 0 && np->auth == -1) 1230183008Srodrigc np->auth = AUTH_SYS; 1231183008Srodrigc if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 12329336Sdfr np->stat = EAUTH; 12339336Sdfr return (1); 12349336Sdfr }; 12359336Sdfr return (0); 12361558Srgrimes} 12371558Srgrimes 1238203461Sdelphijstatic void 1239203461Sdelphijusage(void) 12401558Srgrimes{ 124137427Scharnier (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1242192578Srwatson"usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1243141611Sru" [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1244141611Sru" [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1245141611Sru" rhost:path node"); 12461558Srgrimes exit(1); 12471558Srgrimes} 1248