mount_nfs.c revision 229778
1246847Sjkim/* 2246847Sjkim * Copyright (c) 1992, 1993, 1994 3246847Sjkim * The Regents of the University of California. All rights reserved. 4246847Sjkim * 5246847Sjkim * This code is derived from software contributed to Berkeley by 6246847Sjkim * Rick Macklem at The University of Guelph. 7246847Sjkim * 8281075Sdim * Redistribution and use in source and binary forms, with or without 9246847Sjkim * modification, are permitted provided that the following conditions 10246847Sjkim * are met: 11246847Sjkim * 1. Redistributions of source code must retain the above copyright 12246847Sjkim * notice, this list of conditions and the following disclaimer. 13246847Sjkim * 2. Redistributions in binary form must reproduce the above copyright 14246847Sjkim * notice, this list of conditions and the following disclaimer in the 15246847Sjkim * documentation and/or other materials provided with the distribution. 16246847Sjkim * 4. Neither the name of the University nor the names of its contributors 17246847Sjkim * may be used to endorse or promote products derived from this software 18246847Sjkim * without specific prior written permission. 19246847Sjkim * 20246847Sjkim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21246847Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22246847Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23246847Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24246847Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25246847Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26246847Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27246847Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28246847Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29246847Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30246847Sjkim * SUCH DAMAGE. 31246847Sjkim */ 32246847Sjkim 33246847Sjkim#if 0 34246847Sjkim#ifndef lint 35246847Sjkimstatic const char copyright[] = 36246847Sjkim"@(#) Copyright (c) 1992, 1993, 1994\n\ 37246847Sjkim The Regents of the University of California. All rights reserved.\n"; 38246847Sjkim#endif /* not lint */ 39246847Sjkim 40246847Sjkim#ifndef lint 41246847Sjkimstatic char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 42246847Sjkim#endif /* not lint */ 43246847Sjkim#endif 44246849Sjkim#include <sys/cdefs.h> 45246847Sjkim__FBSDID("$FreeBSD: head/sbin/mount_nfs/mount_nfs.c 229778 2012-01-07 16:09:33Z uqs $"); 46246849Sjkim 47246847Sjkim#include <sys/param.h> 48246847Sjkim#include <sys/linker.h> 49246847Sjkim#include <sys/module.h> 50246847Sjkim#include <sys/mount.h> 51246847Sjkim#include <sys/socket.h> 52246847Sjkim#include <sys/stat.h> 53246847Sjkim#include <sys/syslog.h> 54246847Sjkim#include <sys/uio.h> 55246847Sjkim 56246847Sjkim#include <rpc/rpc.h> 57249663Sjkim#include <rpc/pmap_clnt.h> 58249663Sjkim#include <rpc/pmap_prot.h> 59249663Sjkim#include <rpcsvc/nfs_prot.h> 60249663Sjkim#include <rpcsvc/mount.h> 61249663Sjkim 62249663Sjkim#include <nfsclient/nfs.h> 63246847Sjkim 64246847Sjkim#include <arpa/inet.h> 65246847Sjkim 66246847Sjkim#include <ctype.h> 67246847Sjkim#include <err.h> 68246847Sjkim#include <errno.h> 69246847Sjkim#include <fcntl.h> 70246847Sjkim#include <netdb.h> 71246847Sjkim#include <stdio.h> 72246847Sjkim#include <stdlib.h> 73246847Sjkim#include <string.h> 74246847Sjkim#include <strings.h> 75246847Sjkim#include <sysexits.h> 76246847Sjkim#include <unistd.h> 77246847Sjkim 78246847Sjkim#include "mntopts.h" 79246847Sjkim#include "mounttab.h" 80246847Sjkim 81246847Sjkim/* Table for af,sotype -> netid conversions. */ 82246847Sjkimstruct nc_protos { 83246847Sjkim const char *netid; 84246847Sjkim int af; 85246847Sjkim int sotype; 86246847Sjkim} nc_protos[] = { 87246847Sjkim {"udp", AF_INET, SOCK_DGRAM}, 88246847Sjkim {"tcp", AF_INET, SOCK_STREAM}, 89246847Sjkim {"udp6", AF_INET6, SOCK_DGRAM}, 90246847Sjkim {"tcp6", AF_INET6, SOCK_STREAM}, 91246847Sjkim {NULL, 0, 0} 92246847Sjkim}; 93246847Sjkim 94246847Sjkimstruct nfhret { 95246847Sjkim u_long stat; 96249663Sjkim long vers; 97249663Sjkim long auth; 98249663Sjkim long fhsize; 99246847Sjkim u_char nfh[NFS3_FHSIZE]; 100246847Sjkim}; 101246847Sjkim#define BGRND 1 102246847Sjkim#define ISBGRND 2 103246847Sjkim#define OF_NOINET4 4 104246847Sjkim#define OF_NOINET6 8 105246847Sjkimint retrycnt = -1; 106246847Sjkimint opflags = 0; 107246847Sjkimint nfsproto = IPPROTO_TCP; 108246847Sjkimint mnttcp_ok = 1; 109246847Sjkimint noconn = 0; 110246847Sjkimchar *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 111246847Sjkimstruct sockaddr *addr; 112246847Sjkimint addrlen = 0; 113246847Sjkimu_char *fh = NULL; 114246847Sjkimint fhsize = 0; 115246847Sjkimint secflavor = -1; 116246847Sjkimint got_principal = 0; 117246847Sjkim 118246847Sjkimenum mountmode { 119246847Sjkim ANY, 120246847Sjkim V2, 121246847Sjkim V3, 122246847Sjkim V4 123246847Sjkim} mountmode = ANY; 124246847Sjkim 125246847Sjkim/* Return codes for nfs_tryproto. */ 126246847Sjkimenum tryret { 127246847Sjkim TRYRET_SUCCESS, 128246847Sjkim TRYRET_TIMEOUT, /* No response received. */ 129246847Sjkim TRYRET_REMOTEERR, /* Error received from remote server. */ 130249112Sjkim TRYRET_LOCALERR /* Local failure. */ 131249112Sjkim}; 132249112Sjkim 133249112Sjkimstatic int fallback_mount(struct iovec *iov, int iovlen, int mntflags); 134249112Sjkimstatic int sec_name_to_num(char *sec); 135246847Sjkimstatic char *sec_num_to_name(int num); 136246847Sjkimstatic int getnfsargs(char *, struct iovec **iov, int *iovlen); 137246847Sjkim/* void set_rpc_maxgrouplist(int); */ 138249112Sjkimstatic struct netconfig *getnetconf_cached(const char *netid); 139246847Sjkimstatic const char *netidbytype(int af, int sotype); 140249112Sjkimstatic void usage(void) __dead2; 141249112Sjkimstatic int xdr_dir(XDR *, char *); 142249112Sjkimstatic int xdr_fh(XDR *, struct nfhret *); 143249112Sjkimstatic enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, 144249112Sjkim char **errstr, struct iovec **iov, int *iovlen); 145246847Sjkimstatic enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 146249112Sjkim 147249112Sjkimint 148249112Sjkimmain(int argc, char *argv[]) 149249112Sjkim{ 150249112Sjkim int c; 151249112Sjkim struct iovec *iov; 152249112Sjkim int mntflags, num, iovlen; 153249112Sjkim int osversion; 154284460Sjkim char *name, *p, *spec, *fstype; 155249112Sjkim char mntpath[MAXPATHLEN], errmsg[255]; 156249112Sjkim char hostname[MAXHOSTNAMELEN + 1], *gssname, gssn[MAXHOSTNAMELEN + 50]; 157249112Sjkim 158246847Sjkim mntflags = 0; 159249112Sjkim iov = NULL; 160246847Sjkim iovlen = 0; 161246847Sjkim memset(errmsg, 0, sizeof(errmsg)); 162246847Sjkim gssname = NULL; 163246847Sjkim 164246847Sjkim fstype = strrchr(argv[0], '_'); 165246847Sjkim if (fstype == NULL) 166246847Sjkim errx(EX_USAGE, "argv[0] must end in _fstype"); 167246847Sjkim 168246847Sjkim ++fstype; 169246847Sjkim 170246847Sjkim while ((c = getopt(argc, argv, 171246847Sjkim "23a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1) 172246847Sjkim switch (c) { 173281075Sdim case '2': 174246847Sjkim mountmode = V2; 175246847Sjkim break; 176246847Sjkim case '3': 177246847Sjkim mountmode = V3; 178246847Sjkim break; 179246847Sjkim case 'a': 180246847Sjkim printf("-a deprecated, use -o readahead=<value>\n"); 181246847Sjkim build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1); 182246847Sjkim break; 183246847Sjkim case 'b': 184246847Sjkim opflags |= BGRND; 185246847Sjkim break; 186246847Sjkim case 'c': 187246847Sjkim printf("-c deprecated, use -o noconn\n"); 188246847Sjkim build_iovec(&iov, &iovlen, "noconn", NULL, 0); 189246847Sjkim noconn = 1; 190246847Sjkim break; 191246847Sjkim case 'D': 192246847Sjkim printf("-D deprecated, use -o deadthresh=<value>\n"); 193246847Sjkim build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1); 194246847Sjkim break; 195246847Sjkim case 'd': 196246847Sjkim printf("-d deprecated, use -o dumbtimer"); 197246847Sjkim build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0); 198281075Sdim break; 199249663Sjkim case 'g': 200246847Sjkim printf("-g deprecated, use -o maxgroups"); 201246847Sjkim num = strtol(optarg, &p, 10); 202246847Sjkim if (*p || num <= 0) 203246847Sjkim errx(1, "illegal -g value -- %s", optarg); 204246847Sjkim //set_rpc_maxgrouplist(num); 205246847Sjkim build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1); 206246847Sjkim break; 207246847Sjkim case 'I': 208246847Sjkim printf("-I deprecated, use -o readdirsize=<value>\n"); 209246847Sjkim build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1); 210246847Sjkim break; 211281075Sdim case 'i': 212249663Sjkim printf("-i deprecated, use -o intr\n"); 213249663Sjkim build_iovec(&iov, &iovlen, "intr", NULL, 0); 214246847Sjkim break; 215246847Sjkim case 'L': 216246847Sjkim printf("-L deprecated, use -o nolockd\n"); 217246847Sjkim build_iovec(&iov, &iovlen, "nolockd", NULL, 0); 218246847Sjkim break; 219246847Sjkim case 'l': 220246847Sjkim printf("-l deprecated, -o rdirplus\n"); 221246847Sjkim build_iovec(&iov, &iovlen, "rdirplus", NULL, 0); 222246847Sjkim break; 223246847Sjkim case 'N': 224246847Sjkim printf("-N deprecated, do not specify -o resvport\n"); 225246847Sjkim break; 226246847Sjkim case 'o': { 227246847Sjkim int pass_flag_to_nmount; 228246847Sjkim char *opt = optarg; 229246847Sjkim while (opt) { 230246847Sjkim char *pval = NULL; 231246847Sjkim char *pnextopt = NULL; 232246847Sjkim char *val = ""; 233246847Sjkim pass_flag_to_nmount = 1; 234246847Sjkim pnextopt = strchr(opt, ','); 235246847Sjkim if (pnextopt != NULL) { 236246847Sjkim *pnextopt = '\0'; 237246847Sjkim pnextopt++; 238246847Sjkim } 239246847Sjkim pval = strchr(opt, '='); 240246847Sjkim if (pval != NULL) { 241246847Sjkim *pval = '\0'; 242246847Sjkim val = pval + 1; 243246847Sjkim } 244246847Sjkim if (strcmp(opt, "bg") == 0) { 245246847Sjkim opflags |= BGRND; 246246847Sjkim pass_flag_to_nmount=0; 247246847Sjkim } else if (strcmp(opt, "fg") == 0) { 248246847Sjkim /* same as not specifying -o bg */ 249246847Sjkim pass_flag_to_nmount=0; 250246847Sjkim } else if (strcmp(opt, "gssname") == 0) { 251246847Sjkim pass_flag_to_nmount = 0; 252246847Sjkim gssname = val; 253246847Sjkim } else if (strcmp(opt, "mntudp") == 0) { 254246847Sjkim mnttcp_ok = 0; 255281075Sdim nfsproto = IPPROTO_UDP; 256246847Sjkim } else if (strcmp(opt, "udp") == 0) { 257246847Sjkim nfsproto = IPPROTO_UDP; 258246847Sjkim } else if (strcmp(opt, "tcp") == 0) { 259246847Sjkim nfsproto = IPPROTO_TCP; 260246847Sjkim } else if (strcmp(opt, "noinet4") == 0) { 261246847Sjkim pass_flag_to_nmount=0; 262246847Sjkim opflags |= OF_NOINET4; 263281075Sdim } else if (strcmp(opt, "noinet6") == 0) { 264246847Sjkim pass_flag_to_nmount=0; 265246847Sjkim opflags |= OF_NOINET6; 266246847Sjkim } else if (strcmp(opt, "noconn") == 0) { 267246847Sjkim noconn = 1; 268246847Sjkim } else if (strcmp(opt, "nfsv2") == 0) { 269246847Sjkim pass_flag_to_nmount=0; 270246847Sjkim mountmode = V2; 271246847Sjkim } else if (strcmp(opt, "nfsv3") == 0) { 272246847Sjkim mountmode = V3; 273249663Sjkim } else if (strcmp(opt, "nfsv4") == 0) { 274249663Sjkim pass_flag_to_nmount=0; 275246847Sjkim mountmode = V4; 276246847Sjkim fstype = "nfs"; 277246847Sjkim nfsproto = IPPROTO_TCP; 278246847Sjkim if (portspec == NULL) 279246847Sjkim portspec = "2049"; 280246847Sjkim } else if (strcmp(opt, "port") == 0) { 281246847Sjkim pass_flag_to_nmount=0; 282246847Sjkim asprintf(&portspec, "%d", 283246847Sjkim atoi(val)); 284246847Sjkim if (portspec == NULL) 285246847Sjkim err(1, "asprintf"); 286246847Sjkim } else if (strcmp(opt, "principal") == 0) { 287281075Sdim got_principal = 1; 288246847Sjkim } else if (strcmp(opt, "sec") == 0) { 289246847Sjkim /* 290246847Sjkim * Don't add this option to 291246847Sjkim * the iovec yet - we will 292246847Sjkim * negotiate which sec flavor 293281075Sdim * to use with the remote 294281075Sdim * mountd. 295281075Sdim */ 296281075Sdim pass_flag_to_nmount=0; 297281075Sdim secflavor = sec_name_to_num(val); 298281075Sdim if (secflavor < 0) { 299281075Sdim errx(1, 300281075Sdim "illegal sec value -- %s", 301281075Sdim val); 302281075Sdim } 303281075Sdim } else if (strcmp(opt, "retrycnt") == 0) { 304281075Sdim pass_flag_to_nmount=0; 305281075Sdim num = strtol(val, &p, 10); 306281075Sdim if (*p || num < 0) 307281075Sdim errx(1, "illegal retrycnt value -- %s", val); 308281075Sdim retrycnt = num; 309281075Sdim } else if (strcmp(opt, "maxgroups") == 0) { 310281075Sdim num = strtol(val, &p, 10); 311281075Sdim if (*p || num <= 0) 312281075Sdim errx(1, "illegal maxgroups value -- %s", val); 313281075Sdim //set_rpc_maxgrouplist(num); 314281075Sdim } 315281075Sdim if (pass_flag_to_nmount) 316281075Sdim build_iovec(&iov, &iovlen, opt, val, 317281075Sdim strlen(val) + 1); 318281075Sdim opt = pnextopt; 319281075Sdim } 320281075Sdim } 321281075Sdim break; 322281075Sdim case 'P': 323281075Sdim /* obsolete for -o noresvport now default */ 324281075Sdim printf("-P deprecated, use -o noresvport\n"); 325281075Sdim build_iovec(&iov, &iovlen, "noresvport", NULL, 0); 326281075Sdim break; 327281075Sdim case 'R': 328284460Sjkim printf("-R deprecated, use -o retrycnt=<retrycnt>\n"); 329284460Sjkim num = strtol(optarg, &p, 10); 330284460Sjkim if (*p || num < 0) 331284460Sjkim errx(1, "illegal -R value -- %s", optarg); 332284460Sjkim retrycnt = num; 333284460Sjkim break; 334284460Sjkim case 'r': 335284460Sjkim printf("-r deprecated, use -o rsize=<rsize>\n"); 336284460Sjkim build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1); 337284460Sjkim break; 338284460Sjkim case 's': 339284460Sjkim printf("-s deprecated, use -o soft\n"); 340284460Sjkim build_iovec(&iov, &iovlen, "soft", NULL, 0); 341284460Sjkim break; 342284460Sjkim case 'T': 343284460Sjkim nfsproto = IPPROTO_TCP; 344284460Sjkim printf("-T deprecated, use -o tcp\n"); 345284460Sjkim break; 346284460Sjkim case 't': 347284460Sjkim printf("-t deprecated, use -o timeout=<value>\n"); 348284460Sjkim build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1); 349284460Sjkim break; 350284460Sjkim case 'w': 351284460Sjkim printf("-w deprecated, use -o wsize=<value>\n"); 352284460Sjkim build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1); 353284460Sjkim break; 354246847Sjkim case 'x': 355246847Sjkim printf("-x deprecated, use -o retrans=<value>\n"); 356246847Sjkim build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1); 357246847Sjkim break; 358246847Sjkim case 'U': 359246847Sjkim printf("-U deprecated, use -o mntudp\n"); 360246847Sjkim mnttcp_ok = 0; 361281075Sdim nfsproto = IPPROTO_UDP; 362246847Sjkim build_iovec(&iov, &iovlen, "mntudp", NULL, 0); 363246847Sjkim break; 364281075Sdim default: 365246847Sjkim usage(); 366246847Sjkim break; 367246847Sjkim } 368246847Sjkim argc -= optind; 369246847Sjkim argv += optind; 370246847Sjkim 371246847Sjkim if (argc != 2) { 372246847Sjkim usage(); 373246847Sjkim /* NOTREACHED */ 374246847Sjkim } 375246847Sjkim 376246847Sjkim spec = *argv++; 377246847Sjkim name = *argv; 378246847Sjkim 379246847Sjkim if (retrycnt == -1) 380246847Sjkim /* The default is to keep retrying forever. */ 381246847Sjkim retrycnt = 0; 382246847Sjkim 383246847Sjkim /* 384246847Sjkim * If the fstye is "oldnfs", run the old NFS client unless the 385246847Sjkim * "nfsv4" option was specified. 386249663Sjkim */ 387249663Sjkim if (strcmp(fstype, "nfs") == 0) { 388249663Sjkim if (modfind("nfscl") < 0) { 389249663Sjkim /* Not present in kernel, try loading it */ 390249663Sjkim if (kldload("nfscl") < 0 || 391249663Sjkim modfind("nfscl") < 0) 392246847Sjkim errx(1, "nfscl is not available"); 393246847Sjkim } 394246847Sjkim } 395246847Sjkim 396246847Sjkim /* 397246847Sjkim * Add the fqdn to the gssname, as required. 398246847Sjkim */ 399246847Sjkim if (gssname != NULL) { 400246847Sjkim if (strchr(gssname, '@') == NULL && 401246847Sjkim gethostname(hostname, MAXHOSTNAMELEN) == 0) { 402246847Sjkim snprintf(gssn, sizeof (gssn), "%s@%s", gssname, 403246847Sjkim hostname); 404246847Sjkim gssname = gssn; 405246847Sjkim } 406246847Sjkim build_iovec(&iov, &iovlen, "gssname", gssname, 407246847Sjkim strlen(gssname) + 1); 408246847Sjkim } 409246847Sjkim 410246847Sjkim if (!getnfsargs(spec, &iov, &iovlen)) 411246847Sjkim exit(1); 412246847Sjkim 413246847Sjkim /* resolve the mountpoint with realpath(3) */ 414246847Sjkim (void)checkpath(name, mntpath); 415246847Sjkim 416246847Sjkim build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); 417246847Sjkim build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); 418246847Sjkim build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 419246847Sjkim 420246847Sjkim /* 421246847Sjkim * XXX: 422246847Sjkim * Backwards compatibility routines for older kernels. 423246847Sjkim * Remove this and fallback_mount() code when we do not need to support 424246847Sjkim * NFS mounts against older kernels which still need 425246847Sjkim * struct nfs_args to be passed in via nmount(). 426246847Sjkim */ 427246847Sjkim osversion = getosreldate(); 428246847Sjkim if (osversion >= 702100) { 429246847Sjkim if (nmount(iov, iovlen, mntflags)) 430246847Sjkim err(1, "%s, %s", mntpath, errmsg); 431246847Sjkim } else { 432246847Sjkim if (fallback_mount(iov, iovlen, mntflags)) 433246847Sjkim err(1, "%s, %s", mntpath, errmsg); 434246847Sjkim } 435246847Sjkim 436246847Sjkim exit(0); 437246847Sjkim} 438246847Sjkim 439246847Sjkimstatic int 440246847Sjkimfindopt(struct iovec *iov, int iovlen, const char *name, 441246847Sjkim char **valuep, int *lenp) 442246847Sjkim{ 443246847Sjkim int i; 444246847Sjkim 445246847Sjkim for (i = 0; i < iovlen/2; i++, iov += 2) { 446246847Sjkim if (strcmp(name, iov[0].iov_base) == 0) { 447246847Sjkim if (valuep) 448246847Sjkim *valuep = iov[1].iov_base; 449246847Sjkim if (lenp) 450246847Sjkim *lenp = iov[1].iov_len; 451246847Sjkim return (0); 452246847Sjkim } 453246847Sjkim } 454246847Sjkim return (ENOENT); 455246847Sjkim} 456246847Sjkim 457284460Sjkimstatic void 458246847Sjkimcopyopt(struct iovec **newiov, int *newiovlen, 459246847Sjkim struct iovec *iov, int iovlen, const char *name) 460246847Sjkim{ 461246847Sjkim char *value; 462246847Sjkim int len; 463246847Sjkim 464246847Sjkim if (findopt(iov, iovlen, name, &value, &len) == 0) 465246847Sjkim build_iovec(newiov, newiovlen, name, value, len); 466246847Sjkim} 467246847Sjkim 468246847Sjkim/* 469246847Sjkim * XXX: This function is provided for backwards 470246847Sjkim * compatibility with older kernels which did not support 471246847Sjkim * passing NFS mount options to nmount() as individual 472246847Sjkim * parameters. It should be eventually be removed. 473246847Sjkim */ 474246847Sjkimstatic int 475246847Sjkimfallback_mount(struct iovec *iov, int iovlen, int mntflags) 476246847Sjkim{ 477246847Sjkim struct nfs_args args = { 478246847Sjkim .version = NFS_ARGSVERSION, 479246847Sjkim .addr = NULL, 480246847Sjkim .addrlen = sizeof (struct sockaddr_in), 481246847Sjkim .sotype = SOCK_STREAM, 482246847Sjkim .proto = 0, 483246847Sjkim .fh = NULL, 484246847Sjkim .fhsize = 0, 485246847Sjkim .flags = NFSMNT_RESVPORT, 486246847Sjkim .wsize = NFS_WSIZE, 487246847Sjkim .rsize = NFS_RSIZE, 488246847Sjkim .readdirsize = NFS_READDIRSIZE, 489246847Sjkim .timeo = 10, 490246847Sjkim .retrans = NFS_RETRANS, 491246847Sjkim .maxgrouplist = NFS_MAXGRPS, 492246847Sjkim .readahead = NFS_DEFRAHEAD, 493246847Sjkim .wcommitsize = 0, /* was: NQ_DEFLEASE */ 494246847Sjkim .deadthresh = NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 495246847Sjkim .hostname = NULL, 496246847Sjkim /* args version 4 */ 497246847Sjkim .acregmin = NFS_MINATTRTIMO, 498246847Sjkim .acregmax = NFS_MAXATTRTIMO, 499246847Sjkim .acdirmin = NFS_MINDIRATTRTIMO, 500246847Sjkim .acdirmax = NFS_MAXDIRATTRTIMO, 501246847Sjkim }; 502246847Sjkim int ret; 503246847Sjkim char *opt; 504284460Sjkim struct iovec *newiov; 505284460Sjkim int newiovlen; 506284460Sjkim 507284460Sjkim if (findopt(iov, iovlen, "dumbtimer", NULL, NULL) == 0) 508284460Sjkim args.flags |= NFSMNT_DUMBTIMR; 509284460Sjkim if (findopt(iov, iovlen, "noconn", NULL, NULL) == 0) 510246847Sjkim args.flags |= NFSMNT_NOCONN; 511246847Sjkim if (findopt(iov, iovlen, "conn", NULL, NULL) == 0) 512246847Sjkim args.flags |= NFSMNT_NOCONN; 513246847Sjkim if (findopt(iov, iovlen, "nolockd", NULL, NULL) == 0) 514246847Sjkim args.flags |= NFSMNT_NOLOCKD; 515246847Sjkim if (findopt(iov, iovlen, "lockd", NULL, NULL) == 0) 516246847Sjkim args.flags &= ~NFSMNT_NOLOCKD; 517246847Sjkim if (findopt(iov, iovlen, "intr", NULL, NULL) == 0) 518246847Sjkim args.flags |= NFSMNT_INT; 519246847Sjkim if (findopt(iov, iovlen, "rdirplus", NULL, NULL) == 0) 520246847Sjkim args.flags |= NFSMNT_RDIRPLUS; 521246847Sjkim if (findopt(iov, iovlen, "resvport", NULL, NULL) == 0) 522246847Sjkim args.flags |= NFSMNT_RESVPORT; 523246847Sjkim if (findopt(iov, iovlen, "noresvport", NULL, NULL) == 0) 524246847Sjkim args.flags &= ~NFSMNT_RESVPORT; 525246847Sjkim if (findopt(iov, iovlen, "soft", NULL, NULL) == 0) 526246847Sjkim args.flags |= NFSMNT_SOFT; 527246847Sjkim if (findopt(iov, iovlen, "hard", NULL, NULL) == 0) 528246847Sjkim args.flags &= ~NFSMNT_SOFT; 529246847Sjkim if (findopt(iov, iovlen, "mntudp", NULL, NULL) == 0) 530246847Sjkim args.sotype = SOCK_DGRAM; 531246847Sjkim if (findopt(iov, iovlen, "udp", NULL, NULL) == 0) 532246847Sjkim args.sotype = SOCK_DGRAM; 533246847Sjkim if (findopt(iov, iovlen, "tcp", NULL, NULL) == 0) 534246847Sjkim args.sotype = SOCK_STREAM; 535246847Sjkim if (findopt(iov, iovlen, "nfsv3", NULL, NULL) == 0) 536281075Sdim args.flags |= NFSMNT_NFSV3; 537281075Sdim if (findopt(iov, iovlen, "readdirsize", &opt, NULL) == 0) { 538281075Sdim if (opt == NULL) { 539281075Sdim errx(1, "illegal readdirsize"); 540281075Sdim } 541281075Sdim ret = sscanf(opt, "%d", &args.readdirsize); 542246847Sjkim if (ret != 1 || args.readdirsize <= 0) { 543246847Sjkim errx(1, "illegal readdirsize: %s", opt); 544246847Sjkim } 545246847Sjkim args.flags |= NFSMNT_READDIRSIZE; 546246847Sjkim } 547246847Sjkim if (findopt(iov, iovlen, "readahead", &opt, NULL) == 0) { 548246847Sjkim if (opt == NULL) { 549246847Sjkim errx(1, "illegal readahead"); 550246847Sjkim } 551246847Sjkim ret = sscanf(opt, "%d", &args.readahead); 552246847Sjkim if (ret != 1 || args.readahead <= 0) { 553246847Sjkim errx(1, "illegal readahead: %s", opt); 554246847Sjkim } 555246847Sjkim args.flags |= NFSMNT_READAHEAD; 556246847Sjkim } 557246847Sjkim if (findopt(iov, iovlen, "wsize", &opt, NULL) == 0) { 558246847Sjkim if (opt == NULL) { 559246847Sjkim errx(1, "illegal wsize"); 560246847Sjkim } 561246847Sjkim ret = sscanf(opt, "%d", &args.wsize); 562246847Sjkim if (ret != 1 || args.wsize <= 0) { 563246847Sjkim errx(1, "illegal wsize: %s", opt); 564246847Sjkim } 565246847Sjkim args.flags |= NFSMNT_WSIZE; 566246847Sjkim } 567284460Sjkim if (findopt(iov, iovlen, "rsize", &opt, NULL) == 0) { 568284460Sjkim if (opt == NULL) { 569284460Sjkim errx(1, "illegal rsize"); 570284460Sjkim } 571284460Sjkim ret = sscanf(opt, "%d", &args.rsize); 572284460Sjkim if (ret != 1 || args.rsize <= 0) { 573284460Sjkim errx(1, "illegal wsize: %s", opt); 574284460Sjkim } 575284460Sjkim args.flags |= NFSMNT_RSIZE; 576284460Sjkim } 577284460Sjkim if (findopt(iov, iovlen, "retrans", &opt, NULL) == 0) { 578284460Sjkim if (opt == NULL) { 579284460Sjkim errx(1, "illegal retrans"); 580284460Sjkim } 581284460Sjkim ret = sscanf(opt, "%d", &args.retrans); 582284460Sjkim if (ret != 1 || args.retrans <= 0) { 583284460Sjkim errx(1, "illegal retrans: %s", opt); 584284460Sjkim } 585284460Sjkim args.flags |= NFSMNT_RETRANS; 586246847Sjkim } 587246847Sjkim if (findopt(iov, iovlen, "acregmin", &opt, NULL) == 0) { 588281075Sdim ret = sscanf(opt, "%d", &args.acregmin); 589246847Sjkim if (ret != 1 || args.acregmin < 0) { 590246847Sjkim errx(1, "illegal acregmin: %s", opt); 591246847Sjkim } 592246847Sjkim args.flags |= NFSMNT_ACREGMIN; 593246847Sjkim } 594246847Sjkim if (findopt(iov, iovlen, "acregmax", &opt, NULL) == 0) { 595246847Sjkim ret = sscanf(opt, "%d", &args.acregmax); 596246847Sjkim if (ret != 1 || args.acregmax < 0) { 597281075Sdim errx(1, "illegal acregmax: %s", opt); 598281075Sdim } 599281075Sdim args.flags |= NFSMNT_ACREGMAX; 600281075Sdim } 601281075Sdim if (findopt(iov, iovlen, "acdirmin", &opt, NULL) == 0) { 602281075Sdim ret = sscanf(opt, "%d", &args.acdirmin); 603246847Sjkim if (ret != 1 || args.acdirmin < 0) { 604246847Sjkim errx(1, "illegal acdirmin: %s", opt); 605246847Sjkim } 606246847Sjkim args.flags |= NFSMNT_ACDIRMIN; 607246847Sjkim } 608246847Sjkim if (findopt(iov, iovlen, "acdirmax", &opt, NULL) == 0) { 609246847Sjkim ret = sscanf(opt, "%d", &args.acdirmax); 610246847Sjkim if (ret != 1 || args.acdirmax < 0) { 611246847Sjkim errx(1, "illegal acdirmax: %s", opt); 612246847Sjkim } 613246847Sjkim args.flags |= NFSMNT_ACDIRMAX; 614246847Sjkim } 615246847Sjkim if (findopt(iov, iovlen, "wcommitsize", &opt, NULL) == 0) { 616246847Sjkim ret = sscanf(opt, "%d", &args.wcommitsize); 617281075Sdim if (ret != 1 || args.wcommitsize < 0) { 618246847Sjkim errx(1, "illegal wcommitsize: %s", opt); 619246847Sjkim } 620246847Sjkim args.flags |= NFSMNT_WCOMMITSIZE; 621246847Sjkim } 622246847Sjkim if (findopt(iov, iovlen, "deadthresh", &opt, NULL) == 0) { 623246847Sjkim ret = sscanf(opt, "%d", &args.deadthresh); 624246847Sjkim if (ret != 1 || args.deadthresh <= 0) { 625246847Sjkim errx(1, "illegal deadthresh: %s", opt); 626246847Sjkim } 627281075Sdim args.flags |= NFSMNT_DEADTHRESH; 628246847Sjkim } 629246847Sjkim if (findopt(iov, iovlen, "timeout", &opt, NULL) == 0) { 630246847Sjkim ret = sscanf(opt, "%d", &args.timeo); 631246847Sjkim if (ret != 1 || args.timeo <= 0) { 632246847Sjkim errx(1, "illegal timeout: %s", opt); 633246847Sjkim } 634246847Sjkim args.flags |= NFSMNT_TIMEO; 635246847Sjkim } 636246847Sjkim if (findopt(iov, iovlen, "maxgroups", &opt, NULL) == 0) { 637246847Sjkim ret = sscanf(opt, "%d", &args.maxgrouplist); 638246847Sjkim if (ret != 1 || args.timeo <= 0) { 639246847Sjkim errx(1, "illegal maxgroups: %s", opt); 640246847Sjkim } 641246847Sjkim args.flags |= NFSMNT_MAXGRPS; 642246847Sjkim } 643246847Sjkim if (findopt(iov, iovlen, "addr", &opt, 644246847Sjkim &args.addrlen) == 0) { 645246847Sjkim args.addr = (struct sockaddr *) opt; 646246847Sjkim } 647246847Sjkim if (findopt(iov, iovlen, "fh", &opt, &args.fhsize) == 0) { 648246847Sjkim args.fh = opt; 649246847Sjkim } 650246847Sjkim if (findopt(iov, iovlen, "hostname", &args.hostname, 651246847Sjkim NULL) == 0) { 652246847Sjkim } 653246847Sjkim if (args.hostname == NULL) { 654246847Sjkim errx(1, "Invalid hostname"); 655246847Sjkim } 656246847Sjkim 657246847Sjkim newiov = NULL; 658246847Sjkim newiovlen = 0; 659246847Sjkim 660246847Sjkim build_iovec(&newiov, &newiovlen, "nfs_args", &args, sizeof(args)); 661246847Sjkim copyopt(&newiov, &newiovlen, iov, iovlen, "fstype"); 662246847Sjkim copyopt(&newiov, &newiovlen, iov, iovlen, "fspath"); 663246847Sjkim copyopt(&newiov, &newiovlen, iov, iovlen, "errmsg"); 664246847Sjkim 665246847Sjkim return nmount(newiov, newiovlen, mntflags); 666246847Sjkim} 667246847Sjkim 668246847Sjkimstatic int 669246847Sjkimsec_name_to_num(char *sec) 670246847Sjkim{ 671246847Sjkim if (!strcmp(sec, "krb5")) 672246847Sjkim return (RPCSEC_GSS_KRB5); 673246847Sjkim if (!strcmp(sec, "krb5i")) 674246847Sjkim return (RPCSEC_GSS_KRB5I); 675246847Sjkim if (!strcmp(sec, "krb5p")) 676281075Sdim return (RPCSEC_GSS_KRB5P); 677246847Sjkim if (!strcmp(sec, "sys")) 678246847Sjkim return (AUTH_SYS); 679246847Sjkim return (-1); 680246847Sjkim} 681246847Sjkim 682246847Sjkimstatic char * 683246847Sjkimsec_num_to_name(int flavor) 684246847Sjkim{ 685246847Sjkim switch (flavor) { 686246847Sjkim case RPCSEC_GSS_KRB5: 687246847Sjkim return ("krb5"); 688246847Sjkim case RPCSEC_GSS_KRB5I: 689246847Sjkim return ("krb5i"); 690246847Sjkim case RPCSEC_GSS_KRB5P: 691246847Sjkim return ("krb5p"); 692246847Sjkim case AUTH_SYS: 693246847Sjkim return ("sys"); 694246847Sjkim } 695246847Sjkim return (NULL); 696246847Sjkim} 697246847Sjkim 698246847Sjkimstatic int 699246847Sjkimgetnfsargs(char *spec, struct iovec **iov, int *iovlen) 700246847Sjkim{ 701246847Sjkim struct addrinfo hints, *ai_nfs, *ai; 702246847Sjkim enum tryret ret; 703246847Sjkim int ecode, speclen, remoteerr, offset, have_bracket = 0; 704246847Sjkim char *hostp, *delimp, *errstr; 705246847Sjkim size_t len; 706246847Sjkim static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; 707246847Sjkim 708246847Sjkim if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 709246847Sjkim *(delimp + 1) == ':') { 710246847Sjkim hostp = spec + 1; 711246847Sjkim spec = delimp + 2; 712246847Sjkim have_bracket = 1; 713246847Sjkim } else if ((delimp = strrchr(spec, ':')) != NULL) { 714246847Sjkim hostp = spec; 715246847Sjkim spec = delimp + 1; 716246847Sjkim } else if ((delimp = strrchr(spec, '@')) != NULL) { 717246847Sjkim warnx("path@server syntax is deprecated, use server:path"); 718246847Sjkim hostp = delimp + 1; 719246847Sjkim } else { 720246847Sjkim warnx("no <host>:<dirpath> nfs-name"); 721246847Sjkim return (0); 722246847Sjkim } 723246847Sjkim *delimp = '\0'; 724246847Sjkim 725246847Sjkim /* 726246847Sjkim * If there has been a trailing slash at mounttime it seems 727246847Sjkim * that some mountd implementations fail to remove the mount 728246847Sjkim * entries from their mountlist while unmounting. 729246847Sjkim */ 730246847Sjkim for (speclen = strlen(spec); 731246847Sjkim speclen > 1 && spec[speclen - 1] == '/'; 732246847Sjkim speclen--) 733246847Sjkim spec[speclen - 1] = '\0'; 734246847Sjkim if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 735246847Sjkim warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 736246847Sjkim return (0); 737246847Sjkim } 738246847Sjkim /* Make both '@' and ':' notations equal */ 739246847Sjkim if (*hostp != '\0') { 740246847Sjkim len = strlen(hostp); 741246847Sjkim offset = 0; 742246847Sjkim if (have_bracket) 743246847Sjkim nam[offset++] = '['; 744246847Sjkim memmove(nam + offset, hostp, len); 745246847Sjkim if (have_bracket) 746246847Sjkim nam[len + offset++] = ']'; 747246847Sjkim nam[len + offset++] = ':'; 748246847Sjkim memmove(nam + len + offset, spec, speclen); 749246847Sjkim nam[len + speclen + offset] = '\0'; 750246847Sjkim } 751246847Sjkim 752246847Sjkim /* 753246847Sjkim * Handle an internet host address. 754246847Sjkim */ 755246847Sjkim memset(&hints, 0, sizeof hints); 756246847Sjkim hints.ai_flags = AI_NUMERICHOST; 757246847Sjkim if (nfsproto == IPPROTO_TCP) 758246847Sjkim hints.ai_socktype = SOCK_STREAM; 759246847Sjkim else if (nfsproto == IPPROTO_UDP) 760246847Sjkim hints.ai_socktype = SOCK_DGRAM; 761246847Sjkim 762246847Sjkim if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 763246847Sjkim hints.ai_flags = AI_CANONNAME; 764246847Sjkim if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 765246847Sjkim != 0) { 766246847Sjkim if (portspec == NULL) 767246847Sjkim errx(1, "%s: %s", hostp, gai_strerror(ecode)); 768246847Sjkim else 769246847Sjkim errx(1, "%s:%s: %s", hostp, portspec, 770246847Sjkim gai_strerror(ecode)); 771246847Sjkim return (0); 772246847Sjkim } 773246847Sjkim 774246847Sjkim /* 775246847Sjkim * For a Kerberized nfs mount where the "principal" 776246847Sjkim * argument has not been set, add it here. 777246847Sjkim */ 778246847Sjkim if (got_principal == 0 && secflavor >= 0 && 779246847Sjkim secflavor != AUTH_SYS && ai_nfs->ai_canonname != NULL) { 780 snprintf(pname, sizeof (pname), "nfs@%s", 781 ai_nfs->ai_canonname); 782 build_iovec(iov, iovlen, "principal", pname, 783 strlen(pname) + 1); 784 } 785 } 786 787 ret = TRYRET_LOCALERR; 788 for (;;) { 789 /* 790 * Try each entry returned by getaddrinfo(). Note the 791 * occurrence of remote errors by setting `remoteerr'. 792 */ 793 remoteerr = 0; 794 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 795 if ((ai->ai_family == AF_INET6) && 796 (opflags & OF_NOINET6)) 797 continue; 798 if ((ai->ai_family == AF_INET) && 799 (opflags & OF_NOINET4)) 800 continue; 801 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 802 iovlen); 803 if (ret == TRYRET_SUCCESS) 804 break; 805 if (ret != TRYRET_LOCALERR) 806 remoteerr = 1; 807 if ((opflags & ISBGRND) == 0) 808 fprintf(stderr, "%s\n", errstr); 809 } 810 if (ret == TRYRET_SUCCESS) 811 break; 812 813 /* Exit if all errors were local. */ 814 if (!remoteerr) 815 exit(1); 816 817 /* 818 * If retrycnt == 0, we are to keep retrying forever. 819 * Otherwise decrement it, and exit if it hits zero. 820 */ 821 if (retrycnt != 0 && --retrycnt == 0) 822 exit(1); 823 824 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 825 warnx("Cannot immediately mount %s:%s, backgrounding", 826 hostp, spec); 827 opflags |= ISBGRND; 828 if (daemon(0, 0) != 0) 829 err(1, "daemon"); 830 } 831 sleep(60); 832 } 833 freeaddrinfo(ai_nfs); 834 835 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 836 /* Add mounted file system to PATH_MOUNTTAB */ 837 if (!add_mtab(hostp, spec)) 838 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 839 return (1); 840} 841 842/* 843 * Try to set up the NFS arguments according to the address 844 * family, protocol (and possibly port) specified in `ai'. 845 * 846 * Returns TRYRET_SUCCESS if successful, or: 847 * TRYRET_TIMEOUT The server did not respond. 848 * TRYRET_REMOTEERR The server reported an error. 849 * TRYRET_LOCALERR Local failure. 850 * 851 * In all error cases, *errstr will be set to a statically-allocated string 852 * describing the error. 853 */ 854static enum tryret 855nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 856 struct iovec **iov, int *iovlen) 857{ 858 static char errbuf[256]; 859 struct sockaddr_storage nfs_ss; 860 struct netbuf nfs_nb; 861 struct nfhret nfhret; 862 struct timeval try; 863 struct rpc_err rpcerr; 864 CLIENT *clp; 865 struct netconfig *nconf, *nconf_mnt; 866 const char *netid, *netid_mnt; 867 char *secname; 868 int doconnect, nfsvers, mntvers, sotype; 869 enum clnt_stat stat; 870 enum mountmode trymntmode; 871 872 sotype = 0; 873 trymntmode = mountmode; 874 errbuf[0] = '\0'; 875 *errstr = errbuf; 876 877 if (nfsproto == IPPROTO_TCP) 878 sotype = SOCK_STREAM; 879 else if (nfsproto == IPPROTO_UDP) 880 sotype = SOCK_DGRAM; 881 882 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 883 snprintf(errbuf, sizeof errbuf, 884 "af %d sotype %d not supported", ai->ai_family, sotype); 885 return (TRYRET_LOCALERR); 886 } 887 if ((nconf = getnetconf_cached(netid)) == NULL) { 888 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 889 return (TRYRET_LOCALERR); 890 } 891 /* The RPCPROG_MNT netid may be different. */ 892 if (mnttcp_ok) { 893 netid_mnt = netid; 894 nconf_mnt = nconf; 895 } else { 896 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 897 == NULL) { 898 snprintf(errbuf, sizeof errbuf, 899 "af %d sotype SOCK_DGRAM not supported", 900 ai->ai_family); 901 return (TRYRET_LOCALERR); 902 } 903 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 904 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 905 nc_sperror()); 906 return (TRYRET_LOCALERR); 907 } 908 } 909 910tryagain: 911 if (trymntmode == V4) { 912 nfsvers = 4; 913 } else if (trymntmode == V2) { 914 nfsvers = 2; 915 mntvers = 1; 916 } else { 917 nfsvers = 3; 918 mntvers = 3; 919 } 920 921 if (portspec != NULL) { 922 /* `ai' contains the complete nfsd sockaddr. */ 923 nfs_nb.buf = ai->ai_addr; 924 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 925 } else { 926 /* Ask the remote rpcbind. */ 927 nfs_nb.buf = &nfs_ss; 928 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 929 930 if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb, 931 hostp)) { 932 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 933 trymntmode == ANY) { 934 trymntmode = V2; 935 goto tryagain; 936 } 937 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 938 netid, hostp, spec, 939 clnt_spcreateerror("RPCPROG_NFS")); 940 return (returncode(rpc_createerr.cf_stat, 941 &rpc_createerr.cf_error)); 942 } 943 } 944 945 /* Check that the server (nfsd) responds on the port we have chosen. */ 946 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers, 947 0, 0); 948 if (clp == NULL) { 949 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 950 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 951 return (returncode(rpc_createerr.cf_stat, 952 &rpc_createerr.cf_error)); 953 } 954 if (sotype == SOCK_DGRAM && noconn == 0) { 955 /* 956 * Use connect(), to match what the kernel does. This 957 * catches cases where the server responds from the 958 * wrong source address. 959 */ 960 doconnect = 1; 961 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 962 clnt_destroy(clp); 963 snprintf(errbuf, sizeof errbuf, 964 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 965 spec); 966 return (TRYRET_LOCALERR); 967 } 968 } 969 970 try.tv_sec = 10; 971 try.tv_usec = 0; 972 stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 973 (xdrproc_t)xdr_void, NULL, try); 974 if (stat != RPC_SUCCESS) { 975 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 976 clnt_destroy(clp); 977 trymntmode = V2; 978 goto tryagain; 979 } 980 clnt_geterr(clp, &rpcerr); 981 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 982 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 983 clnt_destroy(clp); 984 return (returncode(stat, &rpcerr)); 985 } 986 clnt_destroy(clp); 987 988 /* 989 * For NFSv4, there is no mount protocol. 990 */ 991 if (trymntmode == V4) { 992 /* 993 * Store the server address in nfsargsp, making 994 * sure to copy any locally allocated structures. 995 */ 996 addrlen = nfs_nb.len; 997 addr = malloc(addrlen); 998 if (addr == NULL) 999 err(1, "malloc"); 1000 bcopy(nfs_nb.buf, addr, addrlen); 1001 1002 build_iovec(iov, iovlen, "addr", addr, addrlen); 1003 secname = sec_num_to_name(secflavor); 1004 if (secname != NULL) 1005 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1006 build_iovec(iov, iovlen, "nfsv4", NULL, 0); 1007 build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); 1008 1009 return (TRYRET_SUCCESS); 1010 } 1011 1012 /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ 1013 try.tv_sec = 10; 1014 try.tv_usec = 0; 1015 clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); 1016 if (clp == NULL) { 1017 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1018 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 1019 return (returncode(rpc_createerr.cf_stat, 1020 &rpc_createerr.cf_error)); 1021 } 1022 clp->cl_auth = authsys_create_default(); 1023 nfhret.auth = secflavor; 1024 nfhret.vers = mntvers; 1025 stat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, 1026 (xdrproc_t)xdr_fh, &nfhret, 1027 try); 1028 auth_destroy(clp->cl_auth); 1029 if (stat != RPC_SUCCESS) { 1030 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 1031 clnt_destroy(clp); 1032 trymntmode = V2; 1033 goto tryagain; 1034 } 1035 clnt_geterr(clp, &rpcerr); 1036 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1037 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 1038 clnt_destroy(clp); 1039 return (returncode(stat, &rpcerr)); 1040 } 1041 clnt_destroy(clp); 1042 1043 if (nfhret.stat != 0) { 1044 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1045 hostp, spec, strerror(nfhret.stat)); 1046 return (TRYRET_REMOTEERR); 1047 } 1048 1049 /* 1050 * Store the filehandle and server address in nfsargsp, making 1051 * sure to copy any locally allocated structures. 1052 */ 1053 addrlen = nfs_nb.len; 1054 addr = malloc(addrlen); 1055 fhsize = nfhret.fhsize; 1056 fh = malloc(fhsize); 1057 if (addr == NULL || fh == NULL) 1058 err(1, "malloc"); 1059 bcopy(nfs_nb.buf, addr, addrlen); 1060 bcopy(nfhret.nfh, fh, fhsize); 1061 1062 build_iovec(iov, iovlen, "addr", addr, addrlen); 1063 build_iovec(iov, iovlen, "fh", fh, fhsize); 1064 secname = sec_num_to_name(nfhret.auth); 1065 if (secname) 1066 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1067 if (nfsvers == 3) 1068 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 1069 1070 return (TRYRET_SUCCESS); 1071} 1072 1073/* 1074 * Catagorise a RPC return status and error into an `enum tryret' 1075 * return code. 1076 */ 1077static enum tryret 1078returncode(enum clnt_stat stat, struct rpc_err *rpcerr) 1079{ 1080 switch (stat) { 1081 case RPC_TIMEDOUT: 1082 return (TRYRET_TIMEOUT); 1083 case RPC_PMAPFAILURE: 1084 case RPC_PROGNOTREGISTERED: 1085 case RPC_PROGVERSMISMATCH: 1086 /* XXX, these can be local or remote. */ 1087 case RPC_CANTSEND: 1088 case RPC_CANTRECV: 1089 return (TRYRET_REMOTEERR); 1090 case RPC_SYSTEMERROR: 1091 switch (rpcerr->re_errno) { 1092 case ETIMEDOUT: 1093 return (TRYRET_TIMEOUT); 1094 case ENOMEM: 1095 break; 1096 default: 1097 return (TRYRET_REMOTEERR); 1098 } 1099 /* FALLTHROUGH */ 1100 default: 1101 break; 1102 } 1103 return (TRYRET_LOCALERR); 1104} 1105 1106/* 1107 * Look up a netid based on an address family and socket type. 1108 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1109 * 1110 * XXX there should be a library function for this. 1111 */ 1112static const char * 1113netidbytype(int af, int sotype) 1114{ 1115 struct nc_protos *p; 1116 1117 for (p = nc_protos; p->netid != NULL; p++) { 1118 if (af != p->af || sotype != p->sotype) 1119 continue; 1120 return (p->netid); 1121 } 1122 return (NULL); 1123} 1124 1125/* 1126 * Look up a netconfig entry based on a netid, and cache the result so 1127 * that we don't need to remember to call freenetconfigent(). 1128 * 1129 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1130 * work on failure. 1131 */ 1132static struct netconfig * 1133getnetconf_cached(const char *netid) 1134{ 1135 static struct nc_entry { 1136 struct netconfig *nconf; 1137 struct nc_entry *next; 1138 } *head; 1139 struct nc_entry *p; 1140 struct netconfig *nconf; 1141 1142 for (p = head; p != NULL; p = p->next) 1143 if (strcmp(netid, p->nconf->nc_netid) == 0) 1144 return (p->nconf); 1145 1146 if ((nconf = getnetconfigent(netid)) == NULL) 1147 return (NULL); 1148 if ((p = malloc(sizeof(*p))) == NULL) 1149 err(1, "malloc"); 1150 p->nconf = nconf; 1151 p->next = head; 1152 head = p; 1153 1154 return (p->nconf); 1155} 1156 1157/* 1158 * xdr routines for mount rpc's 1159 */ 1160static int 1161xdr_dir(XDR *xdrsp, char *dirp) 1162{ 1163 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 1164} 1165 1166static int 1167xdr_fh(XDR *xdrsp, struct nfhret *np) 1168{ 1169 int i; 1170 long auth, authcnt, authfnd = 0; 1171 1172 if (!xdr_u_long(xdrsp, &np->stat)) 1173 return (0); 1174 if (np->stat) 1175 return (1); 1176 switch (np->vers) { 1177 case 1: 1178 np->fhsize = NFS_FHSIZE; 1179 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE)); 1180 case 3: 1181 if (!xdr_long(xdrsp, &np->fhsize)) 1182 return (0); 1183 if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE) 1184 return (0); 1185 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1186 return (0); 1187 if (!xdr_long(xdrsp, &authcnt)) 1188 return (0); 1189 for (i = 0; i < authcnt; i++) { 1190 if (!xdr_long(xdrsp, &auth)) 1191 return (0); 1192 if (np->auth == -1) { 1193 np->auth = auth; 1194 authfnd++; 1195 } else if (auth == np->auth) { 1196 authfnd++; 1197 } 1198 } 1199 /* 1200 * Some servers, such as DEC's OSF/1 return a nil authenticator 1201 * list to indicate RPCAUTH_UNIX. 1202 */ 1203 if (authcnt == 0 && np->auth == -1) 1204 np->auth = AUTH_SYS; 1205 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1206 np->stat = EAUTH; 1207 return (1); 1208 }; 1209 return (0); 1210} 1211 1212static void 1213usage(void) 1214{ 1215 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1216"usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1217" [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1218" [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1219" rhost:path node"); 1220 exit(1); 1221} 1222