-#include <ccan/tdb2/tdb2.h>
-#include <ccan/hash/hash.h>
-#include <ccan/build_assert/build_assert.h>
-#include <ccan/likely/likely.h>
-#include <assert.h>
-
-/* The null return. */
-struct tdb_data tdb_null = { .dptr = NULL, .dsize = 0 };
-
-/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
-static struct tdb_context *tdbs = NULL;
-
-PRINTF_ATTRIBUTE(4, 5) static void
-null_log_fn(struct tdb_context *tdb,
- enum tdb_debug_level level, void *priv,
- const char *fmt, ...)
-{
-}
-
-/* We do a lot of work assuming our copy of the header volatile area
- * is uptodate, and usually it is. However, once we grab a lock, we have to
- * re-check it. */
-bool header_changed(struct tdb_context *tdb)
-{
- uint64_t gen;
-
- if (!(tdb->flags & TDB_NOLOCK) && tdb->header_uptodate) {
- tdb->log(tdb, TDB_DEBUG_WARNING, tdb->log_priv,
- "warning: header uptodate already\n");
- }
-
- /* We could get a partial update if we're not holding any locks. */
- assert((tdb->flags & TDB_NOLOCK) || tdb_has_locks(tdb));
-
- tdb->header_uptodate = true;
- gen = tdb_read_off(tdb, offsetof(struct tdb_header, v.generation));
- if (unlikely(gen != tdb->header.v.generation)) {
- tdb_read_convert(tdb, offsetof(struct tdb_header, v),
- &tdb->header.v, sizeof(tdb->header.v));
- return true;
- }
- return false;
-}
-
-int write_header(struct tdb_context *tdb)
-{
- assert(tdb_read_off(tdb, offsetof(struct tdb_header, v.generation))
- == tdb->header.v.generation);
- tdb->header.v.generation++;
- return tdb_write_convert(tdb, offsetof(struct tdb_header, v),
- &tdb->header.v, sizeof(tdb->header.v));
-}
-
-static uint64_t jenkins_hash(const void *key, size_t length, uint64_t seed,
- void *arg)
-{
- return hash64_stable((const unsigned char *)key, length, seed);
-}
-
-uint64_t tdb_hash(struct tdb_context *tdb, const void *ptr, size_t len)
-{
- return tdb->khash(ptr, len, tdb->header.hash_seed, tdb->hash_priv);
-}
-
-static bool tdb_already_open(dev_t device, ino_t ino)
-{
- struct tdb_context *i;
-
- for (i = tdbs; i; i = i->next) {
- if (i->device == device && i->inode == ino) {
- return true;
- }
- }
-
- return false;
-}
-
-static uint64_t random_number(struct tdb_context *tdb)
-{
- int fd;
- uint64_t ret = 0;
- struct timeval now;
-
- fd = open("/dev/urandom", O_RDONLY);
- if (fd >= 0) {
- if (tdb_read_all(fd, &ret, sizeof(ret))) {
- tdb->log(tdb, TDB_DEBUG_TRACE, tdb->log_priv,
- "tdb_open: random from /dev/urandom\n");
- close(fd);
- return ret;
- }
- close(fd);
- }
- /* FIXME: Untested! Based on Wikipedia protocol description! */
- fd = open("/dev/egd-pool", O_RDWR);
- if (fd >= 0) {
- /* Command is 1, next byte is size we want to read. */
- char cmd[2] = { 1, sizeof(uint64_t) };
- if (write(fd, cmd, sizeof(cmd)) == sizeof(cmd)) {
- char reply[1 + sizeof(uint64_t)];
- int r = read(fd, reply, sizeof(reply));
- if (r > 1) {
- tdb->log(tdb, TDB_DEBUG_TRACE, tdb->log_priv,
- "tdb_open: %u random bytes from"
- " /dev/egd-pool\n", r-1);
- /* Copy at least some bytes. */
- memcpy(&ret, reply+1, r - 1);
- if (reply[0] == sizeof(uint64_t)
- && r == sizeof(reply)) {
- close(fd);
- return ret;
- }
- }
- }
- close(fd);
- }
-
- /* Fallback: pid and time. */
- gettimeofday(&now, NULL);
- ret = getpid() * 100132289ULL + now.tv_sec * 1000000ULL + now.tv_usec;
- tdb->log(tdb, TDB_DEBUG_TRACE, tdb->log_priv,
- "tdb_open: random from getpid and time\n");
- return ret;
-}
-
-struct new_database {
- struct tdb_header hdr;
- struct tdb_used_record hrec;
- tdb_off_t hash[1ULL << INITIAL_HASH_BITS];
- struct tdb_used_record frec;
- tdb_off_t free[INITIAL_FREE_BUCKETS + 1]; /* One overflow bucket */
-};
-
-/* initialise a new database */
-static int tdb_new_database(struct tdb_context *tdb)
-{
- /* We make it up in memory, then write it out if not internal */
- struct new_database newdb;
- unsigned int magic_off = offsetof(struct tdb_header, magic_food);
-
- /* Fill in the header */
- newdb.hdr.version = TDB_VERSION;
- newdb.hdr.hash_seed = random_number(tdb);
- newdb.hdr.hash_test = TDB_HASH_MAGIC;
- newdb.hdr.hash_test = tdb->khash(&newdb.hdr.hash_test,
- sizeof(newdb.hdr.hash_test),
- newdb.hdr.hash_seed,
- tdb->hash_priv);
-
- newdb.hdr.v.generation = 0;
-
- /* The initial zone must cover the initial database size! */
- BUILD_ASSERT((1ULL << INITIAL_ZONE_BITS) >= sizeof(newdb));
-
- /* Free array has 1 zone, 10 buckets. All buckets empty. */
- newdb.hdr.v.num_zones = 1;
- newdb.hdr.v.zone_bits = INITIAL_ZONE_BITS;
- newdb.hdr.v.free_buckets = INITIAL_FREE_BUCKETS;
- newdb.hdr.v.free_off = offsetof(struct new_database, free);
- set_header(tdb, &newdb.frec, 0,
- sizeof(newdb.free), sizeof(newdb.free), 0);
- memset(newdb.free, 0, sizeof(newdb.free));
-
- /* Initial hashes are empty. */
- newdb.hdr.v.hash_bits = INITIAL_HASH_BITS;
- newdb.hdr.v.hash_off = offsetof(struct new_database, hash);
- set_header(tdb, &newdb.hrec, 0,
- sizeof(newdb.hash), sizeof(newdb.hash), 0);
- memset(newdb.hash, 0, sizeof(newdb.hash));
-
- /* Magic food */
- memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
- strcpy(newdb.hdr.magic_food, TDB_MAGIC_FOOD);
-
- /* This creates an endian-converted database, as if read from disk */
- tdb_convert(tdb,
- (char *)&newdb.hdr + magic_off,
- sizeof(newdb) - magic_off);
-
- tdb->header = newdb.hdr;
-
- if (tdb->flags & TDB_INTERNAL) {
- tdb->map_size = sizeof(newdb);
- tdb->map_ptr = malloc(tdb->map_size);
- if (!tdb->map_ptr) {
- tdb->ecode = TDB_ERR_OOM;
- return -1;
- }
- memcpy(tdb->map_ptr, &newdb, tdb->map_size);
- return 0;
- }
- if (lseek(tdb->fd, 0, SEEK_SET) == -1)
- return -1;
-
- if (ftruncate(tdb->fd, 0) == -1)
- return -1;
-
- if (!tdb_pwrite_all(tdb->fd, &newdb, sizeof(newdb), 0)) {
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
- return 0;
-}
-
-struct tdb_context *tdb_open(const char *name, int tdb_flags,
- int open_flags, mode_t mode,
- union tdb_attribute *attr)