/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include "db_headers.h"
#include "db_entry.h"
#include "db_dictionary.h"
#include "db_dictlog.h"
#include "db_vers.h"
#include "nisdb_mt.h"
#include "nisdb_rw.h"
#include "ldap_parse.h"
#include "ldap_map.h"
#include "nis_hashitem.h"
#include "ldap_util.h"
#include "nis_db.h"
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#ifdef TDRPC
#include <sysent.h>
#endif
#include <unistd.h>
#include <syslog.h>
extern db_dictionary *InUseDictionary;
extern db_dictionary *FreeDictionary;
/* *************** dictionary version ****************** */
#define DB_MAJOR 0
/*
* Checks for valid version. For now, there are two:
* DB_VERSION_ORIG was the ORIGINAL one with major = 0, minor = 9
* DB_CURRENT_VERSION is the latest one with changes in the database format
* for entry objects and the change in the dictionary format.
*
* Our current implementation can support both versions.
*/
static inline bool_t
{
}
static char *
{
return (vstr);
}
/*
* Special XDR version that checks for a valid version number.
* If we don't find an acceptable version, exit immediately instead
* of continuing to xdr rest of dictionary, which might lead to
* a core dump if the formats between versions are incompatible.
* In the future, there might be a switch to determine versions
* and their corresponding XDR routines for the rest of the dictionary.
*/
extern "C" {
{
"db_dictionary: invalid dictionary format! Expecting %s",
"db_dictionary: invalid dictionary format! Expecting %s\n",
exit(1);
}
return (FALSE);
return (TRUE);
}
void
{
v->zero();
}
};
/* ******************* dictionary data structures *************** */
/* Delete contents of single db_table_desc pointed to by 'current.' */
static void
{
delete current;
}
/* Create new table descriptor using given table name and table_object. */
{
"db_dictionary::add_table: could not allocate space for new table",
}
"db_dictionary::add_table: could not allocate space for scheme",
}
"db_dictionary::add_table: could not translate table_obj to scheme");
return (DB_BADOBJECT);
}
"db_dictionary::add_table: could not allocate space for table name",
}
if (answer)
return (DB_SUCCESS);
}
/* Delete list of db_table_desc pointed to by 'head.' */
static void
{
}
}
static void
{
int i;
if (dict) {
/* delete each bucket */
if (bucket)
/* delete table */
}
/* delete dictionary */
delete dict;
}
}
/* Relocate bucket starting with this entry to new hashtable 'new_tab'. */
static void
{
}
}
/*
* Return pointer to entry with same hash value and table_name
* as those supplied. Returns NULL if not found.
*/
static db_status
{
if (status != DB_SUCCESS)
return (status);
}
return (DB_SUCCESS);
}
/*
* Return pointer to entry with same hash value and table_name
* as those supplied. Returns NULL if not found.
*/
static db_table_desc_p
{
break;
}
}
return (np);
}
/*
* Remove entry with the specified hashvalue and target table name.
* Returns 'TRUE' if successful, FALSE otherwise.
* If the entry being removed is at the head of the list, then
* the head is updated to reflect the removal. The storage for the
* entry is freed if desired.
*/
static bool_t
{
/* Search for it in the bucket */
break;
} else {
}
}
return (FALSE); // cannot delete if it is not there
} else {
}
if (free_storage)
return (TRUE);
}
/*
* Add given entry to the bucket pointed to by 'bucket'.
* If an entry with the same table_name is found, no addition
* is done. The entry is added to the head of the bucket.
*/
static bool_t
{
register char *target_name;
unsigned long target_hval;
/* Search for it in the bucket */
break;
} else {
}
}
return (FALSE); /* duplicates not allowed */
return (TRUE);
}
/* Print bucket starting with this entry. */
static void
{
}
}
static db_status
{
return (DB_BADTABLE);
return (DB_SUCCESS);
}
11,
53,
113,
337,
977,
2053,
4073,
8011,
16001,
0
};
// prevents wrap around numbers from being passed
/* Returns the next size to use for the hash table */
static unsigned int
{
long unsigned newsize, n;
if (oldsize == 0)
else {
break;
}
if (newsize == 0)
}
return (newsize);
}
/*
* Grow the current hashtable upto the next size.
* The contents of the existing hashtable is copied to the new one and
* relocated according to its hashvalue relative to the new size.
* Old table is deleted after the relocation.
*/
static void
{
if (new_size > CALLOC_LIMIT) {
FATAL("db_dictionary::grow: table size exceeds calloc limit",
}
if ((newtab = (db_table_desc_p*)
sizeof (db_table_desc_p))) == NULL) {
FATAL("db_dictionary::grow: cannot allocate space",
}
for (i = 0; i < oldsize; i++) {
}
delete oldtab; // delete old hashtable
}
}
static u_int
{
int i, len;
for (i = 0; i < len; i++) {
}
return (hval);
}
static db_status
{
int i;
return (DB_SUCCESS);
if (bucket) {
if (status != DB_SUCCESS)
return (status);
}
}
return (DB_SUCCESS);
}
/*
* Look up target table_name in hashtable and return its db_table_desc.
* Return NULL if not found.
*/
static db_table_desc *
{
register unsigned long hval;
unsigned long bucket;
return (NULL);
else
return (NULL);
}
/*
* Remove the entry with the target table_name from the dictionary.
* If successful, return DB_SUCCESS; otherwise DB_NOTUNIQUE if target
* is null; DB_NOTFOUND if entry is not found.
* If successful, decrement count of number of entries in hash table.
*/
static db_status
{
register unsigned long hval;
unsigned long bucket;
return (DB_NOTUNIQUE);
return (DB_NOTFOUND);
return (DB_NOTFOUND);
return (DB_SUCCESS);
} else
return (DB_NOTFOUND);
}
/*
* Add a new db_table_desc to the dictionary.
* Return DB_NOTUNIQUE, if entry with identical table_name
* already exists. If entry is added, return DB_SUCCESS.
* Increment count of number of entries in index table and grow table
* if number of entries equals size of table.
*
* Inputs: db_dict_desc_p dd pointer to dictionary to add to.
* db_table_desc *td pointer to table entry to be added. The
* db_table_desc.next field will be altered
* without regard to it's current setting.
* This means that if next points to a list of
* table entries, they may be either linked into
* the dictionary unexpectly or cut off (leaked).
*/
static db_status
{
register unsigned long hval;
char *target;
return (DB_NOTFOUND);
return (DB_NOTFOUND);
return (DB_NOTUNIQUE);
unsigned long bucket;
return (DB_NOTUNIQUE);
}
/* increase hash table size if number of entries equals table size */
return (DB_SUCCESS);
}
/* ******************* pickling routines for dictionary ******************* */
static bool_t
{
}
public:
};
/* ************************ dictionary methods *************************** */
{
dictionary = NULL;
initialized = FALSE;
tmpfilename = NULL;
logfilename = NULL;
}
/*
* This routine clones an entire hash bucket chain. If you clone a
* data dictionary entry with the ->next pointer set, you will get a
* clone of that entry, as well as the entire linked list. This can cause
* pain if you then pass the cloned bucket to routines such as
* add_to_dictionary(), which do not expect nor handle dictionary hash
* entries with the ->next pointer set. You might either get duplicate
* entires or lose entries. If you wish to clone the entire bucket chain
* and add it to a new dictionary, loop through the db_table_desc->next list
* and call add_to_dictionary() for each item.
*/
int
{
if (!bufin) {
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: out of memory");
FATAL3("db_dictionary::insert_modified_table: out of memory",
DB_MEMORY_LIMIT, 0);
}
xdr_destroy(&xdrs);
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: xdr encode failed");
"db_dictionary::insert_modified_table: xdr encode failed.",
DB_MEMORY_LIMIT, 0);
}
if (!*clone) {
xdr_destroy(&xdrs);
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: out of memory");
FATAL3("db_dictionary::insert_modified_table: out of memory",
DB_MEMORY_LIMIT, 0);
}
xdr_destroy(&xdrs);
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: xdr encode failed");
"db_dictionary::insert_modified_table: xdr encode failed.",
DB_MEMORY_LIMIT, 0);
}
xdr_destroy(&xdrs);
return (1);
}
int
{
char *newname;
while (clone) {
/*
* Special case for a tok="". This is used for the
* nisrestore(1M), when restoring a replica in another
* domain. This routine is used to change the datafile
* names in the data.dict (see bugid #4031273). This will not
* effect massage_dict(), since it never generates an empty
* string for tok.
*/
continue;
}
if (!newname) {
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::change_table_name: out of memory");
FATAL3("db_dictionary::change_table_name: out of memory.",
DB_MEMORY_LIMIT, 0);
}
} else {
}
}
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::change_table_name");
return (1);
}
#ifdef curdict
#endif
/*
* A function to initialize the temporary dictionary from the real
* dictionary.
*/
{
int status;
db_shutdown();
if (initialized) {
/* Someone else got in between db_shutdown() and lock() */
return (TRUE);
}
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: could not allocate space");
FATAL3("db_dictionary::inittemp: could not allocate space",
}
if (tmpfilename == NULL) {
delete filename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: could not allocate space");
FATAL3("db_dictionary::inittemp: could not allocate space",
}
if (logfilename == NULL) {
delete filename;
delete tmpfilename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: cannot allocate space");
FATAL3("db_dictionary::inittemp: cannot allocate space",
}
dictionary = NULL;
initialized = FALSE;
dictionary = new db_dict_desc;
if (dictionary == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: could not allocate space");
"db_dictionary::inittemp: could not allocate space",
}
sizeof (db_table_desc_p))) == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: cannot allocate space");
"db_dictionary::inittemp: cannot allocate space",
DB_MEMORY_LIMIT, 0);
}
dictionary->count = 0;
initialized = TRUE;
} else /* dictionary loaded successfully */
initialized = TRUE;
if (initialized == TRUE) {
reset_log();
}
return (initialized);
}
/*
* This method replaces the token string specified with the replacment
* string specified. It assumes that at least one and only one instance of
* the token exists. It is the responsibility of the caller to ensure that
* the above assumption stays valid.
*/
{
int retval;
if (dictionary == NULL) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"db_dictionary::massage_dict: uninitialized dictionary file");
"db_dictionary::massage_dict: uninitialized dictionary file.",
}
WRITEUNLOCK(this, DB_SUCCESS,
"wu db_dictionary::massage_dict");
return (DB_SUCCESS);
}
/* First checkpoint */
return (status);
}
#ifdef DEBUG
#endif
/* Initialize the free dictionary so that we can start populating it */
if (bucket) {
if (retval != 1) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
/*
* We know we don't have a log file, so we will
* just add to the in-memory database and dump
* all of it once we are done.
*/
clone);
if (status != DB_SUCCESS) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
if (status != DB_SUCCESS) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
}
}
}
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
"db_dictionary::massage_dict: Unable to dump new dictionary.",
}
/*
* Now, shutdown the inuse dictionary and update the FreeDictionary
* and InUseDictionary pointers as well. Also, delete the old dictionary
* file.
*/
db_shutdown();
return (DB_SUCCESS);
}
{
int retval, i;
if (!tbl)
continue;
if (retval != 1) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::merge_dict");
return (DB_INTERNAL_ERROR);
}
while (clone) {
if ((tok) &&
if (next_td)
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::merge_dict");
return (DB_INTERNAL_ERROR);
}
if (dbstat == DB_NOTUNIQUE) {
/* Overide */
if (dbstat != DB_SUCCESS) {
WRITEUNLOCK(this, dbstat,
"wu db_dictionary::merge_dict");
return (dbstat);
}
clone);
} else {
if (dbstat != DB_SUCCESS) {
WRITEUNLOCK(this, dbstat,
"wu db_dictionary::merge_dict");
return (dbstat);
}
}
}
}
/*
* If we were successful in merging the dictionaries, then mark the
* dictionary changed, so that it will be properly checkpointed and
* dumped to disk.
*/
if (dbstat == DB_SUCCESS)
return (dbstat);
}
int
{
int ret;
/*
* We need to hold the read-lock until the dump() is done.
* However, we must avoid the lock migration (read -> write)
* that would happen in find_table() if the db must be loaded.
* Hence, look first look for an already loaded db.
*/
/* Release the read-lock, and try again, allowing load */
return (DB_NOTFOUND);
/*
* Read-lock again, and get a 'tbl' we can use since we're
* still holding the lock.
*/
READUNLOCK(this, DB_NOTFOUND,
"ru db_dictionary::copyfile");
return (DB_NOTFOUND);
}
}
return (ret);
}
{
int i, retval;
for (i = 0; i < fscnt; ++i) {
if (!tbl) {
"extract_entries: no dictionary entry for %s",
fs[i]);
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
} else {
}
if (retval != 1) {
"extract_entries: unable to clone entry for %s",
fs[i]);
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
}
if (dbstat != DB_SUCCESS) {
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
}
}
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
}
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (TRUE);
}
/*
* Initialize dictionary from contents in 'file'.
* If there is already information in this dictionary, it is removed.
* Therefore, regardless of whether the load from the file succeeds,
* the contents of this dictionary will be altered. Returns
* whether table has been initialized successfully.
*/
{
int status;
db_shutdown();
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: could not allocate space");
FATAL3("db_dictionary::init: could not allocate space",
}
if (tmpfilename == NULL) {
delete filename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: could not allocate space");
FATAL3("db_dictionary::init: could not allocate space",
}
if (logfilename == NULL) {
delete filename;
delete tmpfilename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: cannot allocate space");
FATAL3("db_dictionary::init: cannot allocate space",
}
dictionary = NULL;
/* load dictionary */
initialized = FALSE;
dictionary = new db_dict_desc;
if (dictionary == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: could not allocate space");
FATAL3("db_dictionary::init: could not allocate space",
}
dictionary->count = 0;
initialized = TRUE;
} else /* dictionary loaded successfully */
initialized = TRUE;
if (initialized == TRUE) {
int num_changes = 0;
reset_log();
"incorporation of dictionary logfile '%s' failed",
changed = (num_changes > 0);
}
return (initialized);
}
/*
* Execute log entry 'j' on the dictionary identified by 'dict' if the
* version of j is later than that of the dictionary. If 'j' is executed,
* 'count' is incremented and the dictionary's verison is updated to
* that of 'j'.
* Returns TRUE always for valid log entries; FALSE otherwise.
*/
static bool_t
{
++ *count;
#ifdef DEBUG
j->print();
#endif /* DEBUG */
switch (j->get_action()) {
case DB_ADD_TABLE:
j->get_table_object(), INMEMORY_ONLY);
// ignore status
break;
case DB_REMOVE_TABLE:
// ignore status
break;
default:
WARNING("db::apply_log_entry: unknown action_type");
return (FALSE);
}
}
return (TRUE);
}
int
{
int ret;
return (ret);
}
/* Frees memory of filename and tables. Has no effect on disk storage. */
{
if (!initialized) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::db_shutdown");
return (DB_SUCCESS); /* DB_NOTFOUND? */
}
if (filename) {
delete filename;
}
if (tmpfilename) {
delete tmpfilename;
tmpfilename = NULL;
}
if (logfilename) {
delete logfilename;
logfilename = NULL;
}
if (dictionary) {
dictionary = NULL;
}
initialized = FALSE;
reset_log();
return (DB_SUCCESS);
}
/*
* Dump contents of this dictionary (minus the database representations)
* to its file. Returns 0 if operation succeeds, -1 otherwise.
*/
int
{
int status;
if (!initialized) {
return (-1);
}
if (status != 0) {
WARNING("db_dictionary::dump: could not write out dictionary");
WARNING_M("db_dictionary::dump: could not rename temp file: ");
status = -1;
}
return (status);
}
/*
* Write out in-memory copy of dictionary to file.
* 1. Update major version.
* 2. Dump contents to temporary file.
* 3. Rename temporary file to real dictionary file.
* 4. Remove log file.
* A checkpoint is done only if it has changed since the previous checkpoint.
* Returns DB_SUCCESS if checkpoint was successful; error code otherwise
*/
{
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::checkpoint");
return (DB_SUCCESS);
}
delete newv;
if (dump() != 0) {
"db_dictionary::checkpoint: could not dump dictionary: ");
delete oldv;
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::checkpoint");
return (DB_INTERNAL_ERROR);
}
reset_log(); /* should check for what? */
delete oldv;
return (DB_SUCCESS);
}
/* close existing logfile and delete its structure */
int
{
/* try to close old log file */
/* doesnot matter since we do synchronous writes only */
if (logfile_opened == TRUE) {
"db_dictionary::reset_log: could not close log file: ");
}
}
delete logfile;
}
return (0);
}
/* close existing logfile, but leave its structure if exists */
int
{
}
return (0);
}
/* open logfile, creating its structure if it does not exist */
int
{
NULL) {
"db_dictionary::reset_log: cannot allocate space",
DB_MEMORY_LIMIT, -1);
}
}
if (logfile_opened == TRUE) {
return (0);
}
WARNING_M("db_dictionary::open_log: could not open log file: ");
delete logfile;
return (-1);
}
return (0);
}
/*
* closes any open log files for all tables in dictionary or 'tab'.
* "tab" is an optional argument.
*/
static int close_standby_list();
{
if (!initialized) {
WRITEUNLOCK(this, DB_BADDICTIONARY,
"wu db_dictionary::db_standby");
return (DB_BADDICTIONARY);
}
close_log(); // close dictionary log
return (DB_SUCCESS);
}
return (DB_BADTABLE);
}
return (DB_SUCCESS);
}
/*
* Returns db_table_desc of table name 'tab'. 'prev', if supplied,
* is set to the entry located ahead of 'tab's entry in the dictionary.
*/
{
if (initialized)
else
return (ret);
}
/* If desired, look in the deferred dictionary first */
/* No result yet => search the "normal" dictionary */
return (ret);
}
db *
/* Most operations should use the deferred dictionary if it exists */
}
db *
}
db *
int lstat;
/* If found, or shouldn't try LDAP, we're done */
return (res);
/* See if we can retrieve the object from LDAP */
if (dstat != DB_SUCCESS) {
if (dstat == DB_NOTFOUND) {
if (lstat != LDAP_SUCCESS) {
"%s: LDAP error for \"%s\": %s",
}
} else {
"%s: DB error %d for \"%s\"",
}
return (0);
}
/* Try the dictionary again */
return (res);
}
/*
* Return database structure of table named by 'tab'.
* If 'where' is set, set it to the table_desc of 'tab.'
* If the database is loaded in from stable store if it has not been loaded.
* If it cannot be loaded, it is initialized using the scheme stored in
* the table_desc. NULL is returned if the initialization fails.
*/
db *
{
if (!initialized)
return (NULL);
int lret;
return (NULL); // not found
}
}
/* Re-check; some other thread might have loaded the db */
}
WRITEUNLOCK(this, NULL,
"db_dictionary::find_table: could not allocate space");
FATAL3("db_dictionary::find_table: could not allocate space",
}
/*
* Lock the newly created 'dbase', so we can release the general
* db_dictionary lock.
*/
if (lret != 0) {
WRITEUNLOCK(this, NULL,
"db_dictionary::find_table: could not lock dbase");
FATAL3("db_dictionary::find_table: could not lock dbase",
}
/* Assign tbl->database, and then release the 'this' lock */
return (dbase);
}
delete dbase;
WARNING("db_dictionary::find_table: could not load database");
return (NULL);
}
/* Log action to be taken on the dictionary and update db_update_version. */
{
if (open_log() < 0) {
delete newv;
WRITEUNLOCK(this, DB_STORAGE_LIMIT,
"wu db_dictionary::log_action");
return (DB_STORAGE_LIMIT);
}
WARNING_M("db::log_action: could not add log entry: ");
close_log();
delete newv;
WRITEUNLOCK(this, DB_STORAGE_LIMIT,
"wu db_dictionary::log_action");
return (DB_STORAGE_LIMIT);
}
delete newv;
return (DB_SUCCESS);
}
// For a complete 'delete' operation, we want the following behaviour:
// 1. If there is an entry in the log, the physical table exists and is
// stable.
// 2. If there is no entry in the log, the physical table may or may not
// exist.
{
if (!initialized) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::delete_table_aux");
return (DB_BADDICTIONARY);
}
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::delete_table_aux");
return (DB_NOTFOUND);
}
if (mode != INMEMORY_ONLY) {
int need_free = 0;
// Update log.
if (status != DB_SUCCESS) {
WRITEUNLOCK(this, status,
"wu db_dictionary::delete_table_aux");
return (status);
}
// Remove physical structures
need_free = 1;
}
"db_dictionary::delete_table: could not create db structure");
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"wu db_dictionary::delete_table_aux");
return (DB_MEMORY_LIMIT);
}
if (need_free)
delete dbase;
}
// Remove in-memory structures
return (ret);
}
/*
* Delete table with given name 'tab' from dictionary.
* Returns error code if table does not exist or if dictionary has not been
* initialized. Dictionary is updated to stable store if deletion is
* successful. Fatal error occurs if dictionary cannot be saved.
* Returns DB_SUCCESS if dictionary has been updated successfully.
* Note that the files associated with the table are also removed.
*/
{
}
// For a complete 'add' operation, we want the following behaviour:
// 1. If there is an entry in the log, then the physical table exists and
// has been initialized properly.
// 2. If there is no entry in the log, the physical table may or may not
// exist. In this case, we don't really care because we cannot get at
// it. The next time we add a table with the same name to the dictionary,
// it will be initialized properly.
// This mode is used when the table is first created.
//
// For an INMEMORY_ONLY operation, only the internal structure is created and
// updated. This mode is used when the database gets loaded and the internal
// dictionary gets updated from the log entries.
{
if (!initialized) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::add_table_aux");
return (DB_BADDICTIONARY);
}
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::add_table_aux");
return (DB_NOTUNIQUE); // table already exists
}
// create data structures for table
if (status != DB_SUCCESS) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::add_table_aux");
return (status);
}
if (mode != INMEMORY_ONLY) {
// create physical structures for table
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::add_table: could not allocate space for db");
"db_dictionary::add_table: could not allocate space for db",
}
"db_dictionary::add_table: could not initialize database from scheme");
WRITEUNLOCK(this, DB_STORAGE_LIMIT,
"wu db_dictionary::add_table_aux");
return (DB_STORAGE_LIMIT);
}
// update 'external' copy of dictionary
if (status != DB_SUCCESS) {
WRITEUNLOCK(this, status,
"wu db_dictionary::add_table_aux");
return (status);
}
}
// finally, update in-memory copy of dictionary
return (ret);
}
/*
* Add table with given name 'tab' and description 'zdesc' to dictionary.
* Returns errror code if table already exists, or if no memory can be found
* to store the descriptor, or if dictionary has not been intialized.
* Dictionary is updated to stable store if addition is successful.
* Fatal error occurs if dictionary cannot be saved.
* Returns DB_SUCCESS if dictionary has been updated successfully.
*/
{
}
/*
* Translate given NIS attribute list to a db_query structure.
* Return FALSE if dictionary has not been initialized, or
* table does not have a scheme (which should be a fatal error?).
*/
{
if (!initialized ||
return (NULL);
}
if (q == NULL) {
READUNLOCK(this, NULL,
"db_dictionary::translate: could not allocate space");
FATAL3("db_dictionary::translate: could not allocate space",
}
if (q->size() == 0) {
delete q;
return (NULL);
}
return (q);
}
static int gt_posn;
static db_status
{
if (tbl)
return (DB_BADTABLE);
else
return (DB_BADTABLE);
return (DB_SUCCESS);
}
/*
* Return the names of tables in this dictionary.
* XXX This routine is used only for testing only;
* if to be used for real, need to free memory sensibly, or
* caller of get_table_names should have freed them.
*/
{
gt_posn = 0;
READUNLOCK(this, NULL,
"db_dictionary::get_table_names: could not allocate space for names");
"db_dictionary::get_table_names: could not allocate space for names",
}
return (>_answer);
}
static db_status
{
int status;
return (DB_BADTABLE);
"db_dictionary::db_checkpoint: could not allocate space",
}
"db_dictionary::db_checkpoint: could not load table %s",
delete dbase;
return (DB_BADTABLE);
}
delete dbase; // unload
} else
if (status == 0)
return (DB_STORAGE_LIMIT);
return (DB_SUCCESS);
}
/* Like db_checkpoint_aux except only stops on LIMIT errors */
static db_status
{
return (status);
else
return (DB_SUCCESS);
}
{
init = initialized;
if (!init)
return (DB_BADDICTIONARY);
checkpoint(); // checkpoint dictionary first
return (ret);
}
READUNLOCK(this, DB_LOCK_ERROR,
"ru db_dictionary::db_checkpoint");
return (DB_BADTABLE);
}
return (ret);
}
/* *********************** db_standby **************************** */
/* Deal with list of tables that need to be 'closed' */
/*
* Returns 1 if all databases on the list could be closed, 0
* otherwise.
*/
static int
{
int i, ret;
if (db_standby_count == 0) {
return (1);
}
for (i = 0, ret = 0; i < db_standby_size; i++) {
if ((database = db_standby_list[i])) {
/*
* In order to avoid a potential dead-lock, we
* check to see if close_log() would be able to
* lock the db; if not, just skip the db.
*/
int lockok;
"try w db_dictionary::close_standby_list");
if (lockok == 0) {
"db_dictionary::close_standby_list");
if (db_standby_count == 0) {
ret = 1;
break;
}
"%s: try-lock error %d",
} /* else it's EBUSY; skip to the next one */
}
}
return (ret);
}
/*
* Add given database to list of databases that have been opened for updates.
* If size of list exceeds maximum, close opened databases first.
*/
int
{
int i;
if (database == 0) {
return (1);
}
/* Try to keep the list below OPENED_DBS_CHUNK */
if (db_standby_count >= OPENED_DBS_CHUNK) {
}
if (db_standby_count >= db_standby_size) {
sizeof (ndsl[0]));
if (ndsl == 0) {
"%s: realloc(%d) => NULL",
sizeof (ndsl[0]));
return (0);
}
i++)
db_standby_list[i] = 0;
}
for (i = 0; i < db_standby_size; i++) {
db_standby_list[i] = database;
return (1);
}
}
return (0);
}
int
{
int i;
if (database == 0) {
return (1);
}
for (i = 0; i < db_standby_size; i++) {
if ((database == db_standby_list[i])) {
"remove_from_standby_list");
return (1);
}
}
return (0);
}
/* Release space for copied dictionary */
static void
int i;
if (d != NULL) {
for (i = 0; i < d->tables.tables_len; i++) {
while (t != NULL) {
n = t->next;
t = n;
}
}
delete d;
}
return;
}
/*
* Make a copy of the dictionary
*/
if (dictionary == NULL) {
WRITEUNLOCK(this, NULL,
"db_dictionary::db_copy_dictionary wu");
return (NULL);
}
tmp = new db_dict_desc;
WRITEUNLOCK(this, NULL,
"db_dictionary::db_copy_dictionary wu: no memory");
return (NULL);
}
delete tmp;
WRITEUNLOCK(this, NULL,
"db_dictionary::db_copy_dictionary wu: no memory");
return (NULL);
}
/* For each table ... */
/* ... and each bucket in the chain ... */
if (db_clone_bucket(t, &n)) {
} else {
}
tbl = n;
} else {
ok = 0;
}
}
}
if (ok) {
#ifdef NISDB_LDAP_DEBUG
abort();
#endif /* NISDB_LDAP_DEBUG */
} else {
}
return (tmp);
}
/*
* Set deferred commit mode. To do this, we make a copy of the table
* (structures and data), and put that on the deferred dictionary list.
* This list is used for lookups during a resync, so clients continue
* to see the pre-resync data. Meanwhile, any changes (including table
* deletes) are done to the (temporarily hidden to clients) table in
* the normal dictionary.
*/
int res;
/*
* Only want to clone one db_table_desc, so temporarily
* unlink the tail.
*/
/* Restore link to tail */
if (res == 1) {
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"wu db_dictionary::defer");
return (DB_MEMORY_LIMIT);
}
}
ret = DB_SUCCESS;
/* Initialize and load the database for the clone */
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
"%s: Clone DB for \"%s\" loaded",
} else {
"%s: Error loading clone DB for \"%s\"",
}
} else {
"%s: Unable to clone DB for \"%s\"",
}
}
clone);
if (stat != DB_SUCCESS) {
delete clone;
if (stat == DB_NOTUNIQUE) {
/* Already deferred */
ret = DB_SUCCESS;
}
}
} else {
delete clone;
/* Return value already set above */
}
} else {
}
} else {
ret = DB_NOTFOUND;
}
return (ret);
}
/*
* Unset deferred commit mode and roll back changes; doesn't recover the
* disk data, but that's OK, since we only want to be able to continue
* serving the table until we can try a full dump again.
*
* The rollback is done by removing (and deleting) the updated table from
* the dictionary, and then moving the saved table from the deferred
* dictionary list to the actual one.
*/
return (DB_NOTFOUND);
}
/*
* Remove old incarnation from deferred dictionary. We already hold
* a pointer ('old') to it, so don't delete.
*/
if (ret != DB_SUCCESS) {
#ifdef NISDB_LDAP_DEBUG
abort();
#endif /* NISDB_LDAP_DEBUG */
return (ret);
}
/*
* Remove updated incarnation from dictionary. If 'upd' is NULL,
* the table has been removed while we were in deferred mode, and
* that's OK; we just need to retain the old incarnation.
*/
if (ret != DB_SUCCESS) {
#ifdef NISDB_LDAP_DEBUG
abort();
#endif /* NISDB_LDAP_DEBUG */
/*
* Cut our losses; delete old incarnation, and leave
* updated one in place.
*/
return (ret);
}
/* Throw away updates */
}
/* (Re-)insert old incarnation in the dictionary */
if (ret != DB_SUCCESS) {
#ifdef NISDB_LDAP_DEBUG
abort();
#endif /* NISDB_LDAP_DEBUG */
/* At least avoid memory leak */
}
return (ret);
}
/*
* Commit changes. Done by simply removing and deleting the pre-resync
* data from the deferred dictionary.
*/
/* Fine (we hope); nothing to do */
return (DB_SUCCESS);
}
if (ret == DB_SUCCESS)
#ifdef NISDB_LDAP_DEBUG
else
abort();
#endif /* NISDB_LDAP_DEBUG */
return (ret);
}
/*
* while we're incorporating log data into the in-memory tables.
*/
void
ASSERTWHELD(this->dict);
}
void
ASSERTWHELD(this->dict);
if (noWriteThrough.flag > 0)
#ifdef NISDB_LDAP_DEBUG
else
abort();
#endif /* NISDB_LDAP_DEBUG */
}