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