int debug = 0;
struct dirlist *dirp_search __P((struct dirlist *, char *));int	do_mount __P((struct exportlist *, struct grouplist *, int,				struct ucred *, char *, int, struct statfs *));int	do_opt __P((char **, char **, struct exportlist *, struct grouplist *,				int *, int *, struct ucred *));struct	exportlist *ex_search __P((fsid_t *));struct	exportlist *get_exp __P((void));void	free_dir __P((struct dirlist *));void	free_exp __P((struct exportlist *));void	free_grp __P((struct grouplist *));void	free_host __P((struct hostlist *));void	get_exportlist __P((void));int	get_host __P((char *, struct grouplist *));struct hostlist *get_ht __P((void));int	get_line __P((void));void	get_mountlist __P((void));int	get_net __P((char *, struct netmsk *, int));void	getexp_err __P((struct exportlist *, struct grouplist *));struct grouplist *get_grp __P((void));void	hang_dirp __P((struct dirlist *, struct grouplist *,				struct exportlist *, int));void	mntsrv __P((struct svc_req *, SVCXPRT *));void	nextfield __P((char **, char **));void	out_of_mem __P((void));void	parsecred __P((char *, struct ucred *));int	put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));int	scan_tree __P((struct dirlist *, u_long));void	send_umntall __P((void));int	umntall_each __P((caddr_t, struct sockaddr_in *));int	xdr_dir __P((XDR *, char *));int	xdr_explist __P((XDR *, caddr_t));int	xdr_fhs __P((XDR *, nfsv2fh_t *));int	xdr_mlist __P((XDR *, caddr_t));/* C library */int	getnetgrent();void	endnetgrent();void	setnetgrent();#ifdef ISOstruct iso_addr *iso_addr();#endifstruct exportlist *exphead;struct mountlist *mlhead;struct grouplist *grphead;char exname[MAXPATHLEN];struct ucred def_anon = {	1,	(uid_t) -2,	(gid_t) -2,	0,	{ }};int resvport_only = 1;int opt_flags;/* Bits for above */#define	OP_MAPROOT	0x01#define	OP_MAPALL	0x02#define	OP_KERB		0x04#define	OP_MASK		0x08#define	OP_NET		0x10#define	OP_ISO		0x20#define	OP_ALLDIRS	0x40#ifdef DEBUGint debug = 1;void	SYSLOG __P((int, const char *, ...));#define syslog SYSLOG#elseint debug = 0;#endif/* * Mountd server for NFS mount protocol as described in: * NFS: Network File System Protocol Specification, RFC1094, Appendix A * The optional arguments are the exports file name * default: _PATH_EXPORTS * and "-n" to allow nonroot mount. */intmain(argc, argv)	int argc;	char **argv;{	SVCXPRT *udptransp, *tcptransp;	int c;	while ((c = getopt(argc, argv, "n")) != EOF)		switch (c) {		case 'n':			resvport_only = 0;			break;		default:			fprintf(stderr, "Usage: mountd [-n] [export_file]\n");			exit(1);		};	argc -= optind;	argv += optind;	grphead = (struct grouplist *)NULL;	exphead = (struct exportlist *)NULL;	mlhead = (struct mountlist *)NULL;	if (argc == 1) {		strncpy(exname, *argv, MAXPATHLEN-1);		exname[MAXPATHLEN-1] = '\0';	} else		strcpy(exname, _PATH_EXPORTS);	openlog("mountd", LOG_PID, LOG_DAEMON);	if (debug)		fprintf(stderr,"Getting export list.\n");	get_exportlist();	if (debug)		fprintf(stderr,"Getting mount list.\n");	get_mountlist();	if (debug)		fprintf(stderr,"Here we go.\n");	if (debug == 0) {		daemon(0, 0);		signal(SIGINT, SIG_IGN);		signal(SIGQUIT, SIG_IGN);	}	signal(SIGHUP, (void (*) __P((int))) get_exportlist);	signal(SIGTERM, (void (*) __P((int))) send_umntall);	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");	  if (pidfile != NULL) {		fprintf(pidfile, "%d\n", getpid());		fclose(pidfile);	  }	}	if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||	    (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {		syslog(LOG_ERR, "Can't create socket");		exit(1);	}	pmap_unset(RPCPROG_MNT, RPCMNT_VER1);	if (!svc_register(udptransp, RPCPROG_MNT, RPCMNT_VER1, mntsrv,	    IPPROTO_UDP) ||	    !svc_register(tcptransp, RPCPROG_MNT, RPCMNT_VER1, mntsrv,	    IPPROTO_TCP)) {		syslog(LOG_ERR, "Can't register mount");		exit(1);	}	svc_run();	syslog(LOG_ERR, "Mountd died");	exit(1);}/* * The mount rpc service */voidmntsrv(rqstp, transp)	struct svc_req *rqstp;	SVCXPRT *transp;{	struct exportlist *ep;	struct dirlist *dp;	nfsv2fh_t nfh;	struct stat stb;	struct statfs fsb;	struct hostent *hp;	u_long saddr;	u_short sport;	char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN];	int bad = ENOENT, defset;	sigset_t sigset, osigset;	saddr = transp->xp_raddr.sin_addr.s_addr;	sport = ntohs(transp->xp_raddr.sin_port);	hp = (struct hostent *)NULL;	switch (rqstp->rq_proc) {	case NULLPROC:		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))			syslog(LOG_ERR, "Can't send reply");		return;	case RPCMNT_MOUNT:		if (sport >= IPPORT_RESERVED && resvport_only) {			svcerr_weakauth(transp);			return;		}		if (!svc_getargs(transp, xdr_dir, rpcpath)) {			svcerr_decode(transp);			return;		}		/*		 * Get the real pathname and make sure it is a file or		 * directory that exists.		 */		if (realpath(rpcpath, dirpath) == 0 ||		    stat(dirpath, &stb) < 0 ||		    (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||		    statfs(dirpath, &fsb) < 0) {			chdir("/");	/* Just in case realpath doesn't */			if (debug)				fprintf(stderr, "stat failed on %s\n", dirpath);			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))				syslog(LOG_ERR, "Can't send reply");			return;		}		/* Check in the exports list */		sigemptyset(&sigset);		sigaddset(&sigset, SIGHUP);		sigprocmask(SIG_BLOCK, &sigset, &osigset);		ep = ex_search(&fsb.f_fsid);		defset = 0;		if (ep && (chk_host(ep->ex_defdir, saddr, &defset) ||		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&		     chk_host(dp, saddr, &defset)) ||		     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&		      scan_tree(ep->ex_dirl, saddr) == 0))) {			/* Get the file handle */			memset(&nfh, 0, sizeof(nfh));			if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {				bad = errno;				syslog(LOG_ERR, "Can't get fh for %s", dirpath);				if (!svc_sendreply(transp, xdr_long,				    (caddr_t)&bad))					syslog(LOG_ERR, "Can't send reply");				sigprocmask(SIG_SETMASK, &osigset, NULL);				return;			}			if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))				syslog(LOG_ERR, "Can't send reply");			if (hp == NULL)				hp = gethostbyaddr((caddr_t)&saddr,				    sizeof(saddr), AF_INET);			if (hp)				add_mlist(hp->h_name, dirpath);			else				add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),					dirpath);			if (debug)				fprintf(stderr,"Mount successfull.\n");		} else {			bad = EACCES;			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))				syslog(LOG_ERR, "Can't send reply");		}		sigprocmask(SIG_SETMASK, &osigset, NULL);		return;	case RPCMNT_DUMP:		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))			syslog(LOG_ERR, "Can't send reply");		return;	case RPCMNT_UMOUNT:		if (sport >= IPPORT_RESERVED && resvport_only) {			svcerr_weakauth(transp);			return;		}		if (!svc_getargs(transp, xdr_dir, dirpath)) {			svcerr_decode(transp);			return;		}		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))			syslog(LOG_ERR, "Can't send reply");		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);		if (hp)			del_mlist(hp->h_name, dirpath);		del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);		return;	case RPCMNT_UMNTALL:		if (sport >= IPPORT_RESERVED && resvport_only) {			svcerr_weakauth(transp);			return;		}		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))			syslog(LOG_ERR, "Can't send reply");		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);		if (hp)			del_mlist(hp->h_name, (char *)NULL);		del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);		return;	case RPCMNT_EXPORT:		if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))			syslog(LOG_ERR, "Can't send reply");		return;	default:		svcerr_noproc(transp);		return;	}}/* * Xdr conversion for a dirpath string */intxdr_dir(xdrsp, dirp)	XDR *xdrsp;	char *dirp;{	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));}/* * Xdr routine to generate fhstatus */intxdr_fhs(xdrsp, nfh)	XDR *xdrsp;	nfsv2fh_t *nfh;{	long ok = 0;	if (!xdr_long(xdrsp, &ok))		return (0);	return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));}intxdr_mlist(xdrsp, cp)	XDR *xdrsp;	caddr_t cp;{	struct mountlist *mlp;	int true = 1;	int false = 0;	char *strp;	mlp = mlhead;	while (mlp) {		if (!xdr_bool(xdrsp, &true))			return (0);		strp = &mlp->ml_host[0];		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))			return (0);		strp = &mlp->ml_dirp[0];		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))			return (0);		mlp = mlp->ml_next;	}	if (!xdr_bool(xdrsp, &false))		return (0);	return (1);}/* * Xdr conversion for export list */intxdr_explist(xdrsp, cp)	XDR *xdrsp;	caddr_t cp;{	struct exportlist *ep;	int false = 0;	int putdef;	sigset_t sigset, osigset;	sigemptyset(&sigset);	sigaddset(&sigset, SIGHUP);	sigprocmask(SIG_BLOCK, &sigset, &osigset);	ep = exphead;	while (ep) {		putdef = 0;		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))			goto errout;		if (ep->ex_defdir && putdef == 0 &&			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,			&putdef))			goto errout;		ep = ep->ex_next;	}	sigprocmask(SIG_SETMASK, &osigset, NULL);	if (!xdr_bool(xdrsp, &false))		return (0);	return (1);errout:	sigprocmask(SIG_SETMASK, &osigset, NULL);	return (0);}/* * Called from xdr_explist() to traverse the tree and export the * directory paths. */intput_exlist(dp, xdrsp, adp, putdefp)	struct dirlist *dp;	XDR *xdrsp;	struct dirlist *adp;	int *putdefp;{	struct grouplist *grp;	struct hostlist *hp;	int true = 1;	int false = 0;	int gotalldir = 0;	char *strp;	if (dp) {		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))			return (1);		if (!xdr_bool(xdrsp, &true))			return (1);		strp = dp->dp_dirp;		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))			return (1);		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {			gotalldir = 1;			*putdefp = 1;		}		if ((dp->dp_flag & DP_DEFSET) == 0 &&		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {			hp = dp->dp_hosts;			while (hp) {				grp = hp->ht_grp;				if (grp->gr_type == GT_HOST) {					if (!xdr_bool(xdrsp, &true))						return (1);					strp = grp->gr_ptr.gt_hostent->h_name;					if (!xdr_string(xdrsp, &strp, 					    RPCMNT_NAMELEN))						return (1);				} else if (grp->gr_type == GT_NET) {					if (!xdr_bool(xdrsp, &true))						return (1);					strp = grp->gr_ptr.gt_net.nt_name;					if (!xdr_string(xdrsp, &strp, 					    RPCMNT_NAMELEN))						return (1);				}				hp = hp->ht_next;				if (gotalldir && hp == (struct hostlist *)NULL) {					hp = adp->dp_hosts;					gotalldir = 0;				}			}		}		if (!xdr_bool(xdrsp, &false))			return (1);		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))			return (1);	}	return (0);}#define LINESIZ	10240char line[LINESIZ];FILE *exp_file;/* * Get the export list */voidget_exportlist(){	struct exportlist *ep, *ep2;	struct grouplist *grp, *tgrp;	struct exportlist **epp;	struct dirlist *dirhead;	struct statfs fsb, *fsp;	struct hostent *hpe;	struct ucred anon;	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;	/*	 * First, get rid of the old list	 */	ep = exphead;	while (ep) {		ep2 = ep;		ep = ep->ex_next;		free_exp(ep2);	}	exphead = (struct exportlist *)NULL;	grp = grphead;	while (grp) {		tgrp = grp;		grp = grp->gr_next;		free_grp(tgrp);	}	grphead = (struct grouplist *)NULL;	/*	 * And delete exports that are in the kernel for all local	 * file systems.	 * XXX: Should know how to handle all local exportable file systems	 *      instead of just MOUNT_FFS.	 */	num = getmntinfo(&fsp, MNT_NOWAIT);	for (i = 0; i < num; i++) {		union {			struct ufs_args ua;			struct iso_args ia;			struct mfs_args ma;			struct msdosfs_args da;			struct adosfs_args aa;		} targs;		if (!strncmp(fsp->f_fstypename, MOUNT_MFS, MFSNAMELEN) ||		    !strncmp(fsp->f_fstypename, MOUNT_FFS, MFSNAMELEN) ||		    !strncmp(fsp->f_fstypename, MOUNT_MSDOS, MFSNAMELEN) ||		    !strncmp(fsp->f_fstypename, MOUNT_ADOSFS, MFSNAMELEN) ||		    !strncmp(fsp->f_fstypename, MOUNT_CD9660, MFSNAMELEN)) {			bzero((char *)&targs, sizeof(targs));			targs.ua.fspec = NULL;			targs.ua.export.ex_flags = MNT_DELEXPORT;			if (mount(fsp->f_fstypename, fsp->f_mntonname,				  fsp->f_flags | MNT_UPDATE,				  (caddr_t)&targs) < 0)				syslog(LOG_ERR, "Can't delete exports for %s",				       fsp->f_mntonname);		}		fsp++;	}	/*	 * Read in the exports file and build the list, calling	 * mount() as we go along to push the export rules into the kernel.	 */	if ((exp_file = fopen(exname, "r")) == NULL) {		syslog(LOG_ERR, "Can't open %s", exname);		exit(2);	}	dirhead = (struct dirlist *)NULL;	while (get_line()) {		if (debug)			fprintf(stderr,"Got line %s\n",line);		cp = line;		nextfield(&cp, &endcp);		if (*cp == '#')			goto nextline;		/*		 * Set defaults.		 */		has_host = FALSE;		anon = def_anon;		exflags = MNT_EXPORTED;		got_nondir = 0;		opt_flags = 0;		ep = (struct exportlist *)NULL;		/*		 * Create new exports list entry		 */		len = endcp-cp;		tgrp = grp = get_grp();		while (len > 0) {			if (len > RPCMNT_NAMELEN) {			    getexp_err(ep, tgrp);			    goto nextline;			}			if (*cp == '-') {			    if (ep == (struct exportlist *)NULL) {				getexp_err(ep, tgrp);				goto nextline;			    }			    if (debug)				fprintf(stderr, "doing opt %s\n", cp);			    got_nondir = 1;			    if (do_opt(&cp, &endcp, ep, grp, &has_host,				&exflags, &anon)) {				getexp_err(ep, tgrp);				goto nextline;			    }			} else if (*cp == '/') {			    savedc = *endcp;			    *endcp = '\0';			    if (check_dirpath(cp) &&				statfs(cp, &fsb) >= 0) {				if (got_nondir) {				    syslog(LOG_ERR, "Dirs must be first");				    getexp_err(ep, tgrp);				    goto nextline;				}				if (ep) {				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {					getexp_err(ep, tgrp);					goto nextline;				    }				} else {				    /*				     * See if this directory is already				     * in the list.				     */				    ep = ex_search(&fsb.f_fsid);				    if (ep == (struct exportlist *)NULL) {					ep = get_exp();					ep->ex_fs = fsb.f_fsid;					ep->ex_fsdir = (char *)					    malloc(strlen(fsb.f_mntonname) + 1);					if (ep->ex_fsdir)					    strcpy(ep->ex_fsdir,						fsb.f_mntonname);					else					    out_of_mem();					if (debug)					  fprintf(stderr,					      "Making new ep fs=0x%x,0x%x\n",					      fsb.f_fsid.val[0],					      fsb.f_fsid.val[1]);				    } else if (debug)					fprintf(stderr,					    "Found ep fs=0x%x,0x%x\n",					    fsb.f_fsid.val[0],					    fsb.f_fsid.val[1]);				}				/*				 * Add dirpath to export mount point.				 */				dirp = add_expdir(&dirhead, cp, len);				dirplen = len;			    } else {				getexp_err(ep, tgrp);				goto nextline;			    }			    *endcp = savedc;			} else {			    savedc = *endcp;			    *endcp = '\0';			    got_nondir = 1;			    if (ep == (struct exportlist *)NULL) {				getexp_err(ep, tgrp);				goto nextline;			    }			    /*			     * Get the host or netgroup.			     */			    setnetgrent(cp);			    netgrp = getnetgrent(&hst, &usr, &dom);			    do {				if (has_host) {				    grp->gr_next = get_grp();				    grp = grp->gr_next;				}				if (netgrp) {				    if (get_host(hst, grp)) {					syslog(LOG_ERR, "Bad netgroup %s", cp);					getexp_err(ep, tgrp);					goto nextline;				    }				} else if (get_host(cp, grp)) {				    getexp_err(ep, tgrp);				    goto nextline;				}				has_host = TRUE;			    } while (netgrp && getnetgrent(&hst, &usr, &dom));			    endnetgrent();			    *endcp = savedc;			}			cp = endcp;			nextfield(&cp, &endcp);			len = endcp - cp;		}		if (check_options(dirhead)) {			getexp_err(ep, tgrp);			goto nextline;		}		if (!has_host) {			grp->gr_type = GT_HOST;			if (debug)				fprintf(stderr,"Adding a default entry\n");			/* add a default group and make the grp list NULL */			hpe = (struct hostent *)malloc(sizeof(struct hostent));			if (hpe == (struct hostent *)NULL)				out_of_mem();			hpe->h_name = "Default";			hpe->h_addrtype = AF_INET;			hpe->h_length = sizeof (u_long);			hpe->h_addr_list = (char **)NULL;			grp->gr_ptr.gt_hostent = hpe;		/*		 * Don't allow a network export coincide with a list of		 * host(s) on the same line.		 */		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {			getexp_err(ep, tgrp);			goto nextline;		}		/*		 * Loop through hosts, pushing the exports into the kernel.		 * After loop, tgrp points to the start of the list and		 * grp points to the last entry in the list.		 */		grp = tgrp;		do {		    if (do_mount(ep, grp, exflags, &anon, dirp,			dirplen, &fsb)) {			getexp_err(ep, tgrp);			goto nextline;		    }		} while (grp->gr_next && (grp = grp->gr_next));		/*		 * Success. Update the data structures.		 */		if (has_host) {			hang_dirp(dirhead, tgrp, ep, (opt_flags & OP_ALLDIRS));			grp->gr_next = grphead;			grphead = tgrp;		} else {			hang_dirp(dirhead, (struct grouplist *)NULL, ep,			(opt_flags & OP_ALLDIRS));			free_grp(grp);		}		dirhead = (struct dirlist *)NULL;		if ((ep->ex_flag & EX_LINKED) == 0) {			ep2 = exphead;			epp = &exphead;			/*			 * Insert in the list in alphabetical order.			 */			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {				epp = &ep2->ex_next;				ep2 = ep2->ex_next;			}			if (ep2)				ep->ex_next = ep2;			*epp = ep;			ep->ex_flag |= EX_LINKED;		}nextline:		if (dirhead) {			free_dir(dirhead);			dirhead = (struct dirlist *)NULL;		}	}	fclose(exp_file);}/* * Allocate an export list element */struct exportlist *get_exp(){	struct exportlist *ep;	ep = (struct exportlist *)malloc(sizeof (struct exportlist));	if (ep == (struct exportlist *)NULL)		out_of_mem();	memset(ep, 0, sizeof(struct exportlist));	return (ep);}/* * Allocate a group list element */struct grouplist *get_grp(){	struct grouplist *gp;	gp = (struct grouplist *)malloc(sizeof (struct grouplist));	if (gp == (struct grouplist *)NULL)		out_of_mem();	memset(gp, 0, sizeof(struct grouplist));	return (gp);}/* * Clean up upon an error in get_exportlist(). */voidgetexp_err(ep, grp)	struct exportlist *ep;	struct grouplist *grp;{	struct grouplist *tgrp;	syslog(LOG_ERR, "Bad exports list line %s", line);	if (ep && (ep->ex_flag & EX_LINKED) == 0)		free_exp(ep);	while (grp) {		tgrp = grp;		grp = grp->gr_next;		free_grp(tgrp);	}}/* * Search the export list for a matching fs. */struct exportlist *ex_search(fsid)	fsid_t *fsid;{	struct exportlist *ep;	ep = exphead;	while (ep) {		if (ep->ex_fs.val[0] == fsid->val[0] &&		    ep->ex_fs.val[1] == fsid->val[1])			return (ep);		ep = ep->ex_next;	}	return (ep);}/* * Add a directory path to the list. */char *add_expdir(dpp, cp, len)	struct dirlist **dpp;	char *cp;	int len;{	struct dirlist *dp;	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);	dp->dp_left = *dpp;	dp->dp_right = (struct dirlist *)NULL;	dp->dp_flag = 0;	dp->dp_hosts = (struct hostlist *)NULL;	strcpy(dp->dp_dirp, cp);	*dpp = dp;	return (dp->dp_dirp);}/* * Hang the dir list element off the dirpath binary tree as required * and update the entry for host. */voidhang_dirp(dp, grp, ep, alldirs)	struct dirlist *dp;	struct grouplist *grp;	struct exportlist *ep;	int alldirs;{	struct hostlist *hp;	struct dirlist *dp2;	if (alldirs) {		if (ep->ex_defdir)			free((caddr_t)dp);		else			ep->ex_defdir = dp;		if (grp == (struct grouplist *)NULL)			ep->ex_defdir->dp_flag |= DP_DEFSET;		else while (grp) {			hp = get_ht();			hp->ht_grp = grp;			hp->ht_next = ep->ex_defdir->dp_hosts;			ep->ex_defdir->dp_hosts = hp;			grp = grp->gr_next;		}	} else {		/*		 * Loop throught the directories adding them to the tree.		 */		while (dp) {			dp2 = dp->dp_left;			add_dlist(&ep->ex_dirl, dp, grp);			dp = dp2;		}	}}/* * Traverse the binary tree either updating a node that is already there * for the new directory or adding the new node. */voidadd_dlist(dpp, newdp, grp)	struct dirlist **dpp;	struct dirlist *newdp;	struct grouplist *grp;{	struct dirlist *dp;	struct hostlist *hp;	int cmp;	dp = *dpp;	if (dp) {		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);		if (cmp > 0) {			add_dlist(&dp->dp_left, newdp, grp);			return;		} else if (cmp < 0) {			add_dlist(&dp->dp_right, newdp, grp);			return;		} else			free((caddr_t)newdp);	} else {		dp = newdp;		dp->dp_left = (struct dirlist *)NULL;		*dpp = dp;	}	if (grp) {		/*		 * Hang all of the host(s) off of the directory point.		 */		do {			hp = get_ht();			hp->ht_grp = grp;			hp->ht_next = dp->dp_hosts;			dp->dp_hosts = hp;			grp = grp->gr_next;		} while (grp);	} else		dp->dp_flag |= DP_DEFSET;}/* * Search for a dirpath on the export point. */struct dirlist *dirp_search(dp, dirpath)	struct dirlist *dp;	char *dirpath;{	int cmp;	if (dp) {		cmp = strcmp(dp->dp_dirp, dirpath);		if (cmp > 0)			return (dirp_search(dp->dp_left, dirpath));		else if (cmp < 0)			return (dirp_search(dp->dp_right, dirpath));		else			return (dp);	}	return (dp);}