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 */ 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). */ 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 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 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 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 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 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 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 * 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 * 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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: 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. */ 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 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 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 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 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 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 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