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