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: stable/10/sbin/mount_nfs/mount_nfs.c 318683 2017-05-22 21:52:06Z rmacklem $");
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. */
82275257Straszstatic struct 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
105275257Straszstatic int retrycnt = -1;
106275257Straszstatic int opflags = 0;
107275257Straszstatic int nfsproto = IPPROTO_TCP;
108275257Straszstatic int mnttcp_ok = 1;
109275257Straszstatic int noconn = 0;
110275257Strasz/* The 'portspec' is the server nfs port; NULL means look up via rpcbind. */
111275257Straszstatic const char *portspec = NULL;
112275257Straszstatic struct sockaddr *addr;
113275257Straszstatic int addrlen = 0;
114275257Straszstatic u_char *fh = NULL;
115275257Straszstatic int fhsize = 0;
116275257Straszstatic int secflavor = -1;
117275257Straszstatic int got_principal = 0;
118183008Srodrigc
119275257Straszstatic enum mountmode {
12025004Sdfr	ANY,
12125004Sdfr	V2,
122166183Srodrigc	V3,
123192930Srmacklem	V4
12425004Sdfr} mountmode = ANY;
1251558Srgrimes
12675394Siedowse/* Return codes for nfs_tryproto. */
12775394Siedowseenum tryret {
12875394Siedowse	TRYRET_SUCCESS,
12975394Siedowse	TRYRET_TIMEOUT,		/* No response received. */
13075394Siedowse	TRYRET_REMOTEERR,	/* Error received from remote server. */
13175394Siedowse	TRYRET_LOCALERR		/* Local failure. */
13275394Siedowse};
13375394Siedowse
134275257Straszstatic int	sec_name_to_num(const char *sec);
135275257Straszstatic const 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;
153275257Strasz	char *mntname, *p, *spec, *tmp;
154164733Srodrigc	char mntpath[MAXPATHLEN], errmsg[255];
155275257Strasz	char hostname[MAXHOSTNAMELEN + 1], gssn[MAXHOSTNAMELEN + 50];
156275257Strasz	const char *fstype, *gssname;
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;
231275257Strasz				const 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;
281275257Strasz					asprintf(&tmp, "%d", atoi(val));
282275257Strasz					if (tmp == NULL)
283103039Speter						err(1, "asprintf");
284275257Strasz					portspec = tmp;
285192930Srmacklem				} else if (strcmp(opt, "principal") == 0) {
286192930Srmacklem					got_principal = 1;
287275249Strasz				} else if (strcmp(opt, "proto") == 0) {
288275249Strasz					pass_flag_to_nmount=0;
289275249Strasz					if (strcmp(val, "tcp") == 0) {
290275249Strasz						nfsproto = IPPROTO_TCP;
291275249Strasz						opflags |= OF_NOINET6;
292275249Strasz						build_iovec(&iov, &iovlen,
293275249Strasz						    "tcp", NULL, 0);
294275249Strasz					} else if (strcmp(val, "udp") == 0) {
295275249Strasz						mnttcp_ok = 0;
296275249Strasz						nfsproto = IPPROTO_UDP;
297275249Strasz						opflags |= OF_NOINET6;
298275249Strasz						build_iovec(&iov, &iovlen,
299275249Strasz						    "udp", NULL, 0);
300275249Strasz					} else if (strcmp(val, "tcp6") == 0) {
301275249Strasz						nfsproto = IPPROTO_TCP;
302275249Strasz						opflags |= OF_NOINET4;
303275249Strasz						build_iovec(&iov, &iovlen,
304275249Strasz						    "tcp", NULL, 0);
305275249Strasz					} else if (strcmp(val, "udp6") == 0) {
306275249Strasz						mnttcp_ok = 0;
307275249Strasz						nfsproto = IPPROTO_UDP;
308275249Strasz						opflags |= OF_NOINET4;
309275249Strasz						build_iovec(&iov, &iovlen,
310275249Strasz						    "udp", NULL, 0);
311275249Strasz					} else {
312275249Strasz						errx(1,
313275249Strasz						    "illegal proto value -- %s",
314275249Strasz						    val);
315275249Strasz					}
316184588Sdfr				} else if (strcmp(opt, "sec") == 0) {
317184588Sdfr					/*
318184588Sdfr					 * Don't add this option to
319184588Sdfr					 * the iovec yet - we will
320184588Sdfr					 * negotiate which sec flavor
321184588Sdfr					 * to use with the remote
322184588Sdfr					 * mountd.
323184588Sdfr					 */
324184588Sdfr					pass_flag_to_nmount=0;
325184588Sdfr					secflavor = sec_name_to_num(val);
326184588Sdfr					if (secflavor < 0) {
327184588Sdfr						errx(1,
328184588Sdfr						    "illegal sec value -- %s",
329184588Sdfr						    val);
330184588Sdfr					}
331183008Srodrigc				} else if (strcmp(opt, "retrycnt") == 0) {
332183008Srodrigc					pass_flag_to_nmount=0;
333183008Srodrigc					num = strtol(val, &p, 10);
334183008Srodrigc					if (*p || num < 0)
335183008Srodrigc						errx(1, "illegal retrycnt value -- %s", val);
336183008Srodrigc					retrycnt = num;
337183008Srodrigc				} else if (strcmp(opt, "maxgroups") == 0) {
338183008Srodrigc					num = strtol(val, &p, 10);
339183008Srodrigc					if (*p || num <= 0)
340183008Srodrigc						errx(1, "illegal maxgroups value -- %s", val);
341183008Srodrigc					//set_rpc_maxgrouplist(num);
342270043Sbz				} else if (strcmp(opt, "vers") == 0) {
343270043Sbz					num = strtol(val, &p, 10);
344270043Sbz					if (*p || num <= 0)
345270043Sbz						errx(1, "illegal vers value -- "
346270043Sbz						    "%s", val);
347270043Sbz					switch (num) {
348270043Sbz					case 2:
349270043Sbz						mountmode = V2;
350270043Sbz						break;
351270043Sbz					case 3:
352270043Sbz						mountmode = V3;
353270043Sbz						build_iovec(&iov, &iovlen,
354270043Sbz						    "nfsv3", NULL, 0);
355270043Sbz						break;
356270043Sbz					case 4:
357270043Sbz						mountmode = V4;
358270043Sbz						fstype = "nfs";
359270043Sbz						nfsproto = IPPROTO_TCP;
360270043Sbz						if (portspec == NULL)
361270043Sbz							portspec = "2049";
362270043Sbz						break;
363270043Sbz					default:
364270043Sbz						errx(1, "illegal nfs version "
365270043Sbz						    "value -- %s", val);
366270043Sbz					}
367270043Sbz					pass_flag_to_nmount=0;
368103039Speter				}
369275257Strasz				if (pass_flag_to_nmount) {
370275257Strasz					build_iovec(&iov, &iovlen, opt,
371275257Strasz					    __DECONST(void *, val),
372183008Srodrigc					    strlen(val) + 1);
373275257Strasz				}
374183008Srodrigc				opt = pnextopt;
37575394Siedowse			}
376103039Speter			}
3771558Srgrimes			break;
3781558Srgrimes		case 'P':
379183008Srodrigc			/* obsolete for -o noresvport now default */
380183008Srodrigc			printf("-P deprecated, use -o noresvport\n");
381183008Srodrigc			build_iovec(&iov, &iovlen, "noresvport", NULL, 0);
3821558Srgrimes			break;
3831558Srgrimes		case 'R':
384183008Srodrigc			printf("-R deprecated, use -o retrycnt=<retrycnt>\n");
3851558Srgrimes			num = strtol(optarg, &p, 10);
38680006Siedowse			if (*p || num < 0)
3871558Srgrimes				errx(1, "illegal -R value -- %s", optarg);
3881558Srgrimes			retrycnt = num;
3891558Srgrimes			break;
3901558Srgrimes		case 'r':
391183008Srodrigc			printf("-r deprecated, use -o rsize=<rsize>\n");
392183008Srodrigc			build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1);
3931558Srgrimes			break;
3941558Srgrimes		case 's':
395183008Srodrigc			printf("-s deprecated, use -o soft\n");
396183008Srodrigc			build_iovec(&iov, &iovlen, "soft", NULL, 0);
3971558Srgrimes			break;
3981558Srgrimes		case 'T':
3999336Sdfr			nfsproto = IPPROTO_TCP;
400183008Srodrigc			printf("-T deprecated, use -o tcp\n");
4011558Srgrimes			break;
4021558Srgrimes		case 't':
403183008Srodrigc			printf("-t deprecated, use -o timeout=<value>\n");
404183008Srodrigc			build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1);
4051558Srgrimes			break;
4061558Srgrimes		case 'w':
407183008Srodrigc			printf("-w deprecated, use -o wsize=<value>\n");
408183008Srodrigc			build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1);
4091558Srgrimes			break;
4101558Srgrimes		case 'x':
411183008Srodrigc			printf("-x deprecated, use -o retrans=<value>\n");
412183008Srodrigc			build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1);
4131558Srgrimes			break;
4149336Sdfr		case 'U':
415183008Srodrigc			printf("-U deprecated, use -o mntudp\n");
4169336Sdfr			mnttcp_ok = 0;
417166183Srodrigc			nfsproto = IPPROTO_UDP;
418183008Srodrigc			build_iovec(&iov, &iovlen, "mntudp", NULL, 0);
4199336Sdfr			break;
4201558Srgrimes		default:
4211558Srgrimes			usage();
4221558Srgrimes			break;
4231558Srgrimes		}
4241558Srgrimes	argc -= optind;
4251558Srgrimes	argv += optind;
4261558Srgrimes
42723680Speter	if (argc != 2) {
4285966Sdg		usage();
42923680Speter		/* NOTREACHED */
43023680Speter	}
4311558Srgrimes
4321558Srgrimes	spec = *argv++;
433275257Strasz	mntname = *argv;
4341558Srgrimes
43580006Siedowse	if (retrycnt == -1)
43680086Siedowse		/* The default is to keep retrying forever. */
43780086Siedowse		retrycnt = 0;
4382999Swollman
439192930Srmacklem	/*
440221124Srmacklem	 * If the fstye is "oldnfs", run the old NFS client unless the
441221124Srmacklem	 * "nfsv4" option was specified.
442192930Srmacklem	 */
443221124Srmacklem	if (strcmp(fstype, "nfs") == 0) {
444192930Srmacklem		if (modfind("nfscl") < 0) {
445192930Srmacklem			/* Not present in kernel, try loading it */
446192930Srmacklem			if (kldload("nfscl") < 0 ||
447192930Srmacklem			    modfind("nfscl") < 0)
448192930Srmacklem				errx(1, "nfscl is not available");
449192930Srmacklem		}
450192930Srmacklem	}
451192930Srmacklem
452192930Srmacklem	/*
453192930Srmacklem	 * Add the fqdn to the gssname, as required.
454192930Srmacklem	 */
455192930Srmacklem	if (gssname != NULL) {
456192930Srmacklem		if (strchr(gssname, '@') == NULL &&
457192930Srmacklem		    gethostname(hostname, MAXHOSTNAMELEN) == 0) {
458192930Srmacklem			snprintf(gssn, sizeof (gssn), "%s@%s", gssname,
459192930Srmacklem			    hostname);
460192930Srmacklem			gssname = gssn;
461192930Srmacklem		}
462275257Strasz		build_iovec(&iov, &iovlen, "gssname",
463275257Strasz		    __DECONST(void *, gssname), strlen(gssname) + 1);
464192930Srmacklem	}
465192930Srmacklem
466192578Srwatson	if (!getnfsargs(spec, &iov, &iovlen))
467192578Srwatson		exit(1);
468166183Srodrigc
46952055Sphk	/* resolve the mountpoint with realpath(3) */
470275257Strasz	if (checkpath(mntname, mntpath) != 0)
471230226Sjh		err(1, "%s", mntpath);
47252055Sphk
473275257Strasz	build_iovec(&iov, &iovlen, "fstype",
474275257Strasz	    __DECONST(void *, fstype), (size_t)-1);
475164457Srodrigc	build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
476164733Srodrigc	build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
477164457Srodrigc
478275255Strasz	if (nmount(iov, iovlen, 0))
479275255Strasz		err(1, "%s, %s", mntpath, errmsg);
4809336Sdfr
4811558Srgrimes	exit(0);
4821558Srgrimes}
4831558Srgrimes
484183008Srodrigcstatic int
485275257Straszsec_name_to_num(const char *sec)
486184588Sdfr{
487184588Sdfr	if (!strcmp(sec, "krb5"))
488184588Sdfr		return (RPCSEC_GSS_KRB5);
489184588Sdfr	if (!strcmp(sec, "krb5i"))
490184588Sdfr		return (RPCSEC_GSS_KRB5I);
491184588Sdfr	if (!strcmp(sec, "krb5p"))
492184588Sdfr		return (RPCSEC_GSS_KRB5P);
493184588Sdfr	if (!strcmp(sec, "sys"))
494184588Sdfr		return (AUTH_SYS);
495184588Sdfr	return (-1);
496184588Sdfr}
497184588Sdfr
498275257Straszstatic const char *
499184588Sdfrsec_num_to_name(int flavor)
500184588Sdfr{
501184588Sdfr	switch (flavor) {
502184588Sdfr	case RPCSEC_GSS_KRB5:
503184588Sdfr		return ("krb5");
504184588Sdfr	case RPCSEC_GSS_KRB5I:
505184588Sdfr		return ("krb5i");
506184588Sdfr	case RPCSEC_GSS_KRB5P:
507184588Sdfr		return ("krb5p");
508184588Sdfr	case AUTH_SYS:
509184588Sdfr		return ("sys");
510184588Sdfr	}
511184588Sdfr	return (NULL);
512184588Sdfr}
513184588Sdfr
514203461Sdelphijstatic int
515183008Srodrigcgetnfsargs(char *spec, struct iovec **iov, int *iovlen)
516183008Srodrigc{
51774462Salfred	struct addrinfo hints, *ai_nfs, *ai;
51875394Siedowse	enum tryret ret;
519203490Sume	int ecode, speclen, remoteerr, offset, have_bracket = 0;
52075394Siedowse	char *hostp, *delimp, *errstr;
52152679Sgreen	size_t len;
522192930Srmacklem	static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5];
5231558Srgrimes
524203490Sume	if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL &&
525203490Sume	    *(delimp + 1) == ':') {
526203490Sume		hostp = spec + 1;
527203490Sume		spec = delimp + 2;
528203490Sume		have_bracket = 1;
529203490Sume	} else if ((delimp = strrchr(spec, ':')) != NULL) {
5301558Srgrimes		hostp = spec;
5311558Srgrimes		spec = delimp + 1;
53252055Sphk	} else if ((delimp = strrchr(spec, '@')) != NULL) {
53352055Sphk		warnx("path@server syntax is deprecated, use server:path");
53452055Sphk		hostp = delimp + 1;
5351558Srgrimes	} else {
53652055Sphk		warnx("no <host>:<dirpath> nfs-name");
5371558Srgrimes		return (0);
5381558Srgrimes	}
5391558Srgrimes	*delimp = '\0';
54052055Sphk
5411558Srgrimes	/*
54252055Sphk	 * If there has been a trailing slash at mounttime it seems
54352055Sphk	 * that some mountd implementations fail to remove the mount
54452055Sphk	 * entries from their mountlist while unmounting.
54552055Sphk	 */
54652679Sgreen	for (speclen = strlen(spec);
54752679Sgreen		speclen > 1 && spec[speclen - 1] == '/';
54852679Sgreen		speclen--)
54952055Sphk		spec[speclen - 1] = '\0';
55052055Sphk	if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
55152055Sphk		warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
55252055Sphk		return (0);
55352055Sphk	}
55452055Sphk	/* Make both '@' and ':' notations equal */
55552679Sgreen	if (*hostp != '\0') {
55652679Sgreen		len = strlen(hostp);
557203490Sume		offset = 0;
558203490Sume		if (have_bracket)
559203490Sume			nam[offset++] = '[';
560203490Sume		memmove(nam + offset, hostp, len);
561203490Sume		if (have_bracket)
562203490Sume			nam[len + offset++] = ']';
563203490Sume		nam[len + offset++] = ':';
564203490Sume		memmove(nam + len + offset, spec, speclen);
565203490Sume		nam[len + speclen + offset] = '\0';
56652679Sgreen	}
5671558Srgrimes
5681558Srgrimes	/*
56983653Speter	 * Handle an internet host address.
5701558Srgrimes	 */
57174462Salfred	memset(&hints, 0, sizeof hints);
57274462Salfred	hints.ai_flags = AI_NUMERICHOST;
573183008Srodrigc	if (nfsproto == IPPROTO_TCP)
574183008Srodrigc		hints.ai_socktype = SOCK_STREAM;
575183008Srodrigc	else if (nfsproto == IPPROTO_UDP)
576183008Srodrigc		hints.ai_socktype = SOCK_DGRAM;
577183008Srodrigc
57883653Speter	if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
579192930Srmacklem		hints.ai_flags = AI_CANONNAME;
58075394Siedowse		if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
58175394Siedowse		    != 0) {
58275394Siedowse			if (portspec == NULL)
58375394Siedowse				errx(1, "%s: %s", hostp, gai_strerror(ecode));
58475394Siedowse			else
58575394Siedowse				errx(1, "%s:%s: %s", hostp, portspec,
58675394Siedowse				    gai_strerror(ecode));
5871558Srgrimes			return (0);
5881558Srgrimes		}
589192930Srmacklem
590192930Srmacklem		/*
591192930Srmacklem		 * For a Kerberized nfs mount where the "principal"
592192930Srmacklem		 * argument has not been set, add it here.
593192930Srmacklem		 */
594286483Srmacklem		if (got_principal == 0 && secflavor != AUTH_SYS &&
595286483Srmacklem		    ai_nfs->ai_canonname != NULL) {
596192930Srmacklem			snprintf(pname, sizeof (pname), "nfs@%s",
597192930Srmacklem			    ai_nfs->ai_canonname);
598192930Srmacklem			build_iovec(iov, iovlen, "principal", pname,
599192930Srmacklem			    strlen(pname) + 1);
600192930Srmacklem		}
60174462Salfred	}
6021558Srgrimes
60375394Siedowse	ret = TRYRET_LOCALERR;
60480006Siedowse	for (;;) {
60575394Siedowse		/*
60675394Siedowse		 * Try each entry returned by getaddrinfo(). Note the
607229778Suqs		 * occurrence of remote errors by setting `remoteerr'.
60875394Siedowse		 */
60975394Siedowse		remoteerr = 0;
61075394Siedowse		for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
611112580Smdodd			if ((ai->ai_family == AF_INET6) &&
612112580Smdodd			    (opflags & OF_NOINET6))
613112580Smdodd				continue;
614112580Smdodd			if ((ai->ai_family == AF_INET) &&
615112580Smdodd			    (opflags & OF_NOINET4))
616112580Smdodd				continue;
617183008Srodrigc			ret = nfs_tryproto(ai, hostp, spec, &errstr, iov,
618183008Srodrigc			    iovlen);
61975394Siedowse			if (ret == TRYRET_SUCCESS)
62075394Siedowse				break;
62175394Siedowse			if (ret != TRYRET_LOCALERR)
62275394Siedowse				remoteerr = 1;
62375394Siedowse			if ((opflags & ISBGRND) == 0)
62475394Siedowse				fprintf(stderr, "%s\n", errstr);
62575394Siedowse		}
62675394Siedowse		if (ret == TRYRET_SUCCESS)
62775394Siedowse			break;
62874462Salfred
62980006Siedowse		/* Exit if all errors were local. */
63080006Siedowse		if (!remoteerr)
63180006Siedowse			exit(1);
63280006Siedowse
63374462Salfred		/*
63480006Siedowse		 * If retrycnt == 0, we are to keep retrying forever.
63580006Siedowse		 * Otherwise decrement it, and exit if it hits zero.
63674462Salfred		 */
63780006Siedowse		if (retrycnt != 0 && --retrycnt == 0)
63875394Siedowse			exit(1);
63975394Siedowse
64075394Siedowse		if ((opflags & (BGRND | ISBGRND)) == BGRND) {
64175394Siedowse			warnx("Cannot immediately mount %s:%s, backgrounding",
64275394Siedowse			    hostp, spec);
64375394Siedowse			opflags |= ISBGRND;
64475394Siedowse			if (daemon(0, 0) != 0)
64575394Siedowse				err(1, "daemon");
64674462Salfred		}
64775394Siedowse		sleep(60);
64875394Siedowse	}
64975394Siedowse	freeaddrinfo(ai_nfs);
650183008Srodrigc
651183008Srodrigc	build_iovec(iov, iovlen, "hostname", nam, (size_t)-1);
652102231Strhodes	/* Add mounted file system to PATH_MOUNTTAB */
653318683Srmacklem	if (mountmode != V4 && !add_mtab(hostp, spec))
65475394Siedowse		warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
65575394Siedowse	return (1);
65675394Siedowse}
65774462Salfred
65875394Siedowse/*
65975394Siedowse * Try to set up the NFS arguments according to the address
66075394Siedowse * family, protocol (and possibly port) specified in `ai'.
66175394Siedowse *
66275394Siedowse * Returns TRYRET_SUCCESS if successful, or:
66375394Siedowse *   TRYRET_TIMEOUT		The server did not respond.
66475394Siedowse *   TRYRET_REMOTEERR		The server reported an error.
66575394Siedowse *   TRYRET_LOCALERR		Local failure.
66675394Siedowse *
66775394Siedowse * In all error cases, *errstr will be set to a statically-allocated string
66875394Siedowse * describing the error.
66975394Siedowse */
670203461Sdelphijstatic enum tryret
671183008Srodrigcnfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr,
672183008Srodrigc    struct iovec **iov, int *iovlen)
67375394Siedowse{
67475394Siedowse	static char errbuf[256];
67575394Siedowse	struct sockaddr_storage nfs_ss;
67675394Siedowse	struct netbuf nfs_nb;
67775394Siedowse	struct nfhret nfhret;
67875394Siedowse	struct timeval try;
67975394Siedowse	struct rpc_err rpcerr;
68075394Siedowse	CLIENT *clp;
68175394Siedowse	struct netconfig *nconf, *nconf_mnt;
682275257Strasz	const char *netid, *netid_mnt, *secname;
683183008Srodrigc	int doconnect, nfsvers, mntvers, sotype;
684275257Strasz	enum clnt_stat clntstat;
68575394Siedowse	enum mountmode trymntmode;
68674462Salfred
687212195Skevlo	sotype = 0;
68875394Siedowse	trymntmode = mountmode;
68975394Siedowse	errbuf[0] = '\0';
69075394Siedowse	*errstr = errbuf;
69174462Salfred
692183008Srodrigc	if (nfsproto == IPPROTO_TCP)
693183008Srodrigc		sotype = SOCK_STREAM;
694183008Srodrigc	else if (nfsproto == IPPROTO_UDP)
695183008Srodrigc		sotype = SOCK_DGRAM;
696183008Srodrigc
697183008Srodrigc	if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) {
69876530Siedowse		snprintf(errbuf, sizeof errbuf,
699183008Srodrigc		    "af %d sotype %d not supported", ai->ai_family, sotype);
70076530Siedowse		return (TRYRET_LOCALERR);
70176530Siedowse	}
70276530Siedowse	if ((nconf = getnetconf_cached(netid)) == NULL) {
70375394Siedowse		snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
70475394Siedowse		return (TRYRET_LOCALERR);
70575394Siedowse	}
70675394Siedowse	/* The RPCPROG_MNT netid may be different. */
70775394Siedowse	if (mnttcp_ok) {
70875401Siedowse		netid_mnt = netid;
70975394Siedowse		nconf_mnt = nconf;
71075394Siedowse	} else {
71176530Siedowse		if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM))
71276530Siedowse		     == NULL) {
71376530Siedowse			snprintf(errbuf, sizeof errbuf,
71476530Siedowse			    "af %d sotype SOCK_DGRAM not supported",
71576530Siedowse			     ai->ai_family);
71676530Siedowse			return (TRYRET_LOCALERR);
71776530Siedowse		}
71876530Siedowse		if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) {
71975401Siedowse			snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt,
72075394Siedowse			    nc_sperror());
72175394Siedowse			return (TRYRET_LOCALERR);
72275046Sache		}
72375394Siedowse	}
72475401Siedowse
72575394Siedowsetryagain:
726192930Srmacklem	if (trymntmode == V4) {
727192930Srmacklem		nfsvers = 4;
728275257Strasz		mntvers = 3; /* Workaround for GCC. */
729192930Srmacklem	} else if (trymntmode == V2) {
73075394Siedowse		nfsvers = 2;
73175394Siedowse		mntvers = 1;
73275394Siedowse	} else {
73375394Siedowse		nfsvers = 3;
73475394Siedowse		mntvers = 3;
73575394Siedowse	}
73675046Sache
73775394Siedowse	if (portspec != NULL) {
73875394Siedowse		/* `ai' contains the complete nfsd sockaddr. */
73975394Siedowse		nfs_nb.buf = ai->ai_addr;
74075394Siedowse		nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
74175394Siedowse	} else {
74275394Siedowse		/* Ask the remote rpcbind. */
74375394Siedowse		nfs_nb.buf = &nfs_ss;
74475394Siedowse		nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
74575394Siedowse
746194880Sdfr		if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb,
74775394Siedowse		    hostp)) {
74875394Siedowse			if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH &&
74975394Siedowse			    trymntmode == ANY) {
75075394Siedowse				trymntmode = V2;
75175394Siedowse				goto tryagain;
7521558Srgrimes			}
75375394Siedowse			snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
75475394Siedowse			    netid, hostp, spec,
75575394Siedowse			    clnt_spcreateerror("RPCPROG_NFS"));
75675394Siedowse			return (returncode(rpc_createerr.cf_stat,
75775394Siedowse			    &rpc_createerr.cf_error));
75875046Sache		}
75975394Siedowse	}
76075394Siedowse
76175394Siedowse	/* Check that the server (nfsd) responds on the port we have chosen. */
762194880Sdfr	clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers,
76375394Siedowse	    0, 0);
76475394Siedowse	if (clp == NULL) {
76575394Siedowse		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
76675394Siedowse		    hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
76775394Siedowse		return (returncode(rpc_createerr.cf_stat,
76875394Siedowse		    &rpc_createerr.cf_error));
76975394Siedowse	}
770183008Srodrigc	if (sotype == SOCK_DGRAM && noconn == 0) {
77178679Siedowse		/*
77278679Siedowse		 * Use connect(), to match what the kernel does. This
77378679Siedowse		 * catches cases where the server responds from the
77478679Siedowse		 * wrong source address.
77578679Siedowse		 */
77678679Siedowse		doconnect = 1;
77778679Siedowse		if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
77878679Siedowse			clnt_destroy(clp);
77978679Siedowse			snprintf(errbuf, sizeof errbuf,
78078679Siedowse			    "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp,
78178679Siedowse			    spec);
78278679Siedowse			return (TRYRET_LOCALERR);
78378679Siedowse		}
78478679Siedowse	}
78578679Siedowse
78675394Siedowse	try.tv_sec = 10;
78775394Siedowse	try.tv_usec = 0;
788275257Strasz	clntstat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
789192930Srmacklem			 (xdrproc_t)xdr_void, NULL, try);
790275257Strasz	if (clntstat != RPC_SUCCESS) {
791275257Strasz		if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
79275394Siedowse			clnt_destroy(clp);
79375394Siedowse			trymntmode = V2;
79475394Siedowse			goto tryagain;
7951558Srgrimes		}
79675394Siedowse		clnt_geterr(clp, &rpcerr);
79775394Siedowse		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
79875394Siedowse		    hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
79975394Siedowse		clnt_destroy(clp);
800275257Strasz		return (returncode(clntstat, &rpcerr));
8011558Srgrimes	}
80275394Siedowse	clnt_destroy(clp);
80375394Siedowse
804192930Srmacklem	/*
805192930Srmacklem	 * For NFSv4, there is no mount protocol.
806192930Srmacklem	 */
807192930Srmacklem	if (trymntmode == V4) {
808192930Srmacklem		/*
809192930Srmacklem		 * Store the server address in nfsargsp, making
810192930Srmacklem		 * sure to copy any locally allocated structures.
811192930Srmacklem		 */
812192930Srmacklem		addrlen = nfs_nb.len;
813192930Srmacklem		addr = malloc(addrlen);
814192930Srmacklem		if (addr == NULL)
815192930Srmacklem			err(1, "malloc");
816192930Srmacklem		bcopy(nfs_nb.buf, addr, addrlen);
817192930Srmacklem
818192930Srmacklem		build_iovec(iov, iovlen, "addr", addr, addrlen);
819192930Srmacklem		secname = sec_num_to_name(secflavor);
820275257Strasz		if (secname != NULL) {
821275257Strasz			build_iovec(iov, iovlen, "sec",
822275257Strasz			    __DECONST(void *, secname), (size_t)-1);
823275257Strasz		}
824192930Srmacklem		build_iovec(iov, iovlen, "nfsv4", NULL, 0);
825192930Srmacklem		build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1);
826192930Srmacklem
827192930Srmacklem		return (TRYRET_SUCCESS);
828192930Srmacklem	}
829192930Srmacklem
830194880Sdfr	/* Send the MOUNTPROC_MNT RPC to get the root filehandle. */
83175394Siedowse	try.tv_sec = 10;
83275394Siedowse	try.tv_usec = 0;
833194880Sdfr	clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt);
83475394Siedowse	if (clp == NULL) {
83575401Siedowse		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
83675394Siedowse		    hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
83775394Siedowse		return (returncode(rpc_createerr.cf_stat,
83875394Siedowse		    &rpc_createerr.cf_error));
8391558Srgrimes	}
84075394Siedowse	clp->cl_auth = authsys_create_default();
841184588Sdfr	nfhret.auth = secflavor;
84275394Siedowse	nfhret.vers = mntvers;
843275257Strasz	clntstat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec,
844112570Smdodd			 (xdrproc_t)xdr_fh, &nfhret,
84575394Siedowse	    try);
84675394Siedowse	auth_destroy(clp->cl_auth);
847275257Strasz	if (clntstat != RPC_SUCCESS) {
848275257Strasz		if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
84975394Siedowse			clnt_destroy(clp);
85075394Siedowse			trymntmode = V2;
85175394Siedowse			goto tryagain;
85275394Siedowse		}
85375394Siedowse		clnt_geterr(clp, &rpcerr);
85475401Siedowse		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
85575394Siedowse		    hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
85675394Siedowse		clnt_destroy(clp);
857275257Strasz		return (returncode(clntstat, &rpcerr));
8581558Srgrimes	}
85975394Siedowse	clnt_destroy(clp);
86075394Siedowse
86175394Siedowse	if (nfhret.stat != 0) {
86275401Siedowse		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
86375394Siedowse		    hostp, spec, strerror(nfhret.stat));
86475394Siedowse		return (TRYRET_REMOTEERR);
86575394Siedowse	}
86675394Siedowse
86775394Siedowse	/*
86875394Siedowse	 * Store the filehandle and server address in nfsargsp, making
86975394Siedowse	 * sure to copy any locally allocated structures.
87075394Siedowse	 */
871183008Srodrigc	addrlen = nfs_nb.len;
872183008Srodrigc	addr = malloc(addrlen);
873183008Srodrigc	fhsize = nfhret.fhsize;
874183008Srodrigc	fh = malloc(fhsize);
875183008Srodrigc	if (addr == NULL || fh == NULL)
87675394Siedowse		err(1, "malloc");
877183008Srodrigc	bcopy(nfs_nb.buf, addr, addrlen);
878183008Srodrigc	bcopy(nfhret.nfh, fh, fhsize);
87975394Siedowse
880183008Srodrigc	build_iovec(iov, iovlen, "addr", addr, addrlen);
881183008Srodrigc	build_iovec(iov, iovlen, "fh", fh, fhsize);
882184588Sdfr	secname = sec_num_to_name(nfhret.auth);
883275257Strasz	if (secname) {
884275257Strasz		build_iovec(iov, iovlen, "sec",
885275257Strasz		    __DECONST(void *, secname), (size_t)-1);
886275257Strasz	}
88775394Siedowse	if (nfsvers == 3)
888183008Srodrigc		build_iovec(iov, iovlen, "nfsv3", NULL, 0);
88975394Siedowse
89075394Siedowse	return (TRYRET_SUCCESS);
8911558Srgrimes}
8921558Srgrimes
8931558Srgrimes/*
89475394Siedowse * Catagorise a RPC return status and error into an `enum tryret'
89575394Siedowse * return code.
89675394Siedowse */
897203461Sdelphijstatic enum tryret
898275257Straszreturncode(enum clnt_stat clntstat, struct rpc_err *rpcerr)
89975394Siedowse{
900275257Strasz
901275257Strasz	switch (clntstat) {
90275394Siedowse	case RPC_TIMEDOUT:
90375394Siedowse		return (TRYRET_TIMEOUT);
90475394Siedowse	case RPC_PMAPFAILURE:
90575394Siedowse	case RPC_PROGNOTREGISTERED:
90675394Siedowse	case RPC_PROGVERSMISMATCH:
90778679Siedowse	/* XXX, these can be local or remote. */
90878679Siedowse	case RPC_CANTSEND:
90978679Siedowse	case RPC_CANTRECV:
91075394Siedowse		return (TRYRET_REMOTEERR);
91175394Siedowse	case RPC_SYSTEMERROR:
91275394Siedowse		switch (rpcerr->re_errno) {
91375394Siedowse		case ETIMEDOUT:
91475394Siedowse			return (TRYRET_TIMEOUT);
91575394Siedowse		case ENOMEM:
91675394Siedowse			break;
91775394Siedowse		default:
91875394Siedowse			return (TRYRET_REMOTEERR);
91975394Siedowse		}
92075394Siedowse		/* FALLTHROUGH */
92175394Siedowse	default:
92275394Siedowse		break;
92375394Siedowse	}
92475394Siedowse	return (TRYRET_LOCALERR);
92575394Siedowse}
92675394Siedowse
92775394Siedowse/*
92876530Siedowse * Look up a netid based on an address family and socket type.
92976530Siedowse * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
93076530Siedowse *
93176530Siedowse * XXX there should be a library function for this.
93276530Siedowse */
933203461Sdelphijstatic const char *
934156925Simpnetidbytype(int af, int sotype)
935156925Simp{
93676530Siedowse	struct nc_protos *p;
93776530Siedowse
93876530Siedowse	for (p = nc_protos; p->netid != NULL; p++) {
93976530Siedowse		if (af != p->af || sotype != p->sotype)
94076530Siedowse			continue;
94176530Siedowse		return (p->netid);
94276530Siedowse	}
94376530Siedowse	return (NULL);
94476530Siedowse}
94576530Siedowse
94676530Siedowse/*
94776530Siedowse * Look up a netconfig entry based on a netid, and cache the result so
94876530Siedowse * that we don't need to remember to call freenetconfigent().
94976530Siedowse *
95076530Siedowse * Otherwise it behaves just like getnetconfigent(), so nc_*error()
95176530Siedowse * work on failure.
95276530Siedowse */
953203461Sdelphijstatic struct netconfig *
954156925Simpgetnetconf_cached(const char *netid)
955156925Simp{
95676530Siedowse	static struct nc_entry {
95776530Siedowse		struct netconfig *nconf;
95876530Siedowse		struct nc_entry *next;
95976530Siedowse	} *head;
96076530Siedowse	struct nc_entry *p;
96176530Siedowse	struct netconfig *nconf;
96276530Siedowse
96376530Siedowse	for (p = head; p != NULL; p = p->next)
96476530Siedowse		if (strcmp(netid, p->nconf->nc_netid) == 0)
96576530Siedowse			return (p->nconf);
96676530Siedowse
96776530Siedowse	if ((nconf = getnetconfigent(netid)) == NULL)
96876530Siedowse		return (NULL);
96976530Siedowse	if ((p = malloc(sizeof(*p))) == NULL)
97076530Siedowse		err(1, "malloc");
97176530Siedowse	p->nconf = nconf;
97276530Siedowse	p->next = head;
97376530Siedowse	head = p;
97476530Siedowse
97576530Siedowse	return (p->nconf);
97676530Siedowse}
97776530Siedowse
97876530Siedowse/*
9791558Srgrimes * xdr routines for mount rpc's
9801558Srgrimes */
981203461Sdelphijstatic int
982156925Simpxdr_dir(XDR *xdrsp, char *dirp)
9831558Srgrimes{
984194880Sdfr	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
9851558Srgrimes}
9861558Srgrimes
987203461Sdelphijstatic int
988156925Simpxdr_fh(XDR *xdrsp, struct nfhret *np)
9891558Srgrimes{
99092806Sobrien	int i;
9919336Sdfr	long auth, authcnt, authfnd = 0;
9929336Sdfr
9939336Sdfr	if (!xdr_u_long(xdrsp, &np->stat))
9941558Srgrimes		return (0);
9951558Srgrimes	if (np->stat)
9961558Srgrimes		return (1);
9979336Sdfr	switch (np->vers) {
9989336Sdfr	case 1:
999194880Sdfr		np->fhsize = NFS_FHSIZE;
1000194880Sdfr		return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE));
10019336Sdfr	case 3:
10029336Sdfr		if (!xdr_long(xdrsp, &np->fhsize))
10039336Sdfr			return (0);
1004194880Sdfr		if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE)
10059336Sdfr			return (0);
10069336Sdfr		if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
10079336Sdfr			return (0);
10089336Sdfr		if (!xdr_long(xdrsp, &authcnt))
10099336Sdfr			return (0);
10109336Sdfr		for (i = 0; i < authcnt; i++) {
10119336Sdfr			if (!xdr_long(xdrsp, &auth))
10129336Sdfr				return (0);
1013183008Srodrigc			if (np->auth == -1) {
1014183008Srodrigc				np->auth = auth;
10159336Sdfr				authfnd++;
1016183008Srodrigc			} else if (auth == np->auth) {
1017183008Srodrigc				authfnd++;
1018183008Srodrigc			}
10199336Sdfr		}
10209336Sdfr		/*
10219336Sdfr		 * Some servers, such as DEC's OSF/1 return a nil authenticator
10229336Sdfr		 * list to indicate RPCAUTH_UNIX.
10239336Sdfr		 */
1024183008Srodrigc		if (authcnt == 0 && np->auth == -1)
1025183008Srodrigc			np->auth = AUTH_SYS;
1026183008Srodrigc		if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS))
10279336Sdfr			np->stat = EAUTH;
10289336Sdfr		return (1);
10299336Sdfr	};
10309336Sdfr	return (0);
10311558Srgrimes}
10321558Srgrimes
1033203461Sdelphijstatic void
1034203461Sdelphijusage(void)
10351558Srgrimes{
103637427Scharnier	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1037192578Srwatson"usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]",
1038141611Sru"                 [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]",
1039141611Sru"                 [-r readsize] [-t timeout] [-w writesize] [-x retrans]",
1040141611Sru"                 rhost:path node");
10411558Srgrimes	exit(1);
10421558Srgrimes}
1043