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