tdb2: add tdb_attribute_seed for setting hash seed.
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 18 Oct 2010 02:47:42 +0000 (13:17 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 18 Oct 2010 02:47:42 +0000 (13:17 +1030)
Particularly useful for reproduction, like tdbtorture.

ccan/tdb2/tdb.c
ccan/tdb2/tdb2.h
ccan/tdb2/test/run-seed.c [new file with mode: 0644]
ccan/tdb2/tools/tdbtorture.c

index 9468e604a7f9fbb0d420ef0c0c7ff8a0e5d51e97..4b02755c28887667c1ca2adb157fb7520e8e4835 100644 (file)
@@ -94,7 +94,9 @@ struct new_database {
 };
 
 /* initialise a new database */
-static int tdb_new_database(struct tdb_context *tdb, struct tdb_header *hdr)
+static int tdb_new_database(struct tdb_context *tdb,
+                           struct tdb_attribute_seed *seed,
+                           struct tdb_header *hdr)
 {
        /* We make it up in memory, then write it out if not internal */
        struct new_database newdb;
@@ -105,7 +107,10 @@ static int tdb_new_database(struct tdb_context *tdb, struct tdb_header *hdr)
 
        /* Fill in the header */
        newdb.hdr.version = TDB_VERSION;
-       newdb.hdr.hash_seed = random_number(tdb);
+       if (seed)
+               newdb.hdr.hash_seed = seed->seed;
+       else
+               newdb.hdr.hash_seed = random_number(tdb);
        newdb.hdr.hash_test = TDB_HASH_MAGIC;
        newdb.hdr.hash_test = tdb->khash(&newdb.hdr.hash_test,
                                         sizeof(newdb.hdr.hash_test),
@@ -181,6 +186,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
        uint64_t hash_test;
        unsigned v;
        struct tdb_header hdr;
+       struct tdb_attribute_seed *seed = NULL;
 
        tdb = malloc(sizeof(*tdb));
        if (!tdb) {
@@ -213,6 +219,9 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
                        tdb->khash = attr->hash.hash_fn;
                        tdb->hash_priv = attr->hash.hash_private;
                        break;
+               case TDB_ATTRIBUTE_SEED:
+                       seed = &attr->seed;
+                       break;
                default:
                        tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                                 "tdb_open: unknown attribute type %u\n",
@@ -243,7 +252,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
        /* 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, &hdr) != 0) {
+               if (tdb_new_database(tdb, seed, &hdr) != 0) {
                        tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                                 "tdb_open: tdb_new_database failed!");
                        goto fail;
@@ -275,7 +284,8 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
 
        if (!tdb_pread_all(tdb->fd, &hdr, sizeof(hdr), 0)
            || strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) {
-               if (!(open_flags & O_CREAT) || tdb_new_database(tdb, &hdr) == -1) {
+               if (!(open_flags & O_CREAT)
+                   || tdb_new_database(tdb, seed, &hdr) == -1) {
                        if (errno == 0) {
                                errno = EIO; /* ie bad format or something */
                        }
index 343264d588f0a04db188556f6f1f1f542cd4cd2b..adaf67106551f695d5b5384300b88e812190080c 100644 (file)
@@ -86,7 +86,8 @@ typedef uint64_t (*tdb_hashfn_t)(const void *key, size_t len, uint64_t seed,
 
 enum tdb_attribute_type {
        TDB_ATTRIBUTE_LOG = 0,
-       TDB_ATTRIBUTE_HASH = 1
+       TDB_ATTRIBUTE_HASH = 1,
+       TDB_ATTRIBUTE_SEED = 2
 };
 
 struct tdb_attribute_base {
@@ -106,10 +107,16 @@ struct tdb_attribute_hash {
        void *hash_private;
 };
 
+struct tdb_attribute_seed {
+       struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_SEED */
+       uint64_t seed;
+};
+
 union tdb_attribute {
        struct tdb_attribute_base base;
        struct tdb_attribute_log log;
        struct tdb_attribute_hash hash;
+       struct tdb_attribute_seed seed;
 };
                
 struct tdb_context *tdb_open(const char *name, int tdb_flags,
diff --git a/ccan/tdb2/test/run-seed.c b/ccan/tdb2/test/run-seed.c
new file mode 100644 (file)
index 0000000..d78e863
--- /dev/null
@@ -0,0 +1,65 @@
+#include <ccan/tdb2/tdb.c>
+#include <ccan/tdb2/free.c>
+#include <ccan/tdb2/lock.c>
+#include <ccan/tdb2/io.c>
+#include <ccan/tdb2/hash.c>
+#include <ccan/tdb2/check.c>
+#include <ccan/tap/tap.h>
+#include "logging.h"
+
+static int log_count = 0;
+
+/* Normally we get a log when setting random seed. */
+static void my_log_fn(struct tdb_context *tdb,
+                     enum tdb_debug_level level, void *priv,
+                     const char *fmt, ...)
+{
+       log_count++;
+}
+
+static union tdb_attribute log_attr = {
+       .log = { .base = { .attr = TDB_ATTRIBUTE_LOG },
+                .log_fn = my_log_fn }
+};
+
+int main(int argc, char *argv[])
+{
+       unsigned int i;
+       struct tdb_context *tdb;
+       union tdb_attribute attr;
+       int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
+                       TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, 
+                       TDB_NOMMAP|TDB_CONVERT };
+
+       attr.seed.base.attr = TDB_ATTRIBUTE_SEED;
+       attr.seed.base.next = &log_attr;
+       attr.seed.seed = 42;
+
+       plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 4 * 3);
+       for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+               struct tdb_header hdr;
+               int fd;
+               tdb = tdb_open("run-seed.tdb", flags[i],
+                              O_RDWR|O_CREAT|O_TRUNC, 0600, &attr);
+               ok1(tdb);
+               if (!tdb)
+                       continue;
+               ok1(tdb_check(tdb, NULL, NULL) == 0);
+               ok1(tdb->hash_seed == 42);
+               ok1(log_count == 0);
+               tdb_close(tdb);
+
+               if (flags[i] & TDB_INTERNAL)
+                       continue;
+
+               fd = open("run-seed.tdb", O_RDONLY);
+               ok1(fd >= 0);
+               ok1(read(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
+               if (flags[i] & TDB_CONVERT)
+                       ok1(bswap_64(hdr.hash_seed) == 42);
+               else
+                       ok1(hdr.hash_seed == 42);
+               close(fd);
+       }
+       return exit_status();
+}
index 84223aa32a0ec2ac8ff2aa8ee11d47ff06f44d16..46dc1c72ab70a98a2d3f9095d827bc513190f1f3 100644 (file)
@@ -41,6 +41,7 @@ static int always_transaction = 0;
 static int loopnum;
 static int count_pipe;
 static union tdb_attribute log_attr;
+static union tdb_attribute seed_attr;
 
 #ifdef PRINTF_ATTRIBUTE
 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, void *private, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
@@ -338,7 +339,10 @@ int main(int argc, char * const *argv)
        int kill_random = 0;
        int *done;
 
+       log_attr.base.attr = TDB_ATTRIBUTE_LOG;
+       log_attr.base.next = &seed_attr;
        log_attr.log.log_fn = tdb_log;
+       seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
 
        while ((c = getopt(argc, argv, "n:l:s:thk")) != -1) {
                switch (c) {
@@ -372,6 +376,7 @@ int main(int argc, char * const *argv)
        if (seed == -1) {
                seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
        }
+       seed_attr.seed.seed = (((uint64_t)seed) << 32) | seed; 
 
        if (num_procs == 1 && !kill_random) {
                /* Don't fork for this case, makes debugging easier. */