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