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