Browse Source

tdb2: tdb_set_attribute, tdb_unset_attribute and tdb_get_attribute

It makes sense for some attributes to be manipulated after tdb_open,
so allow that.
Rusty Russell 15 years ago
parent
commit
703cea0c78
6 changed files with 354 additions and 51 deletions
  1. 0 16
      ccan/tdb2/hash.c
  2. 131 18
      ccan/tdb2/open.c
  3. 0 2
      ccan/tdb2/private.h
  4. 55 12
      ccan/tdb2/tdb2.h
  5. 3 3
      ccan/tdb2/test/failtest_helper.h
  6. 165 0
      ccan/tdb2/test/run-90-get-set-attributes.c

+ 0 - 16
ccan/tdb2/hash.c

@@ -17,22 +17,6 @@
 */
 #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)
 {

+ 131 - 18
ccan/tdb2/open.c

@@ -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;
 	}

+ 0 - 2
ccan/tdb2/private.h

@@ -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);

+ 55 - 12
ccan/tdb2/tdb2.h

@@ -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.
  */

+ 3 - 3
ccan/tdb2/test/failtest_helper.h

@@ -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,

+ 165 - 0
ccan/tdb2/test/run-90-get-set-attributes.c

@@ -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();
+}