mount_nfs.c revision 164457
1/*
2 * Copyright (c) 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if 0
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1992, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)mount_nfs.c	8.11 (Berkeley) 5/4/95";
42#endif /* not lint */
43#endif
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: head/sbin/mount_nfs/mount_nfs.c 164457 2006-11-21 01:53:18Z rodrigc $");
46
47#include <sys/param.h>
48#include <sys/mount.h>
49#include <sys/socket.h>
50#include <sys/stat.h>
51#include <sys/syslog.h>
52#include <sys/uio.h>
53
54#include <rpc/rpc.h>
55#include <rpc/pmap_clnt.h>
56#include <rpc/pmap_prot.h>
57
58#include <nfs/rpcv2.h>
59#include <nfs/nfsproto.h>
60#include <nfsclient/nfs.h>
61#include <nfsclient/nfsargs.h>
62
63#include <arpa/inet.h>
64
65#include <ctype.h>
66#include <err.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <netdb.h>
70#include <stdio.h>
71#include <stdlib.h>
72#include <string.h>
73#include <strings.h>
74#include <sysexits.h>
75#include <unistd.h>
76
77#include "mntopts.h"
78#include "mounttab.h"
79
80#define	ALTF_BG		0x1
81#define ALTF_NOCONN	0x2
82#define ALTF_DUMBTIMR	0x4
83#define ALTF_INTR	0x8
84#define ALTF_NFSV3	0x20
85#define ALTF_RDIRPLUS	0x40
86#define ALTF_MNTUDP	0x80
87#define ALTF_RESVPORT	0x100
88#define ALTF_SEQPACKET	0x200
89#define ALTF_SOFT	0x800
90#define ALTF_TCP	0x1000
91#define ALTF_PORT	0x2000
92#define ALTF_NFSV2	0x4000
93#define ALTF_ACREGMIN	0x8000
94#define ALTF_ACREGMAX	0x10000
95#define ALTF_ACDIRMIN	0x20000
96#define ALTF_ACDIRMAX	0x40000
97#define ALTF_NOLOCKD	0x80000
98#define ALTF_NOINET4	0x100000
99#define ALTF_NOINET6	0x200000
100
101struct mntopt mopts[] = {
102	MOPT_STDOPTS,
103	MOPT_FORCE,
104	MOPT_UPDATE,
105	MOPT_ASYNC,
106	{ "bg", 0, ALTF_BG, 1 },
107	{ "conn", 1, ALTF_NOCONN, 1 },
108	{ "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
109	{ "intr", 0, ALTF_INTR, 1 },
110	{ "nfsv3", 0, ALTF_NFSV3, 1 },
111	{ "rdirplus", 0, ALTF_RDIRPLUS, 1 },
112	{ "mntudp", 0, ALTF_MNTUDP, 1 },
113	{ "resvport", 0, ALTF_RESVPORT, 1 },
114	{ "soft", 0, ALTF_SOFT, 1 },
115	{ "tcp", 0, ALTF_TCP, 1 },
116	{ "port=", 0, ALTF_PORT, 1 },
117	{ "nfsv2", 0, ALTF_NFSV2, 1 },
118	{ "acregmin=", 0, ALTF_ACREGMIN, 1 },
119	{ "acregmax=", 0, ALTF_ACREGMAX, 1 },
120	{ "acdirmin=", 0, ALTF_ACDIRMIN, 1 },
121	{ "acdirmax=", 0, ALTF_ACDIRMAX, 1 },
122	{ "lockd", 1, ALTF_NOLOCKD, 1 },
123	{ "inet4", 1, ALTF_NOINET4, 1 },
124	{ "inet6", 1, ALTF_NOINET6, 1 },
125	MOPT_END
126};
127
128struct nfs_args nfsdefargs = {
129	NFS_ARGSVERSION,
130	NULL,
131	sizeof (struct sockaddr_in),
132	SOCK_DGRAM,
133	0,
134	NULL,
135	0,
136	NFSMNT_RESVPORT,
137	NFS_WSIZE,
138	NFS_RSIZE,
139	NFS_READDIRSIZE,
140	10,
141	NFS_RETRANS,
142	NFS_MAXGRPS,
143	NFS_DEFRAHEAD,
144	0,			/* was: NQ_DEFLEASE */
145	NFS_MAXDEADTHRESH,	/* was: NQ_DEADTHRESH */
146	NULL,
147	/* args version 4 */
148	NFS_MINATTRTIMO,
149	NFS_MAXATTRTIMO,
150	NFS_MINDIRATTRTIMO,
151	NFS_MAXDIRATTRTIMO,
152};
153
154/* Table for af,sotype -> netid conversions. */
155struct nc_protos {
156	char *netid;
157	int af;
158	int sotype;
159} nc_protos[] = {
160	{"udp",		AF_INET,	SOCK_DGRAM},
161	{"tcp",		AF_INET,	SOCK_STREAM},
162	{"udp6",	AF_INET6,	SOCK_DGRAM},
163	{"tcp6",	AF_INET6,	SOCK_STREAM},
164	{NULL}
165};
166
167struct nfhret {
168	u_long		stat;
169	long		vers;
170	long		auth;
171	long		fhsize;
172	u_char		nfh[NFSX_V3FHMAX];
173};
174#define	BGRND	1
175#define	ISBGRND	2
176#define	OF_NOINET4	4
177#define	OF_NOINET6	8
178int retrycnt = -1;
179int opflags = 0;
180int nfsproto = IPPROTO_UDP;
181int mnttcp_ok = 1;
182char *portspec = NULL;	/* Server nfs port; NULL means look up via rpcbind. */
183enum mountmode {
184	ANY,
185	V2,
186	V3
187} mountmode = ANY;
188
189/* Return codes for nfs_tryproto. */
190enum tryret {
191	TRYRET_SUCCESS,
192	TRYRET_TIMEOUT,		/* No response received. */
193	TRYRET_REMOTEERR,	/* Error received from remote server. */
194	TRYRET_LOCALERR		/* Local failure. */
195};
196
197int	getnfsargs(char *, struct nfs_args *);
198/* void	set_rpc_maxgrouplist(int); */
199struct netconfig *getnetconf_cached(const char *netid);
200char	*netidbytype(int af, int sotype);
201void	usage(void) __dead2;
202int	xdr_dir(XDR *, char *);
203int	xdr_fh(XDR *, struct nfhret *);
204enum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai,
205    char *hostp, char *spec, char **errstr);
206enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr);
207
208/*
209 * Used to set mount flags with getmntopts.  Call with dir=TRUE to
210 * initialize altflags from the current mount flags.  Call with
211 * dir=FALSE to update mount flags with the new value of altflags after
212 * the call to getmntopts.
213 */
214static void
215set_flags(int* altflags, int* nfsflags, int dir)
216{
217#define F2(af, nf)					\
218	if (dir) {					\
219		if (*nfsflags & NFSMNT_##nf)		\
220			*altflags |= ALTF_##af;		\
221		else					\
222			*altflags &= ~ALTF_##af;	\
223	} else {					\
224		if (*altflags & ALTF_##af)		\
225			*nfsflags |= NFSMNT_##nf;	\
226		else					\
227			*nfsflags &= ~NFSMNT_##nf;	\
228	}
229#define F(f)	F2(f,f)
230
231	F(NOCONN);
232	F(DUMBTIMR);
233	F2(INTR, INT);
234	F(RDIRPLUS);
235	F(RESVPORT);
236	F(SOFT);
237	F(ACREGMIN);
238	F(ACREGMAX);
239	F(ACDIRMIN);
240	F(ACDIRMAX);
241	F(NOLOCKD);
242
243#undef F
244#undef F2
245}
246
247int
248main(int argc, char *argv[])
249{
250	int c;
251	struct nfs_args *nfsargsp;
252	struct nfs_args nfsargs;
253	struct iovec *iov;
254	int mntflags, altflags, num;
255	int iovlen;
256	char *name, *p, *spec;
257	char mntpath[MAXPATHLEN];
258
259	mntflags = 0;
260	altflags = 0;
261	nfsargs = nfsdefargs;
262	nfsargsp = &nfsargs;
263	iov = NULL;
264	iovlen = 0;
265
266	while ((c = getopt(argc, argv,
267	    "23a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1)
268		switch (c) {
269		case '2':
270			mountmode = V2;
271			break;
272		case '3':
273			mountmode = V3;
274			break;
275		case 'a':
276			num = strtol(optarg, &p, 10);
277			if (*p || num < 0)
278				errx(1, "illegal -a value -- %s", optarg);
279			nfsargsp->readahead = num;
280			nfsargsp->flags |= NFSMNT_READAHEAD;
281			break;
282		case 'b':
283			opflags |= BGRND;
284			break;
285		case 'c':
286			nfsargsp->flags |= NFSMNT_NOCONN;
287			break;
288		case 'D':
289			num = strtol(optarg, &p, 10);
290			if (*p || num <= 0)
291				errx(1, "illegal -D value -- %s", optarg);
292			nfsargsp->deadthresh = num;
293			nfsargsp->flags |= NFSMNT_DEADTHRESH;
294			break;
295		case 'd':
296			nfsargsp->flags |= NFSMNT_DUMBTIMR;
297			break;
298#if 0 /* XXXX */
299		case 'g':
300			num = strtol(optarg, &p, 10);
301			if (*p || num <= 0)
302				errx(1, "illegal -g value -- %s", optarg);
303			set_rpc_maxgrouplist(num);
304			nfsargsp->maxgrouplist = num;
305			nfsargsp->flags |= NFSMNT_MAXGRPS;
306			break;
307#endif
308		case 'I':
309			num = strtol(optarg, &p, 10);
310			if (*p || num <= 0)
311				errx(1, "illegal -I value -- %s", optarg);
312			nfsargsp->readdirsize = num;
313			nfsargsp->flags |= NFSMNT_READDIRSIZE;
314			break;
315		case 'i':
316			nfsargsp->flags |= NFSMNT_INT;
317			break;
318		case 'L':
319			nfsargsp->flags |= NFSMNT_NOLOCKD;
320			break;
321		case 'l':
322			nfsargsp->flags |= NFSMNT_RDIRPLUS;
323			break;
324		case 'N':
325			nfsargsp->flags &= ~NFSMNT_RESVPORT;
326			break;
327		case 'o':
328			altflags = 0;
329			set_flags(&altflags, &nfsargsp->flags, TRUE);
330			if (mountmode == V2)
331				altflags |= ALTF_NFSV2;
332			else if (mountmode == V3)
333				altflags |= ALTF_NFSV3;
334			getmntopts(optarg, mopts, &mntflags, &altflags);
335			set_flags(&altflags, &nfsargsp->flags, FALSE);
336			/*
337			 * Handle altflags which don't map directly to
338			 * mount flags.
339			 */
340			if (altflags & ALTF_BG)
341				opflags |= BGRND;
342			if (altflags & ALTF_NOINET4)
343				opflags |= OF_NOINET4;
344			if (altflags & ALTF_NOINET6)
345				opflags |= OF_NOINET6;
346			if (altflags & ALTF_MNTUDP)
347				mnttcp_ok = 0;
348			if (altflags & ALTF_TCP) {
349				nfsargsp->sotype = SOCK_STREAM;
350				nfsproto = IPPROTO_TCP;
351			}
352			if (altflags & ALTF_PORT) {
353				/*
354				 * XXX Converting from a string to an int
355				 * and back again is silly, and we should
356				 * allow /etc/services names.
357				 */
358				p = strstr(optarg, "port=");
359				if (p) {
360					asprintf(&portspec, "%d",
361					    atoi(p + 5));
362					if (portspec == NULL)
363						err(1, "asprintf");
364				}
365			}
366			mountmode = ANY;
367			if (altflags & ALTF_NFSV2)
368				mountmode = V2;
369			if (altflags & ALTF_NFSV3)
370				mountmode = V3;
371			if (altflags & ALTF_ACREGMIN) {
372				p = strstr(optarg, "acregmin=");
373				if (p)
374					nfsargsp->acregmin = atoi(p + 9);
375			}
376			if (altflags & ALTF_ACREGMAX) {
377				p = strstr(optarg, "acregmax=");
378				if (p)
379					nfsargsp->acregmax = atoi(p + 9);
380			}
381			if (altflags & ALTF_ACDIRMIN) {
382				p = strstr(optarg, "acdirmin=");
383				if (p)
384					nfsargsp->acdirmin = atoi(p + 9);
385			}
386			if (altflags & ALTF_ACDIRMAX) {
387				p = strstr(optarg, "acdirmax=");
388				if (p)
389					nfsargsp->acdirmax = atoi(p + 9);
390			}
391			break;
392		case 'P':
393			/* obsolete for NFSMNT_RESVPORT, now default */
394			break;
395		case 'R':
396			num = strtol(optarg, &p, 10);
397			if (*p || num < 0)
398				errx(1, "illegal -R value -- %s", optarg);
399			retrycnt = num;
400			break;
401		case 'r':
402			num = strtol(optarg, &p, 10);
403			if (*p || num <= 0)
404				errx(1, "illegal -r value -- %s", optarg);
405			nfsargsp->rsize = num;
406			nfsargsp->flags |= NFSMNT_RSIZE;
407			break;
408		case 's':
409			nfsargsp->flags |= NFSMNT_SOFT;
410			break;
411		case 'T':
412			nfsargsp->sotype = SOCK_STREAM;
413			nfsproto = IPPROTO_TCP;
414			break;
415		case 't':
416			num = strtol(optarg, &p, 10);
417			if (*p || num <= 0)
418				errx(1, "illegal -t value -- %s", optarg);
419			nfsargsp->timeo = num;
420			nfsargsp->flags |= NFSMNT_TIMEO;
421			break;
422		case 'w':
423			num = strtol(optarg, &p, 10);
424			if (*p || num <= 0)
425				errx(1, "illegal -w value -- %s", optarg);
426			nfsargsp->wsize = num;
427			nfsargsp->flags |= NFSMNT_WSIZE;
428			break;
429		case 'x':
430			num = strtol(optarg, &p, 10);
431			if (*p || num <= 0)
432				errx(1, "illegal -x value -- %s", optarg);
433			nfsargsp->retrans = num;
434			nfsargsp->flags |= NFSMNT_RETRANS;
435			break;
436		case 'U':
437			mnttcp_ok = 0;
438			break;
439		default:
440			usage();
441			break;
442		}
443	argc -= optind;
444	argv += optind;
445
446	if (argc != 2) {
447		usage();
448		/* NOTREACHED */
449	}
450
451	spec = *argv++;
452	name = *argv;
453
454	if (retrycnt == -1)
455		/* The default is to keep retrying forever. */
456		retrycnt = 0;
457	if (!getnfsargs(spec, nfsargsp))
458		exit(1);
459
460	/* resolve the mountpoint with realpath(3) */
461	(void)checkpath(name, mntpath);
462
463	build_iovec(&iov, &iovlen, "nfs_args", nfsargsp, sizeof(*nfsargsp));
464	build_iovec(&iov, &iovlen, "fstype", "nfs", (size_t)-1);
465	build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
466
467	if (nmount(iov, iovlen, mntflags))
468		err(1, "%s", mntpath);
469
470	exit(0);
471}
472
473int
474getnfsargs(char *spec, struct nfs_args *nfsargsp)
475{
476	struct addrinfo hints, *ai_nfs, *ai;
477	enum tryret ret;
478	int ecode, speclen, remoteerr;
479	char *hostp, *delimp, *errstr;
480	size_t len;
481	static char nam[MNAMELEN + 1];
482
483	if ((delimp = strrchr(spec, ':')) != NULL) {
484		hostp = spec;
485		spec = delimp + 1;
486	} else if ((delimp = strrchr(spec, '@')) != NULL) {
487		warnx("path@server syntax is deprecated, use server:path");
488		hostp = delimp + 1;
489	} else {
490		warnx("no <host>:<dirpath> nfs-name");
491		return (0);
492	}
493	*delimp = '\0';
494
495	/*
496	 * If there has been a trailing slash at mounttime it seems
497	 * that some mountd implementations fail to remove the mount
498	 * entries from their mountlist while unmounting.
499	 */
500	for (speclen = strlen(spec);
501		speclen > 1 && spec[speclen - 1] == '/';
502		speclen--)
503		spec[speclen - 1] = '\0';
504	if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
505		warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
506		return (0);
507	}
508	/* Make both '@' and ':' notations equal */
509	if (*hostp != '\0') {
510		len = strlen(hostp);
511		memmove(nam, hostp, len);
512		nam[len] = ':';
513		memmove(nam + len + 1, spec, speclen);
514		nam[len + speclen + 1] = '\0';
515	}
516
517	/*
518	 * Handle an internet host address.
519	 */
520	memset(&hints, 0, sizeof hints);
521	hints.ai_flags = AI_NUMERICHOST;
522	hints.ai_socktype = nfsargsp->sotype;
523	if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
524		hints.ai_flags = 0;
525		if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
526		    != 0) {
527			if (portspec == NULL)
528				errx(1, "%s: %s", hostp, gai_strerror(ecode));
529			else
530				errx(1, "%s:%s: %s", hostp, portspec,
531				    gai_strerror(ecode));
532			return (0);
533		}
534	}
535
536	ret = TRYRET_LOCALERR;
537	for (;;) {
538		/*
539		 * Try each entry returned by getaddrinfo(). Note the
540		 * occurence of remote errors by setting `remoteerr'.
541		 */
542		remoteerr = 0;
543		for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
544			if ((ai->ai_family == AF_INET6) &&
545			    (opflags & OF_NOINET6))
546				continue;
547			if ((ai->ai_family == AF_INET) &&
548			    (opflags & OF_NOINET4))
549				continue;
550			ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr);
551			if (ret == TRYRET_SUCCESS)
552				break;
553			if (ret != TRYRET_LOCALERR)
554				remoteerr = 1;
555			if ((opflags & ISBGRND) == 0)
556				fprintf(stderr, "%s\n", errstr);
557		}
558		if (ret == TRYRET_SUCCESS)
559			break;
560
561		/* Exit if all errors were local. */
562		if (!remoteerr)
563			exit(1);
564
565		/*
566		 * If retrycnt == 0, we are to keep retrying forever.
567		 * Otherwise decrement it, and exit if it hits zero.
568		 */
569		if (retrycnt != 0 && --retrycnt == 0)
570			exit(1);
571
572		if ((opflags & (BGRND | ISBGRND)) == BGRND) {
573			warnx("Cannot immediately mount %s:%s, backgrounding",
574			    hostp, spec);
575			opflags |= ISBGRND;
576			if (daemon(0, 0) != 0)
577				err(1, "daemon");
578		}
579		sleep(60);
580	}
581	freeaddrinfo(ai_nfs);
582	nfsargsp->hostname = nam;
583	/* Add mounted file system to PATH_MOUNTTAB */
584	if (!add_mtab(hostp, spec))
585		warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
586	return (1);
587}
588
589/*
590 * Try to set up the NFS arguments according to the address
591 * family, protocol (and possibly port) specified in `ai'.
592 *
593 * Returns TRYRET_SUCCESS if successful, or:
594 *   TRYRET_TIMEOUT		The server did not respond.
595 *   TRYRET_REMOTEERR		The server reported an error.
596 *   TRYRET_LOCALERR		Local failure.
597 *
598 * In all error cases, *errstr will be set to a statically-allocated string
599 * describing the error.
600 */
601enum tryret
602nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp,
603    char *spec, char **errstr)
604{
605	static char errbuf[256];
606	struct sockaddr_storage nfs_ss;
607	struct netbuf nfs_nb;
608	struct nfhret nfhret;
609	struct timeval try;
610	struct rpc_err rpcerr;
611	CLIENT *clp;
612	struct netconfig *nconf, *nconf_mnt;
613	char *netid, *netid_mnt;
614	int doconnect, nfsvers, mntvers;
615	enum clnt_stat stat;
616	enum mountmode trymntmode;
617
618	trymntmode = mountmode;
619	errbuf[0] = '\0';
620	*errstr = errbuf;
621
622	if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) {
623		snprintf(errbuf, sizeof errbuf,
624		    "af %d sotype %d not supported", ai->ai_family,
625		    nfsargsp->sotype);
626		return (TRYRET_LOCALERR);
627	}
628	if ((nconf = getnetconf_cached(netid)) == NULL) {
629		snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
630		return (TRYRET_LOCALERR);
631	}
632	/* The RPCPROG_MNT netid may be different. */
633	if (mnttcp_ok) {
634		netid_mnt = netid;
635		nconf_mnt = nconf;
636	} else {
637		if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM))
638		     == NULL) {
639			snprintf(errbuf, sizeof errbuf,
640			    "af %d sotype SOCK_DGRAM not supported",
641			     ai->ai_family);
642			return (TRYRET_LOCALERR);
643		}
644		if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) {
645			snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt,
646			    nc_sperror());
647			return (TRYRET_LOCALERR);
648		}
649	}
650
651tryagain:
652	if (trymntmode == V2) {
653		nfsvers = 2;
654		mntvers = 1;
655	} else {
656		nfsvers = 3;
657		mntvers = 3;
658	}
659
660	if (portspec != NULL) {
661		/* `ai' contains the complete nfsd sockaddr. */
662		nfs_nb.buf = ai->ai_addr;
663		nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
664	} else {
665		/* Ask the remote rpcbind. */
666		nfs_nb.buf = &nfs_ss;
667		nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
668
669		if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
670		    hostp)) {
671			if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH &&
672			    trymntmode == ANY) {
673				trymntmode = V2;
674				goto tryagain;
675			}
676			snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
677			    netid, hostp, spec,
678			    clnt_spcreateerror("RPCPROG_NFS"));
679			return (returncode(rpc_createerr.cf_stat,
680			    &rpc_createerr.cf_error));
681		}
682	}
683
684	/* Check that the server (nfsd) responds on the port we have chosen. */
685	clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers,
686	    0, 0);
687	if (clp == NULL) {
688		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
689		    hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
690		return (returncode(rpc_createerr.cf_stat,
691		    &rpc_createerr.cf_error));
692	}
693	if (nfsargsp->sotype == SOCK_DGRAM &&
694	    !(nfsargsp->flags & NFSMNT_NOCONN)) {
695		/*
696		 * Use connect(), to match what the kernel does. This
697		 * catches cases where the server responds from the
698		 * wrong source address.
699		 */
700		doconnect = 1;
701		if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
702			clnt_destroy(clp);
703			snprintf(errbuf, sizeof errbuf,
704			    "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp,
705			    spec);
706			return (TRYRET_LOCALERR);
707		}
708	}
709
710	try.tv_sec = 10;
711	try.tv_usec = 0;
712	stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
713			 (xdrproc_t)xdr_void, NULL,
714	    try);
715	if (stat != RPC_SUCCESS) {
716		if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
717			clnt_destroy(clp);
718			trymntmode = V2;
719			goto tryagain;
720		}
721		clnt_geterr(clp, &rpcerr);
722		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
723		    hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
724		clnt_destroy(clp);
725		return (returncode(stat, &rpcerr));
726	}
727	clnt_destroy(clp);
728
729	/* Send the RPCMNT_MOUNT RPC to get the root filehandle. */
730	try.tv_sec = 10;
731	try.tv_usec = 0;
732	clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt);
733	if (clp == NULL) {
734		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
735		    hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
736		return (returncode(rpc_createerr.cf_stat,
737		    &rpc_createerr.cf_error));
738	}
739	clp->cl_auth = authsys_create_default();
740	nfhret.auth = RPCAUTH_UNIX;
741	nfhret.vers = mntvers;
742	stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec,
743			 (xdrproc_t)xdr_fh, &nfhret,
744	    try);
745	auth_destroy(clp->cl_auth);
746	if (stat != RPC_SUCCESS) {
747		if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
748			clnt_destroy(clp);
749			trymntmode = V2;
750			goto tryagain;
751		}
752		clnt_geterr(clp, &rpcerr);
753		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
754		    hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
755		clnt_destroy(clp);
756		return (returncode(stat, &rpcerr));
757	}
758	clnt_destroy(clp);
759
760	if (nfhret.stat != 0) {
761		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
762		    hostp, spec, strerror(nfhret.stat));
763		return (TRYRET_REMOTEERR);
764	}
765
766	/*
767	 * Store the filehandle and server address in nfsargsp, making
768	 * sure to copy any locally allocated structures.
769	 */
770	nfsargsp->addrlen = nfs_nb.len;
771	nfsargsp->addr = malloc(nfsargsp->addrlen);
772	nfsargsp->fhsize = nfhret.fhsize;
773	nfsargsp->fh = malloc(nfsargsp->fhsize);
774	if (nfsargsp->addr == NULL || nfsargsp->fh == NULL)
775		err(1, "malloc");
776	bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen);
777	bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize);
778
779	if (nfsvers == 3)
780		nfsargsp->flags |= NFSMNT_NFSV3;
781	else
782		nfsargsp->flags &= ~NFSMNT_NFSV3;
783
784	return (TRYRET_SUCCESS);
785}
786
787
788/*
789 * Catagorise a RPC return status and error into an `enum tryret'
790 * return code.
791 */
792enum tryret
793returncode(enum clnt_stat stat, struct rpc_err *rpcerr)
794{
795	switch (stat) {
796	case RPC_TIMEDOUT:
797		return (TRYRET_TIMEOUT);
798	case RPC_PMAPFAILURE:
799	case RPC_PROGNOTREGISTERED:
800	case RPC_PROGVERSMISMATCH:
801	/* XXX, these can be local or remote. */
802	case RPC_CANTSEND:
803	case RPC_CANTRECV:
804		return (TRYRET_REMOTEERR);
805	case RPC_SYSTEMERROR:
806		switch (rpcerr->re_errno) {
807		case ETIMEDOUT:
808			return (TRYRET_TIMEOUT);
809		case ENOMEM:
810			break;
811		default:
812			return (TRYRET_REMOTEERR);
813		}
814		/* FALLTHROUGH */
815	default:
816		break;
817	}
818	return (TRYRET_LOCALERR);
819}
820
821/*
822 * Look up a netid based on an address family and socket type.
823 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
824 *
825 * XXX there should be a library function for this.
826 */
827char *
828netidbytype(int af, int sotype)
829{
830	struct nc_protos *p;
831
832	for (p = nc_protos; p->netid != NULL; p++) {
833		if (af != p->af || sotype != p->sotype)
834			continue;
835		return (p->netid);
836	}
837	return (NULL);
838}
839
840/*
841 * Look up a netconfig entry based on a netid, and cache the result so
842 * that we don't need to remember to call freenetconfigent().
843 *
844 * Otherwise it behaves just like getnetconfigent(), so nc_*error()
845 * work on failure.
846 */
847struct netconfig *
848getnetconf_cached(const char *netid)
849{
850	static struct nc_entry {
851		struct netconfig *nconf;
852		struct nc_entry *next;
853	} *head;
854	struct nc_entry *p;
855	struct netconfig *nconf;
856
857	for (p = head; p != NULL; p = p->next)
858		if (strcmp(netid, p->nconf->nc_netid) == 0)
859			return (p->nconf);
860
861	if ((nconf = getnetconfigent(netid)) == NULL)
862		return (NULL);
863	if ((p = malloc(sizeof(*p))) == NULL)
864		err(1, "malloc");
865	p->nconf = nconf;
866	p->next = head;
867	head = p;
868
869	return (p->nconf);
870}
871
872/*
873 * xdr routines for mount rpc's
874 */
875int
876xdr_dir(XDR *xdrsp, char *dirp)
877{
878	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
879}
880
881int
882xdr_fh(XDR *xdrsp, struct nfhret *np)
883{
884	int i;
885	long auth, authcnt, authfnd = 0;
886
887	if (!xdr_u_long(xdrsp, &np->stat))
888		return (0);
889	if (np->stat)
890		return (1);
891	switch (np->vers) {
892	case 1:
893		np->fhsize = NFSX_V2FH;
894		return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
895	case 3:
896		if (!xdr_long(xdrsp, &np->fhsize))
897			return (0);
898		if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
899			return (0);
900		if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
901			return (0);
902		if (!xdr_long(xdrsp, &authcnt))
903			return (0);
904		for (i = 0; i < authcnt; i++) {
905			if (!xdr_long(xdrsp, &auth))
906				return (0);
907			if (auth == np->auth)
908				authfnd++;
909		}
910		/*
911		 * Some servers, such as DEC's OSF/1 return a nil authenticator
912		 * list to indicate RPCAUTH_UNIX.
913		 */
914		if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
915			np->stat = EAUTH;
916		return (1);
917	};
918	return (0);
919}
920
921void
922usage()
923{
924	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
925"usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]",
926"                 [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]",
927"                 [-r readsize] [-t timeout] [-w writesize] [-x retrans]",
928"                 rhost:path node");
929	exit(1);
930}
931