Commit 703cea0c authored by Rusty Russell's avatar Rusty Russell

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.
parent 7fe32184
......@@ -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)
{
......
#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;
}
......
......@@ -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);
......
......@@ -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.
*/
......
......@@ -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,
......
#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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment