mount_nfs.c revision 183008
1/*
2 * copyright (c) 2003
3 * the regents of the university of michigan
4 * all rights reserved
5 *
6 * permission is granted to use, copy, create derivative works and redistribute
7 * this software and such derivative works for any purpose, so long as the name
8 * of the university of michigan is not used in any advertising or publicity
9 * pertaining to the use or distribution of this software without specific,
10 * written prior authorization.  if the above copyright notice or any other
11 * identification of the university of michigan is included in any copy of any
12 * portion of this software, then the disclaimer below must also be included.
13 *
14 * this software is provided as is, without representation from the university
15 * of michigan as to its fitness for any purpose, and without warranty by the
16 * university of michigan of any kind, either express or implied, including
17 * without limitation the implied warranties of merchantability and fitness for
18 * a particular purpose. the regents of the university of michigan shall not be
19 * liable for any damages, including special, indirect, incidental, or
20 * consequential damages, with respect to any claim arising out of or in
21 * connection with the use of the software, even if it has been or is hereafter
22 * advised of the possibility of such damages.
23 */
24
25/*
26 * Copyright (c) 1992, 1993, 1994
27 *	The Regents of the University of California.  All rights reserved.
28 *
29 * This code is derived from software contributed to Berkeley by
30 * Rick Macklem at The University of Guelph.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 *    notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 * 4. Neither the name of the University nor the names of its contributors
41 *    may be used to endorse or promote products derived from this software
42 *    without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57#if 0
58#ifndef lint
59static const char copyright[] =
60"@(#) Copyright (c) 1992, 1993, 1994\n\
61	The Regents of the University of California.  All rights reserved.\n";
62#endif /* not lint */
63
64#ifndef lint
65static char sccsid[] = "@(#)mount_nfs.c	8.11 (Berkeley) 5/4/95";
66#endif /* not lint */
67#endif
68#include <sys/cdefs.h>
69__FBSDID("$FreeBSD: head/sbin/mount_nfs/mount_nfs.c 183008 2008-09-13 20:22:46Z rodrigc $");
70
71#include <sys/param.h>
72#include <sys/mount.h>
73#include <sys/socket.h>
74#include <sys/stat.h>
75#include <sys/syslog.h>
76#include <sys/uio.h>
77
78#include <rpc/rpc.h>
79#include <rpc/pmap_clnt.h>
80#include <rpc/pmap_prot.h>
81
82#include <nfs/rpcv2.h>
83#include <nfs/nfsproto.h>
84#include <nfsclient/nfs.h>
85
86#include <arpa/inet.h>
87
88#include <ctype.h>
89#include <err.h>
90#include <errno.h>
91#include <fcntl.h>
92#include <netdb.h>
93#include <stdio.h>
94#include <stdlib.h>
95#include <string.h>
96#include <strings.h>
97#include <sysexits.h>
98#include <unistd.h>
99
100#include "mntopts.h"
101#include "mounttab.h"
102
103/* Table for af,sotype -> netid conversions. */
104struct nc_protos {
105	const char *netid;
106	int af;
107	int sotype;
108} nc_protos[] = {
109	{"udp",		AF_INET,	SOCK_DGRAM},
110	{"tcp",		AF_INET,	SOCK_STREAM},
111	{"udp6",	AF_INET6,	SOCK_DGRAM},
112	{"tcp6",	AF_INET6,	SOCK_STREAM},
113	{NULL,		0,		0}
114};
115
116struct nfhret {
117	u_long		stat;
118	long		vers;
119	long		auth;
120	long		fhsize;
121	u_char		nfh[NFSX_V3FHMAX];
122};
123#define	BGRND	1
124#define	ISBGRND	2
125#define	OF_NOINET4	4
126#define	OF_NOINET6	8
127int retrycnt = -1;
128int opflags = 0;
129int nfsproto = IPPROTO_UDP;
130int mnttcp_ok = 1;
131int noconn = 0;
132char *portspec = NULL;	/* Server nfs port; NULL means look up via rpcbind. */
133struct sockaddr *addr;
134int addrlen = 0;
135u_char *fh = NULL;
136int fhsize = 0;
137
138enum mountmode {
139	ANY,
140	V2,
141	V3,
142	V4
143} mountmode = ANY;
144
145/* Return codes for nfs_tryproto. */
146enum tryret {
147	TRYRET_SUCCESS,
148	TRYRET_TIMEOUT,		/* No response received. */
149	TRYRET_REMOTEERR,	/* Error received from remote server. */
150	TRYRET_LOCALERR		/* Local failure. */
151};
152
153int	fallback_mount(struct iovec *iov, int iovlen, int mntflags);
154int	getnfsargs(char *, struct iovec **iov, int *iovlen);
155int	getnfs4args(char *, struct iovec **iov, int *iovlen);
156/* void	set_rpc_maxgrouplist(int); */
157struct netconfig *getnetconf_cached(const char *netid);
158const char	*netidbytype(int af, int sotype);
159void	usage(void) __dead2;
160int	xdr_dir(XDR *, char *);
161int	xdr_fh(XDR *, struct nfhret *);
162enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec,
163    char **errstr, struct iovec **iov, int *iovlen);
164enum tryret nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec,
165    char **errstr);
166enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr);
167
168int
169main(int argc, char *argv[])
170{
171	int c;
172	struct iovec *iov;
173	int mntflags, altflags, num;
174	int iovlen;
175	char *name, *p, *spec, *fstype;
176	char mntpath[MAXPATHLEN], errmsg[255];
177
178	mntflags = 0;
179	altflags = 0;
180	iov = NULL;
181	iovlen = 0;
182	memset(errmsg, 0, sizeof(errmsg));
183
184	fstype = strrchr(argv[0], '_');
185	if (fstype == NULL)
186		errx(EX_USAGE, "argv[0] must end in _fstype");
187
188	++fstype;
189
190	if (strcmp(fstype, "nfs4") == 0) {
191		nfsproto = IPPROTO_TCP;
192		portspec = "2049";
193		build_iovec(&iov, &iovlen, "tcp", NULL, 0);
194		mountmode = V4;
195	}
196
197	while ((c = getopt(argc, argv,
198	    "234a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1)
199		switch (c) {
200		case '2':
201			mountmode = V2;
202			break;
203		case '3':
204			mountmode = V3;
205			break;
206		case '4':
207			mountmode = V4;
208			fstype = "nfs4";
209			break;
210		case 'a':
211			printf("-a deprecated, use -o readhead=<value>\n");
212			build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1);
213			break;
214		case 'b':
215			opflags |= BGRND;
216			break;
217		case 'c':
218			printf("-c deprecated, use -o noconn\n");
219			build_iovec(&iov, &iovlen, "noconn", NULL, 0);
220			noconn = 1;
221			break;
222		case 'D':
223			printf("-D deprecated, use -o deadthresh=<value>\n");
224			build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1);
225			break;
226		case 'd':
227			printf("-d deprecated, use -o dumbtimer");
228			build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0);
229			break;
230		case 'g':
231			printf("-g deprecated, use -o maxgroups");
232			num = strtol(optarg, &p, 10);
233			if (*p || num <= 0)
234				errx(1, "illegal -g value -- %s", optarg);
235			//set_rpc_maxgrouplist(num);
236			build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1);
237			break;
238		case 'I':
239			printf("-I deprecated, use -o readdirsize=<value>\n");
240			build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1);
241			break;
242		case 'i':
243			printf("-i deprecated, use -o intr\n");
244			build_iovec(&iov, &iovlen, "intr", NULL, 0);
245			break;
246		case 'L':
247			printf("-i deprecated, use -o nolockd\n");
248			build_iovec(&iov, &iovlen, "nolockd", NULL, 0);
249			break;
250		case 'l':
251			printf("-l deprecated, -o rdirplus\n");
252			build_iovec(&iov, &iovlen, "rdirplus", NULL, 0);
253			break;
254		case 'N':
255			printf("-N deprecated, do not specify -o resvport\n");
256			break;
257		case 'o': {
258			int pass_flag_to_nmount;
259			char *opt = optarg;
260			while (opt) {
261				char *pval = NULL;
262				char *pnextopt = NULL;
263				char *val = "";
264				pass_flag_to_nmount = 1;
265				pval = strchr(opt, '=');
266				pnextopt = strchr(opt, ',');
267				if (pval != NULL) {
268					*pval = '\0';
269					val = pval + 1;
270				}
271				if (pnextopt) {
272					*pnextopt = '\0';
273					pnextopt++;
274				}
275				if (strcmp(opt, "bg") == 0) {
276					opflags |= BGRND;
277					pass_flag_to_nmount=0;
278				} else if (strcmp(opt, "fg") == 0) {
279					/* same as not specifying -o bg */
280					pass_flag_to_nmount=0;
281				} else if (strcmp(opt, "mntudp") == 0) {
282					mnttcp_ok = 0;
283					nfsproto = IPPROTO_UDP;
284				} else if (strcmp(opt, "udp") == 0) {
285					nfsproto = IPPROTO_UDP;
286				} else if (strcmp(opt, "tcp") == 0) {
287					nfsproto = IPPROTO_TCP;
288				} else if (strcmp(opt, "noinet4") == 0) {
289					pass_flag_to_nmount=0;
290					opflags |= OF_NOINET4;
291				} else if (strcmp(opt, "noinet6") == 0) {
292					pass_flag_to_nmount=0;
293					opflags |= OF_NOINET6;
294				} else if (strcmp(opt, "noconn") == 0) {
295					noconn = 1;
296				} else if (strcmp(opt, "nfsv2") == 0) {
297					pass_flag_to_nmount=0;
298					mountmode = V2;
299				} else if (strcmp(opt, "nfsv3") == 0) {
300					mountmode = V3;
301				} else if (strcmp(opt, "nfsv4") == 0) {
302					pass_flag_to_nmount=0;
303					mountmode = V4;
304					fstype = "nfs4";
305				} else if (strcmp(opt, "port") == 0) {
306					pass_flag_to_nmount=0;
307					asprintf(&portspec, "%d",
308					    atoi(val));
309					if (portspec == NULL)
310						err(1, "asprintf");
311				} else if (strcmp(opt, "retrycnt") == 0) {
312					pass_flag_to_nmount=0;
313					num = strtol(val, &p, 10);
314					if (*p || num < 0)
315						errx(1, "illegal retrycnt value -- %s", val);
316					retrycnt = num;
317				} else if (strcmp(opt, "maxgroups") == 0) {
318					num = strtol(val, &p, 10);
319					if (*p || num <= 0)
320						errx(1, "illegal maxgroups value -- %s", val);
321					//set_rpc_maxgrouplist(num);
322				}
323				if (pass_flag_to_nmount)
324					build_iovec(&iov, &iovlen, opt, val,
325					    strlen(val) + 1);
326				opt = pnextopt;
327			}
328			}
329			break;
330		case 'P':
331			/* obsolete for -o noresvport now default */
332			printf("-P deprecated, use -o noresvport\n");
333			build_iovec(&iov, &iovlen, "noresvport", NULL, 0);
334			break;
335		case 'R':
336			printf("-R deprecated, use -o retrycnt=<retrycnt>\n");
337			num = strtol(optarg, &p, 10);
338			if (*p || num < 0)
339				errx(1, "illegal -R value -- %s", optarg);
340			retrycnt = num;
341			break;
342		case 'r':
343			printf("-r deprecated, use -o rsize=<rsize>\n");
344			build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1);
345			break;
346		case 's':
347			printf("-s deprecated, use -o soft\n");
348			build_iovec(&iov, &iovlen, "soft", NULL, 0);
349			break;
350		case 'T':
351			nfsproto = IPPROTO_TCP;
352			printf("-T deprecated, use -o tcp\n");
353			break;
354		case 't':
355			printf("-t deprecated, use -o timeout=<value>\n");
356			build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1);
357			break;
358		case 'w':
359			printf("-w deprecated, use -o wsize=<value>\n");
360			build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1);
361			break;
362		case 'x':
363			printf("-x deprecated, use -o retrans=<value>\n");
364			build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1);
365			break;
366		case 'U':
367			printf("-U deprecated, use -o mntudp\n");
368			mnttcp_ok = 0;
369			nfsproto = IPPROTO_UDP;
370			build_iovec(&iov, &iovlen, "mntudp", NULL, 0);
371			break;
372		default:
373			usage();
374			break;
375		}
376	argc -= optind;
377	argv += optind;
378
379	if (argc != 2) {
380		usage();
381		/* NOTREACHED */
382	}
383
384	spec = *argv++;
385	name = *argv;
386
387	if (retrycnt == -1)
388		/* The default is to keep retrying forever. */
389		retrycnt = 0;
390
391	if (mountmode == V4) {
392		if (!getnfs4args(spec, &iov, &iovlen))
393			exit(1);
394	} else {
395		if (!getnfsargs(spec, &iov, &iovlen))
396			exit(1);
397	}
398
399	/* resolve the mountpoint with realpath(3) */
400	(void)checkpath(name, mntpath);
401
402	build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
403	build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
404	build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
405
406	if (nmount(iov, iovlen, mntflags)) {
407		if (errno != ENOENT
408		    || fallback_mount(iov, iovlen, mntflags))
409			err(1, "%s, %s", mntpath, errmsg);
410	}
411
412	exit(0);
413}
414
415static int
416findopt(struct iovec *iov, int iovlen, const char *name,
417    char **valuep, int *lenp)
418{
419	int i;
420
421	for (i = 0; i < iovlen/2; i++, iov += 2) {
422		if (strcmp(name, iov[0].iov_base) == 0) {
423			if (valuep)
424				*valuep = iov[1].iov_base;
425			if (lenp)
426				*lenp = iov[1].iov_len;
427			return (0);
428		}
429	}
430	return (ENOENT);
431}
432
433static void
434copyopt(struct iovec **newiov, int *newiovlen,
435    struct iovec *iov, int iovlen, const char *name)
436{
437	char *value;
438	int len;
439
440	if (findopt(iov, iovlen, name, &value, &len) == 0)
441		build_iovec(newiov, newiovlen, name, value, len);
442}
443
444int
445fallback_mount(struct iovec *iov, int iovlen, int mntflags)
446{
447	struct nfs_args args = {
448	    .version = NFS_ARGSVERSION,
449	    .addr = NULL,
450	    .addrlen = sizeof (struct sockaddr_in),
451	    .sotype = SOCK_STREAM,
452	    .proto = 0,
453	    .fh = NULL,
454	    .fhsize = 0,
455	    .flags = NFSMNT_RESVPORT,
456	    .wsize = NFS_WSIZE,
457	    .rsize = NFS_RSIZE,
458	    .readdirsize = NFS_READDIRSIZE,
459	    .timeo = 10,
460	    .retrans = NFS_RETRANS,
461	    .maxgrouplist = NFS_MAXGRPS,
462	    .readahead = NFS_DEFRAHEAD,
463	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
464	    .deadthresh = NFS_MAXDEADTHRESH,	/* was: NQ_DEADTHRESH */
465	    .hostname = NULL,
466	    /* args version 4 */
467	    .acregmin = NFS_MINATTRTIMO,
468	    .acregmax = NFS_MAXATTRTIMO,
469	    .acdirmin = NFS_MINDIRATTRTIMO,
470	    .acdirmax = NFS_MAXDIRATTRTIMO,
471	};
472	int ret;
473	char *opt;
474	struct iovec *newiov;
475	int newiovlen;
476
477	if (findopt(iov, iovlen, "dumbtimer", NULL, NULL) == 0)
478		args.flags |= NFSMNT_DUMBTIMR;
479	if (findopt(iov, iovlen, "noconn", NULL, NULL) == 0)
480		args.flags |= NFSMNT_NOCONN;
481	if (findopt(iov, iovlen, "conn", NULL, NULL) == 0)
482		args.flags |= NFSMNT_NOCONN;
483	if (findopt(iov, iovlen, "nolockd", NULL, NULL) == 0)
484		args.flags |= NFSMNT_NOLOCKD;
485	if (findopt(iov, iovlen, "lockd", NULL, NULL) == 0)
486		args.flags &= ~NFSMNT_NOLOCKD;
487	if (findopt(iov, iovlen, "intr", NULL, NULL) == 0)
488		args.flags |= NFSMNT_INT;
489	if (findopt(iov, iovlen, "rdirplus", NULL, NULL) == 0)
490		args.flags |= NFSMNT_RDIRPLUS;
491	if (findopt(iov, iovlen, "resvport", NULL, NULL) == 0)
492		args.flags |= NFSMNT_RESVPORT;
493	if (findopt(iov, iovlen, "noresvport", NULL, NULL) == 0)
494		args.flags &= ~NFSMNT_RESVPORT;
495	if (findopt(iov, iovlen, "soft", NULL, NULL) == 0)
496		args.flags |= NFSMNT_SOFT;
497	if (findopt(iov, iovlen, "hard", NULL, NULL) == 0)
498		args.flags &= ~NFSMNT_SOFT;
499	if (findopt(iov, iovlen, "mntudp", NULL, NULL) == 0)
500		args.sotype = SOCK_DGRAM;
501	if (findopt(iov, iovlen, "udp", NULL, NULL) == 0)
502		args.sotype = SOCK_DGRAM;
503	if (findopt(iov, iovlen, "tcp", NULL, NULL) == 0)
504		args.sotype = SOCK_STREAM;
505	if (findopt(iov, iovlen, "nfsv3", NULL, NULL) == 0)
506		args.flags |= NFSMNT_NFSV3;
507	if (findopt(iov, iovlen, "readdirsize", &opt, NULL) == 0) {
508		if (opt == NULL) {
509			errx(1, "illegal readdirsize");
510		}
511		ret = sscanf(opt, "%d", &args.readdirsize);
512		if (ret != 1 || args.readdirsize <= 0) {
513			errx(1, "illegal readdirsize: %s", opt);
514		}
515		args.flags |= NFSMNT_READDIRSIZE;
516	}
517	if (findopt(iov, iovlen, "readahead", &opt, NULL) == 0) {
518		if (opt == NULL) {
519			errx(1, "illegal readahead");
520		}
521		ret = sscanf(opt, "%d", &args.readahead);
522		if (ret != 1 || args.readahead <= 0) {
523			errx(1, "illegal readahead: %s", opt);
524		}
525		args.flags |= NFSMNT_READAHEAD;
526	}
527	if (findopt(iov, iovlen, "wsize", &opt, NULL) == 0) {
528		if (opt == NULL) {
529			errx(1, "illegal wsize");
530		}
531		ret = sscanf(opt, "%d", &args.wsize);
532		if (ret != 1 || args.wsize <= 0) {
533			errx(1, "illegal wsize: %s", opt);
534		}
535		args.flags |= NFSMNT_WSIZE;
536	}
537	if (findopt(iov, iovlen, "rsize", &opt, NULL) == 0) {
538		if (opt == NULL) {
539			errx(1, "illegal rsize");
540		}
541		ret = sscanf(opt, "%d", &args.rsize);
542		if (ret != 1 || args.rsize <= 0) {
543			errx(1, "illegal wsize: %s", opt);
544		}
545		args.flags |= NFSMNT_RSIZE;
546	}
547	if (findopt(iov, iovlen, "retrans", &opt, NULL) == 0) {
548		if (opt == NULL) {
549			errx(1, "illegal retrans");
550		}
551		ret = sscanf(opt, "%d", &args.retrans);
552		if (ret != 1 || args.retrans <= 0) {
553			errx(1, "illegal retrans: %s", opt);
554		}
555		args.flags |= NFSMNT_RETRANS;
556	}
557	if (findopt(iov, iovlen, "acregmin", &opt, NULL) == 0) {
558		ret = sscanf(opt, "%d", &args.acregmin);
559		if (ret != 1 || args.acregmin <= 0) {
560			errx(1, "illegal acregmin: %s", opt);
561		}
562	}
563	if (findopt(iov, iovlen, "acregmax", &opt, NULL) == 0) {
564		ret = sscanf(opt, "%d", &args.acregmax);
565		if (ret != 1 || args.acregmax <= 0) {
566			errx(1, "illegal acregmax: %s", opt);
567		}
568	}
569	if (findopt(iov, iovlen, "acdirmin", &opt, NULL) == 0) {
570		ret = sscanf(opt, "%d", &args.acdirmin);
571		if (ret != 1 || args.acdirmin <= 0) {
572			errx(1, "illegal acdirmin: %s", opt);
573		}
574	}
575	if (findopt(iov, iovlen, "acdirmax", &opt, NULL) == 0) {
576		ret = sscanf(opt, "%d", &args.acdirmax);
577		if (ret != 1 || args.acdirmax <= 0) {
578			errx(1, "illegal acdirmax: %s", opt);
579		}
580	}
581	if (findopt(iov, iovlen, "deadthresh", &opt, NULL) == 0) {
582		ret = sscanf(opt, "%d", &args.deadthresh);
583		if (ret != 1 || args.deadthresh <= 0) {
584			errx(1, "illegal deadthresh: %s", opt);
585		}
586		args.flags |= NFSMNT_DEADTHRESH;
587	}
588	if (findopt(iov, iovlen, "timeout", &opt, NULL) == 0) {
589		ret = sscanf(opt, "%d", &args.timeo);
590		if (ret != 1 || args.timeo <= 0) {
591			errx(1, "illegal timeout: %s", opt);
592		}
593		args.flags |= NFSMNT_TIMEO;
594	}
595	if (findopt(iov, iovlen, "maxgroups", &opt, NULL) == 0) {
596		ret = sscanf(opt, "%d", &args.maxgrouplist);
597		if (ret != 1 || args.timeo <= 0) {
598			errx(1, "illegal maxgroups: %s", opt);
599		}
600		args.flags |= NFSMNT_MAXGRPS;
601	}
602	if (findopt(iov, iovlen, "addr", &opt,
603		&args.addrlen) == 0) {
604		args.addr = (struct sockaddr *) opt;
605	}
606	if (findopt(iov, iovlen, "fh", &opt, &args.fhsize) == 0) {
607		args.fh = opt;
608	}
609	if (findopt(iov, iovlen, "hostname", &args.hostname,
610		NULL) == 0) {
611	}
612	if (args.hostname == NULL) {
613		errx(1, "Invalid hostname");
614	}
615
616	newiov = NULL;
617	newiovlen = 0;
618
619	build_iovec(&newiov, &newiovlen, "nfs_args", &args, sizeof(args));
620	copyopt(&newiov, &newiovlen, iov, iovlen, "fstype");
621	copyopt(&newiov, &newiovlen, iov, iovlen, "fspath");
622	copyopt(&newiov, &newiovlen, iov, iovlen, "errmsg");
623
624	return nmount(newiov, newiovlen, mntflags);
625}
626
627int
628getnfsargs(char *spec, struct iovec **iov, int *iovlen)
629{
630	struct addrinfo hints, *ai_nfs, *ai;
631	enum tryret ret;
632	int ecode, speclen, remoteerr;
633	char *hostp, *delimp, *errstr;
634	size_t len;
635	static char nam[MNAMELEN + 1];
636
637	if ((delimp = strrchr(spec, ':')) != NULL) {
638		hostp = spec;
639		spec = delimp + 1;
640	} else if ((delimp = strrchr(spec, '@')) != NULL) {
641		warnx("path@server syntax is deprecated, use server:path");
642		hostp = delimp + 1;
643	} else {
644		warnx("no <host>:<dirpath> nfs-name");
645		return (0);
646	}
647	*delimp = '\0';
648
649	/*
650	 * If there has been a trailing slash at mounttime it seems
651	 * that some mountd implementations fail to remove the mount
652	 * entries from their mountlist while unmounting.
653	 */
654	for (speclen = strlen(spec);
655		speclen > 1 && spec[speclen - 1] == '/';
656		speclen--)
657		spec[speclen - 1] = '\0';
658	if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
659		warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
660		return (0);
661	}
662	/* Make both '@' and ':' notations equal */
663	if (*hostp != '\0') {
664		len = strlen(hostp);
665		memmove(nam, hostp, len);
666		nam[len] = ':';
667		memmove(nam + len + 1, spec, speclen);
668		nam[len + speclen + 1] = '\0';
669	}
670
671	/*
672	 * Handle an internet host address.
673	 */
674	memset(&hints, 0, sizeof hints);
675	hints.ai_flags = AI_NUMERICHOST;
676	if (nfsproto == IPPROTO_TCP)
677		hints.ai_socktype = SOCK_STREAM;
678	else if (nfsproto == IPPROTO_UDP)
679		hints.ai_socktype = SOCK_DGRAM;
680
681	if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
682		hints.ai_flags = 0;
683		if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
684		    != 0) {
685			if (portspec == NULL)
686				errx(1, "%s: %s", hostp, gai_strerror(ecode));
687			else
688				errx(1, "%s:%s: %s", hostp, portspec,
689				    gai_strerror(ecode));
690			return (0);
691		}
692	}
693
694	ret = TRYRET_LOCALERR;
695	for (;;) {
696		/*
697		 * Try each entry returned by getaddrinfo(). Note the
698		 * occurence of remote errors by setting `remoteerr'.
699		 */
700		remoteerr = 0;
701		for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
702			if ((ai->ai_family == AF_INET6) &&
703			    (opflags & OF_NOINET6))
704				continue;
705			if ((ai->ai_family == AF_INET) &&
706			    (opflags & OF_NOINET4))
707				continue;
708			ret = nfs_tryproto(ai, hostp, spec, &errstr, iov,
709			    iovlen);
710			if (ret == TRYRET_SUCCESS)
711				break;
712			if (ret != TRYRET_LOCALERR)
713				remoteerr = 1;
714			if ((opflags & ISBGRND) == 0)
715				fprintf(stderr, "%s\n", errstr);
716		}
717		if (ret == TRYRET_SUCCESS)
718			break;
719
720		/* Exit if all errors were local. */
721		if (!remoteerr)
722			exit(1);
723
724		/*
725		 * If retrycnt == 0, we are to keep retrying forever.
726		 * Otherwise decrement it, and exit if it hits zero.
727		 */
728		if (retrycnt != 0 && --retrycnt == 0)
729			exit(1);
730
731		if ((opflags & (BGRND | ISBGRND)) == BGRND) {
732			warnx("Cannot immediately mount %s:%s, backgrounding",
733			    hostp, spec);
734			opflags |= ISBGRND;
735			if (daemon(0, 0) != 0)
736				err(1, "daemon");
737		}
738		sleep(60);
739	}
740	freeaddrinfo(ai_nfs);
741
742	build_iovec(iov, iovlen, "hostname", nam, (size_t)-1);
743	/* Add mounted file system to PATH_MOUNTTAB */
744	if (!add_mtab(hostp, spec))
745		warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
746	return (1);
747}
748
749
750int
751getnfs4args(char *spec, struct iovec **iov, int *iovlen)
752{
753	struct addrinfo hints, *ai_nfs, *ai;
754	enum tryret ret;
755	int ecode, speclen, remoteerr, sotype;
756	char *hostp, *delimp, *errstr;
757	size_t len;
758	static char nam[MNAMELEN + 1];
759
760	if (nfsproto == IPPROTO_TCP)
761		sotype = SOCK_STREAM;
762	else if (nfsproto == IPPROTO_UDP)
763		sotype = SOCK_DGRAM;
764
765
766	if ((delimp = strrchr(spec, ':')) != NULL) {
767		hostp = spec;
768		spec = delimp + 1;
769	} else if ((delimp = strrchr(spec, '@')) != NULL) {
770		warnx("path@server syntax is deprecated, use server:path");
771		hostp = delimp + 1;
772	} else {
773		warnx("no <host>:<dirpath> nfs-name");
774		return (0);
775	}
776	*delimp = '\0';
777
778	/*
779	 * If there has been a trailing slash at mounttime it seems
780	 * that some mountd implementations fail to remove the mount
781	 * entries from their mountlist while unmounting.
782	 */
783	for (speclen = strlen(spec);
784		speclen > 1 && spec[speclen - 1] == '/';
785		speclen--)
786		spec[speclen - 1] = '\0';
787	if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
788		warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
789		return (0);
790	}
791	/* Make both '@' and ':' notations equal */
792	if (*hostp != '\0') {
793		len = strlen(hostp);
794		memmove(nam, hostp, len);
795		nam[len] = ':';
796		memmove(nam + len + 1, spec, speclen);
797		nam[len + speclen + 1] = '\0';
798	}
799
800	/*
801	 * Handle an internet host address.
802	 */
803	memset(&hints, 0, sizeof hints);
804	hints.ai_flags = AI_NUMERICHOST;
805	hints.ai_socktype = sotype;
806	if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
807		hints.ai_flags = 0;
808		if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
809		    != 0) {
810			if (portspec == NULL)
811				errx(1, "%s: %s", hostp, gai_strerror(ecode));
812			else
813				errx(1, "%s:%s: %s", hostp, portspec,
814				    gai_strerror(ecode));
815			return (0);
816		}
817	}
818
819	ret = TRYRET_LOCALERR;
820	for (;;) {
821		/*
822		 * Try each entry returned by getaddrinfo(). Note the
823		 * occurence of remote errors by setting `remoteerr'.
824		 */
825		remoteerr = 0;
826		for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
827			if ((ai->ai_family == AF_INET6) &&
828			    (opflags & OF_NOINET6))
829				continue;
830			if ((ai->ai_family == AF_INET) &&
831			    (opflags & OF_NOINET4))
832				continue;
833			ret = nfs4_tryproto(ai, hostp, spec, &errstr);
834			if (ret == TRYRET_SUCCESS)
835				break;
836			if (ret != TRYRET_LOCALERR)
837				remoteerr = 1;
838			if ((opflags & ISBGRND) == 0)
839				fprintf(stderr, "%s\n", errstr);
840		}
841		if (ret == TRYRET_SUCCESS)
842			break;
843
844		/* Exit if all errors were local. */
845		if (!remoteerr)
846			exit(1);
847
848		/*
849		 * If retrycnt == 0, we are to keep retrying forever.
850		 * Otherwise decrement it, and exit if it hits zero.
851		 */
852		if (retrycnt != 0 && --retrycnt == 0)
853			exit(1);
854
855		if ((opflags & (BGRND | ISBGRND)) == BGRND) {
856			warnx("Cannot immediately mount %s:%s, backgrounding",
857			    hostp, spec);
858			opflags |= ISBGRND;
859			if (daemon(0, 0) != 0)
860				err(1, "daemon");
861		}
862		sleep(60);
863	}
864	freeaddrinfo(ai_nfs);
865	build_iovec(iov, iovlen, "hostname", nam, (size_t)-1);
866	/* Add mounted file system to PATH_MOUNTTAB */
867	if (!add_mtab(hostp, spec))
868		warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
869	return (1);
870}
871
872/*
873 * Try to set up the NFS arguments according to the address
874 * family, protocol (and possibly port) specified in `ai'.
875 *
876 * Returns TRYRET_SUCCESS if successful, or:
877 *   TRYRET_TIMEOUT		The server did not respond.
878 *   TRYRET_REMOTEERR		The server reported an error.
879 *   TRYRET_LOCALERR		Local failure.
880 *
881 * In all error cases, *errstr will be set to a statically-allocated string
882 * describing the error.
883 */
884enum tryret
885nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr,
886    struct iovec **iov, int *iovlen)
887{
888	static char errbuf[256];
889	struct sockaddr_storage nfs_ss;
890	struct netbuf nfs_nb;
891	struct nfhret nfhret;
892	struct timeval try;
893	struct rpc_err rpcerr;
894	CLIENT *clp;
895	struct netconfig *nconf, *nconf_mnt;
896	const char *netid, *netid_mnt;
897	int doconnect, nfsvers, mntvers, sotype;
898	enum clnt_stat stat;
899	enum mountmode trymntmode;
900
901	trymntmode = mountmode;
902	errbuf[0] = '\0';
903	*errstr = errbuf;
904
905	if (nfsproto == IPPROTO_TCP)
906		sotype = SOCK_STREAM;
907	else if (nfsproto == IPPROTO_UDP)
908		sotype = SOCK_DGRAM;
909
910	if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) {
911		snprintf(errbuf, sizeof errbuf,
912		    "af %d sotype %d not supported", ai->ai_family, sotype);
913		return (TRYRET_LOCALERR);
914	}
915	if ((nconf = getnetconf_cached(netid)) == NULL) {
916		snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
917		return (TRYRET_LOCALERR);
918	}
919	/* The RPCPROG_MNT netid may be different. */
920	if (mnttcp_ok) {
921		netid_mnt = netid;
922		nconf_mnt = nconf;
923	} else {
924		if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM))
925		     == NULL) {
926			snprintf(errbuf, sizeof errbuf,
927			    "af %d sotype SOCK_DGRAM not supported",
928			     ai->ai_family);
929			return (TRYRET_LOCALERR);
930		}
931		if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) {
932			snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt,
933			    nc_sperror());
934			return (TRYRET_LOCALERR);
935		}
936	}
937
938tryagain:
939	if (trymntmode == V2) {
940		nfsvers = 2;
941		mntvers = 1;
942	} else {
943		nfsvers = 3;
944		mntvers = 3;
945	}
946
947	if (portspec != NULL) {
948		/* `ai' contains the complete nfsd sockaddr. */
949		nfs_nb.buf = ai->ai_addr;
950		nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
951	} else {
952		/* Ask the remote rpcbind. */
953		nfs_nb.buf = &nfs_ss;
954		nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
955
956		if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
957		    hostp)) {
958			if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH &&
959			    trymntmode == ANY) {
960				trymntmode = V2;
961				goto tryagain;
962			}
963			snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
964			    netid, hostp, spec,
965			    clnt_spcreateerror("RPCPROG_NFS"));
966			return (returncode(rpc_createerr.cf_stat,
967			    &rpc_createerr.cf_error));
968		}
969	}
970
971	/* Check that the server (nfsd) responds on the port we have chosen. */
972	clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers,
973	    0, 0);
974	if (clp == NULL) {
975		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
976		    hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
977		return (returncode(rpc_createerr.cf_stat,
978		    &rpc_createerr.cf_error));
979	}
980	if (sotype == SOCK_DGRAM && noconn == 0) {
981		/*
982		 * Use connect(), to match what the kernel does. This
983		 * catches cases where the server responds from the
984		 * wrong source address.
985		 */
986		doconnect = 1;
987		if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
988			clnt_destroy(clp);
989			snprintf(errbuf, sizeof errbuf,
990			    "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp,
991			    spec);
992			return (TRYRET_LOCALERR);
993		}
994	}
995
996	try.tv_sec = 10;
997	try.tv_usec = 0;
998	stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
999			 (xdrproc_t)xdr_void, NULL,
1000	    try);
1001	if (stat != RPC_SUCCESS) {
1002		if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
1003			clnt_destroy(clp);
1004			trymntmode = V2;
1005			goto tryagain;
1006		}
1007		clnt_geterr(clp, &rpcerr);
1008		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
1009		    hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
1010		clnt_destroy(clp);
1011		return (returncode(stat, &rpcerr));
1012	}
1013	clnt_destroy(clp);
1014
1015	/* Send the RPCMNT_MOUNT RPC to get the root filehandle. */
1016	try.tv_sec = 10;
1017	try.tv_usec = 0;
1018	clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt);
1019	if (clp == NULL) {
1020		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
1021		    hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
1022		return (returncode(rpc_createerr.cf_stat,
1023		    &rpc_createerr.cf_error));
1024	}
1025	clp->cl_auth = authsys_create_default();
1026	nfhret.auth = -1;
1027	nfhret.vers = mntvers;
1028	stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec,
1029			 (xdrproc_t)xdr_fh, &nfhret,
1030	    try);
1031	auth_destroy(clp->cl_auth);
1032	if (stat != RPC_SUCCESS) {
1033		if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
1034			clnt_destroy(clp);
1035			trymntmode = V2;
1036			goto tryagain;
1037		}
1038		clnt_geterr(clp, &rpcerr);
1039		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
1040		    hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
1041		clnt_destroy(clp);
1042		return (returncode(stat, &rpcerr));
1043	}
1044	clnt_destroy(clp);
1045
1046	if (nfhret.stat != 0) {
1047		snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
1048		    hostp, spec, strerror(nfhret.stat));
1049		return (TRYRET_REMOTEERR);
1050	}
1051
1052	/*
1053	 * Store the filehandle and server address in nfsargsp, making
1054	 * sure to copy any locally allocated structures.
1055	 */
1056	addrlen = nfs_nb.len;
1057	addr = malloc(addrlen);
1058	fhsize = nfhret.fhsize;
1059	fh = malloc(fhsize);
1060	if (addr == NULL || fh == NULL)
1061		err(1, "malloc");
1062	bcopy(nfs_nb.buf, addr, addrlen);
1063	bcopy(nfhret.nfh, fh, fhsize);
1064
1065	build_iovec(iov, iovlen, "addr", addr, addrlen);
1066	build_iovec(iov, iovlen, "fh", fh, fhsize);
1067	if (nfsvers == 3)
1068		build_iovec(iov, iovlen, "nfsv3", NULL, 0);
1069
1070	return (TRYRET_SUCCESS);
1071}
1072
1073
1074/*
1075 * Try to set up the NFS arguments according to the address
1076 * family, protocol (and possibly port) specified in `ai'.
1077 *
1078 * Returns TRYRET_SUCCESS if successful, or:
1079 *   TRYRET_TIMEOUT		The server did not respond.
1080 *   TRYRET_REMOTEERR		The server reported an error.
1081 *   TRYRET_LOCALERR		Local failure.
1082 *
1083 * In all error cases, *errstr will be set to a statically-allocated string
1084 * describing the error.
1085 */
1086enum tryret
1087nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr)
1088{
1089	static char errbuf[256];
1090	struct sockaddr_storage nfs_ss;
1091	struct netbuf nfs_nb;
1092	struct netconfig *nconf;
1093	const char *netid;
1094	int nfsvers, sotype;
1095
1096	errbuf[0] = '\0';
1097	*errstr = errbuf;
1098
1099	if (nfsproto == IPPROTO_TCP)
1100		sotype = SOCK_STREAM;
1101	else if (nfsproto == IPPROTO_UDP)
1102		sotype = SOCK_DGRAM;
1103
1104	if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) {
1105		snprintf(errbuf, sizeof errbuf,
1106		    "af %d sotype %d not supported", ai->ai_family, sotype);
1107		return (TRYRET_LOCALERR);
1108	}
1109	if ((nconf = getnetconf_cached(netid)) == NULL) {
1110		snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
1111		return (TRYRET_LOCALERR);
1112	}
1113
1114	nfsvers = 4;
1115
1116	if (portspec != NULL && atoi(portspec) != 0) {
1117		/* `ai' contains the complete nfsd sockaddr. */
1118		nfs_nb.buf = ai->ai_addr;
1119		nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
1120	} else {
1121		/* Ask the remote rpcbind. */
1122		nfs_nb.buf = &nfs_ss;
1123		nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
1124
1125		if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
1126		    hostp)) {
1127			snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
1128			    netid, hostp, spec,
1129			    clnt_spcreateerror("RPCPROG_NFS"));
1130			return (returncode(rpc_createerr.cf_stat,
1131			    &rpc_createerr.cf_error));
1132		}
1133	}
1134
1135	/*
1136	 * Store the filehandle and server address in nfsargsp, making
1137	 * sure to copy any locally allocated structures.
1138	 */
1139	addrlen = nfs_nb.len;
1140	addr = malloc(addrlen);
1141
1142	if (addr == NULL)
1143		err(1, "malloc");
1144	bcopy(nfs_nb.buf, addr, addrlen);
1145
1146	return (TRYRET_SUCCESS);
1147}
1148
1149/*
1150 * Catagorise a RPC return status and error into an `enum tryret'
1151 * return code.
1152 */
1153enum tryret
1154returncode(enum clnt_stat stat, struct rpc_err *rpcerr)
1155{
1156	switch (stat) {
1157	case RPC_TIMEDOUT:
1158		return (TRYRET_TIMEOUT);
1159	case RPC_PMAPFAILURE:
1160	case RPC_PROGNOTREGISTERED:
1161	case RPC_PROGVERSMISMATCH:
1162	/* XXX, these can be local or remote. */
1163	case RPC_CANTSEND:
1164	case RPC_CANTRECV:
1165		return (TRYRET_REMOTEERR);
1166	case RPC_SYSTEMERROR:
1167		switch (rpcerr->re_errno) {
1168		case ETIMEDOUT:
1169			return (TRYRET_TIMEOUT);
1170		case ENOMEM:
1171			break;
1172		default:
1173			return (TRYRET_REMOTEERR);
1174		}
1175		/* FALLTHROUGH */
1176	default:
1177		break;
1178	}
1179	return (TRYRET_LOCALERR);
1180}
1181
1182/*
1183 * Look up a netid based on an address family and socket type.
1184 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
1185 *
1186 * XXX there should be a library function for this.
1187 */
1188const char *
1189netidbytype(int af, int sotype)
1190{
1191	struct nc_protos *p;
1192
1193	for (p = nc_protos; p->netid != NULL; p++) {
1194		if (af != p->af || sotype != p->sotype)
1195			continue;
1196		return (p->netid);
1197	}
1198	return (NULL);
1199}
1200
1201/*
1202 * Look up a netconfig entry based on a netid, and cache the result so
1203 * that we don't need to remember to call freenetconfigent().
1204 *
1205 * Otherwise it behaves just like getnetconfigent(), so nc_*error()
1206 * work on failure.
1207 */
1208struct netconfig *
1209getnetconf_cached(const char *netid)
1210{
1211	static struct nc_entry {
1212		struct netconfig *nconf;
1213		struct nc_entry *next;
1214	} *head;
1215	struct nc_entry *p;
1216	struct netconfig *nconf;
1217
1218	for (p = head; p != NULL; p = p->next)
1219		if (strcmp(netid, p->nconf->nc_netid) == 0)
1220			return (p->nconf);
1221
1222	if ((nconf = getnetconfigent(netid)) == NULL)
1223		return (NULL);
1224	if ((p = malloc(sizeof(*p))) == NULL)
1225		err(1, "malloc");
1226	p->nconf = nconf;
1227	p->next = head;
1228	head = p;
1229
1230	return (p->nconf);
1231}
1232
1233/*
1234 * xdr routines for mount rpc's
1235 */
1236int
1237xdr_dir(XDR *xdrsp, char *dirp)
1238{
1239	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
1240}
1241
1242int
1243xdr_fh(XDR *xdrsp, struct nfhret *np)
1244{
1245	int i;
1246	long auth, authcnt, authfnd = 0;
1247
1248	if (!xdr_u_long(xdrsp, &np->stat))
1249		return (0);
1250	if (np->stat)
1251		return (1);
1252	switch (np->vers) {
1253	case 1:
1254		np->fhsize = NFSX_V2FH;
1255		return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
1256	case 3:
1257		if (!xdr_long(xdrsp, &np->fhsize))
1258			return (0);
1259		if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
1260			return (0);
1261		if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
1262			return (0);
1263		if (!xdr_long(xdrsp, &authcnt))
1264			return (0);
1265		for (i = 0; i < authcnt; i++) {
1266			if (!xdr_long(xdrsp, &auth))
1267				return (0);
1268			if (np->auth == -1) {
1269				np->auth = auth;
1270				authfnd++;
1271			} else if (auth == np->auth) {
1272				authfnd++;
1273			}
1274		}
1275		/*
1276		 * Some servers, such as DEC's OSF/1 return a nil authenticator
1277		 * list to indicate RPCAUTH_UNIX.
1278		 */
1279		if (authcnt == 0 && np->auth == -1)
1280			np->auth = AUTH_SYS;
1281		if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS))
1282			np->stat = EAUTH;
1283		return (1);
1284	};
1285	return (0);
1286}
1287
1288void
1289usage()
1290{
1291	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1292"usage: mount_nfs [-234bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]",
1293"                 [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]",
1294"                 [-r readsize] [-t timeout] [-w writesize] [-x retrans]",
1295"                 rhost:path node");
1296	exit(1);
1297}
1298