xref: /freebsd/usr.sbin/mountd/mountd.c (revision dd21556857e8d40f66bf5ad54754d9d52669ebf7)
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 <netdb.h>
68 #include <pwd.h>
69 #include <signal.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74 #include <vis.h>
75 #include "pathnames.h"
76 #include "mntopts.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_WARNING,
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 	if (iov != NULL) {
2103 		/* Free strings allocated by strdup() in getmntopts.c */
2104 		free(iov[0].iov_base); /* fstype */
2105 		free(iov[2].iov_base); /* fspath */
2106 		free(iov[4].iov_base); /* from */
2107 		free(iov[6].iov_base); /* update */
2108 		free(iov[8].iov_base); /* export */
2109 		free(iov[10].iov_base); /* errmsg */
2110 
2111 		/* free iov, allocated by realloc() */
2112 		free(iov);
2113 		iovlen = 0;
2114 	}
2115 
2116 	/*
2117 	 * If there was no public fh, clear any previous one set.
2118 	 */
2119 	if (has_publicfh == 0) {
2120 		LOGDEBUG("clear public fh");
2121 		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
2122 	}
2123 
2124 	/* Resume the nfsd. If they weren't suspended, this is harmless. */
2125 	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
2126 	LOGDEBUG("eo get_exportlist");
2127 }
2128 
2129 /*
2130  * Insert an export entry in the appropriate list.
2131  */
2132 static void
2133 insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
2134 {
2135 	uint32_t i;
2136 
2137 	i = EXPHASH(&ep->ex_fs);
2138 	LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i);
2139 	SLIST_INSERT_HEAD(&exhp[i], ep, entries);
2140 }
2141 
2142 /*
2143  * Free up the exports lists passed in as arguments.
2144  */
2145 static void
2146 free_exports(struct exportlisthead *exhp)
2147 {
2148 	struct exportlist *ep, *ep2;
2149 	int i;
2150 
2151 	for (i = 0; i < exphashsize; i++) {
2152 		SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
2153 			SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
2154 			free_exp(ep);
2155 		}
2156 		SLIST_INIT(&exhp[i]);
2157 	}
2158 }
2159 
2160 /*
2161  * Read the exports file(s) and call get_exportlist_one() for each line.
2162  */
2163 static void
2164 read_exportfile(int passno)
2165 {
2166 	int done, i;
2167 
2168 	/*
2169 	 * Read in the exports file and build the list, calling
2170 	 * nmount() as we go along to push the export rules into the kernel.
2171 	 */
2172 	done = 0;
2173 	for (i = 0; exnames[i] != NULL; i++) {
2174 		if (debug)
2175 			warnx("reading exports from %s", exnames[i]);
2176 		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
2177 			syslog(LOG_WARNING, "can't open %s", exnames[i]);
2178 			continue;
2179 		}
2180 		get_exportlist_one(passno);
2181 		fclose(exp_file);
2182 		done++;
2183 	}
2184 	if (done == 0) {
2185 		syslog(LOG_ERR, "can't open any exports file");
2186 		exit(2);
2187 	}
2188 }
2189 
2190 /*
2191  * Compare the export lists against the old ones and do nmount() operations
2192  * for any cases that have changed.  This avoids doing nmount() for entries
2193  * that have not changed.
2194  * Return 0 upon success, 1 otherwise.
2195  */
2196 static int
2197 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg)
2198 {
2199 	struct exportlist *ep, *oep;
2200 	struct grouplist *grp;
2201 	struct statfs fs, ofs;
2202 	int i, ret;
2203 
2204 	/*
2205 	 * Loop through the current list and look for an entry in the old
2206 	 * list.
2207 	 * If found, check to see if it the same.
2208 	 *        If it is not the same, delete and re-export.
2209 	 *        Then mark it done on the old list.
2210 	 * else (not found)
2211 	 *        export it.
2212 	 * Any entries left in the old list after processing must have their
2213 	 * exports deleted.
2214 	 */
2215 	for (i = 0; i < exphashsize; i++)
2216 		SLIST_FOREACH(ep, &exphead[i], entries) {
2217 			LOGDEBUG("foreach ep=%s", ep->ex_fsdir);
2218 			oep = ex_search(&ep->ex_fs, oldexphead);
2219 			if (oep != NULL) {
2220 				/*
2221 				 * Check the mount paths are the same.
2222 				 * If not, return 1 so that the reload of the
2223 				 * exports will be done in bulk, the
2224 				 * passno == 0 way.
2225 				 */
2226 				LOGDEBUG("found old exp");
2227 				if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2228 					return (1);
2229 				LOGDEBUG("same fsdir");
2230 				/*
2231 				 * Test to see if the entry is the same.
2232 				 * If not the same delete exports and
2233 				 * re-export.
2234 				 */
2235 				if (compare_export(ep, oep) != 0) {
2236 					/*
2237 					 * Clear has_publicfh if if was set
2238 					 * in the old exports, but only if it
2239 					 * has not been set during processing of
2240 					 * the exports for this pass, as
2241 					 * indicated by has_set_publicfh.
2242 					 */
2243 					if (has_set_publicfh == 0 &&
2244 					    (oep->ex_flag & EX_PUBLICFH) != 0)
2245 						has_publicfh = 0;
2246 
2247 					/* Delete and re-export. */
2248 					if (statfs(ep->ex_fsdir, &fs) < 0)
2249 						return (1);
2250 					delete_export(iov, iovlen, &fs, errmsg);
2251 					ret = do_export_mount(ep, &fs);
2252 					if (ret != 0)
2253 						return (ret);
2254 				}
2255 				oep->ex_flag |= EX_DONE;
2256 				LOGDEBUG("exdone");
2257 			} else {
2258 				LOGDEBUG("not found so export");
2259 				/* Not found, so do export. */
2260 				if (statfs(ep->ex_fsdir, &fs) < 0)
2261 					return (1);
2262 				ret = do_export_mount(ep, &fs);
2263 				if (ret != 0)
2264 					return (ret);
2265 			}
2266 		}
2267 
2268 	/* Delete exports not done. */
2269 	for (i = 0; i < exphashsize; i++)
2270 		SLIST_FOREACH(oep, &oldexphead[i], entries) {
2271 			if ((oep->ex_flag & EX_DONE) == 0) {
2272 				LOGDEBUG("not done delete=%s", oep->ex_fsdir);
2273 				if (statfs(oep->ex_fsdir, &ofs) >= 0 &&
2274 				    fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) {
2275 					LOGDEBUG("do delete");
2276 					/*
2277 					 * Clear has_publicfh if if was set
2278 					 * in the old exports, but only if it
2279 					 * has not been set during processing of
2280 					 * the exports for this pass, as
2281 					 * indicated by has_set_publicfh.
2282 					 */
2283 					if (has_set_publicfh == 0 &&
2284 					    (oep->ex_flag & EX_PUBLICFH) != 0)
2285 						has_publicfh = 0;
2286 
2287 					delete_export(iov, iovlen, &ofs,
2288 					    errmsg);
2289 				}
2290 			}
2291 		}
2292 
2293 	/* Do the V4 root exports, as required. */
2294 	grp = NULL;
2295 	if (v4root_ep != NULL)
2296 		grp = v4root_ep->ex_grphead;
2297 	v4root_phase = 2;
2298 	while (v4root_ep != NULL && grp != NULL) {
2299 		LOGDEBUG("v4root expath=%s", v4root_dirpath);
2300 		ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon,
2301 		    v4root_dirpath, strlen(v4root_dirpath), &fs,
2302 		    grp->gr_numsecflavors, grp->gr_secflavors);
2303 		if (ret != 0) {
2304 			v4root_phase = 0;
2305 			return (ret);
2306 		}
2307 		grp = grp->gr_next;
2308 	}
2309 	v4root_phase = 0;
2310 	free_v4rootexp();
2311 	return (0);
2312 }
2313 
2314 /*
2315  * Compare old and current exportlist entries for the fsid and return 0
2316  * if they are the same, 1 otherwise.
2317  */
2318 static int
2319 compare_export(struct exportlist *ep, struct exportlist *oep)
2320 {
2321 	struct grouplist *grp, *ogrp;
2322 
2323 	if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2324 		return (1);
2325 	if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET))
2326 		return (1);
2327 	if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) ||
2328 	    (ep->ex_defdir == NULL && oep->ex_defdir != NULL))
2329 		return (1);
2330 	if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) !=
2331 	    (oep->ex_defdir->dp_flag & DP_DEFSET))
2332 		return (1);
2333 	if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors !=
2334 	    oep->ex_defnumsecflavors || ep->ex_defexflags !=
2335 	    oep->ex_defexflags || compare_cred(&ep->ex_defanon,
2336 	    &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors,
2337 	    oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0))
2338 		return (1);
2339 
2340 	/* Now, check all the groups. */
2341 	for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2342 		ogrp->gr_flag = 0;
2343 	for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) {
2344 		for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp =
2345 		    ogrp->gr_next)
2346 			if ((ogrp->gr_flag & GR_FND) == 0 &&
2347 			    grp->gr_numsecflavors == ogrp->gr_numsecflavors &&
2348 			    grp->gr_exflags == ogrp->gr_exflags &&
2349 			    compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
2350 			    compare_secflavor(grp->gr_secflavors,
2351 			    ogrp->gr_secflavors, grp->gr_numsecflavors) == 0)
2352 				break;
2353 		if (ogrp != NULL)
2354 			ogrp->gr_flag |= GR_FND;
2355 		else
2356 			return (1);
2357 	}
2358 	for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2359 		if ((ogrp->gr_flag & GR_FND) == 0)
2360 			return (1);
2361 	return (0);
2362 }
2363 
2364 /*
2365  * This algorithm compares two arrays of "n" items. It returns 0 if they are
2366  * the "same" and 1 otherwise.  Although suboptimal, it is always safe to
2367  * return 1, which makes compare_nmount_export() reload the exports entry.
2368  * "same" refers to having the same set of values in the two arrays.
2369  * The arrays are in no particular order and duplicates (multiple entries
2370  * in an array with the same value) is allowed.
2371  * The algorithm is inefficient, but the common case of identical arrays is
2372  * handled first and "n" is normally fairly small.
2373  * Since the two functions need the same algorithm but for arrays of
2374  * different types (gid_t vs int), this is done as a macro.
2375  */
2376 #define	COMPARE_ARRAYS(a1, a2, n)					\
2377 	do {								\
2378 		int fnd, fndarray[(n)], i, j;				\
2379 		/* Handle common case of identical arrays. */		\
2380 		for (i = 0; i < (n); i++)				\
2381 			if ((a1)[i] != (a2)[i])				\
2382 				break;					\
2383 		if (i == (n))						\
2384 			return (0);					\
2385 		for (i = 0; i < (n); i++)				\
2386 			fndarray[i] = 0;				\
2387 		for (i = 0; i < (n); i++) {				\
2388 			fnd = 0;					\
2389 			for (j = 0; j < (n); j++) {			\
2390 				if ((a1)[i] == (a2)[j]) {		\
2391 					fndarray[j] = 1;		\
2392 					fnd = 1;			\
2393 				}					\
2394 			}						\
2395 			if (fnd == 0)					\
2396 				return (1);				\
2397 		}							\
2398 		for (i = 0; i < (n); i++)				\
2399 			if (fndarray[i] == 0)				\
2400 				return (1);				\
2401 		return (0);						\
2402 	} while (0)
2403 
2404 /*
2405  * Compare two struct expcred's.  Return 0 if the same and 1 otherwise.
2406  */
2407 static int
2408 compare_cred(struct expcred *cr0, struct expcred *cr1)
2409 {
2410 
2411 	if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups)
2412 		return (1);
2413 
2414 	COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups);
2415 }
2416 
2417 /*
2418  * Compare two lists of security flavors.  Return 0 if the same and 1 otherwise.
2419  */
2420 static int
2421 compare_secflavor(int *sec1, int *sec2, int nsec)
2422 {
2423 
2424 	COMPARE_ARRAYS(sec1, sec2, nsec);
2425 }
2426 
2427 /*
2428  * Delete an exports entry.
2429  */
2430 static void
2431 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
2432 {
2433 	struct xvfsconf vfc;
2434 
2435 	if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
2436 		syslog(LOG_ERR, "getvfsbyname() failed for %s",
2437 		    fsp->f_fstypename);
2438 		return;
2439 	}
2440 
2441 	/*
2442 	 * We do not need to delete "export" flag from
2443 	 * filesystems that do not have it set.
2444 	 */
2445 	if (!(fsp->f_flags & MNT_EXPORTED))
2446 		return;
2447 	/*
2448 	 * Do not delete export for network filesystem by
2449 	 * passing "export" arg to nmount().
2450 	 * It only makes sense to do this for local filesystems.
2451 	 */
2452 	if (vfc.vfc_flags & VFCF_NETWORK)
2453 		return;
2454 
2455 	iov[1].iov_base = fsp->f_fstypename;
2456 	iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
2457 	iov[3].iov_base = fsp->f_mntonname;
2458 	iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
2459 	iov[5].iov_base = fsp->f_mntfromname;
2460 	iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
2461 	errmsg[0] = '\0';
2462 
2463 	/*
2464 	 * EXDEV is returned when path exists but is not a
2465 	 * mount point.  May happens if raced with unmount.
2466 	 */
2467 	if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
2468 	    errno != ENOTSUP && errno != EXDEV) {
2469 		syslog(LOG_ERR,
2470 		    "can't delete exports for %s: %m %s",
2471 		    fsp->f_mntonname, errmsg);
2472 	}
2473 }
2474 
2475 /*
2476  * Allocate an export list element
2477  */
2478 static struct exportlist *
2479 get_exp(void)
2480 {
2481 	struct exportlist *ep;
2482 
2483 	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
2484 	if (ep == (struct exportlist *)NULL)
2485 		out_of_mem();
2486 	return (ep);
2487 }
2488 
2489 /*
2490  * Allocate a group list element
2491  */
2492 static struct grouplist *
2493 get_grp(void)
2494 {
2495 	struct grouplist *gp;
2496 
2497 	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
2498 	if (gp == (struct grouplist *)NULL)
2499 		out_of_mem();
2500 	return (gp);
2501 }
2502 
2503 /*
2504  * Clean up upon an error in get_exportlist().
2505  */
2506 static void
2507 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
2508 {
2509 	struct grouplist *tgrp;
2510 
2511 	if (!(opt_flags & OP_QUIET)) {
2512 		if (reason != NULL)
2513 			syslog(LOG_ERR, "bad exports list line '%s': %s", line,
2514 			    reason);
2515 		else
2516 			syslog(LOG_ERR, "bad exports list line '%s'", line);
2517 	}
2518 	if (ep && (ep->ex_flag & EX_LINKED) == 0)
2519 		free_exp(ep);
2520 	while (grp) {
2521 		tgrp = grp;
2522 		grp = grp->gr_next;
2523 		free_grp(tgrp);
2524 	}
2525 }
2526 
2527 /*
2528  * Search the export list for a matching fs.
2529  */
2530 static struct exportlist *
2531 ex_search(fsid_t *fsid, struct exportlisthead *exhp)
2532 {
2533 	struct exportlist *ep;
2534 	uint32_t i;
2535 
2536 	i = EXPHASH(fsid);
2537 	SLIST_FOREACH(ep, &exhp[i], entries) {
2538 		if (fsidcmp(&ep->ex_fs, fsid) == 0)
2539 			return (ep);
2540 	}
2541 
2542 	return (ep);
2543 }
2544 
2545 /*
2546  * Add a directory path to the list.
2547  */
2548 static char *
2549 add_expdir(struct dirlist **dpp, char *cp, int len)
2550 {
2551 	struct dirlist *dp;
2552 
2553 	dp = malloc(sizeof (struct dirlist));
2554 	if (dp == (struct dirlist *)NULL)
2555 		out_of_mem();
2556 	dp->dp_left = *dpp;
2557 	dp->dp_right = (struct dirlist *)NULL;
2558 	dp->dp_flag = 0;
2559 	dp->dp_hosts = (struct hostlist *)NULL;
2560 	dp->dp_dirp = strndup(cp, len);
2561 	if (dp->dp_dirp == NULL)
2562 		out_of_mem();
2563 	*dpp = dp;
2564 	return (dp->dp_dirp);
2565 }
2566 
2567 /*
2568  * Hang the dir list element off the dirpath binary tree as required
2569  * and update the entry for host.
2570  */
2571 static void
2572 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2573 	int flags, struct expcred *anoncrp, uint64_t exflags)
2574 {
2575 	struct hostlist *hp;
2576 	struct dirlist *dp2;
2577 
2578 	if (flags & OP_ALLDIRS) {
2579 		if (ep->ex_defdir)
2580 			free((caddr_t)dp);
2581 		else
2582 			ep->ex_defdir = dp;
2583 		if (grp == (struct grouplist *)NULL) {
2584 			ep->ex_flag |= EX_DEFSET;
2585 			ep->ex_defdir->dp_flag |= DP_DEFSET;
2586 			/* Save the default security flavors list. */
2587 			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2588 			if (ep->ex_numsecflavors > 0)
2589 				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2590 				    sizeof(ep->ex_secflavors));
2591 			cp_cred(&ep->ex_defanon, anoncrp);
2592 			ep->ex_defexflags = exflags;
2593 		} else while (grp) {
2594 			hp = get_ht();
2595 			hp->ht_grp = grp;
2596 			hp->ht_next = ep->ex_defdir->dp_hosts;
2597 			ep->ex_defdir->dp_hosts = hp;
2598 			/* Save the security flavors list for this host set. */
2599 			grp->gr_numsecflavors = ep->ex_numsecflavors;
2600 			if (ep->ex_numsecflavors > 0)
2601 				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2602 				    sizeof(ep->ex_secflavors));
2603 			grp = grp->gr_next;
2604 		}
2605 	} else {
2606 
2607 		/*
2608 		 * Loop through the directories adding them to the tree.
2609 		 */
2610 		while (dp) {
2611 			dp2 = dp->dp_left;
2612 			add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp,
2613 			    exflags);
2614 			dp = dp2;
2615 		}
2616 	}
2617 }
2618 
2619 /*
2620  * Traverse the binary tree either updating a node that is already there
2621  * for the new directory or adding the new node.
2622  */
2623 static void
2624 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2625 	int flags, struct exportlist *ep, struct expcred *anoncrp,
2626 	uint64_t exflags)
2627 {
2628 	struct dirlist *dp;
2629 	struct hostlist *hp;
2630 	int cmp;
2631 
2632 	dp = *dpp;
2633 	if (dp) {
2634 		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2635 		if (cmp > 0) {
2636 			add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp,
2637 			    exflags);
2638 			return;
2639 		} else if (cmp < 0) {
2640 			add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp,
2641 			    exflags);
2642 			return;
2643 		} else
2644 			free((caddr_t)newdp);
2645 	} else {
2646 		dp = newdp;
2647 		dp->dp_left = (struct dirlist *)NULL;
2648 		*dpp = dp;
2649 	}
2650 	if (grp) {
2651 
2652 		/*
2653 		 * Hang all of the host(s) off of the directory point.
2654 		 */
2655 		do {
2656 			hp = get_ht();
2657 			hp->ht_grp = grp;
2658 			hp->ht_next = dp->dp_hosts;
2659 			dp->dp_hosts = hp;
2660 			/* Save the security flavors list for this host set. */
2661 			grp->gr_numsecflavors = ep->ex_numsecflavors;
2662 			if (ep->ex_numsecflavors > 0)
2663 				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2664 				    sizeof(ep->ex_secflavors));
2665 			grp = grp->gr_next;
2666 		} while (grp);
2667 	} else {
2668 		ep->ex_flag |= EX_DEFSET;
2669 		dp->dp_flag |= DP_DEFSET;
2670 		/* Save the default security flavors list. */
2671 		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2672 		if (ep->ex_numsecflavors > 0)
2673 			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2674 			    sizeof(ep->ex_secflavors));
2675 		cp_cred(&ep->ex_defanon, anoncrp);
2676 		ep->ex_defexflags = exflags;
2677 	}
2678 }
2679 
2680 /*
2681  * Search for a dirpath on the export point.
2682  */
2683 static struct dirlist *
2684 dirp_search(struct dirlist *dp, char *dirp)
2685 {
2686 	int cmp;
2687 
2688 	if (dp) {
2689 		cmp = strcmp(dp->dp_dirp, dirp);
2690 		if (cmp > 0)
2691 			return (dirp_search(dp->dp_left, dirp));
2692 		else if (cmp < 0)
2693 			return (dirp_search(dp->dp_right, dirp));
2694 		else
2695 			return (dp);
2696 	}
2697 	return (dp);
2698 }
2699 
2700 /*
2701  * Scan for a host match in a directory tree.
2702  */
2703 static int
2704 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2705 	int *hostsetp, int *numsecflavors, int **secflavorsp)
2706 {
2707 	struct hostlist *hp;
2708 	struct grouplist *grp;
2709 	struct addrinfo *ai;
2710 
2711 	if (dp) {
2712 		if (dp->dp_flag & DP_DEFSET)
2713 			*defsetp = dp->dp_flag;
2714 		hp = dp->dp_hosts;
2715 		while (hp) {
2716 			grp = hp->ht_grp;
2717 			switch (grp->gr_type) {
2718 			case GT_HOST:
2719 				ai = grp->gr_ptr.gt_addrinfo;
2720 				for (; ai; ai = ai->ai_next) {
2721 					if (!sacmp(ai->ai_addr, saddr, NULL)) {
2722 						*hostsetp =
2723 						    (hp->ht_flag | DP_HOSTSET);
2724 						if (numsecflavors != NULL) {
2725 							*numsecflavors =
2726 							    grp->gr_numsecflavors;
2727 							*secflavorsp =
2728 							    grp->gr_secflavors;
2729 						}
2730 						return (1);
2731 					}
2732 				}
2733 				break;
2734 			case GT_NET:
2735 				if (!sacmp(saddr, (struct sockaddr *)
2736 				    &grp->gr_ptr.gt_net.nt_net,
2737 				    (struct sockaddr *)
2738 				    &grp->gr_ptr.gt_net.nt_mask)) {
2739 					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2740 					if (numsecflavors != NULL) {
2741 						*numsecflavors =
2742 						    grp->gr_numsecflavors;
2743 						*secflavorsp =
2744 						    grp->gr_secflavors;
2745 					}
2746 					return (1);
2747 				}
2748 				break;
2749 			}
2750 			hp = hp->ht_next;
2751 		}
2752 	}
2753 	return (0);
2754 }
2755 
2756 /*
2757  * Scan tree for a host that matches the address.
2758  */
2759 static int
2760 scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2761 {
2762 	int defset, hostset;
2763 
2764 	if (dp) {
2765 		if (scan_tree(dp->dp_left, saddr))
2766 			return (1);
2767 		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2768 			return (1);
2769 		if (scan_tree(dp->dp_right, saddr))
2770 			return (1);
2771 	}
2772 	return (0);
2773 }
2774 
2775 /*
2776  * Traverse the dirlist tree and free it up.
2777  */
2778 static void
2779 free_dir(struct dirlist *dp)
2780 {
2781 
2782 	if (dp) {
2783 		free_dir(dp->dp_left);
2784 		free_dir(dp->dp_right);
2785 		free_host(dp->dp_hosts);
2786 		free(dp->dp_dirp);
2787 		free(dp);
2788 	}
2789 }
2790 
2791 /*
2792  * Parse a colon separated list of security flavors
2793  */
2794 static int
2795 parsesec(char *seclist, struct exportlist *ep)
2796 {
2797 	char *cp, savedc;
2798 	int flavor;
2799 
2800 	ep->ex_numsecflavors = 0;
2801 	for (;;) {
2802 		cp = strchr(seclist, ':');
2803 		if (cp) {
2804 			savedc = *cp;
2805 			*cp = '\0';
2806 		}
2807 
2808 		if (!strcmp(seclist, "sys"))
2809 			flavor = AUTH_SYS;
2810 		else if (!strcmp(seclist, "krb5"))
2811 			flavor = RPCSEC_GSS_KRB5;
2812 		else if (!strcmp(seclist, "krb5i"))
2813 			flavor = RPCSEC_GSS_KRB5I;
2814 		else if (!strcmp(seclist, "krb5p"))
2815 			flavor = RPCSEC_GSS_KRB5P;
2816 		else {
2817 			if (cp)
2818 				*cp = savedc;
2819 			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2820 			return (1);
2821 		}
2822 		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2823 			if (cp)
2824 				*cp = savedc;
2825 			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2826 			return (1);
2827 		}
2828 		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2829 		ep->ex_numsecflavors++;
2830 		if (cp) {
2831 			*cp = savedc;
2832 			seclist = cp + 1;
2833 		} else {
2834 			break;
2835 		}
2836 	}
2837 	return (0);
2838 }
2839 
2840 /*
2841  * Parse the option string and update fields.
2842  * Option arguments may either be -<option>=<value> or
2843  * -<option> <value>
2844  */
2845 static int
2846 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2847 	int *has_hostp, uint64_t *exflagsp, struct expcred *cr, char *unvis_dir)
2848 {
2849 	char *cpoptarg, *cpoptend;
2850 	char *cp, *endcp, *cpopt, savedc, savedc2;
2851 	int allflag, usedarg, fnd_equal;
2852 
2853 	savedc2 = '\0';
2854 	cpopt = *cpp;
2855 	cpopt++;
2856 	cp = *endcpp;
2857 	savedc = *cp;
2858 	*cp = '\0';
2859 	while (cpopt && *cpopt) {
2860 		allflag = 1;
2861 		usedarg = -2;
2862 		fnd_equal = 0;
2863 		if ((cpoptend = strchr(cpopt, ','))) {
2864 			*cpoptend++ = '\0';
2865 			if ((cpoptarg = strchr(cpopt, '='))) {
2866 				*cpoptarg++ = '\0';
2867 				fnd_equal = 1;
2868 			}
2869 		} else {
2870 			if ((cpoptarg = strchr(cpopt, '='))) {
2871 				*cpoptarg++ = '\0';
2872 				fnd_equal = 1;
2873 			} else {
2874 				*cp = savedc;
2875 				nextfield(&cp, &endcp);
2876 				**endcpp = '\0';
2877 				if (endcp > cp && *cp != '-') {
2878 					cpoptarg = cp;
2879 					savedc2 = *endcp;
2880 					*endcp = '\0';
2881 					usedarg = 0;
2882 				}
2883 			}
2884 		}
2885 		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2886 			if (fnd_equal == 1) {
2887 				syslog(LOG_ERR, "= after op: %s", cpopt);
2888 				return (1);
2889 			}
2890 			*exflagsp |= MNT_EXRDONLY;
2891 		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2892 		    !(allflag = strcmp(cpopt, "mapall")) ||
2893 		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2894 			usedarg++;
2895 			parsecred(cpoptarg, cr);
2896 			if (allflag == 0) {
2897 				*exflagsp |= MNT_EXPORTANON;
2898 				opt_flags |= OP_MAPALL;
2899 			} else
2900 				opt_flags |= OP_MAPROOT;
2901 		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2902 		    !strcmp(cpopt, "m"))) {
2903 			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2904 				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2905 				return (1);
2906 			}
2907 			usedarg++;
2908 			opt_flags |= OP_MASK;
2909 		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2910 			!strcmp(cpopt, "n"))) {
2911 			if (strchr(cpoptarg, '/') != NULL) {
2912 				if (debug)
2913 					fprintf(stderr, "setting OP_MASKLEN\n");
2914 				opt_flags |= OP_MASKLEN;
2915 			}
2916 			if (grp->gr_type != GT_NULL) {
2917 				syslog(LOG_ERR, "network/host conflict");
2918 				return (1);
2919 			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2920 				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2921 				return (1);
2922 			}
2923 			grp->gr_type = GT_NET;
2924 			*has_hostp = 1;
2925 			usedarg++;
2926 			opt_flags |= OP_NET;
2927 		} else if (!strcmp(cpopt, "alldirs")) {
2928 			if (fnd_equal == 1) {
2929 				syslog(LOG_ERR, "= after op: %s", cpopt);
2930 				return (1);
2931 			}
2932 			if ((opt_flags & OP_NOTROOT) != 0) {
2933 				syslog(LOG_ERR, "%s: path %s not mount point",
2934 				    cpopt, unvis_dir);
2935 				if (alldirs_fail != 0)
2936 					return (1);
2937 			}
2938 			opt_flags |= OP_ALLDIRS;
2939 		} else if (!strcmp(cpopt, "public")) {
2940 			if (fnd_equal == 1) {
2941 				syslog(LOG_ERR, "= after op: %s", cpopt);
2942 				return (1);
2943 			}
2944 			*exflagsp |= MNT_EXPUBLIC;
2945 		} else if (!strcmp(cpopt, "webnfs")) {
2946 			if (fnd_equal == 1) {
2947 				syslog(LOG_ERR, "= after op: %s", cpopt);
2948 				return (1);
2949 			}
2950 			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2951 			opt_flags |= OP_MAPALL;
2952 		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2953 			ep->ex_indexfile = strdup(cpoptarg);
2954 		} else if (!strcmp(cpopt, "quiet")) {
2955 			if (fnd_equal == 1) {
2956 				syslog(LOG_ERR, "= after op: %s", cpopt);
2957 				return (1);
2958 			}
2959 			opt_flags |= OP_QUIET;
2960 		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2961 			if (parsesec(cpoptarg, ep))
2962 				return (1);
2963 			opt_flags |= OP_SEC;
2964 			usedarg++;
2965 		} else if (!strcmp(cpopt, "tls")) {
2966 			if (fnd_equal == 1) {
2967 				syslog(LOG_ERR, "= after op: %s", cpopt);
2968 				return (1);
2969 			}
2970 			*exflagsp |= MNT_EXTLS;
2971 		} else if (!strcmp(cpopt, "tlscert")) {
2972 			if (fnd_equal == 1) {
2973 				syslog(LOG_ERR, "= after op: %s", cpopt);
2974 				return (1);
2975 			}
2976 			*exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT);
2977 		} else if (!strcmp(cpopt, "tlscertuser")) {
2978 			if (fnd_equal == 1) {
2979 				syslog(LOG_ERR, "= after op: %s", cpopt);
2980 				return (1);
2981 			}
2982 			*exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT |
2983 			    MNT_EXTLSCERTUSER);
2984 		} else {
2985 			syslog(LOG_ERR, "bad opt %s", cpopt);
2986 			return (1);
2987 		}
2988 		if (usedarg >= 0) {
2989 			*endcp = savedc2;
2990 			**endcpp = savedc;
2991 			if (usedarg > 0) {
2992 				*cpp = cp;
2993 				*endcpp = endcp;
2994 			}
2995 			return (0);
2996 		}
2997 		cpopt = cpoptend;
2998 	}
2999 	**endcpp = savedc;
3000 	return (0);
3001 }
3002 
3003 /*
3004  * Translate a character string to the corresponding list of network
3005  * addresses for a hostname.
3006  */
3007 static int
3008 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
3009 {
3010 	struct grouplist *checkgrp;
3011 	struct addrinfo *ai, *tai, hints;
3012 	int ecode;
3013 	char host[NI_MAXHOST];
3014 
3015 	if (grp->gr_type != GT_NULL) {
3016 		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
3017 		return (1);
3018 	}
3019 	memset(&hints, 0, sizeof hints);
3020 	hints.ai_flags = AI_CANONNAME;
3021 	hints.ai_protocol = IPPROTO_UDP;
3022 	ecode = getaddrinfo(cp, NULL, &hints, &ai);
3023 	if (ecode != 0) {
3024 		syslog(LOG_ERR,"can't get address info for host %s", cp);
3025 		return 1;
3026 	}
3027 	grp->gr_ptr.gt_addrinfo = ai;
3028 	while (ai != NULL) {
3029 		if (ai->ai_canonname == NULL) {
3030 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
3031 			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
3032 				strlcpy(host, "?", sizeof(host));
3033 			ai->ai_canonname = strdup(host);
3034 			ai->ai_flags |= AI_CANONNAME;
3035 		}
3036 		if (debug)
3037 			fprintf(stderr, "got host %s\n", ai->ai_canonname);
3038 		/*
3039 		 * Sanity check: make sure we don't already have an entry
3040 		 * for this host in the grouplist.
3041 		 */
3042 		for (checkgrp = tgrp; checkgrp != NULL;
3043 		    checkgrp = checkgrp->gr_next) {
3044 			if (checkgrp->gr_type != GT_HOST)
3045 				continue;
3046 			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
3047 			    tai = tai->ai_next) {
3048 				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
3049 					continue;
3050 				if (debug)
3051 					fprintf(stderr,
3052 					    "ignoring duplicate host %s\n",
3053 					    ai->ai_canonname);
3054 				grp->gr_type = GT_IGNORE;
3055 				return (0);
3056 			}
3057 		}
3058 		ai = ai->ai_next;
3059 	}
3060 	grp->gr_type = GT_HOST;
3061 	return (0);
3062 }
3063 
3064 /*
3065  * Free up an exports list component
3066  */
3067 static void
3068 free_exp(struct exportlist *ep)
3069 {
3070 	struct grouplist *grp, *tgrp;
3071 
3072 	if (ep->ex_defdir) {
3073 		free_host(ep->ex_defdir->dp_hosts);
3074 		free((caddr_t)ep->ex_defdir);
3075 	}
3076 	if (ep->ex_fsdir)
3077 		free(ep->ex_fsdir);
3078 	if (ep->ex_indexfile)
3079 		free(ep->ex_indexfile);
3080 	free_dir(ep->ex_dirl);
3081 	grp = ep->ex_grphead;
3082 	while (grp) {
3083 		tgrp = grp;
3084 		grp = grp->gr_next;
3085 		free_grp(tgrp);
3086 	}
3087 	if (ep->ex_defanon.cr_groups != ep->ex_defanon.cr_smallgrps)
3088 		free(ep->ex_defanon.cr_groups);
3089 	free((caddr_t)ep);
3090 }
3091 
3092 /*
3093  * Free up the v4root exports.
3094  */
3095 static void
3096 free_v4rootexp(void)
3097 {
3098 
3099 	if (v4root_ep != NULL) {
3100 		free_exp(v4root_ep);
3101 		v4root_ep = NULL;
3102 	}
3103 }
3104 
3105 /*
3106  * Free hosts.
3107  */
3108 static void
3109 free_host(struct hostlist *hp)
3110 {
3111 	struct hostlist *hp2;
3112 
3113 	while (hp) {
3114 		hp2 = hp;
3115 		hp = hp->ht_next;
3116 		free((caddr_t)hp2);
3117 	}
3118 }
3119 
3120 static struct hostlist *
3121 get_ht(void)
3122 {
3123 	struct hostlist *hp;
3124 
3125 	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
3126 	if (hp == (struct hostlist *)NULL)
3127 		out_of_mem();
3128 	hp->ht_next = (struct hostlist *)NULL;
3129 	hp->ht_flag = 0;
3130 	return (hp);
3131 }
3132 
3133 /*
3134  * Out of memory, fatal
3135  */
3136 static void
3137 out_of_mem(void)
3138 {
3139 
3140 	syslog(LOG_ERR, "out of memory");
3141 	exit(2);
3142 }
3143 
3144 /*
3145  * Call do_mount() from the struct exportlist, for each case needed.
3146  */
3147 static int
3148 do_export_mount(struct exportlist *ep, struct statfs *fsp)
3149 {
3150 	struct grouplist *grp, defgrp;
3151 	int ret;
3152 	size_t dirlen;
3153 
3154 	LOGDEBUG("do_export_mount=%s", ep->ex_fsdir);
3155 	dirlen = strlen(ep->ex_fsdir);
3156 	if ((ep->ex_flag & EX_DEFSET) != 0) {
3157 		defgrp.gr_type = GT_DEFAULT;
3158 		defgrp.gr_next = NULL;
3159 		/* We have an entry for all other hosts/nets. */
3160 		LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags);
3161 		ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon,
3162 		    ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors,
3163 		    ep->ex_defsecflavors);
3164 		if (ret != 0)
3165 			return (ret);
3166 	}
3167 
3168 	/* Do a mount for each group. */
3169 	grp = ep->ex_grphead;
3170 	while (grp != NULL) {
3171 		LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx",
3172 		    grp->gr_type, (uintmax_t)grp->gr_exflags);
3173 		ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon,
3174 		    ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors,
3175 		    grp->gr_secflavors);
3176 		if (ret != 0)
3177 			return (ret);
3178 		grp = grp->gr_next;
3179 	}
3180 	return (0);
3181 }
3182 
3183 /*
3184  * Do the nmount() syscall with the update flag to push the export info into
3185  * the kernel.
3186  */
3187 static int
3188 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags,
3189     struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb,
3190     int numsecflavors, int *secflavors)
3191 {
3192 	struct statfs fsb1;
3193 	struct addrinfo *ai;
3194 	struct export_args *eap;
3195 	char errmsg[255];
3196 	char *cp;
3197 	int done;
3198 	char savedc;
3199 	struct iovec *iov;
3200 	int i, iovlen;
3201 	int ret;
3202 	struct nfsex_args nfsea;
3203 
3204 	eap = &nfsea.export;
3205 
3206 	cp = NULL;
3207 	savedc = '\0';
3208 	iov = NULL;
3209 	iovlen = 0;
3210 	ret = 0;
3211 
3212 	bzero(eap, sizeof (struct export_args));
3213 	bzero(errmsg, sizeof(errmsg));
3214 	eap->ex_flags = exflags;
3215 	eap->ex_uid = anoncrp->cr_uid;
3216 	eap->ex_ngroups = anoncrp->cr_ngroups;
3217 	/*
3218 	 * Use the memory pointed to by 'anoncrp', as it outlives 'eap' which is
3219 	 * local to this function.
3220 	 */
3221 	eap->ex_groups = anoncrp->cr_groups;
3222 	LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags);
3223 	eap->ex_indexfile = ep->ex_indexfile;
3224 	if (grp->gr_type == GT_HOST)
3225 		ai = grp->gr_ptr.gt_addrinfo;
3226 	else
3227 		ai = NULL;
3228 	eap->ex_numsecflavors = numsecflavors;
3229 	LOGDEBUG("do_mount numsec=%d", numsecflavors);
3230 	for (i = 0; i < eap->ex_numsecflavors; i++)
3231 		eap->ex_secflavors[i] = secflavors[i];
3232 	if (eap->ex_numsecflavors == 0) {
3233 		eap->ex_numsecflavors = 1;
3234 		eap->ex_secflavors[0] = AUTH_SYS;
3235 	}
3236 	done = FALSE;
3237 
3238 	if (v4root_phase == 0) {
3239 		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
3240 		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
3241 		build_iovec(&iov, &iovlen, "from", NULL, 0);
3242 		build_iovec(&iov, &iovlen, "update", NULL, 0);
3243 		build_iovec(&iov, &iovlen, "export", eap,
3244 		    sizeof (struct export_args));
3245 		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
3246 	}
3247 
3248 	while (!done) {
3249 		switch (grp->gr_type) {
3250 		case GT_HOST:
3251 			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
3252 				goto skip;
3253 			eap->ex_addr = ai->ai_addr;
3254 			eap->ex_addrlen = ai->ai_addrlen;
3255 			eap->ex_masklen = 0;
3256 			break;
3257 		case GT_NET:
3258 			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
3259 			    have_v6 == 0)
3260 				goto skip;
3261 			eap->ex_addr =
3262 			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
3263 			eap->ex_addrlen =
3264 			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
3265 			eap->ex_mask =
3266 			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
3267 			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
3268 			break;
3269 		case GT_DEFAULT:
3270 			eap->ex_addr = NULL;
3271 			eap->ex_addrlen = 0;
3272 			eap->ex_mask = NULL;
3273 			eap->ex_masklen = 0;
3274 			break;
3275 		case GT_IGNORE:
3276 			ret = 0;
3277 			goto error_exit;
3278 			break;
3279 		default:
3280 			syslog(LOG_ERR, "bad grouptype");
3281 			if (cp)
3282 				*cp = savedc;
3283 			ret = 1;
3284 			goto error_exit;
3285 		}
3286 
3287 		/*
3288 		 * For V4:, use the nfssvc() syscall, instead of mount().
3289 		 */
3290 		if (v4root_phase == 2) {
3291 			nfsea.fspec = v4root_dirpath;
3292 			if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT,
3293 			    (caddr_t)&nfsea) < 0) {
3294 				syslog(LOG_ERR, "Exporting V4: failed");
3295 				ret = 2;
3296 				goto error_exit;
3297 			}
3298 		} else {
3299 			/*
3300 			 * XXX:
3301 			 * Maybe I should just use the fsb->f_mntonname path
3302 			 * instead of looping back up the dirp to the mount
3303 			 * point??
3304 			 * Also, needs to know how to export all types of local
3305 			 * exportable filesystems and not just "ufs".
3306 			 */
3307 			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
3308 			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
3309 			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
3310 			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
3311 			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
3312 			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
3313 			errmsg[0] = '\0';
3314 
3315 			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
3316 				if (cp)
3317 					*cp-- = savedc;
3318 				else
3319 					cp = dirp + dirplen - 1;
3320 				if (opt_flags & OP_QUIET) {
3321 					ret = 1;
3322 					goto error_exit;
3323 				}
3324 				if (errno == EPERM) {
3325 					if (debug)
3326 						warnx("can't change attributes for %s: %s",
3327 						    dirp, errmsg);
3328 					syslog(LOG_ERR,
3329 					   "can't change attributes for %s: %s",
3330 					    dirp, errmsg);
3331 					ret = 1;
3332 					goto error_exit;
3333 				}
3334 				if ((opt_flags & OP_ALLDIRS) &&
3335 				    alldirs_fail != 0) {
3336 					if (errno == EINVAL)
3337 						syslog(LOG_ERR,
3338 		"-alldirs requested but %s is not a filesystem mountpoint",
3339 						    dirp);
3340 					else
3341 						syslog(LOG_ERR,
3342 						    "could not remount %s: %m",
3343 						    dirp);
3344 					ret = 1;
3345 					goto error_exit;
3346 				}
3347 				/* back up over the last component */
3348 				while (cp > dirp && *cp == '/')
3349 					cp--;
3350 				while (cp > dirp && *(cp - 1) != '/')
3351 					cp--;
3352 				if (cp == dirp) {
3353 					if (debug)
3354 						warnx("mnt unsucc");
3355 					syslog(LOG_ERR, "can't export %s %s",
3356 					    dirp, errmsg);
3357 					ret = 1;
3358 					goto error_exit;
3359 				}
3360 				savedc = *cp;
3361 				*cp = '\0';
3362 				/*
3363 				 * Check that we're still on the same
3364 				 * filesystem.
3365 				 */
3366 				if (statfs(dirp, &fsb1) != 0 ||
3367 				    fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) {
3368 					*cp = savedc;
3369 					syslog(LOG_ERR,
3370 					    "can't export %s %s", dirp,
3371 					    errmsg);
3372 					ret = 1;
3373 					goto error_exit;
3374 				}
3375 			}
3376 		}
3377 
3378 		/*
3379 		 * For the experimental server:
3380 		 * If this is the public directory, get the file handle
3381 		 * and load it into the kernel via the nfssvc() syscall.
3382 		 */
3383 		if ((exflags & MNT_EXPUBLIC) != 0) {
3384 			fhandle_t fh;
3385 			char *public_name;
3386 
3387 			if (eap->ex_indexfile != NULL)
3388 				public_name = eap->ex_indexfile;
3389 			else
3390 				public_name = dirp;
3391 			if (getfh(public_name, &fh) < 0)
3392 				syslog(LOG_ERR,
3393 				    "Can't get public fh for %s", public_name);
3394 			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
3395 				syslog(LOG_ERR,
3396 				    "Can't set public fh for %s", public_name);
3397 			else {
3398 				has_publicfh = 1;
3399 				has_set_publicfh = 1;
3400 				ep->ex_flag |= EX_PUBLICFH;
3401 			}
3402 		}
3403 skip:
3404 		if (ai != NULL)
3405 			ai = ai->ai_next;
3406 		if (ai == NULL)
3407 			done = TRUE;
3408 	}
3409 	if (cp)
3410 		*cp = savedc;
3411 error_exit:
3412 	/* free strings allocated by strdup() in getmntopts.c */
3413 	if (iov != NULL) {
3414 		free(iov[0].iov_base); /* fstype */
3415 		free(iov[2].iov_base); /* fspath */
3416 		free(iov[4].iov_base); /* from */
3417 		free(iov[6].iov_base); /* update */
3418 		free(iov[8].iov_base); /* export */
3419 		free(iov[10].iov_base); /* errmsg */
3420 
3421 		/* free iov, allocated by realloc() */
3422 		free(iov);
3423 	}
3424 	return (ret);
3425 }
3426 
3427 /*
3428  * Translate a net address.
3429  *
3430  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
3431  */
3432 static int
3433 get_net(char *cp, struct netmsk *net, int maskflg)
3434 {
3435 	struct netent *np = NULL;
3436 	char *name, *p, *prefp;
3437 	struct sockaddr_in sin;
3438 	struct sockaddr *sa = NULL;
3439 	struct addrinfo hints, *ai = NULL;
3440 	char netname[NI_MAXHOST];
3441 	long preflen;
3442 
3443 	p = prefp = NULL;
3444 	if ((opt_flags & OP_MASKLEN) && !maskflg) {
3445 		p = strchr(cp, '/');
3446 		*p = '\0';
3447 		prefp = p + 1;
3448 	}
3449 
3450 	/*
3451 	 * Check for a numeric address first. We wish to avoid
3452 	 * possible DNS lookups in getnetbyname().
3453 	 */
3454 	if (isxdigit(*cp) || *cp == ':') {
3455 		memset(&hints, 0, sizeof hints);
3456 		/* Ensure the mask and the network have the same family. */
3457 		if (maskflg && (opt_flags & OP_NET))
3458 			hints.ai_family = net->nt_net.ss_family;
3459 		else if (!maskflg && (opt_flags & OP_HAVEMASK))
3460 			hints.ai_family = net->nt_mask.ss_family;
3461 		else
3462 			hints.ai_family = AF_UNSPEC;
3463 		hints.ai_flags = AI_NUMERICHOST;
3464 		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
3465 			sa = ai->ai_addr;
3466 		if (sa != NULL && ai->ai_family == AF_INET) {
3467 			/*
3468 			 * The address in `cp' is really a network address, so
3469 			 * use inet_network() to re-interpret this correctly.
3470 			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
3471 			 */
3472 			bzero(&sin, sizeof sin);
3473 			sin.sin_family = AF_INET;
3474 			sin.sin_len = sizeof sin;
3475 			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
3476 			if (debug)
3477 				fprintf(stderr, "get_net: v4 addr %s\n",
3478 				    inet_ntoa(sin.sin_addr));
3479 			sa = (struct sockaddr *)&sin;
3480 		}
3481 	}
3482 	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
3483 		bzero(&sin, sizeof sin);
3484 		sin.sin_family = AF_INET;
3485 		sin.sin_len = sizeof sin;
3486 		sin.sin_addr = inet_makeaddr(np->n_net, 0);
3487 		sa = (struct sockaddr *)&sin;
3488 	}
3489 	if (sa == NULL)
3490 		goto fail;
3491 
3492 	if (maskflg) {
3493 		/* The specified sockaddr is a mask. */
3494 		if (checkmask(sa) != 0)
3495 			goto fail;
3496 		bcopy(sa, &net->nt_mask, sa->sa_len);
3497 		opt_flags |= OP_HAVEMASK;
3498 		opt_flags &= ~OP_CLASSMASK;
3499 	} else {
3500 		/* The specified sockaddr is a network address. */
3501 		bcopy(sa, &net->nt_net, sa->sa_len);
3502 
3503 		/* Get a network name for the export list. */
3504 		if (np) {
3505 			name = np->n_name;
3506 		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
3507 		   NULL, 0, NI_NUMERICHOST) == 0) {
3508 			name = netname;
3509 		} else {
3510 			goto fail;
3511 		}
3512 		if ((net->nt_name = strdup(name)) == NULL)
3513 			out_of_mem();
3514 
3515 		/*
3516 		 * Extract a mask from either a "/<masklen>" suffix, or
3517 		 * from the class of an IPv4 address.
3518 		 */
3519 		if (opt_flags & OP_MASKLEN) {
3520 			preflen = strtol(prefp, NULL, 10);
3521 			if (preflen < 0L || preflen == LONG_MAX)
3522 				goto fail;
3523 			bcopy(sa, &net->nt_mask, sa->sa_len);
3524 			if (makemask(&net->nt_mask, (int)preflen) != 0)
3525 				goto fail;
3526 			opt_flags |= OP_HAVEMASK;
3527 			*p = '/';
3528 		} else if (sa->sa_family == AF_INET &&
3529 		    (opt_flags & OP_MASK) == 0) {
3530 			in_addr_t addr;
3531 
3532 			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
3533 			if (IN_CLASSA(addr))
3534 				preflen = 8;
3535 			else if (IN_CLASSB(addr))
3536 				preflen = 16;
3537 			else if (IN_CLASSC(addr))
3538 				preflen = 24;
3539 			else if (IN_CLASSD(addr))	/* XXX Multicast??? */
3540 				preflen = 28;
3541 			else
3542 				preflen = 32;	/* XXX */
3543 
3544 			bcopy(sa, &net->nt_mask, sa->sa_len);
3545 			makemask(&net->nt_mask, (int)preflen);
3546 			opt_flags |= OP_HAVEMASK | OP_CLASSMASK;
3547 		}
3548 	}
3549 
3550 	if (ai)
3551 		freeaddrinfo(ai);
3552 	return 0;
3553 
3554 fail:
3555 	if (ai)
3556 		freeaddrinfo(ai);
3557 	return 1;
3558 }
3559 
3560 /*
3561  * Parse out the next white space separated field
3562  */
3563 static void
3564 nextfield(char **cp, char **endcp)
3565 {
3566 	char *p;
3567 	char quot = 0;
3568 
3569 	p = *cp;
3570 	while (*p == ' ' || *p == '\t')
3571 		p++;
3572 	*cp = p;
3573 	while (*p != '\0') {
3574 		if (quot) {
3575 			if (*p == quot)
3576 				quot = 0;
3577 		} else {
3578 			if (*p == '\\' && *(p + 1) != '\0')
3579 				p++;
3580 			else if (*p == '\'' || *p == '"')
3581 				quot = *p;
3582 			else if (*p == ' ' || *p == '\t')
3583 				break;
3584 		}
3585 		p++;
3586 	};
3587 	*endcp = p;
3588 }
3589 
3590 /*
3591  * Get an exports file line. Skip over blank lines and handle line
3592  * continuations.
3593  */
3594 static int
3595 get_line(void)
3596 {
3597 	char *p, *cp;
3598 	size_t len;
3599 	int totlen, cont_line;
3600 
3601 	/*
3602 	 * Loop around ignoring blank lines and getting all continuation lines.
3603 	 */
3604 	p = line;
3605 	totlen = 0;
3606 	do {
3607 		if ((p = fgetln(exp_file, &len)) == NULL)
3608 			return (0);
3609 		cp = p + len - 1;
3610 		cont_line = 0;
3611 		while (cp >= p &&
3612 		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
3613 			if (*cp == '\\')
3614 				cont_line = 1;
3615 			cp--;
3616 			len--;
3617 		}
3618 		if (cont_line) {
3619 			*++cp = ' ';
3620 			len++;
3621 		}
3622 		if (linesize < len + totlen + 1) {
3623 			linesize = len + totlen + 1;
3624 			line = realloc(line, linesize);
3625 			if (line == NULL)
3626 				out_of_mem();
3627 		}
3628 		memcpy(line + totlen, p, len);
3629 		totlen += len;
3630 		line[totlen] = '\0';
3631 	} while (totlen == 0 || cont_line);
3632 	return (1);
3633 }
3634 
3635 /*
3636  * Parse a description of a credential.
3637  */
3638 static void
3639 parsecred(char *names, struct expcred *cr)
3640 {
3641 	char *name;
3642 	struct passwd *pw;
3643 	unsigned long name_ul;
3644 	char *end = NULL;
3645 
3646 	assert(cr->cr_groups == tmp_groups);
3647 
3648 	/*
3649 	 * Parse the user and if possible get its password table entry.
3650 	 * 'cr_uid' is filled when exiting this block.
3651 	 */
3652 	name = strsep_quote(&names, ":");
3653 	name_ul = strtoul(name, &end, 10);
3654 	if (*end != '\0' || end == name)
3655 		pw = getpwnam(name);
3656 	else
3657 		pw = getpwuid((uid_t)name_ul);
3658 	if (pw != NULL) {
3659 		cr->cr_uid = pw->pw_uid;
3660 	} else if (*end != '\0' || end == name) {
3661 		syslog(LOG_ERR, "unknown user: %s", name);
3662 		cr->cr_uid = UID_NOBODY;
3663 		goto nogroup;
3664 	} else {
3665 		cr->cr_uid = name_ul;
3666 	}
3667 
3668 	/*
3669 	 * Credentials specified as those of a user (i.e., use its associated
3670 	 * groups as specified in the password database).
3671 	 */
3672 	if (names == NULL) {
3673 		if (pw == NULL) {
3674 			syslog(LOG_ERR, "no passwd entry for user: %s, "
3675 			    "can't determine groups", name);
3676 			goto nogroup;
3677 		}
3678 
3679 		cr->cr_ngroups = tngroups_max;
3680 		if (getgrouplist(pw->pw_name, pw->pw_gid,
3681 		    cr->cr_groups, &cr->cr_ngroups) != 0) {
3682 			syslog(LOG_ERR, "too many groups");
3683 			cr->cr_ngroups = tngroups_max;
3684 		}
3685 		return;
3686 	}
3687 
3688 	/*
3689 	 * Explicit credentials specified as a colon separated list:
3690 	 *	uid:gid:gid:...
3691 	 */
3692 	cr->cr_ngroups = 0;
3693 	while (names != NULL && *names != '\0') {
3694 		const struct group *gr;
3695 		gid_t group;
3696 
3697 		name = strsep_quote(&names, ":");
3698 		name_ul = strtoul(name, &end, 10);
3699 		if (*end != '\0' || end == name) {
3700 			if ((gr = getgrnam(name)) == NULL) {
3701 				syslog(LOG_ERR, "unknown group: %s", name);
3702 				continue;
3703 			}
3704 			group = gr->gr_gid;
3705 		} else {
3706 			group = name_ul;
3707 		}
3708 		if (cr->cr_ngroups == tngroups_max) {
3709 			syslog(LOG_ERR, "too many groups");
3710 			break;
3711 		}
3712 		cr->cr_groups[cr->cr_ngroups++] = group;
3713 	}
3714 	if (cr->cr_ngroups == 0)
3715 		goto nogroup;
3716 	return;
3717 
3718 nogroup:
3719 	cr->cr_ngroups = 1;
3720 	cr->cr_groups[0] = nogroup();
3721 }
3722 
3723 #define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
3724 /*
3725  * Routines that maintain the remote mounttab
3726  */
3727 static void
3728 get_mountlist(void)
3729 {
3730 	struct mountlist *mlp;
3731 	char *host, *dirp, *cp;
3732 	char str[STRSIZ];
3733 	FILE *mlfile;
3734 
3735 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3736 		if (errno == ENOENT)
3737 			return;
3738 		else {
3739 			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3740 			return;
3741 		}
3742 	}
3743 	while (fgets(str, STRSIZ, mlfile) != NULL) {
3744 		cp = str;
3745 		host = strsep(&cp, " \t\n");
3746 		dirp = strsep(&cp, " \t\n");
3747 		if (host == NULL || dirp == NULL)
3748 			continue;
3749 		mlp = (struct mountlist *)malloc(sizeof (*mlp));
3750 		if (mlp == (struct mountlist *)NULL)
3751 			out_of_mem();
3752 		strncpy(mlp->ml_host, host, MNTNAMLEN);
3753 		mlp->ml_host[MNTNAMLEN] = '\0';
3754 		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3755 		mlp->ml_dirp[MNTPATHLEN] = '\0';
3756 
3757 		SLIST_INSERT_HEAD(&mlhead, mlp, next);
3758 	}
3759 	fclose(mlfile);
3760 }
3761 
3762 static void
3763 del_mlist(char *hostp, char *dirp)
3764 {
3765 	struct mountlist *mlp, *mlp2;
3766 	FILE *mlfile;
3767 	int fnd = 0;
3768 
3769 	SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3770 		if (!strcmp(mlp->ml_host, hostp) &&
3771 		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3772 			fnd = 1;
3773 			SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3774 			free((caddr_t)mlp);
3775 		}
3776 	}
3777 	if (fnd) {
3778 		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3779 			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3780 			return;
3781 		}
3782 		SLIST_FOREACH(mlp, &mlhead, next) {
3783 			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3784 		}
3785 		fclose(mlfile);
3786 	}
3787 }
3788 
3789 static void
3790 add_mlist(char *hostp, char *dirp)
3791 {
3792 	struct mountlist *mlp;
3793 	FILE *mlfile;
3794 
3795 	SLIST_FOREACH(mlp, &mlhead, next) {
3796 		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3797 			return;
3798 	}
3799 
3800 	mlp = (struct mountlist *)malloc(sizeof (*mlp));
3801 	if (mlp == (struct mountlist *)NULL)
3802 		out_of_mem();
3803 	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3804 	mlp->ml_host[MNTNAMLEN] = '\0';
3805 	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3806 	mlp->ml_dirp[MNTPATHLEN] = '\0';
3807 	SLIST_INSERT_HEAD(&mlhead, mlp, next);
3808 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3809 		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3810 		return;
3811 	}
3812 	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3813 	fclose(mlfile);
3814 }
3815 
3816 /*
3817  * Free up a group list.
3818  */
3819 static void
3820 free_grp(struct grouplist *grp)
3821 {
3822 	if (grp->gr_type == GT_HOST) {
3823 		if (grp->gr_ptr.gt_addrinfo != NULL)
3824 			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3825 	} else if (grp->gr_type == GT_NET) {
3826 		if (grp->gr_ptr.gt_net.nt_name)
3827 			free(grp->gr_ptr.gt_net.nt_name);
3828 	}
3829 	if (grp->gr_anon.cr_groups != grp->gr_anon.cr_smallgrps)
3830 		free(grp->gr_anon.cr_groups);
3831 	free((caddr_t)grp);
3832 }
3833 
3834 #ifdef DEBUG
3835 static void
3836 SYSLOG(int pri, const char *fmt, ...)
3837 {
3838 	va_list ap;
3839 
3840 	va_start(ap, fmt);
3841 	vfprintf(stderr, fmt, ap);
3842 	va_end(ap);
3843 }
3844 #endif /* DEBUG */
3845 
3846 /*
3847  * Check options for consistency.
3848  */
3849 static int
3850 check_options(struct dirlist *dp)
3851 {
3852 
3853 	if (v4root_phase == 0 && dp == NULL)
3854 	    return (1);
3855 	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3856 	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3857 	    return (1);
3858 	}
3859 	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3860 		syslog(LOG_ERR, "-mask requires -network");
3861 		return (1);
3862 	}
3863 	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3864 		syslog(LOG_ERR, "-network requires mask specification");
3865 		return (1);
3866 	}
3867 	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3868 		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3869 		return (1);
3870 	}
3871 	if (v4root_phase > 0 &&
3872 	    (opt_flags &
3873 	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3874 	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3875 	    return (1);
3876 	}
3877 	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3878 	    syslog(LOG_ERR, "-alldirs has multiple directories");
3879 	    return (1);
3880 	}
3881 	return (0);
3882 }
3883 
3884 static int
3885 check_path_component(const char *path, char **err)
3886 {
3887 	struct stat sb;
3888 
3889 	if (lstat(path, &sb)) {
3890 		asprintf(err, "%s: lstat() failed: %s.\n",
3891 		    path, strerror(errno));
3892 		return (0);
3893 	}
3894 
3895 	switch (sb.st_mode & S_IFMT) {
3896 	case S_IFDIR:
3897 		return (1);
3898 	case S_IFLNK:
3899 		asprintf(err, "%s: path is a symbolic link.\n", path);
3900 		break;
3901 	case S_IFREG:
3902 		asprintf(err, "%s: path is a file rather than a directory.\n",
3903 		    path);
3904 		break;
3905 	default:
3906 		asprintf(err, "%s: path is not a directory.\n", path);
3907 	}
3908 
3909 	return (0);
3910 }
3911 
3912 /*
3913  * Check each path component for the presence of symbolic links. Return true
3914  */
3915 static int
3916 check_dirpath(char *dirp, char **err)
3917 {
3918 	char *cp;
3919 
3920 	cp = dirp + 1;
3921 	while (*cp) {
3922 		if (*cp == '/') {
3923 			*cp = '\0';
3924 
3925 			if (!check_path_component(dirp, err)) {
3926 				*cp = '/';
3927 				return (0);
3928 			}
3929 
3930 			*cp = '/';
3931 		}
3932 		cp++;
3933 	}
3934 
3935 	if (!check_path_component(dirp, err))
3936 		return (0);
3937 
3938 	return (1);
3939 }
3940 
3941 /*
3942  * Populate statfs information. Return true on success.
3943  */
3944 static int
3945 check_statfs(const char *dirp, struct statfs *fsb, char **err)
3946 {
3947 	if (statfs(dirp, fsb)) {
3948 		asprintf(err, "%s: statfs() failed: %s\n", dirp,
3949 		    strerror(errno));
3950 		return (0);
3951 	}
3952 
3953 	return (1);
3954 }
3955 
3956 /*
3957  * Make a netmask according to the specified prefix length. The ss_family
3958  * and other non-address fields must be initialised before calling this.
3959  */
3960 static int
3961 makemask(struct sockaddr_storage *ssp, int bitlen)
3962 {
3963 	u_char *p;
3964 	int bits, i, len;
3965 
3966 	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3967 		return (-1);
3968 	if (bitlen > len * CHAR_BIT)
3969 		return (-1);
3970 
3971 	for (i = 0; i < len; i++) {
3972 		bits = MIN(CHAR_BIT, bitlen);
3973 		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3974 		bitlen -= bits;
3975 	}
3976 	return 0;
3977 }
3978 
3979 /*
3980  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3981  * is acceptable (i.e. of the form 1...10....0).
3982  */
3983 static int
3984 checkmask(struct sockaddr *sa)
3985 {
3986 	u_char *mask;
3987 	int i, len;
3988 
3989 	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3990 		return (-1);
3991 
3992 	for (i = 0; i < len; i++)
3993 		if (mask[i] != 0xff)
3994 			break;
3995 	if (i < len) {
3996 		if (~mask[i] & (u_char)(~mask[i] + 1))
3997 			return (-1);
3998 		i++;
3999 	}
4000 	for (; i < len; i++)
4001 		if (mask[i] != 0)
4002 			return (-1);
4003 	return (0);
4004 }
4005 
4006 /*
4007  * Compare two sockaddrs according to a specified mask. Return zero if
4008  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
4009  * If samask is NULL, perform a full comparison.
4010  */
4011 static int
4012 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
4013 {
4014 	unsigned char *p1, *p2, *mask;
4015 	int len, i;
4016 
4017 	if (sa1->sa_family != sa2->sa_family ||
4018 	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
4019 	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
4020 		return (1);
4021 
4022 	switch (sa1->sa_family) {
4023 	case AF_INET6:
4024 		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
4025 		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
4026 			return (1);
4027 		break;
4028 	}
4029 
4030 	/* Simple binary comparison if no mask specified. */
4031 	if (samask == NULL)
4032 		return (memcmp(p1, p2, len));
4033 
4034 	/* Set up the mask, and do a mask-based comparison. */
4035 	if (sa1->sa_family != samask->sa_family ||
4036 	    (mask = sa_rawaddr(samask, NULL)) == NULL)
4037 		return (1);
4038 
4039 	for (i = 0; i < len; i++)
4040 		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
4041 			return (1);
4042 	return (0);
4043 }
4044 
4045 /*
4046  * Return a pointer to the part of the sockaddr that contains the
4047  * raw address, and set *nbytes to its length in bytes. Returns
4048  * NULL if the address family is unknown.
4049  */
4050 static void *
4051 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
4052 	void *p;
4053 	int len;
4054 
4055 	switch (sa->sa_family) {
4056 	case AF_INET:
4057 		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
4058 		p = &((struct sockaddr_in *)sa)->sin_addr;
4059 		break;
4060 	case AF_INET6:
4061 		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
4062 		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
4063 		break;
4064 	default:
4065 		p = NULL;
4066 		len = 0;
4067 	}
4068 
4069 	if (nbytes != NULL)
4070 		*nbytes = len;
4071 	return (p);
4072 }
4073 
4074 static void
4075 huphandler(int sig __unused)
4076 {
4077 
4078 	got_sighup = 1;
4079 }
4080 
4081 static void
4082 terminate(int sig __unused)
4083 {
4084 	free(tmp_groups);
4085 	pidfile_remove(pfh);
4086 	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
4087 	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
4088 	exit (0);
4089 }
4090 
4091 static void
4092 cp_cred(struct expcred *outcr, struct expcred *incr)
4093 {
4094 
4095 	outcr->cr_uid = incr->cr_uid;
4096 	outcr->cr_ngroups = incr->cr_ngroups;
4097 	if (outcr->cr_ngroups > SMALLNGROUPS)
4098 		outcr->cr_groups = malloc(outcr->cr_ngroups * sizeof(gid_t));
4099 	else
4100 		outcr->cr_groups = outcr->cr_smallgrps;
4101 	memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups *
4102 	    sizeof(gid_t));
4103 }
4104 
4105 static gid_t
4106 nogroup()
4107 {
4108 	static gid_t nogroup = 0;	/* 0 means unset. */
4109 
4110 	if (nogroup == 0) {
4111 		const struct group *gr = getgrnam("nogroup");
4112 
4113 		if (gr != NULL && gr->gr_gid != 0)
4114 			nogroup = gr->gr_gid;
4115 		else
4116 			nogroup = GID_NOGROUP;
4117 	}
4118 	return (nogroup);
4119 }
4120