tdb: enforce hashing, via example hash in old rwlocks entry in header.
authorRusty Russell <rusty@rustcorp.com.au>
Fri, 10 Sep 2010 03:59:54 +0000 (13:29 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Fri, 10 Sep 2010 03:59:54 +0000 (13:29 +0930)
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.

ccan/tdb/check.c
ccan/tdb/open.c
ccan/tdb/tdb_private.h
ccan/tdb/test/run-wronghash-fail.c [new file with mode: 0644]
ccan/tdb/test/run-wronghash-old.c

index 49a21c1883afee5a138120a96551af434dc14c20..c741cef3316ee36d21a4b4b0926e697b5e8ba317 100644 (file)
@@ -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)
index bfa26dec58fd256db124fcd18ca9c50598ea4fe7..437a5fc38d1706e9b5f3e846876f8aa4e8d9da75 100644 (file)
@@ -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;
        }
 
index 228e8dcf9b09eaedf64fd166b8a683deb50070a0..16f5d925c0954e2105ed63eeb969a45a374ad9cb 100644 (file)
@@ -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 (file)
index 0000000..2804515
--- /dev/null
@@ -0,0 +1,97 @@
+#define _XOPEN_SOURCE 500
+#include <ccan/tdb/tdb.h>
+#include <ccan/tdb/io.c>
+#include <ccan/tdb/tdb.c>
+#include <ccan/tdb/lock.c>
+#include <ccan/tdb/freelist.c>
+#include <ccan/tdb/traverse.c>
+#include <ccan/tdb/transaction.c>
+#include <ccan/tdb/error.c>
+#include <ccan/tdb/open.c>
+#include <ccan/tdb/check.c>
+#include <ccan/hash/hash.h>
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <err.h>
+
+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();
+}
index 3f1955eec772f5ae1a0458a18dffc986050d854f..af82ea28f3d5b45716a6308b0a48b6c8fbe7c08b 100644 (file)
@@ -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)++;
 }