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