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