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