Browse Source

tdb2: add tdb_attribute_seed for setting hash seed.

Particularly useful for reproduction, like tdbtorture.
Rusty Russell 15 years ago
parent
commit
e2cb71e5d4
4 changed files with 92 additions and 5 deletions
  1. 14 4
      ccan/tdb2/tdb.c
  2. 8 1
      ccan/tdb2/tdb2.h
  3. 65 0
      ccan/tdb2/test/run-seed.c
  4. 5 0
      ccan/tdb2/tools/tdbtorture.c

+ 14 - 4
ccan/tdb2/tdb.c

@@ -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 */
 			}

+ 8 - 1
ccan/tdb2/tdb2.h

@@ -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,

+ 65 - 0
ccan/tdb2/test/run-seed.c

@@ -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();
+}

+ 5 - 0
ccan/tdb2/tools/tdbtorture.c

@@ -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. */