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_dictionary.cc 24 * 25 * Copyright 2004 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 "db_headers.h" 32 #include "db_entry.h" 33 #include "db_dictionary.h" 34 #include "db_dictlog.h" 35 #include "db_vers.h" 36 #include "nisdb_mt.h" 37 #include "nisdb_rw.h" 38 #include "ldap_parse.h" 39 #include "ldap_map.h" 40 #include "nis_hashitem.h" 41 #include "ldap_util.h" 42 #include "nis_db.h" 43 #include <rpcsvc/nis.h> 44 45 #include <stdio.h> 46 #include <string.h> 47 #include <malloc.h> 48 #ifdef TDRPC 49 #include <sysent.h> 50 #endif 51 #include <unistd.h> 52 #include <syslog.h> 53 #include <rpc/rpc.h> 54 55 typedef bool_t (*db_func)(XDR *, db_table_desc *); 56 57 extern db_dictionary *InUseDictionary; 58 extern db_dictionary *FreeDictionary; 59 60 /* *************** dictionary version ****************** */ 61 62 #define DB_MAGIC 0x12340000 63 #define DB_MAJOR 0 64 #define DB_MINOR 10 65 #define DB_VERSION_0_9 (DB_MAGIC|(DB_MAJOR<<8)|9) 66 #define DB_ORIG_VERSION DB_VERSION_0_9 67 #define DB_CURRENT_VERSION (DB_MAGIC|DB_MAJOR<<8|DB_MINOR) 68 69 vers db_update_version; /* Note 'global' for all dbs. */ 70 71 #define INMEMORY_ONLY 1 72 73 /* 74 * Checks for valid version. For now, there are two: 75 * DB_VERSION_ORIG was the ORIGINAL one with major = 0, minor = 9 76 * DB_CURRENT_VERSION is the latest one with changes in the database format 77 * for entry objects and the change in the dictionary format. 78 * 79 * Our current implementation can support both versions. 80 */ 81 static inline bool_t 82 db_valid_version(u_int vers) 83 { 84 return ((vers == DB_CURRENT_VERSION) || (vers == DB_ORIG_VERSION)); 85 } 86 87 static char * 88 db_version_str(u_int vers) 89 { 90 static char vstr[128]; 91 u_int d_major = (vers&0x0000ff00)>>8; 92 u_int d_minor = (vers&0x000000ff); 93 94 sprintf(vstr, "SunSoft, SSM, Version %d.%d", d_major, d_minor); 95 return (vstr); 96 } 97 98 /* 99 * Special XDR version that checks for a valid version number. 100 * If we don't find an acceptable version, exit immediately instead 101 * of continuing to xdr rest of dictionary, which might lead to 102 * a core dump if the formats between versions are incompatible. 103 * In the future, there might be a switch to determine versions 104 * and their corresponding XDR routines for the rest of the dictionary. 105 */ 106 extern "C" { 107 bool_t 108 xdr_db_dict_version(XDR *xdrs, db_dict_version *objp) 109 { 110 if (xdrs->x_op == XDR_DECODE) { 111 if (!xdr_u_int(xdrs, (u_int*) objp) || 112 !db_valid_version(((u_int) *objp))) { 113 syslog(LOG_ERR, 114 "db_dictionary: invalid dictionary format! Expecting %s", 115 db_version_str(DB_CURRENT_VERSION)); 116 fprintf(stderr, 117 "db_dictionary: invalid dictionary format! Expecting %s\n", 118 db_version_str(DB_CURRENT_VERSION)); 119 exit(1); 120 } 121 } else if (!xdr_u_int(xdrs, (u_int*) objp)) 122 return (FALSE); 123 return (TRUE); 124 } 125 126 void 127 make_zero(vers* v) 128 { 129 v->zero(); 130 } 131 132 133 }; 134 135 136 /* ******************* dictionary data structures *************** */ 137 138 /* Delete contents of single db_table_desc pointed to by 'current.' */ 139 static void 140 delete_table_desc(db_table_desc *current) 141 { 142 if (current->table_name != NULL) delete current->table_name; 143 if (current->scheme != NULL) delete current->scheme; 144 if (current->database != NULL) delete current->database; 145 delete current; 146 } 147 148 /* Create new table descriptor using given table name and table_object. */ 149 db_status 150 db_dictionary::create_table_desc(char *tab, table_obj* zdesc, 151 db_table_desc** answer) 152 { 153 db_table_desc *newtab; 154 if ((newtab = new db_table_desc) == NULL) { 155 FATAL3( 156 "db_dictionary::add_table: could not allocate space for new table", 157 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 158 } 159 160 newtab->database = NULL; 161 newtab->table_name = NULL; 162 newtab->next = NULL; 163 164 if ((newtab->scheme = new db_scheme(zdesc)) == NULL) { 165 delete_table_desc(newtab); 166 FATAL3( 167 "db_dictionary::add_table: could not allocate space for scheme", 168 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 169 } 170 171 if (newtab->scheme->numkeys() == 0) { 172 WARNING( 173 "db_dictionary::add_table: could not translate table_obj to scheme"); 174 delete_table_desc(newtab); 175 return (DB_BADOBJECT); 176 } 177 178 if ((newtab->table_name = strdup(tab)) == NULL) { 179 delete_table_desc(newtab); 180 FATAL3( 181 "db_dictionary::add_table: could not allocate space for table name", 182 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 183 } 184 185 if (answer) 186 *answer = newtab; 187 return (DB_SUCCESS); 188 } 189 190 191 /* Delete list of db_table_desc pointed to by 'head.' */ 192 static void 193 delete_bucket(db_table_desc *head) 194 { 195 db_table_desc * nextone, *current; 196 197 for (current = head; current != NULL; current = nextone) { 198 nextone = current->next; // remember next 199 delete_table_desc(current); 200 } 201 } 202 203 static void 204 delete_dictionary(db_dict_desc *dict) 205 { 206 db_table_desc* bucket; 207 int i; 208 if (dict) { 209 if (dict->tables.tables_val) { 210 /* delete each bucket */ 211 for (i = 0; i < dict->tables.tables_len; i++) 212 bucket = dict->tables.tables_val[i]; 213 if (bucket) 214 delete_bucket(bucket); 215 /* delete table */ 216 delete dict->tables.tables_val; 217 } 218 /* delete dictionary */ 219 delete dict; 220 } 221 } 222 223 /* Relocate bucket starting with this entry to new hashtable 'new_tab'. */ 224 static void 225 relocate_bucket(db_table_desc* bucket, 226 db_table_desc_p *new_tab, unsigned long hashsize) 227 { 228 db_table_desc_p np, next_np, *hp; 229 230 for (np = bucket; np != NULL; np = next_np) { 231 next_np = np->next; 232 hp = &new_tab[np->hashval % hashsize]; 233 np->next = *hp; 234 *hp = np; 235 } 236 } 237 238 /* 239 * Return pointer to entry with same hash value and table_name 240 * as those supplied. Returns NULL if not found. 241 */ 242 static db_status 243 enumerate_bucket(db_table_desc* bucket, db_status(*func)(db_table_desc *)) 244 { 245 db_table_desc_p np; 246 db_status status; 247 248 for (np = bucket; np != NULL; np = np->next) { 249 status = (func)(np); 250 if (status != DB_SUCCESS) 251 return (status); 252 } 253 return (DB_SUCCESS); 254 } 255 256 257 /* 258 * Return pointer to entry with same hash value and table_name 259 * as those supplied. Returns NULL if not found. 260 */ 261 static db_table_desc_p 262 search_bucket(db_table_desc* bucket, unsigned long hval, char *target) 263 { 264 db_table_desc_p np; 265 266 for (np = bucket; np != NULL; np = np->next) { 267 if (np->hashval == hval && 268 strcmp(np->table_name, target) == 0) { 269 break; 270 } 271 } 272 return (np); 273 } 274 275 276 /* 277 * Remove entry with the specified hashvalue and target table name. 278 * Returns 'TRUE' if successful, FALSE otherwise. 279 * If the entry being removed is at the head of the list, then 280 * the head is updated to reflect the removal. The storage for the 281 * entry is freed if desired. 282 */ 283 static bool_t 284 remove_from_bucket(db_table_desc_p bucket, 285 db_table_desc_p *head, unsigned long hval, char *target, 286 bool_t free_storage) 287 { 288 db_table_desc_p np, dp; 289 290 /* Search for it in the bucket */ 291 for (dp = np = bucket; np != NULL; np = np->next) { 292 if (np->hashval == hval && 293 strcmp(np->table_name, target) == 0) { 294 break; 295 } else { 296 dp = np; 297 } 298 } 299 300 if (np == NULL) 301 return (FALSE); // cannot delete if it is not there 302 303 if (dp == np) { 304 *head = np->next; // deleting head of bucket 305 } else { 306 dp->next = np->next; // deleting interior link 307 } 308 if (free_storage) 309 delete_table_desc(np); 310 311 return (TRUE); 312 } 313 314 315 /* 316 * Add given entry to the bucket pointed to by 'bucket'. 317 * If an entry with the same table_name is found, no addition 318 * is done. The entry is added to the head of the bucket. 319 */ 320 static bool_t 321 add_to_bucket(db_table_desc_p bucket, db_table_desc **head, db_table_desc_p td) 322 { 323 db_table_desc_p curr, prev; 324 register char *target_name; 325 unsigned long target_hval; 326 target_name = td->table_name; 327 target_hval = td->hashval; 328 329 /* Search for it in the bucket */ 330 for (prev = curr = bucket; curr != NULL; curr = curr->next) { 331 if (curr->hashval == target_hval && 332 strcmp(curr->table_name, target_name) == 0) { 333 break; 334 } else { 335 prev = curr; 336 } 337 } 338 339 if (curr != NULL) 340 return (FALSE); /* duplicates not allowed */ 341 342 curr = *head; 343 *head = td; 344 td->next = curr; 345 return (TRUE); 346 } 347 348 349 /* Print bucket starting with this entry. */ 350 static void 351 print_bucket(db_table_desc *head) 352 { 353 db_table_desc *np; 354 for (np = head; np != NULL; np = np->next) { 355 printf("%s: %d\n", np->table_name, np->hashval); 356 } 357 } 358 359 static db_status 360 print_table(db_table_desc *tbl) 361 { 362 if (tbl == NULL) 363 return (DB_BADTABLE); 364 printf("%s: %d\n", tbl->table_name, tbl->hashval); 365 return (DB_SUCCESS); 366 } 367 368 369 static int hashsizes[] = { /* hashtable sizes */ 370 11, 371 53, 372 113, 373 337, 374 977, 375 2053, 376 4073, 377 8011, 378 16001, 379 0 380 }; 381 382 // prevents wrap around numbers from being passed 383 #define CALLOC_LIMIT 536870911 384 385 /* Returns the next size to use for the hash table */ 386 static unsigned int 387 get_next_hashsize(long unsigned oldsize) 388 { 389 long unsigned newsize, n; 390 if (oldsize == 0) 391 newsize = hashsizes[0]; 392 else { 393 for (n = 0; newsize = hashsizes[n++]; ) 394 if (oldsize == newsize) { 395 newsize = hashsizes[n]; /* get next size */ 396 break; 397 } 398 if (newsize == 0) 399 newsize = oldsize * 2 + 1; /* just double */ 400 } 401 return (newsize); 402 } 403 404 /* 405 * Grow the current hashtable upto the next size. 406 * The contents of the existing hashtable is copied to the new one and 407 * relocated according to its hashvalue relative to the new size. 408 * Old table is deleted after the relocation. 409 */ 410 static void 411 grow_dictionary(db_dict_desc_p dd) 412 { 413 unsigned int oldsize, i, new_size; 414 db_table_desc_p * oldtab, *newtab; 415 416 oldsize = dd->tables.tables_len; 417 oldtab = dd->tables.tables_val; 418 419 new_size = get_next_hashsize(oldsize); 420 421 if (new_size > CALLOC_LIMIT) { 422 FATAL("db_dictionary::grow: table size exceeds calloc limit", 423 DB_MEMORY_LIMIT); 424 } 425 426 if ((newtab = (db_table_desc_p*) 427 calloc((unsigned int) new_size, 428 sizeof (db_table_desc_p))) == NULL) { 429 FATAL("db_dictionary::grow: cannot allocate space", 430 DB_MEMORY_LIMIT); 431 } 432 433 if (oldtab != NULL) { // must transfer contents of old to new 434 for (i = 0; i < oldsize; i++) { 435 relocate_bucket(oldtab[i], newtab, new_size); 436 } 437 delete oldtab; // delete old hashtable 438 } 439 440 dd->tables.tables_val = newtab; 441 dd->tables.tables_len = new_size; 442 } 443 444 #define HASHSHIFT 3 445 #define HASHMASK 0x1f 446 447 static u_int 448 get_hashval(char *value) 449 { 450 int i, len; 451 u_int hval = 0; 452 453 len = strlen(value); 454 for (i = 0; i < len; i++) { 455 hval = ((hval<<HASHSHIFT)^hval); 456 hval += (value[i] & HASHMASK); 457 } 458 459 return (hval); 460 } 461 462 static db_status 463 enumerate_dictionary(db_dict_desc *dd, db_status (*func) (db_table_desc*)) 464 { 465 int i; 466 db_table_desc *bucket; 467 db_status status; 468 469 if (dd == NULL) 470 return (DB_SUCCESS); 471 472 for (i = 0; i < dd->tables.tables_len; i++) { 473 bucket = dd->tables.tables_val[i]; 474 if (bucket) { 475 status = enumerate_bucket(bucket, func); 476 if (status != DB_SUCCESS) 477 return (status); 478 } 479 } 480 481 return (DB_SUCCESS); 482 } 483 484 485 /* 486 * Look up target table_name in hashtable and return its db_table_desc. 487 * Return NULL if not found. 488 */ 489 static db_table_desc * 490 search_dictionary(db_dict_desc *dd, char *target) 491 { 492 register unsigned long hval; 493 unsigned long bucket; 494 495 if (target == NULL || dd == NULL || dd->tables.tables_len == 0) 496 return (NULL); 497 498 hval = get_hashval(target); 499 bucket = hval % dd->tables.tables_len; 500 501 db_table_desc_p fst = dd->tables.tables_val[bucket]; 502 503 if (fst != NULL) 504 return (search_bucket(fst, hval, target)); 505 else 506 return (NULL); 507 } 508 509 /* 510 * Remove the entry with the target table_name from the dictionary. 511 * If successful, return DB_SUCCESS; otherwise DB_NOTUNIQUE if target 512 * is null; DB_NOTFOUND if entry is not found. 513 * If successful, decrement count of number of entries in hash table. 514 */ 515 static db_status 516 remove_from_dictionary(db_dict_desc *dd, char *target, bool_t remove_storage) 517 { 518 register unsigned long hval; 519 unsigned long bucket; 520 register db_table_desc *fst; 521 522 if (target == NULL) 523 return (DB_NOTUNIQUE); 524 if (dd == NULL || dd->tables.tables_len == 0) 525 return (DB_NOTFOUND); 526 hval = get_hashval(target); 527 bucket = hval % dd->tables.tables_len; 528 fst = dd->tables.tables_val[bucket]; 529 if (fst == NULL) 530 return (DB_NOTFOUND); 531 if (remove_from_bucket(fst, &dd->tables.tables_val[bucket], 532 hval, target, remove_storage)) { 533 --(dd->count); 534 return (DB_SUCCESS); 535 } else 536 return (DB_NOTFOUND); 537 } 538 539 /* 540 * Add a new db_table_desc to the dictionary. 541 * Return DB_NOTUNIQUE, if entry with identical table_name 542 * already exists. If entry is added, return DB_SUCCESS. 543 * Increment count of number of entries in index table and grow table 544 * if number of entries equals size of table. 545 * 546 * Inputs: db_dict_desc_p dd pointer to dictionary to add to. 547 * db_table_desc *td pointer to table entry to be added. The 548 * db_table_desc.next field will be altered 549 * without regard to it's current setting. 550 * This means that if next points to a list of 551 * table entries, they may be either linked into 552 * the dictionary unexpectly or cut off (leaked). 553 */ 554 static db_status 555 add_to_dictionary(db_dict_desc_p dd, db_table_desc *td) 556 { 557 register unsigned long hval; 558 char *target; 559 560 if (dd == NULL) 561 return (DB_NOTFOUND); 562 563 if (td == NULL) 564 return (DB_NOTFOUND); 565 target = td->table_name; 566 if (target == NULL) 567 return (DB_NOTUNIQUE); 568 569 hval = get_hashval(target); 570 571 if (dd->tables.tables_val == NULL) 572 grow_dictionary(dd); 573 574 db_table_desc_p fst; 575 unsigned long bucket; 576 bucket = hval % dd->tables.tables_len; 577 fst = dd->tables.tables_val[bucket]; 578 td->hashval = hval; 579 if (fst == NULL) { /* Empty bucket */ 580 dd->tables.tables_val[bucket] = td; 581 } else if (!add_to_bucket(fst, &dd->tables.tables_val[bucket], td)) { 582 return (DB_NOTUNIQUE); 583 } 584 585 /* increase hash table size if number of entries equals table size */ 586 if (++(dd->count) > dd->tables.tables_len) 587 grow_dictionary(dd); 588 589 return (DB_SUCCESS); 590 } 591 592 /* ******************* pickling routines for dictionary ******************* */ 593 594 595 /* Does the actual writing to/from file specific for db_dict_desc structure. */ 596 static bool_t 597 transfer_aux(XDR* x, pptr tbl) 598 { 599 return (xdr_db_dict_desc_p(x, (db_dict_desc_p *) tbl)); 600 } 601 602 class pickle_dict_desc: public pickle_file { 603 public: 604 pickle_dict_desc(char *f, pickle_mode m) : pickle_file(f, m) {} 605 606 /* Transfers db_dict_desc structure pointed to by dp to/from file. */ 607 int transfer(db_dict_desc_p * dp) 608 { return (pickle_file::transfer((pptr) dp, &transfer_aux)); } 609 }; 610 611 /* ************************ dictionary methods *************************** */ 612 613 db_dictionary::db_dictionary() 614 { 615 dictionary = NULL; 616 initialized = FALSE; 617 filename = NULL; 618 tmpfilename = NULL; 619 logfilename = NULL; 620 logfile = NULL; 621 logfile_opened = FALSE; 622 changed = FALSE; 623 INITRW(dict); 624 READLOCKOK(dict); 625 } 626 627 /* 628 * This routine clones an entire hash bucket chain. If you clone a 629 * data dictionary entry with the ->next pointer set, you will get a 630 * clone of that entry, as well as the entire linked list. This can cause 631 * pain if you then pass the cloned bucket to routines such as 632 * add_to_dictionary(), which do not expect nor handle dictionary hash 633 * entries with the ->next pointer set. You might either get duplicate 634 * entires or lose entries. If you wish to clone the entire bucket chain 635 * and add it to a new dictionary, loop through the db_table_desc->next list 636 * and call add_to_dictionary() for each item. 637 */ 638 int 639 db_dictionary::db_clone_bucket(db_table_desc *bucket, db_table_desc **clone) 640 { 641 u_long size; 642 XDR xdrs; 643 char *bufin = NULL; 644 645 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_clone_bucket"); 646 db_func use_this = xdr_db_table_desc; 647 size = xdr_sizeof((xdrproc_t) use_this, (void *) bucket); 648 bufin = (char *) calloc(1, (size_t) size * sizeof (char)); 649 if (!bufin) { 650 READUNLOCK(this, DB_MEMORY_LIMIT, 651 "db_dictionary::insert_modified_table: out of memory"); 652 FATAL3("db_dictionary::insert_modified_table: out of memory", 653 DB_MEMORY_LIMIT, 0); 654 } 655 xdrmem_create(&xdrs, bufin, (size_t) size, XDR_ENCODE); 656 if (!xdr_db_table_desc(&xdrs, bucket)) { 657 free(bufin); 658 xdr_destroy(&xdrs); 659 READUNLOCK(this, DB_MEMORY_LIMIT, 660 "db_dictionary::insert_modified_table: xdr encode failed"); 661 FATAL3( 662 "db_dictionary::insert_modified_table: xdr encode failed.", 663 DB_MEMORY_LIMIT, 0); 664 } 665 *clone = (db_table_desc *) calloc(1, (size_t) size * sizeof (char)); 666 if (!*clone) { 667 xdr_destroy(&xdrs); 668 free(bufin); 669 READUNLOCK(this, DB_MEMORY_LIMIT, 670 "db_dictionary::insert_modified_table: out of memory"); 671 FATAL3("db_dictionary::insert_modified_table: out of memory", 672 DB_MEMORY_LIMIT, 0); 673 } 674 675 xdrmem_create(&xdrs, bufin, (size_t) size, XDR_DECODE); 676 if (!xdr_db_table_desc(&xdrs, *clone)) { 677 free(bufin); 678 free(*clone); 679 xdr_destroy(&xdrs); 680 READUNLOCK(this, DB_MEMORY_LIMIT, 681 "db_dictionary::insert_modified_table: xdr encode failed"); 682 FATAL3( 683 "db_dictionary::insert_modified_table: xdr encode failed.", 684 DB_MEMORY_LIMIT, 0); 685 } 686 free(bufin); 687 xdr_destroy(&xdrs); 688 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_clone_bucket"); 689 return (1); 690 } 691 692 693 int 694 db_dictionary::change_table_name(db_table_desc *clone, char *tok, char *repl) 695 { 696 char *newname; 697 char *loc_end, *loc_beg; 698 699 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::change_table_name"); 700 while (clone) { 701 /* 702 * Special case for a tok="". This is used for the 703 * nisrestore(1M), when restoring a replica in another 704 * domain. This routine is used to change the datafile 705 * names in the data.dict (see bugid #4031273). This will not 706 * effect massage_dict(), since it never generates an empty 707 * string for tok. 708 */ 709 if (strlen(tok) == 0) { 710 strcat(clone->table_name, repl); 711 clone = clone->next; 712 continue; 713 } 714 newname = (char *) calloc(1, sizeof (char) * 715 strlen(clone->table_name) + 716 strlen(repl) - strlen(tok) + 1); 717 if (!newname) { 718 WRITEUNLOCK(this, DB_MEMORY_LIMIT, 719 "db_dictionary::change_table_name: out of memory"); 720 FATAL3("db_dictionary::change_table_name: out of memory.", 721 DB_MEMORY_LIMIT, 0); 722 } 723 if (loc_beg = strstr(clone->table_name, tok)) { 724 loc_end = loc_beg + strlen(tok); 725 int s = loc_beg - clone->table_name; 726 memcpy(newname, clone->table_name, s); 727 strcat(newname + s, repl); 728 strcat(newname, loc_end); 729 free(clone->table_name); 730 clone->table_name = newname; 731 } else { 732 free(newname); 733 } 734 clone = clone->next; 735 } 736 WRITEUNLOCK(this, DB_LOCK_ERROR, 737 "wu db_dictionary::change_table_name"); 738 return (1); 739 } 740 741 742 #ifdef curdict 743 #undef curdict 744 #endif 745 /* 746 * A function to initialize the temporary dictionary from the real 747 * dictionary. 748 */ 749 bool_t 750 db_dictionary::inittemp(char *dictname, db_dictionary& curdict) 751 { 752 int status; 753 db_table_desc_p *newtab; 754 755 db_shutdown(); 756 757 WRITELOCK(this, FALSE, "w db_dictionary::inittemp"); 758 if (initialized) { 759 /* Someone else got in between db_shutdown() and lock() */ 760 WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp"); 761 return (TRUE); 762 } 763 764 pickle_dict_desc f(dictname, PICKLE_READ); 765 filename = strdup(dictname); 766 if (filename == NULL) { 767 WRITEUNLOCK(this, FALSE, 768 "db_dictionary::inittemp: could not allocate space"); 769 FATAL3("db_dictionary::inittemp: could not allocate space", 770 DB_MEMORY_LIMIT, FALSE); 771 } 772 int len = strlen(filename); 773 tmpfilename = new char[len+5]; 774 if (tmpfilename == NULL) { 775 delete filename; 776 WRITEUNLOCK(this, FALSE, 777 "db_dictionary::inittemp: could not allocate space"); 778 FATAL3("db_dictionary::inittemp: could not allocate space", 779 DB_MEMORY_LIMIT, FALSE); 780 } 781 logfilename = new char[len+5]; 782 if (logfilename == NULL) { 783 delete filename; 784 delete tmpfilename; 785 WRITEUNLOCK(this, FALSE, 786 "db_dictionary::inittemp: cannot allocate space"); 787 FATAL3("db_dictionary::inittemp: cannot allocate space", 788 DB_MEMORY_LIMIT, FALSE); 789 } 790 791 sprintf(tmpfilename, "%s.tmp", filename); 792 sprintf(logfilename, "%s.log", filename); 793 unlink(tmpfilename); /* get rid of partial checkpoints */ 794 dictionary = NULL; 795 796 if ((status = f.transfer(&dictionary)) < 0) { 797 initialized = FALSE; 798 } else if (status == 1) { /* no dictionary exists, create one */ 799 dictionary = new db_dict_desc; 800 if (dictionary == NULL) { 801 WRITEUNLOCK(this, FALSE, 802 "db_dictionary::inittemp: could not allocate space"); 803 FATAL3( 804 "db_dictionary::inittemp: could not allocate space", 805 DB_MEMORY_LIMIT, FALSE); 806 } 807 dictionary->tables.tables_len = 808 curdict.dictionary->tables.tables_len; 809 if ((newtab = (db_table_desc_p *) calloc( 810 (unsigned int) dictionary->tables.tables_len, 811 sizeof (db_table_desc_p))) == NULL) { 812 WRITEUNLOCK(this, FALSE, 813 "db_dictionary::inittemp: cannot allocate space"); 814 FATAL3( 815 "db_dictionary::inittemp: cannot allocate space", 816 DB_MEMORY_LIMIT, 0); 817 } 818 dictionary->tables.tables_val = newtab; 819 dictionary->count = 0; 820 dictionary->impl_vers = curdict.dictionary->impl_vers; 821 initialized = TRUE; 822 } else /* dictionary loaded successfully */ 823 initialized = TRUE; 824 825 if (initialized == TRUE) { 826 changed = FALSE; 827 reset_log(); 828 } 829 830 WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp"); 831 return (initialized); 832 } 833 834 835 /* 836 * This method replaces the token string specified with the replacment 837 * string specified. It assumes that at least one and only one instance of 838 * the token exists. It is the responsibility of the caller to ensure that 839 * the above assumption stays valid. 840 */ 841 db_status 842 db_dictionary::massage_dict(char *newdictname, char *tok, char *repl) 843 { 844 int retval; 845 u_int i, tbl_count; 846 db_status status; 847 db_table_desc *bucket, *np, *clone, *next_np; 848 char tail[NIS_MAXNAMELEN]; 849 db_dictionary *tmpptr; 850 851 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::massage_dict"); 852 if (dictionary == NULL) { 853 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 854 "db_dictionary::massage_dict: uninitialized dictionary file"); 855 FATAL3( 856 "db_dictionary::massage_dict: uninitialized dictionary file.", 857 DB_INTERNAL_ERROR, DB_INTERNAL_ERROR); 858 } 859 860 if ((tbl_count = dictionary->count) == 0) { 861 WRITEUNLOCK(this, DB_SUCCESS, 862 "wu db_dictionary::massage_dict"); 863 return (DB_SUCCESS); 864 } 865 866 /* First checkpoint */ 867 if ((status = checkpoint()) != DB_SUCCESS) { 868 WRITEUNLOCK(this, status, "wu db_dictionary::massage_dict"); 869 return (status); 870 } 871 872 #ifdef DEBUG 873 enumerate_dictionary(dictionary, &print_table); 874 #endif 875 876 /* Initialize the free dictionary so that we can start populating it */ 877 FreeDictionary->inittemp(newdictname, *this); 878 879 for (i = 0; i < dictionary->tables.tables_len; i++) { 880 bucket = dictionary->tables.tables_val[i]; 881 if (bucket) { 882 np = bucket; 883 while (np != NULL) { 884 next_np = np->next; 885 retval = db_clone_bucket(np, &clone); 886 if (retval != 1) { 887 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 888 "wu db_dictionary::massage_dict"); 889 return (DB_INTERNAL_ERROR); 890 } 891 if (change_table_name(clone, tok, repl) == -1) { 892 delete_table_desc(clone); 893 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 894 "wu db_dictionary::massage_dict"); 895 return (DB_INTERNAL_ERROR); 896 } 897 /* 898 * We know we don't have a log file, so we will 899 * just add to the in-memory database and dump 900 * all of it once we are done. 901 */ 902 status = add_to_dictionary 903 (FreeDictionary->dictionary, 904 clone); 905 if (status != DB_SUCCESS) { 906 delete_table_desc(clone); 907 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 908 "wu db_dictionary::massage_dict"); 909 return (DB_INTERNAL_ERROR); 910 } 911 status = remove_from_dictionary(dictionary, 912 np->table_name, TRUE); 913 if (status != DB_SUCCESS) { 914 delete_table_desc(clone); 915 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 916 "wu db_dictionary::massage_dict"); 917 return (DB_INTERNAL_ERROR); 918 } 919 np = next_np; 920 } 921 } 922 } 923 924 if (FreeDictionary->dump() != DB_SUCCESS) { 925 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 926 "wu db_dictionary::massage_dict"); 927 FATAL3( 928 "db_dictionary::massage_dict: Unable to dump new dictionary.", 929 DB_INTERNAL_ERROR, DB_INTERNAL_ERROR); 930 } 931 932 /* 933 * Now, shutdown the inuse dictionary and update the FreeDictionary 934 * and InUseDictionary pointers as well. Also, delete the old dictionary 935 * file. 936 */ 937 unlink(filename); /* There shouldn't be a tmpfile or logfile */ 938 db_shutdown(); 939 tmpptr = InUseDictionary; 940 InUseDictionary = FreeDictionary; 941 FreeDictionary = tmpptr; 942 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::massage_dict"); 943 return (DB_SUCCESS); 944 } 945 946 947 db_status 948 db_dictionary::merge_dict(db_dictionary& tempdict, char *tok, char *repl) 949 { 950 951 db_status dbstat = DB_SUCCESS; 952 953 db_table_desc *tbl = NULL, *clone = NULL, *next_td = NULL; 954 int retval, i; 955 956 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::merge_dict"); 957 958 for (i = 0; i < tempdict.dictionary->tables.tables_len; ++i) { 959 tbl = tempdict.dictionary->tables.tables_val[i]; 960 if (!tbl) 961 continue; 962 retval = db_clone_bucket(tbl, &clone); 963 if (retval != 1) { 964 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 965 "wu db_dictionary::merge_dict"); 966 return (DB_INTERNAL_ERROR); 967 } 968 while (clone) { 969 next_td = clone->next; 970 clone->next = NULL; 971 if ((tok) && 972 (change_table_name(clone, tok, repl) == -1)) { 973 delete_table_desc(clone); 974 if (next_td) 975 delete_table_desc(next_td); 976 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 977 "wu db_dictionary::merge_dict"); 978 return (DB_INTERNAL_ERROR); 979 } 980 981 dbstat = add_to_dictionary(dictionary, clone); 982 if (dbstat == DB_NOTUNIQUE) { 983 /* Overide */ 984 dbstat = remove_from_dictionary(dictionary, 985 clone->table_name, TRUE); 986 if (dbstat != DB_SUCCESS) { 987 WRITEUNLOCK(this, dbstat, 988 "wu db_dictionary::merge_dict"); 989 return (dbstat); 990 } 991 dbstat = add_to_dictionary(dictionary, 992 clone); 993 } else { 994 if (dbstat != DB_SUCCESS) { 995 WRITEUNLOCK(this, dbstat, 996 "wu db_dictionary::merge_dict"); 997 return (dbstat); 998 } 999 } 1000 clone = next_td; 1001 } 1002 } 1003 /* 1004 * If we were successful in merging the dictionaries, then mark the 1005 * dictionary changed, so that it will be properly checkpointed and 1006 * dumped to disk. 1007 */ 1008 if (dbstat == DB_SUCCESS) 1009 changed = TRUE; 1010 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::merge_dict"); 1011 return (dbstat); 1012 } 1013 1014 int 1015 db_dictionary::copyfile(char *infile, char *outfile) 1016 { 1017 db_table_desc *tbl = NULL; 1018 db *dbase; 1019 int ret; 1020 1021 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile"); 1022 /* 1023 * We need to hold the read-lock until the dump() is done. 1024 * However, we must avoid the lock migration (read -> write) 1025 * that would happen in find_table() if the db must be loaded. 1026 * Hence, look first look for an already loaded db. 1027 */ 1028 dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE); 1029 if (dbase == NULL) { 1030 /* Release the read-lock, and try again, allowing load */ 1031 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::copyfile"); 1032 dbase = find_table(infile, &tbl, TRUE, TRUE, TRUE); 1033 if (dbase == NULL) 1034 return (DB_NOTFOUND); 1035 /* 1036 * Read-lock again, and get a 'tbl' we can use since we're 1037 * still holding the lock. 1038 */ 1039 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile"); 1040 dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE); 1041 if (dbase == NULL) { 1042 READUNLOCK(this, DB_NOTFOUND, 1043 "ru db_dictionary::copyfile"); 1044 return (DB_NOTFOUND); 1045 } 1046 } 1047 ret = tbl->database->dump(outfile) ? DB_SUCCESS : DB_INTERNAL_ERROR; 1048 READUNLOCK(this, ret, "ru db_dictionary::copyfile"); 1049 return (ret); 1050 } 1051 1052 1053 bool_t 1054 db_dictionary::extract_entries(db_dictionary& tempdict, char **fs, int fscnt) 1055 { 1056 int i, retval; 1057 db_table_desc *tbl, *clone; 1058 db_table_desc tbl_ent; 1059 db_status dbstat; 1060 1061 READLOCK(this, FALSE, "r db_dictionary::extract_entries"); 1062 for (i = 0; i < fscnt; ++i) { 1063 tbl = find_table_desc(fs[i]); 1064 if (!tbl) { 1065 syslog(LOG_DEBUG, 1066 "extract_entries: no dictionary entry for %s", 1067 fs[i]); 1068 READUNLOCK(this, FALSE, 1069 "ru db_dictionary::extract_entries"); 1070 return (FALSE); 1071 } else { 1072 tbl_ent.table_name = tbl->table_name; 1073 tbl_ent.hashval = tbl->hashval; 1074 tbl_ent.scheme = tbl->scheme; 1075 tbl_ent.database = tbl->database; 1076 tbl_ent.next = NULL;