/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
static struct tdb_context *tdbs = NULL;
-
-/* This is based on the hash algorithm from gdbm */
-static unsigned int default_tdb_hash(TDB_DATA *key)
+/* We use two hashes to double-check they're using the right hash function. */
+void tdb_header_hash(struct tdb_context *tdb,
+ uint32_t *magic1_hash, uint32_t *magic2_hash)
{
- uint32_t value; /* Used to compute the hash value. */
- uint32_t i; /* Used to cycle through random values. */
+ TDB_DATA hash_key;
+ uint32_t tdb_magic = TDB_MAGIC;
- /* Set the initial value from the key size. */
- for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
- value = (value + (key->dptr[i] << (i*5 % 24)));
+ hash_key.dptr = (unsigned char *)TDB_MAGIC_FOOD;
+ hash_key.dsize = sizeof(TDB_MAGIC_FOOD);
+ *magic1_hash = tdb->hash_fn(&hash_key);
- return (1103515243 * value + 12345);
-}
+ hash_key.dptr = (unsigned char *)CONVERT(tdb_magic);
+ hash_key.dsize = sizeof(tdb_magic);
+ *magic2_hash = tdb->hash_fn(&hash_key);
+ /* Make sure at least one hash is non-zero! */
+ if (*magic1_hash == 0 && *magic2_hash == 0)
+ *magic1_hash = 1;
+}
/* initialise a new database with a specified hash size */
static int tdb_new_database(struct tdb_context *tdb, int hash_size)
struct tdb_header *newdb;
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);
/* Fill in the header */
newdb->version = TDB_VERSION;
newdb->hash_size = hash_size;
+
+ tdb_header_hash(tdb, &newdb->magic1_hash, &newdb->magic2_hash);
+
if (tdb->flags & TDB_INTERNAL) {
tdb->map_size = size;
tdb->map_ptr = (char *)newdb;
/* Don't endian-convert the magic food! */
memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
/* we still have "ret == -1" here */
- written = write(tdb->fd, newdb, size);
- if (written == size) {
+ if (tdb_write_all(tdb->fd, newdb, 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:
SAFE_FREE(newdb);
unsigned char *vp;
uint32_t vertest;
unsigned v;
+ uint32_t magic1_hash;
+ uint32_t magic2_hash;
+ const char *hash_alg;
if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
/* Can't log this */
tdb->log.log_fn = null_log_fn;
tdb->log.log_private = NULL;
}
- tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash;
+
+ if (hash_fn) {
+ tdb->hash_fn = hash_fn;
+ hash_alg = "user defined";
+ } else {
+ tdb->hash_fn = tdb_old_hash;
+ hash_alg = "default";
+ }
/* cache the page size */
tdb->page_size = getpagesize();
goto fail;
}
+ if (getenv("TDB_NO_FSYNC")) {
+ tdb->flags |= TDB_NOSYNC;
+ }
+
/*
* TDB_ALLOW_NESTING is the default behavior.
* Note: this may change in future versions!
goto fail;
}
+ tdb_header_hash(tdb, &magic1_hash, &magic2_hash);
+
+ if ((tdb->header.magic1_hash == 0) && (tdb->header.magic2_hash == 0)) {
+ /* older TDB without magic hash references */
+ } else if ((tdb->header.magic1_hash != magic1_hash) ||
+ (tdb->header.magic2_hash != magic2_hash)) {
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+ "%s was not created with the %s hash function we are using\n"
+ "magic1_hash[0x%08X %s 0x%08X] "
+ "magic2_hash[0x%08X %s 0x%08X]\n",
+ name, hash_alg,
+ tdb->header.magic1_hash,
+ (tdb->header.magic1_hash == magic1_hash) ? "==" : "!=",
+ magic1_hash,
+ tdb->header.magic2_hash,
+ (tdb->header.magic2_hash == magic2_hash) ? "==" : "!=",
+ magic2_hash));
+ errno = EINVAL;
+ goto fail;
+ }
+
/* Is it already in the open list? If so, fail. */
if (tdb_already_open(st.st_dev, st.st_ino)) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "