xref: /freebsd/usr.sbin/mountd/mountd.c (revision 2ad872c5794e4c26fdf6ed219ad3f09ca0d5304a)
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 	"$Id: mountd.c,v 1.33 1998/08/02 16:06:34 bde Exp $";
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) == 0 ||
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, dirpath)) {
506 			syslog(LOG_NOTICE, "undecodable umount request from %s",
507 			    inet_ntoa(saddrin));
508 			svcerr_decode(transp);
509 			return;
510 		}
511 		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
512 			syslog(LOG_ERR, "can't send reply");
513 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
514 		if (hp)
515 			del_mlist(hp->h_name, dirpath);
516 		del_mlist(inet_ntoa(saddrin), dirpath);
517 		if (log)
518 			syslog(LOG_NOTICE,
519 			    "umount request succeeded from %s for %s",
520 			    inet_ntoa(saddrin), dirpath);
521 		return;
522 	case RPCMNT_UMNTALL:
523 		if (sport >= IPPORT_RESERVED && resvport_only) {
524 			syslog(LOG_NOTICE,
525 			    "umountall request from %s from unprivileged port",
526 			    inet_ntoa(saddrin));
527 			svcerr_weakauth(transp);
528 			return;
529 		}
530 		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
531 			syslog(LOG_ERR, "can't send reply");
532 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
533 		if (hp)
534 			del_mlist(hp->h_name, (char *)NULL);
535 		del_mlist(inet_ntoa(saddrin), (char *)NULL);
536 		if (log)
537 			syslog(LOG_NOTICE,
538 			    "umountall request succeeded from %s",
539 			    inet_ntoa(saddrin));
540 		return;
541 	case RPCMNT_EXPORT:
542 		if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
543 			syslog(LOG_ERR, "can't send reply");
544 		if (log)
545 			syslog(LOG_NOTICE,
546 			    "export request succeeded from %s",
547 			    inet_ntoa(saddrin));
548 		return;
549 	default:
550 		svcerr_noproc(transp);
551 		return;
552 	}
553 }
554 
555 /*
556  * Xdr conversion for a dirpath string
557  */
558 int
559 xdr_dir(xdrsp, dirp)
560 	XDR *xdrsp;
561 	char *dirp;
562 {
563 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
564 }
565 
566 /*
567  * Xdr routine to generate file handle reply
568  */
569 int
570 xdr_fhs(xdrsp, cp)
571 	XDR *xdrsp;
572 	caddr_t cp;
573 {
574 	register struct fhreturn *fhrp = (struct fhreturn *)cp;
575 	u_long ok = 0, len, auth;
576 
577 	if (!xdr_long(xdrsp, &ok))
578 		return (0);
579 	switch (fhrp->fhr_vers) {
580 	case 1:
581 		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
582 	case 3:
583 		len = NFSX_V3FH;
584 		if (!xdr_long(xdrsp, &len))
585 			return (0);
586 		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
587 			return (0);
588 		if (fhrp->fhr_flag & DP_KERB)
589 			auth = RPCAUTH_KERB4;
590 		else
591 			auth = RPCAUTH_UNIX;
592 		len = 1;
593 		if (!xdr_long(xdrsp, &len))
594 			return (0);
595 		return (xdr_long(xdrsp, &auth));
596 	};
597 	return (0);
598 }
599 
600 int
601 xdr_mlist(xdrsp, cp)
602 	XDR *xdrsp;
603 	caddr_t cp;
604 {
605 	struct mountlist *mlp;
606 	int true = 1;
607 	int false = 0;
608 	char *strp;
609 
610 	mlp = mlhead;
611 	while (mlp) {
612 		if (!xdr_bool(xdrsp, &true))
613 			return (0);
614 		strp = &mlp->ml_host[0];
615 		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
616 			return (0);
617 		strp = &mlp->ml_dirp[0];
618 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
619 			return (0);
620 		mlp = mlp->ml_next;
621 	}
622 	if (!xdr_bool(xdrsp, &false))
623 		return (0);
624 	return (1);
625 }
626 
627 /*
628  * Xdr conversion for export list
629  */
630 int
631 xdr_explist(xdrsp, cp)
632 	XDR *xdrsp;
633 	caddr_t cp;
634 {
635 	struct exportlist *ep;
636 	int false = 0;
637 	int putdef;
638 	sigset_t sighup_mask;
639 
640 	sigemptyset(&sighup_mask);
641 	sigaddset(&sighup_mask, SIGHUP);
642 	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
643 	ep = exphead;
644 	while (ep) {
645 		putdef = 0;
646 		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
647 			goto errout;
648 		if (ep->ex_defdir && putdef == 0 &&
649 			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
650 			&putdef))
651 			goto errout;
652 		ep = ep->ex_next;
653 	}
654 	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
655 	if (!xdr_bool(xdrsp, &false))
656 		return (0);
657 	return (1);
658 errout:
659 	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
660 	return (0);
661 }
662 
663 /*
664  * Called from xdr_explist() to traverse the tree and export the
665  * directory paths.
666  */
667 int
668 put_exlist(dp, xdrsp, adp, putdefp)
669 	struct dirlist *dp;
670 	XDR *xdrsp;
671 	struct dirlist *adp;
672 	int *putdefp;
673 {
674 	struct grouplist *grp;
675 	struct hostlist *hp;
676 	int true = 1;
677 	int false = 0;
678 	int gotalldir = 0;
679 	char *strp;
680 
681 	if (dp) {
682 		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
683 			return (1);
684 		if (!xdr_bool(xdrsp, &true))
685 			return (1);
686 		strp = dp->dp_dirp;
687 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
688 			return (1);
689 		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
690 			gotalldir = 1;
691 			*putdefp = 1;
692 		}
693 		if ((dp->dp_flag & DP_DEFSET) == 0 &&
694 		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
695 			hp = dp->dp_hosts;
696 			while (hp) {
697 				grp = hp->ht_grp;
698 				if (grp->gr_type == GT_HOST) {
699 					if (!xdr_bool(xdrsp, &true))
700 						return (1);
701 					strp = grp->gr_ptr.gt_hostent->h_name;
702 					if (!xdr_string(xdrsp, &strp,
703 					    RPCMNT_NAMELEN))
704 						return (1);
705 				} else if (grp->gr_type == GT_NET) {
706 					if (!xdr_bool(xdrsp, &true))
707 						return (1);
708 					strp = grp->gr_ptr.gt_net.nt_name;
709 					if (!xdr_string(xdrsp, &strp,
710 					    RPCMNT_NAMELEN))
711 						return (1);
712 				}
713 				hp = hp->ht_next;
714 				if (gotalldir && hp == (struct hostlist *)NULL) {
715 					hp = adp->dp_hosts;
716 					gotalldir = 0;
717 				}
718 			}
719 		}
720 		if (!xdr_bool(xdrsp, &false))
721 			return (1);
722 		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
723 			return (1);
724 	}
725 	return (0);
726 }
727 
728 #define LINESIZ	10240
729 char line[LINESIZ];
730 FILE *exp_file;
731 
732 /*
733  * Get the export list
734  */
735 void
736 get_exportlist()
737 {
738 	struct exportlist *ep, *ep2;
739 	struct grouplist *grp, *tgrp;
740 	struct exportlist **epp;
741 	struct dirlist *dirhead;
742 	struct statfs fsb, *fsp;
743 	struct hostent *hpe;
744 	struct ucred anon;
745 	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
746 	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
747 
748 	/*
749 	 * First, get rid of the old list
750 	 */
751 	ep = exphead;
752 	while (ep) {
753 		ep2 = ep;
754 		ep = ep->ex_next;
755 		free_exp(ep2);
756 	}
757 	exphead = (struct exportlist *)NULL;
758 
759 	grp = grphead;
760 	while (grp) {
761 		tgrp = grp;
762 		grp = grp->gr_next;
763 		free_grp(tgrp);
764 	}
765 	grphead = (struct grouplist *)NULL;
766 
767 	/*
768 	 * And delete exports that are in the kernel for all local
769 	 * file systems.
770 	 * XXX: Should know how to handle all local exportable file systems
771 	 *      instead of just "ufs".
772 	 */
773 	num = getmntinfo(&fsp, MNT_NOWAIT);
774 	for (i = 0; i < num; i++) {
775 		union {
776 			struct ufs_args ua;
777 			struct iso_args ia;
778 			struct mfs_args ma;
779 			struct msdosfs_args da;
780 		} targs;
781 
782 		if (!strcmp(fsp->f_fstypename, "mfs") ||
783 		    !strcmp(fsp->f_fstypename, "ufs") ||
784 		    !strcmp(fsp->f_fstypename, "msdos") ||
785 		    !strcmp(fsp->f_fstypename, "cd9660")) {
786 			targs.ua.fspec = NULL;
787 			targs.ua.export.ex_flags = MNT_DELEXPORT;
788 			if (mount(fsp->f_fstypename, fsp->f_mntonname,
789 				  fsp->f_flags | MNT_UPDATE,
790 				  (caddr_t)&targs) < 0)
791 				syslog(LOG_ERR, "can't delete exports for %s",
792 				       fsp->f_mntonname);
793 		}
794 		fsp++;
795 	}
796 
797 	/*
798 	 * Read in the exports file and build the list, calling
799 	 * mount() as we go along to push the export rules into the kernel.
800 	 */
801 	if ((exp_file = fopen(exname, "r")) == NULL) {
802 		syslog(LOG_ERR, "can't open %s", exname);
803 		exit(2);
804 	}
805 	dirhead = (struct dirlist *)NULL;
806 	while (get_line()) {
807 		if (debug)
808 			warnx("got line %s", line);
809 		cp = line;
810 		nextfield(&cp, &endcp);
811 		if (*cp == '#')
812 			goto nextline;
813 
814 		/*
815 		 * Set defaults.
816 		 */
817 		has_host = FALSE;
818 		anon = def_anon;
819 		exflags = MNT_EXPORTED;
820 		got_nondir = 0;
821 		opt_flags = 0;
822 		ep = (struct exportlist *)NULL;
823 
824 		/*
825 		 * Create new exports list entry
826 		 */
827 		len = endcp-cp;
828 		tgrp = grp = get_grp();
829 		while (len > 0) {
830 			if (len > RPCMNT_NAMELEN) {
831 			    getexp_err(ep, tgrp);
832 			    goto nextline;
833 			}
834 			if (*cp == '-') {
835 			    if (ep == (struct exportlist *)NULL) {
836 				getexp_err(ep, tgrp);
837 				goto nextline;
838 			    }
839 			    if (debug)
840 				warnx("doing opt %s", cp);
841 			    got_nondir = 1;
842 			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
843 				&exflags, &anon)) {
844 				getexp_err(ep, tgrp);
845 				goto nextline;
846 			    }
847 			} else if (*cp == '/') {
848 			    savedc = *endcp;
849 			    *endcp = '\0';
850 			    if (check_dirpath(cp) &&
851 				statfs(cp, &fsb) >= 0) {
852 				if (got_nondir) {
853 				    syslog(LOG_ERR, "dirs must be first");
854 				    getexp_err(ep, tgrp);
855 				    goto nextline;
856 				}
857 				if (ep) {
858 				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
859 					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
860 					getexp_err(ep, tgrp);
861 					goto nextline;
862 				    }
863 				} else {
864 				    /*
865 				     * See if this directory is already
866 				     * in the list.
867 				     */
868 				    ep = ex_search(&fsb.f_fsid);
869 				    if (ep == (struct exportlist *)NULL) {
870 					ep = get_exp();
871 					ep->ex_fs = fsb.f_fsid;
872 					ep->ex_fsdir = (char *)
873 					    malloc(strlen(fsb.f_mntonname) + 1);
874 					if (ep->ex_fsdir)
875 					    strcpy(ep->ex_fsdir,
876 						fsb.f_mntonname);
877 					else
878 					    out_of_mem();
879 					if (debug)
880 					  warnx("making new ep fs=0x%x,0x%x",
881 					      fsb.f_fsid.val[0],
882 					      fsb.f_fsid.val[1]);
883 				    } else if (debug)
884 					warnx("found ep fs=0x%x,0x%x",
885 					    fsb.f_fsid.val[0],
886 					    fsb.f_fsid.val[1]);
887 				}
888 
889 				/*
890 				 * Add dirpath to export mount point.
891 				 */
892 				dirp = add_expdir(&dirhead, cp, len);
893 				dirplen = len;
894 			    } else {
895 				getexp_err(ep, tgrp);
896 				goto nextline;
897 			    }
898 			    *endcp = savedc;
899 			} else {
900 			    savedc = *endcp;
901 			    *endcp = '\0';
902 			    got_nondir = 1;
903 			    if (ep == (struct exportlist *)NULL) {
904 				getexp_err(ep, tgrp);
905 				goto nextline;
906 			    }
907 
908 			    /*
909 			     * Get the host or netgroup.
910 			     */
911 			    setnetgrent(cp);
912 			    netgrp = getnetgrent(&hst, &usr, &dom);
913 			    do {
914 				if (has_host) {
915 				    grp->gr_next = get_grp();
916 				    grp = grp->gr_next;
917 				}
918 				if (netgrp) {
919 				    if (hst == 0) {
920 					syslog(LOG_ERR,
921 				"null hostname in netgroup %s, skipping", cp);
922 					grp->gr_type = GT_IGNORE;
923 				    } else if (get_host(hst, grp, tgrp)) {
924 					syslog(LOG_ERR,
925 			"bad host %s in netgroup %s, skipping", hst, cp);
926 					grp->gr_type = GT_IGNORE;
927 				    }
928 				} else if (get_host(cp, grp, tgrp)) {
929 				    syslog(LOG_ERR, "bad host %s, skipping", cp);
930 				    grp->gr_type = GT_IGNORE;
931 				}
932 				has_host = TRUE;
933 			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
934 			    endnetgrent();
935 			    *endcp = savedc;
936 			}
937 			cp = endcp;
938 			nextfield(&cp, &endcp);
939 			len = endcp - cp;
940 		}
941 		if (check_options(dirhead)) {
942 			getexp_err(ep, tgrp);
943 			goto nextline;
944 		}
945 		if (!has_host) {
946 			grp->gr_type = GT_HOST;
947 			if (debug)
948 				warnx("adding a default entry");
949 			/* add a default group and make the grp list NULL */
950 			hpe = (struct hostent *)malloc(sizeof(struct hostent));
951 			if (hpe == (struct hostent *)NULL)
952 				out_of_mem();
953 			hpe->h_name = strdup("Default");
954 			hpe->h_addrtype = AF_INET;
955 			hpe->h_length = sizeof (u_int32_t);
956 			hpe->h_addr_list = (char **)NULL;
957 			grp->gr_ptr.gt_hostent = hpe;
958 
959 		/*
960 		 * Don't allow a network export coincide with a list of
961 		 * host(s) on the same line.
962 		 */
963 		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
964 			getexp_err(ep, tgrp);
965 			goto nextline;
966 
967         	/*
968 	         * If an export list was specified on this line, make sure
969 		 * that we have at least one valid entry, otherwise skip it.
970 		 */
971 		} else {
972 			grp = tgrp;
973         		while (grp && grp->gr_type == GT_IGNORE)
974 				grp = grp->gr_next;
975 			if (! grp) {
976 			    getexp_err(ep, tgrp);
977 			    goto nextline;
978 			}
979 		}
980 
981 		/*
982 		 * Loop through hosts, pushing the exports into the kernel.
983 		 * After loop, tgrp points to the start of the list and
984 		 * grp points to the last entry in the list.
985 		 */
986 		grp = tgrp;
987 		do {
988 		    if (do_mount(ep, grp, exflags, &anon, dirp,
989 			dirplen, &fsb)) {
990 			getexp_err(ep, tgrp);
991 			goto nextline;
992 		    }
993 		} while (grp->gr_next && (grp = grp->gr_next));
994 
995 		/*
996 		 * Success. Update the data structures.
997 		 */
998 		if (has_host) {
999 			hang_dirp(dirhead, tgrp, ep, opt_flags);
1000 			grp->gr_next = grphead;
1001 			grphead = tgrp;
1002 		} else {
1003 			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1004 				opt_flags);
1005 			free_grp(grp);
1006 		}
1007 		dirhead = (struct dirlist *)NULL;
1008 		if ((ep->ex_flag & EX_LINKED) == 0) {
1009 			ep2 = exphead;
1010 			epp = &exphead;
1011 
1012 			/*
1013 			 * Insert in the list in alphabetical order.
1014 			 */
1015 			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1016 				epp = &ep2->ex_next;
1017 				ep2 = ep2->ex_next;
1018 			}
1019 			if (ep2)
1020 				ep->ex_next = ep2;
1021 			*epp = ep;
1022 			ep->ex_flag |= EX_LINKED;
1023 		}
1024 nextline:
1025 		if (dirhead) {
1026 			free_dir(dirhead);
1027 			dirhead = (struct dirlist *)NULL;
1028 		}
1029 	}
1030 	fclose(exp_file);
1031 }
1032 
1033 /*
1034  * Allocate an export list element
1035  */
1036 struct exportlist *
1037 get_exp()
1038 {
1039 	struct exportlist *ep;
1040 
1041 	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1042 	if (ep == (struct exportlist *)NULL)
1043 		out_of_mem();
1044 	memset(ep, 0, sizeof(struct exportlist));
1045 	return (ep);
1046 }
1047 
1048 /*
1049  * Allocate a group list element
1050  */
1051 struct grouplist *
1052 get_grp()
1053 {
1054 	struct grouplist *gp;
1055 
1056 	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1057 	if (gp == (struct grouplist *)NULL)
1058 		out_of_mem();
1059 	memset(gp, 0, sizeof(struct grouplist));
1060 	return (gp);
1061 }
1062 
1063 /*
1064  * Clean up upon an error in get_exportlist().
1065  */
1066 void
1067 getexp_err(ep, grp)
1068 	struct exportlist *ep;
1069 	struct grouplist *grp;
1070 {
1071 	struct grouplist *tgrp;
1072 
1073 	syslog(LOG_ERR, "bad exports list line %s", line);
1074 	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1075 		free_exp(ep);
1076 	while (grp) {
1077 		tgrp = grp;
1078 		grp = grp->gr_next;
1079 		free_grp(tgrp);
1080 	}
1081 }
1082 
1083 /*
1084  * Search the export list for a matching fs.
1085  */
1086 struct exportlist *
1087 ex_search(fsid)
1088 	fsid_t *fsid;
1089 {
1090 	struct exportlist *ep;
1091 
1092 	ep = exphead;
1093 	while (ep) {
1094 		if (ep->ex_fs.val[0] == fsid->val[0] &&
1095 		    ep->ex_fs.val[1] == fsid->val[1])
1096 			return (ep);
1097 		ep = ep->ex_next;
1098 	}
1099 	return (ep);
1100 }
1101 
1102 /*
1103  * Add a directory path to the list.
1104  */
1105 char *
1106 add_expdir(dpp, cp, len)
1107 	struct dirlist **dpp;
1108 	char *cp;
1109 	int len;
1110 {
1111 	struct dirlist *dp;
1112 
1113 	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1114 	if (dp == (struct dirlist *)NULL)
1115 		out_of_mem();
1116 	dp->dp_left = *dpp;
1117 	dp->dp_right = (struct dirlist *)NULL;
1118 	dp->dp_flag = 0;
1119 	dp->dp_hosts = (struct hostlist *)NULL;
1120 	strcpy(dp->dp_dirp, cp);
1121 	*dpp = dp;
1122 	return (dp->dp_dirp);
1123 }
1124 
1125 /*
1126  * Hang the dir list element off the dirpath binary tree as required
1127  * and update the entry for host.
1128  */
1129 void
1130 hang_dirp(dp, grp, ep, flags)
1131 	struct dirlist *dp;
1132 	struct grouplist *grp;
1133 	struct exportlist *ep;
1134 	int flags;
1135 {
1136 	struct hostlist *hp;
1137 	struct dirlist *dp2;
1138 
1139 	if (flags & OP_ALLDIRS) {
1140 		if (ep->ex_defdir)
1141 			free((caddr_t)dp);
1142 		else
1143 			ep->ex_defdir = dp;
1144 		if (grp == (struct grouplist *)NULL) {
1145 			ep->ex_defdir->dp_flag |= DP_DEFSET;
1146 			if (flags & OP_KERB)
1147 				ep->ex_defdir->dp_flag |= DP_KERB;
1148 		} else while (grp) {
1149 			hp = get_ht();
1150 			if (flags & OP_KERB)
1151 				hp->ht_flag |= DP_KERB;
1152 			hp->ht_grp = grp;
1153 			hp->ht_next = ep->ex_defdir->dp_hosts;
1154 			ep->ex_defdir->dp_hosts = hp;
1155 			grp = grp->gr_next;
1156 		}
1157 	} else {
1158 
1159 		/*
1160 		 * Loop through the directories adding them to the tree.
1161 		 */
1162 		while (dp) {
1163 			dp2 = dp->dp_left;
1164 			add_dlist(&ep->ex_dirl, dp, grp, flags);
1165 			dp = dp2;
1166 		}
1167 	}
1168 }
1169 
1170 /*
1171  * Traverse the binary tree either updating a node that is already there
1172  * for the new directory or adding the new node.
1173  */
1174 void
1175 add_dlist(dpp, newdp, grp, flags)
1176 	struct dirlist **dpp;
1177 	struct dirlist *newdp;
1178 	struct grouplist *grp;
1179 	int flags;
1180 {
1181 	struct dirlist *dp;
1182 	struct hostlist *hp;
1183 	int cmp;
1184 
1185 	dp = *dpp;
1186 	if (dp) {
1187 		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1188 		if (cmp > 0) {
1189 			add_dlist(&dp->dp_left, newdp, grp, flags);
1190 			return;
1191 		} else if (cmp < 0) {
1192 			add_dlist(&dp->dp_right, newdp, grp, flags);
1193 			return;
1194 		} else
1195 			free((caddr_t)newdp);
1196 	} else {
1197 		dp = newdp;
1198 		dp->dp_left = (struct dirlist *)NULL;
1199 		*dpp = dp;
1200 	}
1201 	if (grp) {
1202 
1203 		/*
1204 		 * Hang all of the host(s) off of the directory point.
1205 		 */
1206 		do {
1207 			hp = get_ht();
1208 			if (flags & OP_KERB)
1209 				hp->ht_flag |= DP_KERB;
1210 			hp->ht_grp = grp;
1211 			hp->ht_next = dp->dp_hosts;
1212 			dp->dp_hosts = hp;
1213 			grp = grp->gr_next;
1214 		} while (grp);
1215 	} else {
1216 		dp->dp_flag |= DP_DEFSET;
1217 		if (flags & OP_KERB)
1218 			dp->dp_flag |= DP_KERB;
1219 	}
1220 }
1221 
1222 /*
1223  * Search for a dirpath on the export point.
1224  */
1225 struct dirlist *
1226 dirp_search(dp, dirpath)
1227 	struct dirlist *dp;
1228 	char *dirpath;
1229 {
1230 	int cmp;
1231 
1232 	if (dp) {
1233 		cmp = strcmp(dp->dp_dirp, dirpath);
1234 		if (cmp > 0)
1235 			return (dirp_search(dp->dp_left, dirpath));
1236 		else if (cmp < 0)
1237 			return (dirp_search(dp->dp_right, dirpath));
1238 		else
1239 			return (dp);
1240 	}
1241 	return (dp);
1242 }
1243 
1244 /*
1245  * Scan for a host match in a directory tree.
1246  */
1247 int
1248 chk_host(dp, saddr, defsetp, hostsetp)
1249 	struct dirlist *dp;
1250 	u_int32_t saddr;
1251 	int *defsetp;
1252 	int *hostsetp;
1253 {
1254 	struct hostlist *hp;
1255 	struct grouplist *grp;
1256 	u_int32_t **addrp;
1257 
1258 	if (dp) {
1259 		if (dp->dp_flag & DP_DEFSET)
1260 			*defsetp = dp->dp_flag;
1261 		hp = dp->dp_hosts;
1262 		while (hp) {
1263 			grp = hp->ht_grp;
1264 			switch (grp->gr_type) {
1265 			case GT_HOST:
1266 			    addrp = (u_int32_t **)
1267 				grp->gr_ptr.gt_hostent->h_addr_list;
1268 			    while (*addrp) {
1269 				if (**addrp == saddr) {
1270 				    *hostsetp = (hp->ht_flag | DP_HOSTSET);
1271 				    return (1);
1272 				}
1273 				addrp++;
1274 			    }
1275 			    break;
1276 			case GT_NET:
1277 			    if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
1278 				grp->gr_ptr.gt_net.nt_net) {
1279 				*hostsetp = (hp->ht_flag | DP_HOSTSET);
1280 				return (1);
1281 			    }
1282 			    break;
1283 			};
1284 			hp = hp->ht_next;
1285 		}
1286 	}
1287 	return (0);
1288 }
1289 
1290 /*
1291  * Scan tree for a host that matches the address.
1292  */
1293 int
1294 scan_tree(dp, saddr)
1295 	struct dirlist *dp;
1296 	u_int32_t saddr;
1297 {
1298 	int defset, hostset;
1299 
1300 	if (dp) {
1301 		if (scan_tree(dp->dp_left, saddr))
1302 			return (1);
1303 		if (chk_host(dp, saddr, &defset, &hostset))
1304 			return (1);
1305 		if (scan_tree(dp->dp_right, saddr))
1306 			return (1);
1307 	}
1308 	return (0);
1309 }
1310 
1311 /*
1312  * Traverse the dirlist tree and free it up.
1313  */
1314 void
1315 free_dir(dp)
1316 	struct dirlist *dp;
1317 {
1318 
1319 	if (dp) {
1320 		free_dir(dp->dp_left);
1321 		free_dir(dp->dp_right);
1322 		free_host(dp->dp_hosts);
1323 		free((caddr_t)dp);
1324 	}
1325 }
1326 
1327 /*
1328  * Parse the option string and update fields.
1329  * Option arguments may either be -<option>=<value> or
1330  * -<option> <value>
1331  */
1332 int
1333 do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1334 	char **cpp, **endcpp;
1335 	struct exportlist *ep;
1336 	struct grouplist *grp;
1337 	int *has_hostp;
1338 	int *exflagsp;
1339 	struct ucred *cr;
1340 {
1341 	char *cpoptarg, *cpoptend;
1342 	char *cp, *endcp, *cpopt, savedc, savedc2;
1343 	int allflag, usedarg;
1344 
1345 	cpopt = *cpp;
1346 	cpopt++;
1347 	cp = *endcpp;
1348 	savedc = *cp;
1349 	*cp = '\0';
1350 	while (cpopt && *cpopt) {
1351 		allflag = 1;
1352 		usedarg = -2;
1353 		if ((cpoptend = strchr(cpopt, ','))) {
1354 			*cpoptend++ = '\0';
1355 			if ((cpoptarg = strchr(cpopt, '=')))
1356 				*cpoptarg++ = '\0';
1357 		} else {
1358 			if ((cpoptarg = strchr(cpopt, '=')))
1359 				*cpoptarg++ = '\0';
1360 			else {
1361 				*cp = savedc;
1362 				nextfield(&cp, &endcp);
1363 				**endcpp = '\0';
1364 				if (endcp > cp && *cp != '-') {
1365 					cpoptarg = cp;
1366 					savedc2 = *endcp;
1367 					*endcp = '\0';
1368 					usedarg = 0;
1369 				}
1370 			}
1371 		}
1372 		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1373 			*exflagsp |= MNT_EXRDONLY;
1374 		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1375 		    !(allflag = strcmp(cpopt, "mapall")) ||
1376 		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1377 			usedarg++;
1378 			parsecred(cpoptarg, cr);
1379 			if (allflag == 0) {
1380 				*exflagsp |= MNT_EXPORTANON;
1381 				opt_flags |= OP_MAPALL;
1382 			} else
1383 				opt_flags |= OP_MAPROOT;
1384 		} else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1385 			*exflagsp |= MNT_EXKERB;
1386 			opt_flags |= OP_KERB;
1387 		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1388 			!strcmp(cpopt, "m"))) {
1389 			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1390 				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1391 				return (1);
1392 			}
1393 			usedarg++;
1394 			opt_flags |= OP_MASK;
1395 		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
1396 			!strcmp(cpopt, "n"))) {
1397 			if (grp->gr_type != GT_NULL) {
1398 				syslog(LOG_ERR, "network/host conflict");
1399 				return (1);
1400 			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1401 				syslog(LOG_ERR, "bad net: %s", cpoptarg);
1402 				return (1);
1403 			}
1404 			grp->gr_type = GT_NET;
1405 			*has_hostp = 1;
1406 			usedarg++;
1407 			opt_flags |= OP_NET;
1408 		} else if (!strcmp(cpopt, "alldirs")) {
1409 			opt_flags |= OP_ALLDIRS;
1410 		} else if (!strcmp(cpopt, "public")) {
1411 			*exflagsp |= MNT_EXPUBLIC;
1412 		} else if (!strcmp(cpopt, "webnfs")) {
1413 			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1414 			opt_flags |= OP_MAPALL;
1415 		} else if (cpoptarg && !strcmp(cpopt, "index")) {
1416 			ep->ex_indexfile = strdup(cpoptarg);
1417 #ifdef ISO
1418 		} else if (cpoptarg && !strcmp(cpopt, "iso")) {
1419 			if (get_isoaddr(cpoptarg, grp)) {
1420 				syslog(LOG_ERR, "bad iso addr: %s", cpoptarg);
1421 				return (1);
1422 			}
1423 			*has_hostp = 1;
1424 			usedarg++;
1425 			opt_flags |= OP_ISO;
1426 #endif /* ISO */
1427 		} else {
1428 			syslog(LOG_ERR, "bad opt %s", cpopt);
1429 			return (1);
1430 		}
1431 		if (usedarg >= 0) {
1432 			*endcp = savedc2;
1433 			**endcpp = savedc;
1434 			if (usedarg > 0) {
1435 				*cpp = cp;
1436 				*endcpp = endcp;
1437 			}
1438 			return (0);
1439 		}
1440 		cpopt = cpoptend;
1441 	}
1442 	**endcpp = savedc;
1443 	return (0);
1444 }
1445 
1446 /*
1447  * Translate a character string to the corresponding list of network
1448  * addresses for a hostname.
1449  */
1450 int
1451 get_host(cp, grp, tgrp)
1452 	char *cp;
1453 	struct grouplist *grp;
1454 	struct grouplist *tgrp;
1455 {
1456 	struct grouplist *checkgrp;
1457 	struct hostent *hp, *nhp;
1458 	char **addrp, **naddrp;
1459 	struct hostent t_host;
1460 	int i;
1461 	u_int32_t saddr;
1462 	char *aptr[2];
1463 
1464 	if (grp->gr_type != GT_NULL)
1465 		return (1);
1466 	if ((hp = gethostbyname(cp)) == NULL) {
1467 		if (isdigit(*cp)) {
1468 			saddr = inet_addr(cp);
1469 			if (saddr == -1) {
1470  				syslog(LOG_ERR, "inet_addr failed for %s", cp);
1471 				return (1);
1472 			}
1473 			if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
1474 				AF_INET)) == NULL) {
1475 				hp = &t_host;
1476 				hp->h_name = cp;
1477 				hp->h_addrtype = AF_INET;
1478 				hp->h_length = sizeof (u_int32_t);
1479 				hp->h_addr_list = aptr;
1480 				aptr[0] = (char *)&saddr;
1481 				aptr[1] = (char *)NULL;
1482 			}
1483 		} else {
1484  			syslog(LOG_ERR, "gethostbyname failed for %s", cp);
1485 			return (1);
1486 		}
1487 	}
1488         /*
1489          * Sanity check: make sure we don't already have an entry
1490          * for this host in the grouplist.
1491          */
1492         checkgrp = tgrp;
1493         while (checkgrp != NULL) {
1494 		if (checkgrp->gr_type == GT_HOST &&
1495                     checkgrp->gr_ptr.gt_hostent != NULL &&
1496                     (!strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)
1497 		|| *(u_int32_t *)checkgrp->gr_ptr.gt_hostent->h_addr ==
1498 			*(u_int32_t *)hp->h_addr)) {
1499                         grp->gr_type = GT_IGNORE;
1500 			return(0);
1501 		}
1502                 checkgrp = checkgrp->gr_next;
1503         }
1504 
1505 	grp->gr_type = GT_HOST;
1506 	nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
1507 		malloc(sizeof(struct hostent));
1508 	if (nhp == (struct hostent *)NULL)
1509 		out_of_mem();
1510 	memmove(nhp, hp, sizeof(struct hostent));
1511 	i = strlen(hp->h_name)+1;
1512 	nhp->h_name = (char *)malloc(i);
1513 	if (nhp->h_name == (char *)NULL)
1514 		out_of_mem();
1515 	memmove(nhp->h_name, hp->h_name, i);
1516 	addrp = hp->h_addr_list;
1517 	i = 1;
1518 	while (*addrp++)
1519 		i++;
1520 	naddrp = nhp->h_addr_list = (char **)malloc(i*sizeof(char *));
1521 	if (naddrp == (char **)NULL)
1522 		out_of_mem();
1523 	addrp = hp->h_addr_list;
1524 	while (*addrp) {
1525 		*naddrp = (char *)malloc(hp->h_length);
1526 		if (*naddrp == (char *)NULL)
1527 		    out_of_mem();
1528 		memmove(*naddrp, *addrp, hp->h_length);
1529 		addrp++;
1530 		naddrp++;
1531 	}
1532 	*naddrp = (char *)NULL;
1533 	if (debug)
1534 		warnx("got host %s", hp->h_name);
1535 	return (0);
1536 }
1537 
1538 /*
1539  * Free up an exports list component
1540  */
1541 void
1542 free_exp(ep)
1543 	struct exportlist *ep;
1544 {
1545 
1546 	if (ep->ex_defdir) {
1547 		free_host(ep->ex_defdir->dp_hosts);
1548 		free((caddr_t)ep->ex_defdir);
1549 	}
1550 	if (ep->ex_fsdir)
1551 		free(ep->ex_fsdir);
1552 	if (ep->ex_indexfile)
1553 		free(ep->ex_indexfile);
1554 	free_dir(ep->ex_dirl);
1555 	free((caddr_t)ep);
1556 }
1557 
1558 /*
1559  * Free hosts.
1560  */
1561 void
1562 free_host(hp)
1563 	struct hostlist *hp;
1564 {
1565 	struct hostlist *hp2;
1566 
1567 	while (hp) {
1568 		hp2 = hp;
1569 		hp = hp->ht_next;
1570 		free((caddr_t)hp2);
1571 	}
1572 }
1573 
1574 struct hostlist *
1575 get_ht()
1576 {
1577 	struct hostlist *hp;
1578 
1579 	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1580 	if (hp == (struct hostlist *)NULL)
1581 		out_of_mem();
1582 	hp->ht_next = (struct hostlist *)NULL;
1583 	hp->ht_flag = 0;
1584 	return (hp);
1585 }
1586 
1587 #ifdef ISO
1588 /*
1589  * Translate an iso address.
1590  */
1591 get_isoaddr(cp, grp)
1592 	char *cp;
1593 	struct grouplist *grp;
1594 {
1595 	struct iso_addr *isop;
1596 	struct sockaddr_iso *isoaddr;
1597 
1598 	if (grp->gr_type != GT_NULL)
1599 		return (1);
1600 	if ((isop = iso_addr(cp)) == NULL) {
1601 		syslog(LOG_ERR, "iso_addr failed, ignored");
1602 		return (1);
1603 	}
1604 	isoaddr = (struct sockaddr_iso *)malloc(sizeof (struct sockaddr_iso));
1605 	if (isoaddr == (struct sockaddr_iso *)NULL)
1606 		out_of_mem();
1607 	memset(isoaddr, 0, sizeof(struct sockaddr_iso));
1608 	memmove(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
1609 	isoaddr->siso_len = sizeof(struct sockaddr_iso);
1610 	isoaddr->siso_family = AF_ISO;
1611 	grp->gr_type = GT_ISO;
1612 	grp->gr_ptr.gt_isoaddr = isoaddr;
1613 	return (0);
1614 }
1615 #endif	/* ISO */
1616 
1617 /*
1618  * Out of memory, fatal
1619  */
1620 void
1621 out_of_mem()
1622 {
1623 
1624 	syslog(LOG_ERR, "out of memory");
1625 	exit(2);
1626 }
1627 
1628 /*
1629  * Do the mount syscall with the update flag to push the export info into
1630  * the kernel.
1631  */
1632 int
1633 do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1634 	struct exportlist *ep;
1635 	struct grouplist *grp;
1636 	int exflags;
1637 	struct ucred *anoncrp;
1638 	char *dirp;
1639 	int dirplen;
1640 	struct statfs *fsb;
1641 {
1642 	char *cp = (char *)NULL;
1643 	u_int32_t **addrp;
1644 	int done;
1645 	char savedc = '\0';
1646 	struct sockaddr_in sin, imask;
1647 	union {
1648 		struct ufs_args ua;
1649 		struct iso_args ia;
1650 		struct mfs_args ma;
1651 #ifdef __NetBSD__
1652 		struct msdosfs_args da;
1653 #endif
1654 	} args;
1655 	u_int32_t net;
1656 
1657 	args.ua.fspec = 0;
1658 	args.ua.export.ex_flags = exflags;
1659 	args.ua.export.ex_anon = *anoncrp;
1660 	args.ua.export.ex_indexfile = ep->ex_indexfile;
1661 	memset(&sin, 0, sizeof(sin));
1662 	memset(&imask, 0, sizeof(imask));
1663 	sin.sin_family = AF_INET;
1664 	sin.sin_len = sizeof(sin);
1665 	imask.sin_family = AF_INET;
1666 	imask.sin_len = sizeof(sin);
1667 	if (grp->gr_type == GT_HOST)
1668 		addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list;
1669 	else
1670 		addrp = (u_int32_t **)NULL;
1671 	done = FALSE;
1672 	while (!done) {
1673 		switch (grp->gr_type) {
1674 		case GT_HOST:
1675 			if (addrp) {
1676 				sin.sin_addr.s_addr = **addrp;
1677 				args.ua.export.ex_addrlen = sizeof(sin);
1678 			} else
1679 				args.ua.export.ex_addrlen = 0;
1680 			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1681 			args.ua.export.ex_masklen = 0;
1682 			break;
1683 		case GT_NET:
1684 			if (grp->gr_ptr.gt_net.nt_mask)
1685 			    imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
1686 			else {
1687 			    net = ntohl(grp->gr_ptr.gt_net.nt_net);
1688 			    if (IN_CLASSA(net))
1689 				imask.sin_addr.s_addr = inet_addr("255.0.0.0");
1690 			    else if (IN_CLASSB(net))
1691 				imask.sin_addr.s_addr =
1692 				    inet_addr("255.255.0.0");
1693 			    else
1694 				imask.sin_addr.s_addr =
1695 				    inet_addr("255.255.255.0");
1696 			    grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
1697 			}
1698 			sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
1699 			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1700 			args.ua.export.ex_addrlen = sizeof (sin);
1701 			args.ua.export.ex_mask = (struct sockaddr *)&imask;
1702 			args.ua.export.ex_masklen = sizeof (imask);
1703 			break;
1704 #ifdef ISO
1705 		case GT_ISO:
1706 			args.ua.export.ex_addr =
1707 				(struct sockaddr *)grp->gr_ptr.gt_isoaddr;
1708 			args.ua.export.ex_addrlen =
1709 				sizeof(struct sockaddr_iso);
1710 			args.ua.export.ex_masklen = 0;
1711 			break;
1712 #endif	/* ISO */
1713 		case GT_IGNORE:
1714 			return(0);
1715 			break;
1716 		default:
1717 			syslog(LOG_ERR, "bad grouptype");
1718 			if (cp)
1719 				*cp = savedc;
1720 			return (1);
1721 		};
1722 
1723 		/*
1724 		 * XXX:
1725 		 * Maybe I should just use the fsb->f_mntonname path instead
1726 		 * of looping back up the dirp to the mount point??
1727 		 * Also, needs to know how to export all types of local
1728 		 * exportable file systems and not just "ufs".
1729 		 */
1730 		while (mount(fsb->f_fstypename, dirp,
1731 		       fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1732 			if (cp)
1733 				*cp-- = savedc;
1734 			else
1735 				cp = dirp + dirplen - 1;
1736 			if (errno == EPERM) {
1737 				syslog(LOG_ERR,
1738 				   "can't change attributes for %s", dirp);
1739 				return (1);
1740 			}
1741 			if (opt_flags & OP_ALLDIRS) {
1742 				syslog(LOG_ERR, "could not remount %s: %m",
1743 					dirp);
1744 				return (1);
1745 			}
1746 			/* back up over the last component */
1747 			while (*cp == '/' && cp > dirp)
1748 				cp--;
1749 			while (*(cp - 1) != '/' && cp > dirp)
1750 				cp--;
1751 			if (cp == dirp) {
1752 				if (debug)
1753 					warnx("mnt unsucc");
1754 				syslog(LOG_ERR, "can't export %s", dirp);
1755 				return (1);
1756 			}
1757 			savedc = *cp;
1758 			*cp = '\0';
1759 		}
1760 		if (addrp) {
1761 			++addrp;
1762 			if (*addrp == (u_int32_t *)NULL)
1763 				done = TRUE;
1764 		} else
1765 			done = TRUE;
1766 	}
1767 	if (cp)
1768 		*cp = savedc;
1769 	return (0);
1770 }
1771 
1772 /*
1773  * Translate a net address.
1774  */
1775 int
1776 get_net(cp, net, maskflg)
1777 	char *cp;
1778 	struct netmsk *net;
1779 	int maskflg;
1780 {
1781 	struct netent *np;
1782 	long netaddr;
1783 	struct in_addr inetaddr, inetaddr2;
1784 	char *name;
1785 
1786 	if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) {
1787 		inetaddr = inet_makeaddr(netaddr, 0);
1788 		/*
1789 		 * Due to arbitrary subnet masks, you don't know how many
1790 		 * bits to shift the address to make it into a network,
1791 		 * however you do know how to make a network address into
1792 		 * a host with host == 0 and then compare them.
1793 		 * (What a pest)
1794 		 */
1795 		if (!maskflg) {
1796 			setnetent(0);
1797 			while ((np = getnetent())) {
1798 				inetaddr2 = inet_makeaddr(np->n_net, 0);
1799 				if (inetaddr2.s_addr == inetaddr.s_addr)
1800 					break;
1801 			}
1802 			endnetent();
1803 		}
1804 	} else if ((np = getnetbyname(cp)) != NULL) {
1805 		inetaddr = inet_makeaddr(np->n_net, 0);
1806 	} else
1807 		return (1);
1808 
1809 	if (maskflg)
1810 		net->nt_mask = inetaddr.s_addr;
1811 	else {
1812 		if (np)
1813 			name = np->n_name;
1814 		else
1815 			name = inet_ntoa(inetaddr);
1816 		net->nt_name = (char *)malloc(strlen(name) + 1);
1817 		if (net->nt_name == (char *)NULL)
1818 			out_of_mem();
1819 		strcpy(net->nt_name, name);
1820 		net->nt_net = inetaddr.s_addr;
1821 	}
1822 	return (0);
1823 }
1824 
1825 /*
1826  * Parse out the next white space separated field
1827  */
1828 void
1829 nextfield(cp, endcp)
1830 	char **cp;
1831 	char **endcp;
1832 {
1833 	char *p;
1834 
1835 	p = *cp;
1836 	while (*p == ' ' || *p == '\t')
1837 		p++;
1838 	if (*p == '\n' || *p == '\0')
1839 		*cp = *endcp = p;
1840 	else {
1841 		*cp = p++;
1842 		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1843 			p++;
1844 		*endcp = p;
1845 	}
1846 }
1847 
1848 /*
1849  * Get an exports file line. Skip over blank lines and handle line
1850  * continuations.
1851  */
1852 int
1853 get_line()
1854 {
1855 	char *p, *cp;
1856 	int len;
1857 	int totlen, cont_line;
1858 
1859 	/*
1860 	 * Loop around ignoring blank lines and getting all continuation lines.
1861 	 */
1862 	p = line;
1863 	totlen = 0;
1864 	do {
1865 		if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
1866 			return (0);
1867 		len = strlen(p);
1868 		cp = p + len - 1;
1869 		cont_line = 0;
1870 		while (cp >= p &&
1871 		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
1872 			if (*cp == '\\')
1873 				cont_line = 1;
1874 			cp--;
1875 			len--;
1876 		}
1877 		*++cp = '\0';
1878 		if (len > 0) {
1879 			totlen += len;
1880 			if (totlen >= LINESIZ) {
1881 				syslog(LOG_ERR, "exports line too long");
1882 				exit(2);
1883 			}
1884 			p = cp;
1885 		}
1886 	} while (totlen == 0 || cont_line);
1887 	return (1);
1888 }
1889 
1890 /*
1891  * Parse a description of a credential.
1892  */
1893 void
1894 parsecred(namelist, cr)
1895 	char *namelist;
1896 	struct ucred *cr;
1897 {
1898 	char *name;
1899 	int cnt;
1900 	char *names;
1901 	struct passwd *pw;
1902 	struct group *gr;
1903 	int ngroups, groups[NGROUPS + 1];
1904 
1905 	/*
1906 	 * Set up the unprivileged user.
1907 	 */
1908 	cr->cr_ref = 1;
1909 	cr->cr_uid = -2;
1910 	cr->cr_groups[0] = -2;
1911 	cr->cr_ngroups = 1;
1912 	/*
1913 	 * Get the user's password table entry.
1914 	 */
1915 	names = strsep(&namelist, " \t\n");
1916 	name = strsep(&names, ":");
1917 	if (isdigit(*name) || *name == '-')
1918 		pw = getpwuid(atoi(name));
1919 	else
1920 		pw = getpwnam(name);
1921 	/*
1922 	 * Credentials specified as those of a user.
1923 	 */
1924 	if (names == NULL) {
1925 		if (pw == NULL) {
1926 			syslog(LOG_ERR, "unknown user: %s", name);
1927 			return;
1928 		}
1929 		cr->cr_uid = pw->pw_uid;
1930 		ngroups = NGROUPS + 1;
1931 		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
1932 			syslog(LOG_ERR, "too many groups");
1933 		/*
1934 		 * Convert from int's to gid_t's and compress out duplicate
1935 		 */
1936 		cr->cr_ngroups = ngroups - 1;
1937 		cr->cr_groups[0] = groups[0];
1938 		for (cnt = 2; cnt < ngroups; cnt++)
1939 			cr->cr_groups[cnt - 1] = groups[cnt];
1940 		return;
1941 	}
1942 	/*
1943 	 * Explicit credential specified as a colon separated list:
1944 	 *	uid:gid:gid:...
1945 	 */
1946 	if (pw != NULL)
1947 		cr->cr_uid = pw->pw_uid;
1948 	else if (isdigit(*name) || *name == '-')
1949 		cr->cr_uid = atoi(name);
1950 	else {
1951 		syslog(LOG_ERR, "unknown user: %s", name);
1952 		return;
1953 	}
1954 	cr->cr_ngroups = 0;
1955 	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
1956 		name = strsep(&names, ":");
1957 		if (isdigit(*name) || *name == '-') {
1958 			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
1959 		} else {
1960 			if ((gr = getgrnam(name)) == NULL) {
1961 				syslog(LOG_ERR, "unknown group: %s", name);
1962 				continue;
1963 			}
1964 			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
1965 		}
1966 	}
1967 	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
1968 		syslog(LOG_ERR, "too many groups");
1969 }
1970 
1971 #define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
1972 /*
1973  * Routines that maintain the remote mounttab
1974  */
1975 void
1976 get_mountlist()
1977 {
1978 	struct mountlist *mlp, **mlpp;
1979 	char *host, *dirp, *cp;
1980 	char str[STRSIZ];
1981 	FILE *mlfile;
1982 
1983 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
1984 		syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
1985 		return;
1986 	}
1987 	mlpp = &mlhead;
1988 	while (fgets(str, STRSIZ, mlfile) != NULL) {
1989 		cp = str;
1990 		host = strsep(&cp, " \t\n");
1991 		dirp = strsep(&cp, " \t\n");
1992 		if (host == NULL || dirp == NULL)
1993 			continue;
1994 		mlp = (struct mountlist *)malloc(sizeof (*mlp));
1995 		if (mlp == (struct mountlist *)NULL)
1996 			out_of_mem();
1997 		strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
1998 		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
1999 		strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2000 		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2001 		mlp->ml_next = (struct mountlist *)NULL;
2002 		*mlpp = mlp;
2003 		mlpp = &mlp->ml_next;
2004 	}
2005 	fclose(mlfile);
2006 }
2007 
2008 void
2009 del_mlist(hostp, dirp)
2010 	char *hostp, *dirp;
2011 {
2012 	struct mountlist *mlp, **mlpp;
2013 	struct mountlist *mlp2;
2014 	FILE *mlfile;
2015 	int fnd = 0;
2016 
2017 	mlpp = &mlhead;
2018 	mlp = mlhead;
2019 	while (mlp) {
2020 		if (!strcmp(mlp->ml_host, hostp) &&
2021 		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2022 			fnd = 1;
2023 			mlp2 = mlp;
2024 			*mlpp = mlp = mlp->ml_next;
2025 			free((caddr_t)mlp2);
2026 		} else {
2027 			mlpp = &mlp->ml_next;
2028 			mlp = mlp->ml_next;
2029 		}
2030 	}
2031 	if (fnd) {
2032 		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2033 			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2034 			return;
2035 		}
2036 		mlp = mlhead;
2037 		while (mlp) {
2038 			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2039 			mlp = mlp->ml_next;
2040 		}
2041 		fclose(mlfile);
2042 	}
2043 }
2044 
2045 void
2046 add_mlist(hostp, dirp)
2047 	char *hostp, *dirp;
2048 {
2049 	struct mountlist *mlp, **mlpp;
2050 	FILE *mlfile;
2051 
2052 	mlpp = &mlhead;
2053 	mlp = mlhead;
2054 	while (mlp) {
2055 		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2056 			return;
2057 		mlpp = &mlp->ml_next;
2058 		mlp = mlp->ml_next;
2059 	}
2060 	mlp = (struct mountlist *)malloc(sizeof (*mlp));
2061 	if (mlp == (struct mountlist *)NULL)
2062 		out_of_mem();
2063 	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2064 	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2065 	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2066 	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2067 	mlp->ml_next = (struct mountlist *)NULL;
2068 	*mlpp = mlp;
2069 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2070 		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2071 		return;
2072 	}
2073 	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2074 	fclose(mlfile);
2075 }
2076 
2077 /*
2078  * This function is called via. SIGTERM when the system is going down.
2079  * It sends a broadcast RPCMNT_UMNTALL.
2080  */
2081 void
2082 send_umntall()
2083 {
2084 	(void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
2085 		xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
2086 	exit(0);
2087 }
2088 
2089 int
2090 umntall_each(resultsp, raddr)
2091 	caddr_t resultsp;
2092 	struct sockaddr_in *raddr;
2093 {
2094 	return (1);
2095 }
2096 
2097 /*
2098  * Free up a group list.
2099  */
2100 void
2101 free_grp(grp)
2102 	struct grouplist *grp;
2103 {
2104 	char **addrp;
2105 
2106 	if (grp->gr_type == GT_HOST) {
2107 		if (grp->gr_ptr.gt_hostent->h_name) {
2108 			addrp = grp->gr_ptr.gt_hostent->h_addr_list;
2109 			while (addrp && *addrp)
2110 				free(*addrp++);
2111 			free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
2112 			free(grp->gr_ptr.gt_hostent->h_name);
2113 		}
2114 		free((caddr_t)grp->gr_ptr.gt_hostent);
2115 	} else if (grp->gr_type == GT_NET) {
2116 		if (grp->gr_ptr.gt_net.nt_name)
2117 			free(grp->gr_ptr.gt_net.nt_name);
2118 	}
2119 #ifdef ISO
2120 	else if (grp->gr_type == GT_ISO)
2121 		free((caddr_t)grp->gr_ptr.gt_isoaddr);
2122 #endif
2123 	free((caddr_t)grp);
2124 }
2125 
2126 #ifdef DEBUG
2127 void
2128 SYSLOG(int pri, const char *fmt, ...)
2129 {
2130 	va_list ap;
2131 
2132 	va_start(ap, fmt);
2133 	vfprintf(stderr, fmt, ap);
2134 	va_end(ap);
2135 }
2136 #endif /* DEBUG */
2137 
2138 /*
2139  * Check options for consistency.
2140  */
2141 int
2142 check_options(dp)
2143 	struct dirlist *dp;
2144 {
2145 
2146 	if (dp == (struct dirlist *)NULL)
2147 	    return (1);
2148 	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
2149 	    (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
2150 	    (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
2151 	    syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
2152 	    return (1);
2153 	}
2154 	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2155 	    syslog(LOG_ERR, "-mask requires -net");
2156 	    return (1);
2157 	}
2158 	if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
2159 	    syslog(LOG_ERR, "-net and -iso mutually exclusive");
2160 	    return (1);
2161 	}
2162 	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2163 	    syslog(LOG_ERR, "-alldir has multiple directories");
2164 	    return (1);
2165 	}
2166 	return (0);
2167 }
2168 
2169 /*
2170  * Check an absolute directory path for any symbolic links. Return true
2171  * if no symbolic links are found.
2172  */
2173 int
2174 check_dirpath(dirp)
2175 	char *dirp;
2176 {
2177 	char *cp;
2178 	int ret = 1;
2179 	struct stat sb;
2180 
2181 	cp = dirp + 1;
2182 	while (*cp && ret) {
2183 		if (*cp == '/') {
2184 			*cp = '\0';
2185 			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2186 				ret = 0;
2187 			*cp = '/';
2188 		}
2189 		cp++;
2190 	}
2191 	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2192 		ret = 0;
2193 	return (ret);
2194 }
2195 
2196 /*
2197  * Just translate an ascii string to an integer.
2198  */
2199 int
2200 get_num(cp)
2201 	register char *cp;
2202 {
2203 	register int res = 0;
2204 
2205 	while (*cp) {
2206 		if (*cp < '0' || *cp > '9')
2207 			return (-1);
2208 		res = res * 10 + (*cp++ - '0');
2209 	}
2210 	return (res);
2211 }
2212