/*
* 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 <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h> /* srand48() */
#include <lber.h>
#include <ldap.h>
#include "db_headers.h"
#include "db_table.h"
#include "db_pickle.h" /* for dump and load */
#include "db_entry.h"
#include "nisdb_mt.h"
#include "ldap_parse.h"
#include "ldap_util.h"
#include "ldap_map.h"
#include "ldap_xdr.h"
#include "nis_hashitem.h"
#include "nisdb_ldap.h"
#include "nis_parse_ldap_conf.h"
/*
* Find the largest (positive) value of time_t.
*
* If time_t is unsigned, the largest possible value is just ~0.
* However, if it's signed, then ~0 is negative. Since lint (for
* sure), and perhaps the compiler too, dislike comparing an
* unsigned quantity to see if it's less than zero, we compare
* to one instead. If negative, the largest possible value is
* th inverse of 2**(N-1), where N is the number of bits in a
* time_t.
*/
extern "C" {
static void
__setMaxTimeT(void)
{
unsigned char b[sizeof (time_t)];
int i;
/* Compute ~0 for an unknown length integer */
for (i = 0; i < sizeof (time_t); i++) {
b[i] = 0xff;
}
/* Set maxTimeT to ~0 of appropriate length */
if (maxTimeT < 1)
}
#pragma init(__setMaxTimeT)
}
/* How much to grow table by */
/* 0'th not used; might be confusing. */
/* prevents wrap around numbers from being passed */
/* Initial table sizes to use before using 1K increments. */
/* This helps conserve memory usage when there are lots of small tables. */
static int tabsizes[] = {
16,
128,
512,
0
};
/* Returns the next size to use for table */
static long unsigned
{
long unsigned newsize = 0, n;
if (oldsize == 0)
else {
break;
}
if (newsize == 0)
}
return (newsize);
}
/* destructor */
{
WRITELOCKV(this, "w db_free_list::~db_free_list");
reset(); /* free list entries */
}
void
{
WRITELOCKV(this, "w db_free_list::reset");
delete current;
}
count = 0;
WRITEUNLOCKV(this, "wu db_free_list::reset");
}
/* Returns the location of a free entry, or NULL, if there aren't any. */
{
return (NULL);
}
delete old_head;
--count;
return (found);
}
/*
* Adds given location to the free list.
* Returns TRUE if successful, FALSE otherwise (when out of memory).
*/
{
FATAL3("db_free_list::push: cannot allocation space",
}
++count;
return (TRUE);
}
/*
* Returns in a vector the information in the free list.
* Vector returned is of form: [n free cells][n1][n2][loc1], ..[locn].
* Leave the first 'n' cells free.
* n1 is the number of entries that should be in the freelist.
* n2 is the number of entries actually found in the freelist.
* [loc1...locn] are the entries. n2 <= n1 because we never count beyond n1.
* It is up to the caller to free the returned vector when he is through.
*/
long *
{
long realcount = 0,
i,
if (answer == 0) {
FATAL3("db_free_list::stats: cannot allocation space",
}
++realcount;
}
return (answer);
}
/* Set default values for the mapping structure */
void
if (m == 0)
return;
m->enumExpire = 0;
m->storeErrorRetry.attempts =
m->storeErrorRetry.timeout =
m->refreshErrorRetry.timeout =
m->expire = 0;
if (m->tm != 0)
m->tm = 0;
/*
* The 'objType' field obviously indicates the type of object.
* However, we also use it to tell us if we've retrieved mapping
* data from LDAP or not; in the latter case, 'objType' is
* NIS_BOGUS_OBJ. For purposes of maintaining expiration times,
* we may need to know if the object is a table or a directory
* _before_ we've retrieved any mapping data. Hence the 'expireType'
* field, which starts as NIS_BOGUS_OBJ (meaning, don't know, assume
* directory for now), and later is set to NIS_DIRECTORY_OBJ
* (always keep expiration data, in case one of the dir entries
* is mapped) or NIS_TABLE_OBJ (only need expiration data if
* tha table is mapped).
*/
m->objType = NIS_BOGUS_OBJ;
m->expireType = NIS_BOGUS_OBJ;
if (m->objName != 0)
m->objName = 0;
}
void
mapping.enumDeferred = 0;
mapping.enumEntries = 0;
}
/* db_table constructor */
{
table_size = 0;
last_used = 0;
count = 0;
/* grow(); */
}
/*
* db_table destructor:
* 1. Get rid of contents of freelist
* 2. delete all entries hanging off table
* 3. get rid of table itself
*/
{
WRITELOCKV(this, "w db_table::~db_table");
reset();
}
/* reset size and pointers */
void
{
int i, done = 0;
WRITELOCKV(this, "w db_table::reset");
/* Add sanity check in case of table corruption */
for (i = 0;
i++) {
if (tab[i]) {
free_entry(tab[i]);
++done;
}
}
}
delete tab;
/* Leave other values of the mapping structure unchanged */
WRITEUNLOCKV(this, "wu db_table::reset");
}
/* Initialize new portion */
} else {
return (DB_MEMORY_LIMIT);
}
return (DB_SUCCESS);
}
sizeof (entryp));
return (DB_MEMORY_LIMIT);
}
oldSize = 0;
}
newSize * sizeof (entry_object *));
} else if (newEnumArray == 0) {
return (DB_MEMORY_LIMIT);
}
return (DB_SUCCESS);
}
/* Expand the table. Fatal error if insufficient memory. */
void
{
WRITELOCKV(this, "w db_table::grow");
long i;
#ifdef DEBUG
#endif
if (table_size > CALLOC_LIMIT) {
WRITEUNLOCKV(this, "wu db_table::grow");
FATAL("db_table::grow: table size exceeds calloc limit",
}
// if ((tab = new entry_object_p[table_size]) == NULL)
if ((tab = (entry_object_p*)
calloc((unsigned int) table_size,
sizeof (entry_object_p))) == NULL) {
WRITEUNLOCKV(this, "wu db_table::grow");
}
/*
* For directories, we may need the expire time array even if the
* directory itself isn't mapped. If the objType and expireType both
* are bogus, we don't know yet if this is a table or a directory,
* and must proceed accordingly.
*/
if (stat != DB_SUCCESS) {
WRITEUNLOCKV(this, "wu db_table::grow expire");
"db_table::grow: cannot allocate space for expire", stat);
}
}
for (i = 0; i < oldsize; i++) { // transfer old to new
}
delete oldtab;
}
if (stat != DB_SUCCESS) {
WRITEUNLOCKV(this, "wu db_table::grow enumArray");
"db_table::grow: cannot allocate space for enumArray", stat);
}
}
WRITEUNLOCKV(this, "wu db_table::grow");
}
/*
* Return the first entry in table, also return its position in
* 'where'. Return NULL in both if no next entry is found.
*/
{
return (NULL);
} else {
entryp i;
for (i = DB_TABLE_START;
i < table_size && i <= last_used; i++) {
*where = i;
return (tab[i]);
}
}
}
return (NULL);
}
/*
* Return the next entry in table from 'prev', also return its position in
* 'newentry'. Return NULL in both if no next entry is found.
*/
{
long i;
return (NULL);
*newentry = i;
return (tab[i]);
}
}
return (NULL);
}
/* Return entry at location 'where', NULL if location is invalid. */
{
else
return (NULL);
}
void
nis_object *o;
/*
* If we don't know what type of object this is yet, we
* can find out now. If it's a directory, the pseudo-object
* in column zero will have the type "IN_DIRECTORY";
* otherwise, it's a table object.
*/
if (obj != 0) {
} else {
}
}
}
}
/* Set the entry TTL */
/* TTL == 0 means always expired */
if (ttl == 0)
ttl = -1;
} else {
__nis_table_mapping_t *t = 0;
o = unmakePseudoEntryObj(obj, 0);
if (o != 0) {
__nis_buffer_t b = {0, 0};
}
if (t != 0) {
/* TTL == 0 means always expired */
if (ttl == 0)
ttl = -1;
} else {
/*
* No expiration time initialization
* data. Cook up values that will
* result in mapping.expire[where]
* set to maxTimeT.
*/
}
}
if (initialLoad) {
if (interval <= 1) {
} else {
}
if (mapping.enumExpire == 0 ||
} else {
}
}
}
/*
* Add given entry to table in first available slot (either look in freelist
* or add to end of table) and return the the position of where the record
* is placed. 'count' is incremented if entry is added. Table may grow
* as a side-effect of the addition. Copy is made of input.
*/
/*
* We're returning an index of the table array, so the caller
* should hold a lock until done with the index. To save us
* the bother of upgrading to a write lock, it might as well
* be a write lock to begin with.
*/
grow();
}
++count;
return (where);
} else {
return (NULL);
}
}
/*
* Replaces object at specified location by given entry.
* Returns TRUE if replacement successful; FALSE otherwise.
* There must something already at the specified location, otherwise,
* replacement fails. Copy is not made of the input.
* The pre-existing entry is freed.
*/
{
return (FALSE);
/* (Re-)set the entry TTL */
return (TRUE);
}
/*
* Deletes entry at specified location. Returns TRUE if location is valid;
* FALSE if location is invalid, or the freed location cannot be added to
* the freelist. 'count' is decremented if the deletion occurs. The object
* at that location is freed.
*/
{
return (FALSE);
}
--count;
--last_used;
return (TRUE);
} else {
}
return (ret);
}
/*
* Returns statistics of table.
* [vector_size][table_size][last_used][count][freelist].
* It is up to the caller to free the returned vector when his is through.
* The free list is included if 'fl' is TRUE.
*/
long *
{
long *answer;
if (include_freelist)
else {
}
if (answer) {
answer[0] = table_size;
}
return (answer);
}
long i;
/* (Re-)initialize from global info */
/* Retrieve table mapping for this table */
tablePath, &ldapMappingList, 0, 0);
/*
* The mapping.fromLDAP and mapping.toLDAP fields serve as
* quick-references that tell us if mapping is enabled.
* Hence, initialize them appropriately from the table
* mapping objectDN.
*/
}
/* Set the timeout values */
WRITEUNLOCK(this, FALSE,
"db_table::configure wu objName");
FATAL3("db_table::configure objName",
}
}
/*
* In order to initialize the expiration times, we need to know
* if 'this' represents a table or a directory. To that end, we
* find an entry in the table, and invoke setEntryExp() on it.
* As a side effect, setEntryExp() will examine the pseudo-object
* in the entry, and set the expireType accordingly.
*/
if (tab != 0) {
for (i = 0; i <= last_used; i++) {
break;
}
}
}
/*
* If mapping from an LDAP repository, make sure we have the
* expiration time array.
*/
if (stat != DB_SUCCESS) {
WRITEUNLOCK(this, FALSE,
"db_table::configure wu expire");
FATAL3("db_table::configure expire",
}
/* Not using expiration times */
}
/*
* Set initial expire times for entries that don't already have one.
* Establish the enumeration expiration time to be the minimum of
* all expiration times in the table, though no larger than current
* time plus initTtlHi.
*/
if (interval > 1)
for (i = 0; i <= last_used; i++) {
if (interval > 1)
else
} else {
}
}
}
}
return (TRUE);
}
/* Return TRUE if the entry at 'loc' hasn't expired */
(void) gettimeofday(&now, 0);
else
return (ret);
}
/*
* If the supplied object has the same content as the one at 'loc',
* update the expiration time for the latter, and return TRUE.
*/
return (FALSE);
return (TRUE);
}
return (FALSE);
}
/*
* If enumeration mode is enabled, we keep a shadow array that initially
* starts out the same as 'tab'. Any update activity (add, remove, replace,
* or update timestamp) for an entry in the table means we delete the shadow
* array pointer. When ending enumeration mode, we return the shadow array.
* Any non-NULL entries in the array have not been updated since the start
* of the enum mode.
*
* The indended use is for enumeration of an LDAP container, where we
* will update all entries that currently exist in LDAP. The entries we
* don't update are those that don't exist in LDAP, and thus should be
* removed.
*
* Note that any LDAP query strictly speaking can be a partial enumeration
* (i.e., return more than one match). Since the query might also have
* matched multiple local DB entries, we need to do the same work as for
* enumeration for any query. In order to avoid having to work on the
* whole 'tab' array for simple queries (which we expect usually will
* match just one or at most a few entries), we have a "reduced" enum mode,
* where the caller supplies a count of the number of DB entries (derived
* from db_mindex::satisfy_query() or similar), and then uses enumSetup()
* to specify which 'tab' entries we're interested in.
*/
void
if (enumNum < 0)
enumNum = 0;
else if (enumNum >= table_size)
if (stat != DB_SUCCESS) {
"%s: No memory for enum check array; entry removal disabled",
myself);
}
}
}
void
}
}
}
}
entry_object **
long nea;
} else {
nea = table_size;
}
if (numEa != 0)
return (ea);
}
}
if (numEa != 0)
*numEa = 0;
return (0);
}
/*
* Set the appropriate entry in the enum array to NULL.
*/
void
return;
} else {
int i;
break;
}
}
}
}
}
/*
* Add the entry indicated by 'loc' to the enumIndex array, at 'index'.
*/
void
return;
}
/*
* Touch, i.e., update the expiration time for the entry. Also, if enum
* mode is in effect, mark the entry used for enum purposes.
*/
void
return;
}
/* ************************* pickle_table ********************* */
/*
* This was a static earlier with the func name being transfer_aux. The
* backup and restore project needed this to copy files over.
*/
{
}
public:
};
/*
* Writes the contents of table, including the all the entries, into the
* specified file in XDR format. May need to change this to use APPEND
* mode instead.
*/
int
{
int ret;
if (status == 1)
ret = -1;
else
}
/* Constructor that loads in the table from the given file */
{
/* load table */
if (f.transfer(this) < 0) {
/* fell through, something went wrong, initialize to null */
}
}
/* Returns whether location is valid. */
return (ret);
}
/* Empty free list */
WRITELOCKV(this, "w db_free_list::init");
count = 0;
WRITEUNLOCKV(this, "wu db_free_list::init");
}