]> git.ozlabs.org Git - ccan/blobdiff - ccan/tdb2/tdb.c
tdb2: tdb_expand on empty database now tested.
[ccan] / ccan / tdb2 / tdb.c
index 3cee472cfb0ac9990ef83848bbe0455708c18b5d..5d69eedcdc4ecf58079609628d59b902c4623b56 100644 (file)
@@ -1,6 +1,7 @@
 #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>
 
@@ -24,13 +25,13 @@ bool update_header(struct tdb_context *tdb)
 {
        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) {
@@ -48,7 +49,7 @@ bool update_header(struct tdb_context *tdb)
 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)
@@ -77,7 +78,7 @@ static uint64_t random_number(struct tdb_context *tdb)
 
        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);
@@ -130,6 +131,7 @@ static int tdb_new_database(struct tdb_context *tdb)
 {
        /* 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;
@@ -142,6 +144,9 @@ static int tdb_new_database(struct tdb_context *tdb)
 
        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;
@@ -158,6 +163,17 @@ static int tdb_new_database(struct tdb_context *tdb)
                   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);
@@ -166,9 +182,6 @@ static int tdb_new_database(struct tdb_context *tdb)
                        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)
@@ -177,14 +190,6 @@ static int tdb_new_database(struct tdb_context *tdb)
        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;
@@ -202,19 +207,28 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
        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) {
@@ -232,12 +246,13 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
        }
 
        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) {
@@ -246,7 +261,8 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
                        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) {
@@ -268,8 +284,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
                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) {
@@ -325,15 +340,10 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
        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;
@@ -374,8 +384,7 @@ static void unlock_lists(struct tdb_context *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);
 }
 
@@ -873,3 +882,8 @@ int tdb_close(struct tdb_context *tdb)
 
        return ret;
 }
+
+enum TDB_ERROR tdb_error(struct tdb_context *tdb)
+{
+       return tdb->ecode;
+}