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