From: Rusty Russell Date: Fri, 10 Sep 2010 03:59:54 +0000 (+0930) Subject: tdb: enforce hashing, via example hash in old rwlocks entry in header. X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=426c8dc977d0fb46286042f7072d86bc053836a1 tdb: enforce hashing, via example hash in old rwlocks entry in header. This means that older code will not be able to open new TDBs with a non-default hash, *even* if they are using the correct hash. Non-default hashes were unusual, but now SAMBA is considering using a non-default hash, and avoiding corruption seems more important than backwards compatibility for an obscure case. --- diff --git a/ccan/tdb/check.c b/ccan/tdb/check.c index 49a21c18..c741cef3 100644 --- a/ccan/tdb/check.c +++ b/ccan/tdb/check.c @@ -39,7 +39,7 @@ static bool tdb_check_header(struct tdb_context *tdb, tdb_off_t *recovery) if (hdr.version != TDB_VERSION) goto corrupt; - if (hdr.rwlocks != 0) + if (hdr.hashcheck != hashcheck(tdb)) goto corrupt; if (hdr.hash_size == 0) diff --git a/ccan/tdb/open.c b/ccan/tdb/open.c index bfa26dec..437a5fc3 100644 --- a/ccan/tdb/open.c +++ b/ccan/tdb/open.c @@ -63,6 +63,7 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size) /* Fill in the header */ newdb->version = TDB_VERSION; newdb->hash_size = hash_size; + newdb->hashcheck = hashcheck(tdb); if (tdb->flags & TDB_INTERNAL) { tdb->map_size = size; tdb->map_ptr = (char *)newdb; @@ -143,6 +144,18 @@ static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, con { } +uint32_t hashcheck(struct tdb_context *tdb) +{ + uint32_t vals[] = { TDB_VERSION, TDB_MAGIC }; + TDB_DATA hashkey = { (unsigned char *)vals, sizeof(vals) }; + + /* If we're using the default hash, let old code still open the db. */ + if (tdb->hash_fn == default_tdb_hash) + return 0; + + /* Only let new hash-aware TDB code open it (must not be zero!) */ + return (tdb->hash_fn(&hashkey) | 1); +} struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, @@ -288,8 +301,8 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, if (fstat(tdb->fd, &st) == -1) goto fail; - if (tdb->header.rwlocks != 0) { - TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n")); + if (tdb->header.hashcheck != hashcheck(tdb)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: wrong hash?\n")); goto fail; } diff --git a/ccan/tdb/tdb_private.h b/ccan/tdb/tdb_private.h index 228e8dcf..16f5d925 100644 --- a/ccan/tdb/tdb_private.h +++ b/ccan/tdb/tdb_private.h @@ -176,7 +176,7 @@ struct tdb_header { char magic_food[32]; /* for /etc/magic */ uint32_t version; /* version of the code */ uint32_t hash_size; /* number of hash entries */ - tdb_off_t rwlocks; /* obsolete - kept to detect old formats */ + tdb_off_t hashcheck; /* 0 for default hash. */ tdb_off_t recovery_start; /* offset of transaction recovery region */ tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */ tdb_off_t reserved[29]; @@ -248,6 +248,7 @@ struct tdb_context { /* internal prototypes */ +uint32_t hashcheck(struct tdb_context *tdb); int tdb_munmap(struct tdb_context *tdb); void tdb_mmap(struct tdb_context *tdb); int tdb_lock(struct tdb_context *tdb, int list, int ltype); diff --git a/ccan/tdb/test/run-wronghash-fail.c b/ccan/tdb/test/run-wronghash-fail.c new file mode 100644 index 00000000..2804515a --- /dev/null +++ b/ccan/tdb/test/run-wronghash-fail.c @@ -0,0 +1,97 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int jenkins_hash(TDB_DATA *key) +{ + return hash_stable(key->dptr, key->dsize, 0); +} + +static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb_get_logging_private(tdb); + /* Old code used to complain about spinlocks when we put something + here. */ + if (strstr(fmt, "wrong hash") || strstr(fmt, "spinlock")) + (*count)++; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + unsigned int log_count; + struct tdb_logging_context log_ctx = { log_fn, &log_count }; + + plan_tests(16); + + /* Create with default hash. */ + log_count = 0; + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, + O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + tdb_close(tdb); + + /* Fail to open with different hash. */ + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0, + &log_ctx, jenkins_hash); + ok1(!tdb); + ok1(log_count == 1); + + /* Create with different hash. */ + log_count = 0; + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, + O_CREAT|O_RDWR|O_TRUNC, + 0600, &log_ctx, jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + tdb_close(tdb); + + /* Endian should be no problem. */ + log_count = 0; + tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + /* Fail to open with defailt hash. */ + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + tdb_close(tdb); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb/test/run-wronghash-old.c b/ccan/tdb/test/run-wronghash-old.c index 3f1955ee..af82ea28 100644 --- a/ccan/tdb/test/run-wronghash-old.c +++ b/ccan/tdb/test/run-wronghash-old.c @@ -22,8 +22,7 @@ static unsigned int non_jenkins_hash(TDB_DATA *key) static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) { unsigned int *count = tdb_get_logging_private(tdb); - /* Old code used to complain about spinlocks on new databases. */ - if (strstr(fmt, "spinlock")) + if (strstr(fmt, "wrong hash")) (*count)++; }