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