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