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