mountd.c revision 23681
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 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/*static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95"; */
45static const char rcsid[] =
46	"$Id: mountd.c,v 1.13 1997/02/22 14:33:02 peter Exp $";
47#endif /*not lint*/
48
49#include <sys/param.h>
50#include <sys/file.h>
51#include <sys/ioctl.h>
52#include <sys/mount.h>
53#include <sys/socket.h>
54#include <sys/stat.h>
55#include <sys/syslog.h>
56#include <sys/ucred.h>
57
58#include <rpc/rpc.h>
59#include <rpc/pmap_clnt.h>
60#include <rpc/pmap_prot.h>
61#ifdef ISO
62#include <netiso/iso.h>
63#endif
64#include <nfs/rpcv2.h>
65#include <nfs/nfsproto.h>
66#include <ufs/ufs/ufsmount.h>
67#include <msdosfs/msdosfsmount.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 <errno.h>
74#include <grp.h>
75#include <netdb.h>
76#include <pwd.h>
77#include <signal.h>
78#include <stdio.h>
79#include <stdlib.h>
80#include <string.h>
81#include <unistd.h>
82#include "pathnames.h"
83
84#ifdef DEBUG
85#include <stdarg.h>
86#endif
87
88/*
89 * Structures for keeping the mount list and export list
90 */
91struct mountlist {
92	struct mountlist *ml_next;
93	char	ml_host[RPCMNT_NAMELEN+1];
94	char	ml_dirp[RPCMNT_PATHLEN+1];
95};
96
97struct dirlist {
98	struct dirlist	*dp_left;
99	struct dirlist	*dp_right;
100	int		dp_flag;
101	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
102	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
103};
104/* dp_flag bits */
105#define	DP_DEFSET	0x1
106#define DP_HOSTSET	0x2
107#define DP_KERB		0x4
108
109struct exportlist {
110	struct exportlist *ex_next;
111	struct dirlist	*ex_dirl;
112	struct dirlist	*ex_defdir;
113	int		ex_flag;
114	fsid_t		ex_fs;
115	char		*ex_fsdir;
116};
117/* ex_flag bits */
118#define	EX_LINKED	0x1
119
120struct netmsk {
121	u_long	nt_net;
122	u_long	nt_mask;
123	char *nt_name;
124};
125
126union grouptypes {
127	struct hostent *gt_hostent;
128	struct netmsk	gt_net;
129#ifdef ISO
130	struct sockaddr_iso *gt_isoaddr;
131#endif
132};
133
134struct grouplist {
135	int gr_type;
136	union grouptypes gr_ptr;
137	struct grouplist *gr_next;
138};
139/* Group types */
140#define	GT_NULL		0x0
141#define	GT_HOST		0x1
142#define	GT_NET		0x2
143#define	GT_ISO		0x4
144#define GT_IGNORE	0x5
145
146struct hostlist {
147	int		 ht_flag;	/* Uses DP_xx bits */
148	struct grouplist *ht_grp;
149	struct hostlist	 *ht_next;
150};
151
152struct fhreturn {
153	int	fhr_flag;
154	int	fhr_vers;
155	nfsfh_t	fhr_fh;
156};
157
158/* Global defs */
159char	*add_expdir __P((struct dirlist **, char *, int));
160void	add_dlist __P((struct dirlist **, struct dirlist *,
161				struct grouplist *, int));
162void	add_mlist __P((char *, char *));
163int	check_dirpath __P((char *));
164int	check_options __P((struct dirlist *));
165int	chk_host __P((struct dirlist *, u_long, int *, int *));
166void	del_mlist __P((char *, char *));
167struct dirlist *dirp_search __P((struct dirlist *, char *));
168int	do_mount __P((struct exportlist *, struct grouplist *, int,
169		struct ucred *, char *, int, struct statfs *));
170int	do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
171				int *, int *, struct ucred *));
172struct	exportlist *ex_search __P((fsid_t *));
173struct	exportlist *get_exp __P((void));
174void	free_dir __P((struct dirlist *));
175void	free_exp __P((struct exportlist *));
176void	free_grp __P((struct grouplist *));
177void	free_host __P((struct hostlist *));
178void	get_exportlist __P((void));
179int	get_host __P((char *, struct grouplist *, struct grouplist *));
180int	get_num __P((char *));
181struct hostlist *get_ht __P((void));
182int	get_line __P((void));
183void	get_mountlist __P((void));
184int	get_net __P((char *, struct netmsk *, int));
185void	getexp_err __P((struct exportlist *, struct grouplist *));
186struct grouplist *get_grp __P((void));
187void	hang_dirp __P((struct dirlist *, struct grouplist *,
188				struct exportlist *, int));
189void	mntsrv __P((struct svc_req *, SVCXPRT *));
190void	nextfield __P((char **, char **));
191void	out_of_mem __P((void));
192void	parsecred __P((char *, struct ucred *));
193int	put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
194int	scan_tree __P((struct dirlist *, u_long));
195void	send_umntall __P((void));
196int	umntall_each __P((caddr_t, struct sockaddr_in *));
197int	xdr_dir __P((XDR *, char *));
198int	xdr_explist __P((XDR *, caddr_t));
199int	xdr_fhs __P((XDR *, caddr_t));
200int	xdr_mlist __P((XDR *, caddr_t));
201
202/* C library */
203int	getnetgrent();
204void	endnetgrent();
205void	setnetgrent();
206
207#ifdef ISO
208struct iso_addr *iso_addr();
209#endif
210
211struct exportlist *exphead;
212struct mountlist *mlhead;
213struct grouplist *grphead;
214char exname[MAXPATHLEN];
215struct ucred def_anon = {
216	1,
217	(uid_t) -2,
218	1,
219	{ (gid_t) -2 }
220};
221int resvport_only = 1;
222int dir_only = 1;
223int opt_flags;
224/* Bits for above */
225#define	OP_MAPROOT	0x01
226#define	OP_MAPALL	0x02
227#define	OP_KERB		0x04
228#define	OP_MASK		0x08
229#define	OP_NET		0x10
230#define	OP_ISO		0x20
231#define	OP_ALLDIRS	0x40
232
233#ifdef DEBUG
234int debug = 1;
235void	SYSLOG __P((int, const char *, ...));
236#define syslog SYSLOG
237#else
238int debug = 0;
239#endif
240
241/*
242 * Mountd server for NFS mount protocol as described in:
243 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
244 * The optional arguments are the exports file name
245 * default: _PATH_EXPORTS
246 * and "-n" to allow nonroot mount.
247 */
248int
249main(argc, argv)
250	int argc;
251	char **argv;
252{
253	SVCXPRT *udptransp, *tcptransp;
254	int c;
255#ifdef __FreeBSD__
256	struct vfsconf vfc;
257	int error;
258
259	error = getvfsbyname("nfs", &vfc);
260	if (error && vfsisloadable("nfs")) {
261		if(vfsload("nfs"))
262			err(1, "vfsload(nfs)");
263		endvfsent();	/* flush cache */
264		error = getvfsbyname("nfs", &vfc);
265	}
266	if (error)
267		errx(1, "NFS support is not available in the running kernel");
268#endif	/* __FreeBSD__ */
269
270	while ((c = getopt(argc, argv, "dnr")) != EOF)
271		switch (c) {
272		case 'n':
273			resvport_only = 0;
274			break;
275		case 'r':
276			dir_only = 0;
277			break;
278		case 'd':
279			debug = debug ? 0 : 1;
280			break;
281		default:
282			fprintf(stderr, "Usage: mountd [-d] [-r] [-n] [export_file]\n");
283			exit(1);
284		};
285	argc -= optind;
286	argv += optind;
287	grphead = (struct grouplist *)NULL;
288	exphead = (struct exportlist *)NULL;
289	mlhead = (struct mountlist *)NULL;
290	if (argc == 1) {
291		strncpy(exname, *argv, MAXPATHLEN-1);
292		exname[MAXPATHLEN-1] = '\0';
293	} else
294		strcpy(exname, _PATH_EXPORTS);
295	openlog("mountd", LOG_PID, LOG_DAEMON);
296	if (debug)
297		fprintf(stderr,"Getting export list.\n");
298	get_exportlist();
299	if (debug)
300		fprintf(stderr,"Getting mount list.\n");
301	get_mountlist();
302	if (debug)
303		fprintf(stderr,"Here we go.\n");
304	if (debug == 0) {
305		daemon(0, 0);
306		signal(SIGINT, SIG_IGN);
307		signal(SIGQUIT, SIG_IGN);
308	}
309	signal(SIGHUP, (void (*) __P((int))) get_exportlist);
310	signal(SIGTERM, (void (*) __P((int))) send_umntall);
311	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
312	  if (pidfile != NULL) {
313		fprintf(pidfile, "%d\n", getpid());
314		fclose(pidfile);
315	  }
316	}
317	if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
318	    (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
319		syslog(LOG_ERR, "Can't create socket");
320		exit(1);
321	}
322	pmap_unset(RPCPROG_MNT, 1);
323	pmap_unset(RPCPROG_MNT, 3);
324	if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
325	    !svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
326	    !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP) ||
327	    !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
328		syslog(LOG_ERR, "Can't register mount");
329		exit(1);
330	}
331	svc_run();
332	syslog(LOG_ERR, "Mountd died");
333	exit(1);
334}
335
336/*
337 * The mount rpc service
338 */
339void
340mntsrv(rqstp, transp)
341	struct svc_req *rqstp;
342	SVCXPRT *transp;
343{
344	struct exportlist *ep;
345	struct dirlist *dp;
346	struct fhreturn fhr;
347	struct stat stb;
348	struct statfs fsb;
349	struct hostent *hp;
350	u_long saddr;
351	u_short sport;
352	char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
353	int bad = ENOENT, defset, hostset;
354	sigset_t sighup_mask;
355
356	sigemptyset(&sighup_mask);
357	sigaddset(&sighup_mask, SIGHUP);
358	saddr = transp->xp_raddr.sin_addr.s_addr;
359	sport = ntohs(transp->xp_raddr.sin_port);
360	hp = (struct hostent *)NULL;
361	switch (rqstp->rq_proc) {
362	case NULLPROC:
363		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
364			syslog(LOG_ERR, "Can't send reply");
365		return;
366	case RPCMNT_MOUNT:
367		if (sport >= IPPORT_RESERVED && resvport_only) {
368			svcerr_weakauth(transp);
369			return;
370		}
371		if (!svc_getargs(transp, xdr_dir, rpcpath)) {
372			svcerr_decode(transp);
373			return;
374		}
375
376		/*
377		 * Get the real pathname and make sure it is a directory
378		 * or a regular file if the -r option was specified
379		 * and it exists.
380		 */
381		if (realpath(rpcpath, dirpath) == 0 ||
382		    stat(dirpath, &stb) < 0 ||
383		    (!S_ISDIR(stb.st_mode) &&
384		     (dir_only || !S_ISREG(stb.st_mode))) ||
385		    statfs(dirpath, &fsb) < 0) {
386			chdir("/");	/* Just in case realpath doesn't */
387			if (debug)
388				fprintf(stderr, "stat failed on %s\n", dirpath);
389			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
390				syslog(LOG_ERR, "Can't send reply");
391			return;
392		}
393
394		/* Check in the exports list */
395		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
396		ep = ex_search(&fsb.f_fsid);
397		hostset = defset = 0;
398		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
399		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
400		     chk_host(dp, saddr, &defset, &hostset)) ||
401		     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
402		      scan_tree(ep->ex_dirl, saddr) == 0))) {
403			if (hostset & DP_HOSTSET)
404				fhr.fhr_flag = hostset;
405			else
406				fhr.fhr_flag = defset;
407			fhr.fhr_vers = rqstp->rq_vers;
408			/* Get the file handle */
409			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
410			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
411				bad = errno;
412				syslog(LOG_ERR, "Can't get fh for %s", dirpath);
413				if (!svc_sendreply(transp, xdr_long,
414				    (caddr_t)&bad))
415					syslog(LOG_ERR, "Can't send reply");
416				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
417				return;
418			}
419			if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
420				syslog(LOG_ERR, "Can't send reply");
421			if (hp == NULL)
422				hp = gethostbyaddr((caddr_t)&saddr,
423				    sizeof(saddr), AF_INET);
424			if (hp)
425				add_mlist(hp->h_name, dirpath);
426			else
427				add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),
428					dirpath);
429			if (debug)
430				fprintf(stderr,"Mount successfull.\n");
431		} else {
432			bad = EACCES;
433			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
434				syslog(LOG_ERR, "Can't send reply");
435		}
436		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
437		return;
438	case RPCMNT_DUMP:
439		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
440			syslog(LOG_ERR, "Can't send reply");
441		return;
442	case RPCMNT_UMOUNT:
443		if (sport >= IPPORT_RESERVED && resvport_only) {
444			svcerr_weakauth(transp);
445			return;
446		}
447		if (!svc_getargs(transp, xdr_dir, dirpath)) {
448			svcerr_decode(transp);
449			return;
450		}
451		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
452			syslog(LOG_ERR, "Can't send reply");
453		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
454		if (hp)
455			del_mlist(hp->h_name, dirpath);
456		del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
457		return;
458	case RPCMNT_UMNTALL:
459		if (sport >= IPPORT_RESERVED && resvport_only) {
460			svcerr_weakauth(transp);
461			return;
462		}
463		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
464			syslog(LOG_ERR, "Can't send reply");
465		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
466		if (hp)
467			del_mlist(hp->h_name, (char *)NULL);
468		del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);
469		return;
470	case RPCMNT_EXPORT:
471		if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
472			syslog(LOG_ERR, "Can't send reply");
473		return;
474	default:
475		svcerr_noproc(transp);
476		return;
477	}
478}
479
480/*
481 * Xdr conversion for a dirpath string
482 */
483int
484xdr_dir(xdrsp, dirp)
485	XDR *xdrsp;
486	char *dirp;
487{
488	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
489}
490
491/*
492 * Xdr routine to generate file handle reply
493 */
494int
495xdr_fhs(xdrsp, cp)
496	XDR *xdrsp;
497	caddr_t cp;
498{
499	register struct fhreturn *fhrp = (struct fhreturn *)cp;
500	u_long ok = 0, len, auth;
501
502	if (!xdr_long(xdrsp, &ok))
503		return (0);
504	switch (fhrp->fhr_vers) {
505	case 1:
506		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
507	case 3:
508		len = NFSX_V3FH;
509		if (!xdr_long(xdrsp, &len))
510			return (0);
511		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
512			return (0);
513		if (fhrp->fhr_flag & DP_KERB)
514			auth = RPCAUTH_KERB4;
515		else
516			auth = RPCAUTH_UNIX;
517		len = 1;
518		if (!xdr_long(xdrsp, &len))
519			return (0);
520		return (xdr_long(xdrsp, &auth));
521	};
522	return (0);
523}
524
525int
526xdr_mlist(xdrsp, cp)
527	XDR *xdrsp;
528	caddr_t cp;
529{
530	struct mountlist *mlp;
531	int true = 1;
532	int false = 0;
533	char *strp;
534
535	mlp = mlhead;
536	while (mlp) {
537		if (!xdr_bool(xdrsp, &true))
538			return (0);
539		strp = &mlp->ml_host[0];
540		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
541			return (0);
542		strp = &mlp->ml_dirp[0];
543		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
544			return (0);
545		mlp = mlp->ml_next;
546	}
547	if (!xdr_bool(xdrsp, &false))
548		return (0);
549	return (1);
550}
551
552/*
553 * Xdr conversion for export list
554 */
555int
556xdr_explist(xdrsp, cp)
557	XDR *xdrsp;
558	caddr_t cp;
559{
560	struct exportlist *ep;
561	int false = 0;
562	int putdef;
563	sigset_t sighup_mask;
564
565	sigemptyset(&sighup_mask);
566	sigaddset(&sighup_mask, SIGHUP);
567	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
568	ep = exphead;
569	while (ep) {
570		putdef = 0;
571		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
572			goto errout;
573		if (ep->ex_defdir && putdef == 0 &&
574			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
575			&putdef))
576			goto errout;
577		ep = ep->ex_next;
578	}
579	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
580	if (!xdr_bool(xdrsp, &false))
581		return (0);
582	return (1);
583errout:
584	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
585	return (0);
586}
587
588/*
589 * Called from xdr_explist() to traverse the tree and export the
590 * directory paths.
591 */
592int
593put_exlist(dp, xdrsp, adp, putdefp)
594	struct dirlist *dp;
595	XDR *xdrsp;
596	struct dirlist *adp;
597	int *putdefp;
598{
599	struct grouplist *grp;
600	struct hostlist *hp;
601	int true = 1;
602	int false = 0;
603	int gotalldir = 0;
604	char *strp;
605
606	if (dp) {
607		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
608			return (1);
609		if (!xdr_bool(xdrsp, &true))
610			return (1);
611		strp = dp->dp_dirp;
612		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
613			return (1);
614		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
615			gotalldir = 1;
616			*putdefp = 1;
617		}
618		if ((dp->dp_flag & DP_DEFSET) == 0 &&
619		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
620			hp = dp->dp_hosts;
621			while (hp) {
622				grp = hp->ht_grp;
623				if (grp->gr_type == GT_HOST) {
624					if (!xdr_bool(xdrsp, &true))
625						return (1);
626					strp = grp->gr_ptr.gt_hostent->h_name;
627					if (!xdr_string(xdrsp, &strp,
628					    RPCMNT_NAMELEN))
629						return (1);
630				} else if (grp->gr_type == GT_NET) {
631					if (!xdr_bool(xdrsp, &true))
632						return (1);
633					strp = grp->gr_ptr.gt_net.nt_name;
634					if (!xdr_string(xdrsp, &strp,
635					    RPCMNT_NAMELEN))
636						return (1);
637				}
638				hp = hp->ht_next;
639				if (gotalldir && hp == (struct hostlist *)NULL) {
640					hp = adp->dp_hosts;
641					gotalldir = 0;
642				}
643			}
644		}
645		if (!xdr_bool(xdrsp, &false))
646			return (1);
647		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
648			return (1);
649	}
650	return (0);
651}
652
653#define LINESIZ	10240
654char line[LINESIZ];
655FILE *exp_file;
656
657/*
658 * Get the export list
659 */
660void
661get_exportlist()
662{
663	struct exportlist *ep, *ep2;
664	struct grouplist *grp, *tgrp;
665	struct exportlist **epp;
666	struct dirlist *dirhead;
667	struct statfs fsb, *fsp;
668	struct hostent *hpe;
669	struct ucred anon;
670	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
671	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
672
673	/*
674	 * First, get rid of the old list
675	 */
676	ep = exphead;
677	while (ep) {
678		ep2 = ep;
679		ep = ep->ex_next;
680		free_exp(ep2);
681	}
682	exphead = (struct exportlist *)NULL;
683
684	grp = grphead;
685	while (grp) {
686		tgrp = grp;
687		grp = grp->gr_next;
688		free_grp(tgrp);
689	}
690	grphead = (struct grouplist *)NULL;
691
692	/*
693	 * And delete exports that are in the kernel for all local
694	 * file systems.
695	 * XXX: Should know how to handle all local exportable file systems
696	 *      instead of just "ufs".
697	 */
698	num = getmntinfo(&fsp, MNT_NOWAIT);
699	for (i = 0; i < num; i++) {
700		union {
701			struct ufs_args ua;
702			struct iso_args ia;
703			struct mfs_args ma;
704			struct msdosfs_args da;
705		} targs;
706
707		if (!strcmp(fsp->f_fstypename, "mfs") ||
708		    !strcmp(fsp->f_fstypename, "ufs") ||
709		    !strcmp(fsp->f_fstypename, "msdos") ||
710		    !strcmp(fsp->f_fstypename, "cd9660")) {
711			targs.ua.fspec = NULL;
712			targs.ua.export.ex_flags = MNT_DELEXPORT;
713			if (mount(fsp->f_fstypename, fsp->f_mntonname,
714				  fsp->f_flags | MNT_UPDATE,
715				  (caddr_t)&targs) < 0)
716				syslog(LOG_ERR, "Can't delete exports for %s",
717				       fsp->f_mntonname);
718		}
719		fsp++;
720	}
721
722	/*
723	 * Read in the exports file and build the list, calling
724	 * mount() as we go along to push the export rules into the kernel.
725	 */
726	if ((exp_file = fopen(exname, "r")) == NULL) {
727		syslog(LOG_ERR, "Can't open %s", exname);
728		exit(2);
729	}
730	dirhead = (struct dirlist *)NULL;
731	while (get_line()) {
732		if (debug)
733			fprintf(stderr,"Got line %s\n",line);
734		cp = line;
735		nextfield(&cp, &endcp);
736		if (*cp == '#')
737			goto nextline;
738
739		/*
740		 * Set defaults.
741		 */
742		has_host = FALSE;
743		anon = def_anon;
744		exflags = MNT_EXPORTED;
745		got_nondir = 0;
746		opt_flags = 0;
747		ep = (struct exportlist *)NULL;
748
749		/*
750		 * Create new exports list entry
751		 */
752		len = endcp-cp;
753		tgrp = grp = get_grp();
754		while (len > 0) {
755			if (len > RPCMNT_NAMELEN) {
756			    getexp_err(ep, tgrp);
757			    goto nextline;
758			}
759			if (*cp == '-') {
760			    if (ep == (struct exportlist *)NULL) {
761				getexp_err(ep, tgrp);
762				goto nextline;
763			    }
764			    if (debug)
765				fprintf(stderr, "doing opt %s\n", cp);
766			    got_nondir = 1;
767			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
768				&exflags, &anon)) {
769				getexp_err(ep, tgrp);
770				goto nextline;
771			    }
772			} else if (*cp == '/') {
773			    savedc = *endcp;
774			    *endcp = '\0';
775			    if (check_dirpath(cp) &&
776				statfs(cp, &fsb) >= 0) {
777				if (got_nondir) {
778				    syslog(LOG_ERR, "Dirs must be first");
779				    getexp_err(ep, tgrp);
780				    goto nextline;
781				}
782				if (ep) {
783				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
784					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
785					getexp_err(ep, tgrp);
786					goto nextline;
787				    }
788				} else {
789				    /*
790				     * See if this directory is already
791				     * in the list.
792				     */
793				    ep = ex_search(&fsb.f_fsid);
794				    if (ep == (struct exportlist *)NULL) {
795					ep = get_exp();
796					ep->ex_fs = fsb.f_fsid;
797					ep->ex_fsdir = (char *)
798					    malloc(strlen(fsb.f_mntonname) + 1);
799					if (ep->ex_fsdir)
800					    strcpy(ep->ex_fsdir,
801						fsb.f_mntonname);
802					else
803					    out_of_mem();
804					if (debug)
805					  fprintf(stderr,
806					      "Making new ep fs=0x%x,0x%x\n",
807					      fsb.f_fsid.val[0],
808					      fsb.f_fsid.val[1]);
809				    } else if (debug)
810					fprintf(stderr,
811					    "Found ep fs=0x%x,0x%x\n",
812					    fsb.f_fsid.val[0],
813					    fsb.f_fsid.val[1]);
814				}
815
816				/*
817				 * Add dirpath to export mount point.
818				 */
819				dirp = add_expdir(&dirhead, cp, len);
820				dirplen = len;
821			    } else {
822				getexp_err(ep, tgrp);
823				goto nextline;
824			    }
825			    *endcp = savedc;
826			} else {
827			    savedc = *endcp;
828			    *endcp = '\0';
829			    got_nondir = 1;
830			    if (ep == (struct exportlist *)NULL) {
831				getexp_err(ep, tgrp);
832				goto nextline;
833			    }
834
835			    /*
836			     * Get the host or netgroup.
837			     */
838			    setnetgrent(cp);
839			    netgrp = getnetgrent(&hst, &usr, &dom);
840			    do {
841				if (has_host) {
842				    grp->gr_next = get_grp();
843				    grp = grp->gr_next;
844				}
845				if (netgrp) {
846				    if (get_host(hst, grp, tgrp)) {
847					syslog(LOG_ERR, "Bad netgroup %s", cp);
848					getexp_err(ep, tgrp);
849					endnetgrent();
850					goto nextline;
851				    }
852				} else if (get_host(cp, grp, tgrp)) {
853				    getexp_err(ep, tgrp);
854				    goto nextline;
855				}
856				has_host = TRUE;
857			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
858			    endnetgrent();
859			    *endcp = savedc;
860			}
861			cp = endcp;
862			nextfield(&cp, &endcp);
863			len = endcp - cp;
864		}
865		if (check_options(dirhead)) {
866			getexp_err(ep, tgrp);
867			goto nextline;
868		}
869		if (!has_host) {
870			grp->gr_type = GT_HOST;
871			if (debug)
872				fprintf(stderr,"Adding a default entry\n");
873			/* add a default group and make the grp list NULL */
874			hpe = (struct hostent *)malloc(sizeof(struct hostent));
875			if (hpe == (struct hostent *)NULL)
876				out_of_mem();
877			hpe->h_name = strdup("Default");
878			hpe->h_addrtype = AF_INET;
879			hpe->h_length = sizeof (u_long);
880			hpe->h_addr_list = (char **)NULL;
881			grp->gr_ptr.gt_hostent = hpe;
882
883		/*
884		 * Don't allow a network export coincide with a list of
885		 * host(s) on the same line.
886		 */
887		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
888			getexp_err(ep, tgrp);
889			goto nextline;
890		}
891
892		/*
893		 * Loop through hosts, pushing the exports into the kernel.
894		 * After loop, tgrp points to the start of the list and
895		 * grp points to the last entry in the list.
896		 */
897		grp = tgrp;
898		do {
899		    if (do_mount(ep, grp, exflags, &anon, dirp,
900			dirplen, &fsb)) {
901			getexp_err(ep, tgrp);
902			goto nextline;
903		    }
904		} while (grp->gr_next && (grp = grp->gr_next));
905
906		/*
907		 * Success. Update the data structures.
908		 */
909		if (has_host) {
910			hang_dirp(dirhead, tgrp, ep, opt_flags);
911			grp->gr_next = grphead;
912			grphead = tgrp;
913		} else {
914			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
915				opt_flags);
916			free_grp(grp);
917		}
918		dirhead = (struct dirlist *)NULL;
919		if ((ep->ex_flag & EX_LINKED) == 0) {
920			ep2 = exphead;
921			epp = &exphead;
922
923			/*
924			 * Insert in the list in alphabetical order.
925			 */
926			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
927				epp = &ep2->ex_next;
928				ep2 = ep2->ex_next;
929			}
930			if (ep2)
931				ep->ex_next = ep2;
932			*epp = ep;
933			ep->ex_flag |= EX_LINKED;
934		}
935nextline:
936		if (dirhead) {
937			free_dir(dirhead);
938			dirhead = (struct dirlist *)NULL;
939		}
940	}
941	fclose(exp_file);
942}
943
944/*
945 * Allocate an export list element
946 */
947struct exportlist *
948get_exp()
949{
950	struct exportlist *ep;
951
952	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
953	if (ep == (struct exportlist *)NULL)
954		out_of_mem();
955	memset(ep, 0, sizeof(struct exportlist));
956	return (ep);
957}
958
959/*
960 * Allocate a group list element
961 */
962struct grouplist *
963get_grp()
964{
965	struct grouplist *gp;
966
967	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
968	if (gp == (struct grouplist *)NULL)
969		out_of_mem();
970	memset(gp, 0, sizeof(struct grouplist));
971	return (gp);
972}
973
974/*
975 * Clean up upon an error in get_exportlist().
976 */
977void
978getexp_err(ep, grp)
979	struct exportlist *ep;
980	struct grouplist *grp;
981{
982	struct grouplist *tgrp;
983
984	syslog(LOG_ERR, "Bad exports list line %s", line);
985	if (ep && (ep->ex_flag & EX_LINKED) == 0)
986		free_exp(ep);
987	while (grp) {
988		tgrp = grp;
989		grp = grp->gr_next;
990		free_grp(tgrp);
991	}
992}
993
994/*
995 * Search the export list for a matching fs.
996 */
997struct exportlist *
998ex_search(fsid)
999	fsid_t *fsid;
1000{
1001	struct exportlist *ep;
1002
1003	ep = exphead;
1004	while (ep) {
1005		if (ep->ex_fs.val[0] == fsid->val[0] &&
1006		    ep->ex_fs.val[1] == fsid->val[1])
1007			return (ep);
1008		ep = ep->ex_next;
1009	}
1010	return (ep);
1011}
1012
1013/*
1014 * Add a directory path to the list.
1015 */
1016char *
1017add_expdir(dpp, cp, len)
1018	struct dirlist **dpp;
1019	char *cp;
1020	int len;
1021{
1022	struct dirlist *dp;
1023
1024	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1025	dp->dp_left = *dpp;
1026	dp->dp_right = (struct dirlist *)NULL;
1027	dp->dp_flag = 0;
1028	dp->dp_hosts = (struct hostlist *)NULL;
1029	strcpy(dp->dp_dirp, cp);
1030	*dpp = dp;
1031	return (dp->dp_dirp);
1032}
1033
1034/*
1035 * Hang the dir list element off the dirpath binary tree as required
1036 * and update the entry for host.
1037 */
1038void
1039hang_dirp(dp, grp, ep, flags)
1040	struct dirlist *dp;
1041	struct grouplist *grp;
1042	struct exportlist *ep;
1043	int flags;
1044{
1045	struct hostlist *hp;
1046	struct dirlist *dp2;
1047
1048	if (flags & OP_ALLDIRS) {
1049		if (ep->ex_defdir)
1050			free((caddr_t)dp);
1051		else
1052			ep->ex_defdir = dp;
1053		if (grp == (struct grouplist *)NULL) {
1054			ep->ex_defdir->dp_flag |= DP_DEFSET;
1055			if (flags & OP_KERB)
1056				ep->ex_defdir->dp_flag |= DP_KERB;
1057		} else while (grp) {
1058			hp = get_ht();
1059			if (flags & OP_KERB)
1060				hp->ht_flag |= DP_KERB;
1061			hp->ht_grp = grp;
1062			hp->ht_next = ep->ex_defdir->dp_hosts;
1063			ep->ex_defdir->dp_hosts = hp;
1064			grp = grp->gr_next;
1065		}
1066	} else {
1067
1068		/*
1069		 * Loop throught the directories adding them to the tree.
1070		 */
1071		while (dp) {
1072			dp2 = dp->dp_left;
1073			add_dlist(&ep->ex_dirl, dp, grp, flags);
1074			dp = dp2;
1075		}
1076	}
1077}
1078
1079/*
1080 * Traverse the binary tree either updating a node that is already there
1081 * for the new directory or adding the new node.
1082 */
1083void
1084add_dlist(dpp, newdp, grp, flags)
1085	struct dirlist **dpp;
1086	struct dirlist *newdp;
1087	struct grouplist *grp;
1088	int flags;
1089{
1090	struct dirlist *dp;
1091	struct hostlist *hp;
1092	int cmp;
1093
1094	dp = *dpp;
1095	if (dp) {
1096		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1097		if (cmp > 0) {
1098			add_dlist(&dp->dp_left, newdp, grp, flags);
1099			return;
1100		} else if (cmp < 0) {
1101			add_dlist(&dp->dp_right, newdp, grp, flags);
1102			return;
1103		} else
1104			free((caddr_t)newdp);
1105	} else {
1106		dp = newdp;
1107		dp->dp_left = (struct dirlist *)NULL;
1108		*dpp = dp;
1109	}
1110	if (grp) {
1111
1112		/*
1113		 * Hang all of the host(s) off of the directory point.
1114		 */
1115		do {
1116			hp = get_ht();
1117			if (flags & OP_KERB)
1118				hp->ht_flag |= DP_KERB;
1119			hp->ht_grp = grp;
1120			hp->ht_next = dp->dp_hosts;
1121			dp->dp_hosts = hp;
1122			grp = grp->gr_next;
1123		} while (grp);
1124	} else {
1125		dp->dp_flag |= DP_DEFSET;
1126		if (flags & OP_KERB)
1127			dp->dp_flag |= DP_KERB;
1128	}
1129}
1130
1131/*
1132 * Search for a dirpath on the export point.
1133 */
1134struct dirlist *
1135dirp_search(dp, dirpath)
1136	struct dirlist *dp;
1137	char *dirpath;
1138{
1139	int cmp;
1140
1141	if (dp) {
1142		cmp = strcmp(dp->dp_dirp, dirpath);
1143		if (cmp > 0)
1144			return (dirp_search(dp->dp_left, dirpath));
1145		else if (cmp < 0)
1146			return (dirp_search(dp->dp_right, dirpath));
1147		else
1148			return (dp);
1149	}
1150	return (dp);
1151}
1152
1153/*
1154 * Scan for a host match in a directory tree.
1155 */
1156int
1157chk_host(dp, saddr, defsetp, hostsetp)
1158	struct dirlist *dp;
1159	u_long saddr;
1160	int *defsetp;
1161	int *hostsetp;
1162{
1163	struct hostlist *hp;
1164	struct grouplist *grp;
1165	u_long **addrp;
1166
1167	if (dp) {
1168		if (dp->dp_flag & DP_DEFSET)
1169			*defsetp = dp->dp_flag;
1170		hp = dp->dp_hosts;
1171		while (hp) {
1172			grp = hp->ht_grp;
1173			switch (grp->gr_type) {
1174			case GT_HOST:
1175			    addrp = (u_long **)
1176				grp->gr_ptr.gt_hostent->h_addr_list;
1177			    while (*addrp) {
1178				if (**addrp == saddr) {
1179				    *hostsetp = (hp->ht_flag | DP_HOSTSET);
1180				    return (1);
1181				}
1182				addrp++;
1183			    }
1184			    break;
1185			case GT_NET:
1186			    if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
1187				grp->gr_ptr.gt_net.nt_net) {
1188				*hostsetp = (hp->ht_flag | DP_HOSTSET);
1189				return (1);
1190			    }
1191			    break;
1192			};
1193			hp = hp->ht_next;
1194		}
1195	}
1196	return (0);
1197}
1198
1199/*
1200 * Scan tree for a host that matches the address.
1201 */
1202int
1203scan_tree(dp, saddr)
1204	struct dirlist *dp;
1205	u_long saddr;
1206{
1207	int defset, hostset;
1208
1209	if (dp) {
1210		if (scan_tree(dp->dp_left, saddr))
1211			return (1);
1212		if (chk_host(dp, saddr, &defset, &hostset))
1213			return (1);
1214		if (scan_tree(dp->dp_right, saddr))
1215			return (1);
1216	}
1217	return (0);
1218}
1219
1220/*
1221 * Traverse the dirlist tree and free it up.
1222 */
1223void
1224free_dir(dp)
1225	struct dirlist *dp;
1226{
1227
1228	if (dp) {
1229		free_dir(dp->dp_left);
1230		free_dir(dp->dp_right);
1231		free_host(dp->dp_hosts);
1232		free((caddr_t)dp);
1233	}
1234}
1235
1236/*
1237 * Parse the option string and update fields.
1238 * Option arguments may either be -<option>=<value> or
1239 * -<option> <value>
1240 */
1241int
1242do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1243	char **cpp, **endcpp;
1244	struct exportlist *ep;
1245	struct grouplist *grp;
1246	int *has_hostp;
1247	int *exflagsp;
1248	struct ucred *cr;
1249{
1250	char *cpoptarg, *cpoptend;
1251	char *cp, *endcp, *cpopt, savedc, savedc2;
1252	int allflag, usedarg;
1253
1254	cpopt = *cpp;
1255	cpopt++;
1256	cp = *endcpp;
1257	savedc = *cp;
1258	*cp = '\0';
1259	while (cpopt && *cpopt) {
1260		allflag = 1;
1261		usedarg = -2;
1262		if (cpoptend = strchr(cpopt, ',')) {
1263			*cpoptend++ = '\0';
1264			if (cpoptarg = strchr(cpopt, '='))
1265				*cpoptarg++ = '\0';
1266		} else {
1267			if (cpoptarg = strchr(cpopt, '='))
1268				*cpoptarg++ = '\0';
1269			else {
1270				*cp = savedc;
1271				nextfield(&cp, &endcp);
1272				**endcpp = '\0';
1273				if (endcp > cp && *cp != '-') {
1274					cpoptarg = cp;
1275					savedc2 = *endcp;
1276					*endcp = '\0';
1277					usedarg = 0;
1278				}
1279			}
1280		}
1281		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1282			*exflagsp |= MNT_EXRDONLY;
1283		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1284		    !(allflag = strcmp(cpopt, "mapall")) ||
1285		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1286			usedarg++;
1287			parsecred(cpoptarg, cr);
1288			if (allflag == 0) {
1289				*exflagsp |= MNT_EXPORTANON;
1290				opt_flags |= OP_MAPALL;
1291			} else
1292				opt_flags |= OP_MAPROOT;
1293		} else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1294			*exflagsp |= MNT_EXKERB;
1295			opt_flags |= OP_KERB;
1296		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1297			!strcmp(cpopt, "m"))) {
1298			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1299				syslog(LOG_ERR, "Bad mask: %s", cpoptarg);
1300				return (1);
1301			}
1302			usedarg++;
1303			opt_flags |= OP_MASK;
1304		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
1305			!strcmp(cpopt, "n"))) {
1306			if (grp->gr_type != GT_NULL) {
1307				syslog(LOG_ERR, "Network/host conflict");
1308				return (1);
1309			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1310				syslog(LOG_ERR, "Bad net: %s", cpoptarg);
1311				return (1);
1312			}
1313			grp->gr_type = GT_NET;
1314			*has_hostp = 1;
1315			usedarg++;
1316			opt_flags |= OP_NET;
1317		} else if (!strcmp(cpopt, "alldirs")) {
1318			opt_flags |= OP_ALLDIRS;
1319#ifdef ISO
1320		} else if (cpoptarg && !strcmp(cpopt, "iso")) {
1321			if (get_isoaddr(cpoptarg, grp)) {
1322				syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg);
1323				return (1);
1324			}
1325			*has_hostp = 1;
1326			usedarg++;
1327			opt_flags |= OP_ISO;
1328#endif /* ISO */
1329		} else {
1330			syslog(LOG_ERR, "Bad opt %s", cpopt);
1331			return (1);
1332		}
1333		if (usedarg >= 0) {
1334			*endcp = savedc2;
1335			**endcpp = savedc;
1336			if (usedarg > 0) {
1337				*cpp = cp;
1338				*endcpp = endcp;
1339			}
1340			return (0);
1341		}
1342		cpopt = cpoptend;
1343	}
1344	**endcpp = savedc;
1345	return (0);
1346}
1347
1348/*
1349 * Translate a character string to the corresponding list of network
1350 * addresses for a hostname.
1351 */
1352int
1353get_host(cp, grp, tgrp)
1354	char *cp;
1355	struct grouplist *grp;
1356	struct grouplist *tgrp;
1357{
1358	struct grouplist *checkgrp;
1359	struct hostent *hp, *nhp;
1360	char **addrp, **naddrp;
1361	struct hostent t_host;
1362	int i;
1363	u_long saddr;
1364	char *aptr[2];
1365
1366	if (grp->gr_type != GT_NULL)
1367		return (1);
1368	if ((hp = gethostbyname(cp)) == NULL) {
1369		if (isdigit(*cp)) {
1370			saddr = inet_addr(cp);
1371			if (saddr == -1) {
1372 				syslog(LOG_ERR, "Inet_addr failed for %s", cp);
1373				return (1);
1374			}
1375			if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
1376				AF_INET)) == NULL) {
1377				hp = &t_host;
1378				hp->h_name = cp;
1379				hp->h_addrtype = AF_INET;
1380				hp->h_length = sizeof (u_long);
1381				hp->h_addr_list = aptr;
1382				aptr[0] = (char *)&saddr;
1383				aptr[1] = (char *)NULL;
1384			}
1385		} else {
1386 			syslog(LOG_ERR, "Gethostbyname failed for %s", cp);
1387			return (1);
1388		}
1389	}
1390        /*
1391         * Sanity check: make sure we don't already have an entry
1392         * for this host in the grouplist.
1393         */
1394        checkgrp = tgrp;
1395        while (checkgrp) {
1396		if (checkgrp->gr_type == GT_HOST &&
1397                    checkgrp->gr_ptr.gt_hostent != NULL &&
1398                    !strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)) {
1399                        grp->gr_type = GT_IGNORE;
1400			return(0);
1401		}
1402                checkgrp = checkgrp->gr_next;
1403        }
1404
1405	grp->gr_type = GT_HOST;
1406	nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
1407		malloc(sizeof(struct hostent));
1408	if (nhp == (struct hostent *)NULL)
1409		out_of_mem();
1410	memmove(nhp, hp, sizeof(struct hostent));
1411	i = strlen(hp->h_name)+1;
1412	nhp->h_name = (char *)malloc(i);
1413	if (nhp->h_name == (char *)NULL)
1414		out_of_mem();
1415	memmove(nhp->h_name, hp->h_name, i);
1416	addrp = hp->h_addr_list;
1417	i = 1;
1418	while (*addrp++)
1419		i++;
1420	naddrp = nhp->h_addr_list = (char **)
1421		malloc(i*sizeof(char *));
1422	if (naddrp == (char **)NULL)
1423		out_of_mem();
1424	addrp = hp->h_addr_list;
1425	while (*addrp) {
1426		*naddrp = (char *)
1427		    malloc(hp->h_length);
1428		if (*naddrp == (char *)NULL)
1429		    out_of_mem();
1430		memmove(*naddrp, *addrp, hp->h_length);
1431		addrp++;
1432		naddrp++;
1433	}
1434	*naddrp = (char *)NULL;
1435	if (debug)
1436		fprintf(stderr, "got host %s\n", hp->h_name);
1437	return (0);
1438}
1439
1440/*
1441 * Free up an exports list component
1442 */
1443void
1444free_exp(ep)
1445	struct exportlist *ep;
1446{
1447
1448	if (ep->ex_defdir) {
1449		free_host(ep->ex_defdir->dp_hosts);
1450		free((caddr_t)ep->ex_defdir);
1451	}
1452	if (ep->ex_fsdir)
1453		free(ep->ex_fsdir);
1454	free_dir(ep->ex_dirl);
1455	free((caddr_t)ep);
1456}
1457
1458/*
1459 * Free hosts.
1460 */
1461void
1462free_host(hp)
1463	struct hostlist *hp;
1464{
1465	struct hostlist *hp2;
1466
1467	while (hp) {
1468		hp2 = hp;
1469		hp = hp->ht_next;
1470		free((caddr_t)hp2);
1471	}
1472}
1473
1474struct hostlist *
1475get_ht()
1476{
1477	struct hostlist *hp;
1478
1479	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1480	if (hp == (struct hostlist *)NULL)
1481		out_of_mem();
1482	hp->ht_next = (struct hostlist *)NULL;
1483	hp->ht_flag = 0;
1484	return (hp);
1485}
1486
1487#ifdef ISO
1488/*
1489 * Translate an iso address.
1490 */
1491get_isoaddr(cp, grp)
1492	char *cp;
1493	struct grouplist *grp;
1494{
1495	struct iso_addr *isop;
1496	struct sockaddr_iso *isoaddr;
1497
1498	if (grp->gr_type != GT_NULL)
1499		return (1);
1500	if ((isop = iso_addr(cp)) == NULL) {
1501		syslog(LOG_ERR,
1502		    "iso_addr failed, ignored");
1503		return (1);
1504	}
1505	isoaddr = (struct sockaddr_iso *)
1506	    malloc(sizeof (struct sockaddr_iso));
1507	if (isoaddr == (struct sockaddr_iso *)NULL)
1508		out_of_mem();
1509	memset(isoaddr, 0, sizeof(struct sockaddr_iso));
1510	memmove(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
1511	isoaddr->siso_len = sizeof(struct sockaddr_iso);
1512	isoaddr->siso_family = AF_ISO;
1513	grp->gr_type = GT_ISO;
1514	grp->gr_ptr.gt_isoaddr = isoaddr;
1515	return (0);
1516}
1517#endif	/* ISO */
1518
1519/*
1520 * Out of memory, fatal
1521 */
1522void
1523out_of_mem()
1524{
1525
1526	syslog(LOG_ERR, "Out of memory");
1527	exit(2);
1528}
1529
1530/*
1531 * Do the mount syscall with the update flag to push the export info into
1532 * the kernel.
1533 */
1534int
1535do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1536	struct exportlist *ep;
1537	struct grouplist *grp;
1538	int exflags;
1539	struct ucred *anoncrp;
1540	char *dirp;
1541	int dirplen;
1542	struct statfs *fsb;
1543{
1544	char *cp = (char *)NULL;
1545	u_long **addrp;
1546	int done;
1547	char savedc = '\0';
1548	struct sockaddr_in sin, imask;
1549	union {
1550		struct ufs_args ua;
1551		struct iso_args ia;
1552		struct mfs_args ma;
1553#ifdef __NetBSD__
1554		struct msdosfs_args da;
1555#endif
1556	} args;
1557	u_long net;
1558
1559	args.ua.fspec = 0;
1560	args.ua.export.ex_flags = exflags;
1561	args.ua.export.ex_anon = *anoncrp;
1562	memset(&sin, 0, sizeof(sin));
1563	memset(&imask, 0, sizeof(imask));
1564	sin.sin_family = AF_INET;
1565	sin.sin_len = sizeof(sin);
1566	imask.sin_family = AF_INET;
1567	imask.sin_len = sizeof(sin);
1568	if (grp->gr_type == GT_HOST)
1569		addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list;
1570	else
1571		addrp = (u_long **)NULL;
1572	done = FALSE;
1573	while (!done) {
1574		switch (grp->gr_type) {
1575		case GT_HOST:
1576			if (addrp) {
1577				sin.sin_addr.s_addr = **addrp;
1578				args.ua.export.ex_addrlen = sizeof(sin);
1579			} else
1580				args.ua.export.ex_addrlen = 0;
1581			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1582			args.ua.export.ex_masklen = 0;
1583			break;
1584		case GT_NET:
1585			if (grp->gr_ptr.gt_net.nt_mask)
1586			    imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
1587			else {
1588			    net = ntohl(grp->gr_ptr.gt_net.nt_net);
1589			    if (IN_CLASSA(net))
1590				imask.sin_addr.s_addr = inet_addr("255.0.0.0");
1591			    else if (IN_CLASSB(net))
1592				imask.sin_addr.s_addr =
1593				    inet_addr("255.255.0.0");
1594			    else
1595				imask.sin_addr.s_addr =
1596				    inet_addr("255.255.255.0");
1597			    grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
1598			}
1599			sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
1600			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1601			args.ua.export.ex_addrlen = sizeof (sin);
1602			args.ua.export.ex_mask = (struct sockaddr *)&imask;
1603			args.ua.export.ex_masklen = sizeof (imask);
1604			break;
1605#ifdef ISO
1606		case GT_ISO:
1607			args.ua.export.ex_addr =
1608				(struct sockaddr *)grp->gr_ptr.gt_isoaddr;
1609			args.ua.export.ex_addrlen =
1610				sizeof(struct sockaddr_iso);
1611			args.ua.export.ex_masklen = 0;
1612			break;
1613#endif	/* ISO */
1614		case GT_IGNORE:
1615			return(0);
1616			break;
1617		default:
1618			syslog(LOG_ERR, "Bad grouptype");
1619			if (cp)
1620				*cp = savedc;
1621			return (1);
1622		};
1623
1624		/*
1625		 * XXX:
1626		 * Maybe I should just use the fsb->f_mntonname path instead
1627		 * of looping back up the dirp to the mount point??
1628		 * Also, needs to know how to export all types of local
1629		 * exportable file systems and not just "ufs".
1630		 */
1631		while (mount(fsb->f_fstypename, dirp,
1632		       fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1633			if (cp)
1634				*cp-- = savedc;
1635			else
1636				cp = dirp + dirplen - 1;
1637			if (errno == EPERM) {
1638				syslog(LOG_ERR,
1639				   "Can't change attributes for %s.\n", dirp);
1640				return (1);
1641			}
1642			if (opt_flags & OP_ALLDIRS) {
1643				syslog(LOG_ERR, "Could not remount %s: %m",
1644					dirp);
1645				return (1);
1646			}
1647			/* back up over the last component */
1648			while (*cp == '/' && cp > dirp)
1649				cp--;
1650			while (*(cp - 1) != '/' && cp > dirp)
1651				cp--;
1652			if (cp == dirp) {
1653				if (debug)
1654					fprintf(stderr,"mnt unsucc\n");
1655				syslog(LOG_ERR, "Can't export %s", dirp);
1656				return (1);
1657			}
1658			savedc = *cp;
1659			*cp = '\0';
1660		}
1661		if (addrp) {
1662			++addrp;
1663			if (*addrp == (u_long *)NULL)
1664				done = TRUE;
1665		} else
1666			done = TRUE;
1667	}
1668	if (cp)
1669		*cp = savedc;
1670	return (0);
1671}
1672
1673/*
1674 * Translate a net address.
1675 */
1676int
1677get_net(cp, net, maskflg)
1678	char *cp;
1679	struct netmsk *net;
1680	int maskflg;
1681{
1682	struct netent *np;
1683	long netaddr;
1684	struct in_addr inetaddr, inetaddr2;
1685	char *name;
1686
1687	if (np = getnetbyname(cp))
1688		inetaddr = inet_makeaddr(np->n_net, 0);
1689	else if (isdigit(*cp)) {
1690		if ((netaddr = inet_network(cp)) == -1)
1691			return (1);
1692		inetaddr = inet_makeaddr(netaddr, 0);
1693		/*
1694		 * Due to arbritrary subnet masks, you don't know how many
1695		 * bits to shift the address to make it into a network,
1696		 * however you do know how to make a network address into
1697		 * a host with host == 0 and then compare them.
1698		 * (What a pest)
1699		 */
1700		if (!maskflg) {
1701			setnetent(0);
1702			while (np = getnetent()) {
1703				inetaddr2 = inet_makeaddr(np->n_net, 0);
1704				if (inetaddr2.s_addr == inetaddr.s_addr)
1705					break;
1706			}
1707			endnetent();
1708		}
1709	} else
1710		return (1);
1711	if (maskflg)
1712		net->nt_mask = inetaddr.s_addr;
1713	else {
1714		if (np)
1715			name = np->n_name;
1716		else
1717			name = inet_ntoa(inetaddr);
1718		net->nt_name = (char *)malloc(strlen(name) + 1);
1719		if (net->nt_name == (char *)NULL)
1720			out_of_mem();
1721		strcpy(net->nt_name, name);
1722		net->nt_net = inetaddr.s_addr;
1723	}
1724	return (0);
1725}
1726
1727/*
1728 * Parse out the next white space separated field
1729 */
1730void
1731nextfield(cp, endcp)
1732	char **cp;
1733	char **endcp;
1734{
1735	char *p;
1736
1737	p = *cp;
1738	while (*p == ' ' || *p == '\t')
1739		p++;
1740	if (*p == '\n' || *p == '\0')
1741		*cp = *endcp = p;
1742	else {
1743		*cp = p++;
1744		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1745			p++;
1746		*endcp = p;
1747	}
1748}
1749
1750/*
1751 * Get an exports file line. Skip over blank lines and handle line
1752 * continuations.
1753 */
1754int
1755get_line()
1756{
1757	char *p, *cp;
1758	int len;
1759	int totlen, cont_line;
1760
1761	/*
1762	 * Loop around ignoring blank lines and getting all continuation lines.
1763	 */
1764	p = line;
1765	totlen = 0;
1766	do {
1767		if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
1768			return (0);
1769		len = strlen(p);
1770		cp = p + len - 1;
1771		cont_line = 0;
1772		while (cp >= p &&
1773		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
1774			if (*cp == '\\')
1775				cont_line = 1;
1776			cp--;
1777			len--;
1778		}
1779		*++cp = '\0';
1780		if (len > 0) {
1781			totlen += len;
1782			if (totlen >= LINESIZ) {
1783				syslog(LOG_ERR, "Exports line too long");
1784				exit(2);
1785			}
1786			p = cp;
1787		}
1788	} while (totlen == 0 || cont_line);
1789	return (1);
1790}
1791
1792/*
1793 * Parse a description of a credential.
1794 */
1795void
1796parsecred(namelist, cr)
1797	char *namelist;
1798	struct ucred *cr;
1799{
1800	char *name;
1801	int cnt;
1802	char *names;
1803	struct passwd *pw;
1804	struct group *gr;
1805	int ngroups, groups[NGROUPS + 1];
1806
1807	/*
1808	 * Set up the unpriviledged user.
1809	 */
1810	cr->cr_ref = 1;
1811	cr->cr_uid = -2;
1812	cr->cr_groups[0] = -2;
1813	cr->cr_ngroups = 1;
1814	/*
1815	 * Get the user's password table entry.
1816	 */
1817	names = strsep(&namelist, " \t\n");
1818	name = strsep(&names, ":");
1819	if (isdigit(*name) || *name == '-')
1820		pw = getpwuid(atoi(name));
1821	else
1822		pw = getpwnam(name);
1823	/*
1824	 * Credentials specified as those of a user.
1825	 */
1826	if (names == NULL) {
1827		if (pw == NULL) {
1828			syslog(LOG_ERR, "Unknown user: %s", name);
1829			return;
1830		}
1831		cr->cr_uid = pw->pw_uid;
1832		ngroups = NGROUPS + 1;
1833		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
1834			syslog(LOG_ERR, "Too many groups");
1835		/*
1836		 * Convert from int's to gid_t's and compress out duplicate
1837		 */
1838		cr->cr_ngroups = ngroups - 1;
1839		cr->cr_groups[0] = groups[0];
1840		for (cnt = 2; cnt < ngroups; cnt++)
1841			cr->cr_groups[cnt - 1] = groups[cnt];
1842		return;
1843	}
1844	/*
1845	 * Explicit credential specified as a colon separated list:
1846	 *	uid:gid:gid:...
1847	 */
1848	if (pw != NULL)
1849		cr->cr_uid = pw->pw_uid;
1850	else if (isdigit(*name) || *name == '-')
1851		cr->cr_uid = atoi(name);
1852	else {
1853		syslog(LOG_ERR, "Unknown user: %s", name);
1854		return;
1855	}
1856	cr->cr_ngroups = 0;
1857	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
1858		name = strsep(&names, ":");
1859		if (isdigit(*name) || *name == '-') {
1860			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
1861		} else {
1862			if ((gr = getgrnam(name)) == NULL) {
1863				syslog(LOG_ERR, "Unknown group: %s", name);
1864				continue;
1865			}
1866			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
1867		}
1868	}
1869	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
1870		syslog(LOG_ERR, "Too many groups");
1871}
1872
1873#define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
1874/*
1875 * Routines that maintain the remote mounttab
1876 */
1877void
1878get_mountlist()
1879{
1880	struct mountlist *mlp, **mlpp;
1881	char *host, *dirp, *cp;
1882	int len;
1883	char str[STRSIZ];
1884	FILE *mlfile;
1885
1886	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
1887		syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST);
1888		return;
1889	}
1890	mlpp = &mlhead;
1891	while (fgets(str, STRSIZ, mlfile) != NULL) {
1892		cp = str;
1893		host = strsep(&cp, " \t\n");
1894		dirp = strsep(&cp, " \t\n");
1895		if (host == NULL || dirp == NULL)
1896			continue;
1897		mlp = (struct mountlist *)malloc(sizeof (*mlp));
1898		strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
1899		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
1900		strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
1901		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
1902		mlp->ml_next = (struct mountlist *)NULL;
1903		*mlpp = mlp;
1904		mlpp = &mlp->ml_next;
1905	}
1906	fclose(mlfile);
1907}
1908
1909void
1910del_mlist(hostp, dirp)
1911	char *hostp, *dirp;
1912{
1913	struct mountlist *mlp, **mlpp;
1914	struct mountlist *mlp2;
1915	FILE *mlfile;
1916	int fnd = 0;
1917
1918	mlpp = &mlhead;
1919	mlp = mlhead;
1920	while (mlp) {
1921		if (!strcmp(mlp->ml_host, hostp) &&
1922		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
1923			fnd = 1;
1924			mlp2 = mlp;
1925			*mlpp = mlp = mlp->ml_next;
1926			free((caddr_t)mlp2);
1927		} else {
1928			mlpp = &mlp->ml_next;
1929			mlp = mlp->ml_next;
1930		}
1931	}
1932	if (fnd) {
1933		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
1934			syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST);
1935			return;
1936		}
1937		mlp = mlhead;
1938		while (mlp) {
1939			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
1940			mlp = mlp->ml_next;
1941		}
1942		fclose(mlfile);
1943	}
1944}
1945
1946void
1947add_mlist(hostp, dirp)
1948	char *hostp, *dirp;
1949{
1950	struct mountlist *mlp, **mlpp;
1951	FILE *mlfile;
1952
1953	mlpp = &mlhead;
1954	mlp = mlhead;
1955	while (mlp) {
1956		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
1957			return;
1958		mlpp = &mlp->ml_next;
1959		mlp = mlp->ml_next;
1960	}
1961	mlp = (struct mountlist *)malloc(sizeof (*mlp));
1962	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
1963	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
1964	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
1965	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
1966	mlp->ml_next = (struct mountlist *)NULL;
1967	*mlpp = mlp;
1968	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
1969		syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST);
1970		return;
1971	}
1972	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
1973	fclose(mlfile);
1974}
1975
1976/*
1977 * This function is called via. SIGTERM when the system is going down.
1978 * It sends a broadcast RPCMNT_UMNTALL.
1979 */
1980void
1981send_umntall()
1982{
1983	(void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
1984		xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
1985	exit(0);
1986}
1987
1988int
1989umntall_each(resultsp, raddr)
1990	caddr_t resultsp;
1991	struct sockaddr_in *raddr;
1992{
1993	return (1);
1994}
1995
1996/*
1997 * Free up a group list.
1998 */
1999void
2000free_grp(grp)
2001	struct grouplist *grp;
2002{
2003	char **addrp;
2004
2005	if (grp->gr_type == GT_HOST) {
2006		if (grp->gr_ptr.gt_hostent->h_name) {
2007			addrp = grp->gr_ptr.gt_hostent->h_addr_list;
2008			while (addrp && *addrp)
2009				free(*addrp++);
2010			free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
2011			free(grp->gr_ptr.gt_hostent->h_name);
2012		}
2013		free((caddr_t)grp->gr_ptr.gt_hostent);
2014	} else if (grp->gr_type == GT_NET) {
2015		if (grp->gr_ptr.gt_net.nt_name)
2016			free(grp->gr_ptr.gt_net.nt_name);
2017	}
2018#ifdef ISO
2019	else if (grp->gr_type == GT_ISO)
2020		free((caddr_t)grp->gr_ptr.gt_isoaddr);
2021#endif
2022	free((caddr_t)grp);
2023}
2024
2025#ifdef DEBUG
2026void
2027SYSLOG(int pri, const char *fmt, ...)
2028{
2029	va_list ap;
2030
2031	va_start(ap, fmt);
2032	vfprintf(stderr, fmt, ap);
2033	va_end(ap);
2034}
2035#endif /* DEBUG */
2036
2037/*
2038 * Check options for consistency.
2039 */
2040int
2041check_options(dp)
2042	struct dirlist *dp;
2043{
2044
2045	if (dp == (struct dirlist *)NULL)
2046	    return (1);
2047	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
2048	    (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
2049	    (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
2050	    syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
2051	    return (1);
2052	}
2053	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2054	    syslog(LOG_ERR, "-mask requires -net");
2055	    return (1);
2056	}
2057	if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
2058	    syslog(LOG_ERR, "-net and -iso mutually exclusive");
2059	    return (1);
2060	}
2061	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2062	    syslog(LOG_ERR, "-alldir has multiple directories");
2063	    return (1);
2064	}
2065	return (0);
2066}
2067
2068/*
2069 * Check an absolute directory path for any symbolic links. Return true
2070 * if no symbolic links are found.
2071 */
2072int
2073check_dirpath(dirp)
2074	char *dirp;
2075{
2076	char *cp;
2077	int ret = 1;
2078	struct stat sb;
2079
2080	cp = dirp + 1;
2081	while (*cp && ret) {
2082		if (*cp == '/') {
2083			*cp = '\0';
2084			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2085				ret = 0;
2086			*cp = '/';
2087		}
2088		cp++;
2089	}
2090	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2091		ret = 0;
2092	return (ret);
2093}
2094
2095/*
2096 * Just translate an ascii string to an integer.
2097 */
2098int
2099get_num(cp)
2100	register char *cp;
2101{
2102	register int res = 0;
2103
2104	while (*cp) {
2105		if (*cp < '0' || *cp > '9')
2106			return (-1);
2107		res = res * 10 + (*cp++ - '0');
2108	}
2109	return (res);
2110}
2111