Home | History | Annotate | Download | only in libnisdb
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  *	db_mindex.cc
     24  *
     25  *  Copyright 1988-2002 Sun Microsystems, Inc.  All rights reserved.
     26  *  Use is subject to license terms.
     27  */
     28 
     29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     30 
     31 #include <stdio.h>
     32 
     33 #include <malloc.h>
     34 #include <strings.h>
     35 #include <string.h>
     36 #include <sys/param.h>
     37 #include "db_headers.h"
     38 #include "db.h"
     39 #include "db_mindex.h"
     40 #include "db_pickle.h"
     41 #include "nisdb_mt.h"
     42 #include "nisdb_ldap.h"
     43 #include "ldap_nisdbquery.h"
     44 #include "ldap_map.h"
     45 #include "ldap_ruleval.h"
     46 #include "ldap_scheme.h"
     47 #include "ldap_parse.h"
     48 #include "nis_hashitem.h"
     49 #include "ldap_nisplus.h"
     50 
     51 /*
     52  *  Constructor:  Create new table using scheme defintion supplied.
     53  *  (Make copy of scheme and keep it with table.)
     54  */
     55 db_mindex::db_mindex(db_scheme *how, char *tablePath) : rversion()
     56 {
     57 	noWriteThrough.flag = 0;
     58 	noLDAPquery.flag = 0;
     59 	initialLoad.flag = 0;
     60 	objPath.ptr = NULL;
     61 	init(how);
     62 	if (tablePath != NULL)
     63 		configure(tablePath);
     64 }
     65 
     66 /* Constructor:  Create empty table (no scheme, no table or indices). */
     67 db_mindex::db_mindex() : rversion()
     68 {
     69 	scheme = NULL;
     70 	table = NULL;
     71 	indices.indices_len = 0;
     72 	indices.indices_val = NULL;
     73 	noWriteThrough.flag = 0;
     74 	noLDAPquery.flag = 0;
     75 	initialLoad.flag = 0;
     76 	objPath.ptr = NULL;
     77 	INITRW(mindex);
     78 }
     79 
     80 db_mindex::~db_mindex()
     81 {
     82 	reset();   /* get rid of data structures first */
     83 	DESTROYRW(mindex);
     84 }
     85 
     86 /*
     87  * Initialize table using information given in scheme 'how'.
     88  * Record the scheme for later use (make copy of it);
     89  * create the required number of indices; and create table for storing
     90  * entries.
     91  */
     92 void
     93 db_mindex::init(db_scheme * how)
     94 {
     95 	scheme = new db_scheme(how);		// make copy
     96 	if (scheme == NULL)
     97 		FATAL("db_mindex::init: could not allocate space for scheme",
     98 			DB_MEMORY_LIMIT);
     99 
    100 	if (scheme->numkeys() == 0) {
    101 	    WARNING("db_mindex::init: empty scheme encountered");
    102 	    /* what action should we take here? */
    103 	}
    104 
    105 	indices.indices_len = how->numkeys();
    106 	db_key_desc * keys = how->keyloc();
    107 	int i;
    108 
    109 	/* homogeneous indices for now */
    110 	indices.indices_val = new db_index[indices.indices_len];
    111 	if (indices.indices_val == NULL) {
    112 		delete scheme;
    113 		indices.indices_len = 0;
    114 		scheme = NULL;
    115 		FATAL("db_mindex::init: could not allocate space for indices",
    116 			DB_MEMORY_LIMIT);
    117 	}
    118 	for (i = 0; i < indices.indices_len; i++) {
    119 		indices.indices_val[i].init(&(keys[i]));
    120 	}
    121 	table = new db_table();
    122 	if (table == NULL) {
    123 		delete scheme;
    124 		scheme = NULL;
    125 		delete indices.indices_val;
    126 		indices.indices_val = NULL;
    127 		indices.indices_len = 0;
    128 		FATAL("db_mindex::init: could not allocate space for table",
    129 			DB_MEMORY_LIMIT);
    130 	}
    131 	rversion.zero();
    132 	INITRW(mindex);
    133 	objPath.ptr = NULL;
    134 }
    135 
    136 /* empty associated tables associated */
    137 void
    138 db_mindex::reset_tables()
    139 {
    140 	int i;
    141 
    142 	WRITELOCKV(this, "w db_mindex::reset_tables");
    143 	/* Add sanity check in case of table corruption */
    144 	if (indices.indices_val != NULL) {
    145 		for (i = 0; i < indices.indices_len; i++) {
    146 			indices.indices_val[i].reset();
    147 		}
    148 	}
    149 	if (table) table->reset();
    150 	WRITEUNLOCKV(this, "wu db_mindex::reset_tables");
    151 }
    152 
    153 
    154 /*
    155  * Return a list of index_entries that satsify the given query 'q'.
    156  * Return the size of the list in 'count'. Return NULL if list is empty.
    157  * Return in 'valid' FALSE if query is not well formed.
    158 */
    159 db_index_entry_p
    160 db_mindex::satisfy_query(db_query *q, long *count, bool_t *valid) {
    161 	return (satisfy_query(q, count, valid, FALSE));
    162 }
    163 
    164 db_index_entry_p
    165 db_mindex::satisfy_query(db_query *q, long *count, bool_t *valid,
    166 			bool_t fromLDAP) {
    167 	db_index_entry_p	ret;
    168 	bool_t			validRequest;
    169 	int			queryRes;
    170 
    171 	/* Make sure we have somewhere to store the "request valid" status */
    172 	if (valid == NULL)
    173 		valid = &validRequest;
    174 
    175 	/* Prepare for a failed lock */
    176 	*count = 0;
    177 	*valid = FALSE;
    178 
    179 	READLOCK(this, NULL, "r db_mindex::satisfy_query");
    180 
    181 	/*
    182 	 * Only get data from LDAP if the caller requested it,
    183 	 * and if we're mapping for this table.
    184 	 */
    185 	fromLDAP = (fromLDAP && !noLDAPquery.flag &&
    186 		(table->mapping.fromLDAP ||
    187 			table->mapping.objType != NIS_TABLE_OBJ));
    188 
    189 	/*
    190 	 * If we always fetch data from LDAP for query's, then do so now,
    191 	 * before invoking the "real" satisfy_query().
    192 	 */
    193 	if (fromLDAP && table->mapping.matchFetch == mat_always) {
    194 		int	lockcode = 0;
    195 
    196 		READLOCKNR(table, lockcode,
    197 				"r db_mindex::satisfy_query table");
    198 		if (lockcode != 0) {
    199 			READUNLOCK(this, NULL, "ru db_mindex::satisfy_query");
    200 			return (NULL);
    201 		}
    202 
    203 		queryRes = queryLDAP(q, 0, 1);
    204 
    205 		READUNLOCKNR(table, lockcode,
    206 				"ru db_mindex::satisfy_query table");
    207 		if (lockcode != 0) {
    208 			READUNLOCK(this, NULL, "ru db_mindex::satisfy_query");
    209 			return (NULL);
    210 		}
    211 		if (queryRes != LDAP_SUCCESS) {
    212 			/* queryLDAP() sets error codes etc. */
    213 			READUNLOCK(this, NULL, "ru db_mindex::satisfy_query");
    214 			return (NULL);
    215 		}
    216 
    217 	}
    218 
    219 	ret = satisfy_query_dbonly(q, count, fromLDAP ? TRUE : FALSE, valid);
    220 
    221 	/* If we found it, or if we're not mapping, return */
    222 	if (ret != NULL || !fromLDAP) {
    223 		READUNLOCK(this, NULL, "ru db_mindex::satisfy_query");
    224 		return (ret);
    225 	} else if (ret == NULL && !(*valid)) {
    226 		/* No result, and the request wasn't valid */
    227 		READUNLOCK(this, NULL, "ru db_mindex::satisfy_query");
    228 		return (NULL);
    229 	}
    230 
    231 	/* Get data from LDAP */
    232 	if (table->mapping.matchFetch != mat_never) {
    233 		queryRes = queryLDAP(q, 0, 1);
    234 	} else {
    235 		/*
    236 		 * We'll now go on to check for an un-expired entry again,
    237 		 * even though we're pretty sure that won't work (already
    238 		 * did that, and nothing's changed). However, we accept that
    239 		 * slight inefficiency in the interest of keeping the code
    240 		 * simple; we expect 'mat_never' to be used very rarely.
    241 		 */
    242 		queryRes = LDAP_SUCCESS;
    243 	}
    244 
    245 	if (queryRes == LDAP_SUCCESS) {
    246 		/*
    247 		 * Check if we've got a match now. If not, try one
    248 		 * last time for an expired match.
    249 		 */
    250 		ret = satisfy_query_dbonly(q, count, TRUE, valid);
    251 		if (ret == NULL) {
    252 			ret = satisfy_query_dbonly(q, count, FALSE, valid);
    253 			if (ret != NULL)
    254 				setMappingStatus(NIS_CACHEEXPIRED, queryRes);
    255 		}
    256 	} else {
    257 		/*
    258 		 * Check if we have an expired entry; if so, return
    259 		 * it with an appropriate status.
    260 		 */
    261 		ret = satisfy_query_dbonly(q, count, FALSE, valid);
    262 		setMappingStatus(ret ? NIS_CACHEEXPIRED : NIS_SUCCESS,
    263 				queryRes);
    264 	}
    265 
    266 	READUNLOCK(this, NULL, "ru db_mindex::satisfy_query");
    267 
    268 	return (ret);
    269 }
    270 
    271 db_index_entry_p
    272 db_mindex::satisfy_query_dbonly(db_query *q, long *count,
    273 				bool_t checkExpire, bool_t *valid)
    274 {
    275 	db_index_entry_p oldres = NULL, newres;
    276 	int i, curr_ind;
    277 	long num_new, num_old = 0;
    278 	int limit = q->size();
    279 	db_qcomp * comps = q->queryloc();
    280 
    281 	if (valid) *valid = TRUE;   /* True to begin with. */
    282 
    283 	/* Add sanity check in case table corrupted */
    284 	if (indices.indices_len != 0 && indices.indices_val == NULL) {
    285 		WARNING("db_mindex::satisfy_query: table has no indices");
    286 		if (valid) *valid = FALSE;
    287 		*count = 0;
    288 		return (NULL);
    289 	}
    290 
    291 	for (i = 0; i < limit; i++) {
    292 		if ((curr_ind = comps[i].which_index) < indices.indices_len) {
    293 			newres = indices.indices_val[curr_ind].lookup(
    294 					comps[i].index_value, &num_new,
    295 					table, checkExpire);
    296 			if (newres == NULL) {
    297 				*count = 0;
    298 				return (NULL);
    299 			}
    300 			if (oldres == NULL) {
    301 				oldres = newres;
    302 				num_old = num_new;
    303 			} else {
    304 				oldres = newres->join(num_new, num_old,
    305 							oldres, &num_old);
    306 				if (oldres == NULL) {
    307 					*count = 0;
    308 					return (NULL);
    309 				}
    310 			}
    311 		} else {
    312 			WARNING("db_mindex::satisfy_query: index out of range");
    313 			if (valid) *valid = FALSE;
    314 			*count = 0;
    315 			return (NULL);
    316 		}
    317 	}
    318 	*count = num_old;
    319 	return (oldres);
    320 }
    321 
    322 /*
    323  * Returns an array of size 'count' of 'entry_object_p's, pointing to
    324  * copies of entry_objects named by the result list of db_index_entries 'res'.
    325  * Sets db_status 'statp' if error encountered; otherwise, leaves it unchanged.
    326 */
    327 entry_object_p *
    328 db_mindex::prepare_results(int count, db_index_entry_p res, db_status *statp)
    329 {
    330 	READLOCK(this, NULL, "r db_mindex::prepare_results");
    331 	READLOCK2(table, NULL, "r table db_mindex::prepare_results", this);
    332 	entry_object_p * entries = new entry_object_p[count];
    333 	int i;
    334 
    335 	if (entries == NULL) {
    336 		READUNLOCK2(this, table, NULL, NULL,
    337 	"ru db_mindex::prepare_results: could not allocate space",
    338 	"ru table db_mindex::prepare_results: could not allocate space");
    339 		FATAL3("db_mindex::prepare_results: could not allocate space",
    340 			DB_MEMORY_LIMIT, NULL);
    341 	}
    342 
    343 	for (i = 0; i < count; i++) {
    344 		if (res == NULL) {
    345 			int j;
    346 			for (j = 0; j < i; j++) // cleanup
    347 				free_entry(entries[j]);
    348 			syslog(LOG_ERR,
    349 				"db_mindex::prepare_results: incorrect count");
    350 			*statp = DB_INTERNAL_ERROR;
    351 		} else {
    352 			entries[i] =
    353 				new_entry(table->get_entry(res->getlocation()));
    354 			res = res->getnextresult();
    355 		}
    356 	}
    357 	READUNLOCK2(this, table, entries, entries,
    358 			"ru db_mindex::prepare_results",
    359 			"ru db_mindex::prepare_results");
    360 
    361 	return (entries);
    362 }
    363 
    364 /*
    365  * Returns a newly created db_query structure containing the index values
    366  * as obtained from the record named by 'recnum'.  The record itself, along
    367  * with information on the schema definition of this table, will determine
    368  * which values are extracted from the record and placed into the result.
    369  * Returns NULL if recnum is not a valid entry.
    370  * Note that space is allocated for the query and the index values
    371  * (i.e. do not share pointers with strings in 'obj'.)
    372  */
    373 db_query *
    374 db_mindex::extract_index_values_from_record(entryp recnum)
    375 {
    376 	db_query	*ret;
    377 
    378 	ret = extract_index_values_from_object(table->get_entry(recnum));
    379 	return (ret);
    380 }
    381 
    382 /*
    383  * Returns a newly created db_query containing the index values as
    384  * obtained from the given object.  The object itself,
    385  * along with information on the scheme given, will determine
    386  * which values are extracted from the object and placed into the query.
    387  * Returns an empty query if 'obj' is not a valid entry.
    388  * Note that space is allocated for the query and the index values
    389  * (i.e. do not share pointers with strings in 'obj'.)
    390 */
    391 db_query *
    392 db_mindex::extract_index_values_from_object(entry_object_p obj)
    393 {
    394 	READLOCK(this, NULL, "r db_mindex::extract_index_values_from_object");
    395 	if (scheme->numkeys() != indices.indices_len) { // probably built wrong
    396 		syslog(LOG_ERR,
    397 	    "number of keys (%d) does not equal number of indices (%d)",
    398 	    scheme->numkeys(), indices.indices_len);
    399 		READUNLOCK(this, NULL,
    400 			"ru db_mindex::extract_index_values_from_object");
    401 		return (new db_query());	// null query
    402 	} else if (obj == NULL) {
    403 		READUNLOCK(this, NULL,
    404 			"ru db_mindex::extract_index_values_from_object");
    405 		return (NULL);
    406 	} else {
    407 		db_query* answer = new db_query(scheme, obj);
    408 		if (answer) {
    409 			/*
    410 			 * XXX If the unlock fails, and we return NULL,
    411 			 * we leak 'answer'. On the other hand, if we
    412 			 * return 'answer', the object may remain locked,
    413 			 * but the caller doesn't know that anything
    414 			 * went wrong.
    415 			 */
    416 			READUNLOCK(this, NULL,
    417 			"ru db_mindex::extract_index_values_from_object");
    418 			return (answer);
    419 		} else {
    420 			FATAL3("db_mindex::extract: could not allocate space",
    421 				DB_MEMORY_LIMIT, NULL);
    422 		}
    423 	}
    424 	READUNLOCK(this, NULL,
    425 		"ru db_mindex::extract_index_values_from_object");
    426 	return (NULL);
    427 }
    428 
    429 /*
    430  * Returns the first entry found in the table by setting 'answer' to
    431  * point to the a copy of entry_object.  Returns DB_SUCCESS if found;
    432  * DB_NOTFOUND otherwise.
    433 */
    434 db_status
    435 db_mindex::first(entryp *where, entry_object ** answer)
    436 {
    437 	db_status	ret = DB_SUCCESS;
    438 
    439 	/*
    440 	 * table->first_entry() returns a pointer into the table, so
    441 	 * we must keep the table read locked until we've copied the
    442 	 * entry_object. In order to maintain lock integrity, we must
    443 	 * lock the db_mindex (this) before the db_table (table).
    444 	 */
    445 	READLOCK(this, DB_LOCK_ERROR, "r db_mindex::first");
    446 	READLOCK2(table, DB_LOCK_ERROR, "r table db_mindex::first", this);
    447 	if (table->mapping.fromLDAP) {
    448 		struct timeval	now;
    449 		(void) gettimeofday(&now, NULL);
    450 		if (now.tv_sec >= table->mapping.enumExpire) {
    451 			int queryRes = queryLDAP(0, 0, 1);
    452 			if (queryRes == LDAP_SUCCESS)
    453 				table->mapping.enumExpire = now.tv_sec +
    454 					table->mapping.ttl;
    455 			else {
    456 				READUNLOCK2(this, table,
    457 					DB_LOCK_ERROR, DB_LOCK_ERROR,
    458 					"ru db_mindex::first LDAP",
    459 					"ru table db_mindex::first LDAP");
    460 				return (DB_INTERNAL_ERROR);
    461 			}
    462 		}
    463 	}
    464 	entry_object_p ptr = table->first_entry(where);
    465 	if (ptr == NULL)
    466 		ret = DB_NOTFOUND;
    467 	else
    468 		*answer = new_entry(ptr);
    469 	READUNLOCK2(this, table, ret, ret,
    470 		"ru db_mindex::first", "ru table db_mindex::first");
    471 	return (ret);
    472 }
    473 
    474 /*
    475  * Returns the next entry in the table after 'previous' by setting 'answer' to
    476  * point to copy of the entry_object.  Returns DB_SUCCESS if 'previous' is
    477  * valid and next entry is found; DB_NOTFOUND otherwise.  Sets 'where' to
    478  * location of where entry is found for input as subsequent 'next' operation.
    479 */
    480 db_status
    481 db_mindex::next(entryp previous, entryp *where, entry_object **answer)
    482 {
    483 	db_status	ret = DB_SUCCESS;
    484 
    485 	READLOCK(this, DB_LOCK_ERROR, "r db_mindex::next");
    486 	READLOCK2(table, DB_LOCK_ERROR, "r db_mindex::next", this);
    487 	if (!(table->entry_exists_p(previous)))
    488 		ret = DB_NOTFOUND;
    489 	else {
    490 		entry_object * ptr = table->next_entry(previous, where);
    491 		if (ptr == NULL)
    492 			ret = DB_NOTFOUND;
    493 		else
    494 			*answer = new_entry(ptr);
    495 	}
    496 	READUNLOCK2(this, table, ret, ret,
    497 		"ru db_mindex::next", "ru table db_mindex::next");
    498 	return (ret);
    499 }
    500 
    501 static void
    502 delete_result_list(db_next_index_desc* orig)
    503 {
    504 	db_next_index_desc* curr, *save_next;
    505 	for (curr = orig; curr != NULL; 0) {
    506 		save_next = curr->next;
    507 		delete curr;
    508 		curr = save_next;
    509 	}
    510 }
    511 
    512 
    513 static db_next_index_desc *
    514 copy_result_list(db_index_entry* orig)
    515 {
    516 	db_next_index_desc *head = NULL, *curr;
    517 	db_index_entry *current;
    518 
    519 	for (current = orig; current != NULL;
    520 		current = current->getnextresult()) {
    521 		curr = new db_next_index_desc(current->getlocation(), head);
    522 		if (curr == NULL) {
    523 			FATAL3(
    524 			"db_mindex::copy_result_list: could not allocate space",
    525 			DB_MEMORY_LIMIT, NULL);
    526 		}
    527 		head = curr;  // list is actually reversed
    528 	}
    529 	return (head);
    530 }
    531 
    532 /*
    533  * Delete the given list of results; used when no longer interested in
    534  * the results of the first/next query that returned this list.
    535  */
    536 db_status
    537 db_mindex::reset_next(db_next_index_desc *orig)
    538 {
    539 	if (orig == NULL)
    540 		return (DB_NOTFOUND);
    541 
    542 	delete_result_list(orig);
    543 	return (DB_SUCCESS);
    544 }
    545 
    546 /*
    547 * Finds entry that satisfy the query 'q'.  Returns the first answer by
    548 * setting the pointer 'answer' to point to a copy of it.  'where' is set
    549 * so that the other answers could be gotten by passing 'where' to 'next'
    550 * successively.   Note that the answer is a  pointer to a copy of the entry.
    551 * Returns DB_SUCCESS if search was successful; DB_NOTFOUND otherwise.
    552  */
    553 db_status
    554 db_mindex::first(db_query *q,
    555 		db_next_index_desc **where, entry_object ** answer)
    556 {
    557 	READLOCK(this, DB_LOCK_ERROR, "r db_mindex::first");
    558 	READLOCK2(table, DB_LOCK_ERROR, "r table db_mindex::first", this);
    559 	long count;
    560 	bool_t valid_query;
    561 	db_status	ret = DB_SUCCESS;
    562 	db_index_entry * rp = satisfy_query(q, &count, &valid_query, TRUE);
    563 
    564 	if (valid_query != TRUE)
    565 		ret =  DB_BADQUERY;
    566 	else if (rp == NULL) {
    567 		*answer = NULL;
    568 		ret = DB_NOTFOUND;
    569 	} else {
    570 		*where = copy_result_list(rp);
    571 
    572 		entry_object_p ptr = table->get_entry((*where)->location);
    573 		if (ptr == NULL)
    574 			ret = DB_NOTFOUND;
    575 		else
    576 			*answer = new_entry(ptr);
    577 	}
    578 	READUNLOCK2(this, table, ret, ret,
    579 		"ru db_mindex::first", "ru table db_mindex::first");
    580 	return (ret);
    581 }
    582 
    583 /*
    584  * Returns the next entry in the table after 'previous' by setting 'answer' to
    585  * point to copy of the entry_object.  Next is next in chain of answers found
    586  * in previous first search with query.   Returns DB_SUCCESS if 'previous' is
    587  * valid and next entry is found; DB_NOTFOUND otherwise.  Sets 'where' to
    588  * location of where entry is found for input as subsequent 'next' operation.
    589 */
    590 db_status
    591 db_mindex::next(db_next_index_desc *previous, db_next_index_desc **where,
    592 		entry_object **answer)
    593 {
    594 	READLOCK(this, DB_LOCK_ERROR, "r db_mindex::next");
    595 	READLOCK2(table, DB_LOCK_ERROR, "r table db_mindex::next", this);
    596 	db_status	ret = DB_SUCCESS;
    597 
    598 	if (previous == NULL)
    599 		ret = DB_NOTFOUND;
    600 	else {
    601 		// should further check validity of 'previous' pointer
    602 		*where = previous->next;
    603 		delete previous;    // delete previous entry
    604 		if (*where == NULL)
    605 			ret = DB_NOTFOUND;
    606 		else {
    607 			entry_object * ptr =
    608 				table->get_entry((*where)->location);
    609 			if (ptr == NULL)
    610 				ret = DB_NOTFOUND;
    611 			else {
    612 				*answer = new_entry(ptr);
    613 				ret = DB_SUCCESS;
    614 			}
    615 		}
    616 	}
    617 	READUNLOCK2(this, table, ret, ret,
    618 		"ru db_mindex::next", "ru table db_mindex::next");
    619 	return (ret);
    620 }
    621 
    622 /*
    623  * Finds entry that satisfy the query 'q'.  Returns the answer by
    624  * setting the pointer 'rp' to point to the list of answers.
    625  * Note that the answers are pointers to the COPIES of entries.
    626  * Returns the number of answers find in 'count'.
    627  * Returns DB_SUCCESS if search found at least one answer;
    628  * returns DB_NOTFOUND if none is found.
    629 */
    630 db_status
    631 db_mindex::lookup(db_query *q, long *count, entry_object_p **result)
    632 {
    633 	bool_t valid_query;
    634 	db_index_entry * rp = satisfy_query(q, count, &valid_query, TRUE);
    635 	db_status stat = DB_SUCCESS;
    636 
    637 	if (valid_query != TRUE)
    638 		return (DB_BADQUERY);
    639 
    640 	if (rp == NULL) {
    641 		*result = NULL;
    642 		return (DB_NOTFOUND);
    643 	}
    644 
    645 	*result = prepare_results((int)*count, rp, &stat);
    646 
    647 	return (stat);
    648 }
    649 
    650 /*
    651  * Return all entries within table.  Returns the answer by
    652  * setting the pointer 'rp' to point to the list of answers.
    653  * Note that the answers are pointers to copies of the entries.
    654  * Returns the number of answers find in 'count'.
    655  * Returns DB_SUCCESS if search found at least one answer;
    656  * returns DB_NOTFOUND if none is found.
    657 */
    658 db_status
    659 db_mindex::all(long *count, entry_object_p **result)
    660 {
    661 	entry_object *ptr;
    662 	entryp where;
    663 	long how_many, i;
    664 	int	lret = 0;
    665 
    666 	if (table == NULL) {
    667 		*result = NULL;
    668 		return (DB_NOTFOUND);
    669 	}
    670 
    671 	READLOCK(this, DB_LOCK_ERROR, "r db_mindex::all");
    672 	/* Read lock 'table' while we're traversing it */
    673 	READLOCKNR(table, lret, "r table db_mindex::all");
    674 	if (lret != 0) {
    675 		READUNLOCK(this, DB_LOCK_ERROR, "ru db_mindex::all");
    676 		return (DB_LOCK_ERROR);
    677 	}
    678 
    679 	if (table->mapping.fromLDAP) {
    680 		struct timeval	now;
    681 		(void) gettimeofday(&now, NULL);
    682 		if (now.tv_sec >= table->mapping.enumExpire) {
    683 			int	queryRes = queryLDAP(0, 0, 1);
    684 			if (queryRes != LDAP_SUCCESS) {
    685 				READUNLOCKNR(table, lret,
    686 					"ru table db_mindex::all LDAP");
    687 				READUNLOCK(this, DB_LOCK_ERROR,
    688 					"ru db_mindex::all LDAP");
    689 				return (DB_INTERNAL_ERROR);
    690 			}
    691 		}
    692 	}
    693 
    694 	if ((how_many = table->fullness()) <= 0) {
    695 		/*
    696 		 * Set '*count' so that the caller avoids putting garbage
    697 		 * in an 'objects_len' field.
    698 		 */
    699 		*count = 0;
    700 		*result = NULL;
    701 		READUNLOCKNR(table, lret, "ru table db_mindex::all");
    702 		READUNLOCK(this, DB_NOTFOUND, "ru db_mindex::all");
    703 		return (DB_NOTFOUND);
    704 	}
    705 
    706 	entry_object_p * answer = new entry_object_p[how_many];
    707 	if (answer == NULL) {
    708 		READUNLOCKNR(table, lret, "ru table db_mindex::all");
    709 		READUNLOCK(this, DB_MEMORY_LIMIT, "ru db_mindex::all");
    710 		FATAL3("db_mindex::all: could not allocate space",
    711 			DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
    712 	}
    713 
    714 	*count = how_many;
    715 
    716 	ptr = table->first_entry(&where);
    717 	if (ptr != NULL)
    718 		answer[0] = new_entry(ptr);
    719 	else {
    720 		WARNING("db_mindex::all: null first entry found in all");
    721 		answer[0] = NULL;
    722 	}
    723 	for (i = 1; i < how_many; i++) {
    724 		ptr = table->next_entry(where, &where);
    725 		if (ptr != NULL)
    726 			answer[i] = new_entry(ptr);
    727 		else {
    728 			WARNING(
    729 			    "db_mindex::all: null internal entry found in all");
    730 			answer[i] = NULL; /* Answer gets null too. -CM */
    731 		}
    732 	}
    733 
    734 	READUNLOCKNR(table, lret, "ru table db_mindex::all");
    735 
    736 	*result = answer;
    737 	READUNLOCK(this, DB_SUCCESS, "ru db_mindex::all");
    738 	return (DB_SUCCESS);
    739 }
    740 
    741 /*
    742  * Remove the entry identified by 'recloc' from:
    743  * 1.  all indices, as obtained by extracting the index values from the entry
    744  * 2.  table where entry is stored.
    745 */
    746 db_status
    747 db_mindex::remove_aux(entryp recloc)
    748 {
    749 	int i, curr_ind;
    750 	db_status	res = DB_SUCCESS;
    751 
    752 	WRITELOCK(this, DB_LOCK_ERROR, "w db_mindex::remove_aux");
    753 	/* get index values of this record */
    754 	db_query * cq = extract_index_values_from_record(recloc);
    755 	if (cq == NULL) {
    756 		WRITEUNLOCK(this, DB_MEMORY_LIMIT, "wu db_mindex::remove_aux");
    757 		FATAL3("db_mindex::remove_aux: could not allocate space",
    758 			DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
    759 	}
    760 	if (cq->size() != indices.indices_len) { /* something is wrong */
    761 		delete cq; // clean up
    762 		syslog(LOG_ERR,
    763 	    "db_mindex::remove_aux: record contains wrong number of indices");
    764 		WRITEUNLOCK(this, DB_INTERNAL_ERROR,
    765 			"wu db_mindex::remove_aux");
    766 		return (DB_INTERNAL_ERROR);
    767 	}
    768 
    769 	if (!noWriteThrough.flag) {
    770 		nis_object	*o = 0;
    771 		entry_object    *e = table->get_entry(recloc);
    772 		int		queryRes, doingModify;
    773 
    774 		/*
    775 		 * If the removal is part of a modify operation, we
    776 		 * defer the LDAP update until the modified NIS+ object
    777 		 * is added back.
    778 		 */
    779 		if (saveOldObjForModify((entry_obj *)e, &doingModify) == 0)
    780 			res = DB_INTERNAL_ERROR;
    781 
    782 		if (res == DB_SUCCESS && !doingModify) {
    783 			/*
    784 			 * If we're removing a directory entry, and the
    785 			 * entry is LDAP-mapped, but the directory isn't,
    786 			 * we need a copy of the entry object in order
    787 			 * to remove if from LDAP.
    788 			 */
    789 			if (e != 0 && e->en_type != 0 &&
    790 					strcmp(e->en_type, "IN_DIRECTORY") == 0)
    791 				o = unmakePseudoEntryObj(e, 0);
    792 			queryRes = removeLDAP(cq, o);
    793 			if (queryRes != LDAP_SUCCESS) {
    794 				setMappingStatus(NIS_SUCCESS, queryRes);
    795 				if (table->mapping.storeErrorDisp == abandon)
    796 					res = DB_INTERNAL_ERROR;
    797 			}
    798 			if (o != 0)
    799 				nis_destroy_object(o);
    800 		}
    801 	}
    802 
    803 	if (res == DB_SUCCESS) {
    804 		db_qcomp * comps = cq->queryloc();
    805 
    806 		/* Add sanity check in case of corrupted table */
    807 		if (indices.indices_val != NULL) {
    808 			/* update indices */
    809 			for (i = 0; i < indices.indices_len; i++) {
    810 				/* unnec. if sorted */
    811 				curr_ind = comps[i].which_index;
    812 				indices.indices_val[curr_ind].remove(
    813 						comps[i].index_value, recloc);
    814 			}
    815 		}
    816 
    817 		/* update table where record is stored */
    818 		table->delete_entry(recloc);
    819 	}
    820 
    821 	/* delete query */
    822 	delete cq;
    823 
    824 	WRITEUNLOCK(this, DB_SUCCESS, "wu db_mindex::remove_aux");
    825 
    826 	return (res);
    827 }
    828 
    829 /*
    830  * Removes the entry in the table named by given query 'q'.
    831  * If a NULL query is supplied, all entries in table are removed.
    832  * Returns DB_NOTFOUND if no entry is found.
    833  * Returns DB_SUCCESS if one entry is found; this entry is removed from
    834  * its record storage, and it is also removed from all the indices of the
    835  * table. If more than one entry satisfying 'q' is found, all are removed.
    836  */
    837 db_status
    838 db_mindex::remove(db_query *q)
    839 {
    840 	long count = 0;
    841 	db_index_entry *rp;
    842 	db_status rstat;
    843 	bool_t valid_query;
    844 
    845 	WRITELOCK(this, DB_LOCK_ERROR, "w db_mindex::remove");
    846 	WRITELOCK2(table, DB_LOCK_ERROR, "w table db_mindex::remove", this);
    847 	if (q == NULL)  {  /* remove all entries in table */
    848 		if (table->mapping.toLDAP && !noWriteThrough.flag) {
    849 			int	queryRes = removeLDAP(q, 0);
    850 #ifdef	NISDB_LDAP_DEBUG
    851 			if (queryRes != LDAP_SUCCESS)
    852 				abort();
    853 #endif	/* NISDB_LDAP_DEBUG */
    854 		}
    855 		if (table != NULL && table->getsize() > 0) {
    856 			reset_tables();
    857 			WRITEUNLOCK2(table, this, DB_SUCCESS, DB_SUCCESS,
    858 					"wu table db_mindex::remove",
    859 					"wu db_mindex::remove");
    860 			return (DB_SUCCESS);
    861 		} else {
    862 			WRITEUNLOCK2(table, this, DB_NOTFOUND, DB_NOTFOUND,
    863 					"wu table db_mindex::remove",
    864 					"wu db_mindex::remove");
    865 			return (DB_NOTFOUND);
    866 		}
    867 	}
    868 
    869 	rp = satisfy_query(q, &count, &valid_query, FALSE);
    870 
    871 	if (valid_query != TRUE) {
    872 		WRITEUNLOCK2(table, this, DB_BADQUERY, DB_BADQUERY,
    873 			"wu table db_mindex::remove", "wu db_mindex::remove");
    874 		return (DB_BADQUERY);
    875 	}
    876 
    877 	if (count == 0) {	/* not found */
    878 		WRITEUNLOCK2(table, this, DB_NOTFOUND, DB_NOTFOUND,
    879 			"wu table db_mindex::remove", "wu db_mindex::remove");
    880 		return (DB_NOTFOUND);
    881 	} else if (count == 1) {	/* found, update indices  */
    882 		db_status	s;
    883 
    884 		s = remove_aux(rp->getlocation());
    885 
    886 		WRITEUNLOCK2(table, this, s, s,
    887 			"wu table db_mindex::remove", "wu db_mindex::remove");
    888 		return (s);
    889 	} else {		/* ambiguous, remove all entries */
    890 		int i;
    891 		db_index_entry *next_entry;
    892 		for (i = 0; i < count; i++) {
    893 			if (rp == NULL) {
    894 				syslog(LOG_ERR,
    895 			"db_mindex::remove:  incorrect number of indices");
    896 				WRITEUNLOCK2(table, this, DB_INTERNAL_ERROR,
    897 					DB_INTERNAL_ERROR,
    898 					"wu table db_mindex::remove",
    899 					"wu db_mindex::remove");
    900 				return (DB_INTERNAL_ERROR);
    901 			}
    902 
    903 			next_entry = rp->getnextresult(); // save before removal
    904 			rstat = remove_aux(rp->getlocation());
    905 			if (rstat != DB_SUCCESS) {
    906 				WRITEUNLOCK2(table, this, rstat, rstat,
    907 					"wu table db_mindex::remove",
    908 					"wu db_mindex::remove");
    909 				return (rstat);
    910 			}
    911 			rp = next_entry;		// go on to next
    912 		}
    913 		WRITEUNLOCK2(table, this, DB_SUCCESS, DB_SUCCESS,
    914 			"wu table db_mindex::remove", "wu db_mindex::remove");
    915 		return (DB_SUCCESS);
    916 	}
    917 }
    918 
    919 /*
    920  * Add copy of given entry to table.  Entry is identified by query 'q'.
    921  * The entry (if any) satisfying the query is first deleted, then
    922  *  added to the indices (using index values extracted form the given entry)
    923  * and the table.
    924  * Returns DB_NOTUNIQUE if more than one entry satisfies the query.
    925  * Returns DB_NOTFOUND if query is not well-formed.
    926  * Returns DB_SUCCESS if entry can be added.
    927 */
    928 db_status
    929 db_mindex::add(db_query *q, entry_object * obj)
    930 {
    931 	long count = 0;
    932 	int i, curr_ind;
    933 	bool_t valid;
    934 	db_index_entry *rp = NULL;
    935 	db_status rstat;
    936 	char		*myself = "db_mindex::add";
    937 
    938 	/*
    939 	 *  The argument q is only NULL when we know that there are
    940 	 *  no objects in the database that match the object.
    941 	 */
    942 	WRITELOCK(this, DB_LOCK_ERROR, "w db_mindex::add");
    943 	WRITELOCK2(table, DB_LOCK_ERROR, "w table db_mindex::add", this);
    944 	if (q) {
    945 		rp = satisfy_query(q, &count, &valid, FALSE);
    946 		if (!valid) {
    947 			WRITEUNLOCK2(this, table, DB_LOCK_ERROR, DB_LOCK_ERROR,
    948 					"wu db_mindex::add",
    949 					"wu table db_mindex::add");
    950 			return (DB_BADQUERY);
    951 		}
    952 	}
    953 	if (count == 1) {	/* found, first delete */
    954 		rstat = remove_aux(rp->getlocation());
    955 		if (rstat != DB_SUCCESS) {
    956 			WRITEUNLOCK2(this, table, rstat, rstat,
    957 				"wu db_mindex::add",
    958 				"wu table db_mindex::add");
    959 			return (rstat);
    960 		}
    961 		count = 0;	/* fall through to add */
    962 	}
    963 
    964 	if (count == 0) { 	/* not found, insert */
    965 		/* add object to table */
    966 		entryp recloc = table->add_entry(obj, initialLoad.flag);
    967 		/* get index values of this object, might be same as 'q' */
    968 		db_query *cq = extract_index_values_from_object(obj);
    969 		if (cq == NULL) {
    970 			table->delete_entry(recloc);
    971 			WRITEUNLOCK2(this, table,
    972 				DB_MEMORY_LIMIT, DB_MEMORY_LIMIT,
    973 				"wu db_mindex::add DB_MEMORY_LIMIT",
    974 				"wu table db_mindex::add DB_MEMORY_LIMIT");
    975 			FATAL3("db_mindex::add: could not allocate space for",
    976 				DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
    977 		}
    978 		if (cq ->size() != indices.indices_len) { /* something wrong */
    979 			table->delete_entry(recloc);
    980 			delete cq; // clean up
    981 			syslog(LOG_ERR,
    982 		    "db_mindex::add: record contains wrong number of indices");
    983 			WRITEUNLOCK2(this, table,
    984 				DB_INTERNAL_ERROR, DB_INTERNAL_ERROR,
    985 				"wu db_mindex::add DB_INTERNAL_ERROR",
    986 				"wu table db_mindex::add DB_INTERNAL_ERROR");
    987 			return (DB_INTERNAL_ERROR);
    988 		}
    989 		db_qcomp * comps = cq->queryloc();
    990 
    991 		/* update indices */
    992 		if (indices.indices_val != NULL) {
    993 			for (i = 0; i < indices.indices_len; i++) {
    994 				curr_ind = comps[i].which_index;
    995 				indices.indices_val[curr_ind].add(
    996 					comps[i].index_value, recloc);
    997 			}
    998 		}
    999 		delete cq;  // clean up
   1000 		if (!noWriteThrough.flag) {
   1001 			int		queryRes;
   1002 			entry_object	*e = 0;
   1003 
   1004 			if (retrieveOldObjForModify((entry_obj **)&e) == 0) {
   1005 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
   1006 			"%s: Error retrieving old object for LDAP update",
   1007 					myself);
   1008 				return (DB_INTERNAL_ERROR);
   1009 			}
   1010 
   1011 			queryRes = storeLDAP(q, obj, 0, e, 0);
   1012 			if (queryRes != LDAP_SUCCESS) {
   1013 				if (table->mapping.storeErrorDisp == abandon) {
   1014 					WRITEUNLOCK2(this, table,
   1015 						DB_INTERNAL_ERROR,
   1016 						DB_INTERNAL_ERROR,
   1017 						"wu db_mindex::add LDAP",
   1018 						"wu table db_mindex::add LDAP");
   1019 					return (DB_INTERNAL_ERROR);
   1020 				} else {
   1021 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
   1022 						"%s: LDAP store failed: %s",
   1023 						myself,
   1024 						ldap_err2string(queryRes));
   1025 				}
   1026 			}
   1027 		}
   1028 		rstat = DB_SUCCESS;
   1029 	} else  /* ambiguous */
   1030 		rstat = DB_NOTUNIQUE;
   1031 
   1032 	WRITEUNLOCK2(this, table, rstat, rstat,
   1033 			"wu db_mindex::add",
   1034 			"wu table db_mindex::add");
   1035 	return (rstat);
   1036 }
   1037 
   1038 /* ************************* pickle_mindex ********************* */
   1039 /* Does the actual writing to/from file specific for db_mindex structure. */
   1040 static bool_t
   1041 transfer_aux(XDR* x, pptr rp)
   1042 {
   1043 	return (xdr_db_mindex(x, (db_mindex*) rp));
   1044 }
   1045 
   1046 class pickle_mindex: public pickle_file {
   1047     public:
   1048 	pickle_mindex(char *f, pickle_mode m) : pickle_file(f, m) {}
   1049 
   1050 	/* Transfers db_mindex structure pointed to by dp to/from file. */
   1051 	int transfer(db_mindex* dp)
   1052 		{
   1053 			int	ret;
   1054 
   1055 			WRITELOCK(dp, -1, "w pickle_mindex::transfer");
   1056 			ret = pickle_file::transfer((pptr) dp, &transfer_aux);
   1057 			WRITEUNLOCK(dp, ret, "wu pickle_mindex::transfer");
   1058 			return (ret);
   1059 		}
   1060 };
   1061 
   1062 /* Write this structure (table, indices, scheme) into the specified file. */
   1063 int
   1064 db_mindex::dump(char *file)
   1065 {
   1066 	pickle_mindex f(file, PICKLE_WRITE);
   1067 	int status = f.transfer(this);
   1068 
   1069 	if (status == 1)
   1070 		return (-1); /* could not open for write */
   1071 	else
   1072 		return (status);
   1073 }
   1074 
   1075 /*
   1076  * Reset the table by: deleting all the indices, table of entries, and its
   1077  * scheme.
   1078 */
   1079 void
   1080 db_mindex::reset()
   1081 {
   1082 	WRITELOCKV(this, "w db_mindex::reset");
   1083 	reset_tables();   /* clear table contents first */
   1084 
   1085 	if (indices.indices_val) {
   1086 		delete [] indices.indices_val;