mountd.c revision 66fe452845254a482b05304fe3ebcb773a920085
1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1989, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Herb Hasler and Rick Macklem at The University of Guelph.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36static const char copyright[] =
37"@(#) Copyright (c) 1989, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /*not lint*/
40
41#if 0
42#ifndef lint
43static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
44#endif /*not lint*/
45#endif
46
47#include <sys/cdefs.h>
48__FBSDID("$FreeBSD$");
49
50#include <sys/param.h>
51#include <sys/fcntl.h>
52#include <sys/fnv_hash.h>
53#include <sys/linker.h>
54#include <sys/module.h>
55#include <sys/mount.h>
56#include <sys/queue.h>
57#include <sys/stat.h>
58#include <sys/sysctl.h>
59#include <sys/syslog.h>
60
61#include <rpc/rpc.h>
62#include <rpc/rpc_com.h>
63#include <rpc/pmap_clnt.h>
64#include <rpc/pmap_prot.h>
65#include <rpcsvc/mount.h>
66#include <nfs/nfsproto.h>
67#include <nfs/nfssvc.h>
68#include <nfsserver/nfs.h>
69
70#include <fs/nfs/nfsport.h>
71
72#include <arpa/inet.h>
73
74#include <ctype.h>
75#include <err.h>
76#include <errno.h>
77#include <grp.h>
78#include <libutil.h>
79#include <limits.h>
80#include <netdb.h>
81#include <pwd.h>
82#include <signal.h>
83#include <stdio.h>
84#include <stdlib.h>
85#include <string.h>
86#include <unistd.h>
87#include "pathnames.h"
88#include "mntopts.h"
89
90#ifdef DEBUG
91#include <stdarg.h>
92#endif
93
94/*
95 * Structures for keeping the mount list and export list
96 */
97struct mountlist {
98	char	ml_host[MNTNAMLEN+1];
99	char	ml_dirp[MNTPATHLEN+1];
100
101	SLIST_ENTRY(mountlist)	next;
102};
103
104struct dirlist {
105	struct dirlist	*dp_left;
106	struct dirlist	*dp_right;
107	int		dp_flag;
108	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
109	char		*dp_dirp;
110};
111/* dp_flag bits */
112#define	DP_DEFSET	0x1
113#define DP_HOSTSET	0x2
114
115struct exportlist {
116	struct dirlist	*ex_dirl;
117	struct dirlist	*ex_defdir;
118	struct grouplist *ex_grphead;
119	int		ex_flag;
120	fsid_t		ex_fs;
121	char		*ex_fsdir;
122	char		*ex_indexfile;
123	int		ex_numsecflavors;
124	int		ex_secflavors[MAXSECFLAVORS];
125	int		ex_defnumsecflavors;
126	int		ex_defsecflavors[MAXSECFLAVORS];
127
128	SLIST_ENTRY(exportlist) entries;
129};
130/* ex_flag bits */
131#define	EX_LINKED	0x1
132
133SLIST_HEAD(exportlisthead, exportlist);
134
135struct netmsk {
136	struct sockaddr_storage nt_net;
137	struct sockaddr_storage nt_mask;
138	char		*nt_name;
139};
140
141union grouptypes {
142	struct addrinfo *gt_addrinfo;
143	struct netmsk	gt_net;
144};
145
146struct grouplist {
147	int gr_type;
148	union grouptypes gr_ptr;
149	struct grouplist *gr_next;
150	int gr_numsecflavors;
151	int gr_secflavors[MAXSECFLAVORS];
152};
153/* Group types */
154#define	GT_NULL		0x0
155#define	GT_HOST		0x1
156#define	GT_NET		0x2
157#define	GT_DEFAULT	0x3
158#define GT_IGNORE	0x5
159
160struct hostlist {
161	int		 ht_flag;	/* Uses DP_xx bits */
162	struct grouplist *ht_grp;
163	struct hostlist	 *ht_next;
164};
165
166struct fhreturn {
167	int	fhr_flag;
168	int	fhr_vers;
169	nfsfh_t	fhr_fh;
170	int	fhr_numsecflavors;
171	int	*fhr_secflavors;
172};
173
174#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
175
176/* Global defs */
177static char	*add_expdir(struct dirlist **, char *, int);
178static void	add_dlist(struct dirlist **, struct dirlist *,
179		    struct grouplist *, int, struct exportlist *);
180static void	add_mlist(char *, char *);
181static int	check_dirpath(char *);
182static int	check_options(struct dirlist *);
183static int	checkmask(struct sockaddr *sa);
184static int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
185		    int *, int **);
186static char	*strsep_quote(char **stringp, const char *delim);
187static int	create_service(struct netconfig *nconf);
188static void	complete_service(struct netconfig *nconf, char *port_str);
189static void	clearout_service(void);
190static void	del_mlist(char *hostp, char *dirp);
191static struct dirlist	*dirp_search(struct dirlist *, char *);
192static int	do_mount(struct exportlist *, struct grouplist *, int,
193		    struct xucred *, char *, int, struct statfs *);
194static int	do_opt(char **, char **, struct exportlist *,
195		    struct grouplist *, int *, int *, struct xucred *);
196static struct exportlist	*ex_search(fsid_t *, struct exportlisthead *);
197static struct exportlist	*get_exp(void);
198static void	free_dir(struct dirlist *);
199static void	free_exp(struct exportlist *);
200static void	free_grp(struct grouplist *);
201static void	free_host(struct hostlist *);
202static void	get_exportlist(void);
203static void	insert_exports(struct exportlist *, struct exportlisthead *);
204static void	free_exports(struct exportlisthead *);
205static void	read_exportfile(void);
206static void	delete_export(struct iovec *, int, struct statfs *, char *);
207static int	get_host(char *, struct grouplist *, struct grouplist *);
208static struct hostlist *get_ht(void);
209static int	get_line(void);
210static void	get_mountlist(void);
211static int	get_net(char *, struct netmsk *, int);
212static void	getexp_err(struct exportlist *, struct grouplist *, const char *);
213static struct grouplist	*get_grp(void);
214static void	hang_dirp(struct dirlist *, struct grouplist *,
215				struct exportlist *, int);
216static void	huphandler(int sig);
217static int	makemask(struct sockaddr_storage *ssp, int bitlen);
218static void	mntsrv(struct svc_req *, SVCXPRT *);
219static void	nextfield(char **, char **);
220static void	out_of_mem(void);
221static void	parsecred(char *, struct xucred *);
222static int	parsesec(char *, struct exportlist *);
223static int	put_exlist(struct dirlist *, XDR *, struct dirlist *,
224		    int *, int);
225static void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
226static int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
227		    struct sockaddr *samask);
228static int	scan_tree(struct dirlist *, struct sockaddr *);
229static void	usage(void);
230static int	xdr_dir(XDR *, char *);
231static int	xdr_explist(XDR *, caddr_t);
232static int	xdr_explist_brief(XDR *, caddr_t);
233static int	xdr_explist_common(XDR *, caddr_t, int);
234static int	xdr_fhs(XDR *, caddr_t);
235static int	xdr_mlist(XDR *, caddr_t);
236static void	terminate(int);
237
238#define	EXPHASH(f)	(fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
239static struct exportlisthead *exphead = NULL;
240static int exphashsize = 0;
241static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
242static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
243static char **exnames;
244static char **hosts = NULL;
245static struct xucred def_anon = {
246	XUCRED_VERSION,
247	(uid_t)65534,
248	1,
249	{ (gid_t)65533 },
250	{ NULL }
251};
252static int force_v2 = 0;
253static int resvport_only = 1;
254static int nhosts = 0;
255static int dir_only = 1;
256static int dolog = 0;
257static int got_sighup = 0;
258static int xcreated = 0;
259
260static char *svcport_str = NULL;
261static int mallocd_svcport = 0;
262static int *sock_fd;
263static int sock_fdcnt;
264static int sock_fdpos;
265static int suspend_nfsd = 0;
266
267static int opt_flags;
268static int have_v6 = 1;
269
270static int v4root_phase = 0;
271static char v4root_dirpath[PATH_MAX + 1];
272static int has_publicfh = 0;
273
274static struct pidfh *pfh = NULL;
275/* Bits for opt_flags above */
276#define	OP_MAPROOT	0x01
277#define	OP_MAPALL	0x02
278/* 0x4 free */
279#define	OP_MASK		0x08
280#define	OP_NET		0x10
281#define	OP_ALLDIRS	0x40
282#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
283#define	OP_QUIET	0x100
284#define OP_MASKLEN	0x200
285#define OP_SEC		0x400
286
287#ifdef DEBUG
288static int debug = 1;
289static void	SYSLOG(int, const char *, ...) __printflike(2, 3);
290#define syslog SYSLOG
291#else
292static int debug = 0;
293#endif
294
295/*
296 * Similar to strsep(), but it allows for quoted strings
297 * and escaped characters.
298 *
299 * It returns the string (or NULL, if *stringp is NULL),
300 * which is a de-quoted version of the string if necessary.
301 *
302 * It modifies *stringp in place.
303 */
304static char *
305strsep_quote(char **stringp, const char *delim)
306{
307	char *srcptr, *dstptr, *retval;
308	char quot = 0;
309
310	if (stringp == NULL || *stringp == NULL)
311		return (NULL);
312
313	srcptr = dstptr = retval = *stringp;
314
315	while (*srcptr) {
316		/*
317		 * We're looking for several edge cases here.
318		 * First:  if we're in quote state (quot != 0),
319		 * then we ignore the delim characters, but otherwise
320		 * process as normal, unless it is the quote character.
321		 * Second:  if the current character is a backslash,
322		 * we take the next character as-is, without checking
323		 * for delim, quote, or backslash.  Exception:  if the
324		 * next character is a NUL, that's the end of the string.
325		 * Third:  if the character is a quote character, we toggle
326		 * quote state.
327		 * Otherwise:  check the current character for NUL, or
328		 * being in delim, and end the string if either is true.
329		 */
330		if (*srcptr == '\\') {
331			srcptr++;
332			/*
333			 * The edge case here is if the next character
334			 * is NUL, we want to stop processing.  But if
335			 * it's not NUL, then we simply want to copy it.
336			 */
337			if (*srcptr) {
338				*dstptr++ = *srcptr++;
339			}
340			continue;
341		}
342		if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
343			quot = *srcptr++;
344			continue;
345		}
346		if (quot && *srcptr == quot) {
347			/* End of the quoted part */
348			quot = 0;
349			srcptr++;
350			continue;
351		}
352		if (!quot && strchr(delim, *srcptr))
353			break;
354		*dstptr++ = *srcptr++;
355	}
356
357	*dstptr = 0; /* Terminate the string */
358	*stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
359	return (retval);
360}
361
362/*
363 * Mountd server for NFS mount protocol as described in:
364 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
365 * The optional arguments are the exports file name
366 * default: _PATH_EXPORTS
367 * and "-n" to allow nonroot mount.
368 */
369int
370main(int argc, char **argv)
371{
372	fd_set readfds;
373	struct netconfig *nconf;
374	char *endptr, **hosts_bak;
375	void *nc_handle;
376	pid_t otherpid;
377	in_port_t svcport;
378	int c, k, s;
379	int maxrec = RPC_MAXDATASIZE;
380	int attempt_cnt, port_len, port_pos, ret;
381	char **port_list;
382
383	/* Check that another mountd isn't already running. */
384	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
385	if (pfh == NULL) {
386		if (errno == EEXIST)
387			errx(1, "mountd already running, pid: %d.", otherpid);
388		warn("cannot open or create pidfile");
389	}
390
391	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
392	if (s < 0)
393		have_v6 = 0;
394	else
395		close(s);
396
397	while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
398		switch (c) {
399		case '2':
400			force_v2 = 1;
401			break;
402		case 'e':
403			/* now a no-op, since this is the default */
404			break;
405		case 'n':
406			resvport_only = 0;
407			break;
408		case 'r':
409			dir_only = 0;
410			break;
411		case 'd':
412			debug = debug ? 0 : 1;
413			break;
414		case 'l':
415			dolog = 1;
416			break;
417		case 'p':
418			endptr = NULL;
419			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
420			if (endptr == NULL || *endptr != '\0' ||
421			    svcport == 0 || svcport >= IPPORT_MAX)
422				usage();
423			svcport_str = strdup(optarg);
424			break;
425		case 'h':
426			++nhosts;
427			hosts_bak = hosts;
428			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
429			if (hosts_bak == NULL) {
430				if (hosts != NULL) {
431					for (k = 0; k < nhosts; k++)
432						free(hosts[k]);
433					free(hosts);
434					out_of_mem();
435				}
436			}
437			hosts = hosts_bak;
438			hosts[nhosts - 1] = strdup(optarg);
439			if (hosts[nhosts - 1] == NULL) {
440				for (k = 0; k < (nhosts - 1); k++)
441					free(hosts[k]);
442				free(hosts);
443				out_of_mem();
444			}
445			break;
446		case 'S':
447			suspend_nfsd = 1;
448			break;
449		default:
450			usage();
451		}
452
453	if (modfind("nfsd") < 0) {
454		/* Not present in kernel, try loading it */
455		if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
456			errx(1, "NFS server is not available");
457	}
458
459	argc -= optind;
460	argv += optind;
461	if (argc > 0)
462		exnames = argv;
463	else
464		exnames = exnames_default;
465	openlog("mountd", LOG_PID, LOG_DAEMON);
466	if (debug)
467		warnx("getting export list");
468	get_exportlist();
469	if (debug)
470		warnx("getting mount list");
471	get_mountlist();
472	if (debug)
473		warnx("here we go");
474	if (debug == 0) {
475		daemon(0, 0);
476		signal(SIGINT, SIG_IGN);
477		signal(SIGQUIT, SIG_IGN);
478	}
479	signal(SIGHUP, huphandler);
480	signal(SIGTERM, terminate);
481	signal(SIGPIPE, SIG_IGN);
482
483	pidfile_write(pfh);
484
485	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
486	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
487	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
488
489	if (!resvport_only) {
490		if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
491		    &resvport_only, sizeof(resvport_only)) != 0 &&
492		    errno != ENOENT) {
493			syslog(LOG_ERR, "sysctl: %m");
494			exit(1);
495		}
496	}
497
498	/*
499	 * If no hosts were specified, add a wildcard entry to bind to
500	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
501	 * list.
502	 */
503	if (nhosts == 0) {
504		hosts = malloc(sizeof(char *));
505		if (hosts == NULL)
506			out_of_mem();
507		hosts[0] = "*";
508		nhosts = 1;
509	} else {
510		hosts_bak = hosts;
511		if (have_v6) {
512			hosts_bak = realloc(hosts, (nhosts + 2) *
513			    sizeof(char *));
514			if (hosts_bak == NULL) {
515				for (k = 0; k < nhosts; k++)
516					free(hosts[k]);
517		    		free(hosts);
518		    		out_of_mem();
519			} else
520				hosts = hosts_bak;
521			nhosts += 2;
522			hosts[nhosts - 2] = "::1";
523		} else {
524			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
525			if (hosts_bak == NULL) {
526				for (k = 0; k < nhosts; k++)
527					free(hosts[k]);
528				free(hosts);
529				out_of_mem();
530			} else {
531				nhosts += 1;
532				hosts = hosts_bak;
533			}
534		}
535
536		hosts[nhosts - 1] = "127.0.0.1";
537	}
538
539	attempt_cnt = 1;
540	sock_fdcnt = 0;
541	sock_fd = NULL;
542	port_list = NULL;
543	port_len = 0;
544	nc_handle = setnetconfig();
545	while ((nconf = getnetconfig(nc_handle))) {
546		if (nconf->nc_flag & NC_VISIBLE) {
547			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
548			    "inet6") == 0) {
549				/* DO NOTHING */
550			} else {
551				ret = create_service(nconf);
552				if (ret == 1)
553					/* Ignore this call */
554					continue;
555				if (ret < 0) {
556					/*
557					 * Failed to bind port, so close off
558					 * all sockets created and try again
559					 * if the port# was dynamically
560					 * assigned via bind(2).
561					 */
562					clearout_service();
563					if (mallocd_svcport != 0 &&
564					    attempt_cnt < GETPORT_MAXTRY) {
565						free(svcport_str);
566						svcport_str = NULL;
567						mallocd_svcport = 0;
568					} else {
569						errno = EADDRINUSE;
570						syslog(LOG_ERR,
571						    "bindresvport_sa: %m");
572						exit(1);
573					}
574
575					/* Start over at the first service. */
576					free(sock_fd);
577					sock_fdcnt = 0;
578					sock_fd = NULL;
579					nc_handle = setnetconfig();
580					attempt_cnt++;
581				} else if (mallocd_svcport != 0 &&
582				    attempt_cnt == GETPORT_MAXTRY) {
583					/*
584					 * For the last attempt, allow
585					 * different port #s for each nconf
586					 * by saving the svcport_str and
587					 * setting it back to NULL.
588					 */
589					port_list = realloc(port_list,
590					    (port_len + 1) * sizeof(char *));
591					if (port_list == NULL)
592						out_of_mem();
593					port_list[port_len++] = svcport_str;
594					svcport_str = NULL;
595					mallocd_svcport = 0;
596				}
597			}
598		}
599	}
600
601	/*
602	 * Successfully bound the ports, so call complete_service() to
603	 * do the rest of the setup on the service(s).
604	 */
605	sock_fdpos = 0;
606	port_pos = 0;
607	nc_handle = setnetconfig();
608	while ((nconf = getnetconfig(nc_handle))) {
609		if (nconf->nc_flag & NC_VISIBLE) {
610			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
611			    "inet6") == 0) {
612				/* DO NOTHING */
613			} else if (port_list != NULL) {
614				if (port_pos >= port_len) {
615					syslog(LOG_ERR, "too many port#s");
616					exit(1);
617				}
618				complete_service(nconf, port_list[port_pos++]);
619			} else
620				complete_service(nconf, svcport_str);
621		}
622	}
623	endnetconfig(nc_handle);
624	free(sock_fd);
625	if (port_list != NULL) {
626		for (port_pos = 0; port_pos < port_len; port_pos++)
627			free(port_list[port_pos]);
628		free(port_list);
629	}
630
631	if (xcreated == 0) {
632		syslog(LOG_ERR, "could not create any services");
633		exit(1);
634	}
635
636	/* Expand svc_run() here so that we can call get_exportlist(). */
637	for (;;) {
638		if (got_sighup) {
639			get_exportlist();
640			got_sighup = 0;
641		}
642		readfds = svc_fdset;
643		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
644		case -1:
645			if (errno == EINTR)
646                                continue;
647			syslog(LOG_ERR, "mountd died: select: %m");
648			exit(1);
649		case 0:
650			continue;
651		default:
652			svc_getreqset(&readfds);
653		}
654	}
655}
656
657/*
658 * This routine creates and binds sockets on the appropriate
659 * addresses. It gets called one time for each transport.
660 * It returns 0 upon success, 1 for ingore the call and -1 to indicate
661 * bind failed with EADDRINUSE.
662 * Any file descriptors that have been created are stored in sock_fd and
663 * the total count of them is maintained in sock_fdcnt.
664 */
665static int
666create_service(struct netconfig *nconf)
667{
668	struct addrinfo hints, *res = NULL;
669	struct sockaddr_in *sin;
670	struct sockaddr_in6 *sin6;
671	struct __rpc_sockinfo si;
672	int aicode;
673	int fd;
674	int nhostsbak;
675	int one = 1;
676	int r;
677	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
678	int mallocd_res;
679
680	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
681	    (nconf->nc_semantics != NC_TPI_COTS) &&
682	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
683		return (1);	/* not my type */
684
685	/*
686	 * XXX - using RPC library internal functions.
687	 */
688	if (!__rpc_nconf2sockinfo(nconf, &si)) {
689		syslog(LOG_ERR, "cannot get information for %s",
690		    nconf->nc_netid);
691		return (1);
692	}
693
694	/* Get mountd's address on this transport */
695	memset(&hints, 0, sizeof hints);
696	hints.ai_family = si.si_af;
697	hints.ai_socktype = si.si_socktype;
698	hints.ai_protocol = si.si_proto;
699
700	/*
701	 * Bind to specific IPs if asked to
702	 */
703	nhostsbak = nhosts;
704	while (nhostsbak > 0) {
705		--nhostsbak;
706		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
707		if (sock_fd == NULL)
708			out_of_mem();
709		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
710		mallocd_res = 0;
711
712		hints.ai_flags = AI_PASSIVE;
713
714		/*
715		 * XXX - using RPC library internal functions.
716		 */
717		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
718			int non_fatal = 0;
719	    		if (errno == EAFNOSUPPORT &&
720			    nconf->nc_semantics != NC_TPI_CLTS)
721				non_fatal = 1;
722
723			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
724			    "cannot create socket for %s", nconf->nc_netid);
725			if (non_fatal != 0)
726				continue;
727			exit(1);
728		}
729
730		switch (hints.ai_family) {
731		case AF_INET:
732			if (inet_pton(AF_INET, hosts[nhostsbak],
733			    host_addr) == 1) {
734				hints.ai_flags |= AI_NUMERICHOST;
735			} else {
736				/*
737				 * Skip if we have an AF_INET6 address.
738				 */
739				if (inet_pton(AF_INET6, hosts[nhostsbak],
740				    host_addr) == 1) {
741					close(fd);
742					continue;
743				}
744			}
745			break;
746		case AF_INET6:
747			if (inet_pton(AF_INET6, hosts[nhostsbak],
748			    host_addr) == 1) {
749				hints.ai_flags |= AI_NUMERICHOST;
750			} else {
751				/*
752				 * Skip if we have an AF_INET address.
753				 */
754				if (inet_pton(AF_INET, hosts[nhostsbak],
755				    host_addr) == 1) {
756					close(fd);
757					continue;
758				}
759			}
760
761			/*
762			 * We're doing host-based access checks here, so don't
763			 * allow v4-in-v6 to confuse things. The kernel will
764			 * disable it by default on NFS sockets too.
765			 */
766			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
767			    sizeof one) < 0) {
768				syslog(LOG_ERR,
769				    "can't disable v4-in-v6 on IPv6 socket");
770				exit(1);
771			}
772			break;
773		default:
774			break;
775		}
776
777		/*
778		 * If no hosts were specified, just bind to INADDR_ANY
779		 */
780		if (strcmp("*", hosts[nhostsbak]) == 0) {
781			if (svcport_str == NULL) {
782				res = malloc(sizeof(struct addrinfo));
783				if (res == NULL)
784					out_of_mem();
785				mallocd_res = 1;
786				res->ai_flags = hints.ai_flags;
787				res->ai_family = hints.ai_family;
788				res->ai_protocol = hints.ai_protocol;
789				switch (res->ai_family) {
790				case AF_INET:
791					sin = malloc(sizeof(struct sockaddr_in));
792					if (sin == NULL)
793						out_of_mem();
794					sin->sin_family = AF_INET;
795					sin->sin_port = htons(0);
796					sin->sin_addr.s_addr = htonl(INADDR_ANY);
797					res->ai_addr = (struct sockaddr*) sin;
798					res->ai_addrlen = (socklen_t)
799					    sizeof(struct sockaddr_in);
800					break;
801				case AF_INET6:
802					sin6 = malloc(sizeof(struct sockaddr_in6));
803					if (sin6 == NULL)
804						out_of_mem();
805					sin6->sin6_family = AF_INET6;
806					sin6->sin6_port = htons(0);
807					sin6->sin6_addr = in6addr_any;
808					res->ai_addr = (struct sockaddr*) sin6;
809					res->ai_addrlen = (socklen_t)
810					    sizeof(struct sockaddr_in6);
811					break;
812				default:
813					syslog(LOG_ERR, "bad addr fam %d",
814					    res->ai_family);
815					exit(1);
816				}
817			} else {
818				if ((aicode = getaddrinfo(NULL, svcport_str,
819				    &hints, &res)) != 0) {
820					syslog(LOG_ERR,
821					    "cannot get local address for %s: %s",
822					    nconf->nc_netid,
823					    gai_strerror(aicode));
824					close(fd);
825					continue;
826				}
827			}
828		} else {
829			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
830			    &hints, &res)) != 0) {
831				syslog(LOG_ERR,
832				    "cannot get local address for %s: %s",
833				    nconf->nc_netid, gai_strerror(aicode));
834				close(fd);
835				continue;
836			}
837		}
838
839		/* Store the fd. */
840		sock_fd[sock_fdcnt - 1] = fd;
841
842		/* Now, attempt the bind. */
843		r = bindresvport_sa(fd, res->ai_addr);
844		if (r != 0) {
845			if (errno == EADDRINUSE && mallocd_svcport != 0) {
846				if (mallocd_res != 0) {
847					free(res->ai_addr);
848					free(res);
849				} else
850					freeaddrinfo(res);
851				return (-1);
852			}
853			syslog(LOG_ERR, "bindresvport_sa: %m");
854			exit(1);
855		}
856
857		if (svcport_str == NULL) {
858			svcport_str = malloc(NI_MAXSERV * sizeof(char));
859			if (svcport_str == NULL)
860				out_of_mem();
861			mallocd_svcport = 1;
862
863			if (getnameinfo(res->ai_addr,
864			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
865			    svcport_str, NI_MAXSERV * sizeof(char),
866			    NI_NUMERICHOST | NI_NUMERICSERV))
867				errx(1, "Cannot get port number");
868		}
869		if (mallocd_res != 0) {
870			free(res->ai_addr);
871			free(res);
872		} else
873			freeaddrinfo(res);
874		res = NULL;
875	}
876	return (0);
877}
878
879/*
880 * Called after all the create_service() calls have succeeded, to complete
881 * the setup and registration.
882 */
883static void
884complete_service(struct netconfig *nconf, char *port_str)
885{
886	struct addrinfo hints, *res = NULL;
887	struct __rpc_sockinfo si;
888	struct netbuf servaddr;
889	SVCXPRT	*transp = NULL;
890	int aicode, fd, nhostsbak;
891	int registered = 0;
892
893	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
894	    (nconf->nc_semantics != NC_TPI_COTS) &&
895	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
896		return;	/* not my type */
897
898	/*
899	 * XXX - using RPC library internal functions.
900	 */
901	if (!__rpc_nconf2sockinfo(nconf, &si)) {
902		syslog(LOG_ERR, "cannot get information for %s",
903		    nconf->nc_netid);
904		return;
905	}
906
907	nhostsbak = nhosts;
908	while (nhostsbak > 0) {
909		--nhostsbak;
910		if (sock_fdpos >= sock_fdcnt) {
911			/* Should never happen. */
912			syslog(LOG_ERR, "Ran out of socket fd's");
913			return;
914		}
915		fd = sock_fd[sock_fdpos++];
916		if (fd < 0)
917			continue;
918
919		/*
920		 * Using -1 tells listen(2) to use
921		 * kern.ipc.soacceptqueue for the backlog.
922		 */
923		if (nconf->nc_semantics != NC_TPI_CLTS)
924			listen(fd, -1);
925
926		if (nconf->nc_semantics == NC_TPI_CLTS )
927			transp = svc_dg_create(fd, 0, 0);
928		else
929			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
930			    RPC_MAXDATASIZE);
931
932		if (transp != (SVCXPRT *) NULL) {
933			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
934			    NULL))
935				syslog(LOG_ERR,
936				    "can't register %s MOUNTVERS service",
937				    nconf->nc_netid);
938			if (!force_v2) {
939				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
940				    mntsrv, NULL))
941					syslog(LOG_ERR,
942					    "can't register %s MOUNTVERS3 service",
943					    nconf->nc_netid);
944			}
945		} else
946			syslog(LOG_WARNING, "can't create %s services",
947			    nconf->nc_netid);
948
949		if (registered == 0) {
950			registered = 1;
951			memset(&hints, 0, sizeof hints);
952			hints.ai_flags = AI_PASSIVE;
953			hints.ai_family = si.si_af;
954			hints.ai_socktype = si.si_socktype;
955			hints.ai_protocol = si.si_proto;
956
957			if ((aicode = getaddrinfo(NULL, port_str, &hints,
958			    &res)) != 0) {
959				syslog(LOG_ERR, "cannot get local address: %s",
960				    gai_strerror(aicode));
961				exit(1);
962			}
963
964			servaddr.buf = malloc(res->ai_addrlen);
965			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
966			servaddr.len = res->ai_addrlen;
967
968			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
969			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
970
971			xcreated++;
972			freeaddrinfo(res);
973		}
974	} /* end while */
975}
976
977/*
978 * Clear out sockets after a failure to bind one of them, so that the
979 * cycle of socket creation/binding can start anew.
980 */
981static void
982clearout_service(void)
983{
984	int i;
985
986	for (i = 0; i < sock_fdcnt; i++) {
987		if (sock_fd[i] >= 0) {
988			shutdown(sock_fd[i], SHUT_RDWR);
989			close(sock_fd[i]);
990		}
991	}
992}
993
994static void
995usage(void)
996{
997	fprintf(stderr,
998		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
999		"[-S] [-h <bindip>] [export_file ...]\n");
1000	exit(1);
1001}
1002
1003/*
1004 * The mount rpc service
1005 */
1006void
1007mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
1008{
1009	struct exportlist *ep;
1010	struct dirlist *dp;
1011	struct fhreturn fhr;
1012	struct stat stb;
1013	struct statfs fsb;
1014	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
1015	int lookup_failed = 1;
1016	struct sockaddr *saddr;
1017	u_short sport;
1018	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
1019	int bad = 0, defset, hostset;
1020	sigset_t sighup_mask;
1021	int numsecflavors, *secflavorsp;
1022
1023	sigemptyset(&sighup_mask);
1024	sigaddset(&sighup_mask, SIGHUP);
1025	saddr = svc_getrpccaller(transp)->buf;
1026	switch (saddr->sa_family) {
1027	case AF_INET6:
1028		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1029		break;
1030	case AF_INET:
1031		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1032		break;
1033	default:
1034		syslog(LOG_ERR, "request from unknown address family");
1035		return;
1036	}
1037	switch (rqstp->rq_proc) {
1038	case MOUNTPROC_MNT:
1039	case MOUNTPROC_UMNT:
1040	case MOUNTPROC_UMNTALL:
1041		lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1042		    sizeof host, NULL, 0, 0);
1043	}
1044	getnameinfo(saddr, saddr->sa_len, numerichost,
1045	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
1046	switch (rqstp->rq_proc) {
1047	case NULLPROC:
1048		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
1049			syslog(LOG_ERR, "can't send reply");
1050		return;
1051	case MOUNTPROC_MNT:
1052		if (sport >= IPPORT_RESERVED && resvport_only) {
1053			syslog(LOG_NOTICE,
1054			    "mount request from %s from unprivileged port",
1055			    numerichost);
1056			svcerr_weakauth(transp);
1057			return;
1058		}
1059		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1060			syslog(LOG_NOTICE, "undecodable mount request from %s",
1061			    numerichost);
1062			svcerr_decode(transp);
1063			return;
1064		}
1065
1066		/*
1067		 * Get the real pathname and make sure it is a directory
1068		 * or a regular file if the -r option was specified
1069		 * and it exists.
1070		 */
1071		if (realpath(rpcpath, dirpath) == NULL ||
1072		    stat(dirpath, &stb) < 0 ||
1073		    statfs(dirpath, &fsb) < 0) {
1074			chdir("/");	/* Just in case realpath doesn't */
1075			syslog(LOG_NOTICE,
1076			    "mount request from %s for non existent path %s",
1077			    numerichost, dirpath);
1078			if (debug)
1079				warnx("stat failed on %s", dirpath);
1080			bad = ENOENT;	/* We will send error reply later */
1081		}
1082		if (!bad &&
1083		    !S_ISDIR(stb.st_mode) &&
1084		    (dir_only || !S_ISREG(stb.st_mode))) {
1085			syslog(LOG_NOTICE,
1086			    "mount request from %s for non-directory path %s",
1087			    numerichost, dirpath);
1088			if (debug)
1089				warnx("mounting non-directory %s", dirpath);
1090			bad = ENOTDIR;	/* We will send error reply later */
1091		}
1092
1093		/* Check in the exports list */
1094		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1095		if (bad)
1096			ep = NULL;
1097		else
1098			ep = ex_search(&fsb.f_fsid, exphead);
1099		hostset = defset = 0;
1100		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1101		    &numsecflavors, &secflavorsp) ||
1102		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1103		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1104		       &secflavorsp)) ||
1105		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1106		     scan_tree(ep->ex_dirl, saddr) == 0))) {
1107			if (bad) {
1108				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1109				    (caddr_t)&bad))
1110					syslog(LOG_ERR, "can't send reply");
1111				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1112				return;
1113			}
1114			if (hostset & DP_HOSTSET) {
1115				fhr.fhr_flag = hostset;
1116				fhr.fhr_numsecflavors = numsecflavors;
1117				fhr.fhr_secflavors = secflavorsp;
1118			} else {
1119				fhr.fhr_flag = defset;
1120				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1121				fhr.fhr_secflavors = ep->ex_defsecflavors;
1122			}
1123			fhr.fhr_vers = rqstp->rq_vers;
1124			/* Get the file handle */
1125			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1126			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1127				bad = errno;
1128				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1129				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1130				    (caddr_t)&bad))
1131					syslog(LOG_ERR, "can't send reply");
1132				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1133				return;
1134			}
1135			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1136			    (caddr_t)&fhr))
1137				syslog(LOG_ERR, "can't send reply");
1138			if (!lookup_failed)
1139				add_mlist(host, dirpath);
1140			else
1141				add_mlist(numerichost, dirpath);
1142			if (debug)
1143				warnx("mount successful");
1144			if (dolog)
1145				syslog(LOG_NOTICE,
1146				    "mount request succeeded from %s for %s",
1147				    numerichost, dirpath);
1148		} else {
1149			if (!bad)
1150				bad = EACCES;
1151			syslog(LOG_NOTICE,
1152			    "mount request denied from %s for %s",
1153			    numerichost, dirpath);
1154		}
1155
1156		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1157		    (caddr_t)&bad))
1158			syslog(LOG_ERR, "can't send reply");
1159		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1160		return;
1161	case MOUNTPROC_DUMP:
1162		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1163			syslog(LOG_ERR, "can't send reply");
1164		else if (dolog)
1165			syslog(LOG_NOTICE,
1166			    "dump request succeeded from %s",
1167			    numerichost);
1168		return;
1169	case MOUNTPROC_UMNT:
1170		if (sport >= IPPORT_RESERVED && resvport_only) {
1171			syslog(LOG_NOTICE,
1172			    "umount request from %s from unprivileged port",
1173			    numerichost);
1174			svcerr_weakauth(transp);
1175			return;
1176		}
1177		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1178			syslog(LOG_NOTICE, "undecodable umount request from %s",
1179			    numerichost);
1180			svcerr_decode(transp);
1181			return;
1182		}
1183		if (realpath(rpcpath, dirpath) == NULL) {
1184			syslog(LOG_NOTICE, "umount request from %s "
1185			    "for non existent path %s",
1186			    numerichost, dirpath);
1187		}
1188		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1189			syslog(LOG_ERR, "can't send reply");
1190		if (!lookup_failed)
1191			del_mlist(host, dirpath);
1192		del_mlist(numerichost, dirpath);
1193		if (dolog)
1194			syslog(LOG_NOTICE,
1195			    "umount request succeeded from %s for %s",
1196			    numerichost, dirpath);
1197		return;
1198	case MOUNTPROC_UMNTALL:
1199		if (sport >= IPPORT_RESERVED && resvport_only) {
1200			syslog(LOG_NOTICE,
1201			    "umountall request from %s from unprivileged port",
1202			    numerichost);
1203			svcerr_weakauth(transp);
1204			return;
1205		}
1206		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1207			syslog(LOG_ERR, "can't send reply");
1208		if (!lookup_failed)
1209			del_mlist(host, NULL);
1210		del_mlist(numerichost, NULL);
1211		if (dolog)
1212			syslog(LOG_NOTICE,
1213			    "umountall request succeeded from %s",
1214			    numerichost);
1215		return;
1216	case MOUNTPROC_EXPORT:
1217		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1218			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1219			    (caddr_t)NULL))
1220				syslog(LOG_ERR, "can't send reply");
1221		if (dolog)
1222			syslog(LOG_NOTICE,
1223			    "export request succeeded from %s",
1224			    numerichost);
1225		return;
1226	default:
1227		svcerr_noproc(transp);
1228		return;
1229	}
1230}
1231
1232/*
1233 * Xdr conversion for a dirpath string
1234 */
1235static int
1236xdr_dir(XDR *xdrsp, char *dirp)
1237{
1238	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1239}
1240
1241/*
1242 * Xdr routine to generate file handle reply
1243 */
1244static int
1245xdr_fhs(XDR *xdrsp, caddr_t cp)
1246{
1247	struct fhreturn *fhrp = (struct fhreturn *)cp;
1248	u_long ok = 0, len, auth;
1249	int i;
1250
1251	if (!xdr_long(xdrsp, &ok))
1252		return (0);
1253	switch (fhrp->fhr_vers) {
1254	case 1:
1255		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1256	case 3:
1257		len = NFSX_V3FH;
1258		if (!xdr_long(xdrsp, &len))
1259			return (0);
1260		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1261			return (0);
1262		if (fhrp->fhr_numsecflavors) {
1263			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1264				return (0);
1265			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1266				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1267					return (0);
1268			return (1);
1269		} else {
1270			auth = AUTH_SYS;
1271			len = 1;
1272			if (!xdr_long(xdrsp, &len))
1273				return (0);
1274			return (xdr_long(xdrsp, &auth));
1275		}
1276	}
1277	return (0);
1278}
1279
1280static int
1281xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1282{
1283	struct mountlist *mlp;
1284	int true = 1;
1285	int false = 0;
1286	char *strp;
1287
1288	SLIST_FOREACH(mlp, &mlhead, next) {
1289		if (!xdr_bool(xdrsp, &true))
1290			return (0);
1291		strp = &mlp->ml_host[0];
1292		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1293			return (0);
1294		strp = &mlp->ml_dirp[0];
1295		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1296			return (0);
1297	}
1298	if (!xdr_bool(xdrsp, &false))
1299		return (0);
1300	return (1);
1301}
1302
1303/*
1304 * Xdr conversion for export list
1305 */
1306static int
1307xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1308{
1309	struct exportlist *ep;
1310	int false = 0;
1311	int putdef;
1312	sigset_t sighup_mask;
1313	int i;
1314
1315	sigemptyset(&sighup_mask);
1316	sigaddset(&sighup_mask, SIGHUP);
1317	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1318
1319	for (i = 0; i < exphashsize; i++)
1320		SLIST_FOREACH(ep, &exphead[i], entries) {
1321			putdef = 0;
1322			if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1323				       &putdef, brief))
1324				goto errout;
1325			if (ep->ex_defdir && putdef == 0 &&
1326				put_exlist(ep->ex_defdir, xdrsp, NULL,
1327				&putdef, brief))
1328				goto errout;
1329		}
1330	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1331	if (!xdr_bool(xdrsp, &false))
1332		return (0);
1333	return (1);
1334errout:
1335	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1336	return (0);
1337}
1338
1339/*
1340 * Called from xdr_explist() to traverse the tree and export the
1341 * directory paths.
1342 */
1343static int
1344put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1345	int brief)
1346{
1347	struct grouplist *grp;
1348	struct hostlist *hp;
1349	int true = 1;
1350	int false = 0;
1351	int gotalldir = 0;
1352	char *strp;
1353
1354	if (dp) {
1355		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1356			return (1);
1357		if (!xdr_bool(xdrsp, &true))
1358			return (1);
1359		strp = dp->dp_dirp;
1360		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1361			return (1);
1362		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1363			gotalldir = 1;
1364			*putdefp = 1;
1365		}
1366		if (brief) {
1367			if (!xdr_bool(xdrsp, &true))
1368				return (1);
1369			strp = "(...)";
1370			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1371				return (1);
1372		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1373		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1374			hp = dp->dp_hosts;
1375			while (hp) {
1376				grp = hp->ht_grp;
1377				if (grp->gr_type == GT_HOST) {
1378					if (!xdr_bool(xdrsp, &true))
1379						return (1);
1380					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1381					if (!xdr_string(xdrsp, &strp,
1382					    MNTNAMLEN))
1383						return (1);
1384				} else if (grp->gr_type == GT_NET) {
1385					if (!xdr_bool(xdrsp, &true))
1386						return (1);
1387					strp = grp->gr_ptr.gt_net.nt_name;
1388					if (!xdr_string(xdrsp, &strp,
1389					    MNTNAMLEN))
1390						return (1);
1391				}
1392				hp = hp->ht_next;
1393				if (gotalldir && hp == (struct hostlist *)NULL) {
1394					hp = adp->dp_hosts;
1395					gotalldir = 0;
1396				}
1397			}
1398		}
1399		if (!xdr_bool(xdrsp, &false))
1400			return (1);
1401		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1402			return (1);
1403	}
1404	return (0);
1405}
1406
1407static int
1408xdr_explist(XDR *xdrsp, caddr_t cp)
1409{
1410
1411	return xdr_explist_common(xdrsp, cp, 0);
1412}
1413
1414static int
1415xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1416{
1417
1418	return xdr_explist_common(xdrsp, cp, 1);
1419}
1420
1421static char *line;
1422static size_t linesize;
1423static FILE *exp_file;
1424
1425/*
1426 * Get the export list from one, currently open file
1427 */
1428static void
1429get_exportlist_one(void)
1430{
1431	struct exportlist *ep;
1432	struct grouplist *grp, *tgrp;
1433	struct dirlist *dirhead;
1434	struct statfs fsb;
1435	struct xucred anon;
1436	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1437	int len, has_host, exflags, got_nondir, dirplen, netgrp;
1438
1439	v4root_phase = 0;
1440	dirhead = (struct dirlist *)NULL;
1441	while (get_line()) {
1442		if (debug)
1443			warnx("got line %s", line);
1444		cp = line;
1445		nextfield(&cp, &endcp);
1446		if (*cp == '#')
1447			goto nextline;
1448
1449		/*
1450		 * Set defaults.
1451		 */
1452		has_host = FALSE;
1453		anon = def_anon;
1454		exflags = MNT_EXPORTED;
1455		got_nondir = 0;
1456		opt_flags = 0;
1457		ep = (struct exportlist *)NULL;
1458		dirp = NULL;
1459
1460		/*
1461		 * Handle the V4 root dir.
1462		 */
1463		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1464			/*
1465			 * V4: just indicates that it is the v4 root point,
1466			 * so skip over that and set v4root_phase.
1467			 */
1468			if (v4root_phase > 0) {
1469				syslog(LOG_ERR, "V4:duplicate line, ignored");
1470				goto nextline;
1471			}
1472			v4root_phase = 1;
1473			cp += 3;
1474			nextfield(&cp, &endcp);
1475		}
1476
1477		/*
1478		 * Create new exports list entry
1479		 */
1480		len = endcp-cp;
1481		tgrp = grp = get_grp();
1482		while (len > 0) {
1483			if (len > MNTNAMLEN) {
1484			    getexp_err(ep, tgrp, "mountpoint too long");
1485			    goto nextline;
1486			}
1487			if (*cp == '-') {
1488			    if (ep == (struct exportlist *)NULL) {
1489				getexp_err(ep, tgrp,
1490				    "flag before export path definition");
1491				goto nextline;
1492			    }
1493			    if (debug)
1494				warnx("doing opt %s", cp);
1495			    got_nondir = 1;
1496			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
1497				&exflags, &anon)) {
1498				getexp_err(ep, tgrp, NULL);
1499				goto nextline;
1500			    }
1501			} else if (*cp == '/') {
1502			    savedc = *endcp;
1503			    *endcp = '\0';
1504			    if (v4root_phase > 1) {
1505				    if (dirp != NULL) {
1506					getexp_err(ep, tgrp, "Multiple V4 dirs");
1507					goto nextline;
1508				    }
1509			    }
1510			    if (check_dirpath(cp) &&
1511				statfs(cp, &fsb) >= 0) {
1512				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1513				    syslog(LOG_ERR, "Warning: exporting of "
1514					"automounted fs %s not supported", cp);
1515				if (got_nondir) {
1516				    getexp_err(ep, tgrp, "dirs must be first");
1517				    goto nextline;
1518				}
1519				if (v4root_phase == 1) {
1520				    if (dirp != NULL) {
1521					getexp_err(ep, tgrp, "Multiple V4 dirs");
1522					goto nextline;
1523				    }
1524				    if (strlen(v4root_dirpath) == 0) {
1525					strlcpy(v4root_dirpath, cp,
1526					    sizeof (v4root_dirpath));
1527				    } else if (strcmp(v4root_dirpath, cp)
1528					!= 0) {
1529					syslog(LOG_ERR,
1530					    "different V4 dirpath %s", cp);
1531					getexp_err(ep, tgrp, NULL);
1532					goto nextline;
1533				    }
1534				    dirp = cp;
1535				    v4root_phase = 2;
1536				    got_nondir = 1;
1537				    ep = get_exp();
1538				} else {
1539				    if (ep) {
1540					if (ep->ex_fs.val[0] !=
1541					    fsb.f_fsid.val[0] ||
1542					    ep->ex_fs.val[1] !=
1543					    fsb.f_fsid.val[1]) {
1544						getexp_err(ep, tgrp,
1545						    "fsid mismatch");
1546						goto nextline;
1547					}
1548				    } else {
1549					/*
1550					 * See if this directory is already
1551					 * in the list.
1552					 */
1553					ep = ex_search(&fsb.f_fsid, exphead);
1554					if (ep == (struct exportlist *)NULL) {
1555					    ep = get_exp();
1556					    ep->ex_fs = fsb.f_fsid;
1557					    ep->ex_fsdir = strdup(fsb.f_mntonname);
1558					    if (ep->ex_fsdir == NULL)
1559						out_of_mem();
1560					    if (debug)
1561						warnx(
1562						  "making new ep fs=0x%x,0x%x",
1563						  fsb.f_fsid.val[0],
1564						  fsb.f_fsid.val[1]);
1565					} else if (debug)
1566					    warnx("found ep fs=0x%x,0x%x",
1567						fsb.f_fsid.val[0],
1568						fsb.f_fsid.val[1]);
1569				    }
1570
1571				    /*
1572				     * Add dirpath to export mount point.
1573				     */
1574				    dirp = add_expdir(&dirhead, cp, len);
1575				    dirplen = len;
1576				}
1577			    } else {
1578				getexp_err(ep, tgrp,
1579				    "symbolic link in export path or statfs failed");
1580				goto nextline;
1581			    }
1582			    *endcp = savedc;
1583			} else {
1584			    savedc = *endcp;
1585			    *endcp = '\0';
1586			    got_nondir = 1;
1587			    if (ep == (struct exportlist *)NULL) {
1588				getexp_err(ep, tgrp,
1589				    "host(s) before export path definition");
1590				goto nextline;
1591			    }
1592
1593			    /*
1594			     * Get the host or netgroup.
1595			     */
1596			    setnetgrent(cp);
1597			    netgrp = getnetgrent(&hst, &usr, &dom);
1598			    do {
1599				if (has_host) {
1600				    grp->gr_next = get_grp();
1601				    grp = grp->gr_next;
1602				}
1603				if (netgrp) {
1604				    if (hst == 0) {
1605					syslog(LOG_ERR,
1606				"null hostname in netgroup %s, skipping", cp);
1607					grp->gr_type = GT_IGNORE;
1608				    } else if (get_host(hst, grp, tgrp)) {
1609					syslog(LOG_ERR,
1610			"bad host %s in netgroup %s, skipping", hst, cp);
1611					grp->gr_type = GT_IGNORE;
1612				    }
1613				} else if (get_host(cp, grp, tgrp)) {
1614				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1615				    grp->gr_type = GT_IGNORE;
1616				}
1617				has_host = TRUE;
1618			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1619			    endnetgrent();
1620			    *endcp = savedc;
1621			}
1622			cp = endcp;
1623			nextfield(&cp, &endcp);
1624			len = endcp - cp;
1625		}
1626		if (check_options(dirhead)) {
1627			getexp_err(ep, tgrp, NULL);
1628			goto nextline;
1629		}
1630		if (!has_host) {
1631			grp->gr_type = GT_DEFAULT;
1632			if (debug)
1633				warnx("adding a default entry");
1634
1635		/*
1636		 * Don't allow a network export coincide with a list of
1637		 * host(s) on the same line.
1638		 */
1639		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1640			getexp_err(ep, tgrp, "network/host conflict");
1641			goto nextline;
1642
1643		/*
1644		 * If an export list was specified on this line, make sure
1645		 * that we have at least one valid entry, otherwise skip it.
1646		 */
1647		} else {
1648			grp = tgrp;
1649			while (grp && grp->gr_type == GT_IGNORE)
1650				grp = grp->gr_next;
1651			if (! grp) {
1652			    getexp_err(ep, tgrp, "no valid entries");
1653			    goto nextline;
1654			}
1655		}
1656
1657		if (v4root_phase == 1) {
1658			getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1659			goto nextline;
1660		}
1661
1662		/*
1663		 * Loop through hosts, pushing the exports into the kernel.
1664		 * After loop, tgrp points to the start of the list and
1665		 * grp points to the last entry in the list.
1666		 */
1667		grp = tgrp;
1668		do {
1669			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1670			    &fsb)) {
1671				getexp_err(ep, tgrp, NULL);
1672				goto nextline;
1673			}
1674		} while (grp->gr_next && (grp = grp->gr_next));
1675
1676		/*
1677		 * For V4: don't enter in mount lists.
1678		 */
1679		if (v4root_phase > 0 && v4root_phase <= 2) {
1680			/*
1681			 * Since these structures aren't used by mountd,
1682			 * free them up now.
1683			 */
1684			if (ep != NULL)
1685				free_exp(ep);
1686			while (tgrp != NULL) {
1687				grp = tgrp;
1688				tgrp = tgrp->gr_next;
1689				free_grp(grp);
1690			}
1691			goto nextline;
1692		}
1693
1694		/*
1695		 * Success. Update the data structures.
1696		 */
1697		if (has_host) {
1698			hang_dirp(dirhead, tgrp, ep, opt_flags);
1699			grp->gr_next = ep->ex_grphead;
1700			ep->ex_grphead = tgrp;
1701		} else {
1702			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1703				opt_flags);
1704			free_grp(grp);
1705		}
1706		dirhead = (struct dirlist *)NULL;
1707		if ((ep->ex_flag & EX_LINKED) == 0) {
1708			insert_exports(ep, exphead);
1709
1710			ep->ex_flag |= EX_LINKED;
1711		}
1712nextline:
1713		v4root_phase = 0;
1714		if (dirhead) {
1715			free_dir(dirhead);
1716			dirhead = (struct dirlist *)NULL;
1717		}
1718	}
1719}
1720
1721/*
1722 * Get the export list from all specified files
1723 */
1724static void
1725get_exportlist(void)
1726{
1727	struct export_args export;
1728	struct iovec *iov;
1729	struct statfs *mntbufp;
1730	char errmsg[255];
1731	int num, i;
1732	int iovlen;
1733	struct nfsex_args eargs;
1734
1735	if (suspend_nfsd != 0)
1736		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1737	v4root_dirpath[0] = '\0';
1738	bzero(&export, sizeof(export));
1739	export.ex_flags = MNT_DELEXPORT;
1740	iov = NULL;
1741	iovlen = 0;
1742	bzero(errmsg, sizeof(errmsg));
1743
1744	/*
1745	 * First, get rid of the old list
1746	 */
1747	if (exphead != NULL)
1748		free_exports(exphead);
1749
1750	/*
1751	 * and the old V4 root dir.
1752	 */
1753	bzero(&eargs, sizeof (eargs));
1754	eargs.export.ex_flags = MNT_DELEXPORT;
1755	if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1756	    errno != ENOENT)
1757		syslog(LOG_ERR, "Can't delete exports for V4:");
1758
1759	/*
1760	 * and clear flag that notes if a public fh has been exported.
1761	 */
1762	has_publicfh = 0;
1763
1764	/*
1765	 * And delete exports that are in the kernel for all local
1766	 * filesystems.
1767	 * XXX: Should know how to handle all local exportable filesystems.
1768	 */
1769	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1770
1771	/* Allocate hash tables, for first call. */
1772	if (exphead == NULL) {
1773		/* Target an average linked list length of 10. */
1774		exphashsize = num / 10;
1775		if (exphashsize < 1)
1776			exphashsize = 1;
1777		else if (exphashsize > 100000)
1778			exphashsize = 100000;
1779		exphead = malloc(exphashsize * sizeof(*exphead));
1780		if (exphead == NULL)
1781			errx(1, "Can't malloc hash table");
1782
1783		for (i = 0; i < exphashsize; i++)
1784			SLIST_INIT(&exphead[i]);
1785	}
1786	if (num > 0) {
1787		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1788		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1789		build_iovec(&iov, &iovlen, "from", NULL, 0);
1790		build_iovec(&iov, &iovlen, "update", NULL, 0);
1791		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1792		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1793	}
1794
1795	for (i = 0; i < num; i++)
1796		delete_export(iov, iovlen, &mntbufp[i], errmsg);
1797
1798	if (iov != NULL) {
1799		/* Free strings allocated by strdup() in getmntopts.c */
1800		free(iov[0].iov_base); /* fstype */
1801		free(iov[2].iov_base); /* fspath */
1802		free(iov[4].iov_base); /* from */
1803		free(iov[6].iov_base); /* update */
1804		free(iov[8].iov_base); /* export */
1805		free(iov[10].iov_base); /* errmsg */
1806
1807		/* free iov, allocated by realloc() */
1808		free(iov);
1809		iovlen = 0;
1810	}
1811
1812	read_exportfile();
1813
1814	/*
1815	 * If there was no public fh, clear any previous one set.
1816	 */
1817	if (has_publicfh == 0)
1818		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1819
1820	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1821	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1822}
1823
1824/*
1825 * Insert an export entry in the appropriate list.
1826 */
1827static void
1828insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
1829{
1830	uint32_t i;
1831
1832	i = EXPHASH(&ep->ex_fs);
1833	SLIST_INSERT_HEAD(&exhp[i], ep, entries);
1834}
1835
1836/*
1837 * Free up the exports lists passed in as arguments.
1838 */
1839static void
1840free_exports(struct exportlisthead *exhp)
1841{
1842	struct exportlist *ep, *ep2;
1843	int i;
1844
1845	for (i = 0; i < exphashsize; i++) {
1846		SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
1847			SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
1848			free_exp(ep);
1849		}
1850		SLIST_INIT(&exhp[i]);
1851	}
1852}
1853
1854/*
1855 * Read the exports file(s) and call get_exportlist_one() for each line.
1856 */
1857static void
1858read_exportfile(void)
1859{
1860	int done, i;
1861
1862	/*
1863	 * Read in the exports file and build the list, calling
1864	 * nmount() as we go along to push the export rules into the kernel.
1865	 */
1866	done = 0;
1867	for (i = 0; exnames[i] != NULL; i++) {
1868		if (debug)
1869			warnx("reading exports from %s", exnames[i]);
1870		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1871			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1872			continue;
1873		}
1874		get_exportlist_one();
1875		fclose(exp_file);
1876		done++;
1877	}
1878	if (done == 0) {
1879		syslog(LOG_ERR, "can't open any exports file");
1880		exit(2);
1881	}
1882}
1883
1884/*
1885 * Delete an exports entry.
1886 */
1887static void
1888delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
1889{
1890	struct xvfsconf vfc;
1891
1892	if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1893		syslog(LOG_ERR, "getvfsbyname() failed for %s",
1894		    fsp->f_fstypename);
1895		return;
1896	}
1897
1898	/*
1899	 * We do not need to delete "export" flag from
1900	 * filesystems that do not have it set.
1901	 */
1902	if (!(fsp->f_flags & MNT_EXPORTED))
1903		return;
1904	/*
1905	 * Do not delete export for network filesystem by
1906	 * passing "export" arg to nmount().
1907	 * It only makes sense to do this for local filesystems.
1908	 */
1909	if (vfc.vfc_flags & VFCF_NETWORK)
1910		return;
1911
1912	iov[1].iov_base = fsp->f_fstypename;
1913	iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1914	iov[3].iov_base = fsp->f_mntonname;
1915	iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1916	iov[5].iov_base = fsp->f_mntfromname;
1917	iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1918	errmsg[0] = '\0';
1919
1920	/*
1921	 * EXDEV is returned when path exists but is not a
1922	 * mount point.  May happens if raced with unmount.
1923	 */
1924	if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
1925	    errno != ENOTSUP && errno != EXDEV) {
1926		syslog(LOG_ERR,
1927		    "can't delete exports for %s: %m %s",
1928		    fsp->f_mntonname, errmsg);
1929	}
1930}
1931
1932/*
1933 * Allocate an export list element
1934 */
1935static struct exportlist *
1936get_exp(void)
1937{
1938	struct exportlist *ep;
1939
1940	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
1941	if (ep == (struct exportlist *)NULL)
1942		out_of_mem();
1943	return (ep);
1944}
1945
1946/*
1947 * Allocate a group list element
1948 */
1949static struct grouplist *
1950get_grp(void)
1951{
1952	struct grouplist *gp;
1953
1954	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
1955	if (gp == (struct grouplist *)NULL)
1956		out_of_mem();
1957	return (gp);
1958}
1959
1960/*
1961 * Clean up upon an error in get_exportlist().
1962 */
1963static void
1964getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
1965{
1966	struct grouplist *tgrp;
1967
1968	if (!(opt_flags & OP_QUIET)) {
1969		if (reason != NULL)
1970			syslog(LOG_ERR, "bad exports list line '%s': %s", line,
1971			    reason);
1972		else
1973			syslog(LOG_ERR, "bad exports list line '%s'", line);
1974	}
1975	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1976		free_exp(ep);
1977	while (grp) {
1978		tgrp = grp;
1979		grp = grp->gr_next;
1980		free_grp(tgrp);
1981	}
1982}
1983
1984/*
1985 * Search the export list for a matching fs.
1986 */
1987static struct exportlist *
1988ex_search(fsid_t *fsid, struct exportlisthead *exhp)
1989{
1990	struct exportlist *ep;
1991	uint32_t i;
1992
1993	i = EXPHASH(fsid);
1994	SLIST_FOREACH(ep, &exhp[i], entries) {
1995		if (ep->ex_fs.val[0] == fsid->val[0] &&
1996		    ep->ex_fs.val[1] == fsid->val[1])
1997			return (ep);
1998	}
1999
2000	return (ep);
2001}
2002
2003/*
2004 * Add a directory path to the list.
2005 */
2006static char *
2007add_expdir(struct dirlist **dpp, char *cp, int len)
2008{
2009	struct dirlist *dp;
2010
2011	dp = malloc(sizeof (struct dirlist));
2012	if (dp == (struct dirlist *)NULL)
2013		out_of_mem();
2014	dp->dp_left = *dpp;
2015	dp->dp_right = (struct dirlist *)NULL;
2016	dp->dp_flag = 0;
2017	dp->dp_hosts = (struct hostlist *)NULL;
2018	dp->dp_dirp = strndup(cp, len);
2019	if (dp->dp_dirp == NULL)
2020		out_of_mem();
2021	*dpp = dp;
2022	return (dp->dp_dirp);
2023}
2024
2025/*
2026 * Hang the dir list element off the dirpath binary tree as required
2027 * and update the entry for host.
2028 */
2029static void
2030hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2031	int flags)
2032{
2033	struct hostlist *hp;
2034	struct dirlist *dp2;
2035
2036	if (flags & OP_ALLDIRS) {
2037		if (ep->ex_defdir)
2038			free((caddr_t)dp);
2039		else
2040			ep->ex_defdir = dp;
2041		if (grp == (struct grouplist *)NULL) {
2042			ep->ex_defdir->dp_flag |= DP_DEFSET;
2043			/* Save the default security flavors list. */
2044			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2045			if (ep->ex_numsecflavors > 0)
2046				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2047				    sizeof(ep->ex_secflavors));
2048		} else while (grp) {
2049			hp = get_ht();
2050			hp->ht_grp = grp;
2051			hp->ht_next = ep->ex_defdir->dp_hosts;
2052			ep->ex_defdir->dp_hosts = hp;
2053			/* Save the security flavors list for this host set. */
2054			grp->gr_numsecflavors = ep->ex_numsecflavors;
2055			if (ep->ex_numsecflavors > 0)
2056				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2057				    sizeof(ep->ex_secflavors));
2058			grp = grp->gr_next;
2059		}
2060	} else {
2061
2062		/*
2063		 * Loop through the directories adding them to the tree.
2064		 */
2065		while (dp) {
2066			dp2 = dp->dp_left;
2067			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
2068			dp = dp2;
2069		}
2070	}
2071}
2072
2073/*
2074 * Traverse the binary tree either updating a node that is already there
2075 * for the new directory or adding the new node.
2076 */
2077static void
2078add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2079	int flags, struct exportlist *ep)
2080{
2081	struct dirlist *dp;
2082	struct hostlist *hp;
2083	int cmp;
2084
2085	dp = *dpp;
2086	if (dp) {
2087		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2088		if (cmp > 0) {
2089			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
2090			return;
2091		} else if (cmp < 0) {
2092			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
2093			return;
2094		} else
2095			free((caddr_t)newdp);
2096	} else {
2097		dp = newdp;
2098		dp->dp_left = (struct dirlist *)NULL;
2099		*dpp = dp;
2100	}
2101	if (grp) {
2102
2103		/*
2104		 * Hang all of the host(s) off of the directory point.
2105		 */
2106		do {
2107			hp = get_ht();
2108			hp->ht_grp = grp;
2109			hp->ht_next = dp->dp_hosts;
2110			dp->dp_hosts = hp;
2111			/* Save the security flavors list for this host set. */
2112			grp->gr_numsecflavors = ep->ex_numsecflavors;
2113			if (ep->ex_numsecflavors > 0)
2114				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2115				    sizeof(ep->ex_secflavors));
2116			grp = grp->gr_next;
2117		} while (grp);
2118	} else {
2119		dp->dp_flag |= DP_DEFSET;
2120		/* Save the default security flavors list. */
2121		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2122		if (ep->ex_numsecflavors > 0)
2123			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2124			    sizeof(ep->ex_secflavors));
2125	}
2126}
2127
2128/*
2129 * Search for a dirpath on the export point.
2130 */
2131static struct dirlist *
2132dirp_search(struct dirlist *dp, char *dirp)
2133{
2134	int cmp;
2135
2136	if (dp) {
2137		cmp = strcmp(dp->dp_dirp, dirp);
2138		if (cmp > 0)
2139			return (dirp_search(dp->dp_left, dirp));
2140		else if (cmp < 0)
2141			return (dirp_search(dp->dp_right, dirp));
2142		else
2143			return (dp);
2144	}
2145	return (dp);
2146}
2147
2148/*
2149 * Scan for a host match in a directory tree.
2150 */
2151static int
2152chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2153	int *hostsetp, int *numsecflavors, int **secflavorsp)
2154{
2155	struct hostlist *hp;
2156	struct grouplist *grp;
2157	struct addrinfo *ai;
2158
2159	if (dp) {
2160		if (dp->dp_flag & DP_DEFSET)
2161			*defsetp = dp->dp_flag;
2162		hp = dp->dp_hosts;
2163		while (hp) {
2164			grp = hp->ht_grp;
2165			switch (grp->gr_type) {
2166			case GT_HOST:
2167				ai = grp->gr_ptr.gt_addrinfo;
2168				for (; ai; ai = ai->ai_next) {
2169					if (!sacmp(ai->ai_addr, saddr, NULL)) {
2170						*hostsetp =
2171						    (hp->ht_flag | DP_HOSTSET);
2172						if (numsecflavors != NULL) {
2173							*numsecflavors =
2174							    grp->gr_numsecflavors;
2175							*secflavorsp =
2176							    grp->gr_secflavors;
2177						}
2178						return (1);
2179					}
2180				}
2181				break;
2182			case GT_NET:
2183				if (!sacmp(saddr, (struct sockaddr *)
2184				    &grp->gr_ptr.gt_net.nt_net,
2185				    (struct sockaddr *)
2186				    &grp->gr_ptr.gt_net.nt_mask)) {
2187					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2188					if (numsecflavors != NULL) {
2189						*numsecflavors =
2190						    grp->gr_numsecflavors;
2191						*secflavorsp =
2192						    grp->gr_secflavors;
2193					}
2194					return (1);
2195				}
2196				break;
2197			}
2198			hp = hp->ht_next;
2199		}
2200	}
2201	return (0);
2202}
2203
2204/*
2205 * Scan tree for a host that matches the address.
2206 */
2207static int
2208scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2209{
2210	int defset, hostset;
2211
2212	if (dp) {
2213		if (scan_tree(dp->dp_left, saddr))
2214			return (1);
2215		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2216			return (1);
2217		if (scan_tree(dp->dp_right, saddr))
2218			return (1);
2219	}
2220	return (0);
2221}
2222
2223/*
2224 * Traverse the dirlist tree and free it up.
2225 */
2226static void
2227free_dir(struct dirlist *dp)
2228{
2229
2230	if (dp) {
2231		free_dir(dp->dp_left);
2232		free_dir(dp->dp_right);
2233		free_host(dp->dp_hosts);
2234		free(dp->dp_dirp);
2235		free(dp);
2236	}
2237}
2238
2239/*
2240 * Parse a colon separated list of security flavors
2241 */
2242static int
2243parsesec(char *seclist, struct exportlist *ep)
2244{
2245	char *cp, savedc;
2246	int flavor;
2247
2248	ep->ex_numsecflavors = 0;
2249	for (;;) {
2250		cp = strchr(seclist, ':');
2251		if (cp) {
2252			savedc = *cp;
2253			*cp = '\0';
2254		}
2255
2256		if (!strcmp(seclist, "sys"))
2257			flavor = AUTH_SYS;
2258		else if (!strcmp(seclist, "krb5"))
2259			flavor = RPCSEC_GSS_KRB5;
2260		else if (!strcmp(seclist, "krb5i"))
2261			flavor = RPCSEC_GSS_KRB5I;
2262		else if (!strcmp(seclist, "krb5p"))
2263			flavor = RPCSEC_GSS_KRB5P;
2264		else {
2265			if (cp)
2266				*cp = savedc;
2267			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2268			return (1);
2269		}
2270		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2271			if (cp)
2272				*cp = savedc;
2273			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2274			return (1);
2275		}
2276		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2277		ep->ex_numsecflavors++;
2278		if (cp) {
2279			*cp = savedc;
2280			seclist = cp + 1;
2281		} else {
2282			break;
2283		}
2284	}
2285	return (0);
2286}
2287
2288/*
2289 * Parse the option string and update fields.
2290 * Option arguments may either be -<option>=<value> or
2291 * -<option> <value>
2292 */
2293static int
2294do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2295	int *has_hostp, int *exflagsp, struct xucred *cr)
2296{
2297	char *cpoptarg, *cpoptend;
2298	char *cp, *endcp, *cpopt, savedc, savedc2;
2299	int allflag, usedarg;
2300
2301	savedc2 = '\0';
2302	cpopt = *cpp;
2303	cpopt++;
2304	cp = *endcpp;
2305	savedc = *cp;
2306	*cp = '\0';
2307	while (cpopt && *cpopt) {
2308		allflag = 1;
2309		usedarg = -2;
2310		if ((cpoptend = strchr(cpopt, ','))) {
2311			*cpoptend++ = '\0';
2312			if ((cpoptarg = strchr(cpopt, '=')))
2313				*cpoptarg++ = '\0';
2314		} else {
2315			if ((cpoptarg = strchr(cpopt, '=')))
2316				*cpoptarg++ = '\0';
2317			else {
2318				*cp = savedc;
2319				nextfield(&cp, &endcp);
2320				**endcpp = '\0';
2321				if (endcp > cp && *cp != '-') {
2322					cpoptarg = cp;
2323					savedc2 = *endcp;
2324					*endcp = '\0';
2325					usedarg = 0;
2326				}
2327			}
2328		}
2329		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2330			*exflagsp |= MNT_EXRDONLY;
2331		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2332		    !(allflag = strcmp(cpopt, "mapall")) ||
2333		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2334			usedarg++;
2335			parsecred(cpoptarg, cr);
2336			if (allflag == 0) {
2337				*exflagsp |= MNT_EXPORTANON;
2338				opt_flags |= OP_MAPALL;
2339			} else
2340				opt_flags |= OP_MAPROOT;
2341		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2342		    !strcmp(cpopt, "m"))) {
2343			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2344				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2345				return (1);
2346			}
2347			usedarg++;
2348			opt_flags |= OP_MASK;
2349		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2350			!strcmp(cpopt, "n"))) {
2351			if (strchr(cpoptarg, '/') != NULL) {
2352				if (debug)
2353					fprintf(stderr, "setting OP_MASKLEN\n");
2354				opt_flags |= OP_MASKLEN;
2355			}
2356			if (grp->gr_type != GT_NULL) {
2357				syslog(LOG_ERR, "network/host conflict");
2358				return (1);
2359			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2360				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2361				return (1);
2362			}
2363			grp->gr_type = GT_NET;
2364			*has_hostp = 1;
2365			usedarg++;
2366			opt_flags |= OP_NET;
2367		} else if (!strcmp(cpopt, "alldirs")) {
2368			opt_flags |= OP_ALLDIRS;
2369		} else if (!strcmp(cpopt, "public")) {
2370			*exflagsp |= MNT_EXPUBLIC;
2371		} else if (!strcmp(cpopt, "webnfs")) {
2372			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2373			opt_flags |= OP_MAPALL;
2374		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2375			ep->ex_indexfile = strdup(cpoptarg);
2376		} else if (!strcmp(cpopt, "quiet")) {
2377			opt_flags |= OP_QUIET;
2378		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2379			if (parsesec(cpoptarg, ep))
2380				return (1);
2381			opt_flags |= OP_SEC;
2382			usedarg++;
2383		} else {
2384			syslog(LOG_ERR, "bad opt %s", cpopt);
2385			return (1);
2386		}
2387		if (usedarg >= 0) {
2388			*endcp = savedc2;
2389			**endcpp = savedc;
2390			if (usedarg > 0) {
2391				*cpp = cp;
2392				*endcpp = endcp;
2393			}
2394			return (0);
2395		}
2396		cpopt = cpoptend;
2397	}
2398	**endcpp = savedc;
2399	return (0);
2400}
2401
2402/*
2403 * Translate a character string to the corresponding list of network
2404 * addresses for a hostname.
2405 */
2406static int
2407get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2408{
2409	struct grouplist *checkgrp;
2410	struct addrinfo *ai, *tai, hints;
2411	int ecode;
2412	char host[NI_MAXHOST];
2413
2414	if (grp->gr_type != GT_NULL) {
2415		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2416		return (1);
2417	}
2418	memset(&hints, 0, sizeof hints);
2419	hints.ai_flags = AI_CANONNAME;
2420	hints.ai_protocol = IPPROTO_UDP;
2421	ecode = getaddrinfo(cp, NULL, &hints, &ai);
2422	if (ecode != 0) {
2423		syslog(LOG_ERR,"can't get address info for host %s", cp);
2424		return 1;
2425	}
2426	grp->gr_ptr.gt_addrinfo = ai;
2427	while (ai != NULL) {
2428		if (ai->ai_canonname == NULL) {
2429			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2430			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2431				strlcpy(host, "?", sizeof(host));
2432			ai->ai_canonname = strdup(host);
2433			ai->ai_flags |= AI_CANONNAME;
2434		}
2435		if (debug)
2436			fprintf(stderr, "got host %s\n", ai->ai_canonname);
2437		/*
2438		 * Sanity check: make sure we don't already have an entry
2439		 * for this host in the grouplist.
2440		 */
2441		for (checkgrp = tgrp; checkgrp != NULL;
2442		    checkgrp = checkgrp->gr_next) {
2443			if (checkgrp->gr_type != GT_HOST)
2444				continue;
2445			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2446			    tai = tai->ai_next) {
2447				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2448					continue;
2449				if (debug)
2450					fprintf(stderr,
2451					    "ignoring duplicate host %s\n",
2452					    ai->ai_canonname);
2453				grp->gr_type = GT_IGNORE;
2454				return (0);
2455			}
2456		}
2457		ai = ai->ai_next;
2458	}
2459	grp->gr_type = GT_HOST;
2460	return (0);
2461}
2462
2463/*
2464 * Free up an exports list component
2465 */
2466static void
2467free_exp(struct exportlist *ep)
2468{
2469	struct grouplist *grp, *tgrp;
2470
2471	if (ep->ex_defdir) {
2472		free_host(ep->ex_defdir->dp_hosts);
2473		free((caddr_t)ep->ex_defdir);
2474	}
2475	if (ep->ex_fsdir)
2476		free(ep->ex_fsdir);
2477	if (ep->ex_indexfile)
2478		free(ep->ex_indexfile);
2479	free_dir(ep->ex_dirl);
2480	grp = ep->ex_grphead;
2481	while (grp) {
2482		tgrp = grp;
2483		grp = grp->gr_next;
2484		free_grp(tgrp);
2485	}
2486	free((caddr_t)ep);
2487}
2488
2489/*
2490 * Free hosts.
2491 */
2492static void
2493free_host(struct hostlist *hp)
2494{
2495	struct hostlist *hp2;
2496
2497	while (hp) {
2498		hp2 = hp;
2499		hp = hp->ht_next;
2500		free((caddr_t)hp2);
2501	}
2502}
2503
2504static struct hostlist *
2505get_ht(void)
2506{
2507	struct hostlist *hp;
2508
2509	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2510	if (hp == (struct hostlist *)NULL)
2511		out_of_mem();
2512	hp->ht_next = (struct hostlist *)NULL;
2513	hp->ht_flag = 0;
2514	return (hp);
2515}
2516
2517/*
2518 * Out of memory, fatal
2519 */
2520static void
2521out_of_mem(void)
2522{
2523
2524	syslog(LOG_ERR, "out of memory");
2525	exit(2);
2526}
2527
2528/*
2529 * Do the nmount() syscall with the update flag to push the export info into
2530 * the kernel.
2531 */
2532static int
2533do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2534    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
2535{
2536	struct statfs fsb1;
2537	struct addrinfo *ai;
2538	struct export_args *eap;
2539	char errmsg[255];
2540	char *cp;
2541	int done;
2542	char savedc;
2543	struct iovec *iov;
2544	int i, iovlen;
2545	int ret;
2546	struct nfsex_args nfsea;
2547
2548	eap = &nfsea.export;
2549
2550	cp = NULL;
2551	savedc = '\0';
2552	iov = NULL;
2553	iovlen = 0;
2554	ret = 0;
2555
2556	bzero(eap, sizeof (struct export_args));
2557	bzero(errmsg, sizeof(errmsg));
2558	eap->ex_flags = exflags;
2559	eap->ex_anon = *anoncrp;
2560	eap->ex_indexfile = ep->ex_indexfile;
2561	if (grp->gr_type == GT_HOST)
2562		ai = grp->gr_ptr.gt_addrinfo;
2563	else
2564		ai = NULL;
2565	eap->ex_numsecflavors = ep->ex_numsecflavors;
2566	for (i = 0; i < eap->ex_numsecflavors; i++)
2567		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2568	if (eap->ex_numsecflavors == 0) {
2569		eap->ex_numsecflavors = 1;
2570		eap->ex_secflavors[0] = AUTH_SYS;
2571	}
2572	done = FALSE;
2573
2574	if (v4root_phase == 0) {
2575		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2576		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2577		build_iovec(&iov, &iovlen, "from", NULL, 0);
2578		build_iovec(&iov, &iovlen, "update", NULL, 0);
2579		build_iovec(&iov, &iovlen, "export", eap,
2580		    sizeof (struct export_args));
2581		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2582	}
2583
2584	while (!done) {
2585		switch (grp->gr_type) {
2586		case GT_HOST:
2587			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
2588				goto skip;
2589			eap->ex_addr = ai->ai_addr;
2590			eap->ex_addrlen = ai->ai_addrlen;
2591			eap->ex_masklen = 0;
2592			break;
2593		case GT_NET:
2594			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
2595			    have_v6 == 0)
2596				goto skip;
2597			eap->ex_addr =
2598			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2599			eap->ex_addrlen =
2600			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2601			eap->ex_mask =
2602			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2603			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
2604			break;
2605		case GT_DEFAULT:
2606			eap->ex_addr = NULL;
2607			eap->ex_addrlen = 0;
2608			eap->ex_mask = NULL;
2609			eap->ex_masklen = 0;
2610			break;
2611		case GT_IGNORE:
2612			ret = 0;
2613			goto error_exit;
2614			break;
2615		default:
2616			syslog(LOG_ERR, "bad grouptype");
2617			if (cp)
2618				*cp = savedc;
2619			ret = 1;
2620			goto error_exit;
2621		}
2622
2623		/*
2624		 * For V4:, use the nfssvc() syscall, instead of mount().
2625		 */
2626		if (v4root_phase == 2) {
2627			nfsea.fspec = v4root_dirpath;
2628			if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2629				syslog(LOG_ERR, "Exporting V4: failed");
2630				return (2);
2631			}
2632		} else {
2633			/*
2634			 * XXX:
2635			 * Maybe I should just use the fsb->f_mntonname path
2636			 * instead of looping back up the dirp to the mount
2637			 * point??
2638			 * Also, needs to know how to export all types of local
2639			 * exportable filesystems and not just "ufs".
2640			 */
2641			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2642			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2643			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2644			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2645			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2646			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2647			errmsg[0] = '\0';
2648
2649			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2650				if (cp)
2651					*cp-- = savedc;
2652				else
2653					cp = dirp + dirplen - 1;
2654				if (opt_flags & OP_QUIET) {
2655					ret = 1;
2656					goto error_exit;
2657				}
2658				if (errno == EPERM) {
2659					if (debug)
2660						warnx("can't change attributes for %s: %s",
2661						    dirp, errmsg);
2662					syslog(LOG_ERR,
2663					   "can't change attributes for %s: %s",
2664					    dirp, errmsg);
2665					ret = 1;
2666					goto error_exit;
2667				}
2668				if (opt_flags & OP_ALLDIRS) {
2669					if (errno == EINVAL)
2670						syslog(LOG_ERR,
2671		"-alldirs requested but %s is not a filesystem mountpoint",
2672						    dirp);
2673					else
2674						syslog(LOG_ERR,
2675						    "could not remount %s: %m",
2676						    dirp);
2677					ret = 1;
2678					goto error_exit;
2679				}
2680				/* back up over the last component */
2681				while (*cp == '/' && cp > dirp)
2682					cp--;
2683				while (*(cp - 1) != '/' && cp > dirp)
2684					cp--;
2685				if (cp == dirp) {
2686					if (debug)
2687						warnx("mnt unsucc");
2688					syslog(LOG_ERR, "can't export %s %s",
2689					    dirp, errmsg);
2690					ret = 1;
2691					goto error_exit;
2692				}
2693				savedc = *cp;
2694				*cp = '\0';
2695				/*
2696				 * Check that we're still on the same
2697				 * filesystem.
2698				 */
2699				if (statfs(dirp, &fsb1) != 0 ||
2700				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2701				    sizeof (fsb1.f_fsid)) != 0) {
2702					*cp = savedc;
2703					syslog(LOG_ERR,
2704					    "can't export %s %s", dirp,
2705					    errmsg);
2706					ret = 1;
2707					goto error_exit;
2708				}
2709			}
2710		}
2711
2712		/*
2713		 * For the experimental server:
2714		 * If this is the public directory, get the file handle
2715		 * and load it into the kernel via the nfssvc() syscall.
2716		 */
2717		if ((exflags & MNT_EXPUBLIC) != 0) {
2718			fhandle_t fh;
2719			char *public_name;
2720
2721			if (eap->ex_indexfile != NULL)
2722				public_name = eap->ex_indexfile;
2723			else
2724				public_name = dirp;
2725			if (getfh(public_name, &fh) < 0)
2726				syslog(LOG_ERR,
2727				    "Can't get public fh for %s", public_name);
2728			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2729				syslog(LOG_ERR,
2730				    "Can't set public fh for %s", public_name);
2731			else
2732				has_publicfh = 1;
2733		}
2734skip:
2735		if (ai != NULL)
2736			ai = ai->ai_next;
2737		if (ai == NULL)
2738			done = TRUE;
2739	}
2740	if (cp)
2741		*cp = savedc;
2742error_exit:
2743	/* free strings allocated by strdup() in getmntopts.c */
2744	if (iov != NULL) {
2745		free(iov[0].iov_base); /* fstype */
2746		free(iov[2].iov_base); /* fspath */
2747		free(iov[4].iov_base); /* from */
2748		free(iov[6].iov_base); /* update */
2749		free(iov[8].iov_base); /* export */
2750		free(iov[10].iov_base); /* errmsg */
2751
2752		/* free iov, allocated by realloc() */
2753		free(iov);
2754	}
2755	return (ret);
2756}
2757
2758/*
2759 * Translate a net address.
2760 *
2761 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
2762 */
2763static int
2764get_net(char *cp, struct netmsk *net, int maskflg)
2765{
2766	struct netent *np = NULL;
2767	char *name, *p, *prefp;
2768	struct sockaddr_in sin;
2769	struct sockaddr *sa = NULL;
2770	struct addrinfo hints, *ai = NULL;
2771	char netname[NI_MAXHOST];
2772	long preflen;
2773
2774	p = prefp = NULL;
2775	if ((opt_flags & OP_MASKLEN) && !maskflg) {
2776		p = strchr(cp, '/');
2777		*p = '\0';
2778		prefp = p + 1;
2779	}
2780
2781	/*
2782	 * Check for a numeric address first. We wish to avoid
2783	 * possible DNS lookups in getnetbyname().
2784	 */
2785	if (isxdigit(*cp) || *cp == ':') {
2786		memset(&hints, 0, sizeof hints);
2787		/* Ensure the mask and the network have the same family. */
2788		if (maskflg && (opt_flags & OP_NET))
2789			hints.ai_family = net->nt_net.ss_family;
2790		else if (!maskflg && (opt_flags & OP_HAVEMASK))
2791			hints.ai_family = net->nt_mask.ss_family;
2792		else
2793			hints.ai_family = AF_UNSPEC;
2794		hints.ai_flags = AI_NUMERICHOST;
2795		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2796			sa = ai->ai_addr;
2797		if (sa != NULL && ai->ai_family == AF_INET) {
2798			/*
2799			 * The address in `cp' is really a network address, so
2800			 * use inet_network() to re-interpret this correctly.
2801			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
2802			 */
2803			bzero(&sin, sizeof sin);
2804			sin.sin_family = AF_INET;
2805			sin.sin_len = sizeof sin;
2806			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
2807			if (debug)
2808				fprintf(stderr, "get_net: v4 addr %s\n",
2809				    inet_ntoa(sin.sin_addr));
2810			sa = (struct sockaddr *)&sin;
2811		}
2812	}
2813	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
2814		bzero(&sin, sizeof sin);
2815		sin.sin_family = AF_INET;
2816		sin.sin_len = sizeof sin;
2817		sin.sin_addr = inet_makeaddr(np->n_net, 0);
2818		sa = (struct sockaddr *)&sin;
2819	}
2820	if (sa == NULL)
2821		goto fail;
2822
2823	if (maskflg) {
2824		/* The specified sockaddr is a mask. */
2825		if (checkmask(sa) != 0)
2826			goto fail;
2827		bcopy(sa, &net->nt_mask, sa->sa_len);
2828		opt_flags |= OP_HAVEMASK;
2829	} else {
2830		/* The specified sockaddr is a network address. */
2831		bcopy(sa, &net->nt_net, sa->sa_len);
2832
2833		/* Get a network name for the export list. */
2834		if (np) {
2835			name = np->n_name;
2836		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2837		   NULL, 0, NI_NUMERICHOST) == 0) {
2838			name = netname;
2839		} else {
2840			goto fail;
2841		}
2842		if ((net->nt_name = strdup(name)) == NULL)
2843			out_of_mem();
2844
2845		/*
2846		 * Extract a mask from either a "/<masklen>" suffix, or
2847		 * from the class of an IPv4 address.
2848		 */
2849		if (opt_flags & OP_MASKLEN) {
2850			preflen = strtol(prefp, NULL, 10);
2851			if (preflen < 0L || preflen == LONG_MAX)
2852				goto fail;
2853			bcopy(sa, &net->nt_mask, sa->sa_len);
2854			if (makemask(&net->nt_mask, (int)preflen) != 0)
2855				goto fail;
2856			opt_flags |= OP_HAVEMASK;
2857			*p = '/';
2858		} else if (sa->sa_family == AF_INET &&
2859		    (opt_flags & OP_MASK) == 0) {
2860			in_addr_t addr;
2861
2862			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2863			if (IN_CLASSA(addr))
2864				preflen = 8;
2865			else if (IN_CLASSB(addr))
2866				preflen = 16;
2867			else if (IN_CLASSC(addr))
2868				preflen = 24;
2869			else if (IN_CLASSD(addr))
2870				preflen = 28;
2871			else
2872				preflen = 32;	/* XXX */
2873
2874			bcopy(sa, &net->nt_mask, sa->sa_len);
2875			makemask(&net->nt_mask, (int)preflen);
2876			opt_flags |= OP_HAVEMASK;
2877		}
2878	}
2879
2880	if (ai)
2881		freeaddrinfo(ai);
2882	return 0;
2883
2884fail:
2885	if (ai)
2886		freeaddrinfo(ai);
2887	return 1;
2888}
2889
2890/*
2891 * Parse out the next white space separated field
2892 */
2893static void
2894nextfield(char **cp, char **endcp)
2895{
2896	char *p;
2897	char quot = 0;
2898
2899	p = *cp;
2900	while (*p == ' ' || *p == '\t')
2901		p++;
2902	*cp = p;
2903	while (*p != '\0') {
2904		if (quot) {
2905			if (*p == quot)
2906				quot = 0;
2907		} else {
2908			if (*p == '\\' && *(p + 1) != '\0')
2909				p++;
2910			else if (*p == '\'' || *p == '"')
2911				quot = *p;
2912			else if (*p == ' ' || *p == '\t')
2913				break;
2914		}
2915		p++;
2916	};
2917	*endcp = p;
2918}
2919
2920/*
2921 * Get an exports file line. Skip over blank lines and handle line
2922 * continuations.
2923 */
2924static int
2925get_line(void)
2926{
2927	char *p, *cp;
2928	size_t len;
2929	int totlen, cont_line;
2930
2931	/*
2932	 * Loop around ignoring blank lines and getting all continuation lines.
2933	 */
2934	p = line;
2935	totlen = 0;
2936	do {
2937		if ((p = fgetln(exp_file, &len)) == NULL)
2938			return (0);
2939		cp = p + len - 1;
2940		cont_line = 0;
2941		while (cp >= p &&
2942		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2943			if (*cp == '\\')
2944				cont_line = 1;
2945			cp--;
2946			len--;
2947		}
2948		if (cont_line) {
2949			*++cp = ' ';
2950			len++;
2951		}
2952		if (linesize < len + totlen + 1) {
2953			linesize = len + totlen + 1;
2954			line = realloc(line, linesize);
2955			if (line == NULL)
2956				out_of_mem();
2957		}
2958		memcpy(line + totlen, p, len);
2959		totlen += len;
2960		line[totlen] = '\0';
2961	} while (totlen == 0 || cont_line);
2962	return (1);
2963}
2964
2965/*
2966 * Parse a description of a credential.
2967 */
2968static void
2969parsecred(char *namelist, struct xucred *cr)
2970{
2971	char *name;
2972	int cnt;
2973	char *names;
2974	struct passwd *pw;
2975	struct group *gr;
2976	gid_t groups[XU_NGROUPS + 1];
2977	int ngroups;
2978
2979	cr->cr_version = XUCRED_VERSION;
2980	/*
2981	 * Set up the unprivileged user.
2982	 */
2983	cr->cr_uid = 65534;
2984	cr->cr_groups[0] = 65533;
2985	cr->cr_ngroups = 1;
2986	/*
2987	 * Get the user's password table entry.
2988	 */
2989	names = namelist;
2990	name = strsep_quote(&names, ":");
2991	/* Bug?  name could be NULL here */
2992	if (isdigit(*name) || *name == '-')
2993		pw = getpwuid(atoi(name));
2994	else
2995		pw = getpwnam(name);
2996	/*
2997	 * Credentials specified as those of a user.
2998	 */
2999	if (names == NULL) {
3000		if (pw == NULL) {
3001			syslog(LOG_ERR, "unknown user: %s", name);
3002			return;
3003		}
3004		cr->cr_uid = pw->pw_uid;
3005		ngroups = XU_NGROUPS + 1;
3006		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
3007			syslog(LOG_ERR, "too many groups");
3008			ngroups = XU_NGROUPS + 1;
3009		}
3010
3011		/*
3012		 * Compress out duplicate.
3013		 */
3014		cr->cr_ngroups = ngroups - 1;
3015		cr->cr_groups[0] = groups[0];
3016		for (cnt = 2; cnt < ngroups; cnt++)
3017			cr->cr_groups[cnt - 1] = groups[cnt];
3018		return;
3019	}
3020	/*
3021	 * Explicit credential specified as a colon separated list:
3022	 *	uid:gid:gid:...
3023	 */
3024	if (pw != NULL)
3025		cr->cr_uid = pw->pw_uid;
3026	else if (isdigit(*name) || *name == '-')
3027		cr->cr_uid = atoi(name);
3028	else {
3029		syslog(LOG_ERR, "unknown user: %s", name);
3030		return;
3031	}
3032	cr->cr_ngroups = 0;
3033	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
3034		name = strsep_quote(&names, ":");
3035		if (isdigit(*name) || *name == '-') {
3036			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
3037		} else {
3038			if ((gr = getgrnam(name)) == NULL) {
3039				syslog(LOG_ERR, "unknown group: %s", name);
3040				continue;
3041			}
3042			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
3043		}
3044	}
3045	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
3046		syslog(LOG_ERR, "too many groups");
3047}
3048
3049#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
3050/*
3051 * Routines that maintain the remote mounttab
3052 */
3053static void
3054get_mountlist(void)
3055{
3056	struct mountlist *mlp;
3057	char *host, *dirp, *cp;
3058	char str[STRSIZ];
3059	FILE *mlfile;
3060
3061	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3062		if (errno == ENOENT)
3063			return;
3064		else {
3065			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3066			return;
3067		}
3068	}
3069	while (fgets(str, STRSIZ, mlfile) != NULL) {
3070		cp = str;
3071		host = strsep(&cp, " \t\n");
3072		dirp = strsep(&cp, " \t\n");
3073		if (host == NULL || dirp == NULL)
3074			continue;
3075		mlp = (struct mountlist *)malloc(sizeof (*mlp));
3076		if (mlp == (struct mountlist *)NULL)
3077			out_of_mem();
3078		strncpy(mlp->ml_host, host, MNTNAMLEN);
3079		mlp->ml_host[MNTNAMLEN] = '\0';
3080		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3081		mlp->ml_dirp[MNTPATHLEN] = '\0';
3082
3083		SLIST_INSERT_HEAD(&mlhead, mlp, next);
3084	}
3085	fclose(mlfile);
3086}
3087
3088static void
3089del_mlist(char *hostp, char *dirp)
3090{
3091	struct mountlist *mlp, *mlp2;
3092	FILE *mlfile;
3093	int fnd = 0;
3094
3095	SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3096		if (!strcmp(mlp->ml_host, hostp) &&
3097		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3098			fnd = 1;
3099			SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3100			free((caddr_t)mlp);
3101		}
3102	}
3103	if (fnd) {
3104		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3105			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3106			return;
3107		}
3108		SLIST_FOREACH(mlp, &mlhead, next) {
3109			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3110		}
3111		fclose(mlfile);
3112	}
3113}
3114
3115static void
3116add_mlist(char *hostp, char *dirp)
3117{
3118	struct mountlist *mlp;
3119	FILE *mlfile;
3120
3121	SLIST_FOREACH(mlp, &mlhead, next) {
3122		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3123			return;
3124	}
3125
3126	mlp = (struct mountlist *)malloc(sizeof (*mlp));
3127	if (mlp == (struct mountlist *)NULL)
3128		out_of_mem();
3129	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3130	mlp->ml_host[MNTNAMLEN] = '\0';
3131	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3132	mlp->ml_dirp[MNTPATHLEN] = '\0';
3133	SLIST_INSERT_HEAD(&mlhead, mlp, next);
3134	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3135		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3136		return;
3137	}
3138	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3139	fclose(mlfile);
3140}
3141
3142/*
3143 * Free up a group list.
3144 */
3145static void
3146free_grp(struct grouplist *grp)
3147{
3148	if (grp->gr_type == GT_HOST) {
3149		if (grp->gr_ptr.gt_addrinfo != NULL)
3150			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3151	} else if (grp->gr_type == GT_NET) {
3152		if (grp->gr_ptr.gt_net.nt_name)
3153			free(grp->gr_ptr.gt_net.nt_name);
3154	}
3155	free((caddr_t)grp);
3156}
3157
3158#ifdef DEBUG
3159static void
3160SYSLOG(int pri, const char *fmt, ...)
3161{
3162	va_list ap;
3163
3164	va_start(ap, fmt);
3165	vfprintf(stderr, fmt, ap);
3166	va_end(ap);
3167}
3168#endif /* DEBUG */
3169
3170/*
3171 * Check options for consistency.
3172 */
3173static int
3174check_options(struct dirlist *dp)
3175{
3176
3177	if (v4root_phase == 0 && dp == NULL)
3178	    return (1);
3179	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3180	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3181	    return (1);
3182	}
3183	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3184		syslog(LOG_ERR, "-mask requires -network");
3185		return (1);
3186	}
3187	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3188		syslog(LOG_ERR, "-network requires mask specification");
3189		return (1);
3190	}
3191	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3192		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3193		return (1);
3194	}
3195	if (v4root_phase > 0 &&
3196	    (opt_flags &
3197	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3198	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3199	    return (1);
3200	}
3201	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3202	    syslog(LOG_ERR, "-alldirs has multiple directories");
3203	    return (1);
3204	}
3205	return (0);
3206}
3207
3208/*
3209 * Check an absolute directory path for any symbolic links. Return true
3210 */
3211static int
3212check_dirpath(char *dirp)
3213{
3214	char *cp;
3215	int ret = 1;
3216	struct stat sb;
3217
3218	cp = dirp + 1;
3219	while (*cp && ret) {
3220		if (*cp == '/') {
3221			*cp = '\0';
3222			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3223				ret = 0;
3224			*cp = '/';
3225		}
3226		cp++;
3227	}
3228	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3229		ret = 0;
3230	return (ret);
3231}
3232
3233/*
3234 * Make a netmask according to the specified prefix length. The ss_family
3235 * and other non-address fields must be initialised before calling this.
3236 */
3237static int
3238makemask(struct sockaddr_storage *ssp, int bitlen)
3239{
3240	u_char *p;
3241	int bits, i, len;
3242
3243	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3244		return (-1);
3245	if (bitlen > len * CHAR_BIT)
3246		return (-1);
3247
3248	for (i = 0; i < len; i++) {
3249		bits = MIN(CHAR_BIT, bitlen);
3250		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3251		bitlen -= bits;
3252	}
3253	return 0;
3254}
3255
3256/*
3257 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3258 * is acceptable (i.e. of the form 1...10....0).
3259 */
3260static int
3261checkmask(struct sockaddr *sa)
3262{
3263	u_char *mask;
3264	int i, len;
3265
3266	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3267		return (-1);
3268
3269	for (i = 0; i < len; i++)
3270		if (mask[i] != 0xff)
3271			break;
3272	if (i < len) {
3273		if (~mask[i] & (u_char)(~mask[i] + 1))
3274			return (-1);
3275		i++;
3276	}
3277	for (; i < len; i++)
3278		if (mask[i] != 0)
3279			return (-1);
3280	return (0);
3281}
3282
3283/*
3284 * Compare two sockaddrs according to a specified mask. Return zero if
3285 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3286 * If samask is NULL, perform a full comparison.
3287 */
3288static int
3289sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3290{
3291	unsigned char *p1, *p2, *mask;
3292	int len, i;
3293
3294	if (sa1->sa_family != sa2->sa_family ||
3295	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3296	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3297		return (1);
3298
3299	switch (sa1->sa_family) {
3300	case AF_INET6:
3301		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3302		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3303			return (1);
3304		break;
3305	}
3306
3307	/* Simple binary comparison if no mask specified. */
3308	if (samask == NULL)
3309		return (memcmp(p1, p2, len));
3310
3311	/* Set up the mask, and do a mask-based comparison. */
3312	if (sa1->sa_family != samask->sa_family ||
3313	    (mask = sa_rawaddr(samask, NULL)) == NULL)
3314		return (1);
3315
3316	for (i = 0; i < len; i++)
3317		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3318			return (1);
3319	return (0);
3320}
3321
3322/*
3323 * Return a pointer to the part of the sockaddr that contains the
3324 * raw address, and set *nbytes to its length in bytes. Returns
3325 * NULL if the address family is unknown.
3326 */
3327static void *
3328sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3329	void *p;
3330	int len;
3331
3332	switch (sa->sa_family) {
3333	case AF_INET:
3334		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3335		p = &((struct sockaddr_in *)sa)->sin_addr;
3336		break;
3337	case AF_INET6:
3338		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3339		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3340		break;
3341	default:
3342		p = NULL;
3343		len = 0;
3344	}
3345
3346	if (nbytes != NULL)
3347		*nbytes = len;
3348	return (p);
3349}
3350
3351static void
3352huphandler(int sig __unused)
3353{
3354
3355	got_sighup = 1;
3356}
3357
3358static void
3359terminate(int sig __unused)
3360{
3361	pidfile_remove(pfh);
3362	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3363	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3364	exit (0);
3365}
3366