Home | History | Annotate | Download | only in libnisdb
      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;