- /* We make it up in memory, then write it out if not internal */
- struct new_database newdb;
- unsigned int bucket, magic_off, dbsize;
-
- /* Don't want any extra padding! */
- dbsize = offsetof(struct new_database, tailer) + sizeof(newdb.tailer);
-
- /* Fill in the header */
- newdb.h.hdr.version = TDB_VERSION;
- newdb.h.hdr.hash_seed = random_number(tdb);
- newdb.h.hdr.hash_test = TDB_HASH_MAGIC;
- newdb.h.hdr.hash_test = tdb->khash(&newdb.h.hdr.hash_test,
- sizeof(newdb.h.hdr.hash_test),
- newdb.h.hdr.hash_seed,
- tdb->hash_priv);
- memset(newdb.h.hdr.reserved, 0, sizeof(newdb.h.hdr.reserved));
- newdb.h.hdr.v.generation = 0;
- /* Initial hashes are empty. */
- newdb.h.hdr.v.hash_bits = INITIAL_HASH_BITS;
- newdb.h.hdr.v.hash_off = offsetof(struct new_database, h.hash);
- set_header(tdb, &newdb.h.hrec, 0,
- sizeof(newdb.h.hash), sizeof(newdb.h.hash), 0,
- INITIAL_ZONE_BITS);
- memset(newdb.h.hash, 0, sizeof(newdb.h.hash));
-
- /* Create the single free entry. */
- newdb.h.frec.magic_and_meta = TDB_FREE_MAGIC | INITIAL_ZONE_BITS;
- newdb.h.frec.data_len = (sizeof(newdb.h.frec)
- - sizeof(struct tdb_used_record)
- + sizeof(newdb.space));
-
- /* Free is mostly empty... */
- newdb.h.zhdr.zone_bits = INITIAL_ZONE_BITS;
- memset(newdb.h.free, 0, sizeof(newdb.h.free));
-
- /* ... except for this one bucket. */
- bucket = size_to_bucket(INITIAL_ZONE_BITS, newdb.h.frec.data_len);
- newdb.h.free[bucket] = offsetof(struct new_database, h.frec);
- newdb.h.frec.next = newdb.h.frec.prev = 0;
-
- /* Tailer contains maximum number of free_zone bits. */
- newdb.tailer = INITIAL_ZONE_BITS;
-
- /* Magic food */
- memset(newdb.h.hdr.magic_food, 0, sizeof(newdb.h.hdr.magic_food));
- strcpy(newdb.h.hdr.magic_food, TDB_MAGIC_FOOD);
-
- /* This creates an endian-converted database, as if read from disk */
- magic_off = offsetof(struct tdb_header, magic_food);
- tdb_convert(tdb,
- (char *)&newdb.h.hdr + magic_off,
- dbsize - 1 - magic_off);
-
- tdb->header = newdb.h.hdr;
-
- if (tdb->flags & TDB_INTERNAL) {
- tdb->map_size = dbsize;
- 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, dbsize, 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)
-{
- struct tdb_context *tdb;
- struct stat st;
- int save_errno;
- uint64_t hash_test;
- unsigned v;
-
- tdb = malloc(sizeof(*tdb));
- if (!tdb) {
- /* Can't log this */
- errno = ENOMEM;
- goto fail;
- }
- tdb->name = NULL;
- tdb->map_ptr = NULL;
- tdb->fd = -1;
- tdb->map_size = sizeof(struct tdb_header);
- tdb->ecode = TDB_SUCCESS;
- /* header will be read in below. */
- tdb->header_uptodate = false;
- tdb->flags = tdb_flags;
- tdb->log = null_log_fn;
- tdb->log_priv = NULL;
- tdb->khash = jenkins_hash;
- tdb->hash_priv = NULL;
- tdb->transaction = NULL;
- /* last_zone will be set below. */
- tdb_io_init(tdb);
- tdb_lock_init(tdb);
-
- while (attr) {
- switch (attr->base.attr) {
- case TDB_ATTRIBUTE_LOG:
- tdb->log = attr->log.log_fn;
- tdb->log_priv = attr->log.log_private;
- break;
- case TDB_ATTRIBUTE_HASH:
- tdb->khash = attr->hash.hash_fn;
- tdb->hash_priv = attr->hash.hash_private;
- break;
- default:
- tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_open: unknown attribute type %u\n",
- attr->base.attr);
- errno = EINVAL;
- goto fail;
- }
- attr = attr->base.next;
- }
-
- if ((open_flags & O_ACCMODE) == O_WRONLY) {
- tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_open: can't open tdb %s write-only\n", name);
- errno = EINVAL;
- goto fail;
- }
-
- if ((open_flags & O_ACCMODE) == O_RDONLY) {
- tdb->read_only = true;
- /* read only databases don't do locking */
- tdb->flags |= TDB_NOLOCK;
- } else
- tdb->read_only = false;
-
- /* internal databases don't need any of the rest. */
- if (tdb->flags & TDB_INTERNAL) {
- tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
- if (tdb_new_database(tdb) != 0) {
- tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_open: tdb_new_database failed!");
- goto fail;
- }
- TEST_IT(tdb->flags & TDB_CONVERT);
- tdb_convert(tdb, &tdb->header, sizeof(tdb->header));
- tdb_zone_init(tdb);
- return tdb;
- }
-
- if ((tdb->fd = open(name, open_flags, mode)) == -1) {
- tdb->log(tdb, TDB_DEBUG_WARNING, tdb->log_priv,
- "tdb_open: could not open file %s: %s\n",
- name, strerror(errno));
- goto fail; /* errno set by open(2) */
- }
-
- /* on exec, don't inherit the fd */
- v = fcntl(tdb->fd, F_GETFD, 0);
- fcntl(tdb->fd, F_SETFD, v | FD_CLOEXEC);
-
- /* ensure there is only one process initialising at once */
- if (tdb_lock_open(tdb) == -1) {
- tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_open: failed to get open lock on %s: %s\n",
- name, strerror(errno));
- goto fail; /* errno set by tdb_brlock */
- }
-
- if (!tdb_pread_all(tdb->fd, &tdb->header, sizeof(tdb->header), 0)
- || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0) {
- if (!(open_flags & O_CREAT) || tdb_new_database(tdb) == -1) {
- if (errno == 0) {
- errno = EIO; /* ie bad format or something */
- }
- goto fail;
- }
- } else if (tdb->header.version != TDB_VERSION) {
- if (tdb->header.version == bswap_64(TDB_VERSION))
- tdb->flags |= TDB_CONVERT;
- else {
- /* wrong version */
- tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_open: %s is unknown version 0x%llx\n",
- name, (long long)tdb->header.version);
- errno = EIO;
- goto fail;
- }
- }
-
- tdb_convert(tdb, &tdb->header, sizeof(tdb->header));
- hash_test = TDB_HASH_MAGIC;
- hash_test = tdb->khash(&hash_test, sizeof(hash_test),
- tdb->header.hash_seed, tdb->hash_priv);
- if (tdb->header.hash_test != hash_test) {
- /* wrong hash variant */
- tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_open: %s uses a different hash function\n",
- name);
- errno = EIO;
- goto fail;
- }
-
- if (fstat(tdb->fd, &st) == -1)
- goto fail;
-
- /* Is it already in the open list? If so, fail. */
- if (tdb_already_open(st.st_dev, st.st_ino)) {
- /* FIXME */
- tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_open: %s (%d,%d) is already open in this process\n",
- name, (int)st.st_dev, (int)st.st_ino);
- errno = EBUSY;
- goto fail;
- }
-
- tdb->name = strdup(name);
- if (!tdb->name) {
- errno = ENOMEM;
- goto fail;
- }
-
- tdb->device = st.st_dev;
- tdb->inode = st.st_ino;
- tdb_unlock_open(tdb);
-
- /* This make sure we have current map_size and mmap. */
- tdb->methods->oob(tdb, tdb->map_size + 1, true);
-
- /* Now we can pick a random free zone to start from. */
- if (tdb_zone_init(tdb) == -1)
- goto fail;
-
- tdb->next = tdbs;
- tdbs = tdb;
- return tdb;
-
- fail:
- save_errno = errno;
-
- if (!tdb)
- return NULL;