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