Particularly useful for reproduction, like tdbtorture.
};
/* 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;
/* 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),
uint64_t hash_test;
unsigned v;
struct tdb_header hdr;
+ struct tdb_attribute_seed *seed = NULL;
tdb = malloc(sizeof(*tdb));
if (!tdb) {
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",
/* 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;
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 */
}
enum tdb_attribute_type {
TDB_ATTRIBUTE_LOG = 0,
- TDB_ATTRIBUTE_HASH = 1
+ TDB_ATTRIBUTE_HASH = 1,
+ TDB_ATTRIBUTE_SEED = 2
};
struct tdb_attribute_base {
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,
--- /dev/null
+#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();
+}
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);
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) {
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. */