Net2/usr/src/contrib/isode/quipu/entry_load.c

/* entry_load.c - load bits of the database */

#ifndef lint
static char *rcsid = "$Header: /f/osi/quipu/RCS/entry_load.c,v 7.6 91/03/09 11:56:52 mrose Exp $";
#endif

/*
 * $Header: /f/osi/quipu/RCS/entry_load.c,v 7.6 91/03/09 11:56:52 mrose Exp $
 *
 *
 * $Log:	entry_load.c,v $
 * Revision 7.6  91/03/09  11:56:52  mrose
 * update
 * 
 * Revision 7.5  91/02/22  09:39:20  mrose
 * Interim 6.8
 * 
 * Revision 7.4  90/10/17  11:54:17  mrose
 * sync
 * 
 * Revision 7.3  90/07/09  14:46:10  mrose
 * sync
 * 
 * Revision 7.2  90/04/18  08:49:54  mrose
 * 6.2
 * 
 * Revision 7.1  89/12/19  16:20:34  mrose
 * sync
 * 
 * Revision 7.0  89/11/23  22:17:39  mrose
 * Release 6.0
 * 
 */

/*
 *                                NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */

#include "config.h"
#include "quipu/util.h"
#include "quipu/entry.h"
#include "quipu/ds_error.h"
#include "tailor.h"
#include <sys/stat.h>
#include <errno.h>
#ifdef TURBO_AVL
#include "quipu/turbo.h"
#endif

extern char * treedir;
extern LLog * log_dsap;
extern int errno;
extern int parse_status;
struct acl_info * acl_dflt ();
#ifdef TURBO_INDEX
extern AttributeType *turbo_index_types;
#endif

static char filename [LINESIZE];
static PS ps;

#define EDBLEN	3	/* length of string "EDB" */

fileexists (fname)
char * fname;
{
struct stat buf;

	if (stat (fname,&buf) != 0) {
		if (errno != ENOENT)
			DLOG (log_dsap,LLOG_DEBUG,("File %s will not stat - %d",fname,errno));
		return FALSE;
	}
	return TRUE;
}

static read_mapped_rdn (aps,name,file)
PS aps;
char * name;
char * file;
{
FILE * mapfp;
char *ptr, *newname, *tmp, *getline();
extern int parse_line;
register int i;

	if ((mapfp = fopen (file,"r")) == (FILE *)NULL) {
		LLOG(log_dsap,LLOG_EXCEPTIONS,("Can read \"%s\" (%d)",file,errno));
		return FALSE;
	}

	parse_line = 0;
	while ( (ptr = getline(mapfp)) != NULLCP) {
		if ((newname = rindex(ptr,'#')) == NULLCP) {
			LLOG(log_dsap,LLOG_EXCEPTIONS,("Seperator missing in map file \"%s\", line %d",file,parse_line));
			(void) fclose (mapfp);			
			return FALSE;
		}
		tmp = newname;
		*newname++ = 0;
		while (isspace(*--tmp))
			*tmp = 0;

		if (lexequ (name,ptr) == 0) {
			/* got it - replace in ps*/	
			i = strlen (name);
			aps->ps_ptr -= i;
			aps->ps_cnt += i;
			ps_print (aps,SkipSpace(newname));
			(void) fclose (mapfp);			
			return TRUE;
		}
	}

	DLOG (log_dsap, LLOG_DEBUG,("%s not found in map file %s",name,file));
	(void) fclose (mapfp);			
	return FALSE;
}

static write_mapped_rdn (aps,name,file)
PS aps;
char * name;
char * file;
{
FILE * mapfp;
char mapname[LINESIZE];
char sname[LINESIZE];
char *mptr, *nptr;
register int i;

	if (strlen(name) < MAXFILENAMELEN) 
		return FALSE;

	/* Make unique name for it */
	mptr = mapname;
	if ((nptr = index (name,'=')) == NULLCP)
		return FALSE;

	for (i=0 ; (*nptr!=0) && (i < MAXFILENAMELEN-6) ; nptr++)
		if (isalpha(*nptr))
			*mptr++ = *nptr, i++;

	(void) strcpy (sname,name);
	(void) strcpy (mptr,"XXXXXX");
	i = strlen (name);
	nptr = (aps->ps_ptr -= i);
	aps->ps_cnt += i;
	ps_print (aps,mapname);
	*aps->ps_ptr = 0;

	if ((aps->ps_base = mktemp (aps->ps_base)) == NULLCP)
		return FALSE;

	DLOG(log_dsap,LLOG_DEBUG,("mapped name %s",aps->ps_base));

	if (mkdir (aps->ps_base,0700) != 0) {
		LLOG (log_dsap,LLOG_EXCEPTIONS,("map rdn mkdir failure \"%s\" (%d)",aps->ps_base,errno));
		return FALSE;
	}

	/* write it to map file */
	if (fileexists(file)) 
		mapfp = fopen (file,"a");
	else {
		int um;
		um = umask (0177);
		mapfp = fopen (file,"w");
		(void) umask (um);
	}

	if (mapfp == (FILE *)NULL) {
		LLOG(log_dsap,LLOG_EXCEPTIONS,("Can't write to \"%s\" (%d)",file,errno));
		return FALSE;
	}

	if (fprintf (mapfp,"%s#%s\n",sname,nptr) == EOF) {
		LLOG(log_dsap,LLOG_EXCEPTIONS,("Can't write to \"%s\" (%d)",file,errno));
		return FALSE;
	}

	if (fclose (mapfp) != 0) {
		LLOG(log_dsap,LLOG_EXCEPTIONS,("Can't close \"%s\" (%d)",file,errno));
		return FALSE;
	}

	return TRUE;
}

static rdn2filename (aps,rdn,make)
PS aps;
RDN rdn;
char make;
{
	char *start = aps->ps_ptr;
	char mapbuf [LINESIZE];	

	/* look for EDB.map file */
	*aps->ps_ptr = 0;
	(void) sprintf (mapbuf, "%sEDB.map",aps->ps_base);

	rdn_print (aps,rdn,DIROUT);
	*aps->ps_ptr = 0;

	if (fileexists (mapbuf) && read_mapped_rdn (aps,start,mapbuf)) {
		*aps->ps_ptr = 0;
		if (fileexists(aps->ps_base))
			return OK;
		if (make) { 
			if (mkdir (aps->ps_base,0700) != 0) {
				LLOG (log_dsap,LLOG_EXCEPTIONS,("dn2file mkdir (mapped) failure \"%s\" (%d)",aps->ps_base,errno));
				return NOTOK;
			}
			LLOG (log_dsap,LLOG_NOTICE,("WARNING: Needed to make mapped directory \"%s\"",aps->ps_base));
			return OK;
		}
		LLOG (log_dsap,LLOG_EXCEPTIONS,("mapped file missing \"%s\"",aps->ps_base));
		return NOTOK;
	}

#ifdef SYS5
	else if ( strlen(start) > MAXFILENAMELEN ) 
		LLOG (log_dsap,LLOG_EXCEPTIONS,("Potential problem with \"%s\" (name too long)",start));
#endif

	if (fileexists(aps->ps_base))
		return OK;
	
	if (make) { 
		if (write_mapped_rdn (aps,start,mapbuf)) 
			return OK;
		if (mkdir (aps->ps_base,0700) != 0) {
			LLOG (log_dsap,LLOG_EXCEPTIONS,("dn2file mkdir failure \"%s\" (%d)",aps->ps_base,errno));
			return NOTOK;
		}
		return OK;
	}

	return NOTOK;
}

static dn2filename (aps,dn,make)
PS aps;
DN dn;
char make;
{
	if (treedir != NULLCP) {
		ps_print (aps,isodefile(treedir,0));
		if (make) {
			*aps->ps_ptr = 0;
			if ((! fileexists (aps->ps_base)) &&
				(mkdir (aps->ps_base,0700) != 0)) {
				LLOG (log_dsap,LLOG_EXCEPTIONS,("dn2file mkdir failure \"%s\" (%d)",aps->ps_base,errno));
				return NOTOK;
				}
		}
		if (*(aps->ps_ptr - 1) != '/')
			ps_print (aps,"/");
	} else
		ps_print (aps,"./");

	if (dn != NULLDN) {
		if (rdn2filename (aps,dn->dn_rdn,make) == NOTOK)
			return NOTOK;
		if (dn->dn_parent != NULLDN) {
			DN eptr;
			for (eptr = dn->dn_parent; eptr != NULLDN; eptr = eptr->dn_parent) {
				ps_print (aps,"/"); 
				if (rdn2filename (aps,eptr->dn_rdn,make) == NOTOK)
					return NOTOK;
			}
		}
	}

	return OK;

}

char * dn2edbfile (dn)
DN dn;
{
PS aps;
static char result [LINESIZE];

	if ((aps = ps_alloc (str_open)) == NULLPS) {
		LLOG (log_dsap, LLOG_EXCEPTIONS, ("dn2dir ps_alloc failed"));
		return NULLCP;
	}
	if (str_setup (aps,result,LINESIZE,1) == NOTOK) {
		LLOG (log_dsap, LLOG_EXCEPTIONS, ("dn2dir ps_alloc failed"));
		return NULLCP;
	}

	if (dn2filename (aps,dn,TRUE) != OK)
		return NULLCP;

	if (*(aps->ps_ptr - 1) != '/')
		ps_print (aps,"/EDB");
	else
		ps_print (aps,"EDB");
	*aps->ps_ptr = 0;

	ps_free (aps);

	return result;
}

static file_check (offset,entryptr)
register int offset;
register Entry entryptr;
{
	ps->ps_ptr = filename + offset;
	ps->ps_cnt = LINESIZE - offset;

	if (rdn2filename (ps,entryptr->e_name,FALSE) == OK) {
		if (*(ps->ps_ptr - 1) != '/')
			ps_print (ps,"/EDB");
		else
			ps_print (ps,"EDB");
		*ps->ps_ptr = 0;
		return (OK);
	}
	return (NOTOK);
}

#ifdef TURBO_AVL

static char got_all = TRUE;

static load_a_kid(e, offset)
Entry   e;
int     offset;
{
        static int      entry_load_kids();

        if ((!e->e_external) && 
	    (e->e_master == NULLAV) && 
	    (e->e_slave == NULLAV)) {
                e->e_leaf = TRUE;
		e->e_allchildrenpresent = 2;
                return(OK);
        }

        if (file_check(offset, e) == OK) {
                if ((e->e_children = getentry_block(e, filename)) == NULLAVL) {
			if (e->e_allchildrenpresent != FALSE &&
			    e->e_leaf == FALSE)
				return(OK);
			else
				return(NOTOK) ;
		}
		if (parse_status != 0)
			return(NOTOK);

                e->e_leaf = FALSE;

                if (entry_load_kids(e->e_children, strlen( filename ) - EDBLEN)
		    == NOTOK)
                        return (NOTOK);
		if (e->e_allchildrenpresent != 2)
			got_all = FALSE;
        } else {
                LLOG (log_dsap, LLOG_TRACE, ("Sibling file %s/EDB NOT found", filename));
		e->e_allchildrenpresent = FALSE;
		got_all = FALSE;
	}

        return(OK);
}

#endif /* TURBO_AVL */

static entry_load_kids (entryptr,offset)
#ifdef TURBO_AVL
Avlnode	*entryptr;	/* in this case, entryptr is really a tree of kids */
#else
register Entry entryptr;
#endif
register int offset;
{
#ifdef TURBO_AVL
Entry	akid, parent;
#else
register Entry ptr;
char got_all = TRUE;
#endif

	ps->ps_ptr = filename + offset;
	ps->ps_cnt = LINESIZE - offset;
	*ps->ps_ptr = 0;

#ifdef TURBO_AVL

	if (entryptr == NULLAVL)
		return(OK);

	got_all = TRUE;

        if (avl_apply(entryptr, load_a_kid,  (caddr_t) offset, NOTOK, AVL_PREORDER)
            == NOTOK)
                return(NOTOK);

	akid = (Entry) avl_getone(entryptr);
	if (akid && (parent = akid->e_parent)) {
		if (got_all) {
			if (parent->e_allchildrenpresent == 1)
				parent->e_allchildrenpresent = 2;
		} else if (parent->e_allchildrenpresent == 2)
			parent->e_allchildrenpresent = 1;
	}
#else

	if (entryptr == NULLENTRY)
		return OK;

	for ( ptr = entryptr; ptr != NULLENTRY; ptr = ptr->e_sibling) {
		if ((!ptr->e_external) && 
		    (ptr->e_master == NULLAV) && 
		    (ptr->e_slave == NULLAV)) {
			ptr->e_leaf = TRUE;
			ptr->e_allchildrenpresent = 2;
			continue;
		}

		if (file_check (offset,ptr) == OK) {
			if ((ptr->e_child = getentry_block (ptr,filename)) == NULLENTRY)
			{
				if (ptr->e_allchildrenpresent != FALSE &&
				    ptr->e_leaf == FALSE)
					continue;
				else
					return (NOTOK) ;
			}
			if (parse_status != 0)
				return NOTOK;

			ptr->e_leaf = FALSE;
			if (entry_load_kids (ptr->e_child,strlen(filename) - EDBLEN) == NOTOK)
				return (NOTOK);
			if (ptr->e_allchildrenpresent != 2)
				got_all = FALSE;
		} else {
			LLOG (log_dsap,LLOG_TRACE,("WARNING, sibling file %s/EDB NOT found", filename));
			ptr->e_allchildrenpresent = FALSE;
			got_all = FALSE;
		}
	}

	if (entryptr->e_parent) {
		if (got_all) {
			if (entryptr->e_parent->e_allchildrenpresent == 1)
				entryptr->e_parent->e_allchildrenpresent = 2;
		} else if (entryptr->e_parent->e_allchildrenpresent == 2)
			entryptr->e_parent->e_allchildrenpresent = 1;
	}
#endif /* TURBO_AVL */

	return (OK);
}

#ifdef TURBO_AVL
static char got_subtree;

static check_entry_free (e)
Entry e;
{
	if (e->e_allchildrenpresent < 2)
		got_subtree = FALSE;
	entry_free(e);
}

parent_link(e, parent)
Entry   e;
Entry   parent;
{
struct DSError error;
int res;

        e->e_parent = parent;
	set_inheritance (e);
        return(OK);
}

static merge_entry(newentry, oldtree)
Entry   newentry;
Avlnode *oldtree;
{
        Entry   p;
        int     entry_cmp();

        newentry->e_parent = ((Entry) avl_getone(oldtree))->e_parent;

        if ((p = (Entry) avl_find(oldtree, (caddr_t) newentry, entry_cmp))
            != NULLENTRY ) {
                newentry->e_leaf = FALSE;
                newentry->e_allchildrenpresent = p->e_allchildrenpresent;
                newentry->e_children = p->e_children;

                (void) avl_apply(newentry->e_children, parent_link, (caddr_t) newentry,
		    NOTOK, AVL_PREORDER);

                if (p->e_edbversion != NULLCP)
                        newentry->e_edbversion = strdup(p->e_edbversion);
        } else {
		got_subtree = FALSE;	
		newentry->e_allchildrenpresent = FALSE;
	}

        return(OK);
}
#endif /* TURBO_AVL */

Entry subtree_load (parent,dn)
Entry parent;
DN dn;
{
char failed = FALSE;
#ifdef TURBO_AVL
Avlnode	*treetop;
Entry	akid;
int	entry_free();
#else
Entry temp, old_entry;
char got_subtree = TRUE;
Entry	treetop, sibl, find_sibling();
#endif

#ifdef TURBO_AVL

	got_subtree = TRUE;

	if ((parent != NULLENTRY) && (parent->e_children != NULLAVL)) {
		akid = (Entry) avl_getone(parent->e_children);
		if (akid->e_data != E_TYPE_CONSTRUCTOR)
			return (parent);
	}
#else
	if ((parent != NULLENTRY) && (parent->e_child != NULLENTRY))
		if (parent->e_child->e_data != E_TYPE_CONSTRUCTOR)
			return (parent->e_child);
#endif

	if ((ps = ps_alloc (str_open)) == NULLPS) {
		LLOG (log_dsap,LLOG_EXCEPTIONS,("file open ps_alloc failed"));
		return (NULLENTRY);
	}
	if (str_setup (ps,filename,LINESIZE,1) == NOTOK) {
		LLOG (log_dsap,LLOG_EXCEPTIONS,("file open ps_alloc failed"));
		return (NULLENTRY);
	}

	(void) dn2filename (ps,dn,FALSE);
	if (*(ps->ps_ptr - 1) != '/')
		ps_print (ps,"/EDB");
	else
		ps_print (ps,"EDB");
	*ps->ps_ptr = 0;

#ifdef TURBO_AVL
	if (parent != NULLENTRY && parent->e_children != NULLAVL) {
#else
	if ((parent != NULLENTRY) && (parent->e_child != NULLENTRY)) {
#endif
		/* yuk - already got an edb lower in the DIT ... */
		treetop = getentry_block (parent,filename);
		if (parse_status != 0)
			return NULLENTRY;

#ifdef TURBO_AVL
		/*
		 * go through the tree we just loaded, merging it with the
		 * tree previously loaded.
		 */

		(void) avl_apply(treetop, merge_entry, (caddr_t) parent->e_children,
		    NOTOK, AVL_PREORDER);

		if (got_subtree && (parent->e_allchildrenpresent == 1))
			parent->e_allchildrenpresent = 2;

		got_subtree = TRUE;

		/* free the old tree and set got_subtree */
		(void) avl_free(parent->e_children, check_entry_free);

		if (got_subtree && (parent->e_allchildrenpresent == 1))
			parent->e_allchildrenpresent = 2;

		parent->e_children = treetop;

#else
		for (temp = treetop; temp != NULLENTRY; temp=temp->e_sibling) {
			temp->e_parent = parent;

			if ((old_entry = find_sibling (temp->e_name,parent->e_child)) != NULLENTRY) {
				temp->e_leaf = FALSE;
				if ((temp->e_allchildrenpresent = old_entry->e_allchildrenpresent) < 2 )
					got_subtree = FALSE;
				temp->e_child = old_entry->e_child;
				for (sibl = temp->e_child; sibl != NULLENTRY; sibl=sibl->e_sibling) {
					sibl->e_parent = temp;
					set_inheritance (sibl);
				}
				if (old_entry->e_edbversion != NULLCP)
					temp->e_edbversion = strdup (old_entry->e_edbversion);
			} else if ( ! temp->e_leaf) {
				got_subtree = FALSE;
				temp->e_allchildrenpresent = FALSE;
			}

		}
		if (got_subtree && (treetop->e_allchildrenpresent == 1))
			treetop->e_allchildrenpresent = 2;

		got_subtree = TRUE;

		for (temp = parent->e_child; temp != NULLENTRY; temp=old_entry) {
			old_entry = temp->e_sibling;
			if (temp->e_allchildrenpresent < 2)
				got_subtree = FALSE;
			entry_free (temp);
		}
		if (got_subtree && (parent->e_allchildrenpresent == 1))
			parent->e_allchildrenpresent = 2;

		/* do we need to ripple e_allchildrenpresent higher ? */
#endif

		ps_free (ps);
#ifdef TURBO_AVL
		parent->e_children = treetop;
		return(parent);
#else
		parent->e_child = treetop;
		return (treetop);
#endif
	}

	if (dn == NULLDN) {
		parent = get_default_entry (NULLENTRY);
		parent->e_leaf = FALSE;
		parent->e_acl = acl_alloc();
		parent->e_acl->ac_child = acl_dflt ();
		parent->e_acl->ac_entry = acl_dflt ();
		parent->e_acl->ac_default = acl_dflt ();
#ifdef TURBO_AVL
		if ((treetop = getentry_block (parent,filename)) == NULLAVL) 
#else
		if ((treetop = getentry_block (parent,filename)) == NULLENTRY) 
#endif
			return (NULLENTRY);
	} else 
		treetop = getentry_block (parent,filename);

	if (parse_status != 0)
		failed = TRUE;

#ifdef TURBO_AVL
	parent->e_children = treetop;
#else
	parent->e_child = treetop;
#endif

	if (entry_load_kids (treetop,strlen (filename) - EDBLEN) == NOTOK) {
		parse_status++;
		return (NULLENTRY);
	}

	ps_free (ps);

	if (failed) {
		parse_status++;
		return (NULLENTRY);
	}

#ifdef TURBO_AVL
	return(parent);
#else
	if (dn == NULLDN)
		return (parent); /* be wary of this when calling subtree load... */
				 /* if DN == NULL - you may want the child !!! */
	else
		return (treetop);
#endif
}

int	refreshing;

refresh_from_disk(dn)
DN	dn;
{
Entry child;
Entry parent;
Entry tmp;
extern Entry database_root;

	if ((parent = local_find_entry (dn,FALSE)) == NULLENTRY)
		return (NOTOK);

	if (parent->e_data != E_DATA_MASTER)
		LLOG (log_dsap,LLOG_EXCEPTIONS, ("WARNING: refreshing SLAVE EDB file -- should not be needed"));

#ifdef TURBO_AVL
	child = (Entry) entry_cpy(parent);
	child->e_parent = parent->e_parent;
	child->e_children = parent->e_children;
	parent->e_children = NULLAVL;
#else
	child = parent->e_child;
	parent->e_child = NULLENTRY;
#endif

	refreshing = TRUE;
	if (dn == NULLDN)
		tmp = subtree_load (NULLENTRY,NULLDN);
	else
		tmp = subtree_load (parent,dn);
	refreshing = FALSE;
	if (tmp == NULLENTRY)
		return (NOTOK);

	if (dn == NULLDN) {
		database_root = child;
		directory_free (child);
		database_root = tmp;
	} else
		directory_free(child);

	return (OK);
}