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