Commit 426c8dc9 authored by Rusty Russell's avatar Rusty Russell

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.
parent fdf3c148
......@@ -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)
......
......@@ -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;
}
......
......@@ -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);
......
#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();
}
......@@ -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)++;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment