Browse Source

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.
Rusty Russell 15 years ago
parent
commit
426c8dc977

+ 1 - 1
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)
 	if (hdr.version != TDB_VERSION)
 		goto corrupt;
 		goto corrupt;
 
 
-	if (hdr.rwlocks != 0)
+	if (hdr.hashcheck != hashcheck(tdb))
 		goto corrupt;
 		goto corrupt;
 
 
 	if (hdr.hash_size == 0)
 	if (hdr.hash_size == 0)

+ 15 - 2
ccan/tdb/open.c

@@ -63,6 +63,7 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size)
 	/* Fill in the header */
 	/* Fill in the header */
 	newdb->version = TDB_VERSION;
 	newdb->version = TDB_VERSION;
 	newdb->hash_size = hash_size;
 	newdb->hash_size = hash_size;
+	newdb->hashcheck = hashcheck(tdb);
 	if (tdb->flags & TDB_INTERNAL) {
 	if (tdb->flags & TDB_INTERNAL) {
 		tdb->map_size = size;
 		tdb->map_size = size;
 		tdb->map_ptr = (char *)newdb;
 		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,
 struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 				int open_flags, mode_t mode,
 				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)
 	if (fstat(tdb->fd, &st) == -1)
 		goto fail;
 		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;
 		goto fail;
 	}
 	}
 
 

+ 2 - 1
ccan/tdb/tdb_private.h

@@ -176,7 +176,7 @@ struct tdb_header {
 	char magic_food[32]; /* for /etc/magic */
 	char magic_food[32]; /* for /etc/magic */
 	uint32_t version; /* version of the code */
 	uint32_t version; /* version of the code */
 	uint32_t hash_size; /* number of hash entries */
 	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 recovery_start; /* offset of transaction recovery region */
 	tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */
 	tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */
 	tdb_off_t reserved[29];
 	tdb_off_t reserved[29];
@@ -248,6 +248,7 @@ struct tdb_context {
 /*
 /*
   internal prototypes
   internal prototypes
 */
 */
+uint32_t hashcheck(struct tdb_context *tdb);
 int tdb_munmap(struct tdb_context *tdb);
 int tdb_munmap(struct tdb_context *tdb);
 void tdb_mmap(struct tdb_context *tdb);
 void tdb_mmap(struct tdb_context *tdb);
 int tdb_lock(struct tdb_context *tdb, int list, int ltype);
 int tdb_lock(struct tdb_context *tdb, int list, int ltype);

+ 97 - 0
ccan/tdb/test/run-wronghash-fail.c

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

+ 1 - 2
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, ...)
 static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
 {
 {
 	unsigned int *count = tdb_get_logging_private(tdb);
 	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)++;
 		(*count)++;
 }
 }