This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
+ version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "tdb_private.h"
/* This is based on the hash algorithm from gdbm */
static unsigned int default_tdb_hash(TDB_DATA *key)
{
- u32 value; /* Used to compute the hash value. */
- u32 i; /* Used to cycle through random values. */
+ uint32_t value; /* Used to compute the hash value. */
+ uint32_t i; /* Used to cycle through random values. */
/* Set the initial value from the key size. */
for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
static int tdb_new_database(struct tdb_context *tdb, int hash_size)
{
struct tdb_header *newdb;
- int size, ret = -1;
+ size_t size;
+ int ret = -1;
+ ssize_t written;
/* We make it up in memory, then write it out if not internal */
size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t);
memcpy(&tdb->header, newdb, sizeof(tdb->header));
/* Don't endian-convert the magic food! */
memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
- if (write(tdb->fd, newdb, size) != size) {
- ret = -1;
- } else {
+ /* we still have "ret == -1" here */
+ written = write(tdb->fd, newdb, size);
+ if (written == size) {
+ ret = 0;
+ } else if (written != -1) {
+ /* call write once again, this usually should return -1 and
+ * set errno appropriately */
+ size -= written;
+ written = write(tdb->fd, newdb+written, size);
+ if (written == size) {
ret = 0;
+ } else if (written >= 0) {
+ /* a second incomplete write - we give up.
+ * guessing the errno... */
+ errno = ENOSPC;
+ }
}
fail:
struct stat st;
int rev = 0, locked = 0;
unsigned char *vp;
- u32 vertest;
+ uint32_t vertest;
+ unsigned v;
if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
/* Can't log this */
tdb->page_size = 0x2000;
}
+ if (open_flags & TDB_VOLATILE) {
+ tdb->max_dead_records = 5;
+ }
+
if ((open_flags & O_ACCMODE) == O_WRONLY) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n",
name));
goto fail; /* errno set by open(2) */
}
+ /* on exec, don't inherit the fd */
+ v = fcntl(tdb->fd, F_GETFD, 0);
+ fcntl(tdb->fd, F_SETFD, v | FD_CLOEXEC);
+
/* ensure there is only one process initialising at once */
if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n",
}
}
+ errno = 0;
if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
|| strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
|| (tdb->header.version != TDB_VERSION
&& !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
/* its not a valid database - possibly initialise it */
if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+ if (errno == 0) {
errno = EIO; /* ie bad format or something */
+ }
goto fail;
}
rev = (tdb->flags & TDB_CONVERT);
}
vp = (unsigned char *)&tdb->header.version;
- vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
- (((u32)vp[2]) << 8) | (u32)vp[3];
+ vertest = (((uint32_t)vp[0]) << 24) | (((uint32_t)vp[1]) << 16) |
+ (((uint32_t)vp[2]) << 8) | (uint32_t)vp[3];
tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
if (!rev)
tdb->flags &= ~TDB_CONVERT;
tdb->map_size = st.st_size;
tdb->device = st.st_dev;
tdb->inode = st.st_ino;
- tdb->locked = (struct tdb_lock_type *)calloc(tdb->header.hash_size+1,
- sizeof(tdb->locked[0]));
- if (!tdb->locked) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
- "failed to allocate lock structure for %s\n",
- name));
- errno = ENOMEM;
- goto fail;
- }
+ tdb->max_dead_records = 0;
tdb_mmap(tdb);
if (locked) {
if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) {
if (tdb->fd != -1)
if (close(tdb->fd) != 0)
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
- SAFE_FREE(tdb->locked);
SAFE_FREE(tdb);
errno = save_errno;
return NULL;
}
}
+/*
+ * Set the maximum number of dead records per hash chain
+ */
+
+void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
+{
+ tdb->max_dead_records = max_dead;
+}
+
/**
* Close a database.
*
SAFE_FREE(tdb->name);
if (tdb->fd != -1)
ret = close(tdb->fd);
- SAFE_FREE(tdb->locked);
+ SAFE_FREE(tdb->lockrecs);
/* Remove from contexts list */
for (i = &tdbs; *i; i = &(*i)->next) {
/* register a loging function */
void tdb_set_logging_function(struct tdb_context *tdb,
- const struct tdb_logging_context *log)
+ const struct tdb_logging_context *log_ctx)
{
- tdb->log = *log;
+ tdb->log = *log_ctx;
}
void *tdb_get_logging_private(struct tdb_context *tdb)