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