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