Browse Source

tdb2: approximate INCOMPATIBLE_HASH flag with tdb1_incompatible_hash()

Rather than leak TDB_INCOMPATIBLE_HASH through to the TDB2 API, we make
it that if they use the tdb1_incompatible_hash function as their hash,
then we treat it as if they had specified the TDB_INCOMPATIBLE_HASH
flag (ie. we mark the header so it's unusable by tdb < 1.2.6).

This precludes the possibility of using TDB_INCOMPATIBLE_HASH with a
custom hash function: that used to allow the user to ensure that old TDB
versions couldn't open the TDB file (and recent ones check the header to
ensure they're using the right hash).  But that's a small loss.
Rusty Russell 14 years ago
parent
commit
3004f7e89a

+ 1 - 2
ccan/tdb2/tdb1.h

@@ -49,7 +49,6 @@
 #define TDB1_VOLATILE   256 /** Activate the per-hashchain freelist, default 5 */
 #define TDB1_VOLATILE   256 /** Activate the per-hashchain freelist, default 5 */
 #define TDB1_ALLOW_NESTING 512 /** Allow transactions to nest */
 #define TDB1_ALLOW_NESTING 512 /** Allow transactions to nest */
 #define TDB1_DISALLOW_NESTING 1024 /** Disallow transactions to nest */
 #define TDB1_DISALLOW_NESTING 1024 /** Disallow transactions to nest */
-#define TDB1_INCOMPATIBLE_HASH 2048 /** Better hashing: can't be opened by tdb < 1.2.6. */
 
 
 /** This is the context structure that is returned from a db open. */
 /** This is the context structure that is returned from a db open. */
 typedef struct tdb1_context TDB1_CONTEXT;
 typedef struct tdb1_context TDB1_CONTEXT;
@@ -121,7 +120,7 @@ int tdb1_hash_size(struct tdb1_context *tdb);
 
 
 void tdb1_increment_seqnum_nonblock(struct tdb1_context *tdb);
 void tdb1_increment_seqnum_nonblock(struct tdb1_context *tdb);
 
 
-unsigned int tdb1_jenkins_hash(TDB_DATA *key);
+unsigned int tdb1_incompatible_hash(TDB_DATA *key);
 
 
 int tdb1_check(struct tdb1_context *tdb,
 int tdb1_check(struct tdb1_context *tdb,
 	      int (*check) (TDB_DATA key, TDB_DATA data, void *private_data),
 	      int (*check) (TDB_DATA key, TDB_DATA data, void *private_data),

+ 1 - 1
ccan/tdb2/tdb1_hash.c

@@ -339,7 +339,7 @@ static uint32_t hashlittle( const void *key, size_t length )
   return c;
   return c;
 }
 }
 
 
-unsigned int tdb1_jenkins_hash(TDB_DATA *key)
+unsigned int tdb1_incompatible_hash(TDB_DATA *key)
 {
 {
 	return hashlittle(key->dptr, key->dsize);
 	return hashlittle(key->dptr, key->dsize);
 }
 }

+ 24 - 23
ccan/tdb2/tdb1_open.c

@@ -72,7 +72,7 @@ static int tdb1_new_database(struct tdb1_context *tdb, int hash_size)
 
 
 	/* Make sure older tdbs (which don't check the magic hash fields)
 	/* Make sure older tdbs (which don't check the magic hash fields)
 	 * will refuse to open this TDB. */
 	 * will refuse to open this TDB. */
-	if (tdb->flags & TDB1_INCOMPATIBLE_HASH)
+	if (tdb->hash_fn == tdb1_incompatible_hash)
 		newdb->rwlocks = TDB1_HASH_RWLOCK_MAGIC;
 		newdb->rwlocks = TDB1_HASH_RWLOCK_MAGIC;
 
 
 	if (tdb->flags & TDB1_INTERNAL) {
 	if (tdb->flags & TDB1_INTERNAL) {
@@ -135,25 +135,28 @@ struct tdb1_context *tdb1_open(const char *name, int hash_size, int tdb1_flags,
 	return tdb1_open_ex(name, hash_size, tdb1_flags, open_flags, mode, NULL, NULL);
 	return tdb1_open_ex(name, hash_size, tdb1_flags, open_flags, mode, NULL, NULL);
 }
 }
 
 
-static bool check_header_hash(struct tdb1_context *tdb,
-			      bool default_hash, uint32_t *m1, uint32_t *m2)
+static bool hash_correct(struct tdb1_context *tdb,
+			 uint32_t *m1, uint32_t *m2)
 {
 {
 	tdb1_header_hash(tdb, m1, m2);
 	tdb1_header_hash(tdb, m1, m2);
-	if (tdb->header.magic1_hash == *m1 &&
-	    tdb->header.magic2_hash == *m2) {
-		return true;
-	}
+	return (tdb->header.magic1_hash == *m1 &&
+		tdb->header.magic2_hash == *m2);
+}
 
 
-	/* If they explicitly set a hash, always respect it. */
-	if (!default_hash)
-		return false;
+static bool check_header_hash(struct tdb1_context *tdb,
+			      uint32_t *m1, uint32_t *m2)
+{
+	if (hash_correct(tdb, m1, m2))
+		return true;
 
 
-	/* Otherwise, try the other inbuilt hash. */
+	/* If they use one inbuilt, try the other inbuilt hash. */
 	if (tdb->hash_fn == tdb1_old_hash)
 	if (tdb->hash_fn == tdb1_old_hash)
-		tdb->hash_fn = tdb1_jenkins_hash;
-	else
+		tdb->hash_fn = tdb1_incompatible_hash;
+	else if (tdb->hash_fn == tdb1_incompatible_hash)
 		tdb->hash_fn = tdb1_old_hash;
 		tdb->hash_fn = tdb1_old_hash;
-	return check_header_hash(tdb, false, m1, m2);
+	else
+		return false;
+	return hash_correct(tdb, m1, m2);
 }
 }
 
 
 struct tdb1_context *tdb1_open_ex(const char *name, int hash_size, int tdb1_flags,
 struct tdb1_context *tdb1_open_ex(const char *name, int hash_size, int tdb1_flags,
@@ -216,15 +219,13 @@ struct tdb1_context *tdb1_open_ex(const char *name, int hash_size, int tdb1_flag
 
 
 	if (hash_fn) {
 	if (hash_fn) {
 		tdb->hash_fn = hash_fn;
 		tdb->hash_fn = hash_fn;
-		hash_alg = "the user defined";
+		if (hash_fn == tdb1_incompatible_hash)
+			hash_alg = "tdb1_incompatible_hash";
+		else
+			hash_alg = "the user defined";
 	} else {
 	} else {
-		/* This controls what we use when creating a tdb. */
-		if (tdb->flags & TDB1_INCOMPATIBLE_HASH) {
-			tdb->hash_fn = tdb1_jenkins_hash;
-		} else {
-			tdb->hash_fn = tdb1_old_hash;
-		}
-		hash_alg = "either default";
+		tdb->hash_fn = tdb1_old_hash;
+		hash_alg = "default";
 	}
 	}
 
 
 	/* cache the page size */
 	/* cache the page size */
@@ -353,7 +354,7 @@ struct tdb1_context *tdb1_open_ex(const char *name, int hash_size, int tdb1_flag
 	if ((tdb->header.magic1_hash == 0) && (tdb->header.magic2_hash == 0)) {
 	if ((tdb->header.magic1_hash == 0) && (tdb->header.magic2_hash == 0)) {
 		/* older TDB without magic hash references */
 		/* older TDB without magic hash references */
 		tdb->hash_fn = tdb1_old_hash;
 		tdb->hash_fn = tdb1_old_hash;
-	} else if (!check_header_hash(tdb, !hash_fn, &magic1, &magic2)) {
+	} else if (!check_header_hash(tdb, &magic1, &magic2)) {
 		tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_USE_ERROR,
 		tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_USE_ERROR,
 			   "tdb1_open_ex: "
 			   "tdb1_open_ex: "
 			   "%s was not created with %s hash function we are using\n"
 			   "%s was not created with %s hash function we are using\n"

+ 25 - 13
ccan/tdb2/test/run-tdb1-incompatible.c

@@ -31,6 +31,16 @@ static unsigned int hdr_rwlocks(const char *fname)
 	return hdr.rwlocks;
 	return hdr.rwlocks;
 }
 }
 
 
+static unsigned int jenkins_hashfn(TDB_DATA *key)
+{
+	return hashlittle(key->dptr, key->dsize);
+}
+
+static unsigned int old_hash(TDB_DATA *key)
+{
+	return tdb1_old_hash(key);
+}
+
 int main(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
 {
 	struct tdb1_context *tdb;
 	struct tdb1_context *tdb;
@@ -61,11 +71,12 @@ int main(int argc, char *argv[])
 		/* Should not have marked rwlocks field. */
 		/* Should not have marked rwlocks field. */
 		ok1(hdr_rwlocks("run-incompatible.tdb") == 0);
 		ok1(hdr_rwlocks("run-incompatible.tdb") == 0);
 
 
-		/* We can still open any old-style with incompat flag. */
+		/* We can still open any old-style with incompat hash. */
 		log_count = 0;
 		log_count = 0;
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0,
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0,
-				  TDB1_INCOMPATIBLE_HASH,
-				  O_RDWR, 0600, &log_ctx, NULL);
+				  TDB_DEFAULT,
+				  O_RDWR, 0600, &log_ctx,
+				   tdb1_incompatible_hash);
 		ok1(tdb);
 		ok1(tdb);
 		ok1(log_count == 0);
 		ok1(log_count == 0);
 		d = tdb1_fetch(tdb, d);
 		d = tdb1_fetch(tdb, d);
@@ -76,7 +87,7 @@ int main(int argc, char *argv[])
 
 
 		log_count = 0;
 		log_count = 0;
 		tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDONLY,
 		tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDONLY,
-				  0, &log_ctx, tdb1_jenkins_hash);
+				  0, &log_ctx, jenkins_hashfn);
 		ok1(tdb);
 		ok1(tdb);
 		ok1(log_count == 0);
 		ok1(log_count == 0);
 		ok1(tdb1_check(tdb, NULL, NULL) == 0);
 		ok1(tdb1_check(tdb, NULL, NULL) == 0);
@@ -84,18 +95,18 @@ int main(int argc, char *argv[])
 
 
 		log_count = 0;
 		log_count = 0;
 		tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDONLY,
 		tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDONLY,
-				  0, &log_ctx, tdb1_jenkins_hash);
+				  0, &log_ctx, jenkins_hashfn);
 		ok1(tdb);
 		ok1(tdb);
 		ok1(log_count == 0);
 		ok1(log_count == 0);
 		ok1(tdb1_check(tdb, NULL, NULL) == 0);
 		ok1(tdb1_check(tdb, NULL, NULL) == 0);
 		tdb1_close(tdb);
 		tdb1_close(tdb);
 
 
-		/* OK, now create with incompatible flag, default hash. */
+		/* OK, now create with incompatible hash. */
 		log_count = 0;
 		log_count = 0;
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0,
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0,
-				  flags|TDB1_INCOMPATIBLE_HASH,
+				  flags,
 				  O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
 				  O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
-				  NULL);
+				  tdb1_incompatible_hash);
 		ok1(tdb);
 		ok1(tdb);
 		ok1(log_count == 0);
 		ok1(log_count == 0);
 		d.dptr = (void *)"Hello";
 		d.dptr = (void *)"Hello";
@@ -109,14 +120,14 @@ int main(int argc, char *argv[])
 		/* Cannot open with old hash. */
 		/* Cannot open with old hash. */
 		log_count = 0;
 		log_count = 0;
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0, 0,
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0, 0,
-				  O_RDWR, 0600, &log_ctx, tdb1_old_hash);
+				  O_RDWR, 0600, &log_ctx, old_hash);
 		ok1(!tdb);
 		ok1(!tdb);
 		ok1(log_count == 1);
 		ok1(log_count == 1);
 
 
 		/* Can open with jenkins hash. */
 		/* Can open with jenkins hash. */
 		log_count = 0;
 		log_count = 0;
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0, 0,
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0, 0,
-				  O_RDWR, 0600, &log_ctx, tdb1_jenkins_hash);
+				  O_RDWR, 0600, &log_ctx, jenkins_hashfn);
 		ok1(tdb);
 		ok1(tdb);
 		ok1(log_count == 0);
 		ok1(log_count == 0);
 		d = tdb1_fetch(tdb, d);
 		d = tdb1_fetch(tdb, d);
@@ -139,10 +150,11 @@ int main(int argc, char *argv[])
 		ok1(tdb1_check(tdb, NULL, NULL) == 0);
 		ok1(tdb1_check(tdb, NULL, NULL) == 0);
 		tdb1_close(tdb);
 		tdb1_close(tdb);
 
 
+		/* FIXME: Not possible with TDB2 :( */
 		/* We can also use incompatible hash with other hashes. */
 		/* We can also use incompatible hash with other hashes. */
 		log_count = 0;
 		log_count = 0;
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0,
 		tdb = tdb1_open_ex("run-incompatible.tdb", 0,
-				  flags|TDB1_INCOMPATIBLE_HASH,
+				  flags,
 				  O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
 				  O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
 				  tdb1_dumb_hash);
 				  tdb1_dumb_hash);
 		ok1(tdb);
 		ok1(tdb);
@@ -152,8 +164,8 @@ int main(int argc, char *argv[])
 		ok1(tdb1_store(tdb, d, d, TDB_INSERT) == 0);
 		ok1(tdb1_store(tdb, d, d, TDB_INSERT) == 0);
 		tdb1_close(tdb);
 		tdb1_close(tdb);
 
 
-		/* Should have marked rwlocks field. */
-		ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic);
+		/* FIXME: Should have marked rwlocks field. */
+		ok1(hdr_rwlocks("run-incompatible.tdb") != rwmagic);
 
 
 		/* It should not open if we don't specify. */
 		/* It should not open if we don't specify. */
 		log_count = 0;
 		log_count = 0;

+ 2 - 2
ccan/tdb2/test/run-tdb1-oldhash.c

@@ -25,13 +25,13 @@ int main(int argc, char *argv[])
 	tdb1_close(tdb);
 	tdb1_close(tdb);
 
 
 	tdb = tdb1_open_ex("test/old-nohash-le.tdb1", 0, 0, O_RDWR, 0,
 	tdb = tdb1_open_ex("test/old-nohash-le.tdb1", 0, 0, O_RDWR, 0,
-			  &taplogctx, tdb1_jenkins_hash);
+			  &taplogctx, tdb1_incompatible_hash);
 	ok1(tdb);
 	ok1(tdb);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);
 	tdb1_close(tdb);
 	tdb1_close(tdb);
 
 
 	tdb = tdb1_open_ex("test/old-nohash-be.tdb1", 0, 0, O_RDWR, 0,
 	tdb = tdb1_open_ex("test/old-nohash-be.tdb1", 0, 0, O_RDWR, 0,
-			  &taplogctx, tdb1_jenkins_hash);
+			  &taplogctx, tdb1_incompatible_hash);
 	ok1(tdb);
 	ok1(tdb);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);
 	tdb1_close(tdb);
 	tdb1_close(tdb);

+ 19 - 7
ccan/tdb2/test/run-tdb1-wronghash-fail.c

@@ -11,6 +11,18 @@ static void log_fn(struct tdb1_context *tdb, enum tdb_log_level level,
 		(*count)++;
 		(*count)++;
 }
 }
 
 
+static unsigned int jenkins_hashfn(TDB_DATA *key)
+{
+	return hashlittle(key->dptr, key->dsize);
+}
+
+/* the tdb1_old_hash function is "magic" as it automatically makes us test the
+ * tdb1_incompatible_hash as well, so use this wrapper. */
+static unsigned int old_hash(TDB_DATA *key)
+{
+	return tdb1_old_hash(key);
+}
+
 int main(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
 {
 	struct tdb1_context *tdb;
 	struct tdb1_context *tdb;
@@ -33,7 +45,7 @@ int main(int argc, char *argv[])
 
 
 	/* Fail to open with different hash. */
 	/* Fail to open with different hash. */
 	tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
 	tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
-			  &log_ctx, tdb1_jenkins_hash);
+			  &log_ctx, jenkins_hashfn);
 	ok1(!tdb);
 	ok1(!tdb);
 	ok1(log_count == 1);
 	ok1(log_count == 1);
 
 
@@ -41,7 +53,7 @@ int main(int argc, char *argv[])
 	log_count = 0;
 	log_count = 0;
 	tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0,
 	tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0,
 			  O_CREAT|O_RDWR|O_TRUNC,
 			  O_CREAT|O_RDWR|O_TRUNC,
-			  0600, &log_ctx, tdb1_jenkins_hash);
+			  0600, &log_ctx, jenkins_hashfn);
 	ok1(tdb);
 	ok1(tdb);
 	ok1(log_count == 0);
 	ok1(log_count == 0);
 	tdb1_close(tdb);
 	tdb1_close(tdb);
@@ -49,26 +61,26 @@ int main(int argc, char *argv[])
 	/* Endian should be no problem. */
 	/* Endian should be no problem. */
 	log_count = 0;
 	log_count = 0;
 	tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDWR, 0,
 	tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDWR, 0,
-			  &log_ctx, tdb1_old_hash);
+			  &log_ctx, old_hash);
 	ok1(!tdb);
 	ok1(!tdb);
 	ok1(log_count == 1);
 	ok1(log_count == 1);
 
 
 	log_count = 0;
 	log_count = 0;
 	tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDWR, 0,
 	tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDWR, 0,
-			  &log_ctx, tdb1_old_hash);
+			  &log_ctx, old_hash);
 	ok1(!tdb);
 	ok1(!tdb);
 	ok1(log_count == 1);
 	ok1(log_count == 1);
 
 
 	log_count = 0;
 	log_count = 0;
 	/* Fail to open with old default hash. */
 	/* Fail to open with old default hash. */
 	tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
 	tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
-			  &log_ctx, tdb1_old_hash);
+			  &log_ctx, old_hash);
 	ok1(!tdb);
 	ok1(!tdb);
 	ok1(log_count == 1);
 	ok1(log_count == 1);
 
 
 	log_count = 0;
 	log_count = 0;
 	tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDONLY,
 	tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDONLY,
-			  0, &log_ctx, tdb1_jenkins_hash);
+			  0, &log_ctx, tdb1_incompatible_hash);
 	ok1(tdb);
 	ok1(tdb);
 	ok1(log_count == 0);
 	ok1(log_count == 0);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);
@@ -76,7 +88,7 @@ int main(int argc, char *argv[])
 
 
 	log_count = 0;
 	log_count = 0;
 	tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDONLY,
 	tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDONLY,
-			  0, &log_ctx, tdb1_jenkins_hash);
+			  0, &log_ctx, tdb1_incompatible_hash);
 	ok1(tdb);
 	ok1(tdb);
 	ok1(log_count == 0);
 	ok1(log_count == 0);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);
 	ok1(tdb1_check(tdb, NULL, NULL) == 0);