OpenSolaris_b135/lib/libnisdb/nis_db.cc

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 *	nis_db.cc
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <sys/param.h>
#include <strings.h>
#include <syslog.h>
#include "nisdb_mt.h"
#include "db_headers.h"
#include "db_entry.h"
#include "db.h"
#include "db_dictionary.h"
#include "db_pickle.h"
#include "nis_db.h"
#include "nis_ldap.h"
#include "ldap_util.h"
#include "ldap_parse.h"
#include "ldap_glob.h"
#include "ldap_xdr.h"
#include "ldap_glob.h"

db_dictionary	curdict;
db_dictionary	tempdict; /* a temporary one */

db_dictionary *InUseDictionary = &curdict;
db_dictionary *FreeDictionary = &tempdict;

extern "C" {
static db_result	*db_add_entry_x(char *tab, int numattrs,
					nis_attr *attrname, entry_obj * newobj,
					int skiplog, int nosync);
db_status		db_table_exists(char *table_name);

/*
 * (Imported from rpc.nisd/nis_xx_proc.c)
 *
 * 'tbl_prototype' is used to create a table that holds a directory.
 */
static table_col cols[2] = {
	{"object", TA_BINARY+TA_XDR, 0},
	{"name", TA_CASE+TA_SEARCHABLE, 0}
};

table_obj tbl_prototype = { "DIRECTORY", 2, ' ', {2, &cols[0]}, NULL };
}

/*
 * Free resources associated with a db_result structure
 */
void
db_free_result(db_result *dr)
{
	int	i;

	if (dr == 0)
		return;

	/* Can't have valid objects */
	if (dr->status != DB_SUCCESS) {
		free(dr);
		return;
	}

	for (i = 0; i < dr->objects.objects_len; i++)
		free_entry(dr->objects.objects_val[i]);
	free(dr->objects.objects_val);
	free(dr);
}


/* Return an empty db_result structure with its status field set to 's'. */
db_result*
empty_result(db_status s)
{
	db_result * res = new db_result;
	if (res != NULL)  {
		res->status = s;
		res->nextinfo.db_next_desc_len = 0;
		res->nextinfo.db_next_desc_val = NULL;
		res->objects.objects_len = 0;
		res->objects.objects_val = NULL;
	} else {
		WARNING("nis_db::empty_result: cannot allocate space");
	}
	return (res);
}

static db_result*
set_result(db_result* res, db_status s)
{
	if (res != NULL)  {
		res->status = s;
	}
	return (res);
}

/*
 * Given a FQ object name for a table or directory, return the (db *)
 * corresponding to the object.
 */
db *
tableDB(char *tableName) {
	db_table_desc	*tbl = 0;
	char		*intName;
	db		*dbase;

	intName = internalTableName(tableName);
	if (intName == 0)
		return (0);

	dbase = InUseDictionary->find_table(intName, &tbl);

	sfree(intName);

	return (dbase);
}

extern "C" {

bool_t
db_in_dict_file(char *name)
{
	return ((bool_t) InUseDictionary->find_table_desc(name));

}

char
*db_perror(db_status dbstat)
{
	char	*str = NULL;

	switch (dbstat) {
		case DB_SUCCESS:
			str = "Success";
			break;
		case DB_NOTFOUND:
			str = "Not Found";
			break;
		case DB_BADTABLE:
			str = "Bad Table";
			break;
		case DB_BADQUERY:
			str = "Bad Query";
			break;
		case DB_BADOBJECT:
			str = "Bad Object";
			break;
		case DB_MEMORY_LIMIT:
			str = "Memory limit exceeded";
			break;
		case DB_STORAGE_LIMIT:
			str = "Database storage limit exceeded";
			break;
		case DB_INTERNAL_ERROR:
			str = "Database internal error";
			break;
		case DB_SYNC_FAILED:
			str = "Sync of log file failed";
			break;
		default:
			str = "Unknown Error";
			break;
	}
	return (str);
}

bool_t
db_extract_dict_entries(char *newdict, char **fs, int fscnt)
{
	/*
	 * Use the "FreeDictionary" ptr for the backup
	 * dictionary.
	 */
	if (!FreeDictionary->inittemp(newdict, *InUseDictionary))
		return (FALSE);
	return (InUseDictionary->extract_entries (*FreeDictionary,
		fs, fscnt));
}

bool_t
db_copy_file(char *infile, char *outfile)
{
	return (InUseDictionary->copyfile(infile, outfile));

}


/*
 * The tok and repl parameters will allow us to merge two dictionaries
 * that reference tables from different domains (master/replica in live
 * in different domains). If set to NULL, then the dictionary merge is
 * done as normal (no name changing).
 */
db_status
db_begin_merge_dict(char *newdict, char *tok, char *repl)
{
	db_status dbstat;

	/*
	 * It is assumed that InUseDictionary has already been initialized.
	 */
	dbstat = InUseDictionary->checkpoint();
	if (dbstat != DB_SUCCESS)
		return (dbstat);

	/*
	 * Use the "FreeDictionary" ptr for the backup
	 * dictionary.
	 */
	if (!FreeDictionary->init(newdict))
		return (DB_INTERNAL_ERROR);

	return (InUseDictionary->merge_dict(*FreeDictionary,
		tok, repl));
}


db_status
db_end_merge_dict()
{
	db_status	dbstat;

	dbstat = InUseDictionary->checkpoint();
	if (dbstat != DB_SUCCESS) {
		return (dbstat);
	}
	dbstat = InUseDictionary->db_shutdown();
	if (dbstat != DB_SUCCESS) {
		return (dbstat);
	}
	dbstat = FreeDictionary->db_shutdown();
	if (dbstat != DB_SUCCESS) {
		return (dbstat);
	}
	return (dbstat);
}



db_status
db_abort_merge_dict()
{
	db_status	dbstat;

	dbstat = InUseDictionary->db_shutdown();
	if (dbstat != DB_SUCCESS)
		return (dbstat);
	dbstat = FreeDictionary->db_shutdown();
	if (dbstat != DB_SUCCESS)
		return (dbstat);
}


/*
 * Initialize system (dictionary) using file 'filename'.  If system cannot
 * be read from file, it is initialized to be empty. Returns TRUE if
 * initialization succeeds, FALSE otherwise.
 * This function must be called before any other.
*/
bool_t
db_initialize(char * filename)
{
	return (InUseDictionary->init(filename));
}


/*
 * Massage the dictionary file by replacing the specified token with the
 * the replacement string. This function is needed to provide backwards
 * compatibility for providing a transportable dictionary file. The idea
 * is that rpc.nisd will call this function when it wants to change the
 * /var/nis/<hostname> strings with something like /var/nis/data.
 *
 */
db_status
db_massage_dict(char *newdictname, char *tok, char *repl)
{
	return (InUseDictionary->massage_dict(newdictname, tok, repl));
}



/*
 * Create new table using given table name and table descriptor.
 * Returns DB_SUCCESS if successful; appropriate error code otherwise.
*/
db_status
db_create_table(char * table_name, table_obj * table_desc)
{
	return (InUseDictionary->add_table(table_name, table_desc));
}

/*
 * Destroys table named by 'table_name.'  Returns DB_SUCCESS if successful,
 * error code otherwise.  Note that currently, the removed table is no
 * longer accessible from this interface and all files associated with it
 * are removed from stable storage.
*/
db_status
db_destroy_table(char * table_name)
{
	return (InUseDictionary->delete_table(table_name));
}


/*
* Return a copy of the first entry in the specified table, that satisfies
* the given attributes.  The returned structure 'db_result' contains the status,
* the  copy of the object, and a 'db_next_desc' to be used for the 'next'
* operation.
 */
db_result *
db_first_entry(char * table_name, int numattrs, nis_attr * attrname)
{
	db_result * safety = empty_result(DB_SUCCESS);
	db_table_desc * tbl = NULL;
	db * dbase = InUseDictionary->find_table(table_name, &tbl);

	if (tbl == NULL || dbase == NULL)
		return (set_result(safety, DB_BADTABLE));
	else {
		db_result * res = NULL;
		db_query *query = NULL;

		if (numattrs != 0) {
			query = InUseDictionary->translate_to_query(tbl,
					numattrs, attrname);
			if (query == NULL)
				return (set_result(safety,
						DB_BADQUERY));
		}
		res = dbase->execute(DB_FIRST, query, NULL, NULL);
		if (query) delete query;
		if (safety) delete safety;
		return (res);
	}
}

/*
 * Return a copy of the next entry in the specified table as specified by
 * the 'next_desc'.  The returned structure 'db_result' contains the status,
 * a copy of the object, and a db_next_desc to be used for a subsequent
 * 'next' operation.
*/
db_result *
db_next_entry(char * table_name, db_next_desc * next_desc)
{
	db_result * safety = empty_result(DB_SUCCESS);
	db * dbase = InUseDictionary->find_table(table_name);

	if (dbase != NULL) {
		if (safety) delete safety;
		return (dbase->execute(DB_NEXT, NULL, NULL, next_desc));
	} else
		return (set_result(safety, DB_BADTABLE));
}

/*
 * Indicate to the system that you are no longer interested in the rest of the
 * results identified by [next_desc].  After executing this operation, the
 * [next_desc] is no longer valid (cannot  be used as an argument for next).
*/

db_result *
db_reset_next_entry(char * table_name, db_next_desc * next_desc)
{
	db_result * safety = empty_result(DB_SUCCESS);
	db * dbase = InUseDictionary->find_table(table_name);

	if (dbase != NULL) {
		if (safety) delete safety;
		return (dbase->execute(DB_RESET_NEXT,
					NULL, NULL, next_desc));
	} else
		return (set_result(safety, DB_BADTABLE));
}

/*
 * Returns copies of entries that satisfy the given attributes from table.
 * Returns the status and entries in a db_result structure.
 * If no attributes are specified, DB_BADQUERY is returned.
*/
db_result *
__db_list_entries(char * table_name, int numattrs, nis_attr * attrname,
			bool_t useDeferred)
{
	db_result * safety = empty_result(DB_SUCCESS);
	db_table_desc * tbl = NULL;
	db * dbase = InUseDictionary->find_table(table_name, &tbl,
							useDeferred);

	if (tbl == NULL || dbase == NULL)
		return (set_result(safety, DB_BADTABLE));
	else {
		db_result * res = NULL;
		if (numattrs != 0) {
			db_query *query;
			query = InUseDictionary->translate_to_query(tbl,
						    numattrs, attrname);
			if (query == NULL)
				return (set_result(safety,
							DB_BADQUERY));
			res = dbase->execute(DB_LOOKUP, query,
							NULL, NULL);
			delete query;
		} else {
			res = dbase->execute(DB_ALL, NULL, NULL, NULL);
		}
		if (safety) delete safety;
		return (res);
	}
}

db_result *
db_list_entries(char *table_name, int numattrs, nis_attr *attrname) {
	return (__db_list_entries(table_name, numattrs, attrname, TRUE));
}

/*
 * Input:	A fully qualified object name (example: "x.y.z").
 * Output:	Returns the first level of the object name ("x").
 *		If 'tableP' is non-NULL, '*tableP' will contain
 *		the internal table name for "y.z".
 *
 * Both the return value and '*tableP' must be freed by the caller.
 */
char *
entryName(char *msg, char *objName, char **tableP) {
	char	*name, *table, *dir;
	char	*myself = "entryName";

	if (msg == 0)
		msg = myself;

	name = sdup(msg, T, objName);
	if (name == 0)
		return (0);

	dir = strchr(name, '.');
	if (dir == 0) {
		sfree(name);
		return (0);
	}
	*(dir++) = '\0';

	if (tableP == 0)
		return (name);

	table = internalTableName(dir);
	if (table == 0) {
		sfree(name);
		return (0);
	}

	*tableP = table;

	return (name);
}

#define	RETSTAT(obj, status) \
	{ \
		if (statP != 0) \
			*statP = status; \
		return (obj); \
	}

/*
 * Given a fully qualified object name, retrive a copy of the object,
 * using the NIS+ DB only (i.e., no LDAP). Avoids using nis_leaf_of()
 * etc., since they aren't re-entrant.
 */
nis_object *
dbFindObject(char *objName, db_status *statP) {
	char		buf[MAXPATHLEN+NIS_MAXNAMELEN+1];
	char		*name, *table = 0;
	nis_attr	attr;
	db		*dbase;
	db_result	*res;
	db_table_desc	*tbl = 0;
	db_query	*query;
	db_mindex	*mindex;
	nis_object	*o;
	int		lstat;
	char		*myself = "dbFindObject";

	if (objName == 0)
		RETSTAT(0, DB_BADQUERY);

	/* The root dir is treated specially */
	table = internalTableName(objName);
	if (table == 0)
		RETSTAT(0, DB_BADQUERY);
	if (strcmp(ROOTDIRFILE, table) == 0) {
		sfree(table);

		o = get_root_object();
		if (o == 0)
			RETSTAT(0, DB_NOTFOUND);

		RETSTAT(o, DB_SUCCESS);
	}

	/* If not the root dir, find the directory where the entry lives */

	sfree(table);
	name = entryName(myself, objName, &table);
	if (name == 0 || table == 0) {
		sfree(name);
		RETSTAT(0, DB_MEMORY_LIMIT);
	}

	dbase = InUseDictionary->find_table_noLDAP(table, &tbl, TRUE, TRUE);
	sfree(table);
	if (dbase != 0)
		mindex = dbase->mindex();
	if (dbase == 0 || tbl == 0 || mindex == 0) {
		sfree(name);
		RETSTAT(0, DB_BADTABLE);
	}

	WRITELOCKNR(mindex, lstat, "mindex w dbFindObject");
	if (lstat != 0) {
		sfree(name);
		RETSTAT(0, DB_LOCK_ERROR);
	}

	attr.zattr_ndx = "name";
	attr.zattr_val.zattr_val_val = name;
	attr.zattr_val.zattr_val_len = slen(name) + 1;

	query = InUseDictionary->translate_to_query(tbl, 1, &attr);
	if (query == 0) {
		sfree(name);
		WRITEUNLOCKNR(mindex, lstat, "mindex wu dbFindObject");
		RETSTAT(0, DB_BADQUERY);
	}

	/* Only want to look in the local DB */
	mindex->setNoLDAPquery();

	res = dbase->execute(DB_LOOKUP, query, 0, 0);

	mindex->clearNoLDAPquery();

	delete query;

	sfree(name);

	WRITEUNLOCKNR(mindex, lstat, "mindex wu dbFindObject");
	if (lstat != 0) {
		db_free_result(res);
		RETSTAT(0, DB_LOCK_ERROR);
	}

	if (res == 0)
		RETSTAT(0, DB_MEMORY_LIMIT);

	if (res->status != DB_SUCCESS) {
		db_status	st = res->status;

		db_free_result(res);
		RETSTAT(0, st);
	}

	if (res->objects.objects_len != 1 || res->objects.objects_val == 0 ||
			res->objects.objects_val[0] == 0) {
		db_free_result(res);
		RETSTAT(0, DB_BADOBJECT);
	}

	o = unmakePseudoEntryObj(res->objects.objects_val[0], 0);

	db_free_result(res);

	if (o == 0) {
		RETSTAT(0, DB_BADOBJECT);
	}

	RETSTAT(o, DB_SUCCESS);
}

/*
 * Return the object specified by 't' or 'objName' from LDAP. Set
 * the LDAP status in '*statP'.
 */
nis_object *
ldapFindObj(__nis_table_mapping_t *t, char *objName, int *statP) {
	nis_object	*o;
	int		stat;
	char		*myself = "ldapFindObj";

	if (t == 0) {
		char	*table, tbuf[MAXPATHLEN + NIS_MAXNAMELEN + 1];

		if (objName == 0) {
			if (statP != 0)
				*statP = LDAP_PARAM_ERROR;
			return (0);
		}

		/* Look for mapping */
		table = internal_table_name(objName, tbuf);
		if (table == 0) {
			if (statP != 0)
				*statP = LDAP_PARAM_ERROR;
			return (0);
		}

		t = (__nis_table_mapping_t *)__nis_find_item_mt(table,
						&ldapMappingList, 0, 0);
		if (t == 0) {
			/* Not really an error; just not mapped */
			*statP = LDAP_SUCCESS;
			return (0);
		}
	}

	o = 0;
	stat = objFromLDAP(t, &o, 0, 0);

	if (statP != 0)
		*statP = stat;

	return (o);
}

/*
 * Look for the specified object, first locally, then in LDAP.
 */
nis_object *
findObj(char *name, db_status *statP, int *lstatP) {
	nis_object	*o;
	db_status	stat = DB_SUCCESS;
	int		lstat = LDAP_SUCCESS;
	char		*myself = "findObj";

	o = dbFindObject(name, &stat);

	if (o == 0) {
		if (stat != DB_NOTFOUND)
			logmsg(MSG_NOTIMECHECK, LOG_INFO,
				"%s: DB error %d looking for \"%s\"",
				myself, stat, NIL(name));

		o = ldapFindObj(0, name, &lstat);
		if (o == 0) {
			if (lstat != LDAP_SUCCESS &&
					lstat != LDAP_NO_SUCH_OBJECT)
				logmsg(MSG_NOTIMECHECK, LOG_INFO,
				"%s: LDAP error looking for \"%s\": %s",
					myself, NIL(name),
					ldap_err2string(lstat));
		}
	}

	if (statP != 0)
		*statP = stat;
	if (lstatP != 0)
		*lstatP = lstat;

	return (o);
}

/*
 * Delete the specified object from the local DB.
 */
db_status
dbDeleteObj(char *objName) {
	nisdb_tsd_t	*tsd = __nisdb_get_tsd();
	nis_object	*o;
	db_status	stat;
	nisdb_obj_del_t	*nod, *tmp;
	int		xid;
	char		*myself = "dbDeleteObj";

	if (objName == 0)
		return (DB_SUCCESS);

	/*
	 * Since in-structure locks can't completely protect
	 * during structure deletion, we just note that the
	 * object should be deleted, and leave that for a
	 * (slightly) later time in rpc.nisd, where we can
	 * use the rpc.nisd's table/directory locks for
	 * protection.
	 */

	if (tsd == 0)
		return (DB_INTERNAL_ERROR);

	o = dbFindObject(objName, &stat);
	if (o == 0) {
		if (stat == DB_NOTFOUND)
			return (DB_SUCCESS);
		else
			return (stat);
	}

	/*
	 * In order to prevent a chicken-and-egg problem (if the
	 * object doesn't exist in LDAP, is that because we just
	 * haven't written it to LDAP yet, or because it's been
	 * removed), we only allow object deletion if we're the
	 * master for it.
	 */

	nod = (nisdb_obj_del_t *)am(myself, sizeof (*nod));
	if (nod == 0) {
		nis_destroy_object(o);
		return (DB_MEMORY_LIMIT);
	}

	nod->objType = o->zo_data.zo_type;
	nis_destroy_object(o);

	nod->objName = sdup(myself, T, objName);
	if (nod->objName == 0) {
		sfree(nod);
		return (DB_MEMORY_LIMIT);
	}

	/* Check for a dup */
	for (tmp = tsd->objDelList; tmp != 0;
			tmp = (nisdb_obj_del_t *)tmp->next) {
		if (strcmp(nod->objName, tmp->objName) == 0) {
			sfree(nod->objName);
			sfree(nod);
			return (DB_SUCCESS);
		}
	}

	/* Insert at start of list */
	nod->next = tsd->objDelList;
	tsd->objDelList = nod;

	return (DB_SUCCESS);
}

/*
 * Touch (i.e., update the expiration time for) the specified object.
 */
db_status
dbTouchObj(char *objName) {
	char		*ent, *table;
	db		*dbase;
	db_table_desc	*tbl = 0;
	db_mindex	*mindex;
	nis_attr	attr;
	db_query	*query;
	db_status	stat;
	char		*myself = "dbTouchObj";

	table = internalTableName(objName);
	if (table == 0)
		return (DB_BADQUERY);

	if (strcmp(ROOTDIRFILE, table) == 0) {
		sfree(table);

		if (touchRootDir() == 0)
			return (DB_SUCCESS);
		else
			return (DB_INTERNAL_ERROR);
	}

	sfree(table);
	table = 0;
	ent = entryName(myself, objName, &table);
	if (ent == 0 || table == 0) {
		sfree(ent);
		return (DB_MEMORY_LIMIT);
	}

	dbase = InUseDictionary->find_table(table, &tbl, TRUE);
	if (dbase != 0)
		mindex = dbase->mindex();
	if (dbase == 0 || tbl == 0 || mindex == 0) {
		sfree(ent);
		sfree(table);
		return (DB_BADTABLE);
	}

	attr.zattr_ndx = "name";
	attr.zattr_val.zattr_val_val = ent;
	attr.zattr_val.zattr_val_len = slen(ent) + 1;

	query = InUseDictionary->translate_to_query(tbl, 1, &attr);
	if (query == 0) {
		sfree(ent);
		sfree(table);
		return (DB_BADQUERY);
	}

	mindex->touchEntry(query);

	sfree(ent);
	sfree(table);
	delete query;

	return (DB_SUCCESS);
}

/*
 * Create a NIS_TABLE_OBJ.
 * Borrows heavily from rpc.nisd/nis_db.c:__create_table().
 */
db_status
dbCreateTable(char *intName, nis_object *obj) {
	table_col	tc[NIS_MAXCOLUMNS+1];
	table_obj	tobj, *t;
	int		i;
	char		*myself = "dbCreateTable";

	if (intName == 0 || obj == 0)
		return (DB_BADTABLE);

	t = &(obj->TA_data);

	/* Make sure there are searchable columns */
	for (i = 0; i < t->ta_cols.ta_cols_len; i++) {
		if (t->ta_cols.ta_cols_val[i].tc_flags & TA_SEARCHABLE)
			break;
	}
	if (i >= t->ta_cols.ta_cols_len) {
		logmsg(MSG_NOTIMECHECK, LOG_INFO,
			"%s: No searchable columns in \"%s\" (\"%s\")",
			myself, NIL(obj->zo_name), NIL(intName));
		return (DB_BADTABLE);
	}

	tobj = *t;
	/* Shift columns one step right */
	for (i = 0; i < tobj.ta_cols.ta_cols_len; i++) {
		tc[i+1] = tobj.ta_cols.ta_cols_val[i];
	}
	tc[0].tc_name = 0;
	tc[0].tc_flags = TA_XDR | TA_BINARY;
	tc[0].tc_rights = 0;
	tobj.ta_cols.ta_cols_len += 1;
	tobj.ta_cols.ta_cols_val = tc;

	return (db_create_table(intName, &tobj));
}

#define	TABLE_COL(o, n)	o->TA_data.ta_cols.ta_cols_val[n]

/*
 * Refresh (if necessary, create), the specified object in the local DB.
 */
db_status
dbRefreshObj(char *name, nis_object *o) {
	char		*objName;
	__nis_buffer_t	b = {0, 0};
	nis_object	*curObj;
	db_status	stat;
	char		*ent, *table, *objTable;
	int		rstat, isDir = 0, isTable = 0;
	char		*myself = "refreshObj";

	if (o == 0)
		/* Delete it */
		return (dbDeleteObj(name));

	/* We don't work on entry objects */
	if (o->zo_data.zo_type == NIS_ENTRY_OBJ)
		return (DB_BADOBJECT);

	if (name != 0)
		objName = name;
	else {
		bp2buf(myself, &b, "%s.%s", NIL(o->zo_name), NIL(o->zo_domain));
		objName = b.buf;
	}

	curObj = dbFindObject(objName, &stat);
	if (curObj == 0 && stat != DB_NOTFOUND) {
		sfree(b.buf);
		return (stat);
	}

	/*
	 * If the object doesn't change, just touch it to update the
	 * expiration time.
	 */
	if (curObj != 0) {
		if (sameNisPlusObj(o, curObj)) {
			sfree(b.buf);
			nis_destroy_object(curObj);
			return (dbTouchObj(objName));
		}

		/* Otherwise, check that the name and type is the same */
		if (o->zo_data.zo_type != curObj->zo_data.zo_type ||
			o->zo_name == 0 || curObj->zo_name == 0 ||
			o->zo_domain == 0 || curObj->zo_domain == 0 ||
			strcmp(o->zo_name, curObj->zo_name) != 0 ||
			strcmp(o->zo_domain, curObj->zo_domain) != 0) {
			sfree(b.buf);
			nis_destroy_object(curObj);
			return (DB_BADOBJECT);
		}

		/*
		 * If the object is a table, we can't allow the scheme
		 * to change.
		 */
		if (o->zo_data.zo_type == NIS_TABLE_OBJ) {
			int	i;

			if (o->TA_data.ta_maxcol !=
					curObj->TA_data.ta_maxcol) {
				sfree(b.buf);
				nis_destroy_object(curObj);
				return (DB_BADOBJECT);
			}

			for (i = 0; i < o->TA_data.ta_maxcol; i++) {
				if ((TABLE_COL(o, i).tc_flags &
						TA_SEARCHABLE) !=
					(TABLE_COL(curObj, i).tc_flags &
						TA_SEARCHABLE)) {
					sfree(b.buf);
					nis_destroy_object(curObj);
					return (DB_BADOBJECT);
				}
			}
		}
	} else {
		/*
		 * If we're creating a directory object, make a note
		 * so that we can add it to the serving list and create
		 * the disk file. Similarly, if creating a table, we
		 * also need to create the disk file.
		 */
		if (o->zo_data.zo_type == NIS_DIRECTORY_OBJ)
			isDir = 1;
		else if (o->zo_data.zo_type == NIS_TABLE_OBJ)
			isTable = 1;
	}

	objTable = internalTableName(objName);
	if (objTable == 0) {
		sfree(b.buf);
		if (curObj != 0)
			nis_destroy_object(curObj);
		return (DB_BADQUERY);
	}

	if (strcmp(ROOTDIRFILE, objTable) == 0) {
		sfree(objTable);

		rstat = update_root_object(ROOTOBJFILE, o);
		if (rstat == 1)
			stat = DB_SUCCESS;
		else
			stat = DB_INTERNAL_ERROR;
	} else {
		nis_attr	attr;
		entry_object	*e, eo;
		entry_col	ec[2];
		db		*dbase;
		db_table_desc	*tbl = 0;
		db_mindex	*mindex;
		db_result	*dbres;
		int		lstat;

		/* Find parent */
		ent = entryName(myself, objName, &table);
		if (ent == 0 || table == 0) {
			sfree(b.buf);
			sfree(objTable);
			sfree(ent);
			if (curObj != 0)
				nis_destroy_object(curObj);
			return (DB_MEMORY_LIMIT);
		}

		/*
		 * Calling vanilla find_table() here (which might go to
		 * LDAP and recurse back to ourselves) so that it should
		 * work to create a hierarchy of directories.
		 */
		dbase = InUseDictionary->find_table(table, &tbl, TRUE);
		if (dbase != 0)
			mindex = dbase->mindex();
		if (dbase == 0 || tbl == 0 || mindex == 0) {
			sfree(b.buf);
			sfree(objTable);
			sfree(ent);
			sfree(table);
			if (curObj != 0)
				nis_destroy_object(curObj);
			return (DB_BADTABLE);
		}

		/* Construct suitable nis_attr and entry_object */
		attr.zattr_ndx = "name";
		attr.zattr_val.zattr_val_val = ent;
		attr.zattr_val.zattr_val_len = slen(ent) + 1;

		ec[1].ec_flags = 0;
		ec[1].ec_value.ec_value_val = ent;
		ec[1].ec_value.ec_value_len = attr.zattr_val.zattr_val_len;

		eo.en_type = "IN_DIRECTORY";
		eo.en_cols.en_cols_val = ec;
		eo.en_cols.en_cols_len = 2;

		e = makePseudoEntryObj(o, &eo, 0);
		if (e == 0) {
			sfree(objTable);
			sfree(table);
			sfree(ent);
			if (curObj != 0)
				nis_destroy_object(curObj);
			return (DB_INTERNAL_ERROR);
		}

		/* Only want to update the local DB */

		WRITELOCKNR(mindex, lstat, "mindex w dbRefreshObj");
		if (lstat != 0) {
			sfree(objTable);
			sfree(table);
			sfree(ent);
			if (curObj != 0)
				nis_destroy_object(curObj);
			return (DB_LOCK_ERROR);
		}
		mindex->setNoWriteThrough();
		mindex->setNoLDAPquery();

		dbres = db_add_entry_x(table, 1, &attr, e, 0, 0);

		mindex->clearNoLDAPquery();
		mindex->clearNoWriteThrough();
		WRITEUNLOCKNR(mindex, lstat, "mindex wu dbRefreshObj");
		if (lstat != 0) {
			sfree(objTable);
			sfree(table);
			sfree(ent);
			if (curObj != 0)
				nis_destroy_object(curObj);
			db_free_result(dbres);
			return (DB_LOCK_ERROR);
		}

		sfree(ent);
		sfree(table);

		if (dbres == 0)
			stat = DB_MEMORY_LIMIT;
		else
			stat = dbres->status;

		db_free_result(dbres);

		/*
		 * If successful so far, add the transaction.
		 */
		if (stat == DB_SUCCESS) {
			int		xid, st;
			db_status	ds;
			nis_object	*dirObj;

			/* Find the directory where this is added */
			dirObj = dbFindObject(o->zo_domain, &ds);
			if (dirObj == 0) {
				sfree(objTable);
				if (curObj != 0)
					nis_destroy_object(curObj);
				return (ds);
			}

			xid = beginTransaction();
			if (xid == 0) {
				sfree(objTable);
				if (curObj != 0)
					nis_destroy_object(curObj);
				nis_destroy_object(dirObj);
				return (DB_INTERNAL_ERROR);
			}

			st = addUpdate((curObj == 0) ? ADD_NAME : MOD_NAME_NEW,
					objName, 0, 0, o, curObj, 0);
			if (st != 0) {
				(void) abort_transaction(xid);
				sfree(objTable);
				if (curObj != 0)
					nis_destroy_object(curObj);
				nis_destroy_object(dirObj);
				return (DB_INTERNAL_ERROR);
			}

			st = endTransaction(xid, dirObj);
			if (st != 0)
				stat = DB_INTERNAL_ERROR;

			if (curObj != 0)
				nis_destroy_object(curObj);
			nis_destroy_object(dirObj);
		}

		/*
		 * If it's a table or directory, create the DB file.
		 * If a directory, also add it to the serving list.
		 */
		if (stat == DB_SUCCESS &&(isDir || isTable)) {
			if (isDir) {
				stat = db_create_table(objTable,
							&tbl_prototype);
			} else {
				stat = dbCreateTable(objTable, o);
			}
		}
		sfree(objTable);
	}

	sfree(b.buf);

	return (stat);
}

/*
 * Replace the object stored with the mapping 't'. Return TRUE if
 * at least one object was replaced, FALSE otherwise.
 */
bool_t
replaceMappingObj(__nis_table_mapping_t *t, nis_object *n) {
	__nis_table_mapping_t	*x;
	nis_object		*old = 0;
	int			assigned = 0;

	/*
	 * The alternate mappings are usually mostly copies
	 * of the original, so we try to make sure that we
	 * don't free the same nis_object twice.
	 */
	for (x = t; x != 0; x = (__nis_table_mapping_t *)x->next) {
		if (old == 0) {
			old = x->obj;
			if (x->obj != 0)
				nis_destroy_object(x->obj);
		} else {
			if (x->obj != old && x->obj != 0)
				nis_destroy_object(x->obj);
		}
		x->obj = n;
		assigned++;
	}

	return (assigned > 0);
}

/*
 * Set object type, column info, and obj for the specified
 * mapping 't' from the object 'o'. Returns zero if 'o' was unused,
 * and should be freed by the caller, larger than zero otherwise.
 */
int
setMappingObjTypeEtc(__nis_table_mapping_t *t, nis_object *o) {
	__nis_table_mapping_t	*x;
	int			ls, ret;
	int	                i;

	if (t == 0 || o == 0)
		return (0);

	t->objType = o->zo_data.zo_type;
	for (x = t; x != 0; x = (__nis_table_mapping_t *)x->next) {
		if (x != t) {
			x->objType = t->objType;
		}
		if (x->objType == NIS_TABLE_OBJ) {
			/*
			 * If we have rules, this mapping is for table entries,
			 * and we need the column names. Otherwise, remove the
			 * column names (if any).
			 */

                        for (i = 0; i < x->numColumns; i++)
			sfree(x->column[i]);
		        sfree(x->column);
			x->column = 0;
			x->numColumns = 0;
		}
	}
	ret = replaceMappingObj(t, o);

	return (ret);
}

/*
 * Retrieve the specified object (internal DB name) from LDAP, and
 * refresh/create as appropriate.
 */
db_status
dbCreateFromLDAP(char *intName, int *ldapStat) {
	__nis_table_mapping_t	*t;
	int			lstat, doDestroy;
	nis_object		*obj = 0;
	db_status		dstat;
	char			*myself = "dbCreateFromLDAP";

	if (!useLDAPrespository) {
		if (ldapStat != 0)
			*ldapStat = LDAP_SUCCESS;
		return (DB_SUCCESS);
	}

	t = (__nis_table_mapping_t *)__nis_find_item_mt(intName,
							&ldapMappingList,
							0, 0);

	/* No mapping isn't a failure */
	if (t == 0) {
		if (ldapStat != 0)
			*ldapStat = LDAP_SUCCESS;
		return (DB_NOTFOUND);
	}

	lstat = objFromLDAP(t, &obj, 0, 0);
	if (ldapStat != 0)
		*ldapStat = lstat;
	if (lstat != LDAP_SUCCESS)
		return (DB_NOTFOUND);

	/*
	 * If the LDAP operation was successful, but 'obj' is NULL,
	 * there's no mapping for this object, and we're done.
	 */
	if (obj == 0)
		return (DB_SUCCESS);

	/* Update the mapping with object info */
	doDestroy = setMappingObjTypeEtc(t, obj) == 0;

	dstat = dbRefreshObj(t->objName, obj);

	if (doDestroy)
		nis_destroy_object(obj);

	return (dstat);
}

/*
 * Up- (fromLDAP==0) or down- (fromLDAP==1) load all LDAP mapped data.
 * Returns an LDAP error status.
 */
int
loadAllLDAP(int fromLDAP, void *cookie, db_status *dstatP) {
	__nis_table_mapping_t	*t, *start;
	int			stat = LDAP_SUCCESS;
	db_status		dstat = DB_SUCCESS;
	db			*dbase;
	db_table_desc		*tbl = 0;
	db_mindex		*mindex;
	char			*myself = "loadAllLDAP";

	/*
	 * If the 'cookie' and '*cookie' are non-NULL, start scanning
	 * the mappings from '*cookie'. When we return with an error,
	 * we set '*cookie' to point to the mapping being processed.
	 * This enables our caller to react appropriately, and retry
	 * if desired.
	 *
	 * The cookie is opaque to our caller, who's only allowed to
	 * initialize *cookie to NULL.
	 */
	if (cookie != 0) {
		start = *((__nis_table_mapping_t **)cookie);
		if (start == 0)
			start = ldapMappingSeq;
	} else {
		start = ldapMappingSeq;
	}

	for (t = start; t != 0; t = (__nis_table_mapping_t *)t->seqNext) {
		__nis_table_mapping_t	**tp;
		int			nm;

		if (fromLDAP) {
			/* Are there any mappings for the object proper ? */
			tp = selectTableMapping(t, 0, 0, 1, t->dbId, &nm);
			if (tp != 0 && nm > 0) {
				dstat = dbCreateFromLDAP(t->objPath, &stat);
				if (dstat != DB_SUCCESS) {
					logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: DB error %d creating \"%s\": %s",
						myself, dstat, NIL(t->objName),
						ldap_err2string(stat));
					if (cookie != 0)
						*((__nis_table_mapping_t **)
							cookie) = t;
					if (dstatP != 0)
						*dstatP = dstat;
					else if (stat == LDAP_SUCCESS)
						stat = LDAP_OPERATIONS_ERROR;
					sfree(tp);
					return (stat);
				}
			}
			sfree(tp);

			/* Any mappings for table entries ? */
			tp = selectTableMapping(t, 0, 0, 0, t->dbId, &nm);
			if (tp == 0 || nm <= 0) {
				sfree(tp);
				continue;
			}
			sfree(tp);

			/*
			 * The object itself must exist in the local
			 * DB by now. Get the db_mindex and let
			 * db_mindex::queryLDAP() do the work; if
			 * the object isn't a table, queryLDAP()
			 * will do nothing and return success.
			 */
			dbase = InUseDictionary->find_table(t->objPath,
							&tbl, TRUE);
			if (dbase != 0)
				mindex = dbase->mindex();
			if (dbase == 0 || tbl == 0 || mindex == 0) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: No local DB entry for \"%s\" (%s:%s)",
					myself, NIL(t->objPath),
					NIL(t->dbId), NIL(t->objName));
				if (cookie != 0)
					*((__nis_table_mapping_t **)cookie) =
						t;
				if (dstatP != 0)
					*dstatP = DB_BADTABLE;
				return ((dstatP != 0) ?
					LDAP_SUCCESS : LDAP_OPERATIONS_ERROR);
			}
			mindex->setInitialLoad();
			stat = mindex->queryLDAP(0, t->dbId, 0);
			mindex->clearInitialLoad();
			if (stat != LDAP_SUCCESS) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"%s: LDAP error retrieving entries for %s:%s: %s",
					myself, NIL(t->dbId), NIL(t->objName),
					ldap_err2string(stat));
				if (cookie != 0)
					*((__nis_table_mapping_t **)cookie) =
						t;
				if (dstatP != 0)
					*dstatP = DB_SUCCESS;
				return (stat);
			}
		} else {
			nis_object	*obj;
			char		*ent, *objPath;
			int		freeObjPath = 0;

			/*
			 * Up-loading to LDAP, so the object must
			 * already exist in the local DB.
			 */
			obj = dbFindObject(t->objName, &dstat);
			if (obj == 0) {
				if (dstat == DB_NOTFOUND)
					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
		"%s: No local DB object for \"%s\" (%s:%s); skipping up-load",
						myself, NIL(t->objPath),
						NIL(t->dbId),
						NIL(t->objName));
				else
					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
			"%s: DB error %d for \"%s\" (%s:%s); skipping up-load",
						myself, dstat,
						NIL(t->objPath),
						NIL(t->dbId),
						NIL(t->objName));
				continue;
			}

			/*
			 * If it's a table or directory, there will be
			 * a dictionary entry for the object itself.
			 * Otherwise, we need the dictionary entry for
			 * the parent directory.
			 *
			 * For a table, we need the db_mindex for both the
			 * table object itself, as well as for the parent
			 * directory (in order to store table entries).
			 * We start with the latter.
			 */
			if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) {
				objPath = t->objPath;
				ent = 0;
			} else {
				objPath = 0;
				ent = entryName(myself, t->objName, &objPath);
				if (ent == 0 || objPath == 0) {
					logmsg(MSG_NOTIMECHECK, LOG_ERR,
	"%s: Error deriving entry/DB-table names for %s:%s; skipping up-load",
						myself, NIL(t->dbId),
						NIL(t->objName));
					sfree(ent);
					sfree(objPath);
					nis_destroy_object(obj);
					obj = 0;
					continue;
				}
				freeObjPath = 1;
			}

			dbase = InUseDictionary->find_table(objPath,
							&tbl, TRUE);
			if (dbase != 0)
				mindex = dbase->mindex();
			if (dbase == 0 || tbl == 0 || mindex == 0) {
				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
		"%s: No local DB entry for \"%s\" (%s:%s); skipping up-load",
					myself, objPath,
					NIL(t->dbId), NIL(t->objName));
				sfree(ent);
				if (freeObjPath)
					sfree(objPath);
				nis_destroy_object(obj);
				obj = 0;
				continue;
			}

			/*
			 * Our next action(s) depend on the object type:
			 *
			 *	directory	Store dir object
			 *
			 *	table		Store table obj, as well
			 *			as any entries in the
			 *			table
			 *
			 *	other		Store object; we need to
			 *			build a db_query specifying
			 *			the first-level name of the
			 *			object.
			 *
			 * storeLDAP() will just do nothing and return
			 * success if we try to, say, store a table object
			 * when only the table entries are mapped. Hence,
			 * we don't have to worry about those distinctions
			 * here.
			 */
			if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) {
				stat = mindex->storeLDAP(0, 0, obj, 0, t->dbId);
			} else {
				nis_attr	attr;
				db_query	*q;

				attr.zattr_ndx = "name";
				attr.zattr_val.zattr_val_val = ent;
				attr.zattr_val.zattr_val_len = slen(ent) + 1;

				q = new db_query(mindex->getScheme(), 1, &attr);
				if (q == 0) {
					logmsg(MSG_NOTIMECHECK, LOG_ERR,
	"%s: error creating db_query for \"%s\" in \"%s\"; skipping up-load",
						myself, ent, objPath);
					sfree(ent);
					if (freeObjPath)
						sfree(objPath);
					nis_destroy_object(obj);
					obj = 0;
					continue;
				}

				stat = mindex->storeLDAP(q, 0, obj, 0, t->dbId);

				delete q;

			}

			sfree(ent);
			if (freeObjPath)
				sfree(objPath);

			if (stat != LDAP_SUCCESS) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"%s: Error storing %s:%s to LDAP: %s",
					myself, NIL(t->dbId), NIL(t->objName),
					ldap_err2string(stat));
				nis_destroy_object(obj);
				obj = 0;
				if (cookie != 0)
					*((__nis_table_mapping_t **)
						cookie) = t;
				if (dstatP != 0)
					*dstatP = DB_SUCCESS;
				return (stat);
			}

			/* Any mappings for table entries ? */
			tp = selectTableMapping(t, 0, 0, 0, t->dbId, &nm);
			if (tp == 0 || nm <= 0) {
				sfree(tp);
				nis_destroy_object(obj);
				obj = 0;
				continue;
			}
			sfree(tp);

			/*
			 * If it's a table, we also need to store the table
			 * entries.
			 */
			if (obj->zo_data.zo_type == NIS_TABLE_OBJ) {
				tbl = 0;
				dbase = InUseDictionary->find_table(t->objPath,
								&tbl, TRUE);
				if (dbase != 0)
				mindex = dbase->mindex();
				if (dbase == 0 || tbl == 0 || mindex == 0) {
					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
	"%s: No local DB entry for \"%s\" (%s:%s); skipping entry up-load",
						myself, NIL(t->objPath),
						NIL(t->dbId), NIL(t->objName));
					nis_destroy_object(obj);
					obj = 0;
					continue;
				}

				stat = mindex->storeLDAP(0, 0, obj, 0, t->dbId);

				if (stat != LDAP_SUCCESS) {
					logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: Error storing %s:%s entries to LDAP: %s",
						myself, NIL(t->dbId),
						NIL(t->objName),
						ldap_err2string(stat));
					nis_destroy_object(obj);
					obj = 0;
					if (cookie != 0)
						*((__nis_table_mapping_t **)
							cookie) = t;
					if (dstatP != 0)
						*dstatP = DB_SUCCESS;
					return (stat);
				}
			}
			nis_destroy_object(obj);
			obj = 0;
		}
	}

	if (dstatP != 0)
		*dstatP = dstat;
	return (stat);
}

/*
 * Object identified by given attribute name is added to specified table.
 * If object already exists, it is replaced.  If more than one object
 * matches the given attribute name, DB_NOTUNIQUE is returned.
 */
static
db_result *
db_add_entry_x(char * tab, int numattrs, nis_attr * attrname,
		entry_obj * newobj, int skiplog, int nosync)
{
	db_result * safety = empty_result(DB_SUCCESS);
	db_table_desc * tbl = NULL;
	db * dbase = InUseDictionary->find_table(tab, &tbl, FALSE);

	if (tbl == NULL || dbase == NULL) {
		return (set_result(safety, DB_BADTABLE));
	} else if (skiplog) {
		db_result * res;
		res = dbase->execute(DB_ADD_NOLOG, NULL,
			    (entry_object *) newobj, NULL);
		if (safety) delete safety;
		return (res);
	} else {
		db_result *res;
		db_query *
		query = InUseDictionary->translate_to_query(tbl,
						numattrs, attrname);
		if (query == NULL)
			return (set_result(safety, DB_BADQUERY));
		if (nosync)
			res = dbase->execute(DB_ADD_NOSYNC,
				query, (entry_object *) newobj, NULL);
		else
			res = dbase->execute(DB_ADD, query,
				(entry_object *) newobj, NULL);
		delete query;
		if (safety) delete safety;
		return (res);
	}
}

db_result *
db_add_entry(char * tab, int numattrs, nis_attr * attrname,
		entry_obj * newobj)
{
	return (db_add_entry_x(tab, numattrs, attrname, newobj, 0, 0));
}

db_result *
__db_add_entry_nolog(char * tab, int numattrs, nis_attr * attrname,
		entry_obj * newobj)
{
	return (db_add_entry_x(tab, numattrs, attrname, newobj, 1, 0));
}

db_result *
__db_add_entry_nosync(char * tab, int numattrs, nis_attr * attrname,
			entry_obj * newobj)
{
	return (db_add_entry_x(tab, numattrs, attrname, newobj, 0, 1));
}

/*
 * Remove object identified by given attributes from specified table.
 * If no attribute is supplied, all entries in table are removed.
 * If attributes identify more than one object, all objects are removed.
*/

db_result *
db_remove_entry_x(char * table_name, int num_attrs, nis_attr * attrname,
			int nosync)
{
	db_result * safety = empty_result(DB_SUCCESS);
	db_table_desc * tbl = NULL;
	db * dbase = InUseDictionary->find_table(table_name, &tbl, FALSE);
	db_result * res;

	if (tbl == NULL || dbase == NULL)
		return (set_result(safety, DB_BADTABLE));
	else {
		if (num_attrs != 0) {
			db_query *query;
			query = InUseDictionary->translate_to_query(tbl,
					num_attrs, attrname);
			if (query == NULL)
				return (set_result(safety,
						DB_BADQUERY));
			if (nosync)
				res = dbase->execute(DB_REMOVE_NOSYNC,
						query, NULL, NULL);
			else
				res = dbase->execute(DB_REMOVE, query,
						NULL, NULL);
			delete query;
		} else {
			if (nosync)
				res = dbase->execute(DB_REMOVE_NOSYNC,
					NULL, NULL, NULL);
			else
				res = dbase->execute(DB_REMOVE,
					NULL, NULL, NULL);
		}
		if (safety) delete safety;
		return (res);
	}
}

db_result *
db_remove_entry(char * table_name, int num_attrs, nis_attr * attrname)
{
	return (db_remove_entry_x(table_name, num_attrs, attrname, 0));
}

db_result *
__db_remove_entry_nosync(char * table_name, int num_attrs, nis_attr * attrname)
{
	return (db_remove_entry_x(table_name, num_attrs, attrname, 1));
}

/* Return a copy of the version of specified table. */
vers *
db_version(char * table_name)
{
	db * dbase = InUseDictionary->find_table(table_name);

	if (dbase == NULL)
		return (NULL);
	vers* v = new vers(dbase->get_version());
	if (v == NULL)
		WARNING("nis_db::db_version: cannot allocate space");
	return (v);
}

/* Return log entries since (later than) given version 'v' of table. */
db_log_list *
db_log_entries_since(char * table_name, vers * v)
{
	db * dbase = InUseDictionary->find_table(table_name);

	if (dbase == NULL)
		return (NULL);
	return (dbase->get_log_entries_since(v));
}

db_status
db_sync_log(char *table_name) {

	db * dbase = InUseDictionary->find_table(table_name);

	if (dbase == NULL)
		return (DB_BADTABLE);
	return (dbase->sync_log());
}

/*
 * Apply the given update specified in 'entry' to the specified table.
 * Returns DB_SUCCESS if update was executed.
 * Returns DB_NOTFOUND if update occurs too early to be applied.
*/
db_status
db_apply_log_entry(char * table_name, db_log_entry * entry)
{
	db * dbase = InUseDictionary->find_table(table_name, NULL, FALSE);

	if (dbase == NULL)
		return (DB_BADTABLE);
	if (dbase->execute_log_entry(entry))
		return (DB_SUCCESS);   /* got executed */
	else
		return (DB_NOTFOUND);  /* not executed */
}

/*
 * Checkpoint specified table (i.e. incorporate logged updates to main
 * database file).  If table_name is NULL, checkpoint all tables that
 * needs it.
*/
db_status
db_checkpoint(char * table_name)
{
	return (InUseDictionary->db_checkpoint(table_name));
}

/* Print names of tables in system. */
void
db_print_table_names()
{
	int i;
	db_table_names * answer = InUseDictionary->get_table_names();

	if (answer != NULL) {
		for (i = 0; i < answer->db_table_names_len; i++) {
			printf("%s\n", answer->db_table_names_val[i]);
			delete answer->db_table_names_val[i];
		}
		delete answer->db_table_names_val;
		delete answer;
	}
}

/* Print statistics of specified table to stdout. */
db_status
db_stats(char * table_name)
{
	db_table_desc * tbl = NULL;
	db *dbase = InUseDictionary->find_table(table_name, &tbl);

	if (tbl == NULL || dbase == NULL || tbl->scheme == NULL)
		return (DB_BADTABLE);

	dbase->print();
	tbl->scheme->print();
	return (DB_SUCCESS);
}


/* Print statistics of indices of specified table to stdout. */
db_status
db_print_all_indices(char * table_name)
{
	db * dbase = InUseDictionary->find_table(table_name);

	if (dbase == NULL)
		return (DB_BADTABLE);
	dbase->print_all_indices();
	return (DB_SUCCESS);
}

/* Print specified index of table to stdout. */
db_status
db_print_index(char * table_name, int which)
{
	db * dbase = InUseDictionary->find_table(table_name);

	if (dbase == NULL)
		return (DB_BADTABLE);
	dbase->print_index(which);
	return (DB_SUCCESS);
}

/* close open files */
db_status
db_standby(char * table_name)
{
	return (InUseDictionary->db_standby(table_name));
}

/* Returns DB_SUCCESS if table exists; DB_BADTABLE if table does not exist. */
db_status
db_table_exists(char * table_name)
{
	db_table_desc *dbtab = InUseDictionary->find_table_desc(table_name);

	if (dbtab == NULL)
		return (DB_BADTABLE);
	return (DB_SUCCESS);
}

/*
 * Returns DB_SUCCESS if table exists; DB_BADTABLE if table does not exist.
 *  If table already loaded, unload it.
*/
db_status
db_unload_table(char * table_name)
{
	db_table_desc *
	dbtab = InUseDictionary->find_table_desc(table_name);
	if (dbtab == NULL)
		return (DB_BADTABLE);
	// unload
	if (dbtab->database != NULL) {
		delete dbtab->database;
		dbtab->database = NULL;
	}
	return (DB_SUCCESS);
}

/*
 * Put the specified table in deferred mode, which means that updates go
 * to the original table, but reads are satisfied out of a copy (which we
 * make here). Thus, "defer" refers to the table as seen by read requests,
 * since for them, changes are deferred.
 */
db_status
__db_defer(char *table_name) {
	db_status	stat;

	stat = InUseDictionary->defer(table_name);
	return (stat);
}

/*
 * Commit deferred changes for the specified table. I.e., make visible
 * any updates made since the table was deferred.
 */
db_status
__db_commit(char *table_name) {
	db_status	stat;

	stat = InUseDictionary->commit(table_name);
	return (stat);
}

/*
 * Rollback, i.e., return to the state before we entered deferred mode.
 */
db_status
__db_rollback(char *table_name) {
	db_status	stat;

	stat = InUseDictionary->rollback(table_name);
	return (stat);
}

db_status
__db_configure(char *table_name) {
	db_status	stat;
	char		tablePath[MAXPATHLEN + NIS_MAXNAMELEN + 1];
	db		*dbase = InUseDictionary->find_table(table_name, NULL);

	if (dbase == NULL || table_name == 0)
		return (DB_BADTABLE);

	if (strlen(table_name) >= sizeof (tablePath))
		return (DB_BADQUERY);

	if (internal_table_name(table_name, tablePath) == 0)
		return (DB_STORAGE_LIMIT);

	if (dbase->configure(tablePath))
		stat = DB_SUCCESS;
	else
		stat = DB_INTERNAL_ERROR;

	return (stat);
}

/*
 * During some rpc.nisd operations (such as when recovering the trans.log),
 * we don't want to use the LDAP repository, so we provide a main switch.
 * Note that we expect this to be used only when rpc.nisd is single-threaded,
 * so there is no need for synchronization when reading or modifying the
 * value of the main switch.
 */
int	useLDAPrespository = 1;

void
__db_disallowLDAP(void) {
	useLDAPrespository = 0;
}

void
__db_allowLDAP(void) {
	useLDAPrespository = 1;
}

}  /* extern "C" */