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