tdb: put example hashes into header, so we notice incorrect hash_fn.
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 13 Sep 2010 10:35:59 +0000 (20:05 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 7 Oct 2010 04:35:59 +0000 (15:05 +1030)
This is Stefan Metzmacher <metze@samba.org>'s patch with minor changes:
1) Use the TDB_MAGIC constant so both hashes aren't of strings.
2) Check the hash in tdb_check (paranoia, really).
3) Additional check in the (unlikely!) case where both examples hash to 0.
4) Cosmetic changes to var names and complaint message.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
lib/tdb/common/check.c
lib/tdb/common/open.c
lib/tdb/common/tdb_private.h

index fd3c0a91f38348a17291d6fd3ef623ec288947d7..b1b98d4f1ed385a1f2c4e690f9a983296be24ad6 100644 (file)
@@ -28,6 +28,7 @@
 static bool tdb_check_header(struct tdb_context *tdb, tdb_off_t *recovery)
 {
        struct tdb_header hdr;
+       uint32_t h1, h2;
 
        if (tdb->methods->tdb_read(tdb, 0, &hdr, sizeof(hdr), 0) == -1)
                return false;
@@ -41,6 +42,11 @@ static bool tdb_check_header(struct tdb_context *tdb, tdb_off_t *recovery)
        if (hdr.rwlocks != 0)
                goto corrupt;
 
+       tdb_header_hash(tdb, &h1, &h2);
+       if (hdr.magic1_hash && hdr.magic2_hash &&
+           (hdr.magic1_hash != h1 || hdr.magic2_hash != h2))
+               goto corrupt;
+
        if (hdr.hash_size == 0)
                goto corrupt;
 
index 7687ff6e32a1c38bf76d9cd988dd23134bd684b6..401fa74ae0af0abed7de92bb95e825e479762c91 100644 (file)
@@ -44,6 +44,25 @@ static unsigned int default_tdb_hash(TDB_DATA *key)
        return (1103515243 * value + 12345);  
 }
 
+/* We use two hashes to double-check they're using the right hash function. */
+void tdb_header_hash(struct tdb_context *tdb,
+                    uint32_t *magic1_hash, uint32_t *magic2_hash)
+{
+       TDB_DATA hash_key;
+       uint32_t tdb_magic = TDB_MAGIC;
+
+       hash_key.dptr = (unsigned char *)TDB_MAGIC_FOOD;
+       hash_key.dsize = sizeof(TDB_MAGIC_FOOD);
+       *magic1_hash = tdb->hash_fn(&hash_key);
+
+       hash_key.dptr = CONVERT(tdb_magic);
+       hash_key.dsize = sizeof(tdb_magic);
+       *magic2_hash = tdb->hash_fn(&hash_key);
+
+       /* Make sure at least one hash is non-zero! */
+       if (*magic1_hash == 0 && *magic2_hash == 0)
+               *magic1_hash = 1;
+}
 
 /* initialise a new database with a specified hash size */
 static int tdb_new_database(struct tdb_context *tdb, int hash_size)
@@ -62,6 +81,9 @@ 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;
+
+       tdb_header_hash(tdb, &newdb->magic1_hash, &newdb->magic2_hash);
+
        if (tdb->flags & TDB_INTERNAL) {
                tdb->map_size = size;
                tdb->map_ptr = (char *)newdb;
@@ -140,6 +162,9 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
        unsigned char *vp;
        uint32_t vertest;
        unsigned v;
+       uint32_t magic1_hash;
+       uint32_t magic2_hash;
+       const char *hash_alg;
 
        if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
                /* Can't log this */
@@ -161,7 +186,14 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                tdb->log.log_fn = null_log_fn;
                tdb->log.log_private = NULL;
        }
-       tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash;
+
+       if (hash_fn) {
+               tdb->hash_fn = hash_fn;
+               hash_alg = "user defined";
+       } else {
+               tdb->hash_fn = default_tdb_hash;
+               hash_alg = "default";
+       }
 
        /* cache the page size */
        tdb->page_size = getpagesize();
@@ -279,6 +311,27 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                goto fail;
        }
 
+       tdb_header_hash(tdb, &magic1_hash, &magic2_hash);
+
+       if ((tdb->header.magic1_hash == 0) && (tdb->header.magic2_hash == 0)) {
+               /* older TDB without magic hash references */
+       } else if ((tdb->header.magic1_hash != magic1_hash) ||
+                  (tdb->header.magic2_hash != magic2_hash)) {
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                        "%s was not created with the %s hash function we are using\n"
+                        "magic1_hash[0x%08X %s 0x%08X] "
+                        "magic2_hash[0x%08X %s 0x%08X]\n",
+                        name, hash_alg,
+                        tdb->header.magic1_hash,
+                        (tdb->header.magic1_hash == magic1_hash) ? "==" : "!=",
+                        magic1_hash,
+                        tdb->header.magic2_hash,
+                        (tdb->header.magic2_hash == magic2_hash) ? "==" : "!=",
+                        magic2_hash));
+               errno = EINVAL;
+               goto fail;
+       }
+
        /* Is it already in the open list?  If so, fail. */
        if (tdb_already_open(st.st_dev, st.st_ino)) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
index 9d0f3bcd703ab537291a2ab903f468d897ba03c0..eccd6880ef5e3ce0d4785600e2704a40ca5e1f26 100644 (file)
@@ -147,7 +147,9 @@ struct tdb_header {
        tdb_off_t rwlocks; /* obsolete - kept to detect old formats */
        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];
+       uint32_t magic1_hash; /* hash of TDB_MAGIC_FOOD. */
+       uint32_t magic2_hash; /* hash of TDB_MAGIC. */
+       tdb_off_t reserved[27];
 };
 
 struct tdb_lock_type {
@@ -268,3 +270,5 @@ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off,
                      struct tdb_record *rec);
 bool tdb_write_all(int fd, const void *buf, size_t count);
 int tdb_transaction_recover(struct tdb_context *tdb);
+void tdb_header_hash(struct tdb_context *tdb,
+                    uint32_t *magic1_hash, uint32_t *magic2_hash);