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