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