mountd.c revision 53117
1184610Salfred/*
2184610Salfred * Copyright (c) 1989, 1993
3188412Sthompsa *	The Regents of the University of California.  All rights reserved.
4184610Salfred *
5184610Salfred * This code is derived from software contributed to Berkeley by
6184610Salfred * Herb Hasler and Rick Macklem at The University of Guelph.
7184610Salfred *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer.
13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer in the
15184610Salfred *    documentation and/or other materials provided with the distribution.
16184610Salfred * 3. All advertising materials mentioning features or use of this software
17184610Salfred *    must display the following acknowledgement:
18184610Salfred *	This product includes software developed by the University of
19184610Salfred *	California, Berkeley and its contributors.
20184610Salfred * 4. Neither the name of the University nor the names of its contributors
21184610Salfred *    may be used to endorse or promote products derived from this software
22184610Salfred *    without specific prior written permission.
23184610Salfred *
24184610Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27226709Syongari * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28226709Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29226709Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30194677Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31194677Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32226709Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33226709Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34194677Sthompsa * SUCH DAMAGE.
35226709Syongari */
36226709Syongari
37226709Syongari#ifndef lint
38194677Sthompsastatic const char copyright[] =
39194677Sthompsa"@(#) Copyright (c) 1989, 1993\n\
40226709Syongari	The Regents of the University of California.  All rights reserved.\n";
41226709Syongari#endif /*not lint*/
42194677Sthompsa
43194677Sthompsa#ifndef lint
44194677Sthompsa#if 0
45226709Syongaristatic char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
46226709Syongari#endif
47226709Syongaristatic const char rcsid[] =
48226709Syongari  "$FreeBSD: head/usr.sbin/mountd/mountd.c 53117 1999-11-12 21:52:10Z billf $";
49226709Syongari#endif /*not lint*/
50226709Syongari
51226709Syongari#include <sys/param.h>
52226709Syongari#include <sys/mount.h>
53226709Syongari#include <sys/stat.h>
54188942Sthompsa#include <sys/syslog.h>
55194677Sthompsa#include <sys/sysctl.h>
56188412Sthompsa
57188942Sthompsa#include <rpc/rpc.h>
58188942Sthompsa#include <rpc/pmap_clnt.h>
59184610Salfred#include <nfs/rpcv2.h>
60227309Sed#include <nfs/nfsproto.h>
61227309Sed#include <nfs/nfs.h>
62184610Salfred#include <ufs/ufs/ufsmount.h>
63188412Sthompsa#include <msdosfs/msdosfsmount.h>
64188412Sthompsa#include <isofs/cd9660/cd9660_mount.h>	/* XXX need isofs in include */
65188412Sthompsa
66188412Sthompsa#include <arpa/inet.h>
67188942Sthompsa
68188942Sthompsa#include <ctype.h>
69188552Sthompsa#include <err.h>
70188412Sthompsa#include <errno.h>
71188412Sthompsa#include <grp.h>
72193045Sthompsa#include <netdb.h>
73193045Sthompsa#include <pwd.h>
74193045Sthompsa#include <signal.h>
75193045Sthompsa#include <stdio.h>
76193045Sthompsa#include <stdlib.h>
77193045Sthompsa#include <string.h>
78193045Sthompsa#include <unistd.h>
79188412Sthompsa#include "pathnames.h"
80188412Sthompsa
81188412Sthompsa#ifdef DEBUG
82188412Sthompsa#include <stdarg.h>
83188412Sthompsa#endif
84188412Sthompsa
85188412Sthompsa/*
86188412Sthompsa * Structures for keeping the mount list and export list
87188412Sthompsa */
88188412Sthompsastruct mountlist {
89188412Sthompsa	struct mountlist *ml_next;
90188412Sthompsa	char	ml_host[RPCMNT_NAMELEN+1];
91194228Sthompsa	char	ml_dirp[RPCMNT_PATHLEN+1];
92184610Salfred};
93194228Sthompsa
94188412Sthompsastruct dirlist {
95188412Sthompsa	struct dirlist	*dp_left;
96188412Sthompsa	struct dirlist	*dp_right;
97194228Sthompsa	int		dp_flag;
98188412Sthompsa	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
99188412Sthompsa	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
100184610Salfred};
101188412Sthompsa/* dp_flag bits */
102192984Sthompsa#define	DP_DEFSET	0x1
103193045Sthompsa#define DP_HOSTSET	0x2
104192984Sthompsa#define DP_KERB		0x4
105188412Sthompsa
106192984Sthompsastruct exportlist {
107188412Sthompsa	struct exportlist *ex_next;
108188412Sthompsa	struct dirlist	*ex_dirl;
109188412Sthompsa	struct dirlist	*ex_defdir;
110194228Sthompsa	int		ex_flag;
111188412Sthompsa	fsid_t		ex_fs;
112184610Salfred	char		*ex_fsdir;
113188412Sthompsa	char		*ex_indexfile;
114188412Sthompsa};
115188412Sthompsa/* ex_flag bits */
116188412Sthompsa#define	EX_LINKED	0x1
117188412Sthompsa
118192984Sthompsastruct netmsk {
119194228Sthompsa	u_int32_t	nt_net;
120188412Sthompsa	u_int32_t	nt_mask;
121188412Sthompsa	char		*nt_name;
122188412Sthompsa};
123188412Sthompsa
124188412Sthompsaunion grouptypes {
125188412Sthompsa	struct hostent *gt_hostent;
126188412Sthompsa	struct netmsk	gt_net;
127188412Sthompsa};
128188412Sthompsa
129194228Sthompsastruct grouplist {
130184610Salfred	int gr_type;
131184610Salfred	union grouptypes gr_ptr;
132188412Sthompsa	struct grouplist *gr_next;
133194228Sthompsa};
134184610Salfred/* Group types */
135188412Sthompsa#define	GT_NULL		0x0
136188412Sthompsa#define	GT_HOST		0x1
137184610Salfred#define	GT_NET		0x2
138188412Sthompsa#define GT_IGNORE	0x5
139194228Sthompsa
140188412Sthompsastruct hostlist {
141188412Sthompsa	int		 ht_flag;	/* Uses DP_xx bits */
142188412Sthompsa	struct grouplist *ht_grp;
143188412Sthompsa	struct hostlist	 *ht_next;
144188412Sthompsa};
145194228Sthompsa
146188412Sthompsastruct fhreturn {
147188412Sthompsa	int	fhr_flag;
148188412Sthompsa	int	fhr_vers;
149188412Sthompsa	nfsfh_t	fhr_fh;
150188412Sthompsa};
151188412Sthompsa
152188412Sthompsa/* Global defs */
153192984Sthompsachar	*add_expdir __P((struct dirlist **, char *, int));
154188412Sthompsavoid	add_dlist __P((struct dirlist **, struct dirlist *,
155188412Sthompsa				struct grouplist *, int));
156188412Sthompsavoid	add_mlist __P((char *, char *));
157188412Sthompsaint	check_dirpath __P((char *));
158188412Sthompsaint	check_options __P((struct dirlist *));
159188412Sthompsaint	chk_host __P((struct dirlist *, u_int32_t, int *, int *));
160188412Sthompsavoid	del_mlist __P((char *, char *));
161194228Sthompsastruct dirlist *dirp_search __P((struct dirlist *, char *));
162188412Sthompsaint	do_mount __P((struct exportlist *, struct grouplist *, int,
163188412Sthompsa		struct ucred *, char *, int, struct statfs *));
164188412Sthompsaint	do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
165188412Sthompsa				int *, int *, struct ucred *));
166188412Sthompsastruct	exportlist *ex_search __P((fsid_t *));
167188412Sthompsastruct	exportlist *get_exp __P((void));
168188412Sthompsavoid	free_dir __P((struct dirlist *));
169188412Sthompsavoid	free_exp __P((struct exportlist *));
170188412Sthompsavoid	free_grp __P((struct grouplist *));
171188412Sthompsavoid	free_host __P((struct hostlist *));
172194228Sthompsavoid	get_exportlist __P((void));
173188412Sthompsaint	get_host __P((char *, struct grouplist *, struct grouplist *));
174188412Sthompsaint	get_num __P((char *));
175188412Sthompsastruct hostlist *get_ht __P((void));
176188412Sthompsaint	get_line __P((void));
177188412Sthompsavoid	get_mountlist __P((void));
178188412Sthompsaint	get_net __P((char *, struct netmsk *, int));
179188412Sthompsavoid	getexp_err __P((struct exportlist *, struct grouplist *));
180188412Sthompsastruct grouplist *get_grp __P((void));
181188412Sthompsavoid	hang_dirp __P((struct dirlist *, struct grouplist *,
182188412Sthompsa				struct exportlist *, int));
183188412Sthompsavoid	mntsrv __P((struct svc_req *, SVCXPRT *));
184188412Sthompsavoid	nextfield __P((char **, char **));
185188412Sthompsavoid	out_of_mem __P((void));
186188412Sthompsavoid	parsecred __P((char *, struct ucred *));
187188412Sthompsaint	put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
188188412Sthompsaint	scan_tree __P((struct dirlist *, u_int32_t));
189188412Sthompsastatic void usage __P((void));
190188412Sthompsaint	xdr_dir __P((XDR *, char *));
191192984Sthompsaint	xdr_explist __P((XDR *, caddr_t));
192188412Sthompsaint	xdr_fhs __P((XDR *, caddr_t));
193192984Sthompsaint	xdr_mlist __P((XDR *, caddr_t));
194192984Sthompsa
195192984Sthompsa/* C library */
196188412Sthompsaint	getnetgrent();
197188412Sthompsavoid	endnetgrent();
198188412Sthompsavoid	setnetgrent();
199188412Sthompsa
200188412Sthompsastruct exportlist *exphead;
201188412Sthompsastruct mountlist *mlhead;
202188412Sthompsastruct grouplist *grphead;
203188412Sthompsachar exname[MAXPATHLEN];
204188412Sthompsastruct ucred def_anon = {
205188412Sthompsa	1,
206194228Sthompsa	(uid_t) -2,
207188412Sthompsa	1,
208188412Sthompsa	{ (gid_t) -2 }
209226709Syongari};
210188412Sthompsaint force_v2 = 0;
211184610Salfredint resvport_only = 1;
212188412Sthompsaint dir_only = 1;
213226709Syongariint log = 0;
214184610Salfredint opt_flags;
215184610Salfred/* Bits for above */
216188412Sthompsa#define	OP_MAPROOT	0x01
217188412Sthompsa#define	OP_MAPALL	0x02
218226709Syongari#define	OP_KERB		0x04
219226709Syongari#define	OP_MASK		0x08
220226709Syongari#define	OP_NET		0x10
221226709Syongari#define	OP_ALLDIRS	0x40
222226709Syongari
223226709Syongari#ifdef DEBUG
224226709Syongariint debug = 1;
225226709Syongarivoid	SYSLOG __P((int, const char *, ...));
226226709Syongari#define syslog SYSLOG
227226709Syongari#else
228226709Syongariint debug = 0;
229226709Syongari#endif
230226709Syongari
231226709Syongari/*
232226709Syongari * Mountd server for NFS mount protocol as described in:
233184610Salfred * NFS: Network File System Protocol Specification, RFC1094, Appendix A
234226709Syongari * The optional arguments are the exports file name
235226709Syongari * default: _PATH_EXPORTS
236226709Syongari * and "-n" to allow nonroot mount.
237226709Syongari */
238226709Syongariint
239226709Syongarimain(argc, argv)
240226709Syongari	int argc;
241226709Syongari	char **argv;
242188412Sthompsa{
243188412Sthompsa	SVCXPRT *udptransp, *tcptransp;
244184610Salfred	int c, error, mib[3];
245226709Syongari	struct vfsconf vfc;
246226709Syongari
247226709Syongari	error = getvfsbyname("nfs", &vfc);
248226709Syongari	if (error && vfsisloadable("nfs")) {
249226709Syongari		if(vfsload("nfs"))
250188412Sthompsa			err(1, "vfsload(nfs)");
251188412Sthompsa		endvfsent();	/* flush cache */
252226709Syongari		error = getvfsbyname("nfs", &vfc);
253226709Syongari	}
254226709Syongari	if (error)
255188412Sthompsa		errx(1, "NFS support is not available in the running kernel");
256188412Sthompsa
257188412Sthompsa	while ((c = getopt(argc, argv, "2dlnr")) != -1)
258188412Sthompsa		switch (c) {
259188412Sthompsa		case '2':
260188412Sthompsa			force_v2 = 1;
261188412Sthompsa			break;
262217556Smdf		case 'n':
263188412Sthompsa			resvport_only = 0;
264188412Sthompsa			break;
265188412Sthompsa		case 'r':
266188412Sthompsa			dir_only = 0;
267188412Sthompsa			break;
268226709Syongari		case 'd':
269188412Sthompsa			debug = debug ? 0 : 1;
270188412Sthompsa			break;
271188412Sthompsa		case 'l':
272188412Sthompsa			log = 1;
273184610Salfred			break;
274188412Sthompsa		default:
275188412Sthompsa			usage();
276188412Sthompsa		};
277184610Salfred	argc -= optind;
278188412Sthompsa	argv += optind;
279194228Sthompsa	grphead = (struct grouplist *)NULL;
280188412Sthompsa	exphead = (struct exportlist *)NULL;
281188412Sthompsa	mlhead = (struct mountlist *)NULL;
282184610Salfred	if (argc == 1) {
283188412Sthompsa		strncpy(exname, *argv, MAXPATHLEN-1);
284194228Sthompsa		exname[MAXPATHLEN-1] = '\0';
285184610Salfred	} else
286188412Sthompsa		strcpy(exname, _PATH_EXPORTS);
287188412Sthompsa	openlog("mountd", LOG_PID, LOG_DAEMON);
288184610Salfred	if (debug)
289188412Sthompsa		warnx("getting export list");
290184610Salfred	get_exportlist();
291188412Sthompsa	if (debug)
292188412Sthompsa		warnx("getting mount list");
293188412Sthompsa	get_mountlist();
294188412Sthompsa	if (debug)
295184610Salfred		warnx("here we go");
296188412Sthompsa	if (debug == 0) {
297194228Sthompsa		daemon(0, 0);
298188412Sthompsa		signal(SIGINT, SIG_IGN);
299188412Sthompsa		signal(SIGQUIT, SIG_IGN);
300188412Sthompsa	}
301196403Sjhb	signal(SIGHUP, (void (*) __P((int))) get_exportlist);
302188412Sthompsa	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
303196403Sjhb	  if (pidfile != NULL) {
304184610Salfred		fprintf(pidfile, "%d\n", getpid());
305184610Salfred		fclose(pidfile);
306188412Sthompsa	  }
307188412Sthompsa	}
308184610Salfred	if (!resvport_only) {
309188412Sthompsa		mib[0] = CTL_VFS;
310188412Sthompsa		mib[1] = vfc.vfc_typenum;
311188412Sthompsa		mib[2] = NFS_NFSPRIVPORT;
312188412Sthompsa		if (sysctl(mib, 3, NULL, NULL, &resvport_only,
313188412Sthompsa		    sizeof(resvport_only)) != 0 && errno != ENOENT) {
314188412Sthompsa			syslog(LOG_ERR, "sysctl: %m");
315188412Sthompsa			exit(1);
316188412Sthompsa		}
317188412Sthompsa	}
318188412Sthompsa	if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
319188412Sthompsa	    (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
320194228Sthompsa		syslog(LOG_ERR, "can't create socket");
321188412Sthompsa		exit(1);
322188412Sthompsa	}
323188412Sthompsa	pmap_unset(RPCPROG_MNT, 1);
324194228Sthompsa	pmap_unset(RPCPROG_MNT, 3);
325188412Sthompsa	if (!force_v2)
326194228Sthompsa		if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
327188412Sthompsa		    !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
328188412Sthompsa			syslog(LOG_ERR, "can't register mount");
329226709Syongari			exit(1);
330226709Syongari		}
331226709Syongari	if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
332226709Syongari	    !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) {
333226709Syongari		syslog(LOG_ERR, "can't register mount");
334226709Syongari		exit(1);
335226709Syongari	}
336188412Sthompsa	svc_run();
337188412Sthompsa	syslog(LOG_ERR, "mountd died");
338188412Sthompsa	exit(1);
339192984Sthompsa}
340188412Sthompsa
341188412Sthompsastatic void
342188412Sthompsausage()
343188412Sthompsa{
344188412Sthompsa	fprintf(stderr,
345188412Sthompsa		"usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n");
346188412Sthompsa	exit(1);
347188412Sthompsa}
348188412Sthompsa
349192984Sthompsa/*
350188412Sthompsa * The mount rpc service
351192984Sthompsa */
352192984Sthompsavoid
353192984Sthompsamntsrv(rqstp, transp)
354188412Sthompsa	struct svc_req *rqstp;
355188412Sthompsa	SVCXPRT *transp;
356188412Sthompsa{
357188412Sthompsa	struct exportlist *ep;
358188412Sthompsa	struct dirlist *dp;
359188412Sthompsa	struct fhreturn fhr;
360188412Sthompsa	struct stat stb;
361188412Sthompsa	struct statfs fsb;
362188412Sthompsa	struct hostent *hp;
363188412Sthompsa	struct in_addr saddrin;
364194228Sthompsa	u_int32_t saddr;
365188412Sthompsa	u_short sport;
366188412Sthompsa	char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
367188412Sthompsa	int bad = 0, defset, hostset;
368192984Sthompsa	sigset_t sighup_mask;
369188412Sthompsa
370192984Sthompsa	sigemptyset(&sighup_mask);
371192984Sthompsa	sigaddset(&sighup_mask, SIGHUP);
372192984Sthompsa	saddr = transp->xp_raddr.sin_addr.s_addr;
373188412Sthompsa	saddrin = transp->xp_raddr.sin_addr;
374188412Sthompsa	sport = ntohs(transp->xp_raddr.sin_port);
375188412Sthompsa	hp = (struct hostent *)NULL;
376194228Sthompsa	switch (rqstp->rq_proc) {
377188412Sthompsa	case NULLPROC:
378188412Sthompsa		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
379188412Sthompsa			syslog(LOG_ERR, "can't send reply");
380188412Sthompsa		return;
381226709Syongari	case RPCMNT_MOUNT:
382226709Syongari		if (sport >= IPPORT_RESERVED && resvport_only) {
383226709Syongari			syslog(LOG_NOTICE,
384226709Syongari			    "mount request from %s from unprivileged port",
385226709Syongari			    inet_ntoa(saddrin));
386226709Syongari			svcerr_weakauth(transp);
387226709Syongari			return;
388188412Sthompsa		}
389188412Sthompsa		if (!svc_getargs(transp, xdr_dir, rpcpath)) {
390188412Sthompsa			syslog(LOG_NOTICE, "undecodable mount request from %s",
391192984Sthompsa			    inet_ntoa(saddrin));
392188412Sthompsa			svcerr_decode(transp);
393188412Sthompsa			return;
394188412Sthompsa		}
395188412Sthompsa
396188412Sthompsa		/*
397188412Sthompsa		 * Get the real pathname and make sure it is a directory
398188412Sthompsa		 * or a regular file if the -r option was specified
399188412Sthompsa		 * and it exists.
400188412Sthompsa		 */
401188412Sthompsa		if (realpath(rpcpath, dirpath) == NULL ||
402192984Sthompsa		    stat(dirpath, &stb) < 0 ||
403188412Sthompsa		    (!S_ISDIR(stb.st_mode) &&
404192984Sthompsa		     (dir_only || !S_ISREG(stb.st_mode))) ||
405192984Sthompsa		    statfs(dirpath, &fsb) < 0) {
406192984Sthompsa			chdir("/");	/* Just in case realpath doesn't */
407188412Sthompsa			syslog(LOG_NOTICE,
408188412Sthompsa			    "mount request from %s for non existent path %s",
409188412Sthompsa			    inet_ntoa(saddrin), dirpath);
410188412Sthompsa			if (debug)
411188412Sthompsa				warnx("stat failed on %s", dirpath);
412192984Sthompsa			bad = ENOENT;	/* We will send error reply later */
413188412Sthompsa		}
414192984Sthompsa
415192984Sthompsa		/* Check in the exports list */
416192984Sthompsa		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
417188412Sthompsa		ep = ex_search(&fsb.f_fsid);
418188412Sthompsa		hostset = defset = 0;
419188412Sthompsa		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
420188412Sthompsa		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
421226709Syongari		     chk_host(dp, saddr, &defset, &hostset)) ||
422226709Syongari		     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
423226709Syongari		      scan_tree(ep->ex_dirl, saddr) == 0))) {
424226709Syongari			if (bad) {
425226709Syongari				if (!svc_sendreply(transp, xdr_long,
426226709Syongari				    (caddr_t)&bad))
427226709Syongari					syslog(LOG_ERR, "can't send reply");
428188412Sthompsa				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
429188412Sthompsa				return;
430188412Sthompsa			}
431192984Sthompsa			if (hostset & DP_HOSTSET)
432188412Sthompsa				fhr.fhr_flag = hostset;
433188412Sthompsa			else
434188412Sthompsa				fhr.fhr_flag = defset;
435188412Sthompsa			fhr.fhr_vers = rqstp->rq_vers;
436188412Sthompsa			/* Get the file handle */
437188412Sthompsa			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
438188412Sthompsa			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
439188412Sthompsa				bad = errno;
440188412Sthompsa				syslog(LOG_ERR, "can't get fh for %s", dirpath);
441188412Sthompsa				if (!svc_sendreply(transp, xdr_long,
442188412Sthompsa				    (caddr_t)&bad))
443188412Sthompsa					syslog(LOG_ERR, "can't send reply");
444192984Sthompsa				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
445188412Sthompsa				return;
446192984Sthompsa			}
447192984Sthompsa			if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
448192984Sthompsa				syslog(LOG_ERR, "can't send reply");
449188412Sthompsa			if (hp == NULL)
450188412Sthompsa				hp = gethostbyaddr((caddr_t)&saddr,
451188412Sthompsa				    sizeof(saddr), AF_INET);
452188412Sthompsa			if (hp)
453188412Sthompsa				add_mlist(hp->h_name, dirpath);
454188412Sthompsa			else
455188412Sthompsa				add_mlist(inet_ntoa(saddrin),
456188412Sthompsa					dirpath);
457192984Sthompsa			if (debug)
458188412Sthompsa				warnx("mount successful");
459188412Sthompsa			if (log)
460188412Sthompsa				syslog(LOG_NOTICE,
461188412Sthompsa				    "mount request succeeded from %s for %s",
462188412Sthompsa				    inet_ntoa(saddrin), dirpath);
463188412Sthompsa		} else {
464188412Sthompsa			bad = EACCES;
465188412Sthompsa			syslog(LOG_NOTICE,
466188412Sthompsa			    "mount request denied from %s for %s",
467194228Sthompsa			    inet_ntoa(saddrin), dirpath);
468188412Sthompsa		}
469188412Sthompsa
470188412Sthompsa		if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad))
471192984Sthompsa			syslog(LOG_ERR, "can't send reply");
472188412Sthompsa		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
473192984Sthompsa		return;
474192984Sthompsa	case RPCMNT_DUMP:
475192984Sthompsa		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
476188412Sthompsa			syslog(LOG_ERR, "can't send reply");
477188412Sthompsa		else if (log)
478188412Sthompsa			syslog(LOG_NOTICE,
479188412Sthompsa			    "dump request succeeded from %s",
480188412Sthompsa			    inet_ntoa(saddrin));
481188412Sthompsa		return;
482188412Sthompsa	case RPCMNT_UMOUNT:
483188412Sthompsa		if (sport >= IPPORT_RESERVED && resvport_only) {
484188412Sthompsa			syslog(LOG_NOTICE,
485194228Sthompsa			    "umount request from %s from unprivileged port",
486188412Sthompsa			    inet_ntoa(saddrin));
487192984Sthompsa			svcerr_weakauth(transp);
488188412Sthompsa			return;
489188412Sthompsa		}
490188412Sthompsa		if (!svc_getargs(transp, xdr_dir, rpcpath)) {
491188412Sthompsa			syslog(LOG_NOTICE, "undecodable umount request from %s",
492188412Sthompsa			    inet_ntoa(saddrin));
493188412Sthompsa			svcerr_decode(transp);
494188412Sthompsa			return;
495188412Sthompsa		}
496188412Sthompsa		if (realpath(rpcpath, dirpath) == NULL) {
497188412Sthompsa			syslog(LOG_NOTICE, "umount request from %s "
498188412Sthompsa			    "for non existent path %s",
499188412Sthompsa			    inet_ntoa(saddrin), dirpath);
500188412Sthompsa		}
501188412Sthompsa		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
502188412Sthompsa			syslog(LOG_ERR, "can't send reply");
503188412Sthompsa		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
504188412Sthompsa		if (hp)
505188412Sthompsa			del_mlist(hp->h_name, dirpath);
506188412Sthompsa		del_mlist(inet_ntoa(saddrin), dirpath);
507188412Sthompsa		if (log)
508184610Salfred			syslog(LOG_NOTICE,
509188412Sthompsa			    "umount request succeeded from %s for %s",
510188412Sthompsa			    inet_ntoa(saddrin), dirpath);
511188412Sthompsa		return;
512188412Sthompsa	case RPCMNT_UMNTALL:
513188412Sthompsa		if (sport >= IPPORT_RESERVED && resvport_only) {
514188412Sthompsa			syslog(LOG_NOTICE,
515188412Sthompsa			    "umountall request from %s from unprivileged port",
516188412Sthompsa			    inet_ntoa(saddrin));
517188412Sthompsa			svcerr_weakauth(transp);
518188412Sthompsa			return;
519188412Sthompsa		}
520188412Sthompsa		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
521188412Sthompsa			syslog(LOG_ERR, "can't send reply");
522188412Sthompsa		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
523188412Sthompsa		if (hp)
524188412Sthompsa			del_mlist(hp->h_name, (char *)NULL);
525188412Sthompsa		del_mlist(inet_ntoa(saddrin), (char *)NULL);
526188412Sthompsa		if (log)
527188412Sthompsa			syslog(LOG_NOTICE,
528188412Sthompsa			    "umountall request succeeded from %s",
529188412Sthompsa			    inet_ntoa(saddrin));
530184610Salfred		return;
531188412Sthompsa	case RPCMNT_EXPORT:
532184610Salfred		if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
533188412Sthompsa			syslog(LOG_ERR, "can't send reply");
534188412Sthompsa		if (log)
535194228Sthompsa			syslog(LOG_NOTICE,
536188412Sthompsa			    "export request succeeded from %s",
537188412Sthompsa			    inet_ntoa(saddrin));
538188412Sthompsa		return;
539188412Sthompsa	default:
540188412Sthompsa		svcerr_noproc(transp);
541188412Sthompsa		return;
542188412Sthompsa	}
543188412Sthompsa}
544188412Sthompsa
545188412Sthompsa/*
546188412Sthompsa * Xdr conversion for a dirpath string
547188412Sthompsa */
548188412Sthompsaint
549194228Sthompsaxdr_dir(xdrsp, dirp)
550188942Sthompsa	XDR *xdrsp;
551194228Sthompsa	char *dirp;
552241394Skevlo{
553188412Sthompsa	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
554188412Sthompsa}
555189528Sthompsa
556194228Sthompsa/*
557189528Sthompsa * Xdr routine to generate file handle reply
558189528Sthompsa */
559189528Sthompsaint
560243857Sglebiusxdr_fhs(xdrsp, cp)
561189528Sthompsa	XDR *xdrsp;
562189528Sthompsa	caddr_t cp;
563189528Sthompsa{
564189528Sthompsa	register struct fhreturn *fhrp = (struct fhreturn *)cp;
565189528Sthompsa	u_long ok = 0, len, auth;
566189528Sthompsa
567189528Sthompsa	if (!xdr_long(xdrsp, &ok))
568189528Sthompsa		return (0);
569188412Sthompsa	switch (fhrp->fhr_vers) {
570194228Sthompsa	case 1:
571188412Sthompsa		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
572188412Sthompsa	case 3:
573188412Sthompsa		len = NFSX_V3FH;
574188412Sthompsa		if (!xdr_long(xdrsp, &len))
575188412Sthompsa			return (0);
576188412Sthompsa		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
577188412Sthompsa			return (0);
578188412Sthompsa		if (fhrp->fhr_flag & DP_KERB)
579188412Sthompsa			auth = RPCAUTH_KERB4;
580188412Sthompsa		else
581188412Sthompsa			auth = RPCAUTH_UNIX;
582188412Sthompsa		len = 1;
583188412Sthompsa		if (!xdr_long(xdrsp, &len))
584188412Sthompsa			return (0);
585188412Sthompsa		return (xdr_long(xdrsp, &auth));
586188412Sthompsa	};
587188412Sthompsa	return (0);
588194228Sthompsa}
589188412Sthompsa
590188412Sthompsaint
591188412Sthompsaxdr_mlist(xdrsp, cp)
592188412Sthompsa	XDR *xdrsp;
593188412Sthompsa	caddr_t cp;
594188412Sthompsa{
595188412Sthompsa	struct mountlist *mlp;
596189528Sthompsa	int true = 1;
597188412Sthompsa	int false = 0;
598188412Sthompsa	char *strp;
599194228Sthompsa
600188412Sthompsa	mlp = mlhead;
601213438Syongari	while (mlp) {
602188412Sthompsa		if (!xdr_bool(xdrsp, &true))
603188412Sthompsa			return (0);
604188412Sthompsa		strp = &mlp->ml_host[0];
605194228Sthompsa		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
606188412Sthompsa			return (0);
607188412Sthompsa		strp = &mlp->ml_dirp[0];
608188412Sthompsa		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
609188412Sthompsa			return (0);
610188412Sthompsa		mlp = mlp->ml_next;
611188412Sthompsa	}
612188412Sthompsa	if (!xdr_bool(xdrsp, &false))
613188412Sthompsa		return (0);
614188412Sthompsa	return (1);
615188412Sthompsa}
616188412Sthompsa
617188412Sthompsa/*
618194228Sthompsa * Xdr conversion for export list
619188412Sthompsa */
620188412Sthompsaint
621188412Sthompsaxdr_explist(xdrsp, cp)
622188412Sthompsa	XDR *xdrsp;
623188412Sthompsa	caddr_t cp;
624188412Sthompsa{
625188412Sthompsa	struct exportlist *ep;
626188412Sthompsa	int false = 0;
627188412Sthompsa	int putdef;
628188412Sthompsa	sigset_t sighup_mask;
629188412Sthompsa
630188412Sthompsa	sigemptyset(&sighup_mask);
631188412Sthompsa	sigaddset(&sighup_mask, SIGHUP);
632188412Sthompsa	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
633188412Sthompsa	ep = exphead;
634188412Sthompsa	while (ep) {
635188412Sthompsa		putdef = 0;
636188412Sthompsa		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
637188412Sthompsa			goto errout;
638188412Sthompsa		if (ep->ex_defdir && putdef == 0 &&
639194228Sthompsa			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
640188942Sthompsa			&putdef))
641			goto errout;
642		ep = ep->ex_next;
643	}
644	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
645	if (!xdr_bool(xdrsp, &false))
646		return (0);
647	return (1);
648errout:
649	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
650	return (0);
651}
652
653/*
654 * Called from xdr_explist() to traverse the tree and export the
655 * directory paths.
656 */
657int
658put_exlist(dp, xdrsp, adp, putdefp)
659	struct dirlist *dp;
660	XDR *xdrsp;
661	struct dirlist *adp;
662	int *putdefp;
663{
664	struct grouplist *grp;
665	struct hostlist *hp;
666	int true = 1;
667	int false = 0;
668	int gotalldir = 0;
669	char *strp;
670
671	if (dp) {
672		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
673			return (1);
674		if (!xdr_bool(xdrsp, &true))
675			return (1);
676		strp = dp->dp_dirp;
677		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
678			return (1);
679		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
680			gotalldir = 1;
681			*putdefp = 1;
682		}
683		if ((dp->dp_flag & DP_DEFSET) == 0 &&
684		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
685			hp = dp->dp_hosts;
686			while (hp) {
687				grp = hp->ht_grp;
688				if (grp->gr_type == GT_HOST) {
689					if (!xdr_bool(xdrsp, &true))
690						return (1);
691					strp = grp->gr_ptr.gt_hostent->h_name;
692					if (!xdr_string(xdrsp, &strp,
693					    RPCMNT_NAMELEN))
694						return (1);
695				} else if (grp->gr_type == GT_NET) {
696					if (!xdr_bool(xdrsp, &true))
697						return (1);
698					strp = grp->gr_ptr.gt_net.nt_name;
699					if (!xdr_string(xdrsp, &strp,
700					    RPCMNT_NAMELEN))
701						return (1);
702				}
703				hp = hp->ht_next;
704				if (gotalldir && hp == (struct hostlist *)NULL) {
705					hp = adp->dp_hosts;
706					gotalldir = 0;
707				}
708			}
709		}
710		if (!xdr_bool(xdrsp, &false))
711			return (1);
712		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
713			return (1);
714	}
715	return (0);
716}
717
718#define LINESIZ	10240
719char line[LINESIZ];
720FILE *exp_file;
721
722/*
723 * Get the export list
724 */
725void
726get_exportlist()
727{
728	struct exportlist *ep, *ep2;
729	struct grouplist *grp, *tgrp;
730	struct exportlist **epp;
731	struct dirlist *dirhead;
732	struct statfs fsb, *fsp;
733	struct hostent *hpe;
734	struct ucred anon;
735	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
736	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
737
738	dirp = NULL;
739	dirplen = 0;
740
741	/*
742	 * First, get rid of the old list
743	 */
744	ep = exphead;
745	while (ep) {
746		ep2 = ep;
747		ep = ep->ex_next;
748		free_exp(ep2);
749	}
750	exphead = (struct exportlist *)NULL;
751
752	grp = grphead;
753	while (grp) {
754		tgrp = grp;
755		grp = grp->gr_next;
756		free_grp(tgrp);
757	}
758	grphead = (struct grouplist *)NULL;
759
760	/*
761	 * And delete exports that are in the kernel for all local
762	 * file systems.
763	 * XXX: Should know how to handle all local exportable file systems
764	 *      instead of just "ufs".
765	 */
766	num = getmntinfo(&fsp, MNT_NOWAIT);
767	for (i = 0; i < num; i++) {
768		union {
769			struct ufs_args ua;
770			struct iso_args ia;
771			struct mfs_args ma;
772			struct msdosfs_args da;
773		} targs;
774
775		if (!strcmp(fsp->f_fstypename, "mfs") ||
776		    !strcmp(fsp->f_fstypename, "ufs") ||
777		    !strcmp(fsp->f_fstypename, "msdos") ||
778		    !strcmp(fsp->f_fstypename, "cd9660")) {
779			targs.ua.fspec = NULL;
780			targs.ua.export.ex_flags = MNT_DELEXPORT;
781			if (mount(fsp->f_fstypename, fsp->f_mntonname,
782				  fsp->f_flags | MNT_UPDATE,
783				  (caddr_t)&targs) < 0)
784				syslog(LOG_ERR, "can't delete exports for %s",
785				       fsp->f_mntonname);
786		}
787		fsp++;
788	}
789
790	/*
791	 * Read in the exports file and build the list, calling
792	 * mount() as we go along to push the export rules into the kernel.
793	 */
794	if ((exp_file = fopen(exname, "r")) == NULL) {
795		syslog(LOG_ERR, "can't open %s", exname);
796		exit(2);
797	}
798	dirhead = (struct dirlist *)NULL;
799	while (get_line()) {
800		if (debug)
801			warnx("got line %s", line);
802		cp = line;
803		nextfield(&cp, &endcp);
804		if (*cp == '#')
805			goto nextline;
806
807		/*
808		 * Set defaults.
809		 */
810		has_host = FALSE;
811		anon = def_anon;
812		exflags = MNT_EXPORTED;
813		got_nondir = 0;
814		opt_flags = 0;
815		ep = (struct exportlist *)NULL;
816
817		/*
818		 * Create new exports list entry
819		 */
820		len = endcp-cp;
821		tgrp = grp = get_grp();
822		while (len > 0) {
823			if (len > RPCMNT_NAMELEN) {
824			    getexp_err(ep, tgrp);
825			    goto nextline;
826			}
827			if (*cp == '-') {
828			    if (ep == (struct exportlist *)NULL) {
829				getexp_err(ep, tgrp);
830				goto nextline;
831			    }
832			    if (debug)
833				warnx("doing opt %s", cp);
834			    got_nondir = 1;
835			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
836				&exflags, &anon)) {
837				getexp_err(ep, tgrp);
838				goto nextline;
839			    }
840			} else if (*cp == '/') {
841			    savedc = *endcp;
842			    *endcp = '\0';
843			    if (check_dirpath(cp) &&
844				statfs(cp, &fsb) >= 0) {
845				if (got_nondir) {
846				    syslog(LOG_ERR, "dirs must be first");
847				    getexp_err(ep, tgrp);
848				    goto nextline;
849				}
850				if (ep) {
851				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
852					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
853					getexp_err(ep, tgrp);
854					goto nextline;
855				    }
856				} else {
857				    /*
858				     * See if this directory is already
859				     * in the list.
860				     */
861				    ep = ex_search(&fsb.f_fsid);
862				    if (ep == (struct exportlist *)NULL) {
863					ep = get_exp();
864					ep->ex_fs = fsb.f_fsid;
865					ep->ex_fsdir = (char *)
866					    malloc(strlen(fsb.f_mntonname) + 1);
867					if (ep->ex_fsdir)
868					    strcpy(ep->ex_fsdir,
869						fsb.f_mntonname);
870					else
871					    out_of_mem();
872					if (debug)
873					  warnx("making new ep fs=0x%x,0x%x",
874					      fsb.f_fsid.val[0],
875					      fsb.f_fsid.val[1]);
876				    } else if (debug)
877					warnx("found ep fs=0x%x,0x%x",
878					    fsb.f_fsid.val[0],
879					    fsb.f_fsid.val[1]);
880				}
881
882				/*
883				 * Add dirpath to export mount point.
884				 */
885				dirp = add_expdir(&dirhead, cp, len);
886				dirplen = len;
887			    } else {
888				getexp_err(ep, tgrp);
889				goto nextline;
890			    }
891			    *endcp = savedc;
892			} else {
893			    savedc = *endcp;
894			    *endcp = '\0';
895			    got_nondir = 1;
896			    if (ep == (struct exportlist *)NULL) {
897				getexp_err(ep, tgrp);
898				goto nextline;
899			    }
900
901			    /*
902			     * Get the host or netgroup.
903			     */
904			    setnetgrent(cp);
905			    netgrp = getnetgrent(&hst, &usr, &dom);
906			    do {
907				if (has_host) {
908				    grp->gr_next = get_grp();
909				    grp = grp->gr_next;
910				}
911				if (netgrp) {
912				    if (hst == 0) {
913					syslog(LOG_ERR,
914				"null hostname in netgroup %s, skipping", cp);
915					grp->gr_type = GT_IGNORE;
916				    } else if (get_host(hst, grp, tgrp)) {
917					syslog(LOG_ERR,
918			"bad host %s in netgroup %s, skipping", hst, cp);
919					grp->gr_type = GT_IGNORE;
920				    }
921				} else if (get_host(cp, grp, tgrp)) {
922				    syslog(LOG_ERR, "bad host %s, skipping", cp);
923				    grp->gr_type = GT_IGNORE;
924				}
925				has_host = TRUE;
926			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
927			    endnetgrent();
928			    *endcp = savedc;
929			}
930			cp = endcp;
931			nextfield(&cp, &endcp);
932			len = endcp - cp;
933		}
934		if (check_options(dirhead)) {
935			getexp_err(ep, tgrp);
936			goto nextline;
937		}
938		if (!has_host) {
939			grp->gr_type = GT_HOST;
940			if (debug)
941				warnx("adding a default entry");
942			/* add a default group and make the grp list NULL */
943			hpe = (struct hostent *)malloc(sizeof(struct hostent));
944			if (hpe == (struct hostent *)NULL)
945				out_of_mem();
946			hpe->h_name = strdup("Default");
947			hpe->h_addrtype = AF_INET;
948			hpe->h_length = sizeof (u_int32_t);
949			hpe->h_addr_list = (char **)NULL;
950			grp->gr_ptr.gt_hostent = hpe;
951
952		/*
953		 * Don't allow a network export coincide with a list of
954		 * host(s) on the same line.
955		 */
956		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
957			getexp_err(ep, tgrp);
958			goto nextline;
959
960        	/*
961	         * If an export list was specified on this line, make sure
962		 * that we have at least one valid entry, otherwise skip it.
963		 */
964		} else {
965			grp = tgrp;
966        		while (grp && grp->gr_type == GT_IGNORE)
967				grp = grp->gr_next;
968			if (! grp) {
969			    getexp_err(ep, tgrp);
970			    goto nextline;
971			}
972		}
973
974		/*
975		 * Loop through hosts, pushing the exports into the kernel.
976		 * After loop, tgrp points to the start of the list and
977		 * grp points to the last entry in the list.
978		 */
979		grp = tgrp;
980		do {
981		    if (do_mount(ep, grp, exflags, &anon, dirp,
982			dirplen, &fsb)) {
983			getexp_err(ep, tgrp);
984			goto nextline;
985		    }
986		} while (grp->gr_next && (grp = grp->gr_next));
987
988		/*
989		 * Success. Update the data structures.
990		 */
991		if (has_host) {
992			hang_dirp(dirhead, tgrp, ep, opt_flags);
993			grp->gr_next = grphead;
994			grphead = tgrp;
995		} else {
996			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
997				opt_flags);
998			free_grp(grp);
999		}
1000		dirhead = (struct dirlist *)NULL;
1001		if ((ep->ex_flag & EX_LINKED) == 0) {
1002			ep2 = exphead;
1003			epp = &exphead;
1004
1005			/*
1006			 * Insert in the list in alphabetical order.
1007			 */
1008			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1009				epp = &ep2->ex_next;
1010				ep2 = ep2->ex_next;
1011			}
1012			if (ep2)
1013				ep->ex_next = ep2;
1014			*epp = ep;
1015			ep->ex_flag |= EX_LINKED;
1016		}
1017nextline:
1018		if (dirhead) {
1019			free_dir(dirhead);
1020			dirhead = (struct dirlist *)NULL;
1021		}
1022	}
1023	fclose(exp_file);
1024}
1025
1026/*
1027 * Allocate an export list element
1028 */
1029struct exportlist *
1030get_exp()
1031{
1032	struct exportlist *ep;
1033
1034	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1035	if (ep == (struct exportlist *)NULL)
1036		out_of_mem();
1037	memset(ep, 0, sizeof(struct exportlist));
1038	return (ep);
1039}
1040
1041/*
1042 * Allocate a group list element
1043 */
1044struct grouplist *
1045get_grp()
1046{
1047	struct grouplist *gp;
1048
1049	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1050	if (gp == (struct grouplist *)NULL)
1051		out_of_mem();
1052	memset(gp, 0, sizeof(struct grouplist));
1053	return (gp);
1054}
1055
1056/*
1057 * Clean up upon an error in get_exportlist().
1058 */
1059void
1060getexp_err(ep, grp)
1061	struct exportlist *ep;
1062	struct grouplist *grp;
1063{
1064	struct grouplist *tgrp;
1065
1066	syslog(LOG_ERR, "bad exports list line %s", line);
1067	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1068		free_exp(ep);
1069	while (grp) {
1070		tgrp = grp;
1071		grp = grp->gr_next;
1072		free_grp(tgrp);
1073	}
1074}
1075
1076/*
1077 * Search the export list for a matching fs.
1078 */
1079struct exportlist *
1080ex_search(fsid)
1081	fsid_t *fsid;
1082{
1083	struct exportlist *ep;
1084
1085	ep = exphead;
1086	while (ep) {
1087		if (ep->ex_fs.val[0] == fsid->val[0] &&
1088		    ep->ex_fs.val[1] == fsid->val[1])
1089			return (ep);
1090		ep = ep->ex_next;
1091	}
1092	return (ep);
1093}
1094
1095/*
1096 * Add a directory path to the list.
1097 */
1098char *
1099add_expdir(dpp, cp, len)
1100	struct dirlist **dpp;
1101	char *cp;
1102	int len;
1103{
1104	struct dirlist *dp;
1105
1106	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1107	if (dp == (struct dirlist *)NULL)
1108		out_of_mem();
1109	dp->dp_left = *dpp;
1110	dp->dp_right = (struct dirlist *)NULL;
1111	dp->dp_flag = 0;
1112	dp->dp_hosts = (struct hostlist *)NULL;
1113	strcpy(dp->dp_dirp, cp);
1114	*dpp = dp;
1115	return (dp->dp_dirp);
1116}
1117
1118/*
1119 * Hang the dir list element off the dirpath binary tree as required
1120 * and update the entry for host.
1121 */
1122void
1123hang_dirp(dp, grp, ep, flags)
1124	struct dirlist *dp;
1125	struct grouplist *grp;
1126	struct exportlist *ep;
1127	int flags;
1128{
1129	struct hostlist *hp;
1130	struct dirlist *dp2;
1131
1132	if (flags & OP_ALLDIRS) {
1133		if (ep->ex_defdir)
1134			free((caddr_t)dp);
1135		else
1136			ep->ex_defdir = dp;
1137		if (grp == (struct grouplist *)NULL) {
1138			ep->ex_defdir->dp_flag |= DP_DEFSET;
1139			if (flags & OP_KERB)
1140				ep->ex_defdir->dp_flag |= DP_KERB;
1141		} else while (grp) {
1142			hp = get_ht();
1143			if (flags & OP_KERB)
1144				hp->ht_flag |= DP_KERB;
1145			hp->ht_grp = grp;
1146			hp->ht_next = ep->ex_defdir->dp_hosts;
1147			ep->ex_defdir->dp_hosts = hp;
1148			grp = grp->gr_next;
1149		}
1150	} else {
1151
1152		/*
1153		 * Loop through the directories adding them to the tree.
1154		 */
1155		while (dp) {
1156			dp2 = dp->dp_left;
1157			add_dlist(&ep->ex_dirl, dp, grp, flags);
1158			dp = dp2;
1159		}
1160	}
1161}
1162
1163/*
1164 * Traverse the binary tree either updating a node that is already there
1165 * for the new directory or adding the new node.
1166 */
1167void
1168add_dlist(dpp, newdp, grp, flags)
1169	struct dirlist **dpp;
1170	struct dirlist *newdp;
1171	struct grouplist *grp;
1172	int flags;
1173{
1174	struct dirlist *dp;
1175	struct hostlist *hp;
1176	int cmp;
1177
1178	dp = *dpp;
1179	if (dp) {
1180		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1181		if (cmp > 0) {
1182			add_dlist(&dp->dp_left, newdp, grp, flags);
1183			return;
1184		} else if (cmp < 0) {
1185			add_dlist(&dp->dp_right, newdp, grp, flags);
1186			return;
1187		} else
1188			free((caddr_t)newdp);
1189	} else {
1190		dp = newdp;
1191		dp->dp_left = (struct dirlist *)NULL;
1192		*dpp = dp;
1193	}
1194	if (grp) {
1195
1196		/*
1197		 * Hang all of the host(s) off of the directory point.
1198		 */
1199		do {
1200			hp = get_ht();
1201			if (flags & OP_KERB)
1202				hp->ht_flag |= DP_KERB;
1203			hp->ht_grp = grp;
1204			hp->ht_next = dp->dp_hosts;
1205			dp->dp_hosts = hp;
1206			grp = grp->gr_next;
1207		} while (grp);
1208	} else {
1209		dp->dp_flag |= DP_DEFSET;
1210		if (flags & OP_KERB)
1211			dp->dp_flag |= DP_KERB;
1212	}
1213}
1214
1215/*
1216 * Search for a dirpath on the export point.
1217 */
1218struct dirlist *
1219dirp_search(dp, dirpath)
1220	struct dirlist *dp;
1221	char *dirpath;
1222{
1223	int cmp;
1224
1225	if (dp) {
1226		cmp = strcmp(dp->dp_dirp, dirpath);
1227		if (cmp > 0)
1228			return (dirp_search(dp->dp_left, dirpath));
1229		else if (cmp < 0)
1230			return (dirp_search(dp->dp_right, dirpath));
1231		else
1232			return (dp);
1233	}
1234	return (dp);
1235}
1236
1237/*
1238 * Scan for a host match in a directory tree.
1239 */
1240int
1241chk_host(dp, saddr, defsetp, hostsetp)
1242	struct dirlist *dp;
1243	u_int32_t saddr;
1244	int *defsetp;
1245	int *hostsetp;
1246{
1247	struct hostlist *hp;
1248	struct grouplist *grp;
1249	u_int32_t **addrp;
1250
1251	if (dp) {
1252		if (dp->dp_flag & DP_DEFSET)
1253			*defsetp = dp->dp_flag;
1254		hp = dp->dp_hosts;
1255		while (hp) {
1256			grp = hp->ht_grp;
1257			switch (grp->gr_type) {
1258			case GT_HOST:
1259			    addrp = (u_int32_t **)
1260				grp->gr_ptr.gt_hostent->h_addr_list;
1261			    while (*addrp) {
1262				if (**addrp == saddr) {
1263				    *hostsetp = (hp->ht_flag | DP_HOSTSET);
1264				    return (1);
1265				}
1266				addrp++;
1267			    }
1268			    break;
1269			case GT_NET:
1270			    if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
1271				grp->gr_ptr.gt_net.nt_net) {
1272				*hostsetp = (hp->ht_flag | DP_HOSTSET);
1273				return (1);
1274			    }
1275			    break;
1276			};
1277			hp = hp->ht_next;
1278		}
1279	}
1280	return (0);
1281}
1282
1283/*
1284 * Scan tree for a host that matches the address.
1285 */
1286int
1287scan_tree(dp, saddr)
1288	struct dirlist *dp;
1289	u_int32_t saddr;
1290{
1291	int defset, hostset;
1292
1293	if (dp) {
1294		if (scan_tree(dp->dp_left, saddr))
1295			return (1);
1296		if (chk_host(dp, saddr, &defset, &hostset))
1297			return (1);
1298		if (scan_tree(dp->dp_right, saddr))
1299			return (1);
1300	}
1301	return (0);
1302}
1303
1304/*
1305 * Traverse the dirlist tree and free it up.
1306 */
1307void
1308free_dir(dp)
1309	struct dirlist *dp;
1310{
1311
1312	if (dp) {
1313		free_dir(dp->dp_left);
1314		free_dir(dp->dp_right);
1315		free_host(dp->dp_hosts);
1316		free((caddr_t)dp);
1317	}
1318}
1319
1320/*
1321 * Parse the option string and update fields.
1322 * Option arguments may either be -<option>=<value> or
1323 * -<option> <value>
1324 */
1325int
1326do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1327	char **cpp, **endcpp;
1328	struct exportlist *ep;
1329	struct grouplist *grp;
1330	int *has_hostp;
1331	int *exflagsp;
1332	struct ucred *cr;
1333{
1334	char *cpoptarg, *cpoptend;
1335	char *cp, *endcp, *cpopt, savedc, savedc2;
1336	int allflag, usedarg;
1337
1338	savedc2 = '\0';
1339	cpopt = *cpp;
1340	cpopt++;
1341	cp = *endcpp;
1342	savedc = *cp;
1343	*cp = '\0';
1344	while (cpopt && *cpopt) {
1345		allflag = 1;
1346		usedarg = -2;
1347		if ((cpoptend = strchr(cpopt, ','))) {
1348			*cpoptend++ = '\0';
1349			if ((cpoptarg = strchr(cpopt, '=')))
1350				*cpoptarg++ = '\0';
1351		} else {
1352			if ((cpoptarg = strchr(cpopt, '=')))
1353				*cpoptarg++ = '\0';
1354			else {
1355				*cp = savedc;
1356				nextfield(&cp, &endcp);
1357				**endcpp = '\0';
1358				if (endcp > cp && *cp != '-') {
1359					cpoptarg = cp;
1360					savedc2 = *endcp;
1361					*endcp = '\0';
1362					usedarg = 0;
1363				}
1364			}
1365		}
1366		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1367			*exflagsp |= MNT_EXRDONLY;
1368		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1369		    !(allflag = strcmp(cpopt, "mapall")) ||
1370		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1371			usedarg++;
1372			parsecred(cpoptarg, cr);
1373			if (allflag == 0) {
1374				*exflagsp |= MNT_EXPORTANON;
1375				opt_flags |= OP_MAPALL;
1376			} else
1377				opt_flags |= OP_MAPROOT;
1378		} else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1379			*exflagsp |= MNT_EXKERB;
1380			opt_flags |= OP_KERB;
1381		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1382			!strcmp(cpopt, "m"))) {
1383			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1384				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1385				return (1);
1386			}
1387			usedarg++;
1388			opt_flags |= OP_MASK;
1389		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
1390			!strcmp(cpopt, "n"))) {
1391			if (grp->gr_type != GT_NULL) {
1392				syslog(LOG_ERR, "network/host conflict");
1393				return (1);
1394			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1395				syslog(LOG_ERR, "bad net: %s", cpoptarg);
1396				return (1);
1397			}
1398			grp->gr_type = GT_NET;
1399			*has_hostp = 1;
1400			usedarg++;
1401			opt_flags |= OP_NET;
1402		} else if (!strcmp(cpopt, "alldirs")) {
1403			opt_flags |= OP_ALLDIRS;
1404		} else if (!strcmp(cpopt, "public")) {
1405			*exflagsp |= MNT_EXPUBLIC;
1406		} else if (!strcmp(cpopt, "webnfs")) {
1407			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1408			opt_flags |= OP_MAPALL;
1409		} else if (cpoptarg && !strcmp(cpopt, "index")) {
1410			ep->ex_indexfile = strdup(cpoptarg);
1411		} else {
1412			syslog(LOG_ERR, "bad opt %s", cpopt);
1413			return (1);
1414		}
1415		if (usedarg >= 0) {
1416			*endcp = savedc2;
1417			**endcpp = savedc;
1418			if (usedarg > 0) {
1419				*cpp = cp;
1420				*endcpp = endcp;
1421			}
1422			return (0);
1423		}
1424		cpopt = cpoptend;
1425	}
1426	**endcpp = savedc;
1427	return (0);
1428}
1429
1430/*
1431 * Translate a character string to the corresponding list of network
1432 * addresses for a hostname.
1433 */
1434int
1435get_host(cp, grp, tgrp)
1436	char *cp;
1437	struct grouplist *grp;
1438	struct grouplist *tgrp;
1439{
1440	struct grouplist *checkgrp;
1441	struct hostent *hp, *nhp;
1442	char **addrp, **naddrp;
1443	struct hostent t_host;
1444	int i;
1445	u_int32_t saddr;
1446	char *aptr[2];
1447
1448	if (grp->gr_type != GT_NULL)
1449		return (1);
1450	if ((hp = gethostbyname(cp)) == NULL) {
1451		if (isdigit(*cp)) {
1452			saddr = inet_addr(cp);
1453			if (saddr == -1) {
1454 				syslog(LOG_ERR, "inet_addr failed for %s", cp);
1455				return (1);
1456			}
1457			if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
1458				AF_INET)) == NULL) {
1459				hp = &t_host;
1460				hp->h_name = cp;
1461				hp->h_addrtype = AF_INET;
1462				hp->h_length = sizeof (u_int32_t);
1463				hp->h_addr_list = aptr;
1464				aptr[0] = (char *)&saddr;
1465				aptr[1] = (char *)NULL;
1466			}
1467		} else {
1468 			syslog(LOG_ERR, "gethostbyname failed for %s", cp);
1469			return (1);
1470		}
1471	}
1472        /*
1473         * Sanity check: make sure we don't already have an entry
1474         * for this host in the grouplist.
1475         */
1476        checkgrp = tgrp;
1477        while (checkgrp != NULL) {
1478		if (checkgrp->gr_type == GT_HOST &&
1479                    checkgrp->gr_ptr.gt_hostent != NULL &&
1480                    (!strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)
1481		|| *(u_int32_t *)checkgrp->gr_ptr.gt_hostent->h_addr ==
1482			*(u_int32_t *)hp->h_addr)) {
1483                        grp->gr_type = GT_IGNORE;
1484			return(0);
1485		}
1486                checkgrp = checkgrp->gr_next;
1487        }
1488
1489	grp->gr_type = GT_HOST;
1490	nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
1491		malloc(sizeof(struct hostent));
1492	if (nhp == (struct hostent *)NULL)
1493		out_of_mem();
1494	memmove(nhp, hp, sizeof(struct hostent));
1495	i = strlen(hp->h_name)+1;
1496	nhp->h_name = (char *)malloc(i);
1497	if (nhp->h_name == (char *)NULL)
1498		out_of_mem();
1499	memmove(nhp->h_name, hp->h_name, i);
1500	addrp = hp->h_addr_list;
1501	i = 1;
1502	while (*addrp++)
1503		i++;
1504	naddrp = nhp->h_addr_list = (char **)malloc(i*sizeof(char *));
1505	if (naddrp == (char **)NULL)
1506		out_of_mem();
1507	addrp = hp->h_addr_list;
1508	while (*addrp) {
1509		*naddrp = (char *)malloc(hp->h_length);
1510		if (*naddrp == (char *)NULL)
1511		    out_of_mem();
1512		memmove(*naddrp, *addrp, hp->h_length);
1513		addrp++;
1514		naddrp++;
1515	}
1516	*naddrp = (char *)NULL;
1517	if (debug)
1518		warnx("got host %s", hp->h_name);
1519	return (0);
1520}
1521
1522/*
1523 * Free up an exports list component
1524 */
1525void
1526free_exp(ep)
1527	struct exportlist *ep;
1528{
1529
1530	if (ep->ex_defdir) {
1531		free_host(ep->ex_defdir->dp_hosts);
1532		free((caddr_t)ep->ex_defdir);
1533	}
1534	if (ep->ex_fsdir)
1535		free(ep->ex_fsdir);
1536	if (ep->ex_indexfile)
1537		free(ep->ex_indexfile);
1538	free_dir(ep->ex_dirl);
1539	free((caddr_t)ep);
1540}
1541
1542/*
1543 * Free hosts.
1544 */
1545void
1546free_host(hp)
1547	struct hostlist *hp;
1548{
1549	struct hostlist *hp2;
1550
1551	while (hp) {
1552		hp2 = hp;
1553		hp = hp->ht_next;
1554		free((caddr_t)hp2);
1555	}
1556}
1557
1558struct hostlist *
1559get_ht()
1560{
1561	struct hostlist *hp;
1562
1563	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1564	if (hp == (struct hostlist *)NULL)
1565		out_of_mem();
1566	hp->ht_next = (struct hostlist *)NULL;
1567	hp->ht_flag = 0;
1568	return (hp);
1569}
1570
1571/*
1572 * Out of memory, fatal
1573 */
1574void
1575out_of_mem()
1576{
1577
1578	syslog(LOG_ERR, "out of memory");
1579	exit(2);
1580}
1581
1582/*
1583 * Do the mount syscall with the update flag to push the export info into
1584 * the kernel.
1585 */
1586int
1587do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1588	struct exportlist *ep;
1589	struct grouplist *grp;
1590	int exflags;
1591	struct ucred *anoncrp;
1592	char *dirp;
1593	int dirplen;
1594	struct statfs *fsb;
1595{
1596	char *cp = (char *)NULL;
1597	u_int32_t **addrp;
1598	int done;
1599	char savedc = '\0';
1600	struct sockaddr_in sin, imask;
1601	union {
1602		struct ufs_args ua;
1603		struct iso_args ia;
1604		struct mfs_args ma;
1605#ifdef __NetBSD__
1606		struct msdosfs_args da;
1607#endif
1608	} args;
1609	u_int32_t net;
1610
1611	args.ua.fspec = 0;
1612	args.ua.export.ex_flags = exflags;
1613	args.ua.export.ex_anon = *anoncrp;
1614	args.ua.export.ex_indexfile = ep->ex_indexfile;
1615	memset(&sin, 0, sizeof(sin));
1616	memset(&imask, 0, sizeof(imask));
1617	sin.sin_family = AF_INET;
1618	sin.sin_len = sizeof(sin);
1619	imask.sin_family = AF_INET;
1620	imask.sin_len = sizeof(sin);
1621	if (grp->gr_type == GT_HOST)
1622		addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list;
1623	else
1624		addrp = (u_int32_t **)NULL;
1625	done = FALSE;
1626	while (!done) {
1627		switch (grp->gr_type) {
1628		case GT_HOST:
1629			if (addrp) {
1630				sin.sin_addr.s_addr = **addrp;
1631				args.ua.export.ex_addrlen = sizeof(sin);
1632			} else
1633				args.ua.export.ex_addrlen = 0;
1634			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1635			args.ua.export.ex_masklen = 0;
1636			break;
1637		case GT_NET:
1638			if (grp->gr_ptr.gt_net.nt_mask)
1639			    imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
1640			else {
1641			    net = ntohl(grp->gr_ptr.gt_net.nt_net);
1642			    if (IN_CLASSA(net))
1643				imask.sin_addr.s_addr = inet_addr("255.0.0.0");
1644			    else if (IN_CLASSB(net))
1645				imask.sin_addr.s_addr =
1646				    inet_addr("255.255.0.0");
1647			    else
1648				imask.sin_addr.s_addr =
1649				    inet_addr("255.255.255.0");
1650			    grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
1651			}
1652			sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
1653			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1654			args.ua.export.ex_addrlen = sizeof (sin);
1655			args.ua.export.ex_mask = (struct sockaddr *)&imask;
1656			args.ua.export.ex_masklen = sizeof (imask);
1657			break;
1658		case GT_IGNORE:
1659			return(0);
1660			break;
1661		default:
1662			syslog(LOG_ERR, "bad grouptype");
1663			if (cp)
1664				*cp = savedc;
1665			return (1);
1666		};
1667
1668		/*
1669		 * XXX:
1670		 * Maybe I should just use the fsb->f_mntonname path instead
1671		 * of looping back up the dirp to the mount point??
1672		 * Also, needs to know how to export all types of local
1673		 * exportable file systems and not just "ufs".
1674		 */
1675		while (mount(fsb->f_fstypename, dirp,
1676		       fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1677			if (cp)
1678				*cp-- = savedc;
1679			else
1680				cp = dirp + dirplen - 1;
1681			if (errno == EPERM) {
1682				syslog(LOG_ERR,
1683				   "can't change attributes for %s", dirp);
1684				return (1);
1685			}
1686			if (opt_flags & OP_ALLDIRS) {
1687				syslog(LOG_ERR, "could not remount %s: %m",
1688					dirp);
1689				return (1);
1690			}
1691			/* back up over the last component */
1692			while (*cp == '/' && cp > dirp)
1693				cp--;
1694			while (*(cp - 1) != '/' && cp > dirp)
1695				cp--;
1696			if (cp == dirp) {
1697				if (debug)
1698					warnx("mnt unsucc");
1699				syslog(LOG_ERR, "can't export %s", dirp);
1700				return (1);
1701			}
1702			savedc = *cp;
1703			*cp = '\0';
1704		}
1705		if (addrp) {
1706			++addrp;
1707			if (*addrp == (u_int32_t *)NULL)
1708				done = TRUE;
1709		} else
1710			done = TRUE;
1711	}
1712	if (cp)
1713		*cp = savedc;
1714	return (0);
1715}
1716
1717/*
1718 * Translate a net address.
1719 */
1720int
1721get_net(cp, net, maskflg)
1722	char *cp;
1723	struct netmsk *net;
1724	int maskflg;
1725{
1726	struct netent *np;
1727	long netaddr;
1728	struct in_addr inetaddr, inetaddr2;
1729	char *name;
1730
1731	if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) {
1732		inetaddr = inet_makeaddr(netaddr, 0);
1733		/*
1734		 * Due to arbitrary subnet masks, you don't know how many
1735		 * bits to shift the address to make it into a network,
1736		 * however you do know how to make a network address into
1737		 * a host with host == 0 and then compare them.
1738		 * (What a pest)
1739		 */
1740		if (!maskflg) {
1741			setnetent(0);
1742			while ((np = getnetent())) {
1743				inetaddr2 = inet_makeaddr(np->n_net, 0);
1744				if (inetaddr2.s_addr == inetaddr.s_addr)
1745					break;
1746			}
1747			endnetent();
1748		}
1749	} else if ((np = getnetbyname(cp)) != NULL) {
1750		inetaddr = inet_makeaddr(np->n_net, 0);
1751	} else
1752		return (1);
1753
1754	if (maskflg)
1755		net->nt_mask = inetaddr.s_addr;
1756	else {
1757		if (np)
1758			name = np->n_name;
1759		else
1760			name = inet_ntoa(inetaddr);
1761		net->nt_name = (char *)malloc(strlen(name) + 1);
1762		if (net->nt_name == (char *)NULL)
1763			out_of_mem();
1764		strcpy(net->nt_name, name);
1765		net->nt_net = inetaddr.s_addr;
1766	}
1767	return (0);
1768}
1769
1770/*
1771 * Parse out the next white space separated field
1772 */
1773void
1774nextfield(cp, endcp)
1775	char **cp;
1776	char **endcp;
1777{
1778	char *p;
1779
1780	p = *cp;
1781	while (*p == ' ' || *p == '\t')
1782		p++;
1783	if (*p == '\n' || *p == '\0')
1784		*cp = *endcp = p;
1785	else {
1786		*cp = p++;
1787		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1788			p++;
1789		*endcp = p;
1790	}
1791}
1792
1793/*
1794 * Get an exports file line. Skip over blank lines and handle line
1795 * continuations.
1796 */
1797int
1798get_line()
1799{
1800	char *p, *cp;
1801	int len;
1802	int totlen, cont_line;
1803
1804	/*
1805	 * Loop around ignoring blank lines and getting all continuation lines.
1806	 */
1807	p = line;
1808	totlen = 0;
1809	do {
1810		if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
1811			return (0);
1812		len = strlen(p);
1813		cp = p + len - 1;
1814		cont_line = 0;
1815		while (cp >= p &&
1816		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
1817			if (*cp == '\\')
1818				cont_line = 1;
1819			cp--;
1820			len--;
1821		}
1822		*++cp = '\0';
1823		if (len > 0) {
1824			totlen += len;
1825			if (totlen >= LINESIZ) {
1826				syslog(LOG_ERR, "exports line too long");
1827				exit(2);
1828			}
1829			p = cp;
1830		}
1831	} while (totlen == 0 || cont_line);
1832	return (1);
1833}
1834
1835/*
1836 * Parse a description of a credential.
1837 */
1838void
1839parsecred(namelist, cr)
1840	char *namelist;
1841	struct ucred *cr;
1842{
1843	char *name;
1844	int cnt;
1845	char *names;
1846	struct passwd *pw;
1847	struct group *gr;
1848	int ngroups, groups[NGROUPS + 1];
1849
1850	/*
1851	 * Set up the unprivileged user.
1852	 */
1853	cr->cr_ref = 1;
1854	cr->cr_uid = -2;
1855	cr->cr_groups[0] = -2;
1856	cr->cr_ngroups = 1;
1857	/*
1858	 * Get the user's password table entry.
1859	 */
1860	names = strsep(&namelist, " \t\n");
1861	name = strsep(&names, ":");
1862	if (isdigit(*name) || *name == '-')
1863		pw = getpwuid(atoi(name));
1864	else
1865		pw = getpwnam(name);
1866	/*
1867	 * Credentials specified as those of a user.
1868	 */
1869	if (names == NULL) {
1870		if (pw == NULL) {
1871			syslog(LOG_ERR, "unknown user: %s", name);
1872			return;
1873		}
1874		cr->cr_uid = pw->pw_uid;
1875		ngroups = NGROUPS + 1;
1876		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
1877			syslog(LOG_ERR, "too many groups");
1878		/*
1879		 * Convert from int's to gid_t's and compress out duplicate
1880		 */
1881		cr->cr_ngroups = ngroups - 1;
1882		cr->cr_groups[0] = groups[0];
1883		for (cnt = 2; cnt < ngroups; cnt++)
1884			cr->cr_groups[cnt - 1] = groups[cnt];
1885		return;
1886	}
1887	/*
1888	 * Explicit credential specified as a colon separated list:
1889	 *	uid:gid:gid:...
1890	 */
1891	if (pw != NULL)
1892		cr->cr_uid = pw->pw_uid;
1893	else if (isdigit(*name) || *name == '-')
1894		cr->cr_uid = atoi(name);
1895	else {
1896		syslog(LOG_ERR, "unknown user: %s", name);
1897		return;
1898	}
1899	cr->cr_ngroups = 0;
1900	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
1901		name = strsep(&names, ":");
1902		if (isdigit(*name) || *name == '-') {
1903			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
1904		} else {
1905			if ((gr = getgrnam(name)) == NULL) {
1906				syslog(LOG_ERR, "unknown group: %s", name);
1907				continue;
1908			}
1909			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
1910		}
1911	}
1912	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
1913		syslog(LOG_ERR, "too many groups");
1914}
1915
1916#define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
1917/*
1918 * Routines that maintain the remote mounttab
1919 */
1920void
1921get_mountlist()
1922{
1923	struct mountlist *mlp, **mlpp;
1924	char *host, *dirp, *cp;
1925	char str[STRSIZ];
1926	FILE *mlfile;
1927
1928	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
1929		if (errno == ENOENT)
1930			return;
1931		else {
1932			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
1933			return;
1934		}
1935	}
1936	mlpp = &mlhead;
1937	while (fgets(str, STRSIZ, mlfile) != NULL) {
1938		cp = str;
1939		host = strsep(&cp, " \t\n");
1940		dirp = strsep(&cp, " \t\n");
1941		if (host == NULL || dirp == NULL)
1942			continue;
1943		mlp = (struct mountlist *)malloc(sizeof (*mlp));
1944		if (mlp == (struct mountlist *)NULL)
1945			out_of_mem();
1946		strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
1947		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
1948		strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
1949		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
1950		mlp->ml_next = (struct mountlist *)NULL;
1951		*mlpp = mlp;
1952		mlpp = &mlp->ml_next;
1953	}
1954	fclose(mlfile);
1955}
1956
1957void
1958del_mlist(hostp, dirp)
1959	char *hostp, *dirp;
1960{
1961	struct mountlist *mlp, **mlpp;
1962	struct mountlist *mlp2;
1963	FILE *mlfile;
1964	int fnd = 0;
1965
1966	mlpp = &mlhead;
1967	mlp = mlhead;
1968	while (mlp) {
1969		if (!strcmp(mlp->ml_host, hostp) &&
1970		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
1971			fnd = 1;
1972			mlp2 = mlp;
1973			*mlpp = mlp = mlp->ml_next;
1974			free((caddr_t)mlp2);
1975		} else {
1976			mlpp = &mlp->ml_next;
1977			mlp = mlp->ml_next;
1978		}
1979	}
1980	if (fnd) {
1981		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
1982			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
1983			return;
1984		}
1985		mlp = mlhead;
1986		while (mlp) {
1987			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
1988			mlp = mlp->ml_next;
1989		}
1990		fclose(mlfile);
1991	}
1992}
1993
1994void
1995add_mlist(hostp, dirp)
1996	char *hostp, *dirp;
1997{
1998	struct mountlist *mlp, **mlpp;
1999	FILE *mlfile;
2000
2001	mlpp = &mlhead;
2002	mlp = mlhead;
2003	while (mlp) {
2004		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2005			return;
2006		mlpp = &mlp->ml_next;
2007		mlp = mlp->ml_next;
2008	}
2009	mlp = (struct mountlist *)malloc(sizeof (*mlp));
2010	if (mlp == (struct mountlist *)NULL)
2011		out_of_mem();
2012	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2013	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2014	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2015	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2016	mlp->ml_next = (struct mountlist *)NULL;
2017	*mlpp = mlp;
2018	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2019		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2020		return;
2021	}
2022	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2023	fclose(mlfile);
2024}
2025
2026/*
2027 * Free up a group list.
2028 */
2029void
2030free_grp(grp)
2031	struct grouplist *grp;
2032{
2033	char **addrp;
2034
2035	if (grp->gr_type == GT_HOST) {
2036		if (grp->gr_ptr.gt_hostent->h_name) {
2037			addrp = grp->gr_ptr.gt_hostent->h_addr_list;
2038			while (addrp && *addrp)
2039				free(*addrp++);
2040			free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
2041			free(grp->gr_ptr.gt_hostent->h_name);
2042		}
2043		free((caddr_t)grp->gr_ptr.gt_hostent);
2044	} else if (grp->gr_type == GT_NET) {
2045		if (grp->gr_ptr.gt_net.nt_name)
2046			free(grp->gr_ptr.gt_net.nt_name);
2047	}
2048	free((caddr_t)grp);
2049}
2050
2051#ifdef DEBUG
2052void
2053SYSLOG(int pri, const char *fmt, ...)
2054{
2055	va_list ap;
2056
2057	va_start(ap, fmt);
2058	vfprintf(stderr, fmt, ap);
2059	va_end(ap);
2060}
2061#endif /* DEBUG */
2062
2063/*
2064 * Check options for consistency.
2065 */
2066int
2067check_options(dp)
2068	struct dirlist *dp;
2069{
2070
2071	if (dp == (struct dirlist *)NULL)
2072	    return (1);
2073	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
2074	    (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
2075	    (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
2076	    syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
2077	    return (1);
2078	}
2079	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2080	    syslog(LOG_ERR, "-mask requires -net");
2081	    return (1);
2082	}
2083	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2084	    syslog(LOG_ERR, "-alldirs has multiple directories");
2085	    return (1);
2086	}
2087	return (0);
2088}
2089
2090/*
2091 * Check an absolute directory path for any symbolic links. Return true
2092 * if no symbolic links are found.
2093 */
2094int
2095check_dirpath(dirp)
2096	char *dirp;
2097{
2098	char *cp;
2099	int ret = 1;
2100	struct stat sb;
2101
2102	cp = dirp + 1;
2103	while (*cp && ret) {
2104		if (*cp == '/') {
2105			*cp = '\0';
2106			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2107				ret = 0;
2108			*cp = '/';
2109		}
2110		cp++;
2111	}
2112	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2113		ret = 0;
2114	return (ret);
2115}
2116
2117/*
2118 * Just translate an ascii string to an integer.
2119 */
2120int
2121get_num(cp)
2122	register char *cp;
2123{
2124	register int res = 0;
2125
2126	while (*cp) {
2127		if (*cp < '0' || *cp > '9')
2128			return (-1);
2129		res = res * 10 + (*cp++ - '0');
2130	}
2131	return (res);
2132}
2133