xref: /illumos-gate/usr/src/lib/libnisdb/db_mindex.cc (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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;
1087 		indices.indices_val = NULL;
1088 	}
1089 	if (table) { delete table; table = NULL;  }
1090 	if (scheme) { delete scheme; scheme = NULL;  }
1091 	indices.indices_len = 0;
1092 	rversion.zero();
1093 	if (objPath.ptr != 0) {
1094 		free(objPath.ptr);
1095 		objPath.ptr = 0;
1096 	}
1097 	WRITEUNLOCKV(this, "wu db_mindex::reset");
1098 }
1099 
1100 /*
1101  * Initialize table using information from specified file.
1102  * The table is first 'reset', then the attempt to load from the file
1103  * is made.  If the load failed, the table is again reset.
1104  * Therefore, the table will be modified regardless of the success of the
1105  * load.  Returns 0 if successful, 1 if DB disk file couldn't be opened,
1106  * -1 for various other failures.
1107 */
1108 int
1109 db_mindex::load(char *file)
1110 {
1111 	pickle_mindex f(file, PICKLE_READ);
1112 	int status;
1113 	int	init_table = (this->table == NULL);
1114 	int	init_scheme = (this->scheme == NULL);
1115 
1116 	WRITELOCK(this, -1, "w db_mindex::load");
1117 	reset();
1118 
1119 	/* load new mindex */
1120 	if ((status = f.transfer(this)) != 0) {
1121 		/* load failed.  Reset. */
1122 		reset();
1123 	}
1124 
1125 	/* Initialize the 'scheme' locking */
1126 	if (status == 0 && this->scheme != 0 && init_scheme) {
1127 		/*
1128 		 * Since we've added fields to the db_scheme that aren't
1129 		 * read from disk, we need to re-allocate so that the
1130 		 * db_scheme instance is large enough.
1131 		 */
1132 		db_scheme	*tmpscheme = new db_scheme();
1133 		if (tmpscheme != 0) {
1134 			(void) memcpy(tmpscheme, this->scheme,
1135 					this->scheme->oldstructsize());
1136 			free(this->scheme);
1137 			this->scheme = tmpscheme;
1138 		} else {
1139 			status = -1;
1140 		}
1141 	}
1142 	/*
1143 	 * If the 'table' field was NULL before the load, but not now,
1144 	 * initialize the table locking and mapping.
1145 	 */
1146 	if (status == 0 && this->table != 0 && init_table) {
1147 		/*
1148 		 * As for the db_scheme, make sure the db_table is large
1149 		 * enough.
1150 		 */
1151 		db_table	*tmptable = new db_table();
1152 		if (tmptable != 0) {
1153 			(void) memcpy(tmptable, this->table,
1154 					this->table->oldstructsize());
1155 			free(this->table);
1156 			this->table = tmptable;
1157 			(void) this->configure(file);
1158 		} else {
1159 			status = -1;
1160 		}
1161 	}
1162 
1163 	if (status == 0 && this->indices.indices_val != NULL) {
1164 		/*
1165 		 * Recreate the db_index instance so that it is
1166 		 * correctly initialized.
1167 		 */
1168 		db_index *tmp_indices;
1169 		int	n_index = this->indices.indices_len;
1170 
1171 		tmp_indices = new db_index[n_index];
1172 		if (tmp_indices != NULL) {
1173 			for (int i = 0; i < n_index; i++) {
1174 			    if (tmp_indices[i].move_xdr_db_index
1175 				(&this->indices.indices_val[i]) != DB_SUCCESS) {
1176 					status = -1;
1177 					break;
1178 			    }
1179 			}
1180 			free(this->indices.indices_val);
1181 			this->indices.indices_val = tmp_indices;
1182 			this->indices.indices_len = n_index;
1183 		} else {
1184 			status = -1;
1185 		}
1186 	}
1187 
1188 	WRITEUNLOCK(this, status, "wu db_mindex::load");
1189 	return (status);
1190 }
1191 
1192 /*
1193  * Prints statistics of the table.  This includes the size of the table,
1194  * the number of entries, and the index sizes.
1195  */
1196 void
1197 db_mindex::print_stats()
1198 {
1199 	long size, count, i;
1200 	long *stats = table->stats(TRUE);
1201 
1202 	printf("table_size = %d\n", stats[0]);
1203 	printf("last_used = %d\n", stats[1]);
1204 	printf("count = %d\n", stats[2]);
1205 	printf("free list size = %d\n", stats[3]);
1206 	printf("free list count = %d\n", stats[4]);
1207 
1208 	for (i = 5; i < 5+stats[4]; i++) {
1209 		printf("%d, ", stats[i]);
1210 	}
1211 	printf("\n");
1212 	free((char *)stats);
1213 
1214 	/* Add sanity check in case of corrupted table */
1215 	if (indices.indices_val == NULL) {
1216 		printf("No indices to print\n");
1217 		return;
1218 	}
1219 	for (i = 0; i < indices.indices_len; i++) {
1220 		printf("***** INDEX %d ******\n", i);
1221 		indices.indices_val[i].stats(&size, &count);
1222 		printf("index table size = %d\ncount = %d\n", size, count);
1223 	}
1224 }
1225 
1226 /* Prints statistics about all indices of table. */
1227 void
1228 db_mindex::print_all_indices()
1229 {
1230 	int i;
1231 
1232 	READLOCKV(this, "r db_mindex::print_all_indices");
1233 	/* Add sanity check in case of corrupted table */
1234 	if (indices.indices_val == NULL) {
1235 		printf("No indices to print\n");
1236 		READUNLOCKV(this, "ru db_mindex::print_all_indices");
1237 		return;
1238 	}
1239 	for (i = 0; i < indices.indices_len; i++) {
1240 		printf("***** INDEX %d ******\n", i);
1241 		indices.indices_val[i].print();
1242 	}
1243 	READUNLOCKV(this, "ru db_mindex::print_all_indices");
1244 }
1245 
1246 /* Prints statistics about indices identified by 'n'. */
1247 void
1248 db_mindex::print_index(int n)
1249 {
1250 	READLOCKV(this, "r db_mindex::print_index");
1251 	if (n >= 0 && n < indices.indices_len)
1252 		indices.indices_val[n].print();
1253 	READUNLOCKV(this, "ru db_mindex::print_index");
1254 }
1255