TODO lib/tdb: use jenkins_hash for non persistent tdbs
[metze/samba/wip.git] / lib / tdb / common / open.c
index b19e4cea293df769a588db89c71df25529d677ec..97fd5b1cc2f034e0ceefb08db144dc34847edafc 100644 (file)
@@ -44,6 +44,10 @@ static unsigned int default_tdb_hash(TDB_DATA *key)
        return (1103515243 * value + 12345);  
 }
 
+static unsigned int jenkins_hash(TDB_DATA *key)
+{
+       return hash_any(key->dptr, key->dsize, 0);
+}
 
 /* initialise a new database with a specified hash size */
 static int tdb_new_database(struct tdb_context *tdb, int hash_size)
@@ -52,6 +56,7 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size)
        size_t size;
        int ret = -1;
        ssize_t written;
+       TDB_DATA hash_key;
 
        /* We make it up in memory, then write it out if not internal */
        size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t);
@@ -61,6 +66,15 @@ 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;
+
+       hash_key.dptr = discard_const_p(uint8_t, TDB_MAGIC_FOOD);
+       hash_key.dsize = sizeof(TDB_MAGIC_FOOD);
+       newdb->magic_food_hash = tdb->hash_fn(&hash_key);
+
+       hash_key.dptr = discard_const_p(uint8_t, TDB_MAGIC_HASH_VERIFY_KEY);
+       hash_key.dsize = sizeof(TDB_MAGIC_HASH_VERIFY_KEY);
+       newdb->verify_key_hash = tdb->hash_fn(&hash_key);
+
        if (tdb->flags & TDB_INTERNAL) {
                tdb->map_size = size;
                tdb->map_ptr = (char *)newdb;
@@ -153,6 +167,10 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
        unsigned char *vp;
        uint32_t vertest;
        unsigned v;
+       TDB_DATA hash_key;
+       uint32_t magic_food_hash;
+       uint32_t verify_key_hash;
+       const char *hash_alg;
 
        if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
                /* Can't log this */
@@ -171,7 +189,19 @@ 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 {
+               if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+                       tdb->hash_fn = jenkins_hash;
+                       hash_alg = "jenkins";
+               } else {
+                       tdb->hash_fn = default_tdb_hash;
+                       hash_alg = "default";
+               }
+       }
 
        /* cache the page size */
        tdb->page_size = getpagesize();
@@ -270,6 +300,35 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                goto fail;
        }
 
+       hash_key.dptr = discard_const_p(uint8_t, TDB_MAGIC_FOOD);
+       hash_key.dsize = sizeof(TDB_MAGIC_FOOD);
+       magic_food_hash = tdb->hash_fn(&hash_key);
+
+       hash_key.dptr = discard_const_p(uint8_t, TDB_MAGIC_HASH_VERIFY_KEY);
+       hash_key.dsize = sizeof(TDB_MAGIC_HASH_VERIFY_KEY);
+       verify_key_hash = tdb->hash_fn(&hash_key);
+
+       if ((tdb->header.magic_food_hash == 0) &&
+           (tdb->header.verify_key_hash == 0)) {
+               /* older TDB without magic hash references */
+       } else if ((tdb->header.magic_food_hash != magic_food_hash) ||
+                  (tdb->header.verify_key_hash != verify_key_hash)) {
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: %s "
+                        "was created with a different hash function current[%s]"
+                        "magic_food_hash[0x%08X %s 0x%08X] "
+                        "verify_key_hash[0x%08X %s 0x%08X]\n",
+                        name,
+                        hash_alg,
+                        tdb->header.magic_food_hash,
+                        (tdb->header.magic_food_hash == magic_food_hash)?"==":"!=",
+                        magic_food_hash,
+                        tdb->header.verify_key_hash,
+                        (tdb->header.verify_key_hash == verify_key_hash)?"==":"!=",
+                        verify_key_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: "
@@ -425,6 +484,9 @@ int tdb_reopen(struct tdb_context *tdb)
                goto fail;
        }
 
+/* If we have real pread & pwrite, we can skip reopen. */
+#if !defined(LIBREPLACE_PREAD_NOT_REPLACED) || \
+       !defined(LIBREPLACE_PWRITE_NOT_REPLACED)
        if (tdb_munmap(tdb) != 0) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
                goto fail;
@@ -436,11 +498,6 @@ int tdb_reopen(struct tdb_context *tdb)
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno)));
                goto fail;
        }
-       if ((tdb->flags & TDB_CLEAR_IF_FIRST) && 
-           (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) {
-               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n"));
-               goto fail;
-       }
        if (fstat(tdb->fd, &st) != 0) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
                goto fail;
@@ -450,6 +507,13 @@ int tdb_reopen(struct tdb_context *tdb)
                goto fail;
        }
        tdb_mmap(tdb);
+#endif /* fake pread or pwrite */
+
+       if ((tdb->flags & TDB_CLEAR_IF_FIRST) &&
+           (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) {
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n"));
+               goto fail;
+       }
 
        return 0;