From 703cea0c78a896c658272af06306218fc0bb23cc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 7 Apr 2011 16:52:35 +0930 Subject: [PATCH 1/1] 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. --- ccan/tdb2/hash.c | 16 -- ccan/tdb2/open.c | 149 ++++++++++++++++--- ccan/tdb2/private.h | 2 - ccan/tdb2/tdb2.h | 67 +++++++-- ccan/tdb2/test/failtest_helper.h | 6 +- ccan/tdb2/test/run-90-get-set-attributes.c | 165 +++++++++++++++++++++ 6 files changed, 354 insertions(+), 51 deletions(-) create mode 100644 ccan/tdb2/test/run-90-get-set-attributes.c diff --git a/ccan/tdb2/hash.c b/ccan/tdb2/hash.c index c0b34f8a..745f04c8 100644 --- a/ccan/tdb2/hash.c +++ b/ccan/tdb2/hash.c @@ -17,22 +17,6 @@ */ #include "private.h" #include -#include - -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) { diff --git a/ccan/tdb2/open.c b/ccan/tdb2/open.c index 7aae8b46..c6fd9a09 100644 --- a/ccan/tdb2/open.c +++ b/ccan/tdb2/open.c @@ -1,4 +1,5 @@ #include "private.h" +#include #include /* 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; } diff --git a/ccan/tdb2/private.h b/ccan/tdb2/private.h index 9cd8188c..306cb3ff 100644 --- a/ccan/tdb2/private.h +++ b/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); diff --git a/ccan/tdb2/tdb2.h b/ccan/tdb2/tdb2.h index b88568a8..296410e7 100644 --- a/ccan/tdb2/tdb2.h +++ b/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. */ diff --git a/ccan/tdb2/test/failtest_helper.h b/ccan/tdb2/test/failtest_helper.h index 42b38149..214fcfa1 100644 --- a/ccan/tdb2/test/failtest_helper.h +++ b/ccan/tdb2/test/failtest_helper.h @@ -4,9 +4,9 @@ #include /* 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 index 00000000..159d8a01 --- /dev/null +++ b/ccan/tdb2/test/run-90-get-set-attributes.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(); +} -- 2.39.2