Browse Source

tdb2: make tdb_check typesafe.

Rusty Russell 15 years ago
parent
commit
a0e0927e1f
4 changed files with 115 additions and 12 deletions
  1. 5 6
      ccan/tdb2/check.c
  2. 6 0
      ccan/tdb2/doc/design.lyx
  3. 15 6
      ccan/tdb2/tdb2.h
  4. 89 0
      ccan/tdb2/test/run-check-callback.c

+ 5 - 6
ccan/tdb2/check.c

@@ -717,10 +717,10 @@ static enum TDB_ERROR check_linear(struct tdb_context *tdb,
 	return TDB_SUCCESS;
 }
 
-enum TDB_ERROR tdb_check(struct tdb_context *tdb,
-			 enum TDB_ERROR (*check)(TDB_DATA key, TDB_DATA data,
-						 void *private_data),
-			 void *private_data)
+enum TDB_ERROR tdb_check_(struct tdb_context *tdb,
+			  enum TDB_ERROR (*check)(TDB_DATA key, TDB_DATA data,
+						  void *private),
+			  void *private)
 {
 	tdb_off_t *fr = NULL, *used = NULL, ft, recovery;
 	size_t num_free = 0, num_used = 0, num_found = 0, num_ftables = 0;
@@ -759,8 +759,7 @@ enum TDB_ERROR tdb_check(struct tdb_context *tdb,
 	}
 
 	/* FIXME: Check key uniqueness? */
-	ecode = check_hash(tdb, used, num_used, num_ftables, check,
-			   private_data);
+	ecode = check_hash(tdb, used, num_used, num_ftables, check, private);
 	if (ecode != TDB_SUCCESS)
 		goto out;
 

+ 6 - 0
ccan/tdb2/doc/design.lyx

@@ -1282,7 +1282,13 @@ Status
 \end_layout
 
 \begin_layout Standard
+
+\change_deleted 0 1300360712
 Incomplete.
+\change_inserted 0 1300360716
+Complete.
+\change_unchanged
+
 \end_layout
 
 \begin_layout Subsection

+ 15 - 6
ccan/tdb2/tdb2.h

@@ -332,18 +332,27 @@ enum TDB_ERROR tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
  * tdb_check - check a TDB for consistency
  * @tdb: the tdb context returned from tdb_open()
  * @check: function to check each key/data pair (or NULL)
- * @private_data: pointer for @check
+ * @private: argument for @check, must match type.
  *
  * This performs a consistency check of the open database, optionally calling
  * a check() function on each record so you can do your own data consistency
  * checks as well.  If check() returns an error, that is returned from
  * tdb_check().
+ *
+ * Returns TDB_SUCCESS or an error.
  */
-enum TDB_ERROR tdb_check(struct tdb_context *tdb,
-			 enum TDB_ERROR (*check)(TDB_DATA key,
-						 TDB_DATA data,
-						 void *private_data),
-			 void *private_data);
+#define tdb_check(tdb, check, private)					\
+	tdb_check_((tdb), typesafe_cb_preargs(enum TDB_ERROR,		\
+					      (check), (private),	\
+					      struct tdb_data,		\
+					      struct tdb_data),		\
+		   (private))
+
+enum TDB_ERROR tdb_check_(struct tdb_context *tdb,
+			  enum TDB_ERROR (*check)(struct tdb_data key,
+						  struct tdb_data data,
+						  void *private),
+			  void *private);
 
 /**
  * enum tdb_summary_flags - flags for tdb_summary.

+ 89 - 0
ccan/tdb2/test/run-check-callback.c

@@ -0,0 +1,89 @@
+#include <ccan/tdb2/tdb.c>
+#include <ccan/tdb2/free.c>
+#include <ccan/tdb2/lock.c>
+#include <ccan/tdb2/io.c>
+#include <ccan/tdb2/hash.c>
+#include <ccan/tdb2/check.c>
+#include <ccan/tdb2/traverse.c>
+#include <ccan/tdb2/transaction.c>
+#include <ccan/tap/tap.h>
+#include "logging.h"
+
+#define NUM_RECORDS 1000
+
+static bool store_records(struct tdb_context *tdb)
+{
+	int i;
+	struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
+	struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
+
+	for (i = 0; i < NUM_RECORDS; i++)
+		if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
+			return false;
+	return true;
+}
+
+static enum TDB_ERROR check(struct tdb_data key,
+			    struct tdb_data data,
+			    bool *array)
+{
+	int val;
+
+	if (key.dsize != sizeof(val)) {
+		diag("Wrong key size: %u\n", key.dsize);
+		return TDB_ERR_CORRUPT;
+	}
+
+	if (key.dsize != data.dsize
+	    || memcmp(key.dptr, data.dptr, sizeof(val)) != 0) {
+		diag("Key and data differ\n");
+		return TDB_ERR_CORRUPT;
+	}
+
+	memcpy(&val, key.dptr, sizeof(val));
+	if (val >= NUM_RECORDS || val < 0) {
+		diag("check value %i\n", val);
+		return TDB_ERR_CORRUPT;
+	}
+
+	if (array[val]) {
+		diag("Value %i already seen\n", val);
+		return TDB_ERR_CORRUPT;
+	}
+
+	array[val] = true;
+	return TDB_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+	unsigned int i, j;
+	struct tdb_context *tdb;
+	int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
+			TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, 
+			TDB_NOMMAP|TDB_CONVERT };
+
+	plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 1);
+	for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+		bool array[NUM_RECORDS];
+
+		tdb = tdb_open("run-check-callback.tdb", flags[i],
+			       O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+		ok1(tdb);
+		if (!tdb)
+			continue;
+
+		ok1(store_records(tdb));
+		for (j = 0; j < NUM_RECORDS; j++)
+			array[j] = false;
+		ok1(tdb_check(tdb, check, array) == TDB_SUCCESS);
+		for (j = 0; j < NUM_RECORDS; j++)
+			if (!array[j])
+				break;
+		ok1(j == NUM_RECORDS);
+		tdb_close(tdb);
+	}
+
+	ok1(tap_log_messages == 0);
+	return exit_status();
+}