tdb2: tdb_set_attribute, tdb_unset_attribute and tdb_get_attribute
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 7 Apr 2011 07:22:35 +0000 (16:52 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 7 Apr 2011 07:22:35 +0000 (16:52 +0930)
It makes sense for some attributes to be manipulated after tdb_open,
so allow that.

ccan/tdb2/hash.c
ccan/tdb2/open.c
ccan/tdb2/private.h
ccan/tdb2/tdb2.h
ccan/tdb2/test/failtest_helper.h
ccan/tdb2/test/run-90-get-set-attributes.c [new file with mode: 0644]

index c0b34f8a42ab576da4981cbf6b224c69f541bc35..745f04c87e5574f50719b3018b4a78f7167fbbd6 100644 (file)
 */
 #include "private.h"
 #include <assert.h>
-#include <ccan/hash/hash.h>
-
-static uint64_t jenkins_hash(const void *key, size_t length, uint64_t seed,
-                            void *arg)
-{
-       uint64_t ret;
-       /* hash64_stable assumes lower bits are more important; they are a
-        * slightly better hash.  We use the upper bits first, so swap them. */
-       ret = hash64_stable((const unsigned char *)key, length, seed);
-       return (ret >> 32) | (ret << 32);
-}
-
-void tdb_hash_init(struct tdb_context *tdb)
-{
-       tdb->hash_fn = jenkins_hash;
-}
 
 uint64_t tdb_hash(struct tdb_context *tdb, const void *ptr, size_t len)
 {
index 7aae8b466af9c8b051548ec1d3ac691ad64a0d8b..c6fd9a090ecf3078bb4135e6b0651403d8379978 100644 (file)
@@ -1,4 +1,5 @@
 #include "private.h"
+#include <ccan/hash/hash.h>
 #include <assert.h>
 
 /* all lock info, to detect double-opens (fcntl file don't nest!) */
@@ -181,6 +182,129 @@ static enum TDB_ERROR tdb_new_file(struct tdb_context *tdb)
        return TDB_SUCCESS;
 }
 
+enum TDB_ERROR tdb_set_attribute(struct tdb_context *tdb,
+                                const union tdb_attribute *attr)
+{
+       switch (attr->base.attr) {
+       case TDB_ATTRIBUTE_LOG:
+               tdb->log_fn = attr->log.fn;
+               tdb->log_data = attr->log.data;
+               break;
+       case TDB_ATTRIBUTE_HASH:
+       case TDB_ATTRIBUTE_SEED:
+       case TDB_ATTRIBUTE_OPENHOOK:
+               return tdb->last_error
+                       = tdb_logerr(tdb, TDB_ERR_EINVAL,
+                                    TDB_LOG_USE_ERROR,
+                                    "tdb_set_attribute:"
+                                    " cannot set %s after opening",
+                                    attr->base.attr == TDB_ATTRIBUTE_HASH
+                                    ? "TDB_ATTRIBUTE_HASH"
+                                    : attr->base.attr == TDB_ATTRIBUTE_SEED
+                                    ? "TDB_ATTRIBUTE_SEED"
+                                    : "TDB_ATTRIBUTE_OPENHOOK");
+       case TDB_ATTRIBUTE_FLOCK:
+               tdb->lock_fn = attr->flock.lock;
+               tdb->unlock_fn = attr->flock.unlock;
+               tdb->lock_data = attr->flock.data;
+               break;
+       default:
+               return tdb->last_error
+                       = tdb_logerr(tdb, TDB_ERR_EINVAL,
+                                    TDB_LOG_USE_ERROR,
+                                    "tdb_set_attribute:"
+                                    " unknown attribute type %u",
+                                    attr->base.attr);
+       }
+       return TDB_SUCCESS;
+}
+
+static uint64_t jenkins_hash(const void *key, size_t length, uint64_t seed,
+                            void *unused)
+{
+       uint64_t ret;
+       /* hash64_stable assumes lower bits are more important; they are a
+        * slightly better hash.  We use the upper bits first, so swap them. */
+       ret = hash64_stable((const unsigned char *)key, length, seed);
+       return (ret >> 32) | (ret << 32);
+}
+
+enum TDB_ERROR tdb_get_attribute(struct tdb_context *tdb,
+                                union tdb_attribute *attr)
+{
+       switch (attr->base.attr) {
+       case TDB_ATTRIBUTE_LOG:
+               if (!tdb->log_fn)
+                       return tdb->last_error = TDB_ERR_NOEXIST;
+               attr->log.fn = tdb->log_fn;
+               attr->log.data = tdb->log_data;
+               break;
+       case TDB_ATTRIBUTE_HASH:
+               attr->hash.fn = tdb->hash_fn;
+               attr->hash.data = tdb->hash_data;
+               break;
+       case TDB_ATTRIBUTE_SEED:
+               attr->seed.seed = tdb->hash_seed;
+               break;
+       case TDB_ATTRIBUTE_OPENHOOK:
+               return tdb->last_error
+                       = tdb_logerr(tdb, TDB_ERR_EINVAL,
+                                    TDB_LOG_USE_ERROR,
+                                    "tdb_get_attribute:"
+                                    " cannot get TDB_ATTRIBUTE_OPENHOOK");
+       case TDB_ATTRIBUTE_STATS:
+               /* FIXME */
+               return TDB_ERR_EINVAL;
+       case TDB_ATTRIBUTE_FLOCK:
+               attr->flock.lock = tdb->lock_fn;
+               attr->flock.unlock = tdb->unlock_fn;
+               attr->flock.data = tdb->lock_data;
+               break;
+       default:
+               return tdb->last_error
+                       = tdb_logerr(tdb, TDB_ERR_EINVAL,
+                                    TDB_LOG_USE_ERROR,
+                                    "tdb_get_attribute:"
+                                    " unknown attribute type %u",
+                                    attr->base.attr);
+       }
+       attr->base.next = NULL;
+       return TDB_SUCCESS;
+}
+
+void tdb_unset_attribute(struct tdb_context *tdb,
+                        enum tdb_attribute_type type)
+{
+       switch (type) {
+       case TDB_ATTRIBUTE_LOG:
+               tdb->log_fn = NULL;
+               break;
+       case TDB_ATTRIBUTE_HASH:
+       case TDB_ATTRIBUTE_SEED:
+       case TDB_ATTRIBUTE_OPENHOOK:
+               tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
+                          "tdb_unset_attribute: cannot unset %s after opening",
+                          type == TDB_ATTRIBUTE_HASH
+                          ? "TDB_ATTRIBUTE_HASH"
+                          : type == TDB_ATTRIBUTE_SEED
+                          ? "TDB_ATTRIBUTE_SEED"
+                          : "TDB_ATTRIBUTE_OPENHOOK");
+               break;
+       case TDB_ATTRIBUTE_STATS:
+               /* FIXME */
+               break;
+       case TDB_ATTRIBUTE_FLOCK:
+               tdb->lock_fn = tdb_fcntl_lock;
+               tdb->unlock_fn = tdb_fcntl_unlock;
+               break;
+       default:
+               tdb_logerr(tdb, TDB_ERR_EINVAL,
+                          TDB_LOG_USE_ERROR,
+                          "tdb_unset_attribute: unknown attribute type %u",
+                          type);
+       }
+}
+
 struct tdb_context *tdb_open(const char *name, int tdb_flags,
                             int open_flags, mode_t mode,
                             union tdb_attribute *attr)
@@ -212,17 +336,13 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
        tdb->access = NULL;
        tdb->last_error = TDB_SUCCESS;
        tdb->file = NULL;
-       tdb->lock_fn = fcntl_lock;
-       tdb->unlock_fn = fcntl_unlock;
-       tdb_hash_init(tdb);
+       tdb->lock_fn = tdb_fcntl_lock;
+       tdb->unlock_fn = tdb_fcntl_unlock;
+       tdb->hash_fn = jenkins_hash;
        tdb_io_init(tdb);
 
        while (attr) {
                switch (attr->base.attr) {
-               case TDB_ATTRIBUTE_LOG:
-                       tdb->log_fn = attr->log.fn;
-                       tdb->log_data = attr->log.data;
-                       break;
                case TDB_ATTRIBUTE_HASH:
                        tdb->hash_fn = attr->hash.fn;
                        tdb->hash_data = attr->hash.data;
@@ -239,18 +359,11 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
                case TDB_ATTRIBUTE_OPENHOOK:
                        openhook = &attr->openhook;
                        break;
-               case TDB_ATTRIBUTE_FLOCK:
-                       tdb->lock_fn = attr->flock.lock;
-                       tdb->unlock_fn = attr->flock.unlock;
-                       tdb->lock_data = attr->flock.data;
-                       break;
                default:
-                       ecode = tdb_logerr(tdb, TDB_ERR_EINVAL,
-                                          TDB_LOG_USE_ERROR,
-                                          "tdb_open:"
-                                          " unknown attribute type %u",
-                                          attr->base.attr);
-                       goto fail;
+                       /* These are set as normal. */
+                       ecode = tdb_set_attribute(tdb, attr);
+                       if (ecode != TDB_SUCCESS)
+                               goto fail;
                }
                attr = attr->base.next;
        }
index 9cd8188c70c559c3f0e1c2a6f2845cab4571cc3c..306cb3ff467f70397fbcd91709a285c34e9e9de8 100644 (file)
@@ -417,8 +417,6 @@ struct tdb_methods {
   internal prototypes
 */
 /* hash.c: */
-void tdb_hash_init(struct tdb_context *tdb);
-
 tdb_bool_err first_in_hash(struct tdb_context *tdb,
                           struct traverse_info *tinfo,
                           TDB_DATA *kbuf, size_t *dlen);
index b88568a881568a89381aba0e557216eb0d2ecc6e..296410e74196573a955470b78c422daa870f8be5 100644 (file)
@@ -582,6 +582,61 @@ void tdb_add_flag(struct tdb_context *tdb, unsigned flag);
  */
 void tdb_remove_flag(struct tdb_context *tdb, unsigned flag);
 
+/**
+ * enum tdb_attribute_type - descriminator for union tdb_attribute.
+ */
+enum tdb_attribute_type {
+       TDB_ATTRIBUTE_LOG = 0,
+       TDB_ATTRIBUTE_HASH = 1,
+       TDB_ATTRIBUTE_SEED = 2,
+       TDB_ATTRIBUTE_STATS = 3,
+       TDB_ATTRIBUTE_OPENHOOK = 4,
+       TDB_ATTRIBUTE_FLOCK = 5
+};
+
+/**
+ * tdb_get_attribute - get an attribute for an existing tdb
+ * @tdb: the tdb context returned from tdb_open()
+ * @attr: the union tdb_attribute to set.
+ *
+ * This gets an attribute from a TDB which has previously been set (or
+ * may return the default values).  Set @attr.base.attr to the
+ * attribute type you want get.
+ *
+ * Currently this does not work for TDB_ATTRIBUTE_OPENHOOK.
+ */
+enum TDB_ERROR tdb_get_attribute(struct tdb_context *tdb,
+                                union tdb_attribute *attr);
+
+/**
+ * tdb_set_attribute - set an attribute for an existing tdb
+ * @tdb: the tdb context returned from tdb_open()
+ * @attr: the union tdb_attribute to set.
+ *
+ * This sets an attribute on a TDB, overriding any previous attribute
+ * of the same type.  It returns TDB_ERR_EINVAL if the attribute is
+ * unknown or invalid.
+ *
+ * Note that TDB_ATTRIBUTE_HASH, TDB_ATTRIBUTE_SEED and
+ * TDB_ATTRIBUTE_OPENHOOK cannot currently be set after tdb_open.
+ */
+enum TDB_ERROR tdb_set_attribute(struct tdb_context *tdb,
+                                const union tdb_attribute *attr);
+
+/**
+ * tdb_unset_attribute - reset an attribute for an existing tdb
+ * @tdb: the tdb context returned from tdb_open()
+ * @type: the attribute type to unset.
+ *
+ * This unsets an attribute on a TDB, returning it to the defaults
+ * (where applicable).
+ *
+ * Note that it only makes sense for TDB_ATTRIBUTE_LOG and TDB_ATTRIBUTE_FLOCK
+ * to be unset.
+ */
+void tdb_unset_attribute(struct tdb_context *tdb,
+                        enum tdb_attribute_type type);
+
 /**
  * tdb_name - get the name of a tdb
  * @tdb: the tdb context returned from tdb_open()
@@ -602,18 +657,6 @@ const char *tdb_name(const struct tdb_context *tdb);
  */
 int tdb_fd(const struct tdb_context *tdb);
 
-/**
- * enum tdb_attribute_type - descriminator for union tdb_attribute.
- */
-enum tdb_attribute_type {
-       TDB_ATTRIBUTE_LOG = 0,
-       TDB_ATTRIBUTE_HASH = 1,
-       TDB_ATTRIBUTE_SEED = 2,
-       TDB_ATTRIBUTE_STATS = 3,
-       TDB_ATTRIBUTE_OPENHOOK = 4,
-       TDB_ATTRIBUTE_FLOCK = 5
-};
-
 /**
  * struct tdb_attribute_base - common fields for all tdb attributes.
  */
index 42b38149beecce6f4306f6a72ecf8d0f18628a1a..214fcfa1dec3fcd3e0b47203457ecbc3cdaac292 100644 (file)
@@ -4,9 +4,9 @@
 #include <stdbool.h>
 
 /* FIXME: Check these! */
-#define INITIAL_TDB_MALLOC     "open.c", 200, FAILTEST_MALLOC
-#define URANDOM_OPEN           "open.c", 44, FAILTEST_OPEN
-#define URANDOM_READ           "open.c", 24, FAILTEST_READ
+#define INITIAL_TDB_MALLOC     "open.c", 324, FAILTEST_MALLOC
+#define URANDOM_OPEN           "open.c", 45, FAILTEST_OPEN
+#define URANDOM_READ           "open.c", 25, FAILTEST_READ
 
 bool exit_check_log(struct failtest_call *history, unsigned num);
 bool failmatch(const struct failtest_call *call,
diff --git a/ccan/tdb2/test/run-90-get-set-attributes.c b/ccan/tdb2/test/run-90-get-set-attributes.c
new file mode 100644 (file)
index 0000000..159d8a0
--- /dev/null
@@ -0,0 +1,165 @@
+#include <ccan/tdb2/tdb.c>
+#include <ccan/tdb2/open.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/transaction.c>
+#include <ccan/tdb2/traverse.c>
+#include <ccan/tap/tap.h>
+#include "logging.h"
+
+static int mylock(int fd, int rw, off_t off, off_t len, bool waitflag,
+                 void *unused)
+{
+       return 0;
+}
+
+static int myunlock(int fd, int rw, off_t off, off_t len, void *unused)
+{
+       return 0;
+}
+
+static uint64_t hash_fn(const void *key, size_t len, uint64_t seed,
+                       void *priv)
+{
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int i;
+       struct tdb_context *tdb;
+       int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
+                       TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+       union tdb_attribute seed_attr;
+       union tdb_attribute hash_attr;
+       union tdb_attribute lock_attr;
+
+       hash_attr.base.attr = TDB_ATTRIBUTE_HASH;
+       hash_attr.base.next = &seed_attr;
+       hash_attr.hash.fn = hash_fn;
+       hash_attr.hash.data = &hash_attr;
+
+       seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
+       seed_attr.base.next = &lock_attr;
+       seed_attr.seed.seed = 100;
+
+       lock_attr.base.attr = TDB_ATTRIBUTE_FLOCK;
+       lock_attr.base.next = &tap_log_attr;
+       lock_attr.flock.lock = mylock;
+       lock_attr.flock.unlock = myunlock;
+       lock_attr.flock.data = &lock_attr;
+
+       plan_tests(sizeof(flags) / sizeof(flags[0]) * 50);
+
+       for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+               union tdb_attribute attr;
+
+               /* First open with no attributes. */
+               tdb = tdb_open("run-90-get-set-attributes.tdb", flags[i],
+                              O_RDWR|O_CREAT|O_TRUNC, 0600, NULL);
+               ok1(tdb);
+
+               /* Get log on no attributes will fail */
+               attr.base.attr = TDB_ATTRIBUTE_LOG;
+               ok1(tdb_get_attribute(tdb, &attr) == TDB_ERR_NOEXIST);
+               /* These always work. */
+               attr.base.attr = TDB_ATTRIBUTE_HASH;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_HASH);
+               ok1(attr.hash.fn == jenkins_hash);
+               attr.base.attr = TDB_ATTRIBUTE_FLOCK;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_FLOCK);
+               ok1(attr.flock.lock == tdb_fcntl_lock);
+               ok1(attr.flock.unlock == tdb_fcntl_unlock);
+               attr.base.attr = TDB_ATTRIBUTE_SEED;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_SEED);
+               /* This is possible, just astronomically unlikely. */
+               ok1(attr.seed.seed != 0);
+
+               /* Unset attributes. */
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_LOG);
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
+
+               /* Set them. */
+               ok1(tdb_set_attribute(tdb, &tap_log_attr) == 0);
+               ok1(tdb_set_attribute(tdb, &lock_attr) == 0);
+               /* These should fail. */
+               ok1(tdb_set_attribute(tdb, &seed_attr) == TDB_ERR_EINVAL);
+               ok1(tap_log_messages == 1);
+               ok1(tdb_set_attribute(tdb, &hash_attr) == TDB_ERR_EINVAL);
+               ok1(tap_log_messages == 2);
+               tap_log_messages = 0;
+
+               /* Getting them should work as expected. */
+               attr.base.attr = TDB_ATTRIBUTE_LOG;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_LOG);
+               ok1(attr.log.fn == tap_log_attr.log.fn);
+               ok1(attr.log.data == tap_log_attr.log.data);
+
+               attr.base.attr = TDB_ATTRIBUTE_FLOCK;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_FLOCK);
+               ok1(attr.flock.lock == mylock);
+               ok1(attr.flock.unlock == myunlock);
+               ok1(attr.flock.data == &lock_attr);
+
+               /* Unset them again. */
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
+               ok1(tap_log_messages == 0);
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_LOG);
+               ok1(tap_log_messages == 0);
+
+               tdb_close(tdb);
+               ok1(tap_log_messages == 0);
+
+               /* Now open with all attributes. */
+               tdb = tdb_open("run-90-get-set-attributes.tdb", flags[i],
+                              O_RDWR|O_CREAT|O_TRUNC, 0600, &hash_attr);
+               ok1(tdb);
+
+               /* Get will succeed */
+               attr.base.attr = TDB_ATTRIBUTE_LOG;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_LOG);
+               ok1(attr.log.fn == tap_log_attr.log.fn);
+               ok1(attr.log.data == tap_log_attr.log.data);
+
+               attr.base.attr = TDB_ATTRIBUTE_HASH;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_HASH);
+               ok1(attr.hash.fn == hash_fn);
+               ok1(attr.hash.data == &hash_attr);
+
+               attr.base.attr = TDB_ATTRIBUTE_FLOCK;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_FLOCK);
+               ok1(attr.flock.lock == mylock);
+               ok1(attr.flock.unlock == myunlock);
+               ok1(attr.flock.data == &lock_attr);
+
+               attr.base.attr = TDB_ATTRIBUTE_SEED;
+               ok1(tdb_get_attribute(tdb, &attr) == 0);
+               ok1(attr.base.attr == TDB_ATTRIBUTE_SEED);
+               ok1(attr.seed.seed == seed_attr.seed.seed);
+
+               /* Unset attributes. */
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_HASH);
+               ok1(tap_log_messages == 1);
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_SEED);
+               ok1(tap_log_messages == 2);
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
+               tdb_unset_attribute(tdb, TDB_ATTRIBUTE_LOG);
+               ok1(tap_log_messages == 2);
+               tap_log_messages = 0;
+
+               tdb_close(tdb);
+
+       }
+       return exit_status();
+}