tdb2: make tdb_check typesafe.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 17 Mar 2011 11:42:21 +0000 (22:12 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 17 Mar 2011 11:42:21 +0000 (22:12 +1030)
ccan/tdb2/check.c
ccan/tdb2/doc/design.lyx
ccan/tdb2/tdb2.h
ccan/tdb2/test/run-check-callback.c [new file with mode: 0644]

index 6fdfac3605be33d694885c6b73f2863f9e0d32f1..9bbd12645f199ba6a5a62708caccc79d08cf16d1 100644 (file)
@@ -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;
 
index cc453e58decc0895f2034f89757962514c38ab54..3487a7dae8b456d4f46fede2c26a6996ddbcf06c 100644 (file)
@@ -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
index 52c26959d02cb412496b7f00a5e8eff542ed2667..684bc0492a7260d9f284f8e5d59b3c3c0a097cf3 100644 (file)
@@ -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.
diff --git a/ccan/tdb2/test/run-check-callback.c b/ccan/tdb2/test/run-check-callback.c
new file mode 100644 (file)
index 0000000..c9a5bf5
--- /dev/null
@@ -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();
+}