#include "private.h"
#include <ccan/tdb2/tdb2.h>
#include <ccan/hash/hash.h>
+#include <ccan/build_assert/build_assert.h>
#include <ccan/likely/likely.h>
#include <assert.h>
{
struct tdb_header_volatile pad, *v;
- if (tdb->header_uptodate) {
+ if (!(tdb->flags & TDB_NOLOCK) && tdb->header_uptodate) {
tdb->log(tdb, TDB_DEBUG_WARNING, tdb->log_priv,
"warning: header uptodate already\n");
}
/* We could get a partial update if we're not holding any locks. */
- assert(tdb_has_locks(tdb));
+ assert((tdb->flags & TDB_NOLOCK) || tdb_has_locks(tdb));
v = tdb_get(tdb, offsetof(struct tdb_header, v), &pad, sizeof(*v));
if (!v) {
static uint64_t jenkins_hash(const void *key, size_t length, uint64_t seed,
void *arg)
{
- return hash64_any(key, length, seed);
+ return hash64_stable((const unsigned char *)key, length, seed);
}
uint64_t tdb_hash(struct tdb_context *tdb, const void *ptr, size_t len)
fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
- if (read(fd, &ret, sizeof(ret)) == sizeof(ret)) {
+ if (tdb_read_all(fd, &ret, sizeof(ret))) {
tdb->log(tdb, TDB_DEBUG_TRACE, tdb->log_priv,
"tdb_open: random from /dev/urandom\n");
close(fd);
{
/* We make it up in memory, then write it out if not internal */
struct new_database newdb;
+ unsigned int magic_off = offsetof(struct tdb_header, magic_food);
/* Fill in the header */
newdb.hdr.version = TDB_VERSION;
newdb.hdr.v.generation = 0;
+ /* The initial zone must cover the initial database size! */
+ BUILD_ASSERT((1ULL << INITIAL_ZONE_BITS) >= sizeof(newdb));
+
/* Free array has 1 zone, 10 buckets. All buckets empty. */
newdb.hdr.v.num_zones = 1;
newdb.hdr.v.zone_bits = INITIAL_ZONE_BITS;
sizeof(newdb.hash), sizeof(newdb.hash), 0);
memset(newdb.hash, 0, sizeof(newdb.hash));
+ /* Magic food */
+ memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
+ strcpy(newdb.hdr.magic_food, TDB_MAGIC_FOOD);
+
+ /* This creates an endian-converted database, as if read from disk */
+ tdb_convert(tdb,
+ (char *)&newdb.hdr + magic_off,
+ sizeof(newdb) - magic_off);
+
+ tdb->header = newdb.hdr;
+
if (tdb->flags & TDB_INTERNAL) {
tdb->map_size = sizeof(newdb);
tdb->map_ptr = malloc(tdb->map_size);
return -1;
}
memcpy(tdb->map_ptr, &newdb, tdb->map_size);
- tdb->header = newdb.hdr;
- /* Convert the `ondisk' version if asked. */
- tdb_convert(tdb, tdb->map_ptr, sizeof(newdb));
return 0;
}
if (lseek(tdb->fd, 0, SEEK_SET) == -1)
if (ftruncate(tdb->fd, 0) == -1)
return -1;
- /* This creates an endian-converted header, as if read from disk */
- tdb->header = newdb.hdr;
- tdb_convert(tdb, &tdb->header, sizeof(tdb->header));
-
- /* Don't endian-convert the magic food! */
- memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
- strcpy(newdb.hdr.magic_food, TDB_MAGIC_FOOD);
-
if (!tdb_pwrite_all(tdb->fd, &newdb, sizeof(newdb), 0)) {
tdb->ecode = TDB_ERR_IO;
return -1;
uint64_t hash_test;
unsigned v;
- if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
+ tdb = malloc(sizeof(*tdb));
+ if (!tdb) {
/* Can't log this */
errno = ENOMEM;
goto fail;
}
- tdb->fd = -1;
tdb->name = NULL;
tdb->map_ptr = NULL;
+ tdb->fd = -1;
+ /* map_size will be set below. */
+ tdb->ecode = TDB_SUCCESS;
+ /* header will be read in below. */
+ tdb->header_uptodate = false;
tdb->flags = tdb_flags;
tdb->log = null_log_fn;
tdb->log_priv = NULL;
tdb->khash = jenkins_hash;
tdb->hash_priv = NULL;
+ tdb->transaction = NULL;
+ /* last_zone will be set below. */
+ tdb_io_init(tdb);
+ tdb_lock_init(tdb);
/* FIXME */
if (attr) {
}
if ((open_flags & O_ACCMODE) == O_RDONLY) {
- tdb->read_only = 1;
+ tdb->read_only = true;
/* read only databases don't do locking */
tdb->flags |= TDB_NOLOCK;
- }
+ } else
+ tdb->read_only = false;
- /* internal databases don't mmap or lock */
+ /* internal databases don't need any of the rest. */
if (tdb->flags & TDB_INTERNAL) {
tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
if (tdb_new_database(tdb) != 0) {
goto fail;
}
TEST_IT(tdb->flags & TDB_CONVERT);
- goto internal;
+ tdb_convert(tdb, &tdb->header, sizeof(tdb->header));
+ return tdb;
}
if ((tdb->fd = open(name, open_flags, mode)) == -1) {
goto fail; /* errno set by tdb_brlock */
}
- errno = 0;
- if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
+ if (!tdb_pread_all(tdb->fd, &tdb->header, sizeof(tdb->header), 0)
|| strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0) {
if (!(open_flags & O_CREAT) || tdb_new_database(tdb) == -1) {
if (errno == 0) {
tdb->map_size = st.st_size;
tdb->device = st.st_dev;
tdb->inode = st.st_ino;
- tdb_io_init(tdb);
tdb_mmap(tdb);
-
- internal:
- /* Internal (memory-only) databases skip all the code above to
- * do with disk files, and resume here by releasing their
- * open lock and hooking into the active list. */
tdb_unlock_open(tdb);
- tdb->last_zone = random_free_zone(tdb);
+ tdb_zone_init(tdb);
+
tdb->next = tdbs;
tdbs = tdb;
return tdb;
{
do {
tdb_unlock_list(tdb, start, ltype);
- start = (start + ((1ULL << tdb->header.v.hash_bits) - 1))
- & ((1ULL << tdb->header.v.hash_bits) - 1);
+ start = (start + 1) & ((1ULL << tdb->header.v.hash_bits) - 1);
} while (start != end);
}
return ret;
}
+
+enum TDB_ERROR tdb_error(struct tdb_context *tdb)
+{
+ return tdb->ecode;
+}