+++ /dev/null
-../../licenses/LGPL-3
\ No newline at end of file
+++ /dev/null
-#include <string.h>
-#include <stdio.h>
-
-/**
- * tdb - The trivial (transactional) database
- *
- * The tdb module provides an efficient keyword data mapping (usually
- * within a file). It supports transactions, so the contents of the
- * database is reliable even across crashes.
- *
- * Example:
- * #include <ccan/tdb/tdb.h>
- * #include <ccan/str/str.h>
- * #include <err.h>
- * #include <stdio.h>
- *
- * static void usage(const char *argv0)
- * {
- * errx(1, "Usage: %s fetch <dbfile> <key>\n"
- * "OR %s store <dbfile> <key> <data>", argv0, argv0);
- * }
- *
- * int main(int argc, char *argv[])
- * {
- * struct tdb_context *tdb;
- * TDB_DATA key, value;
- *
- * if (argc < 4)
- * usage(argv[0]);
- *
- * tdb = tdb_open(argv[2], 1024, TDB_DEFAULT, O_CREAT|O_RDWR,
- * 0600);
- * if (!tdb)
- * err(1, "Opening %s", argv[2]);
- *
- * key.dptr = (void *)argv[3];
- * key.dsize = strlen(argv[3]);
- *
- * if (streq(argv[1], "fetch")) {
- * if (argc != 4)
- * usage(argv[0]);
- * value = tdb_fetch(tdb, key);
- * if (!value.dptr)
- * errx(1, "fetch %s: %s",
- * argv[3], tdb_errorstr(tdb));
- * printf("%.*s\n", value.dsize, (char *)value.dptr);
- * free(value.dptr);
- * } else if (streq(argv[1], "store")) {
- * if (argc != 5)
- * usage(argv[0]);
- * value.dptr = (void *)argv[4];
- * value.dsize = strlen(argv[4]);
- * if (tdb_store(tdb, key, value, 0) != 0)
- * errx(1, "store %s: %s",
- * argv[3], tdb_errorstr(tdb));
- * } else
- * usage(argv[0]);
- *
- * return 0;
- * }
- *
- * Maintainer: Rusty Russell <rusty@rustcorp.com.au>
- *
- * Author: Andrew Tridgell, Jeremy Allison, Rusty Russell
- *
- * License: LGPL (v3 or any later version)
- *
- * Ccanlint:
- * // valgrind breaks fcntl locks.
- * tests_pass_valgrind test/run-open-during-transaction.c:FAIL
- * tests_pass_valgrind_noleaks test/run-die-during-transaction.c:FAIL
- */
-int main(int argc, char *argv[])
-{
- if (argc != 2)
- return 1;
-
- if (strcmp(argv[1], "depends") == 0) {
- printf("ccan/compiler\n");
- return 0;
- }
-
- return 1;
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Rusty Russell 2009
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#include "tdb_private.h"
-#include <limits.h>
-
-/* Since we opened it, these shouldn't fail unless it's recent corruption. */
-static bool tdb_check_header(struct tdb_context *tdb, tdb_off_t *recovery)
-{
- struct tdb_header hdr;
- uint32_t h1, h2;
-
- if (tdb->methods->tdb_read(tdb, 0, &hdr, sizeof(hdr), 0) == -1)
- return false;
- if (strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0)
- goto corrupt;
-
- CONVERT(hdr);
- if (hdr.version != TDB_VERSION)
- goto corrupt;
-
- if (hdr.rwlocks != 0 && hdr.rwlocks != TDB_HASH_RWLOCK_MAGIC)
- goto corrupt;
-
- tdb_header_hash(tdb, &h1, &h2);
- if (hdr.magic1_hash && hdr.magic2_hash &&
- (hdr.magic1_hash != h1 || hdr.magic2_hash != h2))
- goto corrupt;
-
- if (hdr.hash_size == 0)
- goto corrupt;
-
- if (hdr.hash_size != tdb->header.hash_size)
- goto corrupt;
-
- if (hdr.recovery_start != 0 &&
- hdr.recovery_start < TDB_DATA_START(tdb->header.hash_size))
- goto corrupt;
-
- *recovery = hdr.recovery_start;
- return true;
-
-corrupt:
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "Header is corrupt\n"));
- return false;
-}
-
-/* Generic record header check. */
-static bool tdb_check_record(struct tdb_context *tdb,
- tdb_off_t off,
- const struct tdb_record *rec)
-{
- tdb_off_t tailer;
-
- /* Check rec->next: 0 or points to record offset, aligned. */
- if (rec->next > 0 && rec->next < TDB_DATA_START(tdb->header.hash_size)){
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d too small next %d\n",
- off, rec->next));
- goto corrupt;
- }
- if (rec->next + sizeof(*rec) < rec->next) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d too large next %d\n",
- off, rec->next));
- goto corrupt;
- }
- if ((rec->next % TDB_ALIGNMENT) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d misaligned next %d\n",
- off, rec->next));
- goto corrupt;
- }
- if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0))
- goto corrupt;
-
- /* Check rec_len: similar to rec->next, implies next record. */
- if ((rec->rec_len % TDB_ALIGNMENT) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d misaligned length %d\n",
- off, rec->rec_len));
- goto corrupt;
- }
- /* Must fit tailer. */
- if (rec->rec_len < sizeof(tailer)) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d too short length %d\n",
- off, rec->rec_len));
- goto corrupt;
- }
- /* OOB allows "right at the end" access, so this works for last rec. */
- if (tdb->methods->tdb_oob(tdb, off+sizeof(*rec)+rec->rec_len, 0))
- goto corrupt;
-
- /* Check tailer. */
- if (tdb_ofs_read(tdb, off+sizeof(*rec)+rec->rec_len-sizeof(tailer),
- &tailer) == -1)
- goto corrupt;
- if (tailer != sizeof(*rec) + rec->rec_len) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d invalid tailer\n", off));
- goto corrupt;
- }
-
- return true;
-
-corrupt:
- tdb->ecode = TDB_ERR_CORRUPT;
- return false;
-}
-
-/* Grab some bytes: may copy if can't use mmap.
- Caller has already done bounds check. */
-static TDB_DATA get_bytes(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len)
-{
- TDB_DATA d;
-
- d.dsize = len;
-
- if (tdb->transaction == NULL && tdb->map_ptr != NULL)
- d.dptr = (unsigned char *)tdb->map_ptr + off;
- else
- d.dptr = tdb_alloc_read(tdb, off, d.dsize);
- return d;
-}
-
-/* Frees data if we're not able to simply use mmap. */
-static void put_bytes(struct tdb_context *tdb, TDB_DATA d)
-{
- if (tdb->transaction == NULL && tdb->map_ptr != NULL)
- return;
- free(d.dptr);
-}
-
-/* We use the excellent Jenkins lookup3 hash; this is based on hash_word2.
- * See: http://burtleburtle.net/bob/c/lookup3.c
- */
-#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
-static void hash(uint32_t key, uint32_t *pc, uint32_t *pb)
-{
- uint32_t a,b,c;
-
- /* Set up the internal state */
- a = b = c = 0xdeadbeef + *pc;
- c += *pb;
- a += key;
- c ^= b; c -= rot(b,14);
- a ^= c; a -= rot(c,11);
- b ^= a; b -= rot(a,25);
- c ^= b; c -= rot(b,16);
- a ^= c; a -= rot(c,4);
- b ^= a; b -= rot(a,14);
- c ^= b; c -= rot(b,24);
- *pc=c; *pb=b;
-}
-
-/*
- We want to check that all free records are in the free list
- (only once), and all free list entries are free records. Similarly
- for each hash chain of used records.
-
- Doing that naively (without walking hash chains, since we want to be
- linear) means keeping a list of records which have been seen in each
- hash chain, and another of records pointed to (ie. next pointers
- from records and the initial hash chain heads). These two lists
- should be equal. This will take 8 bytes per record, and require
- sorting at the end.
-
- So instead, we record each offset in a bitmap such a way that
- recording it twice will cancel out. Since each offset should appear
- exactly twice, the bitmap should be zero at the end.
-
- The approach was inspired by Bloom Filters (see Wikipedia). For
- each value, we flip K bits in a bitmap of size N. The number of
- distinct arrangements is:
-
- N! / (K! * (N-K)!)
-
- Of course, not all arrangements are actually distinct, but testing
- shows this formula to be close enough.
-
- So, if K == 8 and N == 256, the probability of two things flipping the same
- bits is 1 in 409,663,695,276,000.
-
- Given that ldb uses a hash size of 10000, using 32 bytes per hash chain
- (320k) seems reasonable.
-*/
-#define NUM_HASHES 8
-#define BITMAP_BITS 256
-
-static void bit_flip(unsigned char bits[], unsigned int idx)
-{
- bits[idx / CHAR_BIT] ^= (1 << (idx % CHAR_BIT));
-}
-
-/* We record offsets in a bitmap for the particular chain it should be in. */
-static void record_offset(unsigned char bits[], tdb_off_t off)
-{
- uint32_t h1 = off, h2 = 0;
- unsigned int i;
-
- /* We get two good hash values out of jhash2, so we use both. Then
- * we keep going to produce further hash values. */
- for (i = 0; i < NUM_HASHES / 2; i++) {
- hash(off, &h1, &h2);
- bit_flip(bits, h1 % BITMAP_BITS);
- bit_flip(bits, h2 % BITMAP_BITS);
- h2++;
- }
-}
-
-/* Check that an in-use record is valid. */
-static bool tdb_check_used_record(struct tdb_context *tdb,
- tdb_off_t off,
- const struct tdb_record *rec,
- unsigned char **hashes,
- int (*check)(TDB_DATA, TDB_DATA, void *),
- void *private_data)
-{
- TDB_DATA key, data;
-
- if (!tdb_check_record(tdb, off, rec))
- return false;
-
- /* key + data + tailer must fit in record */
- if (rec->key_len + rec->data_len + sizeof(tdb_off_t) > rec->rec_len) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d too short for contents\n", off));
- return false;
- }
-
- key = get_bytes(tdb, off + sizeof(*rec), rec->key_len);
- if (!key.dptr)
- return false;
-
- if (tdb->hash_fn(&key) != rec->full_hash) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Record offset %d has incorrect hash\n", off));
- goto fail_put_key;
- }
-
- /* Mark this offset as a known value for this hash bucket. */
- record_offset(hashes[BUCKET(rec->full_hash)+1], off);
- /* And similarly if the next pointer is valid. */
- if (rec->next)
- record_offset(hashes[BUCKET(rec->full_hash)+1], rec->next);
-
- /* If they supply a check function and this record isn't dead,
- get data and feed it. */
- if (check && rec->magic != TDB_DEAD_MAGIC) {
- data = get_bytes(tdb, off + sizeof(*rec) + rec->key_len,
- rec->data_len);
- if (!data.dptr)
- goto fail_put_key;
-
- if (check(key, data, private_data) == -1)
- goto fail_put_data;
- put_bytes(tdb, data);
- }
-
- put_bytes(tdb, key);
- return true;
-
-fail_put_data:
- put_bytes(tdb, data);
-fail_put_key:
- put_bytes(tdb, key);
- return false;
-}
-
-/* Check that an unused record is valid. */
-static bool tdb_check_free_record(struct tdb_context *tdb,
- tdb_off_t off,
- const struct tdb_record *rec,
- unsigned char **hashes)
-{
- if (!tdb_check_record(tdb, off, rec))
- return false;
-
- /* Mark this offset as a known value for the free list. */
- record_offset(hashes[0], off);
- /* And similarly if the next pointer is valid. */
- if (rec->next)
- record_offset(hashes[0], rec->next);
- return true;
-}
-
-/* Slow, but should be very rare. */
-size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off)
-{
- size_t len;
-
- for (len = 0; off + len < tdb->map_size; len++) {
- char c;
- if (tdb->methods->tdb_read(tdb, off, &c, 1, 0))
- return 0;
- if (c != 0 && c != 0x42)
- break;
- }
- return len;
-}
-
-int tdb_check(struct tdb_context *tdb,
- int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
- void *private_data)
-{
- unsigned int h;
- unsigned char **hashes;
- tdb_off_t off, recovery_start;
- struct tdb_record rec;
- bool found_recovery = false;
- tdb_len_t dead;
- bool locked;
-
- /* Read-only databases use no locking at all: it's best-effort.
- * We may have a write lock already, so skip that case too. */
- if (tdb->read_only || tdb->allrecord_lock.count != 0) {
- locked = false;
- } else {
- if (tdb_lockall_read(tdb) == -1)
- return -1;
- locked = true;
- }
-
- /* Make sure we know true size of the underlying file. */
- tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
-
- /* Header must be OK: also gets us the recovery ptr, if any. */
- if (!tdb_check_header(tdb, &recovery_start))
- goto unlock;
-
- /* We should have the whole header, too. */
- if (tdb->map_size < TDB_DATA_START(tdb->header.hash_size)) {
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "File too short for hashes\n"));
- goto unlock;
- }
-
- /* One big malloc: pointers then bit arrays. */
- hashes = (unsigned char **)calloc(
- 1, sizeof(hashes[0]) * (1+tdb->header.hash_size)
- + BITMAP_BITS / CHAR_BIT * (1+tdb->header.hash_size));
- if (!hashes) {
- tdb->ecode = TDB_ERR_OOM;
- goto unlock;
- }
-
- /* Initialize pointers */
- hashes[0] = (unsigned char *)(&hashes[1+tdb->header.hash_size]);
- for (h = 1; h < 1+tdb->header.hash_size; h++)
- hashes[h] = hashes[h-1] + BITMAP_BITS / CHAR_BIT;
-
- /* Freelist and hash headers are all in a row: read them. */
- for (h = 0; h < 1+tdb->header.hash_size; h++) {
- if (tdb_ofs_read(tdb, FREELIST_TOP + h*sizeof(tdb_off_t),
- &off) == -1)
- goto free;
- if (off)
- record_offset(hashes[h], off);
- }
-
- /* For each record, read it in and check it's ok. */
- for (off = TDB_DATA_START(tdb->header.hash_size);
- off < tdb->map_size;
- off += sizeof(rec) + rec.rec_len) {
- if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
- DOCONV()) == -1)
- goto free;
- switch (rec.magic) {
- case TDB_MAGIC:
- case TDB_DEAD_MAGIC:
- if (!tdb_check_used_record(tdb, off, &rec, hashes,
- check, private_data))
- goto free;
- break;
- case TDB_FREE_MAGIC:
- if (!tdb_check_free_record(tdb, off, &rec, hashes))
- goto free;
- break;
- /* If we crash after ftruncate, we can get zeroes or fill. */
- case TDB_RECOVERY_INVALID_MAGIC:
- case 0x42424242:
- if (recovery_start == off) {
- found_recovery = true;
- break;
- }
- dead = tdb_dead_space(tdb, off);
- if (dead < sizeof(rec))
- goto corrupt;
-
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Dead space at %d-%d (of %u)\n",
- off, off + dead, tdb->map_size));
- rec.rec_len = dead - sizeof(rec);
- break;
- case TDB_RECOVERY_MAGIC:
- if (recovery_start != off) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Unexpected recovery record at offset %d\n",
- off));
- goto free;
- }
- found_recovery = true;
- break;
- default: ;
- corrupt:
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Bad magic 0x%x at offset %d\n",
- rec.magic, off));
- goto free;
- }
- }
-
- /* Now, hashes should all be empty: each record exists and is referred
- * to by one other. */
- for (h = 0; h < 1+tdb->header.hash_size; h++) {
- unsigned int i;
- for (i = 0; i < BITMAP_BITS / CHAR_BIT; i++) {
- if (hashes[h][i] != 0) {
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Hashes do not match records\n"));
- goto free;
- }
- }
- }
-
- /* We must have found recovery area if there was one. */
- if (recovery_start != 0 && !found_recovery) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Expected a recovery area at %u\n",
- recovery_start));
- goto free;
- }
-
- free(hashes);
- if (locked) {
- tdb_unlockall_read(tdb);
- }
- return 0;
-
-free:
- free(hashes);
-unlock:
- if (locked) {
- tdb_unlockall_read(tdb);
- }
- return -1;
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-static tdb_off_t tdb_dump_record(struct tdb_context *tdb, int hash,
- tdb_off_t offset)
-{
- struct tdb_record rec;
- tdb_off_t tailer_ofs, tailer;
-
- if (tdb->methods->tdb_read(tdb, offset, (char *)&rec,
- sizeof(rec), DOCONV()) == -1) {
- printf("ERROR: failed to read record at %u\n", offset);
- return 0;
- }
-
- printf(" rec: hash=%d offset=0x%08x next=0x%08x rec_len=%d "
- "key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
- hash, offset, rec.next, rec.rec_len, rec.key_len, rec.data_len,
- rec.full_hash, rec.magic);
-
- tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off_t);
-
- if (tdb_ofs_read(tdb, tailer_ofs, &tailer) == -1) {
- printf("ERROR: failed to read tailer at %u\n", tailer_ofs);
- return rec.next;
- }
-
- if (tailer != rec.rec_len + sizeof(rec)) {
- printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n",
- (unsigned int)tailer, (unsigned int)(rec.rec_len + sizeof(rec)));
- }
- return rec.next;
-}
-
-static int tdb_dump_chain(struct tdb_context *tdb, int i)
-{
- tdb_off_t rec_ptr, top;
-
- top = TDB_HASH_TOP(i);
-
- if (tdb_lock(tdb, i, F_WRLCK) != 0)
- return -1;
-
- if (tdb_ofs_read(tdb, top, &rec_ptr) == -1)
- return tdb_unlock(tdb, i, F_WRLCK);
-
- if (rec_ptr)
- printf("hash=%d\n", i);
-
- while (rec_ptr) {
- rec_ptr = tdb_dump_record(tdb, i, rec_ptr);
- }
-
- return tdb_unlock(tdb, i, F_WRLCK);
-}
-
-void tdb_dump_all(struct tdb_context *tdb)
-{
- int i;
- for (i=0;i<tdb->header.hash_size;i++) {
- tdb_dump_chain(tdb, i);
- }
- printf("freelist:\n");
- tdb_dump_chain(tdb, -1);
-}
-
-int tdb_printfreelist(struct tdb_context *tdb)
-{
- int ret;
- long total_free = 0;
- tdb_off_t offset, rec_ptr;
- struct tdb_record rec;
-
- if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0)
- return ret;
-
- offset = FREELIST_TOP;
-
- /* read in the freelist top */
- if (tdb_ofs_read(tdb, offset, &rec_ptr) == -1) {
- tdb_unlock(tdb, -1, F_WRLCK);
- return 0;
- }
-
- printf("freelist top=[0x%08x]\n", rec_ptr );
- while (rec_ptr) {
- if (tdb->methods->tdb_read(tdb, rec_ptr, (char *)&rec,
- sizeof(rec), DOCONV()) == -1) {
- tdb_unlock(tdb, -1, F_WRLCK);
- return -1;
- }
-
- if (rec.magic != TDB_FREE_MAGIC) {
- printf("bad magic 0x%08x in free list\n", rec.magic);
- tdb_unlock(tdb, -1, F_WRLCK);
- return -1;
- }
-
- printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)] (end = 0x%08x)\n",
- rec_ptr, rec.rec_len, rec.rec_len, rec_ptr + rec.rec_len);
- total_free += rec.rec_len;
-
- /* move to the next record */
- rec_ptr = rec.next;
- }
- printf("total rec_len = [0x%08x (%d)]\n", (int)total_free,
- (int)total_free);
-
- return tdb_unlock(tdb, -1, F_WRLCK);
-}
-
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-enum TDB_ERROR tdb_error(struct tdb_context *tdb)
-{
- return tdb->ecode;
-}
-
-static struct tdb_errname {
- enum TDB_ERROR ecode; const char *estring;
-} emap[] = { {TDB_SUCCESS, "Success"},
- {TDB_ERR_CORRUPT, "Corrupt database"},
- {TDB_ERR_IO, "IO Error"},
- {TDB_ERR_LOCK, "Locking error"},
- {TDB_ERR_OOM, "Out of memory"},
- {TDB_ERR_EXISTS, "Record exists"},
- {TDB_ERR_NOLOCK, "Lock exists on other keys"},
- {TDB_ERR_EINVAL, "Invalid parameter"},
- {TDB_ERR_NOEXIST, "Record does not exist"},
- {TDB_ERR_RDONLY, "write not permitted"} };
-
-/* Error string for the last tdb error */
-const char *tdb_errorstr(struct tdb_context *tdb)
-{
- uint32_t i;
- for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
- if (tdb->ecode == emap[i].ecode)
- return emap[i].estring;
- return "Invalid error code";
-}
-
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-/* 'right' merges can involve O(n^2) cost when combined with a
- traverse, so they are disabled until we find a way to do them in
- O(1) time
-*/
-#define USE_RIGHT_MERGES 0
-
-/* read a freelist record and check for simple errors */
-int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct tdb_record *rec)
-{
- if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
- return -1;
-
- if (rec->magic == TDB_MAGIC) {
- /* this happens when a app is showdown while deleting a record - we should
- not completely fail when this happens */
- TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read non-free magic 0x%x at offset=%d - fixing\n",
- rec->magic, off));
- rec->magic = TDB_FREE_MAGIC;
- if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
- return -1;
- }
-
- if (rec->magic != TDB_FREE_MAGIC) {
- /* Ensure ecode is set for log fn. */
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%d\n",
- rec->magic, off));
- return -1;
- }
- if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
- return -1;
- return 0;
-}
-
-
-#if USE_RIGHT_MERGES
-/* Remove an element from the freelist. Must have alloc lock. */
-static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next)
-{
- tdb_off_t last_ptr, i;
-
- /* read in the freelist top */
- last_ptr = FREELIST_TOP;
- while (tdb_ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
- if (i == off) {
- /* We've found it! */
- return tdb_ofs_write(tdb, last_ptr, &next);
- }
- /* Follow chain (next offset is at start of record) */
- last_ptr = i;
- }
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off));
- return -1;
-}
-#endif
-
-
-/* update a record tailer (must hold allocation lock) */
-static int update_tailer(struct tdb_context *tdb, tdb_off_t offset,
- const struct tdb_record *rec)
-{
- tdb_off_t totalsize;
-
- /* Offset of tailer from record header */
- totalsize = sizeof(*rec) + rec->rec_len;
- return tdb_ofs_write(tdb, offset + totalsize - sizeof(tdb_off_t),
- &totalsize);
-}
-
-/* Add an element into the freelist. Merge adjacent records if
- necessary. */
-int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
-{
- /* Allocation and tailer lock */
- if (tdb_lock(tdb, -1, F_WRLCK) != 0)
- return -1;
-
- /* set an initial tailer, so if we fail we don't leave a bogus record */
- if (update_tailer(tdb, offset, rec) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed!\n"));
- goto fail;
- }
-
-#if USE_RIGHT_MERGES
- /* Look right first (I'm an Australian, dammit) */
- if (offset + sizeof(*rec) + rec->rec_len + sizeof(*rec) <= tdb->map_size) {
- tdb_off_t right = offset + sizeof(*rec) + rec->rec_len;
- struct tdb_record r;
-
- if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right read failed at %u\n", right));
- goto left;
- }
-
- /* If it's free, expand to include it. */
- if (r.magic == TDB_FREE_MAGIC) {
- if (remove_from_freelist(tdb, right, r.next) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right free failed at %u\n", right));
- goto left;
- }
- rec->rec_len += sizeof(r) + r.rec_len;
- if (update_tailer(tdb, offset, rec) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset));
- goto fail;
- }
- }
- }
-left:
-#endif
-
- /* Look left */
- if (offset - sizeof(tdb_off_t) > TDB_DATA_START(tdb->header.hash_size)) {
- tdb_off_t left = offset - sizeof(tdb_off_t);
- struct tdb_record l;
- tdb_off_t leftsize;
-
- /* Read in tailer and jump back to header */
- if (tdb_ofs_read(tdb, left, &leftsize) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left offset read failed at %u\n", left));
- goto update;
- }
-
- /* it could be uninitialised data */
- if (leftsize == 0 || leftsize == TDB_PAD_U32) {
- goto update;
- }
-
- left = offset - leftsize;
-
- if (leftsize > offset ||
- left < TDB_DATA_START(tdb->header.hash_size)) {
- goto update;
- }
-
- /* Now read in the left record */
- if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
- goto update;
- }
-
- /* If it's free, expand to include it. */
- if (l.magic == TDB_FREE_MAGIC) {
- /* we now merge the new record into the left record, rather than the other
- way around. This makes the operation O(1) instead of O(n). This change
- prevents traverse from being O(n^2) after a lot of deletes */
- l.rec_len += sizeof(*rec) + rec->rec_len;
- if (tdb_rec_write(tdb, left, &l) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_left failed at %u\n", left));
- goto fail;
- }
- if (update_tailer(tdb, left, &l) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset));
- goto fail;
- }
- tdb_unlock(tdb, -1, F_WRLCK);
- return 0;
- }
- }
-
-update:
-
- /* Now, prepend to free list */
- rec->magic = TDB_FREE_MAGIC;
-
- if (tdb_ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
- tdb_rec_write(tdb, offset, rec) == -1 ||
- tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%d\n", offset));
- goto fail;
- }
-
- /* And we're done. */
- tdb_unlock(tdb, -1, F_WRLCK);
- return 0;
-
- fail:
- tdb_unlock(tdb, -1, F_WRLCK);
- return -1;
-}
-
-
-
-/*
- the core of tdb_allocate - called when we have decided which
- free list entry to use
-
- Note that we try to allocate by grabbing data from the end of an existing record,
- not the beginning. This is so the left merge in a free is more likely to be
- able to free up the record without fragmentation
- */
-static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb,
- tdb_len_t length, tdb_off_t rec_ptr,
- struct tdb_record *rec, tdb_off_t last_ptr)
-{
-#define MIN_REC_SIZE (sizeof(struct tdb_record) + sizeof(tdb_off_t) + 8)
-
- if (rec->rec_len < length + MIN_REC_SIZE) {
- /* we have to grab the whole record */
-
- /* unlink it from the previous record */
- if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) {
- return 0;
- }
-
- /* mark it not free */
- rec->magic = TDB_MAGIC;
- if (tdb_rec_write(tdb, rec_ptr, rec) == -1) {
- return 0;
- }
- return rec_ptr;
- }
-
- /* we're going to just shorten the existing record */
- rec->rec_len -= (length + sizeof(*rec));
- if (tdb_rec_write(tdb, rec_ptr, rec) == -1) {
- return 0;
- }
- if (update_tailer(tdb, rec_ptr, rec) == -1) {
- return 0;
- }
-
- /* and setup the new record */
- rec_ptr += sizeof(*rec) + rec->rec_len;
-
- memset(rec, '\0', sizeof(*rec));
- rec->rec_len = length;
- rec->magic = TDB_MAGIC;
-
- if (tdb_rec_write(tdb, rec_ptr, rec) == -1) {
- return 0;
- }
-
- if (update_tailer(tdb, rec_ptr, rec) == -1) {
- return 0;
- }
-
- return rec_ptr;
-}
-
-/* allocate some space from the free list. The offset returned points
- to a unconnected tdb_record within the database with room for at
- least length bytes of total data
-
- 0 is returned if the space could not be allocated
- */
-tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_record *rec)
-{
- tdb_off_t rec_ptr, last_ptr, newrec_ptr;
- struct {
- tdb_off_t rec_ptr, last_ptr;
- tdb_len_t rec_len;
- } bestfit;
- float multiplier = 1.0;
-
- if (tdb_lock(tdb, -1, F_WRLCK) == -1)
- return 0;
-
- /* over-allocate to reduce fragmentation */
- length *= 1.25;
-
- /* Extra bytes required for tailer */
- length += sizeof(tdb_off_t);
- length = TDB_ALIGN(length, TDB_ALIGNMENT);
-
- again:
- last_ptr = FREELIST_TOP;
-
- /* read in the freelist top */
- if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
- goto fail;
-
- bestfit.rec_ptr = 0;
- bestfit.last_ptr = 0;
- bestfit.rec_len = 0;
-
- /*
- this is a best fit allocation strategy. Originally we used
- a first fit strategy, but it suffered from massive fragmentation
- issues when faced with a slowly increasing record size.
- */
- while (rec_ptr) {
- if (tdb_rec_free_read(tdb, rec_ptr, rec) == -1) {
- goto fail;
- }
-
- if (rec->rec_len >= length) {
- if (bestfit.rec_ptr == 0 ||
- rec->rec_len < bestfit.rec_len) {
- bestfit.rec_len = rec->rec_len;
- bestfit.rec_ptr = rec_ptr;
- bestfit.last_ptr = last_ptr;
- }
- }
-
- /* move to the next record */
- last_ptr = rec_ptr;
- rec_ptr = rec->next;
-
- /* if we've found a record that is big enough, then
- stop searching if its also not too big. The
- definition of 'too big' changes as we scan
- through */
- if (bestfit.rec_len > 0 &&
- bestfit.rec_len < length * multiplier) {
- break;
- }
-
- /* this multiplier means we only extremely rarely
- search more than 50 or so records. At 50 records we
- accept records up to 11 times larger than what we
- want */
- multiplier *= 1.05;
- }
-
- if (bestfit.rec_ptr != 0) {
- if (tdb_rec_free_read(tdb, bestfit.rec_ptr, rec) == -1) {
- goto fail;
- }
-
- newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr,
- rec, bestfit.last_ptr);
- tdb_unlock(tdb, -1, F_WRLCK);
- return newrec_ptr;
- }
-
- /* we didn't find enough space. See if we can expand the
- database and if we can then try again */
- if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
- goto again;
- fail:
- tdb_unlock(tdb, -1, F_WRLCK);
- return 0;
-}
-
-
-
-/*
- return the size of the freelist - used to decide if we should repack
-*/
-int tdb_freelist_size(struct tdb_context *tdb)
-{
- tdb_off_t ptr;
- int count=0;
-
- if (tdb_lock(tdb, -1, F_RDLCK) == -1) {
- return -1;
- }
-
- ptr = FREELIST_TOP;
- while (tdb_ofs_read(tdb, ptr, &ptr) == 0 && ptr != 0) {
- count++;
- }
-
- tdb_unlock(tdb, -1, F_RDLCK);
- return count;
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Jeremy Allison 2006
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-/* Check the freelist is good and contains no loops.
- Very memory intensive - only do this as a consistency
- checker. Heh heh - uses an in memory tdb as the storage
- for the "seen" record list. For some reason this strikes
- me as extremely clever as I don't have to write another tree
- data structure implementation :-).
- */
-
-static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr)
-{
- TDB_DATA key, data;
-
- memset(&data, '\0', sizeof(data));
- key.dptr = (unsigned char *)&rec_ptr;
- key.dsize = sizeof(rec_ptr);
- return tdb_store(mem_tdb, key, data, TDB_INSERT);
-}
-
-int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries)
-{
- struct tdb_context *mem_tdb = NULL;
- struct tdb_record rec;
- tdb_off_t rec_ptr;
- int ret = -1;
-
- *pnum_entries = 0;
-
- mem_tdb = tdb_open("flval", tdb->header.hash_size,
- TDB_INTERNAL, O_RDWR, 0600);
- if (!mem_tdb) {
- return -1;
- }
-
- if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
- tdb_close(mem_tdb);
- return 0;
- }
-
- /* Store the FREELIST_TOP record. */
- if (seen_insert(mem_tdb, FREELIST_TOP) == -1) {
- tdb->ecode = TDB_ERR_CORRUPT;
- ret = -1;
- goto fail;
- }
-
- /* read in the freelist top */
- if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) {
- goto fail;
- }
-
- while (rec_ptr) {
-
- /* If we can't store this record (we've seen it
- before) then the free list has a loop and must
- be corrupt. */
-
- if (seen_insert(mem_tdb, rec_ptr)) {
- tdb->ecode = TDB_ERR_CORRUPT;
- ret = -1;
- goto fail;
- }
-
- if (tdb_rec_free_read(tdb, rec_ptr, &rec) == -1) {
- goto fail;
- }
-
- /* move to the next record */
- rec_ptr = rec.next;
- *pnum_entries += 1;
- }
-
- ret = 0;
-
- fail:
-
- tdb_close(mem_tdb);
- tdb_unlock(tdb, -1, F_WRLCK);
- return ret;
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Rusty Russell 2010
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#include "tdb_private.h"
-
-/* This is based on the hash algorithm from gdbm */
-unsigned int tdb_old_hash(TDB_DATA *key)
-{
- uint32_t value; /* Used to compute the hash value. */
- uint32_t i; /* Used to cycle through random values. */
-
- /* Set the initial value from the key size. */
- for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
- value = (value + (key->dptr[i] << (i*5 % 24)));
-
- return (1103515243 * value + 12345);
-}
-
-#if HAVE_LITTLE_ENDIAN
-# define HASH_LITTLE_ENDIAN 1
-# define HASH_BIG_ENDIAN 0
-#elif HAVE_BIG_ENDIAN
-# define HASH_LITTLE_ENDIAN 0
-# define HASH_BIG_ENDIAN 1
-#else
-# error Unknown endian
-#endif
-
-/*
--------------------------------------------------------------------------------
-lookup3.c, by Bob Jenkins, May 2006, Public Domain.
-
-These are functions for producing 32-bit hashes for hash table lookup.
-hash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
-are externally useful functions. Routines to test the hash are included
-if SELF_TEST is defined. You can use this free for any purpose. It's in
-the public domain. It has no warranty.
-
-You probably want to use hashlittle(). hashlittle() and hashbig()
-hash byte arrays. hashlittle() is is faster than hashbig() on
-little-endian machines. Intel and AMD are little-endian machines.
-On second thought, you probably want hashlittle2(), which is identical to
-hashlittle() except it returns two 32-bit hashes for the price of one.
-You could implement hashbig2() if you wanted but I haven't bothered here.
-
-If you want to find a hash of, say, exactly 7 integers, do
- a = i1; b = i2; c = i3;
- mix(a,b,c);
- a += i4; b += i5; c += i6;
- mix(a,b,c);
- a += i7;
- final(a,b,c);
-then use c as the hash value. If you have a variable length array of
-4-byte integers to hash, use hash_word(). If you have a byte array (like
-a character string), use hashlittle(). If you have several byte arrays, or
-a mix of things, see the comments above hashlittle().
-
-Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
-then mix those integers. This is fast (you can do a lot more thorough
-mixing with 12*3 instructions on 3 integers than you can with 3 instructions
-on 1 byte), but shoehorning those bytes into integers efficiently is messy.
-*/
-
-#define hashsize(n) ((uint32_t)1<<(n))
-#define hashmask(n) (hashsize(n)-1)
-#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
-
-/*
--------------------------------------------------------------------------------
-mix -- mix 3 32-bit values reversibly.
-
-This is reversible, so any information in (a,b,c) before mix() is
-still in (a,b,c) after mix().
-
-If four pairs of (a,b,c) inputs are run through mix(), or through
-mix() in reverse, there are at least 32 bits of the output that
-are sometimes the same for one pair and different for another pair.
-This was tested for:
-* pairs that differed by one bit, by two bits, in any combination
- of top bits of (a,b,c), or in any combination of bottom bits of
- (a,b,c).
-* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
- the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
- is commonly produced by subtraction) look like a single 1-bit
- difference.
-* the base values were pseudorandom, all zero but one bit set, or
- all zero plus a counter that starts at zero.
-
-Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
-satisfy this are
- 4 6 8 16 19 4
- 9 15 3 18 27 15
- 14 9 3 7 17 3
-Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
-for "differ" defined as + with a one-bit base and a two-bit delta. I
-used http://burtleburtle.net/bob/hash/avalanche.html to choose
-the operations, constants, and arrangements of the variables.
-
-This does not achieve avalanche. There are input bits of (a,b,c)
-that fail to affect some output bits of (a,b,c), especially of a. The
-most thoroughly mixed value is c, but it doesn't really even achieve
-avalanche in c.
-
-This allows some parallelism. Read-after-writes are good at doubling
-the number of bits affected, so the goal of mixing pulls in the opposite
-direction as the goal of parallelism. I did what I could. Rotates
-seem to cost as much as shifts on every machine I could lay my hands
-on, and rotates are much kinder to the top and bottom bits, so I used
-rotates.
--------------------------------------------------------------------------------
-*/
-#define mix(a,b,c) \
-{ \
- a -= c; a ^= rot(c, 4); c += b; \
- b -= a; b ^= rot(a, 6); a += c; \
- c -= b; c ^= rot(b, 8); b += a; \
- a -= c; a ^= rot(c,16); c += b; \
- b -= a; b ^= rot(a,19); a += c; \
- c -= b; c ^= rot(b, 4); b += a; \
-}
-
-/*
--------------------------------------------------------------------------------
-final -- final mixing of 3 32-bit values (a,b,c) into c
-
-Pairs of (a,b,c) values differing in only a few bits will usually
-produce values of c that look totally different. This was tested for
-* pairs that differed by one bit, by two bits, in any combination
- of top bits of (a,b,c), or in any combination of bottom bits of
- (a,b,c).
-* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
- the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
- is commonly produced by subtraction) look like a single 1-bit
- difference.
-* the base values were pseudorandom, all zero but one bit set, or
- all zero plus a counter that starts at zero.
-
-These constants passed:
- 14 11 25 16 4 14 24
- 12 14 25 16 4 14 24
-and these came close:
- 4 8 15 26 3 22 24
- 10 8 15 26 3 22 24
- 11 8 15 26 3 22 24
--------------------------------------------------------------------------------
-*/
-#define final(a,b,c) \
-{ \
- c ^= b; c -= rot(b,14); \
- a ^= c; a -= rot(c,11); \
- b ^= a; b -= rot(a,25); \
- c ^= b; c -= rot(b,16); \
- a ^= c; a -= rot(c,4); \
- b ^= a; b -= rot(a,14); \
- c ^= b; c -= rot(b,24); \
-}
-
-
-/*
--------------------------------------------------------------------------------
-hashlittle() -- hash a variable-length key into a 32-bit value
- k : the key (the unaligned variable-length array of bytes)
- length : the length of the key, counting by bytes
- val2 : IN: can be any 4-byte value OUT: second 32 bit hash.
-Returns a 32-bit value. Every bit of the key affects every bit of
-the return value. Two keys differing by one or two bits will have
-totally different hash values. Note that the return value is better
-mixed than val2, so use that first.
-
-The best hash table sizes are powers of 2. There is no need to do
-mod a prime (mod is sooo slow!). If you need less than 32 bits,
-use a bitmask. For example, if you need only 10 bits, do
- h = (h & hashmask(10));
-In which case, the hash table should have hashsize(10) elements.
-
-If you are hashing n strings (uint8_t **)k, do it like this:
- for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
-
-By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
-code any way you wish, private, educational, or commercial. It's free.
-
-Use for hash table lookup, or anything where one collision in 2^^32 is
-acceptable. Do NOT use for cryptographic purposes.
--------------------------------------------------------------------------------
-*/
-
-static uint32_t hashlittle( const void *key, size_t length )
-{
- uint32_t a,b,c; /* internal state */
- union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
-
- /* Set up the internal state */
- a = b = c = 0xdeadbeef + ((uint32_t)length);
-
- u.ptr = key;
- if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
- const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
-#ifdef VALGRIND
- const uint8_t *k8;
-#endif
-
- /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
- while (length > 12)
- {
- a += k[0];
- b += k[1];
- c += k[2];
- mix(a,b,c);
- length -= 12;
- k += 3;
- }
-
- /*----------------------------- handle the last (probably partial) block */
- /*
- * "k[2]&0xffffff" actually reads beyond the end of the string, but
- * then masks off the part it's not allowed to read. Because the
- * string is aligned, the masked-off tail is in the same word as the
- * rest of the string. Every machine with memory protection I've seen
- * does it on word boundaries, so is OK with this. But VALGRIND will
- * still catch it and complain. The masking trick does make the hash
- * noticably faster for short strings (like English words).
- */
-#ifndef VALGRIND
-
- switch(length)
- {
- case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
- case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
- case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
- case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
- case 8 : b+=k[1]; a+=k[0]; break;
- case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
- case 6 : b+=k[1]&0xffff; a+=k[0]; break;
- case 5 : b+=k[1]&0xff; a+=k[0]; break;
- case 4 : a+=k[0]; break;
- case 3 : a+=k[0]&0xffffff; break;
- case 2 : a+=k[0]&0xffff; break;
- case 1 : a+=k[0]&0xff; break;
- case 0 : return c; /* zero length strings require no mixing */
- }
-
-#else /* make valgrind happy */
-
- k8 = (const uint8_t *)k;
- switch(length)
- {
- case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
- case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
- case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
- case 9 : c+=k8[8]; /* fall through */
- case 8 : b+=k[1]; a+=k[0]; break;
- case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
- case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
- case 5 : b+=k8[4]; /* fall through */
- case 4 : a+=k[0]; break;
- case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
- case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
- case 1 : a+=k8[0]; break;
- case 0 : return c;
- }
-
-#endif /* !valgrind */
-
- } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
- const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
- const uint8_t *k8;
-
- /*--------------- all but last block: aligned reads and different mixing */
- while (length > 12)
- {
- a += k[0] + (((uint32_t)k[1])<<16);
- b += k[2] + (((uint32_t)k[3])<<16);
- c += k[4] + (((uint32_t)k[5])<<16);
- mix(a,b,c);
- length -= 12;
- k += 6;
- }
-
- /*----------------------------- handle the last (probably partial) block */
- k8 = (const uint8_t *)k;
- switch(length)
- {
- case 12: c+=k[4]+(((uint32_t)k[5])<<16);
- b+=k[2]+(((uint32_t)k[3])<<16);
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
- case 10: c+=k[4];
- b+=k[2]+(((uint32_t)k[3])<<16);
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 9 : c+=k8[8]; /* fall through */
- case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
- case 6 : b+=k[2];
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 5 : b+=k8[4]; /* fall through */
- case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
- case 2 : a+=k[0];
- break;
- case 1 : a+=k8[0];
- break;
- case 0 : return c; /* zero length requires no mixing */
- }
-
- } else { /* need to read the key one byte at a time */
- const uint8_t *k = (const uint8_t *)key;
-
- /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
- while (length > 12)
- {
- a += k[0];
- a += ((uint32_t)k[1])<<8;
- a += ((uint32_t)k[2])<<16;
- a += ((uint32_t)k[3])<<24;
- b += k[4];
- b += ((uint32_t)k[5])<<8;
- b += ((uint32_t)k[6])<<16;
- b += ((uint32_t)k[7])<<24;
- c += k[8];
- c += ((uint32_t)k[9])<<8;
- c += ((uint32_t)k[10])<<16;
- c += ((uint32_t)k[11])<<24;
- mix(a,b,c);
- length -= 12;
- k += 12;
- }
-
- /*-------------------------------- last block: affect all 32 bits of (c) */
- switch(length) /* all the case statements fall through */
- {
- case 12: c+=((uint32_t)k[11])<<24;
- case 11: c+=((uint32_t)k[10])<<16;
- case 10: c+=((uint32_t)k[9])<<8;
- case 9 : c+=k[8];
- case 8 : b+=((uint32_t)k[7])<<24;
- case 7 : b+=((uint32_t)k[6])<<16;
- case 6 : b+=((uint32_t)k[5])<<8;
- case 5 : b+=k[4];
- case 4 : a+=((uint32_t)k[3])<<24;
- case 3 : a+=((uint32_t)k[2])<<16;
- case 2 : a+=((uint32_t)k[1])<<8;
- case 1 : a+=k[0];
- break;
- case 0 : return c;
- }
- }
-
- final(a,b,c);
- return c;
-}
-
-unsigned int tdb_jenkins_hash(TDB_DATA *key)
-{
- return hashlittle(key->dptr, key->dsize);
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-#ifndef MAX
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#endif
-
-/* check for an out of bounds access - if it is out of bounds then
- see if the database has been expanded by someone else and expand
- if necessary
- note that "len" is the minimum length needed for the db
-*/
-static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
-{
- struct stat st;
- if (len <= tdb->map_size)
- return 0;
- if (tdb->flags & TDB_INTERNAL) {
- if (!probe) {
- /* Ensure ecode is set for log fn. */
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n",
- (int)len, (int)tdb->map_size));
- }
- return -1;
- }
-
- if (fstat(tdb->fd, &st) == -1) {
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- if (st.st_size < (size_t)len) {
- if (!probe) {
- /* Ensure ecode is set for log fn. */
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n",
- (int)len, (int)st.st_size));
- }
- return -1;
- }
-
- /* Unmap, update size, remap */
- if (tdb_munmap(tdb) == -1) {
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
- tdb->map_size = st.st_size;
- tdb_mmap(tdb);
- return 0;
-}
-
-/* write a lump of data at a specified offset */
-static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
- const void *buf, tdb_len_t len)
-{
- if (len == 0) {
- return 0;
- }
-
- if (tdb->read_only || tdb->traverse_read) {
- tdb->ecode = TDB_ERR_RDONLY;
- return -1;
- }
-
- if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
- return -1;
-
- if (tdb->map_ptr) {
- memcpy(off + (char *)tdb->map_ptr, buf, len);
- } else {
- ssize_t written = pwrite(tdb->fd, buf, len, off);
- if ((written != (ssize_t)len) && (written != -1)) {
- /* try once more */
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
- "%d of %d bytes at %d, trying once more\n",
- (int)written, len, off));
- written = pwrite(tdb->fd, (const char *)buf+written,
- len-written,
- off+written);
- }
- if (written == -1) {
- /* Ensure ecode is set for log fn. */
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
- "len=%d (%s)\n", off, len, strerror(errno)));
- return -1;
- } else if (written != (ssize_t)len) {
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
- "write %d bytes at %d in two attempts\n",
- len, off));
- return -1;
- }
- }
- return 0;
-}
-
-/* Endian conversion: we only ever deal with 4 byte quantities */
-void *tdb_convert(void *buf, uint32_t size)
-{
- uint32_t i;
- unsigned char *p = buf, tmp;
-
- for (i = 0; i < size; i += 4) {
- tmp = p[i];
- p[i] = p[i+3];
- p[i+3] = tmp;
- tmp = p[i+1];
- p[i+1] = p[i+2];
- p[i+2] = tmp;
- }
- return buf;
-}
-
-
-/* read a lump of data at a specified offset, maybe convert */
-static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
- tdb_len_t len, int cv)
-{
- if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) {
- return -1;
- }
-
- if (tdb->map_ptr) {
- memcpy(buf, off + (char *)tdb->map_ptr, len);
- } else {
- ssize_t ret = pread(tdb->fd, buf, len, off);
- if (ret != (ssize_t)len) {
- /* Ensure ecode is set for log fn. */
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d "
- "len=%d ret=%d (%s) map_size=%d\n",
- (int)off, (int)len, (int)ret, strerror(errno),
- (int)tdb->map_size));
- return -1;
- }
- }
- if (cv) {
- tdb_convert(buf, len);
- }
- return 0;
-}
-
-
-
-/*
- do an unlocked scan of the hash table heads to find the next non-zero head. The value
- will then be confirmed with the lock held
-*/
-static void tdb_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
-{
- uint32_t h = *chain;
- if (tdb->map_ptr) {
- for (;h < tdb->header.hash_size;h++) {
- if (0 != *(uint32_t *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
- break;
- }
- }
- } else {
- uint32_t off=0;
- for (;h < tdb->header.hash_size;h++) {
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) {
- break;
- }
- }
- }
- (*chain) = h;
-}
-
-
-int tdb_munmap(struct tdb_context *tdb)
-{
- if (tdb->flags & TDB_INTERNAL)
- return 0;
-
-#if HAVE_MMAP
- if (tdb->map_ptr) {
- int ret;
-
- ret = munmap(tdb->map_ptr, tdb->map_size);
- if (ret != 0)
- return ret;
- }
-#endif
- tdb->map_ptr = NULL;
- return 0;
-}
-
-void tdb_mmap(struct tdb_context *tdb)
-{
- if (tdb->flags & TDB_INTERNAL)
- return;
-
-#if HAVE_MMAP
- if (!(tdb->flags & TDB_NOMMAP)) {
- tdb->map_ptr = mmap(NULL, tdb->map_size,
- PROT_READ|(tdb->read_only? 0:PROT_WRITE),
- MAP_SHARED, tdb->fd, 0);
-
- /*
- * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
- */
-
- if (tdb->map_ptr == MAP_FAILED) {
- tdb->map_ptr = NULL;
- TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n",
- tdb->map_size, strerror(errno)));
- }
- } else {
- tdb->map_ptr = NULL;
- }
-#else
- tdb->map_ptr = NULL;
-#endif
-}
-
-/* expand a file. we prefer to use ftruncate, as that is what posix
- says to use for mmap expansion */
-static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
-{
- char buf[8192];
-
- if (tdb->read_only || tdb->traverse_read) {
- tdb->ecode = TDB_ERR_RDONLY;
- return -1;
- }
-
- if (ftruncate(tdb->fd, size+addition) == -1) {
- char b = 0;
- ssize_t written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
- if (written == 0) {
- /* try once more, potentially revealing errno */
- written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
- }
- if (written == 0) {
- /* again - give up, guessing errno */
- errno = ENOSPC;
- }
- if (written != 1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n",
- size+addition, strerror(errno)));
- return -1;
- }
- }
-
- /* now fill the file with something. This ensures that the
- file isn't sparse, which would be very bad if we ran out of
- disk. This must be done with write, not via mmap */
- memset(buf, TDB_PAD_BYTE, sizeof(buf));
- while (addition) {
- size_t n = addition>sizeof(buf)?sizeof(buf):addition;
- ssize_t written = pwrite(tdb->fd, buf, n, size);
- if (written == 0) {
- /* prevent infinite loops: try _once_ more */
- written = pwrite(tdb->fd, buf, n, size);
- }
- if (written == 0) {
- /* give up, trying to provide a useful errno */
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
- "returned 0 twice: giving up!\n"));
- errno = ENOSPC;
- return -1;
- } else if (written == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
- "%d bytes failed (%s)\n", (int)n,
- strerror(errno)));
- return -1;
- } else if (written != n) {
- TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: wrote "
- "only %d of %d bytes - retrying\n", (int)written,
- (int)n));
- }
- addition -= written;
- size += written;
- }
- return 0;
-}
-
-/* You need 'size', this tells you how much you should expand by. */
-tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size, int page_size)
-{
- tdb_off_t new_size, top_size;
-
- /* limit size in order to avoid using up huge amounts of memory for
- * in memory tdbs if an oddball huge record creeps in */
- if (size > 100 * 1024) {
- top_size = map_size + size * 2;
- } else {
- top_size = map_size + size * 100;
- }
-
- /* always make room for at least top_size more records, and at
- least 25% more space. if the DB is smaller than 100MiB,
- otherwise grow it by 10% only. */
- if (map_size > 100 * 1024 * 1024) {
- new_size = map_size * 1.10;
- } else {
- new_size = map_size * 1.25;
- }
-
- /* Round the database up to a multiple of the page size */
- new_size = MAX(top_size, new_size);
- return TDB_ALIGN(new_size, page_size) - map_size;
-}
-
-/* expand the database at least size bytes by expanding the underlying
- file and doing the mmap again if necessary */
-int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
-{
- struct tdb_record rec;
- tdb_off_t offset;
-
- if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
- return -1;
- }
-
- /* must know about any previous expansions by another process */
- tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
-
- size = tdb_expand_adjust(tdb->map_size, size, tdb->page_size);
-
- if (!(tdb->flags & TDB_INTERNAL))
- tdb_munmap(tdb);
-
- /*
- * We must ensure the file is unmapped before doing this
- * to ensure consistency with systems like OpenBSD where
- * writes and mmaps are not consistent.
- */
-
- /* expand the file itself */
- if (!(tdb->flags & TDB_INTERNAL)) {
- if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0)
- goto fail;
- }
-
- tdb->map_size += size;
-
- if (tdb->flags & TDB_INTERNAL) {
- char *new_map_ptr = (char *)realloc(tdb->map_ptr,
- tdb->map_size);
- if (!new_map_ptr) {
- tdb->map_size -= size;
- goto fail;
- }
- tdb->map_ptr = new_map_ptr;
- } else {
- /*
- * We must ensure the file is remapped before adding the space
- * to ensure consistency with systems like OpenBSD where
- * writes and mmaps are not consistent.
- */
-
- /* We're ok if the mmap fails as we'll fallback to read/write */
- tdb_mmap(tdb);
- }
-
- /* form a new freelist record */
- memset(&rec,'\0',sizeof(rec));
- rec.rec_len = size - sizeof(rec);
-
- /* link it into the free list */
- offset = tdb->map_size - size;
- if (tdb_free(tdb, offset, &rec) == -1)
- goto fail;
-
- tdb_unlock(tdb, -1, F_WRLCK);
- return 0;
- fail:
- tdb_unlock(tdb, -1, F_WRLCK);
- return -1;
-}
-
-/* read/write a tdb_off_t */
-int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
-{
- return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
-}
-
-int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
-{
- tdb_off_t off = *d;
- return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
-}
-
-
-/* read a lump of data, allocating the space for it */
-unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
-{
- unsigned char *buf;
-
- /* some systems don't like zero length malloc */
-
- if (!(buf = (unsigned char *)malloc(len ? len : 1))) {
- /* Ensure ecode is set for log fn. */
- tdb->ecode = TDB_ERR_OOM;
- TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n",
- len, strerror(errno)));
- return NULL;
- }
- if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) {
- SAFE_FREE(buf);
- return NULL;
- }
- return buf;
-}
-
-/* Give a piece of tdb data to a parser */
-
-int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
- tdb_off_t offset, tdb_len_t len,
- int (*parser)(TDB_DATA key, TDB_DATA data,
- void *private_data),
- void *private_data)
-{
- TDB_DATA data;
- int result;
-
- data.dsize = len;
-
- if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) {
- /*
- * Optimize by avoiding the malloc/memcpy/free, point the
- * parser directly at the mmap area.
- */
- if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) {
- return -1;
- }
- data.dptr = offset + (unsigned char *)tdb->map_ptr;
- return parser(key, data, private_data);
- }
-
- if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) {
- return -1;
- }
-
- result = parser(key, data, private_data);
- free(data.dptr);
- return result;
-}
-
-/* read/write a record */
-int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
-{
- if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
- return -1;
- if (TDB_BAD_MAGIC(rec)) {
- /* Ensure ecode is set for log fn. */
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
- return -1;
- }
- return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
-}
-
-int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
-{
- struct tdb_record r = *rec;
- return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r));
-}
-
-static const struct tdb_methods io_methods = {
- tdb_read,
- tdb_write,
- tdb_next_hash_chain,
- tdb_oob,
- tdb_expand_file,
-};
-
-/*
- initialise the default methods table
-*/
-void tdb_io_init(struct tdb_context *tdb)
-{
- tdb->methods = &io_methods;
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
-{
- tdb->interrupt_sig_ptr = ptr;
-}
-
-static int fcntl_lock(struct tdb_context *tdb,
- int rw, off_t off, off_t len, bool waitflag)
-{
- struct flock fl;
-
- fl.l_type = rw;
- fl.l_whence = SEEK_SET;
- fl.l_start = off;
- fl.l_len = len;
- fl.l_pid = 0;
-
- if (waitflag)
- return fcntl(tdb->fd, F_SETLKW, &fl);
- else
- return fcntl(tdb->fd, F_SETLK, &fl);
-}
-
-static int fcntl_unlock(struct tdb_context *tdb, int rw, off_t off, off_t len)
-{
- struct flock fl;
-#if 0 /* Check they matched up locks and unlocks correctly. */
- char line[80];
- FILE *locks;
- bool found = false;
-
- locks = fopen("/proc/locks", "r");
-
- while (fgets(line, 80, locks)) {
- char *p;
- int type, start, l;
-
- /* eg. 1: FLOCK ADVISORY WRITE 2440 08:01:2180826 0 EOF */
- p = strchr(line, ':') + 1;
- if (strncmp(p, " POSIX ADVISORY ", strlen(" POSIX ADVISORY ")))
- continue;
- p += strlen(" FLOCK ADVISORY ");
- if (strncmp(p, "READ ", strlen("READ ")) == 0)
- type = F_RDLCK;
- else if (strncmp(p, "WRITE ", strlen("WRITE ")) == 0)
- type = F_WRLCK;
- else
- abort();
- p += 6;
- if (atoi(p) != getpid())
- continue;
- p = strchr(strchr(p, ' ') + 1, ' ') + 1;
- start = atoi(p);
- p = strchr(p, ' ') + 1;
- if (strncmp(p, "EOF", 3) == 0)
- l = 0;
- else
- l = atoi(p) - start + 1;
-
- if (off == start) {
- if (len != l) {
- fprintf(stderr, "Len %u should be %u: %s",
- (int)len, l, line);
- abort();
- }
- if (type != rw) {
- fprintf(stderr, "Type %s wrong: %s",
- rw == F_RDLCK ? "READ" : "WRITE", line);
- abort();
- }
- found = true;
- break;
- }
- }
-
- if (!found) {
- fprintf(stderr, "Unlock on %u@%u not found!\n",
- (int)off, (int)len);
- abort();
- }
-
- fclose(locks);
-#endif
-
- fl.l_type = F_UNLCK;
- fl.l_whence = SEEK_SET;
- fl.l_start = off;
- fl.l_len = len;
- fl.l_pid = 0;
-
- return fcntl(tdb->fd, F_SETLKW, &fl);
-}
-
-/* list -1 is the alloc list, otherwise a hash chain. */
-static tdb_off_t lock_offset(int list)
-{
- return FREELIST_TOP + 4*list;
-}
-
-/* a byte range locking function - return 0 on success
- this functions locks/unlocks 1 byte at the specified offset.
-
- On error, errno is also set so that errors are passed back properly
- through tdb_open().
-
- note that a len of zero means lock to end of file
-*/
-int tdb_brlock(struct tdb_context *tdb,
- int rw_type, tdb_off_t offset, size_t len,
- enum tdb_lock_flags flags)
-{
- int ret;
-
- if (tdb->flags & TDB_NOLOCK) {
- return 0;
- }
-
- if (flags & TDB_LOCK_MARK_ONLY) {
- return 0;
- }
-
- if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) {
- tdb->ecode = TDB_ERR_RDONLY;
- return -1;
- }
-
- do {
- ret = fcntl_lock(tdb, rw_type, offset, len,
- flags & TDB_LOCK_WAIT);
- /* Check for a sigalarm break. */
- if (ret == -1 && errno == EINTR &&
- tdb->interrupt_sig_ptr &&
- *tdb->interrupt_sig_ptr) {
- break;
- }
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- tdb->ecode = TDB_ERR_LOCK;
- /* Generic lock error. errno set by fcntl.
- * EAGAIN is an expected return from non-blocking
- * locks. */
- if (!(flags & TDB_LOCK_PROBE) && errno != EAGAIN) {
- TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d flags=%d len=%d\n",
- tdb->fd, offset, rw_type, flags, (int)len));
- }
- return -1;
- }
- return 0;
-}
-
-int tdb_brunlock(struct tdb_context *tdb,
- int rw_type, tdb_off_t offset, size_t len)
-{
- int ret;
-
- if (tdb->flags & TDB_NOLOCK) {
- return 0;
- }
-
- do {
- ret = fcntl_unlock(tdb, rw_type, offset, len);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brunlock failed (fd=%d) at offset %d rw_type=%d len=%d\n",
- tdb->fd, offset, rw_type, (int)len));
- }
- return ret;
-}
-
-/*
- upgrade a read lock to a write lock. This needs to be handled in a
- special way as some OSes (such as solaris) have too conservative
- deadlock detection and claim a deadlock when progress can be
- made. For those OSes we may loop for a while.
-*/
-int tdb_allrecord_upgrade(struct tdb_context *tdb)
-{
- int count = 1000;
-
- if (tdb->allrecord_lock.count != 1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "tdb_allrecord_upgrade failed: count %u too high\n",
- tdb->allrecord_lock.count));
- return -1;
- }
-
- if (tdb->allrecord_lock.off != 1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "tdb_allrecord_upgrade failed: already upgraded?\n"));
- return -1;
- }
-
- while (count--) {
- struct timeval tv;
- if (tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0,
- TDB_LOCK_WAIT|TDB_LOCK_PROBE) == 0) {
- tdb->allrecord_lock.ltype = F_WRLCK;
- tdb->allrecord_lock.off = 0;
- return 0;
- }
- if (errno != EDEADLK) {
- break;
- }
- /* sleep for as short a time as we can - more portable than usleep() */
- tv.tv_sec = 0;
- tv.tv_usec = 1;
- select(0, NULL, NULL, NULL, &tv);
- }
- TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_allrecord_upgrade failed\n"));
- return -1;
-}
-
-static struct tdb_lock_type *find_nestlock(struct tdb_context *tdb,
- tdb_off_t offset)
-{
- unsigned int i;
-
- for (i=0; i<tdb->num_lockrecs; i++) {
- if (tdb->lockrecs[i].off == offset) {
- return &tdb->lockrecs[i];
- }
- }
- return NULL;
-}
-
-/* lock an offset in the database. */
-int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype,
- enum tdb_lock_flags flags)
-{
- struct tdb_lock_type *new_lck;
-
- if (offset >= lock_offset(tdb->header.hash_size)) {
- tdb->ecode = TDB_ERR_LOCK;
- TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid offset %u for ltype=%d\n",
- offset, ltype));
- return -1;
- }
- if (tdb->flags & TDB_NOLOCK)
- return 0;
-
- new_lck = find_nestlock(tdb, offset);
- if (new_lck) {
- /*
- * Just increment the in-memory struct, posix locks
- * don't stack.
- */
- new_lck->count++;
- return 0;
- }
-
- new_lck = (struct tdb_lock_type *)realloc(
- tdb->lockrecs,
- sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1));
- if (new_lck == NULL) {
- errno = ENOMEM;
- return -1;
- }
- tdb->lockrecs = new_lck;
-
- /* Since fcntl locks don't nest, we do a lock for the first one,
- and simply bump the count for future ones */
- if (tdb_brlock(tdb, ltype, offset, 1, flags)) {
- return -1;
- }
-
- tdb->lockrecs[tdb->num_lockrecs].off = offset;
- tdb->lockrecs[tdb->num_lockrecs].count = 1;
- tdb->lockrecs[tdb->num_lockrecs].ltype = ltype;
- tdb->num_lockrecs++;
-
- return 0;
-}
-
-static int tdb_lock_and_recover(struct tdb_context *tdb)
-{
- int ret;
-
- /* We need to match locking order in transaction commit. */
- if (tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0, TDB_LOCK_WAIT)) {
- return -1;
- }
-
- if (tdb_brlock(tdb, F_WRLCK, OPEN_LOCK, 1, TDB_LOCK_WAIT)) {
- tdb_brunlock(tdb, F_WRLCK, FREELIST_TOP, 0);
- return -1;
- }
-
- ret = tdb_transaction_recover(tdb);
-
- tdb_brunlock(tdb, F_WRLCK, OPEN_LOCK, 1);
- tdb_brunlock(tdb, F_WRLCK, FREELIST_TOP, 0);
-
- return ret;
-}
-
-static bool have_data_locks(const struct tdb_context *tdb)
-{
- unsigned int i;
-
- for (i = 0; i < tdb->num_lockrecs; i++) {
- if (tdb->lockrecs[i].off >= lock_offset(-1))
- return true;
- }
- return false;
-}
-
-static int tdb_lock_list(struct tdb_context *tdb, int list, int ltype,
- enum tdb_lock_flags waitflag)
-{
- int ret;
- bool check = false;
-
- /* a allrecord lock allows us to avoid per chain locks */
- if (tdb->allrecord_lock.count &&
- (ltype == tdb->allrecord_lock.ltype || ltype == F_RDLCK)) {
- return 0;
- }
-
- if (tdb->allrecord_lock.count) {
- tdb->ecode = TDB_ERR_LOCK;
- ret = -1;
- } else {
- /* Only check when we grab first data lock. */
- check = !have_data_locks(tdb);
- ret = tdb_nest_lock(tdb, lock_offset(list), ltype, waitflag);
-
- if (ret == 0 && check && tdb_needs_recovery(tdb)) {
- tdb_nest_unlock(tdb, lock_offset(list), ltype, false);
-
- if (tdb_lock_and_recover(tdb) == -1) {
- return -1;
- }
- return tdb_lock_list(tdb, list, ltype, waitflag);
- }
- }
- return ret;
-}
-
-/* lock a list in the database. list -1 is the alloc list */
-int tdb_lock(struct tdb_context *tdb, int list, int ltype)
-{
- int ret;
-
- ret = tdb_lock_list(tdb, list, ltype, TDB_LOCK_WAIT);
- if (ret) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
- "ltype=%d (%s)\n", list, ltype, strerror(errno)));
- }
- return ret;
-}
-
-/* lock a list in the database. list -1 is the alloc list. non-blocking lock */
-int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
-{
- return tdb_lock_list(tdb, list, ltype, TDB_LOCK_NOWAIT);
-}
-
-
-int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype,
- bool mark_lock)
-{
- int ret = -1;
- struct tdb_lock_type *lck;
-
- if (tdb->flags & TDB_NOLOCK)
- return 0;
-
- /* Sanity checks */
- if (offset >= lock_offset(tdb->header.hash_size)) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: offset %u invalid (%d)\n", offset, tdb->header.hash_size));
- return ret;
- }
-
- lck = find_nestlock(tdb, offset);
- if ((lck == NULL) || (lck->count == 0)) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
- return -1;
- }
-
- if (lck->count > 1) {
- lck->count--;
- return 0;
- }
-
- /*
- * This lock has count==1 left, so we need to unlock it in the
- * kernel. We don't bother with decrementing the in-memory array
- * element, we're about to overwrite it with the last array element
- * anyway.
- */
-
- if (mark_lock) {
- ret = 0;
- } else {
- ret = tdb_brunlock(tdb, ltype, offset, 1);
- }
-
- /*
- * Shrink the array by overwriting the element just unlocked with the
- * last array element.
- */
- *lck = tdb->lockrecs[--tdb->num_lockrecs];
-
- /*
- * We don't bother with realloc when the array shrinks, but if we have
- * a completely idle tdb we should get rid of the locked array.
- */
-
- if (tdb->num_lockrecs == 0) {
- SAFE_FREE(tdb->lockrecs);
- }
-
- if (ret)
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
- return ret;
-}
-
-int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
-{
- /* a global lock allows us to avoid per chain locks */
- if (tdb->allrecord_lock.count &&
- (ltype == tdb->allrecord_lock.ltype || ltype == F_RDLCK)) {
- return 0;
- }
-
- if (tdb->allrecord_lock.count) {
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- return tdb_nest_unlock(tdb, lock_offset(list), ltype, false);
-}
-
-/*
- get the transaction lock
- */
-int tdb_transaction_lock(struct tdb_context *tdb, int ltype)
-{
- return tdb_nest_lock(tdb, TRANSACTION_LOCK, ltype, TDB_LOCK_WAIT);
-}
-
-/*
- release the transaction lock
- */
-int tdb_transaction_unlock(struct tdb_context *tdb, int ltype)
-{
- return tdb_nest_unlock(tdb, TRANSACTION_LOCK, ltype, false);
-}
-
-/* Returns 0 if all done, -1 if error, 1 if ok. */
-static int tdb_allrecord_check(struct tdb_context *tdb, int ltype,
- enum tdb_lock_flags flags, bool upgradable)
-{
- /* There are no locks on read-only dbs */
- if (tdb->read_only || tdb->traverse_read) {
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- if (tdb->allrecord_lock.count && tdb->allrecord_lock.ltype == ltype) {
- tdb->allrecord_lock.count++;
- return 0;
- }
-
- if (tdb->allrecord_lock.count) {
- /* a global lock of a different type exists */
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- if (tdb_have_extra_locks(tdb)) {
- /* can't combine global and chain locks */
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- if (upgradable && ltype != F_RDLCK) {
- /* tdb error: you can't upgrade a write lock! */
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
- return 1;
-}
-
-/* We only need to lock individual bytes, but Linux merges consecutive locks
- * so we lock in contiguous ranges. */
-static int tdb_chainlock_gradual(struct tdb_context *tdb,
- int ltype, enum tdb_lock_flags flags,
- size_t off, size_t len)
-{
- int ret;
- enum tdb_lock_flags nb_flags = (flags & ~TDB_LOCK_WAIT);
-
- if (len <= 4) {
- /* Single record. Just do blocking lock. */
- return tdb_brlock(tdb, ltype, off, len, flags);
- }
-
- /* First we try non-blocking. */
- ret = tdb_brlock(tdb, ltype, off, len, nb_flags);
- if (ret == 0) {
- return 0;
- }
-
- /* Try locking first half, then second. */
- ret = tdb_chainlock_gradual(tdb, ltype, flags, off, len / 2);
- if (ret == -1)
- return -1;
-
- ret = tdb_chainlock_gradual(tdb, ltype, flags,
- off + len / 2, len - len / 2);
- if (ret == -1) {
- tdb_brunlock(tdb, ltype, off, len / 2);
- return -1;
- }
- return 0;
-}
-
-/* lock/unlock entire database. It can only be upgradable if you have some
- * other way of guaranteeing exclusivity (ie. transaction write lock).
- * We do the locking gradually to avoid being starved by smaller locks. */
-int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
- enum tdb_lock_flags flags, bool upgradable)
-{
- switch (tdb_allrecord_check(tdb, ltype, flags, upgradable)) {
- case -1:
- return -1;
- case 0:
- return 0;
- }
-
- /* We cover two kinds of locks:
- * 1) Normal chain locks. Taken for almost all operations.
- * 3) Individual records locks. Taken after normal or free
- * chain locks.
- *
- * It is (1) which cause the starvation problem, so we're only
- * gradual for that. */
- if (tdb_chainlock_gradual(tdb, ltype, flags, FREELIST_TOP,
- tdb->header.hash_size * 4) == -1) {
- return -1;
- }
-
- /* Grab individual record locks. */
- if (tdb_brlock(tdb, ltype, lock_offset(tdb->header.hash_size), 0,
- flags) == -1) {
- tdb_brunlock(tdb, ltype, FREELIST_TOP,
- tdb->header.hash_size * 4);
- return -1;
- }
-
- tdb->allrecord_lock.count = 1;
- /* If it's upgradable, it's actually exclusive so we can treat
- * it as a write lock. */
- tdb->allrecord_lock.ltype = upgradable ? F_WRLCK : ltype;
- tdb->allrecord_lock.off = upgradable;
-
- if (tdb_needs_recovery(tdb)) {
- bool mark = flags & TDB_LOCK_MARK_ONLY;
- tdb_allrecord_unlock(tdb, ltype, mark);
- if (mark) {
- tdb->ecode = TDB_ERR_LOCK;
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "tdb_lockall_mark cannot do recovery\n"));
- return -1;
- }
- if (tdb_lock_and_recover(tdb) == -1) {
- return -1;
- }
- return tdb_allrecord_lock(tdb, ltype, flags, upgradable);
- }
-
- return 0;
-}
-
-
-
-/* unlock entire db */
-int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock)
-{
- /* There are no locks on read-only dbs */
- if (tdb->read_only || tdb->traverse_read) {
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- if (tdb->allrecord_lock.count == 0) {
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- /* Upgradable locks are marked as write locks. */
- if (tdb->allrecord_lock.ltype != ltype
- && (!tdb->allrecord_lock.off || ltype != F_RDLCK)) {
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- if (tdb->allrecord_lock.count > 1) {
- tdb->allrecord_lock.count--;
- return 0;
- }
-
- if (!mark_lock && tdb_brunlock(tdb, ltype, FREELIST_TOP, 0)) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno)));
- return -1;
- }
-
- tdb->allrecord_lock.count = 0;
- tdb->allrecord_lock.ltype = 0;
-
- return 0;
-}
-
-/* lock entire database with write lock */
-int tdb_lockall(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_lockall");
- return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
-}
-
-/* lock entire database with write lock - mark only */
-int tdb_lockall_mark(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_lockall_mark");
- return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY, false);
-}
-
-/* unlock entire database with write lock - unmark only */
-int tdb_lockall_unmark(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_lockall_unmark");
- return tdb_allrecord_unlock(tdb, F_WRLCK, true);
-}
-
-/* lock entire database with write lock - nonblocking varient */
-int tdb_lockall_nonblock(struct tdb_context *tdb)
-{
- int ret = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_NOWAIT, false);
- tdb_trace_ret(tdb, "tdb_lockall_nonblock", ret);
- return ret;
-}
-
-/* unlock entire database with write lock */
-int tdb_unlockall(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_unlockall");
- return tdb_allrecord_unlock(tdb, F_WRLCK, false);
-}
-
-/* lock entire database with read lock */
-int tdb_lockall_read(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_lockall_read");
- return tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
-}
-
-/* lock entire database with read lock - nonblock varient */
-int tdb_lockall_read_nonblock(struct tdb_context *tdb)
-{
- int ret = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_NOWAIT, false);
- tdb_trace_ret(tdb, "tdb_lockall_read_nonblock", ret);
- return ret;
-}
-
-/* unlock entire database with read lock */
-int tdb_unlockall_read(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_unlockall_read");
- return tdb_allrecord_unlock(tdb, F_RDLCK, false);
-}
-
-/* lock/unlock one hash chain. This is meant to be used to reduce
- contention - it cannot guarantee how many records will be locked */
-int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
-{
- int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
- tdb_trace_1rec(tdb, "tdb_chainlock", key);
- return ret;
-}
-
-/* lock/unlock one hash chain, non-blocking. This is meant to be used
- to reduce contention - it cannot guarantee how many records will be
- locked */
-int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
-{
- int ret = tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
- tdb_trace_1rec_ret(tdb, "tdb_chainlock_nonblock", key, ret);
- return ret;
-}
-
-/* mark a chain as locked without actually locking it. Warning! use with great caution! */
-int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
-{
- int ret = tdb_nest_lock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))),
- F_WRLCK, TDB_LOCK_MARK_ONLY);
- tdb_trace_1rec(tdb, "tdb_chainlock_mark", key);
- return ret;
-}
-
-/* unmark a chain as locked without actually locking it. Warning! use with great caution! */
-int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
-{
- tdb_trace_1rec(tdb, "tdb_chainlock_unmark", key);
- return tdb_nest_unlock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))),
- F_WRLCK, true);
-}
-
-int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
-{
- tdb_trace_1rec(tdb, "tdb_chainunlock", key);
- return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
-}
-
-int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
-{
- int ret;
- ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
- tdb_trace_1rec(tdb, "tdb_chainlock_read", key);
- return ret;
-}
-
-int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
-{
- tdb_trace_1rec(tdb, "tdb_chainunlock_read", key);
- return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
-}
-
-
-
-/* record lock stops delete underneath */
-int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
-{
- if (tdb->allrecord_lock.count) {
- return 0;
- }
- return off ? tdb_brlock(tdb, F_RDLCK, off, 1, TDB_LOCK_WAIT) : 0;
-}
-
-/*
- Write locks override our own fcntl readlocks, so check it here.
- Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
- an error to fail to get the lock here.
-*/
-int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off)
-{
- struct tdb_traverse_lock *i;
- for (i = &tdb->travlocks; i; i = i->next)
- if (i->off == off)
- return -1;
- if (tdb->allrecord_lock.count) {
- if (tdb->allrecord_lock.ltype == F_WRLCK) {
- return 0;
- }
- return -1;
- }
- return tdb_brlock(tdb, F_WRLCK, off, 1, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE);
-}
-
-int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off)
-{
- if (tdb->allrecord_lock.count) {
- return 0;
- }
- return tdb_brunlock(tdb, F_WRLCK, off, 1);
-}
-
-/* fcntl locks don't stack: avoid unlocking someone else's */
-int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off)
-{
- struct tdb_traverse_lock *i;
- uint32_t count = 0;
-
- if (tdb->allrecord_lock.count) {
- return 0;
- }
-
- if (off == 0)
- return 0;
- for (i = &tdb->travlocks; i; i = i->next)
- if (i->off == off)
- count++;
- return (count == 1 ? tdb_brunlock(tdb, F_RDLCK, off, 1) : 0);
-}
-
-bool tdb_have_extra_locks(struct tdb_context *tdb)
-{
- unsigned int extra = tdb->num_lockrecs;
-
- /* A transaction holds the lock for all records. */
- if (!tdb->transaction && tdb->allrecord_lock.count) {
- return true;
- }
-
- /* We always hold the active lock if CLEAR_IF_FIRST. */
- if (find_nestlock(tdb, ACTIVE_LOCK)) {
- extra--;
- }
-
- /* In a transaction, we expect to hold the transaction lock */
- if (tdb->transaction && find_nestlock(tdb, TRANSACTION_LOCK)) {
- extra--;
- }
-
- return extra;
-}
-
-/* The transaction code uses this to remove all locks. Note that this
- may include OPEN_LOCK. */
-void tdb_release_extra_locks(struct tdb_context *tdb)
-{
- unsigned int i, extra = 0;
-
- if (tdb->allrecord_lock.count != 0) {
- tdb_brunlock(tdb, tdb->allrecord_lock.ltype, FREELIST_TOP, 0);
- tdb->allrecord_lock.count = 0;
- }
-
- for (i=0;i<tdb->num_lockrecs;i++) {
- struct tdb_lock_type *lck = &tdb->lockrecs[i];
-
- /* Don't release transaction or active locks! */
- if (tdb->transaction && lck->off == TRANSACTION_LOCK) {
- tdb->lockrecs[extra++] = *lck;
- } else if (lck->off == ACTIVE_LOCK) {
- tdb->lockrecs[extra++] = *lck;
- } else {
- tdb_brunlock(tdb, lck->ltype, lck->off, 1);
- }
- }
- tdb->num_lockrecs = extra;
- if (tdb->num_lockrecs == 0) {
- SAFE_FREE(tdb->lockrecs);
- }
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
-static struct tdb_context *tdbs = NULL;
-
-/* We use two hashes to double-check they're using the right hash function. */
-void tdb_header_hash(struct tdb_context *tdb,
- uint32_t *magic1_hash, uint32_t *magic2_hash)
-{
- TDB_DATA hash_key;
- uint32_t tdb_magic = TDB_MAGIC;
-
- hash_key.dptr = (unsigned char *)TDB_MAGIC_FOOD;
- hash_key.dsize = sizeof(TDB_MAGIC_FOOD);
- *magic1_hash = tdb->hash_fn(&hash_key);
-
- hash_key.dptr = CONVERT(tdb_magic);
- hash_key.dsize = sizeof(tdb_magic);
- *magic2_hash = tdb->hash_fn(&hash_key);
-
- /* Make sure at least one hash is non-zero! */
- if (*magic1_hash == 0 && *magic2_hash == 0)
- *magic1_hash = 1;
-}
-
-/* initialise a new database with a specified hash size */
-static int tdb_new_database(struct tdb_context *tdb, int hash_size)
-{
- struct tdb_header *newdb;
- size_t size;
- int ret = -1;
- ssize_t written;
-
- /* We make it up in memory, then write it out if not internal */
- size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t);
- if (!(newdb = (struct tdb_header *)calloc(size, 1))) {
- tdb->ecode = TDB_ERR_OOM;
- return -1;
- }
-
- /* Fill in the header */
- newdb->version = TDB_VERSION;
- newdb->hash_size = hash_size;
-
- tdb_header_hash(tdb, &newdb->magic1_hash, &newdb->magic2_hash);
-
- /* Make sure older tdbs (which don't check the magic hash fields)
- * will refuse to open this TDB. */
- if (tdb->flags & TDB_INCOMPATIBLE_HASH)
- newdb->rwlocks = TDB_HASH_RWLOCK_MAGIC;
-
- if (tdb->flags & TDB_INTERNAL) {
- tdb->map_size = size;
- tdb->map_ptr = (char *)newdb;
- memcpy(&tdb->header, newdb, sizeof(tdb->header));
- /* Convert the `ondisk' version if asked. */
- CONVERT(*newdb);
- return 0;
- }
- if (lseek(tdb->fd, 0, SEEK_SET) == -1)
- goto fail;
-
- if (ftruncate(tdb->fd, 0) == -1)
- goto fail;
-
- /* This creates an endian-converted header, as if read from disk */
- CONVERT(*newdb);
- memcpy(&tdb->header, newdb, sizeof(tdb->header));
- /* Don't endian-convert the magic food! */
- memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
- /* we still have "ret == -1" here */
- written = write(tdb->fd, newdb, size);
- if (written == size) {
- ret = 0;
- } else if (written != -1) {
- /* call write once again, this usually should return -1 and
- * set errno appropriately */
- size -= written;
- written = write(tdb->fd, newdb+written, size);
- if (written == size) {
- ret = 0;
- } else if (written >= 0) {
- /* a second incomplete write - we give up.
- * guessing the errno... */
- errno = ENOSPC;
- }
- }
-
- fail:
- SAFE_FREE(newdb);
- return ret;
-}
-
-
-
-static int 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 1;
- }
- }
-
- return 0;
-}
-
-/* open the database, creating it if necessary
-
- The open_flags and mode are passed straight to the open call on the
- database file. A flags value of O_WRONLY is invalid. The hash size
- is advisory, use zero for a default value.
-
- Return is NULL on error, in which case errno is also set. Don't
- try to call tdb_error or tdb_errname, just do strerror(errno).
-
- @param name may be NULL for internal databases. */
-struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
- int open_flags, mode_t mode)
-{
- return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
-}
-
-/* a default logging function */
-static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_FMT(3, 4);
-static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
-{
-}
-
-static bool check_header_hash(struct tdb_context *tdb,
- bool default_hash, uint32_t *m1, uint32_t *m2)
-{
- tdb_header_hash(tdb, m1, m2);
- if (tdb->header.magic1_hash == *m1 &&
- tdb->header.magic2_hash == *m2) {
- return true;
- }
-
- /* If they explicitly set a hash, always respect it. */
- if (!default_hash)
- return false;
-
- /* Otherwise, try the other inbuilt hash. */
- if (tdb->hash_fn == tdb_old_hash)
- tdb->hash_fn = tdb_jenkins_hash;
- else
- tdb->hash_fn = tdb_old_hash;
- return check_header_hash(tdb, false, m1, m2);
-}
-
-struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
- int open_flags, mode_t mode,
- const struct tdb_logging_context *log_ctx,
- tdb_hash_func hash_fn)
-{
- struct tdb_context *tdb;
- struct stat st;
- int rev = 0, locked = 0;
- unsigned char *vp;
- uint32_t vertest;
- unsigned v;
- const char *hash_alg;
- uint32_t magic1, magic2;
-
- if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
- /* Can't log this */
- errno = ENOMEM;
- goto fail;
- }
- tdb_io_init(tdb);
- tdb->fd = -1;
-#ifdef TDB_TRACE
- tdb->tracefd = -1;
-#endif
- tdb->name = NULL;
- tdb->map_ptr = NULL;
- tdb->flags = tdb_flags;
- tdb->open_flags = open_flags;
- if (log_ctx) {
- tdb->log = *log_ctx;
- } else {
- tdb->log.log_fn = null_log_fn;
- tdb->log.log_private = NULL;
- }
-
- if (hash_fn) {
- tdb->hash_fn = hash_fn;
- hash_alg = "the user defined";
- } else {
- /* This controls what we use when creating a tdb. */
- if (tdb->flags & TDB_INCOMPATIBLE_HASH) {
- tdb->hash_fn = tdb_jenkins_hash;
- } else {
- tdb->hash_fn = tdb_old_hash;
- }
- hash_alg = "either default";
- }
-
- /* cache the page size */
- tdb->page_size = getpagesize();
- if (tdb->page_size <= 0) {
- tdb->page_size = 0x2000;
- }
-
- tdb->max_dead_records = (tdb_flags & TDB_VOLATILE) ? 5 : 0;
-
- if ((open_flags & O_ACCMODE) == O_WRONLY) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n",
- name));
- errno = EINVAL;
- goto fail;
- }
-
- if (hash_size == 0)
- hash_size = DEFAULT_HASH_SIZE;
- if ((open_flags & O_ACCMODE) == O_RDONLY) {
- tdb->read_only = 1;
- /* read only databases don't do locking or clear if first */
- tdb->flags |= TDB_NOLOCK;
- tdb->flags &= ~TDB_CLEAR_IF_FIRST;
- }
-
- if ((tdb->flags & TDB_ALLOW_NESTING) &&
- (tdb->flags & TDB_DISALLOW_NESTING)) {
- tdb->ecode = TDB_ERR_NESTING;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
- "allow_nesting and disallow_nesting are not allowed together!"));
- errno = EINVAL;
- goto fail;
- }
-
- /*
- * TDB_DISALLOW_NESTING is the default behavior.
- */
- if (!(tdb->flags & TDB_ALLOW_NESTING)) {
- tdb->flags |= TDB_DISALLOW_NESTING;
- }
-
- /* internal databases don't mmap or lock, and start off cleared */
- if (tdb->flags & TDB_INTERNAL) {
- tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
- tdb->flags &= ~TDB_CLEAR_IF_FIRST;
- if (tdb_new_database(tdb, hash_size) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: tdb_new_database failed!"));
- goto fail;
- }
- goto internal;
- }
-
- if ((tdb->fd = open(name, open_flags, mode)) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_open_ex: 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_nest_lock(tdb, OPEN_LOCK, F_WRLCK, TDB_LOCK_WAIT) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get open lock on %s: %s\n",
- name, strerror(errno)));
- goto fail; /* errno set by tdb_brlock */
- }
-
- /* we need to zero database if we are the only one with it open */
- if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
- (!tdb->read_only) &&
- (locked = (tdb_nest_lock(tdb, ACTIVE_LOCK, F_WRLCK, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE) == 0))) {
- open_flags |= O_CREAT;
- if (ftruncate(tdb->fd, 0) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
- "failed to truncate %s: %s\n",
- name, strerror(errno)));
- goto fail; /* errno set by ftruncate */
- }
- }
-
- errno = 0;
- if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
- || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0) {
- if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
- if (errno == 0) {
- errno = EIO; /* ie bad format or something */
- }
- goto fail;
- }
- rev = (tdb->flags & TDB_CONVERT);
- } else if (tdb->header.version != TDB_VERSION
- && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION)))) {
- /* wrong version */
- errno = EIO;
- goto fail;
- }
- vp = (unsigned char *)&tdb->header.version;
- vertest = (((uint32_t)vp[0]) << 24) | (((uint32_t)vp[1]) << 16) |
- (((uint32_t)vp[2]) << 8) | (uint32_t)vp[3];
- tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
- if (!rev)
- tdb->flags &= ~TDB_CONVERT;
- else {
- tdb->flags |= TDB_CONVERT;
- tdb_convert(&tdb->header, sizeof(tdb->header));
- }
- if (fstat(tdb->fd, &st) == -1)
- goto fail;
-
- if (tdb->header.rwlocks != 0 &&
- tdb->header.rwlocks != TDB_HASH_RWLOCK_MAGIC) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n"));
- goto fail;
- }
-
- if ((tdb->header.magic1_hash == 0) && (tdb->header.magic2_hash == 0)) {
- /* older TDB without magic hash references */
- tdb->hash_fn = tdb_old_hash;
- } else if (!check_header_hash(tdb, !hash_fn, &magic1, &magic2)) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
- "%s was not created with %s hash function we are using\n"
- "magic1_hash[0x%08X %s 0x%08X] "
- "magic2_hash[0x%08X %s 0x%08X]\n",
- name, hash_alg,
- tdb->header.magic1_hash,
- (tdb->header.magic1_hash == magic1) ? "==" : "!=",
- magic1,
- tdb->header.magic2_hash,
- (tdb->header.magic2_hash == magic2) ? "==" : "!=",
- magic2));
- errno = EINVAL;
- goto fail;
- }
-
- /* Is it already in the open list? If so, fail. */
- if (tdb_already_open(st.st_dev, st.st_ino)) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
- "%s (%d,%d) is already open in this process\n",
- name, (int)st.st_dev, (int)st.st_ino));
- errno = EBUSY;
- goto fail;
- }
-
- if (!(tdb->name = (char *)strdup(name))) {
- errno = ENOMEM;
- goto fail;
- }
-
- tdb->map_size = st.st_size;
- tdb->device = st.st_dev;
- tdb->inode = st.st_ino;
- tdb_mmap(tdb);
- if (locked) {
- if (tdb_nest_unlock(tdb, ACTIVE_LOCK, F_WRLCK, false) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
- "failed to take ACTIVE_LOCK on %s: %s\n",
- name, strerror(errno)));
- goto fail;
- }
-
- }
-
- /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if
- we didn't get the initial exclusive lock as we need to let all other
- users know we're using it. */
-
- if (tdb_flags & TDB_CLEAR_IF_FIRST) {
- /* leave this lock in place to indicate it's in use */
- if (tdb_nest_lock(tdb, ACTIVE_LOCK, F_RDLCK, TDB_LOCK_WAIT) == -1) {
- goto fail;
- }
- }
-
- /* if needed, run recovery */
- if (tdb_transaction_recover(tdb) == -1) {
- goto fail;
- }
-
-#ifdef TDB_TRACE
- {
- char tracefile[strlen(name) + 32];
-
- snprintf(tracefile, sizeof(tracefile),
- "%s.trace.%li", name, (long)getpid());
- tdb->tracefd = open(tracefile, O_WRONLY|O_CREAT|O_EXCL, 0600);
- if (tdb->tracefd >= 0) {
- tdb_enable_seqnum(tdb);
- tdb_trace_open(tdb, "tdb_open", hash_size, tdb_flags,
- open_flags);
- } else
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to open trace file %s!\n", tracefile));
- }
-#endif
-
- internal:
- /* Internal (memory-only) databases skip all the code above to
- * do with disk files, and resume here by releasing their
- * open lock and hooking into the active list. */
- if (tdb_nest_unlock(tdb, OPEN_LOCK, F_WRLCK, false) == -1) {
- goto fail;
- }
- tdb->next = tdbs;
- tdbs = tdb;
- return tdb;
-
- fail:
- { int save_errno = errno;
-
- if (!tdb)
- return NULL;
-
-#ifdef TDB_TRACE
- close(tdb->tracefd);
-#endif
- if (tdb->map_ptr) {
- if (tdb->flags & TDB_INTERNAL)
- SAFE_FREE(tdb->map_ptr);
- else
- tdb_munmap(tdb);
- }
- SAFE_FREE(tdb->name);
- if (tdb->fd != -1)
- if (close(tdb->fd) != 0)
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
- SAFE_FREE(tdb->lockrecs);
- SAFE_FREE(tdb);
- errno = save_errno;
- return NULL;
- }
-}
-
-/*
- * Set the maximum number of dead records per hash chain
- */
-
-void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
-{
- tdb->max_dead_records = max_dead;
-}
-
-/**
- * Close a database.
- *
- * @returns -1 for error; 0 for success.
- **/
-int tdb_close(struct tdb_context *tdb)
-{
- struct tdb_context **i;
- int ret = 0;
-
- if (tdb->transaction) {
- tdb_transaction_cancel(tdb);
- }
- tdb_trace(tdb, "tdb_close");
-
- if (tdb->map_ptr) {
- if (tdb->flags & TDB_INTERNAL)
- SAFE_FREE(tdb->map_ptr);
- else
- tdb_munmap(tdb);
- }
- SAFE_FREE(tdb->name);
- if (tdb->fd != -1) {
- ret = close(tdb->fd);
- tdb->fd = -1;
- }
- SAFE_FREE(tdb->lockrecs);
-
- /* Remove from contexts list */
- for (i = &tdbs; *i; i = &(*i)->next) {
- if (*i == tdb) {
- *i = tdb->next;
- break;
- }
- }
-
-#ifdef TDB_TRACE
- close(tdb->tracefd);
-#endif
- memset(tdb, 0, sizeof(*tdb));
- SAFE_FREE(tdb);
-
- return ret;
-}
-
-/* register a loging function */
-void tdb_set_logging_function(struct tdb_context *tdb,
- const struct tdb_logging_context *log_ctx)
-{
- tdb->log = *log_ctx;
-}
-
-void *tdb_get_logging_private(struct tdb_context *tdb)
-{
- return tdb->log.log_private;
-}
-
-static int tdb_reopen_internal(struct tdb_context *tdb, bool active_lock)
-{
- struct stat st;
-
- if (tdb->flags & TDB_INTERNAL) {
- return 0; /* Nothing to do. */
- }
-
- if (tdb_have_extra_locks(tdb)) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n"));
- goto fail;
- }
-
- if (tdb->transaction != 0) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed inside a transaction\n"));
- goto fail;
- }
-
- if (tdb_munmap(tdb) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
- goto fail;
- }
- if (close(tdb->fd) != 0)
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
- tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
- if (tdb->fd == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno)));
- goto fail;
- }
- if (fstat(tdb->fd, &st) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
- goto fail;
- }
- if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n"));
- goto fail;
- }
- tdb_mmap(tdb);
-
- /* We may still think we hold the active lock. */
- tdb->num_lockrecs = 0;
- SAFE_FREE(tdb->lockrecs);
-
- if (active_lock && tdb_nest_lock(tdb, ACTIVE_LOCK, F_RDLCK, TDB_LOCK_WAIT) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n"));
- goto fail;
- }
-
- return 0;
-
-fail:
- tdb_close(tdb);
- return -1;
-}
-
-/* reopen a tdb - this can be used after a fork to ensure that we have an independent
- seek pointer from our parent and to re-establish locks */
-int tdb_reopen(struct tdb_context *tdb)
-{
- return tdb_reopen_internal(tdb, tdb->flags & TDB_CLEAR_IF_FIRST);
-}
-
-/* reopen all tdb's */
-int tdb_reopen_all(int parent_longlived)
-{
- struct tdb_context *tdb;
-
- for (tdb=tdbs; tdb; tdb = tdb->next) {
- bool active_lock = (tdb->flags & TDB_CLEAR_IF_FIRST);
-
- /*
- * If the parent is longlived (ie. a
- * parent daemon architecture), we know
- * it will keep it's active lock on a
- * tdb opened with CLEAR_IF_FIRST. Thus
- * for child processes we don't have to
- * add an active lock. This is essential
- * to improve performance on systems that
- * keep POSIX locks as a non-scalable data
- * structure in the kernel.
- */
- if (parent_longlived) {
- /* Ensure no clear-if-first. */
- active_lock = false;
- }
-
- if (tdb_reopen_internal(tdb, active_lock) != 0)
- return -1;
- }
-
- return 0;
-}
+++ /dev/null
- /*
- Trivial Database: human-readable summary code
- Copyright (C) Rusty Russell 2010
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#include "tdb_private.h"
-
-#define SUMMARY_FORMAT \
- "Size of file/data: %u/%zu\n" \
- "Number of records: %zu\n" \
- "Smallest/average/largest keys: %zu/%zu/%zu\n" \
- "Smallest/average/largest data: %zu/%zu/%zu\n" \
- "Smallest/average/largest padding: %zu/%zu/%zu\n" \
- "Number of dead records: %zu\n" \
- "Smallest/average/largest dead records: %zu/%zu/%zu\n" \
- "Number of free records: %zu\n" \
- "Smallest/average/largest free records: %zu/%zu/%zu\n" \
- "Number of hash chains: %zu\n" \
- "Smallest/average/largest hash chains: %zu/%zu/%zu\n" \
- "Number of uncoalesced records: %zu\n" \
- "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n" \
- "Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
-
-/* We don't use tally module, to keep upstream happy. */
-struct tally {
- size_t min, max, total;
- size_t num;
-};
-
-static void tally_init(struct tally *tally)
-{
- tally->total = 0;
- tally->num = 0;
- tally->min = tally->max = 0;
-}
-
-static void tally_add(struct tally *tally, size_t len)
-{
- if (tally->num == 0)
- tally->max = tally->min = len;
- else if (len > tally->max)
- tally->max = len;
- else if (len < tally->min)
- tally->min = len;
- tally->num++;
- tally->total += len;
-}
-
-static size_t tally_mean(const struct tally *tally)
-{
- if (!tally->num)
- return 0;
- return tally->total / tally->num;
-}
-
-static size_t get_hash_length(struct tdb_context *tdb, unsigned int i)
-{
- tdb_off_t rec_ptr;
- size_t count = 0;
-
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(i), &rec_ptr) == -1)
- return 0;
-
- /* keep looking until we find the right record */
- while (rec_ptr) {
- struct tdb_record r;
- ++count;
- if (tdb_rec_read(tdb, rec_ptr, &r) == -1)
- return 0;
- rec_ptr = r.next;
- }
- return count;
-}
-
-char *tdb_summary(struct tdb_context *tdb)
-{
- tdb_off_t off;
- struct tally freet, keys, data, dead, extra, hash, uncoal;
- struct tdb_record rec;
- char *ret = NULL;
- bool locked;
- size_t len, unc = 0;
-
- /* Read-only databases use no locking at all: it's best-effort.
- * We may have a write lock already, so skip that case too. */
- if (tdb->read_only || tdb->allrecord_lock.count != 0) {
- locked = false;
- } else {
- if (tdb_lockall_read(tdb) == -1)
- return NULL;
- locked = true;
- }
-
- tally_init(&freet);
- tally_init(&keys);
- tally_init(&data);
- tally_init(&dead);
- tally_init(&extra);
- tally_init(&hash);
- tally_init(&uncoal);
-
- for (off = TDB_DATA_START(tdb->header.hash_size);
- off < tdb->map_size - 1;
- off += sizeof(rec) + rec.rec_len) {
- if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
- DOCONV()) == -1)
- goto unlock;
- switch (rec.magic) {
- case TDB_MAGIC:
- tally_add(&keys, rec.key_len);
- tally_add(&data, rec.data_len);
- tally_add(&extra, rec.rec_len - (rec.key_len
- + rec.data_len));
- if (unc > 1)
- tally_add(&uncoal, unc - 1);
- unc = 0;
- break;
- case TDB_FREE_MAGIC:
- tally_add(&freet, rec.rec_len);
- unc++;
- break;
- /* If we crash after ftruncate, we can get zeroes or fill. */
- case TDB_RECOVERY_INVALID_MAGIC:
- case 0x42424242:
- unc++;
- rec.rec_len = tdb_dead_space(tdb, off) - sizeof(rec);
- /* Fall through */
- case TDB_DEAD_MAGIC:
- tally_add(&dead, rec.rec_len);
- break;
- default:
- TDB_LOG((tdb, TDB_DEBUG_ERROR,
- "Unexpected record magic 0x%x at offset %d\n",
- rec.magic, off));
- goto unlock;
- }
- }
- if (unc > 1)
- tally_add(&uncoal, unc - 1);
-
- for (off = 0; off < tdb->header.hash_size; off++)
- tally_add(&hash, get_hash_length(tdb, off));
-
- /* 20 is max length of a %zu. */
- len = strlen(SUMMARY_FORMAT) + 35*20 + 1;
- ret = malloc(len);
- if (!ret)
- goto unlock;
-
- sprintf(ret, SUMMARY_FORMAT,
- tdb->map_size, keys.total+data.total,
- keys.num,
- keys.min, tally_mean(&keys), keys.max,
- data.min, tally_mean(&data), data.max,
- extra.min, tally_mean(&extra), extra.max,
- dead.num,
- dead.min, tally_mean(&dead), dead.max,
- freet.num,
- freet.min, tally_mean(&freet), freet.max,
- hash.num,
- hash.min, tally_mean(&hash), hash.max,
- uncoal.total,
- uncoal.min, tally_mean(&uncoal), uncoal.max,
- keys.total * 100.0 / tdb->map_size,
- data.total * 100.0 / tdb->map_size,
- extra.total * 100.0 / tdb->map_size,
- freet.total * 100.0 / tdb->map_size,
- dead.total * 100.0 / tdb->map_size,
- (keys.num + freet.num + dead.num)
- * (sizeof(struct tdb_record) + sizeof(uint32_t))
- * 100.0 / tdb->map_size,
- tdb->header.hash_size * sizeof(tdb_off_t)
- * 100.0 / tdb->map_size);
-
-unlock:
- if (locked) {
- tdb_unlockall_read(tdb);
- }
- return ret;
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-TDB_DATA tdb_null;
-
-/*
- non-blocking increment of the tdb sequence number if the tdb has been opened using
- the TDB_SEQNUM flag
-*/
-void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
-{
- tdb_off_t seqnum=0;
-
- if (!(tdb->flags & TDB_SEQNUM)) {
- return;
- }
-
- /* we ignore errors from this, as we have no sane way of
- dealing with them.
- */
- tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
- seqnum++;
- tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum);
-}
-
-/*
- increment the tdb sequence number if the tdb has been opened using
- the TDB_SEQNUM flag
-*/
-static void tdb_increment_seqnum(struct tdb_context *tdb)
-{
- if (!(tdb->flags & TDB_SEQNUM)) {
- return;
- }
-
- if (tdb_nest_lock(tdb, TDB_SEQNUM_OFS, F_WRLCK,
- TDB_LOCK_WAIT|TDB_LOCK_PROBE) != 0) {
- return;
- }
-
- tdb_increment_seqnum_nonblock(tdb);
-
- tdb_nest_unlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, false);
-}
-
-static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data)
-{
- return memcmp(data.dptr, key.dptr, data.dsize);
-}
-
-/* Returns 0 on fail. On success, return offset of record, and fills
- in rec */
-static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
- struct tdb_record *r)
-{
- tdb_off_t rec_ptr;
-
- /* read in the hash top */
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
- return 0;
-
- /* keep looking until we find the right record */
- while (rec_ptr) {
- if (tdb_rec_read(tdb, rec_ptr, r) == -1)
- return 0;
-
- if (!TDB_DEAD(r) && hash==r->full_hash
- && key.dsize==r->key_len
- && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r),
- r->key_len, tdb_key_compare,
- NULL) == 0) {
- return rec_ptr;
- }
- /* detect tight infinite loop */
- if (rec_ptr == r->next) {
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_find: loop detected.\n"));
- return 0;
- }
- rec_ptr = r->next;
- }
- tdb->ecode = TDB_ERR_NOEXIST;
- return 0;
-}
-
-/* As tdb_find, but if you succeed, keep the lock */
-tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype,
- struct tdb_record *rec)
-{
- uint32_t rec_ptr;
-
- if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
- return 0;
- if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
- tdb_unlock(tdb, BUCKET(hash), locktype);
- return rec_ptr;
-}
-
-static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
-
-/* update an entry in place - this only works if the new data size
- is <= the old data size and the key exists.
- on failure return -1.
-*/
-static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, TDB_DATA dbuf)
-{
- struct tdb_record rec;
- tdb_off_t rec_ptr;
-
- /* find entry */
- if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
- return -1;
-
- /* it could be an exact duplicate of what is there - this is
- * surprisingly common (eg. with a ldb re-index). */
- if (rec.key_len == key.dsize &&
- rec.data_len == dbuf.dsize &&
- rec.full_hash == hash) {
- TDB_DATA data = _tdb_fetch(tdb, key);
- if (data.dsize == dbuf.dsize &&
- memcmp(data.dptr, dbuf.dptr, data.dsize) == 0) {
- if (data.dptr) {
- free(data.dptr);
- }
- return 0;
- }
- if (data.dptr) {
- free(data.dptr);
- }
- }
-
-
- /* must be long enough key, data and tailer */
- if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off_t)) {
- tdb->ecode = TDB_SUCCESS; /* Not really an error */
- return -1;
- }
-
- if (tdb->methods->tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
- dbuf.dptr, dbuf.dsize) == -1)
- return -1;
-
- if (dbuf.dsize != rec.data_len) {
- /* update size */
- rec.data_len = dbuf.dsize;
- return tdb_rec_write(tdb, rec_ptr, &rec);
- }
-
- return 0;
-}
-
-/* find an entry in the database given a key */
-/* If an entry doesn't exist tdb_err will be set to
- * TDB_ERR_NOEXIST. If a key has no data attached
- * then the TDB_DATA will have zero length but
- * a non-zero pointer
- */
-static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
-{
- tdb_off_t rec_ptr;
- struct tdb_record rec;
- TDB_DATA ret;
- uint32_t hash;
-
- /* find which hash bucket it is in */
- hash = tdb->hash_fn(&key);
- if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
- return tdb_null;
-
- ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
- rec.data_len);
- ret.dsize = rec.data_len;
- tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
- return ret;
-}
-
-TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
-{
- TDB_DATA ret = _tdb_fetch(tdb, key);
-
- tdb_trace_1rec_retrec(tdb, "tdb_fetch", key, ret);
- return ret;
-}
-
-/*
- * Find an entry in the database and hand the record's data to a parsing
- * function. The parsing function is executed under the chain read lock, so it
- * should be fast and should not block on other syscalls.
- *
- * DON'T CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.
- *
- * For mmapped tdb's that do not have a transaction open it points the parsing
- * function directly at the mmap area, it avoids the malloc/memcpy in this
- * case. If a transaction is open or no mmap is available, it has to do
- * malloc/read/parse/free.
- *
- * This is interesting for all readers of potentially large data structures in
- * the tdb records, ldb indexes being one example.
- */
-
-int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
- int (*parser)(TDB_DATA key, TDB_DATA data,
- void *private_data),
- void *private_data)
-{
- tdb_off_t rec_ptr;
- struct tdb_record rec;
- int ret;
- uint32_t hash;
-
- /* find which hash bucket it is in */
- hash = tdb->hash_fn(&key);
-
- if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
- tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, -1);
- tdb->ecode = TDB_ERR_NOEXIST;
- return 0;
- }
- tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, 0);
-
- ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
- rec.data_len, parser, private_data);
-
- tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
-
- return ret;
-}
-
-/* check if an entry in the database exists
-
- note that 1 is returned if the key is found and 0 is returned if not found
- this doesn't match the conventions in the rest of this module, but is
- compatible with gdbm
-*/
-static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
-{
- struct tdb_record rec;
-
- if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
- return 0;
- tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
- return 1;
-}
-
-int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
-{
- uint32_t hash = tdb->hash_fn(&key);
- int ret;
-
- ret = tdb_exists_hash(tdb, key, hash);
- tdb_trace_1rec_ret(tdb, "tdb_exists", key, ret);
- return ret;
-}
-
-/* actually delete an entry in the database given the offset */
-int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct tdb_record *rec)
-{
- tdb_off_t last_ptr, i;
- struct tdb_record lastrec;
-
- if (tdb->read_only || tdb->traverse_read) return -1;
-
- if (((tdb->traverse_write != 0) && (!TDB_DEAD(rec))) ||
- tdb_write_lock_record(tdb, rec_ptr) == -1) {
- /* Someone traversing here: mark it as dead */
- rec->magic = TDB_DEAD_MAGIC;
- return tdb_rec_write(tdb, rec_ptr, rec);
- }
- if (tdb_write_unlock_record(tdb, rec_ptr) != 0)
- return -1;
-
- /* find previous record in hash chain */
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
- return -1;
- for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
- if (tdb_rec_read(tdb, i, &lastrec) == -1)
- return -1;
-
- /* unlink it: next ptr is at start of record. */
- if (last_ptr == 0)
- last_ptr = TDB_HASH_TOP(rec->full_hash);
- if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1)
- return -1;
-
- /* recover the space */
- if (tdb_free(tdb, rec_ptr, rec) == -1)
- return -1;
- return 0;
-}
-
-static int tdb_count_dead(struct tdb_context *tdb, uint32_t hash)
-{
- int res = 0;
- tdb_off_t rec_ptr;
- struct tdb_record rec;
-
- /* read in the hash top */
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
- return 0;
-
- while (rec_ptr) {
- if (tdb_rec_read(tdb, rec_ptr, &rec) == -1)
- return 0;
-
- if (rec.magic == TDB_DEAD_MAGIC) {
- res += 1;
- }
- rec_ptr = rec.next;
- }
- return res;
-}
-
-/*
- * Purge all DEAD records from a hash chain
- */
-static int tdb_purge_dead(struct tdb_context *tdb, uint32_t hash)
-{
- int res = -1;
- struct tdb_record rec;
- tdb_off_t rec_ptr;
-
- if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
- return -1;
- }
-
- /* read in the hash top */
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
- goto fail;
-
- while (rec_ptr) {
- tdb_off_t next;
-
- if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) {
- goto fail;
- }
-
- next = rec.next;
-
- if (rec.magic == TDB_DEAD_MAGIC
- && tdb_do_delete(tdb, rec_ptr, &rec) == -1) {
- goto fail;
- }
- rec_ptr = next;
- }
- res = 0;
- fail:
- tdb_unlock(tdb, -1, F_WRLCK);
- return res;
-}
-
-/* delete an entry in the database given a key */
-static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
-{
- tdb_off_t rec_ptr;
- struct tdb_record rec;
- int ret;
-
- if (tdb->max_dead_records != 0) {
-
- /*
- * Allow for some dead records per hash chain, mainly for
- * tdb's with a very high create/delete rate like locking.tdb.
- */
-
- if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
- return -1;
-
- if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) {
- /*
- * Don't let the per-chain freelist grow too large,
- * delete all existing dead records
- */
- tdb_purge_dead(tdb, hash);
- }
-
- if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
- tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
- return -1;
- }
-
- /*
- * Just mark the record as dead.
- */
- rec.magic = TDB_DEAD_MAGIC;
- ret = tdb_rec_write(tdb, rec_ptr, &rec);
- }
- else {
- if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK,
- &rec)))
- return -1;
-
- ret = tdb_do_delete(tdb, rec_ptr, &rec);
- }
-
- if (ret == 0) {
- tdb_increment_seqnum(tdb);
- }
-
- if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
- TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n"));
- return ret;
-}
-
-int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
-{
- uint32_t hash = tdb->hash_fn(&key);
- int ret;
-
- ret = tdb_delete_hash(tdb, key, hash);
- tdb_trace_1rec_ret(tdb, "tdb_delete", key, ret);
- return ret;
-}
-
-/*
- * See if we have a dead record around with enough space
- */
-static tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash,
- struct tdb_record *r, tdb_len_t length)
-{
- tdb_off_t rec_ptr;
-
- /* read in the hash top */
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
- return 0;
-
- /* keep looking until we find the right record */
- while (rec_ptr) {
- if (tdb_rec_read(tdb, rec_ptr, r) == -1)
- return 0;
-
- if (TDB_DEAD(r) && r->rec_len >= length) {
- /*
- * First fit for simple coding, TODO: change to best
- * fit
- */
- return rec_ptr;
- }
- rec_ptr = r->next;
- }
- return 0;
-}
-
-static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
- TDB_DATA dbuf, int flag, uint32_t hash)
-{
- struct tdb_record rec;
- tdb_off_t rec_ptr;
- char *p = NULL;
- int ret = -1;
-
- /* check for it existing, on insert. */
- if (flag == TDB_INSERT) {
- if (tdb_exists_hash(tdb, key, hash)) {
- tdb->ecode = TDB_ERR_EXISTS;
- goto fail;
- }
- } else {
- /* first try in-place update, on modify or replace. */
- if (tdb_update_hash(tdb, key, hash, dbuf) == 0) {
- goto done;
- }
- if (tdb->ecode == TDB_ERR_NOEXIST &&
- flag == TDB_MODIFY) {
- /* if the record doesn't exist and we are in TDB_MODIFY mode then
- we should fail the store */
- goto fail;
- }
- }
- /* reset the error code potentially set by the tdb_update() */
- tdb->ecode = TDB_SUCCESS;
-
- /* delete any existing record - if it doesn't exist we don't
- care. Doing this first reduces fragmentation, and avoids
- coalescing with `allocated' block before it's updated. */
- if (flag != TDB_INSERT)
- tdb_delete_hash(tdb, key, hash);
-
- /* Copy key+value *before* allocating free space in case malloc
- fails and we are left with a dead spot in the tdb. */
-
- if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
- tdb->ecode = TDB_ERR_OOM;
- goto fail;
- }
-
- memcpy(p, key.dptr, key.dsize);
- if (dbuf.dsize)
- memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
-
- if (tdb->max_dead_records != 0) {
- /*
- * Allow for some dead records per hash chain, look if we can
- * find one that can hold the new record. We need enough space
- * for key, data and tailer. If we find one, we don't have to
- * consult the central freelist.
- */
- rec_ptr = tdb_find_dead(
- tdb, hash, &rec,
- key.dsize + dbuf.dsize + sizeof(tdb_off_t));
-
- if (rec_ptr != 0) {
- rec.key_len = key.dsize;
- rec.data_len = dbuf.dsize;
- rec.full_hash = hash;
- rec.magic = TDB_MAGIC;
- if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
- || tdb->methods->tdb_write(
- tdb, rec_ptr + sizeof(rec),
- p, key.dsize + dbuf.dsize) == -1) {
- goto fail;
- }
- goto done;
- }
- }
-
- /*
- * We have to allocate some space from the freelist, so this means we
- * have to lock it. Use the chance to purge all the DEAD records from
- * the hash chain under the freelist lock.
- */
-
- if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
- goto fail;
- }
-
- if ((tdb->max_dead_records != 0)
- && (tdb_purge_dead(tdb, hash) == -1)) {
- tdb_unlock(tdb, -1, F_WRLCK);
- goto fail;
- }
-
- /* we have to allocate some space */
- rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec);
-
- tdb_unlock(tdb, -1, F_WRLCK);
-
- if (rec_ptr == 0) {
- goto fail;
- }
-
- /* Read hash top into next ptr */
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
- goto fail;
-
- rec.key_len = key.dsize;
- rec.data_len = dbuf.dsize;
- rec.full_hash = hash;
- rec.magic = TDB_MAGIC;
-
- /* write out and point the top of the hash chain at it */
- if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
- || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
- || tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
- /* Need to tdb_unallocate() here */
- goto fail;
- }
-
- done:
- ret = 0;
- fail:
- if (ret == 0) {
- tdb_increment_seqnum(tdb);
- }
-
- SAFE_FREE(p);
- return ret;
-}
-
-/* store an element in the database, replacing any existing element
- with the same key
-
- return 0 on success, -1 on failure
-*/
-int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
-{
- uint32_t hash;
- int ret;
-
- if (tdb->read_only || tdb->traverse_read) {
- tdb->ecode = TDB_ERR_RDONLY;
- tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, -1);
- return -1;
- }
-
- /* find which hash bucket it is in */
- hash = tdb->hash_fn(&key);
- if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
- return -1;
-
- ret = _tdb_store(tdb, key, dbuf, flag, hash);
- tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, ret);
- tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
- return ret;
-}
-
-/* Append to an entry. Create if not exist. */
-int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
-{
- uint32_t hash;
- TDB_DATA dbuf;
- int ret = -1;
-
- /* find which hash bucket it is in */
- hash = tdb->hash_fn(&key);
- if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
- return -1;
-
- dbuf = _tdb_fetch(tdb, key);
-
- if (dbuf.dptr == NULL) {
- dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize);
- } else {
- unsigned int new_len = dbuf.dsize + new_dbuf.dsize;
- unsigned char *new_dptr;
-
- /* realloc '0' is special: don't do that. */
- if (new_len == 0)
- new_len = 1;
- new_dptr = (unsigned char *)realloc(dbuf.dptr, new_len);
- if (new_dptr == NULL) {
- free(dbuf.dptr);
- }
- dbuf.dptr = new_dptr;
- }
-
- if (dbuf.dptr == NULL) {
- tdb->ecode = TDB_ERR_OOM;
- goto failed;
- }
-
- memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize);
- dbuf.dsize += new_dbuf.dsize;
-
- ret = _tdb_store(tdb, key, dbuf, 0, hash);
- tdb_trace_2rec_retrec(tdb, "tdb_append", key, new_dbuf, dbuf);
-
-failed:
- tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
- SAFE_FREE(dbuf.dptr);
- return ret;
-}
-
-
-/*
- return the name of the current tdb file
- useful for external logging functions
-*/
-const char *tdb_name(struct tdb_context *tdb)
-{
- return tdb->name;
-}
-
-/*
- return the underlying file descriptor being used by tdb, or -1
- useful for external routines that want to check the device/inode
- of the fd
-*/
-int tdb_fd(struct tdb_context *tdb)
-{
- return tdb->fd;
-}
-
-/*
- return the current logging function
- useful for external tdb routines that wish to log tdb errors
-*/
-tdb_log_func tdb_log_fn(struct tdb_context *tdb)
-{
- return tdb->log.log_fn;
-}
-
-
-/*
- get the tdb sequence number. Only makes sense if the writers opened
- with TDB_SEQNUM set. Note that this sequence number will wrap quite
- quickly, so it should only be used for a 'has something changed'
- test, not for code that relies on the count of the number of changes
- made. If you want a counter then use a tdb record.
-
- The aim of this sequence number is to allow for a very lightweight
- test of a possible tdb change.
-*/
-int tdb_get_seqnum(struct tdb_context *tdb)
-{
- tdb_off_t seqnum=0;
-
- tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
- return seqnum;
-}
-
-int tdb_hash_size(struct tdb_context *tdb)
-{
- return tdb->header.hash_size;
-}
-
-size_t tdb_map_size(struct tdb_context *tdb)
-{
- return tdb->map_size;
-}
-
-int tdb_get_flags(struct tdb_context *tdb)
-{
- return tdb->flags;
-}
-
-void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
-{
- if ((flags & TDB_ALLOW_NESTING) &&
- (flags & TDB_DISALLOW_NESTING)) {
- tdb->ecode = TDB_ERR_NESTING;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_add_flags: "
- "allow_nesting and disallow_nesting are not allowed together!"));
- return;
- }
-
- if (flags & TDB_ALLOW_NESTING) {
- tdb->flags &= ~TDB_DISALLOW_NESTING;
- }
- if (flags & TDB_DISALLOW_NESTING) {
- tdb->flags &= ~TDB_ALLOW_NESTING;
- }
-
- tdb->flags |= flags;
-}
-
-void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
-{
- if ((flags & TDB_ALLOW_NESTING) &&
- (flags & TDB_DISALLOW_NESTING)) {
- tdb->ecode = TDB_ERR_NESTING;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_remove_flags: "
- "allow_nesting and disallow_nesting are not allowed together!"));
- return;
- }
-
- if (flags & TDB_ALLOW_NESTING) {
- tdb->flags |= TDB_DISALLOW_NESTING;
- }
- if (flags & TDB_DISALLOW_NESTING) {
- tdb->flags |= TDB_ALLOW_NESTING;
- }
-
- tdb->flags &= ~flags;
-}
-
-
-/*
- enable sequence number handling on an open tdb
-*/
-void tdb_enable_seqnum(struct tdb_context *tdb)
-{
- tdb->flags |= TDB_SEQNUM;
-}
-
-
-/*
- add a region of the file to the freelist. Length is the size of the region in bytes,
- which includes the free list header that needs to be added
- */
-static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t length)
-{
- struct tdb_record rec;
- if (length <= sizeof(rec)) {
- /* the region is not worth adding */
- return 0;
- }
- if (length + offset > tdb->map_size) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: adding region beyond end of file\n"));
- return -1;
- }
- memset(&rec,'\0',sizeof(rec));
- rec.rec_len = length - sizeof(rec);
- if (tdb_free(tdb, offset, &rec) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: failed to add free record\n"));
- return -1;
- }
- return 0;
-}
-
-/*
- wipe the entire database, deleting all records. This can be done
- very fast by using a allrecord lock. The entire data portion of the
- file becomes a single entry in the freelist.
-
- This code carefully steps around the recovery area, leaving it alone
- */
-int tdb_wipe_all(struct tdb_context *tdb)
-{
- int i;
- tdb_off_t offset = 0;
- ssize_t data_len;
- tdb_off_t recovery_head;
- tdb_len_t recovery_size = 0;
-
- if (tdb_lockall(tdb) != 0) {
- return -1;
- }
-
- tdb_trace(tdb, "tdb_wipe_all");
-
- /* see if the tdb has a recovery area, and remember its size
- if so. We don't want to lose this as otherwise each
- tdb_wipe_all() in a transaction will increase the size of
- the tdb by the size of the recovery area */
- if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery head\n"));
- goto failed;
- }
-
- if (recovery_head != 0) {
- struct tdb_record rec;
- if (tdb->methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery record\n"));
- return -1;
- }
- recovery_size = rec.rec_len + sizeof(rec);
- }
-
- /* wipe the hashes */
- for (i=0;i<tdb->header.hash_size;i++) {
- if (tdb_ofs_write(tdb, TDB_HASH_TOP(i), &offset) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write hash %d\n", i));
- goto failed;
- }
- }
-
- /* wipe the freelist */
- if (tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write freelist\n"));
- goto failed;
- }
-
- /* add all the rest of the file to the freelist, possibly leaving a gap
- for the recovery area */
- if (recovery_size == 0) {
- /* the simple case - the whole file can be used as a freelist */
- data_len = (tdb->map_size - TDB_DATA_START(tdb->header.hash_size));
- if (tdb_free_region(tdb, TDB_DATA_START(tdb->header.hash_size), data_len) != 0) {
- goto failed;
- }
- } else {
- /* we need to add two freelist entries - one on either
- side of the recovery area
-
- Note that we cannot shift the recovery area during
- this operation. Only the transaction.c code may
- move the recovery area or we risk subtle data
- corruption
- */
- data_len = (recovery_head - TDB_DATA_START(tdb->header.hash_size));
- if (tdb_free_region(tdb, TDB_DATA_START(tdb->header.hash_size), data_len) != 0) {
- goto failed;
- }
- /* and the 2nd free list entry after the recovery area - if any */
- data_len = tdb->map_size - (recovery_head+recovery_size);
- if (tdb_free_region(tdb, recovery_head+recovery_size, data_len) != 0) {
- goto failed;
- }
- }
-
- if (tdb_unlockall(tdb) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n"));
- goto failed;
- }
-
- return 0;
-
-failed:
- tdb_unlockall(tdb);
- return -1;
-}
-
-struct traverse_state {
- bool error;
- struct tdb_context *dest_db;
-};
-
-/*
- traverse function for repacking
- */
-static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private_data)
-{
- struct traverse_state *state = (struct traverse_state *)private_data;
- if (tdb_store(state->dest_db, key, data, TDB_INSERT) != 0) {
- state->error = true;
- return -1;
- }
- return 0;
-}
-
-/*
- repack a tdb
- */
-int tdb_repack(struct tdb_context *tdb)
-{
- struct tdb_context *tmp_db;
- struct traverse_state state;
-
- tdb_trace(tdb, "tdb_repack");
-
- if (tdb_transaction_start(tdb) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to start transaction\n"));
- return -1;
- }
-
- tmp_db = tdb_open("tmpdb", tdb_hash_size(tdb), TDB_INTERNAL, O_RDWR|O_CREAT, 0);
- if (tmp_db == NULL) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to create tmp_db\n"));
- tdb_transaction_cancel(tdb);
- return -1;
- }
-
- state.error = false;
- state.dest_db = tmp_db;
-
- if (tdb_traverse_read(tdb, repack_traverse, &state) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to traverse copying out\n"));
- tdb_transaction_cancel(tdb);
- tdb_close(tmp_db);
- return -1;
- }
-
- if (state.error) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Error during traversal\n"));
- tdb_transaction_cancel(tdb);
- tdb_close(tmp_db);
- return -1;
- }
-
- if (tdb_wipe_all(tdb) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to wipe database\n"));
- tdb_transaction_cancel(tdb);
- tdb_close(tmp_db);
- return -1;
- }
-
- state.error = false;
- state.dest_db = tdb;
-
- if (tdb_traverse_read(tmp_db, repack_traverse, &state) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to traverse copying back\n"));
- tdb_transaction_cancel(tdb);
- tdb_close(tmp_db);
- return -1;
- }
-
- if (state.error) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Error during second traversal\n"));
- tdb_transaction_cancel(tdb);
- tdb_close(tmp_db);
- return -1;
- }
-
- tdb_close(tmp_db);
-
- if (tdb_transaction_commit(tdb) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to commit\n"));
- return -1;
- }
-
- return 0;
-}
-
-#ifdef TDB_TRACE
-static void tdb_trace_write(struct tdb_context *tdb, const char *str)
-{
- if (write(tdb->tracefd, str, strlen(str)) != strlen(str)) {
- close(tdb->tracefd);
- tdb->tracefd = -1;
- }
-}
-
-static void tdb_trace_start(struct tdb_context *tdb)
-{
- tdb_off_t seqnum=0;
- char msg[sizeof(tdb_off_t) * 4 + 1];
-
- tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
- snprintf(msg, sizeof(msg), "%u ", seqnum);
- tdb_trace_write(tdb, msg);
-}
-
-static void tdb_trace_end(struct tdb_context *tdb)
-{
- tdb_trace_write(tdb, "\n");
-}
-
-static void tdb_trace_end_ret(struct tdb_context *tdb, int ret)
-{
- char msg[sizeof(ret) * 4 + 4];
- snprintf(msg, sizeof(msg), " = %i\n", ret);
- tdb_trace_write(tdb, msg);
-}
-
-static void tdb_trace_record(struct tdb_context *tdb, TDB_DATA rec)
-{
- char msg[20 + rec.dsize*2], *p;
- unsigned int i;
-
- /* We differentiate zero-length records from non-existent ones. */
- if (rec.dptr == NULL) {
- tdb_trace_write(tdb, " NULL");
- return;
- }
-
- /* snprintf here is purely cargo-cult programming. */
- p = msg;
- p += snprintf(p, sizeof(msg), " %zu:", rec.dsize);
- for (i = 0; i < rec.dsize; i++)
- p += snprintf(p, 2, "%02x", rec.dptr[i]);
-
- tdb_trace_write(tdb, msg);
-}
-
-void tdb_trace(struct tdb_context *tdb, const char *op)
-{
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_end(tdb);
-}
-
-void tdb_trace_seqnum(struct tdb_context *tdb, uint32_t seqnum, const char *op)
-{
- char msg[sizeof(tdb_off_t) * 4 + 1];
-
- snprintf(msg, sizeof(msg), "%u ", seqnum);
- tdb_trace_write(tdb, msg);
- tdb_trace_write(tdb, op);
- tdb_trace_end(tdb);
-}
-
-void tdb_trace_open(struct tdb_context *tdb, const char *op,
- unsigned hash_size, unsigned tdb_flags, unsigned open_flags)
-{
- char msg[128];
-
- snprintf(msg, sizeof(msg),
- "%s %u 0x%x 0x%x", op, hash_size, tdb_flags, open_flags);
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, msg);
- tdb_trace_end(tdb);
-}
-
-void tdb_trace_ret(struct tdb_context *tdb, const char *op, int ret)
-{
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_end_ret(tdb, ret);
-}
-
-void tdb_trace_retrec(struct tdb_context *tdb, const char *op, TDB_DATA ret)
-{
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_write(tdb, " =");
- tdb_trace_record(tdb, ret);
- tdb_trace_end(tdb);
-}
-
-void tdb_trace_1rec(struct tdb_context *tdb, const char *op,
- TDB_DATA rec)
-{
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_record(tdb, rec);
- tdb_trace_end(tdb);
-}
-
-void tdb_trace_1rec_ret(struct tdb_context *tdb, const char *op,
- TDB_DATA rec, int ret)
-{
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_record(tdb, rec);
- tdb_trace_end_ret(tdb, ret);
-}
-
-void tdb_trace_1rec_retrec(struct tdb_context *tdb, const char *op,
- TDB_DATA rec, TDB_DATA ret)
-{
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_record(tdb, rec);
- tdb_trace_write(tdb, " =");
- tdb_trace_record(tdb, ret);
- tdb_trace_end(tdb);
-}
-
-void tdb_trace_2rec_flag_ret(struct tdb_context *tdb, const char *op,
- TDB_DATA rec1, TDB_DATA rec2, unsigned flag,
- int ret)
-{
- char msg[1 + sizeof(ret) * 4];
-
- snprintf(msg, sizeof(msg), " %#x", flag);
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_record(tdb, rec1);
- tdb_trace_record(tdb, rec2);
- tdb_trace_write(tdb, msg);
- tdb_trace_end_ret(tdb, ret);
-}
-
-void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op,
- TDB_DATA rec1, TDB_DATA rec2, TDB_DATA ret)
-{
- tdb_trace_start(tdb);
- tdb_trace_write(tdb, op);
- tdb_trace_record(tdb, rec1);
- tdb_trace_record(tdb, rec2);
- tdb_trace_write(tdb, " =");
- tdb_trace_record(tdb, ret);
- tdb_trace_end(tdb);
-}
-#endif
+++ /dev/null
-#ifndef __TDB_H__
-#define __TDB_H__
-
-/*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2004
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _SAMBA_BUILD_
-/* For mode_t */
-#include <sys/types.h>
-/* For O_* flags. */
-#include <sys/stat.h>
-/* For sig_atomic_t. */
-#include <signal.h>
-#endif
-#include <ccan/compiler/compiler.h>
-
-/* flags to tdb_store() */
-#define TDB_REPLACE 1 /* Unused */
-#define TDB_INSERT 2 /* Don't overwrite an existing entry */
-#define TDB_MODIFY 3 /* Don't create an existing entry */
-
-/* flags for tdb_open() */
-#define TDB_DEFAULT 0 /* just a readability place holder */
-#define TDB_CLEAR_IF_FIRST 1
-#define TDB_INTERNAL 2 /* don't store on disk */
-#define TDB_NOLOCK 4 /* don't do any locking */
-#define TDB_NOMMAP 8 /* don't use mmap */
-#define TDB_CONVERT 16 /* convert endian (internal use) */
-#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
-#define TDB_NOSYNC 64 /* don't use synchronous transactions */
-#define TDB_SEQNUM 128 /* maintain a sequence number */
-#define TDB_VOLATILE 256 /* Activate the per-hashchain freelist, default 5 */
-#define TDB_ALLOW_NESTING 512 /* Allow transactions to nest */
-#define TDB_DISALLOW_NESTING 1024 /* Disallow transactions to nest */
-#define TDB_INCOMPATIBLE_HASH 2048 /* Better hashing: can't be opened by older tdb versions. */
-
-/* error codes */
-enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK,
- TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
- TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY,
- TDB_ERR_NESTING};
-
-/* debugging uses one of the following levels */
-enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR,
- TDB_DEBUG_WARNING, TDB_DEBUG_TRACE};
-
-typedef struct TDB_DATA {
- unsigned char *dptr;
- size_t dsize;
-} TDB_DATA;
-
-/* this is the context structure that is returned from a db open */
-typedef struct tdb_context TDB_CONTEXT;
-
-typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *);
-typedef void (*tdb_log_func)(struct tdb_context *, enum tdb_debug_level, const char *, ...) PRINTF_FMT(3, 4);
-typedef unsigned int (*tdb_hash_func)(TDB_DATA *key);
-
-struct tdb_logging_context {
- tdb_log_func log_fn;
- void *log_private;
-};
-
-struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
- int open_flags, mode_t mode);
-struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
- int open_flags, mode_t mode,
- const struct tdb_logging_context *log_ctx,
- tdb_hash_func hash_fn);
-void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
-
-int tdb_reopen(struct tdb_context *tdb);
-int tdb_reopen_all(int parent_longlived);
-void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx);
-enum TDB_ERROR tdb_error(struct tdb_context *tdb);
-const char *tdb_errorstr(struct tdb_context *tdb);
-TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
-int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
- int (*parser)(TDB_DATA key, TDB_DATA data,
- void *private_data),
- void *private_data);
-int tdb_delete(struct tdb_context *tdb, TDB_DATA key);
-int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
-int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf);
-int tdb_close(struct tdb_context *tdb);
-TDB_DATA tdb_firstkey(struct tdb_context *tdb);
-TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key);
-int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *);
-int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *);
-int tdb_exists(struct tdb_context *tdb, TDB_DATA key);
-int tdb_lockall(struct tdb_context *tdb);
-int tdb_lockall_nonblock(struct tdb_context *tdb);
-int tdb_unlockall(struct tdb_context *tdb);
-int tdb_lockall_read(struct tdb_context *tdb);
-int tdb_lockall_read_nonblock(struct tdb_context *tdb);
-int tdb_unlockall_read(struct tdb_context *tdb);
-int tdb_lockall_mark(struct tdb_context *tdb);
-int tdb_lockall_unmark(struct tdb_context *tdb);
-const char *tdb_name(struct tdb_context *tdb);
-int tdb_fd(struct tdb_context *tdb);
-tdb_log_func tdb_log_fn(struct tdb_context *tdb);
-void *tdb_get_logging_private(struct tdb_context *tdb);
-int tdb_transaction_start(struct tdb_context *tdb);
-int tdb_transaction_prepare_commit(struct tdb_context *tdb);
-int tdb_transaction_commit(struct tdb_context *tdb);
-int tdb_transaction_cancel(struct tdb_context *tdb);
-int tdb_transaction_recover(struct tdb_context *tdb);
-int tdb_get_seqnum(struct tdb_context *tdb);
-int tdb_hash_size(struct tdb_context *tdb);
-size_t tdb_map_size(struct tdb_context *tdb);
-int tdb_get_flags(struct tdb_context *tdb);
-void tdb_add_flags(struct tdb_context *tdb, unsigned flag);
-void tdb_remove_flags(struct tdb_context *tdb, unsigned flag);
-void tdb_enable_seqnum(struct tdb_context *tdb);
-void tdb_increment_seqnum_nonblock(struct tdb_context *tdb);
-unsigned int tdb_jenkins_hash(TDB_DATA *key);
-int tdb_check(struct tdb_context *tdb,
- int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
- void *private_data);
-
-/* Low level locking functions: use with care */
-int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);
-int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key);
-int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
-int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
-int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
-int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
-int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
-
-void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
-
-/* wipe and repack */
-int tdb_wipe_all(struct tdb_context *tdb);
-int tdb_repack(struct tdb_context *tdb);
-
-/* Debug functions. Not used in production. */
-void tdb_dump_all(struct tdb_context *tdb);
-int tdb_printfreelist(struct tdb_context *tdb);
-int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries);
-int tdb_freelist_size(struct tdb_context *tdb);
-char *tdb_summary(struct tdb_context *tdb);
-
-extern TDB_DATA tdb_null;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* tdb.h */
+++ /dev/null
-#ifndef TDB_PRIVATE_H
-#define TDB_PRIVATE_H
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library - private includes
-
- Copyright (C) Andrew Tridgell 2005
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifdef _SAMBA_BUILD_
-#include "replace.h"
-#include "system/filesys.h"
-#include "system/time.h"
-#include "system/shmem.h"
-#include "system/select.h"
-#include "system/wait.h"
-#else
-#include "config.h"
-#define _FILE_OFFSET_BITS 64
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <utime.h>
-#endif
-#include "tdb.h"
-
-/* #define TDB_TRACE 1 */
-
-#ifndef __STRING
-#define __STRING(x) #x
-#endif
-
-#ifndef __STRINGSTRING
-#define __STRINGSTRING(x) __STRING(x)
-#endif
-
-#ifndef __location__
-#define __location__ __FILE__ ":" __STRINGSTRING(__LINE__)
-#endif
-
-#if !HAVE_GETPAGESIZE
-#define getpagesize() 0x2000
-#endif
-
-typedef uint32_t tdb_len_t;
-typedef uint32_t tdb_off_t;
-
-#ifndef offsetof
-#define offsetof(t,f) ((size_t)&((t *)0)->f)
-#endif
-
-#define TDB_MAGIC_FOOD "TDB file\n"
-#define TDB_VERSION (0x26011967 + 6)
-#define TDB_MAGIC (0x26011999U)
-#define TDB_FREE_MAGIC (~TDB_MAGIC)
-#define TDB_DEAD_MAGIC (0xFEE1DEAD)
-#define TDB_RECOVERY_MAGIC (0xf53bc0e7U)
-#define TDB_RECOVERY_INVALID_MAGIC (0x0)
-#define TDB_HASH_RWLOCK_MAGIC (0xbad1a51U)
-#define TDB_ALIGNMENT 4
-#define DEFAULT_HASH_SIZE 131
-#define FREELIST_TOP (sizeof(struct tdb_header))
-#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
-#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
-#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
-#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
-#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off_t))
-#define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t))
-#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + sizeof(tdb_off_t))
-#define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start)
-#define TDB_SEQNUM_OFS offsetof(struct tdb_header, sequence_number)
-#define TDB_PAD_BYTE 0x42
-#define TDB_PAD_U32 0x42424242
-
-/* NB assumes there is a local variable called "tdb" that is the
- * current context, also takes doubly-parenthesized print-style
- * argument. */
-#define TDB_LOG(x) tdb->log.log_fn x
-
-#ifdef TDB_TRACE
-void tdb_trace(struct tdb_context *tdb, const char *op);
-void tdb_trace_seqnum(struct tdb_context *tdb, uint32_t seqnum, const char *op);
-void tdb_trace_open(struct tdb_context *tdb, const char *op,
- unsigned hash_size, unsigned tdb_flags, unsigned open_flags);
-void tdb_trace_ret(struct tdb_context *tdb, const char *op, int ret);
-void tdb_trace_retrec(struct tdb_context *tdb, const char *op, TDB_DATA ret);
-void tdb_trace_1rec(struct tdb_context *tdb, const char *op,
- TDB_DATA rec);
-void tdb_trace_1rec_ret(struct tdb_context *tdb, const char *op,
- TDB_DATA rec, int ret);
-void tdb_trace_1rec_retrec(struct tdb_context *tdb, const char *op,
- TDB_DATA rec, TDB_DATA ret);
-void tdb_trace_2rec_flag_ret(struct tdb_context *tdb, const char *op,
- TDB_DATA rec1, TDB_DATA rec2, unsigned flag,
- int ret);
-void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op,
- TDB_DATA rec1, TDB_DATA rec2, TDB_DATA ret);
-#else
-#define tdb_trace(tdb, op)
-#define tdb_trace_seqnum(tdb, seqnum, op)
-#define tdb_trace_open(tdb, op, hash_size, tdb_flags, open_flags)
-#define tdb_trace_ret(tdb, op, ret)
-#define tdb_trace_retrec(tdb, op, ret)
-#define tdb_trace_1rec(tdb, op, rec)
-#define tdb_trace_1rec_ret(tdb, op, rec, ret)
-#define tdb_trace_1rec_retrec(tdb, op, rec, ret)
-#define tdb_trace_2rec_flag_ret(tdb, op, rec1, rec2, flag, ret)
-#define tdb_trace_2rec_retrec(tdb, op, rec1, rec2, ret)
-#endif /* !TDB_TRACE */
-
-/* lock offsets */
-#define OPEN_LOCK 0
-#define ACTIVE_LOCK 4
-#define TRANSACTION_LOCK 8
-
-/* free memory if the pointer is valid and zero the pointer */
-#ifndef SAFE_FREE
-#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
-#endif
-
-#define BUCKET(hash) ((hash) % tdb->header.hash_size)
-
-#define DOCONV() (tdb->flags & TDB_CONVERT)
-#define CONVERT(x) (DOCONV() ? tdb_convert(&x, sizeof(x)) : &x)
-
-
-/* the body of the database is made of one tdb_record for the free space
- plus a separate data list for each hash value */
-struct tdb_record {
- tdb_off_t next; /* offset of the next record in the list */
- tdb_len_t rec_len; /* total byte length of record */
- tdb_len_t key_len; /* byte length of key */
- tdb_len_t data_len; /* byte length of data */
- uint32_t full_hash; /* the full 32 bit hash of the key */
- uint32_t magic; /* try to catch errors */
- /* the following union is implied:
- union {
- char record[rec_len];
- struct {
- char key[key_len];
- char data[data_len];
- }
- uint32_t totalsize; (tailer)
- }
- */
-};
-
-
-/* this is stored at the front of every database */
-struct tdb_header {
- char magic_food[32]; /* for /etc/magic */
- uint32_t version; /* version of the code */
- uint32_t hash_size; /* number of hash entries */
- tdb_off_t rwlocks; /* obsolete - kept to detect old formats */
- tdb_off_t recovery_start; /* offset of transaction recovery region */
- tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */
- uint32_t magic1_hash; /* hash of TDB_MAGIC_FOOD. */
- uint32_t magic2_hash; /* hash of TDB_MAGIC. */
- tdb_off_t reserved[27];
-};
-
-struct tdb_lock_type {
- uint32_t off;
- uint32_t count;
- uint32_t ltype;
-};
-
-struct tdb_traverse_lock {
- struct tdb_traverse_lock *next;
- uint32_t off;
- uint32_t hash;
- int lock_rw;
-};
-
-enum tdb_lock_flags {
- /* WAIT == F_SETLKW, NOWAIT == F_SETLK */
- TDB_LOCK_NOWAIT = 0,
- TDB_LOCK_WAIT = 1,
- /* If set, don't log an error on failure. */
- TDB_LOCK_PROBE = 2,
- /* If set, don't actually lock at all. */
- TDB_LOCK_MARK_ONLY = 4,
-};
-
-struct tdb_methods {
- int (*tdb_read)(struct tdb_context *, tdb_off_t , void *, tdb_len_t , int );
- int (*tdb_write)(struct tdb_context *, tdb_off_t, const void *, tdb_len_t);
- void (*next_hash_chain)(struct tdb_context *, uint32_t *);
- int (*tdb_oob)(struct tdb_context *, tdb_off_t , int );
- int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t );
-};
-
-struct tdb_context {
- char *name; /* the name of the database */
- void *map_ptr; /* where it is currently mapped */
- int fd; /* open file descriptor for the database */
- tdb_len_t map_size; /* how much space has been mapped */
- int read_only; /* opened read-only */
- int traverse_read; /* read-only traversal */
- int traverse_write; /* read-write traversal */
- struct tdb_lock_type allrecord_lock; /* .offset == upgradable */
- int num_lockrecs;
- struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */
- enum TDB_ERROR ecode; /* error code for last tdb error */
- struct tdb_header header; /* a cached copy of the header */
- uint32_t flags; /* the flags passed to tdb_open */
- struct tdb_traverse_lock travlocks; /* current traversal locks */
- struct tdb_context *next; /* all tdbs to avoid multiple opens */
- dev_t device; /* uniquely identifies this tdb */
- ino_t inode; /* uniquely identifies this tdb */
- struct tdb_logging_context log;
- unsigned int (*hash_fn)(TDB_DATA *key);
- int open_flags; /* flags used in the open - needed by reopen */
- const struct tdb_methods *methods;
- struct tdb_transaction *transaction;
- int page_size;
- int max_dead_records;
-#ifdef TDB_TRACE
- int tracefd;
-#endif
- volatile sig_atomic_t *interrupt_sig_ptr;
-};
-
-
-/*
- internal prototypes
-*/
-int tdb_munmap(struct tdb_context *tdb);
-void tdb_mmap(struct tdb_context *tdb);
-int tdb_lock(struct tdb_context *tdb, int list, int ltype);
-int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype);
-int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype,
- enum tdb_lock_flags flags);
-int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype,
- bool mark_lock);
-bool tdb_have_locks(struct tdb_context *tdb);
-int tdb_unlock(struct tdb_context *tdb, int list, int ltype);
-int tdb_brlock(struct tdb_context *tdb,
- int rw_type, tdb_off_t offset, size_t len,
- enum tdb_lock_flags flags);
-int tdb_brunlock(struct tdb_context *tdb,
- int rw_type, tdb_off_t offset, size_t len);
-bool tdb_have_extra_locks(struct tdb_context *tdb);
-void tdb_release_extra_locks(struct tdb_context *tdb);
-int tdb_transaction_lock(struct tdb_context *tdb, int ltype);
-int tdb_transaction_unlock(struct tdb_context *tdb, int ltype);
-int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
- enum tdb_lock_flags flags, bool upgradable);
-int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock);
-int tdb_allrecord_upgrade(struct tdb_context *tdb);
-int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off);
-int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off);
-int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
-int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
-void *tdb_convert(void *buf, uint32_t size);
-int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec);
-tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_record *rec);
-int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
-int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
-int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off);
-int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off);
-bool tdb_needs_recovery(struct tdb_context *tdb);
-int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec);
-int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec);
-int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct tdb_record *rec);
-unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len);
-int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
- tdb_off_t offset, tdb_len_t len,
- int (*parser)(TDB_DATA key, TDB_DATA data,
- void *private_data),
- void *private_data);
-tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype,
- struct tdb_record *rec);
-void tdb_io_init(struct tdb_context *tdb);
-int tdb_expand(struct tdb_context *tdb, tdb_off_t size);
-tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size, int page_size);
-int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off,
- struct tdb_record *rec);
-void tdb_header_hash(struct tdb_context *tdb,
- uint32_t *magic1_hash, uint32_t *magic2_hash);
-unsigned int tdb_old_hash(TDB_DATA *key);
-size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off);
-#endif
+++ /dev/null
-#include "external-agent.h"
-#include "lock-tracking.h"
-#include "logging.h"
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <err.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-#include <errno.h>
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/tdb_private.h>
-#include <ccan/tap/tap.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-static struct tdb_context *tdb;
-
-static enum agent_return do_operation(enum operation op, const char *name)
-{
- TDB_DATA k;
- enum agent_return ret;
- TDB_DATA data;
-
- if (op != OPEN && op != OPEN_WITH_CLEAR_IF_FIRST && !tdb) {
- diag("external: No tdb open!");
- return OTHER_FAILURE;
- }
-
- k.dptr = (void *)name;
- k.dsize = strlen(name);
-
- locking_would_block = 0;
- switch (op) {
- case OPEN:
- if (tdb) {
- diag("Already have tdb %s open", tdb_name(tdb));
- return OTHER_FAILURE;
- }
- tdb = tdb_open_ex(name, 0, TDB_DEFAULT, O_RDWR, 0,
- &taplogctx, NULL);
- if (!tdb) {
- if (!locking_would_block)
- diag("Opening tdb gave %s", strerror(errno));
- ret = OTHER_FAILURE;
- } else
- ret = SUCCESS;
- break;
- case OPEN_WITH_CLEAR_IF_FIRST:
- if (tdb)
- return OTHER_FAILURE;
- tdb = tdb_open_ex(name, 0, TDB_CLEAR_IF_FIRST, O_RDWR, 0,
- &taplogctx, NULL);
- ret = tdb ? SUCCESS : OTHER_FAILURE;
- break;
- case TRANSACTION_START:
- ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
- break;
- case FETCH:
- data = tdb_fetch(tdb, k);
- if (data.dptr == NULL) {
- if (tdb_error(tdb) == TDB_ERR_NOEXIST)
- ret = FAILED;
- else
- ret = OTHER_FAILURE;
- } else if (data.dsize != k.dsize
- || memcmp(data.dptr, k.dptr, k.dsize) != 0) {
- ret = OTHER_FAILURE;
- } else {
- ret = SUCCESS;
- }
- free(data.dptr);
- break;
- case STORE:
- ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
- break;
- case TRANSACTION_COMMIT:
- ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE;
- break;
- case CHECK:
- ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
- break;
- case NEEDS_RECOVERY:
- ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
- break;
- case CLOSE:
- ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
- tdb = NULL;
- break;
- default:
- ret = OTHER_FAILURE;
- }
-
- if (locking_would_block)
- ret = WOULD_HAVE_BLOCKED;
-
- return ret;
-}
-
-struct agent {
- int cmdfd, responsefd;
-};
-
-/* Do this before doing any tdb stuff. Return handle, or NULL. */
-struct agent *prepare_external_agent(void)
-{
- int pid, ret;
- int command[2], response[2];
- char name[1+PATH_MAX];
-
- if (pipe(command) != 0 || pipe(response) != 0)
- return NULL;
-
- pid = fork();
- if (pid < 0)
- return NULL;
-
- if (pid != 0) {
- struct agent *agent = malloc(sizeof(*agent));
-
- close(command[0]);
- close(response[1]);
- agent->cmdfd = command[1];
- agent->responsefd = response[0];
- return agent;
- }
-
- close(command[1]);
- close(response[0]);
-
- /* We want to fail, not block. */
- nonblocking_locks = true;
- log_prefix = "external: ";
- while ((ret = read(command[0], name, sizeof(name))) > 0) {
- enum agent_return result;
-
- result = do_operation(name[0], name+1);
- if (write(response[1], &result, sizeof(result))
- != sizeof(result))
- err(1, "Writing response");
- }
- exit(0);
-}
-
-/* Ask the external agent to try to do an operation. */
-enum agent_return external_agent_operation(struct agent *agent,
- enum operation op,
- const char *name)
-{
- enum agent_return res;
- unsigned int len;
- char *string;
-
- if (!name)
- name = "";
- len = 1 + strlen(name) + 1;
- string = malloc(len);
-
- string[0] = op;
- strcpy(string+1, name);
-
- if (write(agent->cmdfd, string, len) != len
- || read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
- res = AGENT_DIED;
-
- free(string);
- return res;
-}
-
-const char *agent_return_name(enum agent_return ret)
-{
- return ret == SUCCESS ? "SUCCESS"
- : ret == WOULD_HAVE_BLOCKED ? "WOULD_HAVE_BLOCKED"
- : ret == AGENT_DIED ? "AGENT_DIED"
- : ret == FAILED ? "FAILED"
- : ret == OTHER_FAILURE ? "OTHER_FAILURE"
- : "**INVALID**";
-}
-
-const char *operation_name(enum operation op)
-{
- switch (op) {
- case OPEN: return "OPEN";
- case OPEN_WITH_CLEAR_IF_FIRST: return "OPEN_WITH_CLEAR_IF_FIRST";
- case TRANSACTION_START: return "TRANSACTION_START";
- case FETCH: return "FETCH";
- case STORE: return "STORE";
- case TRANSACTION_COMMIT: return "TRANSACTION_COMMIT";
- case CHECK: return "CHECK";
- case NEEDS_RECOVERY: return "NEEDS_RECOVERY";
- case CLOSE: return "CLOSE";
- }
- return "**INVALID**";
-}
+++ /dev/null
-#ifndef TDB_TEST_EXTERNAL_AGENT_H
-#define TDB_TEST_EXTERNAL_AGENT_H
-
-/* For locking tests, we need a different process to try things at
- * various times. */
-enum operation {
- OPEN,
- OPEN_WITH_CLEAR_IF_FIRST,
- TRANSACTION_START,
- FETCH,
- STORE,
- TRANSACTION_COMMIT,
- CHECK,
- NEEDS_RECOVERY,
- CLOSE,
-};
-
-/* Do this before doing any tdb stuff. Return handle, or -1. */
-struct agent *prepare_external_agent(void);
-
-enum agent_return {
- SUCCESS,
- WOULD_HAVE_BLOCKED,
- AGENT_DIED,
- FAILED, /* For fetch, or NEEDS_RECOVERY */
- OTHER_FAILURE,
-};
-
-/* Ask the external agent to try to do an operation.
- * name == tdb name for OPEN/OPEN_WITH_CLEAR_IF_FIRST,
- * record name for FETCH/STORE (store stores name as data too)
- */
-enum agent_return external_agent_operation(struct agent *handle,
- enum operation op,
- const char *name);
-
-/* Mapping enum -> string. */
-const char *agent_return_name(enum agent_return ret);
-const char *operation_name(enum operation op);
-
-#endif /* TDB_TEST_EXTERNAL_AGENT_H */
+++ /dev/null
-/* We save the locks so we can reaquire them. */
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <ccan/tap/tap.h>
-#include <ccan/tdb/tdb_private.h>
-#include "lock-tracking.h"
-
-struct lock {
- struct lock *next;
- unsigned int off;
- unsigned int len;
- int type;
-};
-static struct lock *locks;
-int locking_errors = 0;
-bool suppress_lockcheck = false;
-bool nonblocking_locks;
-int locking_would_block = 0;
-void (*unlock_callback)(int fd);
-
-int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
-{
- va_list ap;
- int ret, arg3;
- struct flock *fl;
- bool may_block = false;
-
- if (cmd != F_SETLK && cmd != F_SETLKW) {
- /* This may be totally bogus, but we don't know in general. */
- va_start(ap, cmd);
- arg3 = va_arg(ap, int);
- va_end(ap);
-
- return fcntl(fd, cmd, arg3);
- }
-
- va_start(ap, cmd);
- fl = va_arg(ap, struct flock *);
- va_end(ap);
-
- if (cmd == F_SETLKW && nonblocking_locks) {
- cmd = F_SETLK;
- may_block = true;
- }
- ret = fcntl(fd, cmd, fl);
-
- /* Detect when we failed, but might have been OK if we waited. */
- if (may_block && ret == -1 && (errno == EAGAIN || errno == EACCES)) {
- locking_would_block++;
- }
-
- if (fl->l_type == F_UNLCK) {
- struct lock **l;
- struct lock *old = NULL;
-
- for (l = &locks; *l; l = &(*l)->next) {
- if ((*l)->off == fl->l_start
- && (*l)->len == fl->l_len) {
- if (ret == 0) {
- old = *l;
- *l = (*l)->next;
- free(old);
- }
- break;
- }
- }
- if (!old && !suppress_lockcheck) {
- diag("Unknown unlock %u@%u - %i",
- (int)fl->l_len, (int)fl->l_start, ret);
- locking_errors++;
- }
- } else {
- struct lock *new, *i;
- unsigned int fl_end = fl->l_start + fl->l_len;
- if (fl->l_len == 0)
- fl_end = (unsigned int)-1;
-
- /* Check for overlaps: we shouldn't do this. */
- for (i = locks; i; i = i->next) {
- unsigned int i_end = i->off + i->len;
- if (i->len == 0)
- i_end = (unsigned int)-1;
-
- if (fl->l_start >= i->off && fl->l_start < i_end)
- break;
- if (fl_end >= i->off && fl_end < i_end)
- break;
-
- /* tdb_allrecord_lock does this, handle adjacent: */
- if (fl->l_start == i_end && fl->l_type == i->type) {
- if (ret == 0) {
- i->len = fl->l_len
- ? i->len + fl->l_len
- : 0;
- }
- goto done;
- }
- }
- if (i) {
- /* Special case: upgrade of allrecord lock. */
- if (i->type == F_RDLCK && fl->l_type == F_WRLCK
- && i->off == FREELIST_TOP
- && fl->l_start == FREELIST_TOP
- && i->len == 0
- && fl->l_len == 0) {
- if (ret == 0)
- i->type = F_WRLCK;
- goto done;
- }
- if (!suppress_lockcheck) {
- diag("%s lock %u@%u overlaps %u@%u",
- fl->l_type == F_WRLCK ? "write" : "read",
- (int)fl->l_len, (int)fl->l_start,
- i->len, (int)i->off);
- locking_errors++;
- }
- }
-
- if (ret == 0) {
- new = malloc(sizeof *new);
- new->off = fl->l_start;
- new->len = fl->l_len;
- new->type = fl->l_type;
- new->next = locks;
- locks = new;
- }
- }
-done:
- if (ret == 0 && fl->l_type == F_UNLCK && unlock_callback)
- unlock_callback(fd);
- return ret;
-}
-
-unsigned int forget_locking(void)
-{
- unsigned int num = 0;
- while (locks) {
- struct lock *next = locks->next;
- free(locks);
- locks = next;
- num++;
- }
- return num;
-}
+++ /dev/null
-#ifndef LOCK_TRACKING_H
-#define LOCK_TRACKING_H
-#include <stdbool.h>
-
-/* Set this if you want a callback after fnctl unlock. */
-extern void (*unlock_callback)(int fd);
-
-/* Replacement fcntl. */
-int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ );
-
-/* Discard locking info: returns number of locks outstanding. */
-unsigned int forget_locking(void);
-
-/* Number of errors in locking. */
-extern int locking_errors;
-
-/* Suppress lock checking. */
-extern bool suppress_lockcheck;
-
-/* Make all locks non-blocking. */
-extern bool nonblocking_locks;
-
-/* Number of times we failed a lock because we made it non-blocking. */
-extern int locking_would_block;
-#endif /* LOCK_TRACKING_H */
+++ /dev/null
-#include "logging.h"
-#include <ccan/tap/tap.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-bool suppress_logging = false;
-const char *log_prefix = "";
-
-/* Turn log messages into tap diag messages. */
-static void taplog(struct tdb_context *tdb,
- enum tdb_debug_level level,
- const char *fmt, ...)
-{
- va_list ap;
- char line[200];
-
- if (suppress_logging)
- return;
-
- va_start(ap, fmt);
- vsprintf(line, fmt, ap);
- va_end(ap);
-
- /* Strip trailing \n: diag adds it. */
- if (line[0] && line[strlen(line)-1] == '\n')
- diag("%s%.*s", log_prefix, (unsigned)strlen(line)-1, line);
- else
- diag("%s%s", log_prefix, line);
-}
-
-struct tdb_logging_context taplogctx = { taplog, NULL };
+++ /dev/null
-#ifndef TDB_TEST_LOGGING_H
-#define TDB_TEST_LOGGING_H
-#include <ccan/tdb/tdb.h>
-#include <stdbool.h>
-
-extern bool suppress_logging;
-extern const char *log_prefix;
-extern struct tdb_logging_context taplogctx;
-
-#endif /* TDB_TEST_LOGGING_H */
+++ /dev/null
-/* We need this otherwise fcntl locking fails. */
-#define _FILE_OFFSET_BITS 64
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-static int tdb_expand_file_sparse(struct tdb_context *tdb,
- tdb_off_t size,
- tdb_off_t addition)
-{
- if (tdb->read_only || tdb->traverse_read) {
- tdb->ecode = TDB_ERR_RDONLY;
- return -1;
- }
-
- if (ftruncate(tdb->fd, size+addition) == -1) {
- char b = 0;
- ssize_t written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
- if (written == 0) {
- /* try once more, potentially revealing errno */
- written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
- }
- if (written == 0) {
- /* again - give up, guessing errno */
- errno = ENOSPC;
- }
- if (written != 1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n",
- size+addition, strerror(errno)));
- return -1;
- }
- }
-
- return 0;
-}
-
-static const struct tdb_methods large_io_methods = {
- tdb_read,
- tdb_write,
- tdb_next_hash_chain,
- tdb_oob,
- tdb_expand_file_sparse
-};
-
-static int test_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
- void *_data)
-{
- TDB_DATA *expect = _data;
- ok1(key.dsize == strlen("hi"));
- ok1(memcmp(key.dptr, "hi", strlen("hi")) == 0);
- ok1(data.dsize == expect->dsize);
- ok1(memcmp(data.dptr, expect->dptr, data.dsize) == 0);
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, orig_data, data;
- uint32_t hash;
- tdb_off_t rec_ptr;
- struct tdb_record rec;
-
- plan_tests(24);
- tdb = tdb_open_ex("run-36-file.tdb", 1024, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- ok1(tdb);
- tdb->methods = &large_io_methods;
-
- /* Enlarge the file (internally multiplies by 100). */
- ok1(tdb_expand(tdb, 30000000) == 0);
-
- /* Put an entry in, and check it. */
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- orig_data.dsize = strlen("world");
- orig_data.dptr = (void *)"world";
-
- ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == 0);
-
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
-
- /* That currently fills at the end, make sure that's true. */
- hash = tdb->hash_fn(&key);
- rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec);
- ok1(rec_ptr);
- ok1(rec_ptr > 2U*1024*1024*1024);
- tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
-
- /* Traverse must work. */
- ok1(tdb_traverse(tdb, test_traverse, &orig_data) == 1);
-
- /* Delete should work. */
- ok1(tdb_delete(tdb, key) == 0);
-
- ok1(tdb_traverse(tdb, test_traverse, NULL) == 0);
-
- /* Transactions should work. */
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == 0);
-
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
- ok1(tdb_transaction_commit(tdb) == 0);
-
- ok1(tdb_traverse(tdb, test_traverse, &orig_data) == 1);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- struct tdb_header hdr;
- int fd;
-
- plan_tests(11);
- /* Can open fine if complete crap, as long as O_CREAT. */
- fd = open("run-bad-tdb-header.tdb", O_RDWR|O_CREAT|O_TRUNC, 0600);
- ok1(fd >= 0);
- ok1(write(fd, "hello world", 11) == 11);
- close(fd);
- tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(!tdb);
- tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_CREAT|O_RDWR,
- 0600, &taplogctx, NULL);
- ok1(tdb);
- tdb_close(tdb);
-
- /* Now, with wrong version it should *not* overwrite. */
- fd = open("run-bad-tdb-header.tdb", O_RDWR);
- ok1(fd >= 0);
- ok1(read(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
- ok1(hdr.version == TDB_VERSION);
- hdr.version++;
- lseek(fd, 0, SEEK_SET);
- ok1(write(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
- close(fd);
-
- tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_RDWR|O_CREAT,
- 0600, &taplogctx, NULL);
- ok1(errno == EIO);
- ok1(!tdb);
-
- /* With truncate, will be fine. */
- tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0,
- O_RDWR|O_CREAT|O_TRUNC, 0600, &taplogctx, NULL);
- ok1(tdb);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(13);
- tdb = tdb_open_ex("run-check.tdb", 1, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dsize = strlen("world");
- data.dptr = (void *)"world";
-
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- tdb = tdb_open_ex("run-check.tdb", 1024, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- tdb = tdb_open_ex("test/tdb.corrupt", 1024, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == -1);
- ok1(tdb_error(tdb) == TDB_ERR_CORRUPT);
- tdb_close(tdb);
-
- /* Big and little endian should work! */
- tdb = tdb_open_ex("test/old-nohash-le.tdb", 1024, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- tdb = tdb_open_ex("test/old-nohash-be.tdb", 1024, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-static int check(TDB_DATA key, TDB_DATA data, void *private)
-{
- unsigned int *sizes = private;
-
- if (key.dsize > strlen("hello"))
- return -1;
- if (memcmp(key.dptr, "hello", key.dsize) != 0)
- return -1;
-
- if (data.dsize != strlen("world"))
- return -1;
- if (memcmp(data.dptr, "world", data.dsize) != 0)
- return -1;
-
- sizes[0] += key.dsize;
- sizes[1] += data.dsize;
- return 0;
-}
-
-static void tdb_flip_bit(struct tdb_context *tdb, unsigned int bit)
-{
- unsigned int off = bit / CHAR_BIT;
- unsigned char mask = (1 << (bit % CHAR_BIT));
-
- if (tdb->map_ptr)
- ((unsigned char *)tdb->map_ptr)[off] ^= mask;
- else {
- unsigned char c;
- if (pread(tdb->fd, &c, 1, off) != 1)
- err(1, "pread");
- c ^= mask;
- if (pwrite(tdb->fd, &c, 1, off) != 1)
- err(1, "pwrite");
- }
-}
-
-static void check_test(struct tdb_context *tdb)
-{
- TDB_DATA key, data;
- unsigned int i, verifiable, corrupt, sizes[2], dsize, ksize;
-
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- key.dptr = (void *)"hello";
- data.dsize = strlen("world");
- data.dptr = (void *)"world";
-
- /* Key and data size respectively. */
- dsize = ksize = 0;
-
- /* 5 keys in hash size 2 means we'll have multichains. */
- for (key.dsize = 1; key.dsize <= 5; key.dsize++) {
- ksize += key.dsize;
- dsize += data.dsize;
- if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
- abort();
- }
-
- /* This is how many bytes we expect to be verifiable. */
- /* From the file header. */
- verifiable = strlen(TDB_MAGIC_FOOD) + 1
- + 2 * sizeof(uint32_t) + 2 * sizeof(tdb_off_t)
- + 2 * sizeof(uint32_t);
- /* From the free list chain and hash chains. */
- verifiable += 3 * sizeof(tdb_off_t);
- /* From the record headers & tailer */
- verifiable += 5 * (sizeof(struct tdb_record) + sizeof(uint32_t));
- /* The free block: we ignore datalen, keylen, full_hash. */
- verifiable += sizeof(struct tdb_record) - 3*sizeof(uint32_t) +
- sizeof(uint32_t);
- /* Our check function verifies the key and data. */
- verifiable += ksize + dsize;
-
- /* Flip one bit at a time, make sure it detects verifiable bytes. */
- for (i = 0, corrupt = 0; i < tdb->map_size * CHAR_BIT; i++) {
- tdb_flip_bit(tdb, i);
- memset(sizes, 0, sizeof(sizes));
- if (tdb_check(tdb, check, sizes) != 0)
- corrupt++;
- else if (sizes[0] != ksize || sizes[1] != dsize)
- corrupt++;
- tdb_flip_bit(tdb, i);
- }
- ok(corrupt == verifiable * CHAR_BIT, "corrupt %u should be %u",
- corrupt, verifiable * CHAR_BIT);
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
-
- plan_tests(4);
- /* This should use mmap. */
- tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- if (!tdb)
- abort();
- check_test(tdb);
- tdb_close(tdb);
-
- /* This should not. */
- tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST|TDB_NOMMAP,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- if (!tdb)
- abort();
- check_test(tdb);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <unistd.h>
-#include "lock-tracking.h"
-static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
-static ssize_t write_check(int fd, const void *buf, size_t count);
-static int ftruncate_check(int fd, off_t length);
-
-#define pwrite pwrite_check
-#define write write_check
-#define fcntl fcntl_with_lockcheck
-#define ftruncate ftruncate_check
-
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <err.h>
-#include <setjmp.h>
-#include "external-agent.h"
-#include "logging.h"
-
-#undef write
-#undef pwrite
-#undef fcntl
-#undef ftruncate
-
-static bool in_transaction;
-static int target, current;
-static jmp_buf jmpbuf;
-#define TEST_DBNAME "run-die-during-transaction.tdb"
-#define KEY_STRING "helloworld"
-
-static void maybe_die(int fd)
-{
- if (in_transaction && current++ == target) {
- longjmp(jmpbuf, 1);
- }
-}
-
-static ssize_t pwrite_check(int fd,
- const void *buf, size_t count, off_t offset)
-{
- ssize_t ret;
-
- maybe_die(fd);
-
- ret = pwrite(fd, buf, count, offset);
- if (ret != count)
- return ret;
-
- maybe_die(fd);
- return ret;
-}
-
-static ssize_t write_check(int fd, const void *buf, size_t count)
-{
- ssize_t ret;
-
- maybe_die(fd);
-
- ret = write(fd, buf, count);
- if (ret != count)
- return ret;
-
- maybe_die(fd);
- return ret;
-}
-
-static int ftruncate_check(int fd, off_t length)
-{
- int ret;
-
- maybe_die(fd);
-
- ret = ftruncate(fd, length);
-
- maybe_die(fd);
- return ret;
-}
-
-static bool test_death(enum operation op, struct agent *agent)
-{
- struct tdb_context *tdb = NULL;
- TDB_DATA key;
- enum agent_return ret;
- int needed_recovery = 0;
-
- current = target = 0;
-reset:
- unlink(TEST_DBNAME);
- tdb = tdb_open_ex(TEST_DBNAME, 1024, TDB_NOMMAP,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- if (setjmp(jmpbuf) != 0) {
- /* We're partway through. Simulate our death. */
- close(tdb->fd);
- forget_locking();
- in_transaction = false;
-
- ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
- if (ret == SUCCESS)
- needed_recovery++;
- else if (ret != FAILED) {
- diag("Step %u agent NEEDS_RECOVERY = %s", current,
- agent_return_name(ret));
- return false;
- }
-
- ret = external_agent_operation(agent, op, KEY_STRING);
- if (ret != SUCCESS) {
- diag("Step %u op %s failed = %s", current,
- operation_name(op),
- agent_return_name(ret));
- return false;
- }
-
- ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
- if (ret != FAILED) {
- diag("Still needs recovery after step %u = %s",
- current, agent_return_name(ret));
- return false;
- }
-
- ret = external_agent_operation(agent, CHECK, "");
- if (ret != SUCCESS) {
- diag("Step %u check failed = %s", current,
- agent_return_name(ret));
- return false;
- }
-
- ret = external_agent_operation(agent, CLOSE, "");
- if (ret != SUCCESS) {
- diag("Step %u close failed = %s", current,
- agent_return_name(ret));
- return false;
- }
-
- /* Suppress logging as this tries to use closed fd. */
- suppress_logging = true;
- suppress_lockcheck = true;
- tdb_close(tdb);
- suppress_logging = false;
- suppress_lockcheck = false;
- target++;
- current = 0;
- goto reset;
- }
-
- /* Put key for agent to fetch. */
- key.dsize = strlen(KEY_STRING);
- key.dptr = (void *)KEY_STRING;
- if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
- return false;
-
- /* This is the key we insert in transaction. */
- key.dsize--;
-
- ret = external_agent_operation(agent, OPEN, TEST_DBNAME);
- if (ret != SUCCESS)
- errx(1, "Agent failed to open: %s", agent_return_name(ret));
-
- ret = external_agent_operation(agent, FETCH, KEY_STRING);
- if (ret != SUCCESS)
- errx(1, "Agent failed find key: %s", agent_return_name(ret));
-
- in_transaction = true;
- if (tdb_transaction_start(tdb) != 0)
- return false;
-
- if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
- return false;
-
- if (tdb_transaction_commit(tdb) != 0)
- return false;
-
- in_transaction = false;
-
- /* We made it! */
- diag("Completed %u runs", current);
- tdb_close(tdb);
- ret = external_agent_operation(agent, CLOSE, "");
- if (ret != SUCCESS) {
- diag("Step %u close failed = %s", current,
- agent_return_name(ret));
- return false;
- }
-
- ok1(needed_recovery);
- ok1(locking_errors == 0);
- ok1(forget_locking() == 0);
- locking_errors = 0;
- return true;
-}
-
-int main(int argc, char *argv[])
-{
- enum operation ops[] = { FETCH, STORE, TRANSACTION_START };
- struct agent *agent;
- int i;
-
- plan_tests(12);
- unlock_callback = maybe_die;
-
- agent = prepare_external_agent();
- if (!agent)
- err(1, "preparing agent");
-
- for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
- diag("Testing %s after death", operation_name(ops[i]));
- ok1(test_death(ops[i], agent));
- }
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(13);
- tdb = tdb_open_ex("run-endian.tdb", 1024,
- TDB_CLEAR_IF_FIRST|TDB_CONVERT,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- ok1(tdb);
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dsize = strlen("world");
- data.dptr = (void *)"world";
-
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) < 0);
- ok1(tdb_error(tdb) == TDB_ERR_NOEXIST);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) < 0);
- ok1(tdb_error(tdb) == TDB_ERR_EXISTS);
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0);
-
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
-
- key.dsize++;
- data = tdb_fetch(tdb, key);
- ok1(data.dptr == NULL);
- tdb_close(tdb);
-
- /* Reopen: should read it */
- tdb = tdb_open_ex("run-endian.tdb", 1024, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(tdb);
-
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-
-static unsigned int tdb_dumb_hash(TDB_DATA *key)
-{
- return key->dsize;
-}
-
-static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
-{
- unsigned int *count = tdb_get_logging_private(tdb);
- if (strstr(fmt, "hash"))
- (*count)++;
-}
-
-static unsigned int hdr_rwlocks(const char *fname)
-{
- struct tdb_header hdr;
-
- int fd = open(fname, O_RDONLY);
- if (fd == -1)
- return -1;
-
- if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
- return -1;
-
- close(fd);
- return hdr.rwlocks;
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- unsigned int log_count, flags;
- TDB_DATA d, r;
- struct tdb_logging_context log_ctx = { log_fn, &log_count };
-
- plan_tests(38 * 2);
-
- for (flags = 0; flags <= TDB_CONVERT; flags += TDB_CONVERT) {
- unsigned int rwmagic = TDB_HASH_RWLOCK_MAGIC;
-
- if (flags & TDB_CONVERT)
- tdb_convert(&rwmagic, sizeof(rwmagic));
-
- /* Create an old-style hash. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0, flags,
- O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
- NULL);
- ok1(tdb);
- ok1(log_count == 0);
- d.dptr = (void *)"Hello";
- d.dsize = 5;
- ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
- tdb_close(tdb);
-
- /* Should not have marked rwlocks field. */
- ok1(hdr_rwlocks("run-incompatible.tdb") == 0);
-
- /* We can still open any old-style with incompat flag. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0,
- TDB_INCOMPATIBLE_HASH,
- O_RDWR, 0600, &log_ctx, NULL);
- ok1(tdb);
- ok1(log_count == 0);
- r = tdb_fetch(tdb, d);
- ok1(r.dsize == 5);
- free(r.dptr);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDONLY,
- 0, &log_ctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(log_count == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDONLY,
- 0, &log_ctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(log_count == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- /* OK, now create with incompatible flag, default hash. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0,
- flags|TDB_INCOMPATIBLE_HASH,
- O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
- NULL);
- ok1(tdb);
- ok1(log_count == 0);
- d.dptr = (void *)"Hello";
- d.dsize = 5;
- ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
- tdb_close(tdb);
-
- /* Should have marked rwlocks field. */
- ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic);
-
- /* Cannot open with old hash. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0, 0,
- O_RDWR, 0600, &log_ctx, tdb_old_hash);
- ok1(!tdb);
- ok1(log_count == 1);
-
- /* Can open with jenkins hash. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0, 0,
- O_RDWR, 0600, &log_ctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(log_count == 0);
- r = tdb_fetch(tdb, d);
- ok1(r.dsize == 5);
- free(r.dptr);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- /* Can open by letting it figure it out itself. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0, 0,
- O_RDWR, 0600, &log_ctx, NULL);
- ok1(tdb);
- ok1(log_count == 0);
- r = tdb_fetch(tdb, d);
- ok1(r.dsize == 5);
- free(r.dptr);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- /* We can also use incompatible hash with other hashes. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0,
- flags|TDB_INCOMPATIBLE_HASH,
- O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
- tdb_dumb_hash);
- ok1(tdb);
- ok1(log_count == 0);
- d.dptr = (void *)"Hello";
- d.dsize = 5;
- ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
- tdb_close(tdb);
-
- /* Should have marked rwlocks field. */
- ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic);
-
- /* It should not open if we don't specify. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, NULL);
- ok1(!tdb);
- ok1(log_count == 1);
-
- /* Should reopen with correct hash. */
- log_count = 0;
- tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, tdb_dumb_hash);
- ok1(tdb);
- ok1(log_count == 0);
- r = tdb_fetch(tdb, d);
- ok1(r.dsize == 5);
- free(r.dptr);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- }
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(27);
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
-
- tdb = tdb_open_ex("run-nested-transactions.tdb",
- 1024, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
- ok1(tdb);
-
- /* No nesting by default. */
- ok1(tdb_transaction_start(tdb) == 0);
- data.dptr = (void *)"world";
- data.dsize = strlen("world");
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
- ok1(tdb_transaction_start(tdb) != 0);
- ok1(tdb_error(tdb) == TDB_ERR_NESTING);
-
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
- ok1(tdb_transaction_commit(tdb) == 0);
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
- tdb_close(tdb);
-
- tdb = tdb_open_ex("run-nested-transactions.tdb",
- 1024, TDB_ALLOW_NESTING, O_RDWR, 0, &taplogctx, NULL);
- ok1(tdb);
-
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_transaction_commit(tdb) == 0);
- ok1(!tdb_exists(tdb, key));
- ok1(tdb_transaction_cancel(tdb) == 0);
- /* Surprise! Kills inner "committed" transaction. */
- ok1(tdb_exists(tdb, key));
-
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_transaction_commit(tdb) == 0);
- ok1(!tdb_exists(tdb, key));
- ok1(tdb_transaction_commit(tdb) == 0);
- ok1(!tdb_exists(tdb, key));
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include "lock-tracking.h"
-#define fcntl fcntl_with_lockcheck
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#undef fcntl
-#include <stdlib.h>
-#include <stdbool.h>
-#include <err.h>
-#include "external-agent.h"
-#include "logging.h"
-
-static struct agent *agent;
-
-static bool correct_key(TDB_DATA key)
-{
- return key.dsize == strlen("hi")
- && memcmp(key.dptr, "hi", key.dsize) == 0;
-}
-
-static bool correct_data(TDB_DATA data)
-{
- return data.dsize == strlen("world")
- && memcmp(data.dptr, "world", data.dsize) == 0;
-}
-
-static int traverse2(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
- void *p)
-{
- ok1(correct_key(key));
- ok1(correct_data(data));
- return 0;
-}
-
-static int traverse1(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
- void *p)
-{
- ok1(correct_key(key));
- ok1(correct_data(data));
- ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
- == WOULD_HAVE_BLOCKED);
- tdb_traverse(tdb, traverse2, NULL);
-
- /* That should *not* release the transaction lock! */
- ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
- == WOULD_HAVE_BLOCKED);
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(17);
- agent = prepare_external_agent();
- if (!agent)
- err(1, "preparing agent");
-
- tdb = tdb_open_ex("run-nested-traverse.tdb", 1024, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
- ok1(tdb);
-
- ok1(external_agent_operation(agent, OPEN, tdb_name(tdb)) == SUCCESS);
- ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
- == SUCCESS);
- ok1(external_agent_operation(agent, TRANSACTION_COMMIT, tdb_name(tdb))
- == SUCCESS);
-
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dptr = (void *)"world";
- data.dsize = strlen("world");
-
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- tdb_traverse(tdb, traverse1, NULL);
- tdb_traverse_read(tdb, traverse1, NULL);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <unistd.h>
-#include "lock-tracking.h"
-
-#define fcntl fcntl_with_lockcheck
-
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-#undef fcntl
-
-#define NUM_ENTRIES 10
-
-static bool prepare_entries(struct tdb_context *tdb)
-{
- unsigned int i;
- TDB_DATA key, data;
-
- for (i = 0; i < NUM_ENTRIES; i++) {
- key.dsize = sizeof(i);
- key.dptr = (void *)&i;
- data.dsize = strlen("world");
- data.dptr = (void *)"world";
-
- if (tdb_store(tdb, key, data, 0) != 0)
- return false;
- }
- return true;
-}
-
-static void delete_entries(struct tdb_context *tdb)
-{
- unsigned int i;
- TDB_DATA key;
-
- for (i = 0; i < NUM_ENTRIES; i++) {
- key.dsize = sizeof(i);
- key.dptr = (void *)&i;
-
- ok1(tdb_delete(tdb, key) == 0);
- }
-}
-
-/* We don't know how many times this will run. */
-static int delete_other(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
- void *private_data)
-{
- unsigned int i;
- memcpy(&i, key.dptr, 4);
- i = (i + 1) % NUM_ENTRIES;
- key.dptr = (void *)&i;
- if (tdb_delete(tdb, key) != 0)
- (*(int *)private_data)++;
- return 0;
-}
-
-static int delete_self(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
- void *private_data)
-{
- ok1(tdb_delete(tdb, key) == 0);
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- int errors = 0;
-
- plan_tests(41);
- tdb = tdb_open_ex("run-no-lock-during-traverse.tdb",
- 1024, TDB_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR,
- 0600, &taplogctx, NULL);
-
- ok1(tdb);
- ok1(prepare_entries(tdb));
- ok1(locking_errors == 0);
- ok1(tdb_lockall(tdb) == 0);
- ok1(locking_errors == 0);
- tdb_traverse(tdb, delete_other, &errors);
- ok1(errors == 0);
- ok1(locking_errors == 0);
- ok1(tdb_unlockall(tdb) == 0);
-
- ok1(prepare_entries(tdb));
- ok1(locking_errors == 0);
- ok1(tdb_lockall(tdb) == 0);
- ok1(locking_errors == 0);
- tdb_traverse(tdb, delete_self, NULL);
- ok1(locking_errors == 0);
- ok1(tdb_unlockall(tdb) == 0);
-
- ok1(prepare_entries(tdb));
- ok1(locking_errors == 0);
- ok1(tdb_lockall(tdb) == 0);
- ok1(locking_errors == 0);
- delete_entries(tdb);
- ok1(locking_errors == 0);
- ok1(tdb_unlockall(tdb) == 0);
-
- ok1(tdb_close(tdb) == 0);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
-
- plan_tests(8);
-
- /* Old format (with zeroes in the hash magic fields) should
- * open with any hash (since we don't know what hash they used). */
- tdb = tdb_open_ex("test/old-nohash-le.tdb", 0, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- tdb = tdb_open_ex("test/old-nohash-be.tdb", 0, 0, O_RDWR, 0,
- &taplogctx, NULL);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- tdb = tdb_open_ex("test/old-nohash-le.tdb", 0, 0, O_RDWR, 0,
- &taplogctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- tdb = tdb_open_ex("test/old-nohash-be.tdb", 0, 0, O_RDWR, 0,
- &taplogctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <unistd.h>
-#include "lock-tracking.h"
-
-static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
-static ssize_t write_check(int fd, const void *buf, size_t count);
-static int ftruncate_check(int fd, off_t length);
-
-#define pwrite pwrite_check
-#define write write_check
-#define fcntl fcntl_with_lockcheck
-#define ftruncate ftruncate_check
-
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <err.h>
-#include "external-agent.h"
-#include "logging.h"
-
-static struct agent *agent;
-static bool opened;
-static int errors = 0;
-static bool clear_if_first;
-#define TEST_DBNAME "run-open-during-transaction.tdb"
-
-#undef write
-#undef pwrite
-#undef fcntl
-#undef ftruncate
-
-static bool is_same(const char *snapshot, const char *latest, off_t len)
-{
- unsigned i;
-
- for (i = 0; i < len; i++) {
- if (snapshot[i] != latest[i])
- return false;
- }
- return true;
-}
-
-static bool compare_file(int fd, const char *snapshot, off_t snapshot_len)
-{
- char *contents;
- bool same;
-
- /* over-length read serves as length check. */
- contents = malloc(snapshot_len+1);
- same = pread(fd, contents, snapshot_len+1, 0) == snapshot_len
- && is_same(snapshot, contents, snapshot_len);
- free(contents);
- return same;
-}
-
-static void check_file_intact(int fd)
-{
- enum agent_return ret;
- struct stat st;
- char *contents;
-
- fstat(fd, &st);
- contents = malloc(st.st_size);
- if (pread(fd, contents, st.st_size, 0) != st.st_size) {
- diag("Read fail");
- errors++;
- return;
- }
-
- /* Ask agent to open file. */
- ret = external_agent_operation(agent, clear_if_first ?
- OPEN_WITH_CLEAR_IF_FIRST :
- OPEN,
- TEST_DBNAME);
-
- /* It's OK to open it, but it must not have changed! */
- if (!compare_file(fd, contents, st.st_size)) {
- diag("Agent changed file after opening %s",
- agent_return_name(ret));
- errors++;
- }
-
- if (ret == SUCCESS) {
- ret = external_agent_operation(agent, CLOSE, NULL);
- if (ret != SUCCESS) {
- diag("Agent failed to close tdb: %s",
- agent_return_name(ret));
- errors++;
- }
- } else if (ret != WOULD_HAVE_BLOCKED) {
- diag("Agent opening file gave %s",
- agent_return_name(ret));
- errors++;
- }
-
- free(contents);
-}
-
-static void after_unlock(int fd)
-{
- if (opened)
- check_file_intact(fd);
-}
-
-static ssize_t pwrite_check(int fd,
- const void *buf, size_t count, off_t offset)
-{
- if (opened)
- check_file_intact(fd);
-
- return pwrite(fd, buf, count, offset);
-}
-
-static ssize_t write_check(int fd, const void *buf, size_t count)
-{
- if (opened)
- check_file_intact(fd);
-
- return write(fd, buf, count);
-}
-
-static int ftruncate_check(int fd, off_t length)
-{
- if (opened)
- check_file_intact(fd);
-
- return ftruncate(fd, length);
-
-}
-
-int main(int argc, char *argv[])
-{
- const int flags[] = { TDB_DEFAULT,
- TDB_CLEAR_IF_FIRST,
- TDB_NOMMAP,
- TDB_CLEAR_IF_FIRST | TDB_NOMMAP };
- int i;
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(20);
- agent = prepare_external_agent();
- if (!agent)
- err(1, "preparing agent");
-
- unlock_callback = after_unlock;
- for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
- clear_if_first = (flags[i] & TDB_CLEAR_IF_FIRST);
- diag("Test with %s and %s\n",
- clear_if_first ? "CLEAR" : "DEFAULT",
- (flags[i] & TDB_NOMMAP) ? "no mmap" : "mmap");
- unlink(TEST_DBNAME);
- tdb = tdb_open_ex(TEST_DBNAME, 1024, flags[i],
- O_CREAT|O_TRUNC|O_RDWR, 0600,
- &taplogctx, NULL);
- ok1(tdb);
-
- opened = true;
- ok1(tdb_transaction_start(tdb) == 0);
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dptr = (void *)"world";
- data.dsize = strlen("world");
-
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_transaction_commit(tdb) == 0);
- ok(!errors, "We had %u open errors", errors);
-
- opened = false;
- tdb_close(tdb);
- }
-
- return exit_status();
-}
+++ /dev/null
-/* We should be able to tdb_check a O_RDONLY tdb, and we were previously allowed
- * to tdb_check() inside a transaction (though that's paranoia!). */
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(11);
- tdb = tdb_open_ex("run-readonly-check.tdb", 1024,
- TDB_DEFAULT,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- ok1(tdb);
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dsize = strlen("world");
- data.dptr = (void *)"world";
-
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* We are also allowed to do a check inside a transaction. */
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_close(tdb) == 0);
-
- tdb = tdb_open_ex("run-readonly-check.tdb", 1024,
- TDB_DEFAULT, O_RDONLY, 0, &taplogctx, NULL);
-
- ok1(tdb);
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) == -1);
- ok1(tdb_error(tdb) == TDB_ERR_RDONLY);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_close(tdb) == 0);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-
-static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
-{
- unsigned int *count = tdb_get_logging_private(tdb);
- if (strstr(fmt, "spinlocks"))
- (*count)++;
-}
-
-/* The code should barf on TDBs created with rwlocks. */
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- unsigned int log_count;
- struct tdb_logging_context log_ctx = { log_fn, &log_count };
-
- plan_tests(4);
-
- /* We should fail to open rwlock-using tdbs of either endian. */
- log_count = 0;
- tdb = tdb_open_ex("test/rwlock-le.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, NULL);
- ok1(!tdb);
- ok1(log_count == 1);
-
- log_count = 0;
- tdb = tdb_open_ex("test/rwlock-be.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, NULL);
- ok1(!tdb);
- ok1(log_count == 1);
-
- return exit_status();
-}
+++ /dev/null
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tdb/summary.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-
-int main(int argc, char *argv[])
-{
- unsigned int i, j;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- TDB_DATA key = { (unsigned char *)&j, sizeof(j) };
- TDB_DATA data = { (unsigned char *)&j, sizeof(j) };
- char *summary;
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-summary.tdb", 131, flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600);
- ok1(tdb);
- if (!tdb)
- continue;
-
- /* Put some stuff in there. */
- for (j = 0; j < 500; j++) {
- /* Make sure padding varies to we get some graphs! */
- data.dsize = j % (sizeof(j) + 1);
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
- fail("Storing in tdb");
- }
-
- summary = tdb_summary(tdb);
- diag("%s", summary);
- ok1(strstr(summary, "Size of file/data: "));
- ok1(strstr(summary, "Number of records: 500\n"));
- ok1(strstr(summary, "Smallest/average/largest keys: 4/4/4\n"));
- ok1(strstr(summary, "Smallest/average/largest data: 0/2/4\n"));
- ok1(strstr(summary, "Smallest/average/largest padding: "));
- ok1(strstr(summary, "Number of dead records: 0\n"));
- ok1(strstr(summary, "Number of free records: 1\n"));
- ok1(strstr(summary, "Smallest/average/largest free records: "));
- ok1(strstr(summary, "Number of hash chains: 131\n"));
- ok1(strstr(summary, "Smallest/average/largest hash chains: "));
- ok1(strstr(summary, "Number of uncoalesced records: 0\n"));
- ok1(strstr(summary, "Smallest/average/largest uncoalesced runs: 0/0/0\n"));
- ok1(strstr(summary, "Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: "));
-
- free(summary);
- tdb_close(tdb);
- }
-
- return exit_status();
-}
+++ /dev/null
-/* We need this otherwise fcntl locking fails. */
-#define _FILE_OFFSET_BITS 64
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb_private.h>
-
-/* Speed up the tests: setting TDB_NOSYNC removed recovery altogether. */
-static inline int fake_fsync(int fd)
-{
- return 0;
-}
-#define fsync fake_fsync
-
-#ifdef MS_SYNC
-static inline int fake_msync(void *addr, size_t length, int flags)
-{
- return 0;
-}
-#define msync fake_msync
-#endif
-
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-static void write_record(struct tdb_context *tdb, size_t extra_len,
- TDB_DATA *data)
-{
- TDB_DATA key;
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
-
- data->dsize += extra_len;
- tdb_transaction_start(tdb);
- tdb_store(tdb, key, *data, TDB_REPLACE);
- tdb_transaction_commit(tdb);
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- size_t i;
- TDB_DATA data;
- struct tdb_record rec;
- tdb_off_t off;
-
- plan_tests(4);
- tdb = tdb_open_ex("run-transaction-expand.tdb",
- 1024, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
- ok1(tdb);
-
- data.dsize = 0;
- data.dptr = calloc(1000, getpagesize());
-
- /* Simulate a slowly growing record. */
- for (i = 0; i < 1000; i++)
- write_record(tdb, getpagesize(), &data);
-
- tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off);
- tdb_read(tdb, off, &rec, sizeof(rec), DOCONV());
- diag("TDB size = %zu, recovery = %u-%u",
- (size_t)tdb->map_size, off, off + sizeof(rec) + rec.rec_len);
-
- /* We should only be about 5 times larger than largest record. */
- ok1(tdb->map_size < 6 * i * getpagesize());
- tdb_close(tdb);
-
- tdb = tdb_open_ex("run-transaction-expand.tdb",
- 1024, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
- ok1(tdb);
-
- data.dsize = 0;
-
- /* Simulate a slowly growing record, repacking to keep
- * recovery area at end. */
- for (i = 0; i < 1000; i++) {
- write_record(tdb, getpagesize(), &data);
- if (i % 10 == 0)
- tdb_repack(tdb);
- }
-
- tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off);
- tdb_read(tdb, off, &rec, sizeof(rec), DOCONV());
- diag("TDB size = %zu, recovery = %u-%u",
- (size_t)tdb->map_size, off, off + sizeof(rec) + rec.rec_len);
-
- /* We should only be about 4 times larger than largest record. */
- ok1(tdb->map_size < 5 * i * getpagesize());
- tdb_close(tdb);
- free(data.dptr);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include "lock-tracking.h"
-#define fcntl fcntl_with_lockcheck
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#undef fcntl_with_lockcheck
-#include <stdlib.h>
-#include <stdbool.h>
-#include <err.h>
-#include "external-agent.h"
-#include "logging.h"
-
-static struct agent *agent;
-
-static bool correct_key(TDB_DATA key)
-{
- return key.dsize == strlen("hi")
- && memcmp(key.dptr, "hi", key.dsize) == 0;
-}
-
-static bool correct_data(TDB_DATA data)
-{
- return data.dsize == strlen("world")
- && memcmp(data.dptr, "world", data.dsize) == 0;
-}
-
-static int traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
- void *p)
-{
- ok1(correct_key(key));
- ok1(correct_data(data));
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(13);
- agent = prepare_external_agent();
- if (!agent)
- err(1, "preparing agent");
-
- tdb = tdb_open_ex("run-traverse-in-transaction.tdb",
- 1024, TDB_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR,
- 0600, &taplogctx, NULL);
- ok1(tdb);
-
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dptr = (void *)"world";
- data.dsize = strlen("world");
-
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
-
- ok1(external_agent_operation(agent, OPEN, tdb_name(tdb)) == SUCCESS);
-
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
- == WOULD_HAVE_BLOCKED);
- tdb_traverse(tdb, traverse, NULL);
-
- /* That should *not* release the transaction lock! */
- ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
- == WOULD_HAVE_BLOCKED);
- tdb_traverse_read(tdb, traverse, NULL);
-
- /* That should *not* release the transaction lock! */
- ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
- == WOULD_HAVE_BLOCKED);
- ok1(tdb_transaction_commit(tdb) == 0);
- /* Now we should be fine. */
- ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
- == SUCCESS);
-
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-
-static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
-{
- unsigned int *count = tdb_get_logging_private(tdb);
- if (strstr(fmt, "hash"))
- (*count)++;
-}
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- unsigned int log_count;
- TDB_DATA d;
- struct tdb_logging_context log_ctx = { log_fn, &log_count };
-
- plan_tests(28);
-
- /* Create with default hash. */
- log_count = 0;
- tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0,
- O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, NULL);
- ok1(tdb);
- ok1(log_count == 0);
- d.dptr = (void *)"Hello";
- d.dsize = 5;
- ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
- tdb_close(tdb);
-
- /* Fail to open with different hash. */
- tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, tdb_jenkins_hash);
- ok1(!tdb);
- ok1(log_count == 1);
-
- /* Create with different hash. */
- log_count = 0;
- tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0,
- O_CREAT|O_RDWR|O_TRUNC,
- 0600, &log_ctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(log_count == 0);
- tdb_close(tdb);
-
- /* Endian should be no problem. */
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, tdb_old_hash);
- ok1(!tdb);
- ok1(log_count == 1);
-
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, tdb_old_hash);
- ok1(!tdb);
- ok1(log_count == 1);
-
- log_count = 0;
- /* Fail to open with old default hash. */
- tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, tdb_old_hash);
- ok1(!tdb);
- ok1(log_count == 1);
-
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDONLY,
- 0, &log_ctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(log_count == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDONLY,
- 0, &log_ctx, tdb_jenkins_hash);
- ok1(tdb);
- ok1(log_count == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- /* It should open with jenkins hash if we don't specify. */
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, NULL);
- ok1(tdb);
- ok1(log_count == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- log_count = 0;
- tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDWR, 0,
- &log_ctx, NULL);
- ok1(tdb);
- ok1(log_count == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
- log_count = 0;
- tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDONLY,
- 0, &log_ctx, NULL);
- ok1(tdb);
- ok1(log_count == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
-
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(4);
- tdb = tdb_open_ex(NULL, 1024, TDB_INTERNAL, O_CREAT|O_TRUNC|O_RDWR,
- 0600, &taplogctx, NULL);
- ok1(tdb);
-
- /* Tickle bug on appending zero length buffer to zero length buffer. */
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dptr = (void *)"world";
- data.dsize = 0;
-
- ok1(tdb_append(tdb, key, data) == 0);
- ok1(tdb_append(tdb, key, data) == 0);
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == 0);
- tdb_close(tdb);
- free(data.dptr);
-
- return exit_status();
-}
+++ /dev/null
-#define _XOPEN_SOURCE 500
-#include <ccan/tdb/tdb.h>
-#include <ccan/tdb/io.c>
-#include <ccan/tdb/tdb.c>
-#include <ccan/tdb/lock.c>
-#include <ccan/tdb/freelist.c>
-#include <ccan/tdb/traverse.c>
-#include <ccan/tdb/transaction.c>
-#include <ccan/tdb/error.c>
-#include <ccan/tdb/open.c>
-#include <ccan/tdb/check.c>
-#include <ccan/tdb/hash.c>
-#include <ccan/tap/tap.h>
-#include <stdlib.h>
-#include <err.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- struct tdb_context *tdb;
- TDB_DATA key, data;
-
- plan_tests(10);
- tdb = tdb_open_ex("run.tdb", 1024, TDB_CLEAR_IF_FIRST,
- O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
-
- ok1(tdb);
- key.dsize = strlen("hi");
- key.dptr = (void *)"hi";
- data.dsize = strlen("world");
- data.dptr = (void *)"world";
-
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) < 0);
- ok1(tdb_error(tdb) == TDB_ERR_NOEXIST);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) < 0);
- ok1(tdb_error(tdb) == TDB_ERR_EXISTS);
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0);
-
- data = tdb_fetch(tdb, key);
- ok1(data.dsize == strlen("world"));
- ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
- free(data.dptr);
-
- key.dsize++;
- data = tdb_fetch(tdb, key);
- ok1(data.dptr == NULL);
- tdb_close(tdb);
-
- return exit_status();
-}
+++ /dev/null
-LDLIBS:=../../tdb.o ../../tally.o
-CFLAGS:=-I../../.. -Wall -O3 #-g -pg
-LDFLAGS:=-L../../..
-
-default: replay_trace tdbtorture tdbdump tdbtool starvation mktdb speed
-
-benchmark: replay_trace
- @trap "rm -f /tmp/trace.$$$$" 0; for f in benchmarks/*.rz; do if runzip -k $$f -o /tmp/trace.$$$$ && echo -n "$$f": && ./replay_trace --quiet -n 5 replay.tdb /tmp/trace.$$$$ && rm /tmp/trace.$$$$; then rm -f /tmp/trace.$$$$; else exit 1; fi; done
-
-REPLAY_LIBS=$(LDLIBS) ../../str_talloc.o ../../grab_file.o ../../talloc.o ../../noerr.o
-replay_trace: replay_trace.c keywords.c $(REPLAY_LIBS)
- $(LINK.c) $< $(LOADLIBES) $(REPLAY_LIBS) -o $@
-
-keywords.c: keywords.gperf
- gperf $< > $@
-
-check: replay_trace
- @rm -f *.reduced_trace
- @set -e; for f in tests/*.trace.tar.bz2; do \
- tar xvfj $$f; \
- ./replay_trace replay.tdb *.reduced_trace; \
- rm -f *.reduced_trace; \
- done
-
-# Usage: make mytest.trace.tar.bz2 TRACEFILES=*.trace
-%.trace.tar.bz2: $(patsubst %.trace,%.reduced_trace,$(wildcard $(TRACEFILES)))
- tar cvfj $@ $^
-
-%.reduced_trace: %.trace
- @sed 's/\(^[0-9]* traverse\) .*/\1fn/' < $^ > $@
-
-clean:
- rm -f replay_trace tdbtorture tdbdump tdbtool speed *.o
+++ /dev/null
-/* ANSI-C code produced by gperf version 3.0.3 */
-/* Command-line: gperf keywords.gperf */
-/* Computed positions: -k'5,$' */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
- && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
- && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
- && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
- && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
- && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
- && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
- && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
- && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
- && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
- && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
- && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
- && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
- && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
- && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
- && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
- && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
- && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
- && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
- && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
- && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
- && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
- && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646. */
-#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
-#endif
-
-#line 1 "keywords.gperf"
-
-#line 4 "keywords.gperf"
-struct op_table {
- const char *name;
- enum op_type type;
- void (*enhance_op)(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[]);
-};
-/* maximum key range = 53, duplicates = 0 */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-static unsigned int
-hash_keyword (register const char *str, register unsigned int len)
-{
- static const unsigned char asso_values[] =
- {
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 45, 61, 30,
- 5, 0, 0, 5, 5, 61, 61, 0, 0, 0,
- 20, 61, 20, 61, 25, 0, 5, 0, 61, 0,
- 61, 5, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61
- };
- return len + asso_values[(unsigned char)str[4]] + asso_values[(unsigned char)str[len - 1]];
-}
-
-#ifdef __GNUC__
-__inline
-#ifdef __GNUC_STDC_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-const struct op_table *
-find_keyword (register const char *str, register unsigned int len)
-{
- enum
- {
- TOTAL_KEYWORDS = 35,
- MIN_WORD_LENGTH = 8,
- MAX_WORD_LENGTH = 30,
- MIN_HASH_VALUE = 8,
- MAX_HASH_VALUE = 60
- };
-
- static const struct op_table wordlist[] =
- {
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 44 "keywords.gperf"
- {"traverse", OP_TDB_TRAVERSE, op_add_traverse,},
-#line 33 "keywords.gperf"
- {"tdb_store", OP_TDB_STORE, op_add_store,},
-#line 32 "keywords.gperf"
- {"tdb_exists", OP_TDB_EXISTS, op_add_key_ret,},
-#line 16 "keywords.gperf"
- {"tdb_lockall", OP_TDB_LOCKALL, op_add_nothing,},
-#line 36 "keywords.gperf"
- {"tdb_wipe_all", OP_TDB_WIPE_ALL, op_add_wipe_all,},
-#line 20 "keywords.gperf"
- {"tdb_unlockall", OP_TDB_UNLOCKALL, op_add_nothing,},
-#line 48 "keywords.gperf"
- {"tdb_fetch", OP_TDB_FETCH, op_add_key_data,},
-#line 49 "keywords.gperf"
- {"tdb_delete", OP_TDB_DELETE, op_add_key_ret,},
-#line 17 "keywords.gperf"
- {"tdb_lockall_mark", OP_TDB_LOCKALL_MARK, op_add_nothing,},
-#line 46 "keywords.gperf"
- {"tdb_firstkey", OP_TDB_FIRSTKEY, op_add_key,},
-#line 18 "keywords.gperf"
- {"tdb_lockall_unmark", OP_TDB_LOCKALL_UNMARK, op_add_nothing,},
-#line 35 "keywords.gperf"
- {"tdb_get_seqnum", OP_TDB_GET_SEQNUM, op_add_seqnum,},
-#line 19 "keywords.gperf"
- {"tdb_lockall_nonblock", OP_TDB_LOCKALL_NONBLOCK, op_add_nothing,},
-#line 21 "keywords.gperf"
- {"tdb_lockall_read", OP_TDB_LOCKALL_READ, op_add_nothing,},
- {""},
-#line 23 "keywords.gperf"
- {"tdb_unlockall_read", OP_TDB_UNLOCKALL_READ, op_add_nothing,},
- {""},
-#line 22 "keywords.gperf"
- {"tdb_lockall_read_nonblock", OP_TDB_LOCKALL_READ_NONBLOCK, op_add_nothing,},
-#line 43 "keywords.gperf"
- {"tdb_traverse_end", OP_TDB_TRAVERSE_END, op_analyze_traverse,},
-#line 38 "keywords.gperf"
- {"tdb_transaction_cancel", OP_TDB_TRANSACTION_CANCEL, op_analyze_transaction,},
-#line 42 "keywords.gperf"
- {"tdb_traverse_start", OP_TDB_TRAVERSE_START, op_add_traverse_start,},
- {""},
-#line 45 "keywords.gperf"
- {"traversefn", OP_TDB_TRAVERSE, op_add_traversefn,},
-#line 37 "keywords.gperf"
- {"tdb_transaction_start", OP_TDB_TRANSACTION_START, op_add_transaction,},
-#line 39 "keywords.gperf"
- {"tdb_transaction_commit", OP_TDB_TRANSACTION_COMMIT, op_analyze_transaction,},
-#line 41 "keywords.gperf"
- {"tdb_traverse_read_start", OP_TDB_TRAVERSE_READ_START, op_add_traverse_start,},
- {""},
-#line 50 "keywords.gperf"
- {"tdb_repack", OP_TDB_REPACK, op_add_nothing,},
-#line 47 "keywords.gperf"
- {"tdb_nextkey", OP_TDB_NEXTKEY, op_add_key_data,},
- {""}, {""}, {""},
-#line 40 "keywords.gperf"
- {"tdb_transaction_prepare_commit", OP_TDB_TRANSACTION_PREPARE_COMMIT, op_add_nothing,},
-#line 31 "keywords.gperf"
- {"tdb_parse_record", OP_TDB_PARSE_RECORD, op_add_key_ret,},
- {""},
-#line 24 "keywords.gperf"
- {"tdb_chainlock", OP_TDB_CHAINLOCK, op_add_chainlock,},
- {""},
-#line 28 "keywords.gperf"
- {"tdb_chainunlock", OP_TDB_CHAINUNLOCK, op_analyze_chainlock,},
- {""}, {""},
-#line 26 "keywords.gperf"
- {"tdb_chainlock_mark", OP_TDB_CHAINLOCK_MARK, op_add_key,},
- {""},
-#line 27 "keywords.gperf"
- {"tdb_chainlock_unmark", OP_TDB_CHAINLOCK_UNMARK, op_add_key,},
- {""},
-#line 25 "keywords.gperf"
- {"tdb_chainlock_nonblock", OP_TDB_CHAINLOCK_NONBLOCK, op_add_chainlock_ret,},
-#line 29 "keywords.gperf"
- {"tdb_chainlock_read", OP_TDB_CHAINLOCK_READ, op_add_chainlock,},
- {""},
-#line 30 "keywords.gperf"
- {"tdb_chainunlock_read", OP_TDB_CHAINUNLOCK_READ, op_analyze_chainlock,},
- {""}, {""}, {""}, {""},
-#line 34 "keywords.gperf"
- {"tdb_append", OP_TDB_APPEND, op_add_append,}
- };
-
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = hash_keyword (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register const char *s = wordlist[key].name;
-
- if (*str == *s && !strcmp (str + 1, s + 1))
- return &wordlist[key];
- }
- }
- return 0;
-}
+++ /dev/null
-%{
-%}
-%language=ANSI-C
-struct op_table {
- const char *name;
- enum op_type type;
- void (*enhance_op)(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[]);
-};
-%define hash-function-name hash_keyword
-%define lookup-function-name find_keyword
-%readonly-tables
-%struct-type
-%enum
-%%
-tdb_lockall, OP_TDB_LOCKALL, op_add_nothing,
-tdb_lockall_mark, OP_TDB_LOCKALL_MARK, op_add_nothing,
-tdb_lockall_unmark, OP_TDB_LOCKALL_UNMARK, op_add_nothing,
-tdb_lockall_nonblock, OP_TDB_LOCKALL_NONBLOCK, op_add_nothing,
-tdb_unlockall, OP_TDB_UNLOCKALL, op_add_nothing,
-tdb_lockall_read, OP_TDB_LOCKALL_READ, op_add_nothing,
-tdb_lockall_read_nonblock, OP_TDB_LOCKALL_READ_NONBLOCK, op_add_nothing,
-tdb_unlockall_read, OP_TDB_UNLOCKALL_READ, op_add_nothing,
-tdb_chainlock, OP_TDB_CHAINLOCK, op_add_chainlock,
-tdb_chainlock_nonblock, OP_TDB_CHAINLOCK_NONBLOCK, op_add_chainlock_ret,
-tdb_chainlock_mark, OP_TDB_CHAINLOCK_MARK, op_add_key,
-tdb_chainlock_unmark, OP_TDB_CHAINLOCK_UNMARK, op_add_key,
-tdb_chainunlock, OP_TDB_CHAINUNLOCK, op_analyze_chainlock,
-tdb_chainlock_read, OP_TDB_CHAINLOCK_READ, op_add_chainlock,
-tdb_chainunlock_read, OP_TDB_CHAINUNLOCK_READ, op_analyze_chainlock,
-tdb_parse_record, OP_TDB_PARSE_RECORD, op_add_key_ret,
-tdb_exists, OP_TDB_EXISTS, op_add_key_ret,
-tdb_store, OP_TDB_STORE, op_add_store,
-tdb_append, OP_TDB_APPEND, op_add_append,
-tdb_get_seqnum, OP_TDB_GET_SEQNUM, op_add_seqnum,
-tdb_wipe_all, OP_TDB_WIPE_ALL, op_add_wipe_all,
-tdb_transaction_start, OP_TDB_TRANSACTION_START, op_add_transaction,
-tdb_transaction_cancel, OP_TDB_TRANSACTION_CANCEL, op_analyze_transaction,
-tdb_transaction_commit, OP_TDB_TRANSACTION_COMMIT, op_analyze_transaction,
-tdb_transaction_prepare_commit, OP_TDB_TRANSACTION_PREPARE_COMMIT, op_add_nothing,
-tdb_traverse_read_start, OP_TDB_TRAVERSE_READ_START, op_add_traverse_start,
-tdb_traverse_start, OP_TDB_TRAVERSE_START, op_add_traverse_start,
-tdb_traverse_end, OP_TDB_TRAVERSE_END, op_analyze_traverse,
-traverse, OP_TDB_TRAVERSE, op_add_traverse,
-traversefn, OP_TDB_TRAVERSE, op_add_traversefn,
-tdb_firstkey, OP_TDB_FIRSTKEY, op_add_key,
-tdb_nextkey, OP_TDB_NEXTKEY, op_add_key_data,
-tdb_fetch, OP_TDB_FETCH, op_add_key_data,
-tdb_delete, OP_TDB_DELETE, op_add_key_ret,
-tdb_repack, OP_TDB_REPACK, op_add_nothing,
+++ /dev/null
-#include <ccan/tdb/tdb.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <err.h>
-
-int main(int argc, char *argv[])
-{
- unsigned int i, num_recs;
- struct tdb_context *tdb;
-
- if (argc != 3 || (num_recs = atoi(argv[2])) == 0)
- errx(1, "Usage: mktdb <tdbfile> <numrecords>");
-
- tdb = tdb_open(argv[1], 10007, TDB_DEFAULT, O_CREAT|O_TRUNC|O_RDWR, 0600);
- if (!tdb)
- err(1, "Opening %s", argv[1]);
-
- for (i = 0; i < num_recs; i++) {
- TDB_DATA d;
-
- d.dptr = (void *)&i;
- d.dsize = sizeof(i);
- if (tdb_store(tdb, d, d, TDB_INSERT) != 0)
- err(1, "Failed to store record %i", i);
- }
- printf("Done\n");
- return 0;
-}
+++ /dev/null
-#include <ccan/tdb/tdb.h>
-#include <ccan/grab_file/grab_file.h>
-#include <ccan/hash/hash.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str_talloc/str_talloc.h>
-#include <ccan/str/str.h>
-#include <ccan/list/list.h>
-#include <err.h>
-#include <ctype.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <errno.h>
-#include <signal.h>
-#include <assert.h>
-#include <fcntl.h>
-
-#define STRINGIFY2(x) #x
-#define STRINGIFY(x) STRINGIFY2(x)
-
-static bool quiet = false;
-
-/* Avoid mod by zero */
-static unsigned int total_keys = 1;
-
-/* All the wipe_all ops. */
-static struct op_desc *wipe_alls = NULL;
-static unsigned int num_wipe_alls = 0;
-
-/* #define DEBUG_DEPS 1 */
-
-/* Traversals block transactions in the current implementation. */
-#define TRAVERSALS_TAKE_TRANSACTION_LOCK 1
-
-struct pipe {
- int fd[2];
-};
-static struct pipe *pipes;
-static int backoff_fd = -1;
-
-static void __attribute__((noreturn)) fail(const char *filename,
- unsigned int line,
- const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- fprintf(stderr, "%s:%u: FAIL: ", filename, line);
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
- exit(1);
-}
-
-/* Try or die. */
-#define try(expr, expect) \
- do { \
- int ret = (expr); \
- if (ret != (expect)) \
- fail(filename[file], i+1, \
- STRINGIFY(expr) "= %i", ret); \
- } while (0)
-
-/* Try or imitate results. */
-#define unreliable(expr, expect, force, undo) \
- do { \
- int ret = expr; \
- if (ret != expect) { \
- fprintf(stderr, "%s:%u: %s gave %i not %i", \
- filename[file], i+1, STRINGIFY(expr), \
- ret, expect); \
- if (expect == 0) \
- force; \
- else \
- undo; \
- } \
- } while (0)
-
-static bool key_eq(TDB_DATA a, TDB_DATA b)
-{
- if (a.dsize != b.dsize)
- return false;
- return memcmp(a.dptr, b.dptr, a.dsize) == 0;
-}
-
-/* This is based on the hash algorithm from gdbm */
-static unsigned int hash_key(TDB_DATA *key)
-{
- uint32_t value; /* Used to compute the hash value. */
- uint32_t i; /* Used to cycle through random values. */
-
- /* Set the initial value from the key size. */
- for (value = 0x238F13AF ^ key->dsize, i=0; i < key->dsize; i++)
- value = (value + (key->dptr[i] << (i*5 % 24)));
-
- return (1103515243 * value + 12345);
-}
-
-enum op_type {
- OP_TDB_LOCKALL,
- OP_TDB_LOCKALL_MARK,
- OP_TDB_LOCKALL_UNMARK,
- OP_TDB_LOCKALL_NONBLOCK,
- OP_TDB_UNLOCKALL,
- OP_TDB_LOCKALL_READ,
- OP_TDB_LOCKALL_READ_NONBLOCK,
- OP_TDB_UNLOCKALL_READ,
- OP_TDB_CHAINLOCK,
- OP_TDB_CHAINLOCK_NONBLOCK,
- OP_TDB_CHAINLOCK_MARK,
- OP_TDB_CHAINLOCK_UNMARK,
- OP_TDB_CHAINUNLOCK,
- OP_TDB_CHAINLOCK_READ,
- OP_TDB_CHAINUNLOCK_READ,
- OP_TDB_PARSE_RECORD,
- OP_TDB_EXISTS,
- OP_TDB_STORE,
- OP_TDB_APPEND,
- OP_TDB_GET_SEQNUM,
- OP_TDB_WIPE_ALL,
- OP_TDB_TRANSACTION_START,
- OP_TDB_TRANSACTION_CANCEL,
- OP_TDB_TRANSACTION_PREPARE_COMMIT,
- OP_TDB_TRANSACTION_COMMIT,
- OP_TDB_TRAVERSE_READ_START,
- OP_TDB_TRAVERSE_START,
- OP_TDB_TRAVERSE_END,
- OP_TDB_TRAVERSE,
- OP_TDB_TRAVERSE_END_EARLY,
- OP_TDB_FIRSTKEY,
- OP_TDB_NEXTKEY,
- OP_TDB_FETCH,
- OP_TDB_DELETE,
- OP_TDB_REPACK,
-};
-
-struct op {
- unsigned int seqnum;
- enum op_type type;
- TDB_DATA key;
- TDB_DATA data;
- int ret;
-
- /* Who is waiting for us? */
- struct list_head post;
- /* What are we waiting for? */
- struct list_head pre;
-
- /* If I'm part of a group (traverse/transaction) where is
- * start? (Otherwise, 0) */
- unsigned int group_start;
-
- union {
- int flag; /* open and store */
- struct { /* append */
- TDB_DATA pre;
- TDB_DATA post;
- } append;
- /* transaction/traverse start/chainlock */
- unsigned int group_len;
- };
-};
-
-struct op_desc {
- unsigned int file;
- unsigned int op_num;
-};
-
-static unsigned char hex_char(const char *filename, unsigned int line, char c)
-{
- c = toupper(c);
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- if (c >= '0' && c <= '9')
- return c - '0';
- fail(filename, line, "invalid hex character '%c'", c);
-}
-
-/* TDB data is <size>:<%02x>* */
-static TDB_DATA make_tdb_data(const void *ctx,
- const char *filename, unsigned int line,
- const char *word)
-{
- TDB_DATA data;
- unsigned int i;
- const char *p;
-
- if (streq(word, "NULL"))
- return tdb_null;
-
- data.dsize = atoi(word);
- data.dptr = talloc_array(ctx, unsigned char, data.dsize);
- p = strchr(word, ':');
- if (!p)
- fail(filename, line, "invalid tdb data '%s'", word);
- p++;
- for (i = 0; i < data.dsize; i++)
- data.dptr[i] = hex_char(filename, line, p[i*2])*16
- + hex_char(filename, line, p[i*2+1]);
-
- return data;
-}
-
-static void add_op(const char *filename, struct op **op, unsigned int i,
- unsigned int seqnum, enum op_type type)
-{
- struct op *new;
- *op = talloc_realloc(NULL, *op, struct op, i+1);
- new = (*op) + i;
- new->type = type;
- new->seqnum = seqnum;
- new->ret = 0;
- new->group_start = 0;
-}
-
-static void op_add_nothing(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (words[2])
- fail(filename[file], op_num+1, "Expected no arguments");
- op[op_num].key = tdb_null;
-}
-
-static void op_add_key(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (words[2] == NULL || words[3])
- fail(filename[file], op_num+1, "Expected just a key");
-
- op[op_num].key = make_tdb_data(op, filename[file], op_num+1, words[2]);
- total_keys++;
-}
-
-static void op_add_key_ret(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (!words[2] || !words[3] || !words[4] || words[5]
- || !streq(words[3], "="))
- fail(filename[file], op_num+1, "Expected <key> = <ret>");
- op[op_num].ret = atoi(words[4]);
- op[op_num].key = make_tdb_data(op, filename[file], op_num+1, words[2]);
- /* May only be a unique key if it fails */
- if (op[op_num].ret != 0)
- total_keys++;
-}
-
-static void op_add_key_data(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (!words[2] || !words[3] || !words[4] || words[5]
- || !streq(words[3], "="))
- fail(filename[file], op_num+1, "Expected <key> = <data>");
- op[op_num].key = make_tdb_data(op, filename[file], op_num+1, words[2]);
- op[op_num].data = make_tdb_data(op, filename[file], op_num+1, words[4]);
- /* Likely only be a unique key if it fails */
- if (!op[op_num].data.dptr)
- total_keys++;
- else if (random() % 2)
- total_keys++;
-}
-
-/* We don't record the keys or data for a traverse, as we don't use them. */
-static void op_add_traverse(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (!words[2] || !words[3] || !words[4] || words[5]
- || !streq(words[3], "="))
- fail(filename[file], op_num+1, "Expected <key> = <data>");
- op[op_num].key = tdb_null;
-}
-
-/* Full traverse info is useful for debugging, but changing it to
- * "traversefn" without the data makes the traces *much* smaller! */
-static void op_add_traversefn(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (words[2])
- fail(filename[file], op_num+1, "Expected no values");
- op[op_num].key = tdb_null;
-}
-
-/* <seqnum> tdb_store <rec> <rec> <flag> = <ret> */
-static void op_add_store(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (!words[2] || !words[3] || !words[4] || !words[5] || !words[6]
- || words[7] || !streq(words[5], "="))
- fail(filename[file], op_num+1, "Expect <key> <data> <flag> = <ret>");
-
- op[op_num].flag = strtoul(words[4], NULL, 0);
- op[op_num].ret = atoi(words[6]);
- op[op_num].key = make_tdb_data(op, filename[file], op_num+1, words[2]);
- op[op_num].data = make_tdb_data(op, filename[file], op_num+1, words[3]);
- total_keys++;
-}
-
-/* <seqnum> tdb_append <rec> <rec> = <rec> */
-static void op_add_append(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (!words[2] || !words[3] || !words[4] || !words[5] || words[6]
- || !streq(words[4], "="))
- fail(filename[file], op_num+1, "Expect <key> <data> = <rec>");
-
- op[op_num].key = make_tdb_data(op, filename[file], op_num+1, words[2]);
- op[op_num].data = make_tdb_data(op, filename[file], op_num+1, words[3]);
-
- op[op_num].append.post
- = make_tdb_data(op, filename[file], op_num+1, words[5]);
-
- /* By subtraction, figure out what previous data was. */
- op[op_num].append.pre.dptr = op[op_num].append.post.dptr;
- op[op_num].append.pre.dsize
- = op[op_num].append.post.dsize - op[op_num].data.dsize;
- total_keys++;
-}
-
-/* <seqnum> tdb_get_seqnum = <ret> */
-static void op_add_seqnum(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (!words[2] || !words[3] || words[4] || !streq(words[2], "="))
- fail(filename[file], op_num+1, "Expect = <ret>");
-
- op[op_num].key = tdb_null;
- op[op_num].ret = atoi(words[3]);
-}
-
-static void op_add_traverse_start(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (words[2])
- fail(filename[file], op_num+1, "Expect no arguments");
-
- op[op_num].key = tdb_null;
- op[op_num].group_len = 0;
-}
-
-static void op_add_transaction(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (words[2])
- fail(filename[file], op_num+1, "Expect no arguments");
-
- op[op_num].key = tdb_null;
- op[op_num].group_len = 0;
-}
-
-static void op_add_chainlock(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (words[2] == NULL || words[3])
- fail(filename[file], op_num+1, "Expected just a key");
-
- /* A chainlock key isn't a key in the normal sense; it doesn't
- * have to be in the db at all. Also, we don't want to hash this op. */
- op[op_num].data = make_tdb_data(op, filename[file], op_num+1, words[2]);
- op[op_num].key = tdb_null;
- op[op_num].group_len = 0;
-}
-
-static void op_add_chainlock_ret(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (!words[2] || !words[3] || !words[4] || words[5]
- || !streq(words[3], "="))
- fail(filename[file], op_num+1, "Expected <key> = <ret>");
- op[op_num].ret = atoi(words[4]);
- op[op_num].data = make_tdb_data(op, filename[file], op_num+1, words[2]);
- op[op_num].key = tdb_null;
- op[op_num].group_len = 0;
- total_keys++;
-}
-
-static void op_add_wipe_all(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- if (words[2])
- fail(filename[file], op_num+1, "Expected no arguments");
- op[op_num].key = tdb_null;
- wipe_alls = talloc_realloc(NULL, wipe_alls, struct op_desc,
- num_wipe_alls+1);
- wipe_alls[num_wipe_alls].file = file;
- wipe_alls[num_wipe_alls].op_num = op_num;
- num_wipe_alls++;
-}
-
-static int op_find_start(struct op op[], unsigned int op_num, enum op_type type)
-{
- unsigned int i;
-
- for (i = op_num-1; i > 0; i--) {
- if (op[i].type == type && !op[i].group_len)
- return i;
- }
- return 0;
-}
-
-static void op_analyze_transaction(char *filename[], struct op op[],
- unsigned file, unsigned op_num,
- char *words[])
-{
- unsigned int start, i;
-
- op[op_num].key = tdb_null;
-
- if (words[2])
- fail(filename[file], op_num+1, "Expect no arguments");
-
- start = op_find_start(op, op_num, OP_TDB_TRANSACTION_START);
- if (!start)
- fail(filename[file], op_num+1, "no transaction start found");
-
- op[start].group_len = op_num - start;
-
- /* This rolls in nested transactions. I think that's right. */
- for (i = start; i <= op_num; i++)
- op[i].group_start = start;
-}
-
-/* We treat chainlocks a lot like transactions, even though that's overkill */
-static void op_analyze_chainlock(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- unsigned int i, start;
-
- if (words[2] == NULL || words[3])
- fail(filename[file], op_num+1, "Expected just a key");
-
- op[op_num].data = make_tdb_data(op, filename[file], op_num+1, words[2]);
- op[op_num].key = tdb_null;
- total_keys++;
-
- start = op_find_start(op, op_num, OP_TDB_CHAINLOCK);
- if (!start)
- start = op_find_start(op, op_num, OP_TDB_CHAINLOCK_READ);
- if (!start)
- fail(filename[file], op_num+1, "no initial chainlock found");
-
- /* FIXME: We'd have to do something clever to make this work
- * vs. deadlock. */
- if (!key_eq(op[start].data, op[op_num].data))
- fail(filename[file], op_num+1, "nested chainlock calls?");
-
- op[start].group_len = op_num - start;
- for (i = start; i <= op_num; i++)
- op[i].group_start = start;
-}
-
-static void op_analyze_traverse(char *filename[], struct op op[],
- unsigned file, unsigned op_num, char *words[])
-{
- int i, start;
-
- op[op_num].key = tdb_null;
-
- /* = %u means traverse function terminated. */
- if (words[2]) {
- if (!streq(words[2], "=") || !words[3] || words[4])
- fail(filename[file], op_num+1, "expect = <num>");
- op[op_num].ret = atoi(words[3]);
- } else
- op[op_num].ret = 0;
-
- start = op_find_start(op, op_num, OP_TDB_TRAVERSE_START);
- if (!start)
- start = op_find_start(op, op_num, OP_TDB_TRAVERSE_READ_START);
- if (!start)
- fail(filename[file], op_num+1, "no traversal start found");
-
- op[start].group_len = op_num - start;
-
- /* Don't roll in nested traverse/chainlock */
- for (i = start; i <= op_num; i++)
- if (!op[i].group_start)
- op[i].group_start = start;
-}
-
-/* Keep -Wmissing-declarations happy: */
-const struct op_table *
-find_keyword (register const char *str, register unsigned int len);
-
-#include "keywords.c"
-
-struct depend {
- /* We can have more than one */
- struct list_node pre_list;
- struct list_node post_list;
- struct op_desc needs;
- struct op_desc prereq;
-};
-
-static void check_deps(const char *filename, struct op op[], unsigned int num)
-{
-#ifdef DEBUG_DEPS
- unsigned int i;
-
- for (i = 1; i < num; i++)
- if (!list_empty(&op[i].pre))
- fail(filename, i+1, "Still has dependencies");
-#endif
-}
-
-static void dump_pre(char *filename[], struct op *op[],
- unsigned int file, unsigned int i)
-{
- struct depend *dep;
-
- if (!quiet) {
- printf("%s:%u (%u) still waiting for:\n", filename[file], i+1,
- op[file][i].seqnum);
- list_for_each(&op[file][i].pre, dep, pre_list)
- printf(" %s:%u (%u)\n",
- filename[dep->prereq.file], dep->prereq.op_num+1,
- op[dep->prereq.file][dep->prereq.op_num].seqnum);
- }
- check_deps(filename[file], op[file], i);
-}
-
-/* We simply read/write pointers, since we all are children. */
-static bool do_pre(struct tdb_context *tdb,
- char *filename[], struct op *op[],
- unsigned int file, int pre_fd, unsigned int i,
- bool backoff)
-{
- while (!list_empty(&op[file][i].pre)) {
- struct depend *dep;
-
-#if DEBUG_DEPS
- printf("%s:%u:waiting for pre\n", filename[file], i+1);
- fflush(stdout);
-#endif
- if (backoff)
- alarm(2);
- else
- alarm(10);
- while (read(pre_fd, &dep, sizeof(dep)) != sizeof(dep)) {
- if (errno == EINTR) {
- if (backoff) {
- struct op_desc desc = { file,i };
- warnx("%s:%u:avoiding deadlock",
- filename[file], i+1);
- if (write(backoff_fd, &desc,
- sizeof(desc)) != sizeof(desc))
- err(1, "writing backoff_fd");
- return false;
- }
- dump_pre(filename, op, file, i);
- exit(1);
- } else
- errx(1, "Reading from pipe");
- }
- alarm(0);
-
-#if DEBUG_DEPS
- printf("%s:%u:got pre %u from %s:%u\n", filename[file], i+1,
- dep->needs.op_num+1, filename[dep->prereq.file],
- dep->prereq.op_num+1);
- fflush(stdout);
-#endif
- /* This could be any op, not just this one. */
- talloc_free(dep);
- }
- return true;
-}
-
-static void do_post(char *filename[], struct op *op[],
- unsigned int file, unsigned int i)
-{
- struct depend *dep;
-
- list_for_each(&op[file][i].post, dep, post_list) {
-#if DEBUG_DEPS
- printf("%s:%u:sending to file %s:%u\n", filename[file], i+1,
- filename[dep->needs.file], dep->needs.op_num+1);
-#endif
- if (write(pipes[dep->needs.file].fd[1], &dep, sizeof(dep))
- != sizeof(dep))
- err(1, "%s:%u failed to tell file %s",
- filename[file], i+1, filename[dep->needs.file]);
- }
-}
-
-static int get_len(TDB_DATA key, TDB_DATA data, void *private_data)
-{
- return data.dsize;
-}
-
-static unsigned run_ops(struct tdb_context *tdb,
- int pre_fd,
- char *filename[],
- struct op *op[],
- unsigned int file,
- unsigned int start, unsigned int stop,
- bool backoff);
-
-struct traverse_info {
- struct op **op;
- char **filename;
- unsigned file;
- int pre_fd;
- unsigned int start;
- unsigned int i;
-};
-
-/* More complex. Just do whatever's they did at the n'th entry. */
-static int nontrivial_traverse(struct tdb_context *tdb,
- TDB_DATA key, TDB_DATA data,
- void *_tinfo)
-{
- struct traverse_info *tinfo = _tinfo;
- unsigned int trav_len = tinfo->op[tinfo->file][tinfo->start].group_len;
- bool avoid_deadlock = false;
-
- if (tinfo->i == tinfo->start + trav_len) {
- /* This can happen if traverse expects to be empty. */
- if (trav_len == 1)
- return 1;
- fail(tinfo->filename[tinfo->file], tinfo->start + 1,
- "traverse did not terminate");
- }
-
- if (tinfo->op[tinfo->file][tinfo->i].type != OP_TDB_TRAVERSE)
- fail(tinfo->filename[tinfo->file], tinfo->start + 1,
- "%s:%u:traverse terminated early");
-
-#if TRAVERSALS_TAKE_TRANSACTION_LOCK
- avoid_deadlock = true;
-#endif
-
- /* Run any normal ops. */
- tinfo->i = run_ops(tdb, tinfo->pre_fd, tinfo->filename, tinfo->op,
- tinfo->file, tinfo->i+1, tinfo->start + trav_len,
- avoid_deadlock);
-
- /* We backed off, or we hit OP_TDB_TRAVERSE_END/EARLY. */
- if (tinfo->op[tinfo->file][tinfo->i].type != OP_TDB_TRAVERSE)
- return 1;
-
- return 0;
-}
-
-static unsigned op_traverse(struct tdb_context *tdb,
- int pre_fd,
- char *filename[],
- unsigned int file,
- int (*traversefn)(struct tdb_context *,
- tdb_traverse_func, void *),
- struct op *op[],
- unsigned int start)
-{
- struct traverse_info tinfo = { op, filename, file, pre_fd,
- start, start+1 };
-
- traversefn(tdb, nontrivial_traverse, &tinfo);
-
- /* Traversing in wrong order can have strange effects: eg. if
- * original traverse went A (delete A), B, we might do B
- * (delete A). So if we have ops left over, we do it now. */
- while (tinfo.i != start + op[file][start].group_len) {
- if (op[file][tinfo.i].type == OP_TDB_TRAVERSE
- || op[file][tinfo.i].type == OP_TDB_TRAVERSE_END_EARLY)
- tinfo.i++;
- else
- tinfo.i = run_ops(tdb, pre_fd, filename, op, file,
- tinfo.i,
- start + op[file][start].group_len,
- false);
- }
-
- return tinfo.i;
-}
-
-static void break_out(int sig)
-{
-}
-
-static __attribute__((noinline))
-unsigned run_ops(struct tdb_context *tdb,
- int pre_fd,
- char *filename[],
- struct op *op[],
- unsigned int file,
- unsigned int start, unsigned int stop,
- bool backoff)
-{
- unsigned int i;
- struct sigaction sa;
-
- sa.sa_handler = break_out;
- sa.sa_flags = 0;
-
- sigaction(SIGALRM, &sa, NULL);
- for (i = start; i < stop; i++) {
- if (!do_pre(tdb, filename, op, file, pre_fd, i, backoff))
- return i;
-
- switch (op[file][i].type) {
- case OP_TDB_LOCKALL:
- try(tdb_lockall(tdb), op[file][i].ret);
- break;
- case OP_TDB_LOCKALL_MARK:
- try(tdb_lockall_mark(tdb), op[file][i].ret);
- break;
- case OP_TDB_LOCKALL_UNMARK:
- try(tdb_lockall_unmark(tdb), op[file][i].ret);
- break;
- case OP_TDB_LOCKALL_NONBLOCK:
- unreliable(tdb_lockall_nonblock(tdb), op[file][i].ret,
- tdb_lockall(tdb), tdb_unlockall(tdb));
- break;
- case OP_TDB_UNLOCKALL:
- try(tdb_unlockall(tdb), op[file][i].ret);
- break;
- case OP_TDB_LOCKALL_READ:
- try(tdb_lockall_read(tdb), op[file][i].ret);
- break;
- case OP_TDB_LOCKALL_READ_NONBLOCK:
- unreliable(tdb_lockall_read_nonblock(tdb),
- op[file][i].ret,
- tdb_lockall_read(tdb),
- tdb_unlockall_read(tdb));
- break;
- case OP_TDB_UNLOCKALL_READ:
- try(tdb_unlockall_read(tdb), op[file][i].ret);
- break;
- case OP_TDB_CHAINLOCK:
- try(tdb_chainlock(tdb, op[file][i].key),
- op[file][i].ret);
- break;
- case OP_TDB_CHAINLOCK_NONBLOCK:
- unreliable(tdb_chainlock_nonblock(tdb, op[file][i].key),
- op[file][i].ret,
- tdb_chainlock(tdb, op[file][i].key),
- tdb_chainunlock(tdb, op[file][i].key));
- break;
- case OP_TDB_CHAINLOCK_MARK:
- try(tdb_chainlock_mark(tdb, op[file][i].key),
- op[file][i].ret);
- break;
- case OP_TDB_CHAINLOCK_UNMARK:
- try(tdb_chainlock_unmark(tdb, op[file][i].key),
- op[file][i].ret);
- break;
- case OP_TDB_CHAINUNLOCK:
- try(tdb_chainunlock(tdb, op[file][i].key),
- op[file][i].ret);
- break;
- case OP_TDB_CHAINLOCK_READ:
- try(tdb_chainlock_read(tdb, op[file][i].key),
- op[file][i].ret);
- break;
- case OP_TDB_CHAINUNLOCK_READ:
- try(tdb_chainunlock_read(tdb, op[file][i].key),
- op[file][i].ret);
- break;
- case OP_TDB_PARSE_RECORD:
- try(tdb_parse_record(tdb, op[file][i].key, get_len,
- NULL),
- op[file][i].ret);
- break;
- case OP_TDB_EXISTS:
- try(tdb_exists(tdb, op[file][i].key), op[file][i].ret);
- break;
- case OP_TDB_STORE:
- try(tdb_store(tdb, op[file][i].key, op[file][i].data,
- op[file][i].flag),
- op[file][i].ret);
- break;
- case OP_TDB_APPEND:
- try(tdb_append(tdb, op[file][i].key, op[file][i].data),
- op[file][i].ret);
- break;
- case OP_TDB_GET_SEQNUM:
- try(tdb_get_seqnum(tdb), op[file][i].ret);
- break;
- case OP_TDB_WIPE_ALL:
- try(tdb_wipe_all(tdb), op[file][i].ret);
- break;
- case OP_TDB_TRANSACTION_START:
- try(tdb_transaction_start(tdb), op[file][i].ret);
- break;
- case OP_TDB_TRANSACTION_CANCEL:
- try(tdb_transaction_cancel(tdb), op[file][i].ret);
- break;
- case OP_TDB_TRANSACTION_PREPARE_COMMIT:
- try(tdb_transaction_prepare_commit(tdb),
- op[file][i].ret);
- break;
- case OP_TDB_TRANSACTION_COMMIT:
- try(tdb_transaction_commit(tdb), op[file][i].ret);
- break;
- case OP_TDB_TRAVERSE_READ_START:
- i = op_traverse(tdb, pre_fd, filename, file,
- tdb_traverse_read, op, i);
- break;
- case OP_TDB_TRAVERSE_START:
- i = op_traverse(tdb, pre_fd, filename, file,
- tdb_traverse, op, i);
- break;
- case OP_TDB_TRAVERSE:
- case OP_TDB_TRAVERSE_END_EARLY:
- /* Terminate: we're in a traverse, and we've
- * done our ops. */
- return i;
- case OP_TDB_TRAVERSE_END:
- fail(filename[file], i+1, "unexpected end traverse");
- /* FIXME: These must be treated like traverse. */
- case OP_TDB_FIRSTKEY:
- if (!key_eq(tdb_firstkey(tdb), op[file][i].data))
- fail(filename[file], i+1, "bad firstkey");
- break;
- case OP_TDB_NEXTKEY:
- if (!key_eq(tdb_nextkey(tdb, op[file][i].key),
- op[file][i].data))
- fail(filename[file], i+1, "bad nextkey");
- break;
- case OP_TDB_FETCH: {
- TDB_DATA f = tdb_fetch(tdb, op[file][i].key);
- if (!key_eq(f, op[file][i].data))
- fail(filename[file], i+1, "bad fetch %u",
- f.dsize);
- break;
- }
- case OP_TDB_DELETE:
- try(tdb_delete(tdb, op[file][i].key), op[file][i].ret);
- break;
- case OP_TDB_REPACK:
- /* We do nothing here: the transaction and traverse are
- * traced. It's in the trace to mark it, since it
- * may become unnecessary in future. */
- break;
- }
- do_post(filename, op, file, i);
- }
- return i;
-}
-
-/* tdbtorture, in particular, can do a tdb_close with a transaction in
- * progress. */
-static struct op *maybe_cancel_transaction(char *filename[], unsigned int file,
- struct op *op, unsigned int *num)
-{
- unsigned int start = op_find_start(op, *num, OP_TDB_TRANSACTION_START);
-
- if (start) {
- char *words[] = { "<unknown>", "tdb_close", NULL };
- add_op(filename[file], &op, *num, op[start].seqnum,
- OP_TDB_TRANSACTION_CANCEL);
- op_analyze_transaction(filename, op, file, *num, words);
- (*num)++;
- }
- return op;
-}
-
-static struct op *load_tracefile(char *filename[],
- unsigned int file,
- unsigned int *num,
- unsigned int *hashsize,
- unsigned int *tdb_flags,
- unsigned int *open_flags)
-{
- unsigned int i;
- struct op *op = talloc_array(NULL, struct op, 1);
- char **words;
- char **lines;
- char *contents;
-
- contents = grab_file(NULL, filename[file], NULL);
- if (!contents)
- err(1, "Reading %s", filename[file]);
-
- lines = strsplit(contents, contents, "\n");
- if (!lines[0])
- errx(1, "%s is empty", filename[file]);
-
- words = strsplit(lines, lines[0], " ");
- if (!streq(words[1], "tdb_open"))
- fail(filename[file], 1, "does not start with tdb_open");
-
- *hashsize = atoi(words[2]);
- *tdb_flags = strtoul(words[3], NULL, 0);
- *open_flags = strtoul(words[4], NULL, 0);
-
- for (i = 1; lines[i]; i++) {
- const struct op_table *opt;
-
- words = strsplit(lines, lines[i], " ");
- if (!words[0] || !words[1])
- fail(filename[file], i+1,
- "Expected seqnum number and op");
-
- opt = find_keyword(words[1], strlen(words[1]));
- if (!opt) {
- if (streq(words[1], "tdb_close")) {
- if (lines[i+1])
- fail(filename[file], i+2,
- "lines after tdb_close");
- *num = i;
- talloc_free(lines);
- return maybe_cancel_transaction(filename, file,
- op, num);
- }
- fail(filename[file], i+1,
- "Unknown operation '%s'", words[1]);
- }
-
- add_op(filename[file], &op, i, atoi(words[0]), opt->type);
- opt->enhance_op(filename, op, file, i, words);
- }
-
- if (!quiet)
- fprintf(stderr,
- "%s:%u:last operation is not tdb_close: incomplete?",
- filename[file], i);
-
- talloc_free(contents);
- *num = i - 1;
- return maybe_cancel_transaction(filename, file, op, num);
-}
-
-/* We remember all the keys we've ever seen, and who has them. */
-struct keyinfo {
- TDB_DATA key;
- unsigned int num_users;
- struct op_desc *user;
-};
-
-static bool starts_transaction(const struct op *op)
-{
- return op->type == OP_TDB_TRANSACTION_START;
-}
-
-static bool in_transaction(const struct op op[], unsigned int i)
-{
- return op[i].group_start && starts_transaction(&op[op[i].group_start]);
-}
-
-static bool successful_transaction(const struct op *op)
-{
- return starts_transaction(op)
- && op[op->group_len].type == OP_TDB_TRANSACTION_COMMIT;
-}
-
-static bool starts_traverse(const struct op *op)
-{
- return op->type == OP_TDB_TRAVERSE_START
- || op->type == OP_TDB_TRAVERSE_READ_START;
-}
-
-static bool in_traverse(const struct op op[], unsigned int i)
-{
- return op[i].group_start && starts_traverse(&op[op[i].group_start]);
-}
-
-static bool starts_chainlock(const struct op *op)
-{
- return op->type == OP_TDB_CHAINLOCK_READ
- || op->type == OP_TDB_CHAINLOCK;
-}
-
-static bool in_chainlock(const struct op op[], unsigned int i)
-{
- return op[i].group_start && starts_chainlock(&op[op[i].group_start]);
-}
-
-static const TDB_DATA must_not_exist;
-static const TDB_DATA must_exist;
-static const TDB_DATA not_exists_or_empty;
-
-/* NULL means doesn't care if it exists or not, &must_exist means
- * it must exist but we don't care what, &must_not_exist means it must
- * not exist, otherwise the data it needs. */
-static const TDB_DATA *needs(const TDB_DATA *key, const struct op *op)
-{
- /* Look through for an op in this transaction which needs this key. */
- if (starts_transaction(op) || starts_chainlock(op)) {
- unsigned int i;
- const TDB_DATA *need = NULL;
-
- for (i = 1; i < op->group_len; i++) {
- if (key_eq(op[i].key, *key)
- || op[i].type == OP_TDB_WIPE_ALL) {
- need = needs(key, &op[i]);
- /* tdb_exists() is special: there might be
- * something in the transaction with more
- * specific requirements. Other ops don't have
- * specific requirements (eg. store or delete),
- * but they change the value so we can't get
- * more information from future ops. */
- if (op[i].type != OP_TDB_EXISTS)
- break;
- }
- }
-
- return need;
- }
-
- switch (op->type) {
- /* FIXME: Pull forward deps, since we can deadlock */
- case OP_TDB_CHAINLOCK:
- case OP_TDB_CHAINLOCK_NONBLOCK:
- case OP_TDB_CHAINLOCK_MARK:
- case OP_TDB_CHAINLOCK_UNMARK:
- case OP_TDB_CHAINUNLOCK:
- case OP_TDB_CHAINLOCK_READ:
- case OP_TDB_CHAINUNLOCK_READ:
- return NULL;
-
- case OP_TDB_APPEND:
- if (op->append.pre.dsize == 0)
- return ¬_exists_or_empty;
- return &op->append.pre;
-
- case OP_TDB_STORE:
- if (op->flag == TDB_INSERT) {
- if (op->ret < 0)
- return &must_exist;
- else
- return &must_not_exist;
- } else if (op->flag == TDB_MODIFY) {
- if (op->ret < 0)
- return &must_not_exist;
- else
- return &must_exist;
- }
- /* No flags? Don't care */
- return NULL;
-
- case OP_TDB_EXISTS:
- if (op->ret == 1)
- return &must_exist;
- else
- return &must_not_exist;
-
- case OP_TDB_PARSE_RECORD:
- if (op->ret < 0)
- return &must_not_exist;
- return &must_exist;
-
- /* FIXME: handle these. */
- case OP_TDB_WIPE_ALL:
- case OP_TDB_FIRSTKEY:
- case OP_TDB_NEXTKEY:
- case OP_TDB_GET_SEQNUM:
- case OP_TDB_TRAVERSE:
- case OP_TDB_TRANSACTION_COMMIT:
- case OP_TDB_TRANSACTION_CANCEL:
- case OP_TDB_TRANSACTION_START:
- return NULL;
-
- case OP_TDB_FETCH:
- if (!op->data.dptr)
- return &must_not_exist;
- return &op->data;
-
- case OP_TDB_DELETE:
- if (op->ret < 0)
- return &must_not_exist;
- return &must_exist;
-
- default:
- errx(1, "Unexpected op type %i", op->type);
- }
-
-}
-
-/* What's the data after this op? pre if nothing changed. */
-static const TDB_DATA *gives(const TDB_DATA *key, const TDB_DATA *pre,
- const struct op *op)
-{
- if (starts_transaction(op) || starts_chainlock(op)) {
- unsigned int i;
-
- /* Cancelled transactions don't change anything. */
- if (op[op->group_len].type == OP_TDB_TRANSACTION_CANCEL)
- return pre;
- assert(op[op->group_len].type == OP_TDB_TRANSACTION_COMMIT
- || op[op->group_len].type == OP_TDB_CHAINUNLOCK_READ
- || op[op->group_len].type == OP_TDB_CHAINUNLOCK);
-
- for (i = 1; i < op->group_len; i++) {
- /* This skips nested transactions, too */
- if (key_eq(op[i].key, *key)
- || op[i].type == OP_TDB_WIPE_ALL)
- pre = gives(key, pre, &op[i]);
- }
- return pre;
- }
-
- /* Failed ops don't change state of db. */
- if (op->ret < 0)
- return pre;
-
- if (op->type == OP_TDB_DELETE || op->type == OP_TDB_WIPE_ALL)
- return &tdb_null;
-
- if (op->type == OP_TDB_APPEND)
- return &op->append.post;
-
- if (op->type == OP_TDB_STORE)
- return &op->data;
-
- return pre;
-}
-
-static void add_hash_user(struct keyinfo *hash,
- unsigned int h,
- struct op *op[],
- unsigned int file,
- unsigned int op_num)
-{
- hash[h].user = talloc_realloc(hash, hash[h].user,
- struct op_desc, hash[h].num_users+1);
-
- /* If it's in a transaction, it's the transaction which
- * matters from an analysis POV. */
- if (in_transaction(op[file], op_num)
- || in_chainlock(op[file], op_num)) {
- unsigned i;
-
- op_num = op[file][op_num].group_start;
-
- /* Don't include twice. */
- for (i = 0; i < hash[h].num_users; i++) {
- if (hash[h].user[i].file == file
- && hash[h].user[i].op_num == op_num)
- return;
- }
- }
- hash[h].user[hash[h].num_users].op_num = op_num;
- hash[h].user[hash[h].num_users].file = file;
- hash[h].num_users++;
-}
-
-static struct keyinfo *hash_ops(struct op *op[], unsigned int num_ops[],
- unsigned int num)
-{
- unsigned int i, j, h;
- struct keyinfo *hash;
-
- hash = talloc_zero_array(op[0], struct keyinfo, total_keys*2);
- for (i = 0; i < num; i++) {
- for (j = 1; j < num_ops[i]; j++) {
- /* We can't do this on allocation, due to realloc. */
- list_head_init(&op[i][j].post);
- list_head_init(&op[i][j].pre);
-
- if (!op[i][j].key.dptr)
- continue;
-
- h = hash_key(&op[i][j].key) % (total_keys * 2);
- while (!key_eq(hash[h].key, op[i][j].key)) {
- if (!hash[h].key.dptr) {
- hash[h].key = op[i][j].key;
- break;
- }
- h = (h + 1) % (total_keys * 2);
- }
- /* Might as well save some memory if we can. */
- if (op[i][j].key.dptr != hash[h].key.dptr) {
- talloc_free(op[i][j].key.dptr);
- op[i][j].key.dptr = hash[h].key.dptr;
- }
-
- add_hash_user(hash, h, op, i, j);
- }
- }
-
- /* Any wipe all entries need adding to all hash entries. */
- for (h = 0; h < total_keys*2; h++) {
- if (!hash[h].num_users)
- continue;
-
- for (i = 0; i < num_wipe_alls; i++)
- add_hash_user(hash, h, op,
- wipe_alls[i].file, wipe_alls[i].op_num);
- }
-
- return hash;
-}
-
-static bool satisfies(const TDB_DATA *key, const TDB_DATA *data,
- const struct op *op)
-{
- const TDB_DATA *need = needs(key, op);
-
- /* Don't need anything? Cool. */
- if (!need)
- return true;
-
- /* This should be tdb_null or a real value. */
- assert(data != &must_exist);
- assert(data != &must_not_exist);
- assert(data != ¬_exists_or_empty);
-
- /* Must not exist? data must not exist. */
- if (need == &must_not_exist)
- return data == &tdb_null;
-
- /* Must exist? */
- if (need == &must_exist)
- return data != &tdb_null;
-
- /* Either noexist or empty. */
- if (need == ¬_exists_or_empty)
- return data->dsize == 0;
-
- /* Needs something specific. */
- return key_eq(*data, *need);
-}
-
-static void move_to_front(struct op_desc res[], unsigned off, unsigned elem)
-{
- if (elem != off) {
- struct op_desc tmp = res[elem];
- memmove(res + off + 1, res + off, (elem - off)*sizeof(res[0]));
- res[off] = tmp;
- }
-}
-
-static void restore_to_pos(struct op_desc res[], unsigned off, unsigned elem)
-{
- if (elem != off) {
- struct op_desc tmp = res[off];
- memmove(res + off, res + off + 1, (elem - off)*sizeof(res[0]));
- res[elem] = tmp;
- }
-}
-
-static bool sort_deps(char *filename[], struct op *op[],
- struct op_desc res[],
- unsigned off, unsigned num,
- const TDB_DATA *key, const TDB_DATA *data,
- unsigned num_files, unsigned fuzz)
-{
- unsigned int i, files_done;
- struct op *this_op;
- bool done[num_files];
-
- /* None left? We're sorted. */
- if (off == num)
- return true;
-
- /* Does this make sequence number go backwards? Allow a little fuzz. */
- if (off > 0) {
- int seqnum1 = op[res[off-1].file][res[off-1].op_num].seqnum;
- int seqnum2 = op[res[off].file][res[off].op_num].seqnum;
-
- if (seqnum1 - seqnum2 > (int)fuzz) {
-#if DEBUG_DEPS
- printf("Seqnum jump too far (%u -> %u)\n",
- seqnum1, seqnum2);
-#endif
- return false;
- }
- }
-
- memset(done, 0, sizeof(done));
-
- /* Since ops within a trace file are ordered, we just need to figure
- * out which file to try next. Since we don't take into account
- * inter-key relationships (which exist by virtue of trace file order),
- * we minimize the chance of harm by trying to keep in seqnum order. */
- for (files_done = 0, i = off; i < num && files_done < num_files; i++) {
- if (done[res[i].file])
- continue;
-
- this_op = &op[res[i].file][res[i].op_num];
-
- /* Is what we have good enough for this op? */
- if (satisfies(key, data, this_op)) {
- move_to_front(res, off, i);
- if (sort_deps(filename, op, res, off+1, num,
- key, gives(key, data, this_op),
- num_files, fuzz))
- return true;
- restore_to_pos(res, off, i);
- }
- done[res[i].file] = true;
- files_done++;
- }
-
- /* No combination worked. */
- return false;
-}
-
-static void check_dep_sorting(struct op_desc user[], unsigned num_users,
- unsigned num_files)
-{
-#if DEBUG_DEPS
- unsigned int i;
- unsigned minima[num_files];
-
- memset(minima, 0, sizeof(minima));
- for (i = 0; i < num_users; i++) {
- assert(minima[user[i].file] < user[i].op_num);
- minima[user[i].file] = user[i].op_num;
- }
-#endif
-}
-
-/* All these ops happen on the same key. Which comes first?
- *
- * This can happen both because read ops or failed write ops don't
- * change sequence number, and also due to race since we access the
- * number unlocked (the race can cause less detectable ordering problems,
- * in which case we'll deadlock and report: fix manually in that case).
- */
-static bool figure_deps(char *filename[], struct op *op[],
- const TDB_DATA *key, const TDB_DATA *data,
- struct op_desc user[],
- unsigned num_users, unsigned num_files)
-{
- unsigned int fuzz;
-
- /* We prefer to keep strict seqnum order if possible: it's the
- * most likely. We get more lax if that fails. */
- for (fuzz = 0; fuzz < 100; fuzz = (fuzz + 1)*2) {
- if (sort_deps(filename, op, user, 0, num_users, key, data,
- num_files, fuzz))
- break;
- }
-
- if (fuzz >= 100)
- return false;
-
- check_dep_sorting(user, num_users, num_files);
- return true;
-}
-
-/* We're having trouble sorting out dependencies for this key. Assume that it's
- * a pre-existing record in the db, so determine a likely value. */
-static const TDB_DATA *preexisting_data(char *filename[], struct op *op[],
- const TDB_DATA *key,
- struct op_desc *user,
- unsigned int num_users)
-{
- unsigned int i;
- const TDB_DATA *data;
-
- for (i = 0; i < num_users; i++) {
- data = needs(key, &op[user->file][user->op_num]);
- if (data && data != &must_not_exist) {
- if (!quiet)
- printf("%s:%u: needs pre-existing record\n",
- filename[user->file], user->op_num+1);
- return data;
- }
- }
- return &tdb_null;
-}
-
-static void sort_ops(struct tdb_context *tdb,
- struct keyinfo hash[], char *filename[], struct op *op[],
- unsigned int num)
-{
- unsigned int h;
-
- /* Gcc nexted function extension. How cool is this? */
- int compare_seqnum(const void *_a, const void *_b)
- {
- const struct op_desc *a = _a, *b = _b;
-
- /* First, maintain order within any trace file. */
- if (a->file == b->file)
- return a->op_num - b->op_num;
-
- /* Otherwise, arrange by seqnum order. */
- if (op[a->file][a->op_num].seqnum !=
- op[b->file][b->op_num].seqnum)
- return op[a->file][a->op_num].seqnum
- - op[b->file][b->op_num].seqnum;
-
- /* Cancelled transactions are assumed to happen first. */
- if (starts_transaction(&op[a->file][a->op_num])
- && !successful_transaction(&op[a->file][a->op_num]))
- return -1;
- if (starts_transaction(&op[b->file][b->op_num])
- && !successful_transaction(&op[b->file][b->op_num]))
- return 1;
-
- /* No idea. */
- return 0;
- }
-
- /* Now sort into seqnum order. */
- for (h = 0; h < total_keys * 2; h++) {
- struct op_desc *user = hash[h].user;
-
- qsort(user, hash[h].num_users, sizeof(user[0]), compare_seqnum);
- if (!figure_deps(filename, op, &hash[h].key, &tdb_null, user,
- hash[h].num_users, num)) {
- const TDB_DATA *data;
-
- data = preexisting_data(filename, op, &hash[h].key,
- user, hash[h].num_users);
- /* Give the first op what it wants: does that help? */
- if (!figure_deps(filename, op, &hash[h].key, data, user,
- hash[h].num_users, num))
- fail(filename[user[0].file], user[0].op_num+1,
- "Could not resolve inter-dependencies");
- if (tdb_store(tdb, hash[h].key, *data, TDB_INSERT) != 0)
- errx(1, "Could not store initial value");
- }
- }
-}
-
-static int destroy_depend(struct depend *dep)
-{
- list_del(&dep->pre_list);
- list_del(&dep->post_list);
- return 0;
-}
-
-static void add_dependency(void *ctx,
- struct op *op[],
- char *filename[],
- const struct op_desc *needs,
- const struct op_desc *prereq)
-{
- struct depend *dep;
-
- /* We don't depend on ourselves. */
- if (needs->file == prereq->file) {
- assert(prereq->op_num < needs->op_num);
- return;
- }
-
-#if DEBUG_DEPS
- printf("%s:%u: depends on %s:%u\n",
- filename[needs->file], needs->op_num+1,
- filename[prereq->file], prereq->op_num+1);
-#endif
-
- dep = talloc(ctx, struct depend);
- dep->needs = *needs;
- dep->prereq = *prereq;
-
-#if TRAVERSALS_TAKE_TRANSACTION_LOCK
- /* If something in a traverse depends on something in another
- * traverse/transaction, it creates a dependency between the
- * two groups. */
- if ((in_traverse(op[prereq->file], prereq->op_num)
- && (starts_transaction(&op[needs->file][needs->op_num])
- || starts_traverse(&op[needs->file][needs->op_num])))
- || (in_traverse(op[needs->file], needs->op_num)
- && (starts_transaction(&op[prereq->file][prereq->op_num])
- || starts_traverse(&op[prereq->file][prereq->op_num])))) {
- unsigned int start;
-
- /* We are satisfied by end of group. */
- start = op[prereq->file][prereq->op_num].group_start;
- dep->prereq.op_num = start + op[prereq->file][start].group_len;
- /* And we need that done by start of our group. */
- dep->needs.op_num = op[needs->file][needs->op_num].group_start;
- }
-
- /* There is also this case:
- * <traverse> <read foo> ...
- * <transaction> ... </transaction> <create foo>
- * Where if we start the traverse then wait, we could block
- * the transaction and deadlock.
- *
- * We try to address this by ensuring that where seqnum indicates it's
- * possible, we wait for <create foo> before *starting* traverse.
- */
- else if (in_traverse(op[needs->file], needs->op_num)) {
- struct op *need = &op[needs->file][needs->op_num];
- if (op[needs->file][need->group_start].seqnum >
- op[prereq->file][prereq->op_num].seqnum) {
- dep->needs.op_num = need->group_start;
- }
- }
-#endif
-
- /* If you depend on a transaction or chainlock, you actually
- * depend on it ending. */
- if (starts_transaction(&op[prereq->file][dep->prereq.op_num])
- || starts_chainlock(&op[prereq->file][dep->prereq.op_num])) {
- dep->prereq.op_num
- += op[dep->prereq.file][dep->prereq.op_num].group_len;
-#if DEBUG_DEPS
- printf("-> Actually end of transaction %s:%u\n",
- filename[dep->prereq->file], dep->prereq->op_num+1);
-#endif
- } else
- /* We should never create a dependency from middle of
- * a transaction. */
- assert(!in_transaction(op[prereq->file], dep->prereq.op_num)
- || op[prereq->file][dep->prereq.op_num].type
- == OP_TDB_TRANSACTION_COMMIT
- || op[prereq->file][dep->prereq.op_num].type
- == OP_TDB_TRANSACTION_CANCEL);
-
- list_add(&op[dep->prereq.file][dep->prereq.op_num].post,
- &dep->post_list);
- list_add(&op[dep->needs.file][dep->needs.op_num].pre,
- &dep->pre_list);
- talloc_set_destructor(dep, destroy_depend);
-}
-
-static bool changes_db(const TDB_DATA *key, const struct op *op)
-{
- return gives(key, NULL, op) != NULL;
-}
-
-static void depend_on_previous(struct op *op[],
- char *filename[],
- unsigned int num,
- struct op_desc user[],
- unsigned int i,
- int prev)
-{
- bool deps[num];
- int j;
-
- if (i == 0)
- return;
-
- if (prev == i - 1) {
- /* Just depend on previous. */
- add_dependency(NULL, op, filename, &user[i], &user[prev]);
- return;
- }
-
- /* We have to wait for the readers. Find last one in *each* file. */
- memset(deps, 0, sizeof(deps));
- deps[user[i].file] = true;
- for (j = i - 1; j > prev; j--) {
- if (!deps[user[j].file]) {
- add_dependency(NULL, op, filename, &user[i], &user[j]);
- deps[user[j].file] = true;
- }
- }
-}
-
-/* This is simple, but not complete. We don't take into account
- * indirect dependencies. */
-static void optimize_dependencies(struct op *op[], unsigned int num_ops[],
- unsigned int num)
-{
- unsigned int i, j;
-
- /* There can only be one real dependency on each file */
- for (i = 0; i < num; i++) {
- for (j = 1; j < num_ops[i]; j++) {
- struct depend *dep, *next;
- struct depend *prev[num];
-
- memset(prev, 0, sizeof(prev));
-
- list_for_each_safe(&op[i][j].pre, dep, next, pre_list) {
- if (!prev[dep->prereq.file]) {
- prev[dep->prereq.file] = dep;
- continue;
- }
- if (prev[dep->prereq.file]->prereq.op_num
- < dep->prereq.op_num) {
- talloc_free(prev[dep->prereq.file]);
- prev[dep->prereq.file] = dep;
- } else
- talloc_free(dep);
- }
- }
- }
-
- for (i = 0; i < num; i++) {
- int deps[num];
-
- for (j = 0; j < num; j++)
- deps[j] = -1;
-
- for (j = 1; j < num_ops[i]; j++) {
- struct depend *dep, *next;
-
- list_for_each_safe(&op[i][j].pre, dep, next, pre_list) {
- if (deps[dep->prereq.file]
- >= (int)dep->prereq.op_num)
- talloc_free(dep);
- else
- deps[dep->prereq.file]
- = dep->prereq.op_num;
- }
- }
- }
-}
-
-#if TRAVERSALS_TAKE_TRANSACTION_LOCK
-/* Force an order among the traversals, so they don't deadlock (as much) */
-static void make_traverse_depends(char *filename[],
- struct op *op[], unsigned int num_ops[],
- unsigned int num)
-{
- unsigned int i, num_traversals = 0;
- int j;
- struct op_desc *desc;
-
- /* Sort by which one runs first. */
- int compare_traverse_desc(const void *_a, const void *_b)
- {
- const struct op_desc *da = _a, *db = _b;
- const struct op *a = &op[da->file][da->op_num],
- *b = &op[db->file][db->op_num];
-
- if (a->seqnum != b->seqnum)
- return a->seqnum - b->seqnum;
-
- /* If they have same seqnum, it means one didn't make any
- * changes. Thus sort by end in that case. */
- return a[a->group_len].seqnum - b[b->group_len].seqnum;
- }
-
- desc = talloc_array(NULL, struct op_desc, 1);
-
- /* Count them. */
- for (i = 0; i < num; i++) {
- for (j = 1; j < num_ops[i]; j++) {
- /* Traverse start (ignore those in
- * transactions; they're already covered by
- * transaction dependencies). */
- if (starts_traverse(&op[i][j])
- && !in_transaction(op[i], j)) {
- desc = talloc_realloc(NULL, desc,
- struct op_desc,
- num_traversals+1);
- desc[num_traversals].file = i;
- desc[num_traversals].op_num = j;
- num_traversals++;
- }
- }
- }
- qsort(desc, num_traversals, sizeof(desc[0]), compare_traverse_desc);
-
- for (i = 1; i < num_traversals; i++) {
- const struct op *prev = &op[desc[i-1].file][desc[i-1].op_num];
- const struct op *curr = &op[desc[i].file][desc[i].op_num];
-
- /* Read traverses don't depend on each other (read lock). */
- if (prev->type == OP_TDB_TRAVERSE_READ_START
- && curr->type == OP_TDB_TRAVERSE_READ_START)
- continue;
-
- /* Only make dependency if it's clear. */
- if (compare_traverse_desc(&desc[i], &desc[i-1])) {
- /* i depends on end of traverse i-1. */
- struct op_desc end = desc[i-1];
- end.op_num += prev->group_len;
- add_dependency(NULL, op, filename, &desc[i], &end);
- }
- }
- talloc_free(desc);
-}
-
-static void set_nonblock(int fd)
-{
- if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK) != 0)
- err(1, "Setting pipe nonblocking");
-}
-
-static bool handle_backoff(struct op *op[], int fd)
-{
- struct op_desc desc;
- bool handled = false;
-
- /* Sloppy coding: we assume PIPEBUF never fills. */
- while (read(fd, &desc, sizeof(desc)) != -1) {
- unsigned int i;
- handled = true;
- for (i = desc.op_num; i > 0; i--) {
- if (op[desc.file][i].type == OP_TDB_TRAVERSE) {
- /* We insert a fake end here. */
- op[desc.file][i].type
- = OP_TDB_TRAVERSE_END_EARLY;
- break;
- } else if (starts_traverse(&op[desc.file][i])) {
- unsigned int start = i;
- struct op tmp = op[desc.file][i];
- /* Move the ops outside traverse. */
- memmove(&op[desc.file][i],
- &op[desc.file][i+1],
- (desc.op_num-i-1) * sizeof(op[0][0]));
- op[desc.file][desc.op_num] = tmp;
- while (op[desc.file][i].group_start == start) {
- op[desc.file][i++].group_start
- = desc.op_num;
- }
- break;
- }
- }
- }
- return handled;
-}
-
-#else /* !TRAVERSALS_TAKE_TRANSACTION_LOCK */
-static bool handle_backoff(struct op *op[], int fd)
-{
- return false;
-}
-#endif
-
-static void derive_dependencies(struct tdb_context *tdb,
- char *filename[],
- struct op *op[], unsigned int num_ops[],
- unsigned int num)
-{
- struct keyinfo *hash;
- unsigned int h, i;
-
- /* Create hash table for faster key lookup. */
- hash = hash_ops(op, num_ops, num);
-
- /* Sort them by sequence number. */
- sort_ops(tdb, hash, filename, op, num);
-
- /* Create dependencies back to the last change, rather than
- * creating false dependencies by naively making each one
- * depend on the previous. This has two purposes: it makes
- * later optimization simpler, and it also avoids deadlock with
- * same sequence number ops inside traversals (if one
- * traversal doesn't write anything, two ops can have the same
- * sequence number yet we can create a traversal dependency
- * the other way). */
- for (h = 0; h < total_keys * 2; h++) {
- int prev = -1;
-
- if (hash[h].num_users < 2)
- continue;
-
- for (i = 0; i < hash[h].num_users; i++) {
- if (changes_db(&hash[h].key, &op[hash[h].user[i].file]
- [hash[h].user[i].op_num])) {
- depend_on_previous(op, filename, num,
- hash[h].user, i, prev);
- prev = i;
- } else if (prev >= 0)
- add_dependency(hash, op, filename,
- &hash[h].user[i],
- &hash[h].user[prev]);
- }
- }
-
-#if TRAVERSALS_TAKE_TRANSACTION_LOCK
- make_traverse_depends(filename, op, num_ops, num);
-#endif
-
- optimize_dependencies(op, num_ops, num);
-}
-
-static struct timeval run_test(char *argv[],
- unsigned int num_ops[],
- unsigned int hashsize[],
- unsigned int tdb_flags[],
- unsigned int open_flags[],
- struct op *op[],
- int fds[2])
-{
- unsigned int i;
- struct timeval start, end, diff;
- bool ok = true;
-
- for (i = 0; argv[i+2]; i++) {
- struct tdb_context *tdb;
- char c;
-
- switch (fork()) {
- case -1:
- err(1, "fork failed");
- case 0:
- close(fds[1]);
- tdb = tdb_open(argv[1], hashsize[i],
- tdb_flags[i], open_flags[i], 0600);
- if (!tdb)
- err(1, "Opening tdb %s", argv[1]);
-
- /* This catches parent exiting. */
- if (read(fds[0], &c, 1) != 1)
- exit(1);
- run_ops(tdb, pipes[i].fd[0], argv+2, op, i, 1,
- num_ops[i], false);
- check_deps(argv[2+i], op[i], num_ops[i]);
- exit(0);
- default:
- break;
- }
- }
-
- /* Let everything settle. */
- sleep(1);
-
- if (!quiet)
- printf("Starting run...");
- fflush(stdout);
- gettimeofday(&start, NULL);
- /* Tell them all to go! Any write of sufficient length will do. */
- if (write(fds[1], hashsize, i) != i)
- err(1, "Writing to wakeup pipe");
-
- for (i = 0; argv[i + 2]; i++) {
- int status;
- wait(&status);
- if (!WIFEXITED(status)) {
- warnx("Child died with signal %i", WTERMSIG(status));
- ok = false;
- } else if (WEXITSTATUS(status) != 0)
- /* Assume child spat out error. */
- ok = false;
- }
- if (!ok)
- exit(1);
-
- gettimeofday(&end, NULL);
- if (!quiet)
- printf("done\n");
-
- if (end.tv_usec < start.tv_usec) {
- end.tv_usec += 1000000;
- end.tv_sec--;
- }
- diff.tv_sec = end.tv_sec - start.tv_sec;
- diff.tv_usec = end.tv_usec - start.tv_usec;
- return diff;
-}
-
-static void init_tdb(struct tdb_context *master_tdb,
- const char *name, unsigned int hashsize)
-{
- TDB_DATA key, data;
- struct tdb_context *tdb;
-
- tdb = tdb_open(name, hashsize, TDB_CLEAR_IF_FIRST|TDB_NOSYNC,
- O_CREAT|O_TRUNC|O_RDWR, 0600);
- if (!tdb)
- errx(1, "opening tdb %s", name);
-
- for (key = tdb_firstkey(master_tdb);
- key.dptr;
- key = tdb_nextkey(master_tdb, key)) {
- data = tdb_fetch(master_tdb, key);
- if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
- errx(1, "Failed to store initial key");
- }
- tdb_close(tdb);
-}
-
-int main(int argc, char *argv[])
-{
- struct timeval diff;
- unsigned int i, num_ops[argc], hashsize[argc], tdb_flags[argc], open_flags[argc];
- struct op *op[argc];
- int fds[2];
- struct tdb_context *master;
- unsigned int runs = 1;
-
- if (argc < 3)
- errx(1, "Usage: %s [--quiet] [-n <number>] <tdbfile> <tracefile>...", argv[0]);
-
- if (streq(argv[1], "--quiet")) {
- quiet = true;
- argv++;
- argc--;
- }
- if (streq(argv[1], "-n")) {
- runs = atoi(argv[2]);
- argv += 2;
- argc -= 2;
- }
-
- pipes = talloc_array(NULL, struct pipe, argc - 1);
- for (i = 0; i < argc - 2; i++) {
- if (!quiet)
- printf("Loading tracefile %s...", argv[2+i]);
- fflush(stdout);
- op[i] = load_tracefile(argv+2, i, &num_ops[i], &hashsize[i],
- &tdb_flags[i], &open_flags[i]);
- if (pipe(pipes[i].fd) != 0)
- err(1, "creating pipe");
- /* Don't truncate, or clear if first: we do that. */
- open_flags[i] &= ~(O_TRUNC);
- tdb_flags[i] &= ~(TDB_CLEAR_IF_FIRST);
- /* Open NOSYNC, to save time. */
- tdb_flags[i] |= TDB_NOSYNC;
- if (!quiet)
- printf("done\n");
- }
-
- /* Dependency may figure we need to create seed records. */
- master = tdb_open(NULL, 0, TDB_INTERNAL, O_RDWR, 0);
- if (!quiet) {
- printf("Calculating inter-dependencies...");
- fflush(stdout);
- }
- derive_dependencies(master, argv+2, op, num_ops, i);
- if (!quiet)
- printf("done\n");
-
- for (i = 0; i < runs; i++) {
- init_tdb(master, argv[1], hashsize[0]);
-
- /* Don't fork for single arg case: simple debugging. */
- if (argc == 3) {
- struct timeval start, end;
- struct tdb_context *tdb;
-
- tdb = tdb_open(argv[1], hashsize[0], tdb_flags[0],
- open_flags[0], 0600);
- if (!quiet) {
- printf("Single threaded run...");
- fflush(stdout);
- }
- gettimeofday(&start, NULL);
-
- run_ops(tdb, pipes[0].fd[0], argv+2, op, 0, 1,
- num_ops[0], false);
- gettimeofday(&end, NULL);
- if (!quiet)
- printf("done\n");
- tdb_close(tdb);
-
- check_deps(argv[2], op[0], num_ops[0]);
- if (end.tv_usec < start.tv_usec) {
- end.tv_usec += 1000000;
- end.tv_sec--;
- }
- diff.tv_sec = end.tv_sec - start.tv_sec;
- diff.tv_usec = end.tv_usec - start.tv_usec;
- goto print_time;
- }
-
- if (pipe(fds) != 0)
- err(1, "creating pipe");
-
-#if TRAVERSALS_TAKE_TRANSACTION_LOCK
- if (pipe(pipes[argc-2].fd) != 0)
- err(1, "creating pipe");
- backoff_fd = pipes[argc-2].fd[1];
- set_nonblock(pipes[argc-2].fd[1]);
- set_nonblock(pipes[argc-2].fd[0]);
-#endif
-
- do {
- diff = run_test(argv, num_ops, hashsize, tdb_flags,
- open_flags, op, fds);
- } while (handle_backoff(op, pipes[argc-2].fd[0]));
-
- print_time:
- if (!quiet)
- printf("Time replaying: ");
- printf("%lu usec\n", diff.tv_sec * 1000000UL + diff.tv_usec);
- }
-
- exit(0);
-}
+++ /dev/null
-/* Simple speed test for TDB */
-#include <err.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <ccan/tdb/tdb.h>
-
-/* Nanoseconds per operation */
-static size_t normalize(const struct timeval *start,
- const struct timeval *stop,
- unsigned int num)
-{
- struct timeval diff;
-
- timersub(stop, start, &diff);
-
- /* Floating point is more accurate here. */
- return (double)(diff.tv_sec * 1000000 + diff.tv_usec)
- / num * 1000;
-}
-
-static size_t file_size(void)
-{
- struct stat st;
-
- if (stat("/tmp/speed.tdb", &st) != 0)
- return -1;
- return st.st_size;
-}
-
-static int count_record(struct tdb_context *tdb,
- TDB_DATA key, TDB_DATA data, void *p)
-{
- int *total = p;
- *total += *(int *)data.dptr;
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i, j, num = 1000, stage = 0, stopat = -1;
- int flags = TDB_DEFAULT;
- TDB_DATA key, data;
- struct tdb_context *tdb;
- struct timeval start, stop;
- bool transaction = false;
-
- if (argv[1] && strcmp(argv[1], "--internal") == 0) {
- flags = TDB_INTERNAL;
- argc--;
- argv++;
- }
-
- if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
- transaction = true;
- argc--;
- argv++;
- }
-
- tdb = tdb_open("/tmp/speed.tdb", 100003, flags, O_RDWR|O_CREAT|O_TRUNC,
- 0600);
- if (!tdb)
- err(1, "Opening /tmp/speed.tdb");
-
- key.dptr = (void *)&i;
- key.dsize = sizeof(i);
- data = key;
-
- if (argv[1]) {
- num = atoi(argv[1]);
- argv++;
- argc--;
- }
-
- if (argv[1]) {
- stopat = atoi(argv[1]);
- argv++;
- argc--;
- }
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Add 1000 records. */
- printf("Adding %u records: ", num); fflush(stdout);
- gettimeofday(&start, NULL);
- for (i = 0; i < num; i++)
- if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
- errx(1, "Inserting key %u in tdb: %s",
- i, tdb_errorstr(tdb));
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
- if (++stage == stopat)
- exit(0);
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Finding 1000 records. */
- printf("Finding %u records: ", num); fflush(stdout);
- gettimeofday(&start, NULL);
- for (i = 0; i < num; i++) {
- int *dptr;
- dptr = (int *)tdb_fetch(tdb, key).dptr;
- if (!dptr || *dptr != i)
- errx(1, "Fetching key %u in tdb gave %u",
- i, dptr ? *dptr : -1);
- }
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
- if (++stage == stopat)
- exit(0);
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Missing 1000 records. */
- printf("Missing %u records: ", num); fflush(stdout);
- gettimeofday(&start, NULL);
- for (i = num; i < num*2; i++) {
- int *dptr;
- dptr = (int *)tdb_fetch(tdb, key).dptr;
- if (dptr)
- errx(1, "Fetching key %u in tdb gave %u", i, *dptr);
- }
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
- if (++stage == stopat)
- exit(0);
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Traverse 1000 records. */
- printf("Traversing %u records: ", num); fflush(stdout);
- i = 0;
- gettimeofday(&start, NULL);
- if (tdb_traverse(tdb, count_record, &i) != num)
- errx(1, "Traverse returned wrong number of records");
- if (i != (num - 1) * (num / 2))
- errx(1, "Traverse tallied to %u", i);
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
- if (++stage == stopat)
- exit(0);
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Delete 1000 records (not in order). */
- printf("Deleting %u records: ", num); fflush(stdout);
- gettimeofday(&start, NULL);
- for (j = 0; j < num; j++) {
- i = (j + 100003) % num;
- if (tdb_delete(tdb, key) != 0)
- errx(1, "Deleting key %u in tdb: %s",
- i, tdb_errorstr(tdb));
- }
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
- if (++stage == stopat)
- exit(0);
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Re-add 1000 records (not in order). */
- printf("Re-adding %u records: ", num); fflush(stdout);
- gettimeofday(&start, NULL);
- for (j = 0; j < num; j++) {
- i = (j + 100003) % num;
- if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
- errx(1, "Inserting key %u in tdb: %s",
- i, tdb_errorstr(tdb));
- }
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
- if (++stage == stopat)
- exit(0);
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Append 1000 records. */
- printf("Appending %u records: ", num); fflush(stdout);
- gettimeofday(&start, NULL);
- for (i = 0; i < num; i++)
- if (tdb_append(tdb, key, data) != 0)
- errx(1, "Appending key %u in tdb: %s",
- i, tdb_errorstr(tdb));
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
- if (++stage == stopat)
- exit(0);
-
- if (transaction && tdb_transaction_start(tdb))
- errx(1, "starting transaction: %s", tdb_errorstr(tdb));
-
- /* Churn 1000 records: not in order! */
- printf("Churning %u records: ", num); fflush(stdout);
- gettimeofday(&start, NULL);
- for (j = 0; j < num; j++) {
- i = (j + 1000019) % num;
- if (tdb_delete(tdb, key) != 0)
- errx(1, "Deleting key %u in tdb: %s",
- i, tdb_errorstr(tdb));
- i += num;
- if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
- errx(1, "Inserting key %u in tdb: %s",
- i, tdb_errorstr(tdb));
- }
- gettimeofday(&stop, NULL);
- if (transaction && tdb_transaction_commit(tdb))
- errx(1, "committing transaction: %s", tdb_errorstr(tdb));
- printf(" %zu ns (%zu bytes)\n",
- normalize(&start, &stop, num), file_size());
-
- return 0;
-}
+++ /dev/null
-/* Demonstrate starvation of tdb_lockall */
-#include <ccan/tdb/tdb.h>
-#include <err.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/time.h>
-
-static void usage(const char *extra)
-{
- errx(1, "%s%s"
- "Usage: starvation [lockall|gradual] <num> <worktime-in-ms>\n"
- " Each locker holds lock for between 1/2 and 1 1/2 times\n"
- " worktime, then sleeps for one second.\n\n"
- " Main process tries tdb_lockall or tdb_lockall_gradual.",
- extra ? extra : "", extra ? "\n" : "");
-}
-
-static void run_and_sleep(struct tdb_context *tdb, int parentfd, unsigned time)
-{
- char c;
- struct timespec hold;
- unsigned rand, randtime;
- TDB_DATA key;
-
- key.dptr = (void *)&rand;
- key.dsize = sizeof(rand);
-
- while (read(parentfd, &c, 1) != 0) {
- /* Lock a random key. */
- rand = random();
- if (tdb_chainlock(tdb, key) != 0)
- errx(1, "chainlock failed: %s", tdb_errorstr(tdb));
-
- /* Hold it for some variable time. */
- randtime = time / 2 + (random() % time);
- hold.tv_sec = randtime / 1000;
- hold.tv_nsec = (randtime % 1000) * 1000000;
- nanosleep(&hold, NULL);
-
- if (tdb_chainunlock(tdb, key) != 0)
- errx(1, "chainunlock failed: %s", tdb_errorstr(tdb));
-
- /* Wait for a second without the lock. */
- sleep(1);
- }
- exit(0);
-}
-
-static void logfn(struct tdb_context *tdb,
- enum tdb_debug_level level,
- const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
-
-int main(int argc, char *argv[])
-{
- int (*lockall)(struct tdb_context *);
- unsigned int num, worktime, i;
- int pfd[2];
- struct tdb_context *tdb;
- struct tdb_logging_context log = { logfn, NULL };
- struct timeval start, end, duration;
-
- if (argc != 4)
- usage(NULL);
-
- if (strcmp(argv[1], "lockall") == 0)
- lockall = tdb_lockall;
- else if (strcmp(argv[1], "gradual") == 0) {
-#if 0
- lockall = tdb_lockall_gradual;
-#else
- errx(1, "gradual is now the default implementation");
-#endif
- } else
- usage("Arg1 should be 'lockall' or 'gradual'");
-
- num = atoi(argv[2]);
- worktime = atoi(argv[3]);
-
- if (!num || !worktime)
- usage("Number of threads and worktime must be non-zero");
-
- if (pipe(pfd) != 0)
- err(1, "Creating pipe");
-
- tdb = tdb_open_ex("/tmp/starvation.tdb", 10000, TDB_DEFAULT,
- O_RDWR|O_CREAT|O_TRUNC, 0600, &log, NULL);
- if (!tdb)
- err(1, "Opening tdb /tmp/starvation.tdb");
-
- for (i = 0; i < num; i++) {
- switch (fork()) {
- case 0:
- close(pfd[1]);
- fcntl(pfd[0], F_SETFL,
- fcntl(pfd[0], F_GETFL)|O_NONBLOCK);
- srandom(getpid() + i);
- if (tdb_reopen(tdb) != 0)
- err(1, "Reopening tdb %s", tdb_name(tdb));
-
- run_and_sleep(tdb, pfd[0], worktime);
- case -1:
- err(1, "forking");
- }
- /* Stagger the children. */
- usleep(random() % (1000000 / num));
- }
-
- close(pfd[0]);
- sleep(1);
- gettimeofday(&start, NULL);
- if (lockall(tdb) != 0)
- errx(1, "lockall failed: %s", tdb_errorstr(tdb));
- gettimeofday(&end, NULL);
-
- duration.tv_sec = end.tv_sec - start.tv_sec;
- duration.tv_usec = end.tv_usec - start.tv_usec;
- if (duration.tv_usec < 0) {
- --duration.tv_sec;
- duration.tv_usec += 1000000;
- }
-
- if (tdb_unlockall(tdb) != 0)
- errx(1, "unlockall failed: %s", tdb_errorstr(tdb));
- tdb_close(tdb);
- unlink("/tmp/starvation.tdb");
-
- printf("Took %lu.%06lu seconds\n", duration.tv_sec, duration.tv_usec);
- return 0;
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
- simple tdb dump util
- Copyright (C) Andrew Tridgell 2001
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <ccan/tdb/tdb.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <ctype.h>
-
-static void print_data(TDB_DATA d)
-{
- unsigned char *p = (unsigned char *)d.dptr;
- int len = d.dsize;
- while (len--) {
- if (isprint(*p) && !strchr("\"\\", *p)) {
- fputc(*p, stdout);
- } else {
- printf("\\%02X", *p);
- }
- p++;
- }
-}
-
-static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
-{
- printf("{\n");
- printf("key(%d) = \"", (int)key.dsize);
- print_data(key);
- printf("\"\n");
- printf("data(%d) = \"", (int)dbuf.dsize);
- print_data(dbuf);
- printf("\"\n");
- printf("}\n");
- return 0;
-}
-
-static int dump_tdb(const char *fname, const char *keyname)
-{
- TDB_CONTEXT *tdb;
- TDB_DATA key, value;
-
- tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
- if (!tdb) {
- printf("Failed to open %s\n", fname);
- return 1;
- }
-
- if (!keyname) {
- tdb_traverse(tdb, traverse_fn, NULL);
- } else {
- key.dptr = (void *)keyname;
- key.dsize = strlen( keyname);
- value = tdb_fetch(tdb, key);
- if (!value.dptr) {
- return 1;
- } else {
- print_data(value);
- free(value.dptr);
- }
- }
-
- return 0;
-}
-
-static void usage( void)
-{
- printf( "Usage: tdbdump [options] <filename>\n\n");
- printf( " -h this help message\n");
- printf( " -k keyname dumps value of keyname\n");
-}
-
- int main(int argc, char *argv[])
-{
- char *fname, *keyname=NULL;
- int c;
-
- if (argc < 2) {
- printf("Usage: tdbdump <fname>\n");
- exit(1);
- }
-
- while ((c = getopt( argc, argv, "hk:")) != -1) {
- switch (c) {
- case 'h':
- usage();
- exit( 0);
- case 'k':
- keyname = optarg;
- break;
- default:
- usage();
- exit( 1);
- }
- }
-
- fname = argv[optind];
-
- return dump_tdb(fname, keyname);
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
- Samba database functions
- Copyright (C) Andrew Tridgell 1999-2000
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000
- Copyright (C) Andrew Esh 2001
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <ccan/tdb/tdb.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <stdarg.h>
-
-static int do_command(void);
-const char *cmdname;
-char *arg1, *arg2;
-size_t arg1len, arg2len;
-int bIterate = 0;
-char *line;
-TDB_DATA iterate_kbuf;
-char cmdline[1024];
-static int disable_mmap;
-
-enum commands {
- CMD_CREATE_TDB,
- CMD_OPEN_TDB,
- CMD_TRANSACTION_START,
- CMD_TRANSACTION_COMMIT,
- CMD_TRANSACTION_CANCEL,
- CMD_ERASE,
- CMD_DUMP,
- CMD_INSERT,
- CMD_MOVE,
- CMD_STORE,
- CMD_SHOW,
- CMD_KEYS,
- CMD_HEXKEYS,
- CMD_DELETE,
- CMD_LIST_HASH_FREE,
- CMD_LIST_FREE,
- CMD_INFO,
- CMD_MMAP,
- CMD_SPEED,
- CMD_FIRST,
- CMD_NEXT,
- CMD_SYSTEM,
- CMD_CHECK,
- CMD_QUIT,
- CMD_HELP
-};
-
-typedef struct {
- const char *name;
- enum commands cmd;
-} COMMAND_TABLE;
-
-COMMAND_TABLE cmd_table[] = {
- {"create", CMD_CREATE_TDB},
- {"open", CMD_OPEN_TDB},
- {"transaction_start", CMD_TRANSACTION_START},
- {"transaction_commit", CMD_TRANSACTION_COMMIT},
- {"transaction_cancel", CMD_TRANSACTION_CANCEL},
- {"erase", CMD_ERASE},
- {"dump", CMD_DUMP},
- {"insert", CMD_INSERT},
- {"move", CMD_MOVE},
- {"store", CMD_STORE},
- {"show", CMD_SHOW},
- {"keys", CMD_KEYS},
- {"hexkeys", CMD_HEXKEYS},
- {"delete", CMD_DELETE},
- {"list", CMD_LIST_HASH_FREE},
- {"free", CMD_LIST_FREE},
- {"info", CMD_INFO},
- {"speed", CMD_SPEED},
- {"mmap", CMD_MMAP},
- {"first", CMD_FIRST},
- {"1", CMD_FIRST},
- {"next", CMD_NEXT},
- {"n", CMD_NEXT},
- {"check", CMD_CHECK},
- {"quit", CMD_QUIT},
- {"q", CMD_QUIT},
- {"!", CMD_SYSTEM},
- {NULL, CMD_HELP}
-};
-
-struct timeval tp1,tp2;
-
-static void _start_timer(void)
-{
- gettimeofday(&tp1,NULL);
-}
-
-static double _end_timer(void)
-{
- gettimeofday(&tp2,NULL);
- return((tp2.tv_sec - tp1.tv_sec) +
- (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
-}
-
-static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- vfprintf(stderr, format, ap);
- va_end(ap);
-}
-
-/* a tdb tool for manipulating a tdb database */
-
-static TDB_CONTEXT *tdb;
-
-static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
-static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
-static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
-
-static void print_asc(const char *buf,int len)
-{
- int i;
-
- /* We're probably printing ASCII strings so don't try to display
- the trailing NULL character. */
-
- if (buf[len - 1] == 0)
- len--;
-
- for (i=0;i<len;i++)
- printf("%c",isprint(buf[i])?buf[i]:'.');
-}
-
-static void print_data(const char *buf,int len)
-{
- int i=0;
- if (len<=0) return;
- printf("[%03X] ",i);
- for (i=0;i<len;) {
- printf("%02X ",(int)((unsigned char)buf[i]));
- i++;
- if (i%8 == 0) printf(" ");
- if (i%16 == 0) {
- print_asc(&buf[i-16],8); printf(" ");
- print_asc(&buf[i-8],8); printf("\n");
- if (i<len) printf("[%03X] ",i);
- }
- }
- if (i%16) {
- int n;
-
- n = 16 - (i%16);
- printf(" ");
- if (n>8) printf(" ");
- while (n--) printf(" ");
-
- n = i%16;
- if (n > 8) n = 8;
- print_asc(&buf[i-(i%16)],n); printf(" ");
- n = (i%16) - n;
- if (n>0) print_asc(&buf[i-n],n);
- printf("\n");
- }
-}
-
-static void help(void)
-{
- printf("\n"
-"tdbtool: \n"
-" create dbname : create a database\n"
-" open dbname : open an existing database\n"
-" openjh dbname : open an existing database (jenkins hash)\n"
-" transaction_start : start a transaction\n"
-" transaction_commit : commit a transaction\n"
-" transaction_cancel : cancel a transaction\n"
-" erase : erase the database\n"
-" dump : dump the database as strings\n"
-" keys : dump the database keys as strings\n"
-" hexkeys : dump the database keys as hex values\n"
-" info : print summary info about the database\n"
-" insert key data : insert a record\n"
-" move key file : move a record to a destination tdb\n"
-" store key data : store a record (replace)\n"
-" show key : show a record by key\n"
-" delete key : delete a record by key\n"
-" list : print the database hash table and freelist\n"
-" free : print the database freelist\n"
-" check : check the integrity of an opened database\n"
-" speed : perform speed tests on the database\n"
-" ! command : execute system command\n"
-" 1 | first : print the first record\n"
-" n | next : print the next record\n"
-" q | quit : terminate\n"
-" \\n : repeat 'next' command\n"
-"\n");
-}
-
-static void terror(const char *why)
-{
- printf("%s\n", why);
-}
-
-static void create_tdb(const char *tdbname)
-{
- struct tdb_logging_context log_ctx;
- log_ctx.log_fn = tdb_log;
-
- if (tdb) tdb_close(tdb);
- tdb = tdb_open_ex(tdbname, 0, TDB_CLEAR_IF_FIRST | (disable_mmap?TDB_NOMMAP:0),
- O_RDWR | O_CREAT | O_TRUNC, 0600, &log_ctx, NULL);
- if (!tdb) {
- printf("Could not create %s: %s\n", tdbname, strerror(errno));
- }
-}
-
-static void open_tdb(const char *tdbname)
-{
- struct tdb_logging_context log_ctx;
- log_ctx.log_fn = tdb_log;
-
- if (tdb) tdb_close(tdb);
- tdb = tdb_open_ex(tdbname, 0, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600,
- &log_ctx, NULL);
- if (!tdb) {
- printf("Could not open %s: %s\n", tdbname, strerror(errno));
- }
-}
-
-static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
-{
- TDB_DATA key, dbuf;
-
- if ((keyname == NULL) || (keylen == 0)) {
- terror("need key");
- return;
- }
-
- key.dptr = (unsigned char *)keyname;
- key.dsize = keylen;
- dbuf.dptr = (unsigned char *)data;
- dbuf.dsize = datalen;
-
- if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
- terror("insert failed");
- }
-}
-
-static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
-{
- TDB_DATA key, dbuf;
-
- if ((keyname == NULL) || (keylen == 0)) {
- terror("need key");
- return;
- }
-
- if ((data == NULL) || (datalen == 0)) {
- terror("need data");
- return;
- }
-
- key.dptr = (unsigned char *)keyname;
- key.dsize = keylen;
- dbuf.dptr = (unsigned char *)data;
- dbuf.dsize = datalen;
-
- printf("Storing key:\n");
- print_rec(tdb, key, dbuf, NULL);
-
- if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
- terror("store failed");
- }
-}
-
-static void show_tdb(char *keyname, size_t keylen)
-{
- TDB_DATA key, dbuf;
-
- if ((keyname == NULL) || (keylen == 0)) {
- terror("need key");
- return;
- }
-
- key.dptr = (unsigned char *)keyname;
- key.dsize = keylen;
-
- dbuf = tdb_fetch(tdb, key);
- if (!dbuf.dptr) {
- terror("fetch failed");
- return;
- }
-
- print_rec(tdb, key, dbuf, NULL);
-
- free( dbuf.dptr );
-
- return;
-}
-
-static void delete_tdb(char *keyname, size_t keylen)
-{
- TDB_DATA key;
-
- if ((keyname == NULL) || (keylen == 0)) {
- terror("need key");
- return;
- }
-
- key.dptr = (unsigned char *)keyname;
- key.dsize = keylen;
-
- if (tdb_delete(tdb, key) != 0) {
- terror("delete failed");
- }
-}
-
-static void move_rec(char *keyname, size_t keylen, char* tdbname)
-{
- TDB_DATA key, dbuf;
- TDB_CONTEXT *dst_tdb;
-
- if ((keyname == NULL) || (keylen == 0)) {
- terror("need key");
- return;
- }
-
- if ( !tdbname ) {
- terror("need destination tdb name");
- return;
- }
-
- key.dptr = (unsigned char *)keyname;
- key.dsize = keylen;
-
- dbuf = tdb_fetch(tdb, key);
- if (!dbuf.dptr) {
- terror("fetch failed");
- return;
- }
-
- print_rec(tdb, key, dbuf, NULL);
-
- dst_tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
- if ( !dst_tdb ) {
- terror("unable to open destination tdb");
- return;
- }
-
- if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
- terror("failed to move record");
- }
- else
- printf("record moved\n");
-
- tdb_close( dst_tdb );
-
- return;
-}
-
-static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
-{
- printf("\nkey %d bytes\n", (int)key.dsize);
- print_asc((const char *)key.dptr, key.dsize);
- printf("\ndata %d bytes\n", (int)dbuf.dsize);
- print_data((const char *)dbuf.dptr, dbuf.dsize);
- return 0;
-}
-
-static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
-{
- printf("key %d bytes: ", (int)key.dsize);
- print_asc((const char *)key.dptr, key.dsize);
- printf("\n");
- return 0;
-}
-
-static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
-{
- printf("key %d bytes\n", (int)key.dsize);
- print_data((const char *)key.dptr, key.dsize);
- printf("\n");
- return 0;
-}
-
-static int total_bytes;
-
-static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
-{
- total_bytes += dbuf.dsize;
- return 0;
-}
-
-static void info_tdb(void)
-{
- char *summary = tdb_summary(tdb);
-
- if (!summary) {
- printf("Error = %s\n", tdb_errorstr(tdb));
- } else {
- printf("%s", summary);
- free(summary);
- }
-}
-
-static void speed_tdb(const char *tlimit)
-{
- unsigned timelimit = tlimit?atoi(tlimit):0;
- double t;
- int ops;
- if (timelimit == 0) timelimit = 5;
-
- ops = 0;
- printf("Testing store speed for %u seconds\n", timelimit);
- _start_timer();
- do {
- long int r = random();
- TDB_DATA key, dbuf;
- key.dptr = (unsigned char *)"store test";
- key.dsize = strlen((char *)key.dptr);
- dbuf.dptr = (unsigned char *)&r;
- dbuf.dsize = sizeof(r);
- tdb_store(tdb, key, dbuf, TDB_REPLACE);
- t = _end_timer();
- ops++;
- } while (t < timelimit);
- printf("%10.3f ops/sec\n", ops/t);
-
- ops = 0;
- printf("Testing fetch speed for %u seconds\n", timelimit);
- _start_timer();
- do {
- long int r = random();
- TDB_DATA key, dbuf;
- key.dptr = (unsigned char *)"store test";
- key.dsize = strlen((char *)key.dptr);
- dbuf.dptr = (unsigned char *)&r;
- dbuf.dsize = sizeof(r);
- tdb_fetch(tdb, key);
- t = _end_timer();
- ops++;
- } while (t < timelimit);
- printf("%10.3f ops/sec\n", ops/t);
-
- ops = 0;
- printf("Testing transaction speed for %u seconds\n", timelimit);
- _start_timer();
- do {
- long int r = random();
- TDB_DATA key, dbuf;
- key.dptr = (unsigned char *)"transaction test";
- key.dsize = strlen((char *)key.dptr);
- dbuf.dptr = (unsigned char *)&r;
- dbuf.dsize = sizeof(r);
- tdb_transaction_start(tdb);
- tdb_store(tdb, key, dbuf, TDB_REPLACE);
- tdb_transaction_commit(tdb);
- t = _end_timer();
- ops++;
- } while (t < timelimit);
- printf("%10.3f ops/sec\n", ops/t);
-
- ops = 0;
- printf("Testing traverse speed for %u seconds\n", timelimit);
- _start_timer();
- do {
- tdb_traverse(tdb, traverse_fn, NULL);
- t = _end_timer();
- ops++;
- } while (t < timelimit);
- printf("%10.3f ops/sec\n", ops/t);
-}
-
-static void toggle_mmap(void)
-{
- disable_mmap = !disable_mmap;
- if (disable_mmap) {
- printf("mmap is disabled\n");
- } else {
- printf("mmap is enabled\n");
- }
-}
-
-static char *tdb_getline(const char *prompt)
-{
- static char thisline[1024];
- char *p;
- fputs(prompt, stdout);
- thisline[0] = 0;
- p = fgets(thisline, sizeof(thisline)-1, stdin);
- if (p) p = strchr(p, '\n');
- if (p) *p = 0;
- return p?thisline:NULL;
-}
-
-static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
- void *state)
-{
- return tdb_delete(the_tdb, key);
-}
-
-static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
-{
- TDB_DATA dbuf;
- *pkey = tdb_firstkey(the_tdb);
-
- dbuf = tdb_fetch(the_tdb, *pkey);
- if (!dbuf.dptr) terror("fetch failed");
- else {
- print_rec(the_tdb, *pkey, dbuf, NULL);
- }
-}
-
-static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
-{
- TDB_DATA dbuf;
- *pkey = tdb_nextkey(the_tdb, *pkey);
-
- dbuf = tdb_fetch(the_tdb, *pkey);
- if (!dbuf.dptr)
- terror("fetch failed");
- else
- print_rec(the_tdb, *pkey, dbuf, NULL);
-}
-
-static void check_db(TDB_CONTEXT *the_tdb)
-{
- if (!the_tdb) {
- printf("Error: No database opened!\n");
- } else {
- if (tdb_check(the_tdb, NULL, NULL) == -1)
- printf("Integrity check for the opened database failed.\n");
- else
- printf("Database integrity is OK.\n");
- }
-}
-
-static int do_command(void)
-{
- COMMAND_TABLE *ctp = cmd_table;
- enum commands mycmd = CMD_HELP;
- int cmd_len;
-
- if (cmdname && strlen(cmdname) == 0) {
- mycmd = CMD_NEXT;
- } else {
- while (ctp->name) {
- cmd_len = strlen(ctp->name);
- if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
- mycmd = ctp->cmd;
- break;
- }
- ctp++;
- }
- }
-
- switch (mycmd) {
- case CMD_CREATE_TDB:
- bIterate = 0;
- create_tdb(arg1);
- return 0;
- case CMD_OPEN_TDB:
- bIterate = 0;
- open_tdb(arg1);
- return 0;
- case CMD_SYSTEM:
- /* Shell command */
- if (system(arg1) == -1) {
- terror("system() call failed\n");
- }
- return 0;
- case CMD_QUIT:
- return 1;
- default:
- /* all the rest require a open database */
- if (!tdb) {
- bIterate = 0;
- terror("database not open");
- help();
- return 0;
- }
- switch (mycmd) {
- case CMD_TRANSACTION_START:
- bIterate = 0;
- tdb_transaction_start(tdb);
- return 0;
- case CMD_TRANSACTION_COMMIT:
- bIterate = 0;
- tdb_transaction_commit(tdb);
- return 0;
- case CMD_TRANSACTION_CANCEL:
- bIterate = 0;
- tdb_transaction_cancel(tdb);
- return 0;
- case CMD_ERASE:
- bIterate = 0;
- tdb_traverse(tdb, do_delete_fn, NULL);
- return 0;
- case CMD_DUMP:
- bIterate = 0;
- tdb_traverse(tdb, print_rec, NULL);
- return 0;
- case CMD_INSERT:
- bIterate = 0;
- insert_tdb(arg1, arg1len,arg2,arg2len);
- return 0;
- case CMD_MOVE:
- bIterate = 0;
- move_rec(arg1,arg1len,arg2);
- return 0;
- case CMD_STORE:
- bIterate = 0;
- store_tdb(arg1,arg1len,arg2,arg2len);
- return 0;
- case CMD_SHOW:
- bIterate = 0;
- show_tdb(arg1, arg1len);
- return 0;
- case CMD_KEYS:
- tdb_traverse(tdb, print_key, NULL);
- return 0;
- case CMD_HEXKEYS:
- tdb_traverse(tdb, print_hexkey, NULL);
- return 0;
- case CMD_DELETE:
- bIterate = 0;
- delete_tdb(arg1,arg1len);
- return 0;
- case CMD_LIST_HASH_FREE:
- tdb_dump_all(tdb);
- return 0;
- case CMD_LIST_FREE:
- tdb_printfreelist(tdb);
- return 0;
- case CMD_INFO:
- info_tdb();
- return 0;
- case CMD_SPEED:
- speed_tdb(arg1);
- return 0;
- case CMD_MMAP:
- toggle_mmap();
- return 0;
- case CMD_FIRST:
- bIterate = 1;
- first_record(tdb, &iterate_kbuf);
- return 0;
- case CMD_NEXT:
- if (bIterate)
- next_record(tdb, &iterate_kbuf);
- return 0;
- case CMD_CHECK:
- check_db(tdb);
- return 0;
- case CMD_HELP:
- help();
- return 0;
- case CMD_CREATE_TDB:
- case CMD_OPEN_TDB:
- case CMD_SYSTEM:
- case CMD_QUIT:
- /*
- * unhandled commands. cases included here to avoid compiler
- * warnings.
- */
- return 0;
- }
- }
-
- return 0;
-}
-
-static char *convert_string(char *instring, size_t *sizep)
-{
- size_t length = 0;
- char *outp, *inp;
- char temp[3];
-
- outp = inp = instring;
-
- while (*inp) {
- if (*inp == '\\') {
- inp++;
- if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
- temp[0] = *inp++;
- temp[1] = '\0';
- if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
- temp[1] = *inp++;
- temp[2] = '\0';
- }
- *outp++ = (char)strtol((const char *)temp,NULL,16);
- } else {
- *outp++ = *inp++;
- }
- } else {
- *outp++ = *inp++;
- }
- length++;
- }
- *sizep = length;
- return instring;
-}
-
-int main(int argc, char *argv[])
-{
- cmdname = "";
- arg1 = NULL;
- arg1len = 0;
- arg2 = NULL;
- arg2len = 0;
-
- if (argv[1]) {
- cmdname = "open";
- arg1 = argv[1];
- do_command();
- cmdname = "";
- arg1 = NULL;
- }
-
- switch (argc) {
- case 1:
- case 2:
- /* Interactive mode */
- while ((cmdname = tdb_getline("tdb> "))) {
- arg2 = arg1 = NULL;
- if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
- arg1++;
- arg2 = arg1;
- while (*arg2) {
- if (*arg2 == ' ') {
- *arg2++ = '\0';
- break;
- }
- if ((*arg2++ == '\\') && (*arg2 == ' ')) {
- arg2++;
- }
- }
- }
- if (arg1) arg1 = convert_string(arg1,&arg1len);
- if (arg2) arg2 = convert_string(arg2,&arg2len);
- if (do_command()) break;
- }
- break;
- case 5:
- arg2 = convert_string(argv[4],&arg2len);
- case 4:
- arg1 = convert_string(argv[3],&arg1len);
- case 3:
- cmdname = argv[2];
- default:
- do_command();
- break;
- }
-
- if (tdb) tdb_close(tdb);
-
- return 0;
-}
+++ /dev/null
-/* this tests tdb by doing lots of ops from several simultaneous
- writers - that stresses the locking code.
-*/
-
-#include <ccan/tdb/tdb.h>
-#include <stdlib.h>
-#include <err.h>
-#include <getopt.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <time.h>
-#include <sys/wait.h>
-
-#define REOPEN_PROB 30
-#define DELETE_PROB 8
-#define STORE_PROB 4
-#define APPEND_PROB 6
-#define TRANSACTION_PROB 10
-#define TRANSACTION_PREPARE_PROB 2
-#define LOCKSTORE_PROB 5
-#define TRAVERSE_PROB 20
-#define TRAVERSE_READ_PROB 20
-#define TRAVERSE_MOD_PROB 100
-#define TRAVERSE_ABORT_PROB 500
-#define CULL_PROB 100
-#define KEYLEN 3
-#define DATALEN 100
-
-static struct tdb_context *db;
-static int in_transaction;
-static int in_traverse;
-static int error_count;
-static int always_transaction = 0;
-static int hash_size = 2;
-static int loopnum;
-static int count_pipe;
-static struct tdb_logging_context log_ctx;
-
-#ifdef PRINTF_FMT
-static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_FMT(3,4);
-#endif
-static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
-{
- va_list ap;
-
- if (level != TDB_DEBUG_TRACE)
- error_count++;
-
- va_start(ap, format);
- vfprintf(stdout, format, ap);
- va_end(ap);
- fflush(stdout);
-#if 0
- if (level != TDB_DEBUG_TRACE) {
- char *ptr;
- signal(SIGUSR1, SIG_IGN);
- asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
- system(ptr);
- free(ptr);
- }
-#endif
-}
-
-static void fatal(const char *why)
-{
- perror(why);
- error_count++;
-}
-
-static char *randbuf(int len)
-{
- char *buf;
- int i;
- buf = (char *)malloc(len+1);
-
- for (i=0;i<len;i++) {
- buf[i] = 'a' + (rand() % 26);
- }
- buf[i] = 0;
- return buf;
-}
-
-static void addrec_db(void);
-static int modify_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
- void *state)
-{
-#if CULL_PROB
- if (random() % CULL_PROB == 0) {
- tdb_delete(tdb, key);
- }
-#endif
-
-#if TRAVERSE_MOD_PROB
- if (random() % TRAVERSE_MOD_PROB == 0) {
- addrec_db();
- }
-#endif
-
-#if TRAVERSE_ABORT_PROB
- if (random() % TRAVERSE_ABORT_PROB == 0)
- return 1;
-#endif
-
- return 0;
-}
-
-static void addrec_db(void)
-{
- int klen, dlen;
- char *k, *d;
- TDB_DATA key, data;
-
- klen = 1 + (rand() % KEYLEN);
- dlen = 1 + (rand() % DATALEN);
-
- k = randbuf(klen);
- d = randbuf(dlen);
-
- key.dptr = (unsigned char *)k;
- key.dsize = klen+1;
-
- data.dptr = (unsigned char *)d;
- data.dsize = dlen+1;
-
-#if REOPEN_PROB
- if (in_traverse == 0 && in_transaction == 0 && random() % REOPEN_PROB == 0) {
- tdb_reopen_all(0);
- goto next;
- }
-#endif
-
-#if TRANSACTION_PROB
- if (in_traverse == 0 && in_transaction == 0 && (always_transaction || random() % TRANSACTION_PROB == 0)) {
- if (tdb_transaction_start(db) != 0) {
- fatal("tdb_transaction_start failed");
- }
- in_transaction++;
- goto next;
- }
- if (in_traverse == 0 && in_transaction && random() % TRANSACTION_PROB == 0) {
- if (random() % TRANSACTION_PREPARE_PROB == 0) {
- if (tdb_transaction_prepare_commit(db) != 0) {
- fatal("tdb_transaction_prepare_commit failed");
- }
- }
- if (tdb_transaction_commit(db) != 0) {
- fatal("tdb_transaction_commit failed");
- }
- in_transaction--;
- goto next;
- }
-
- if (in_traverse == 0 && in_transaction && random() % TRANSACTION_PROB == 0) {
- if (tdb_transaction_cancel(db) != 0) {
- fatal("tdb_transaction_cancel failed");
- }
- in_transaction--;
- goto next;
- }
-#endif
-
-#if DELETE_PROB
- if (random() % DELETE_PROB == 0) {
- tdb_delete(db, key);
- goto next;
- }
-#endif
-
-#if STORE_PROB
- if (random() % STORE_PROB == 0) {
- if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
- fatal("tdb_store failed");
- }
- goto next;
- }
-#endif
-
-#if APPEND_PROB
- if (random() % APPEND_PROB == 0) {
- if (tdb_append(db, key, data) != 0) {
- fatal("tdb_append failed");
- }
- goto next;
- }
-#endif
-
-#if LOCKSTORE_PROB
- if (random() % LOCKSTORE_PROB == 0) {
- tdb_chainlock(db, key);
- data = tdb_fetch(db, key);
- if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
- fatal("tdb_store failed");
- }
- if (data.dptr) free(data.dptr);
- tdb_chainunlock(db, key);
- goto next;
- }
-#endif
-
-#if TRAVERSE_PROB
- /* FIXME: recursive traverses break transactions? */
- if (in_traverse == 0 && random() % TRAVERSE_PROB == 0) {
- in_traverse++;
- tdb_traverse(db, modify_traverse, NULL);
- in_traverse--;
- goto next;
- }
-#endif
-
-#if TRAVERSE_READ_PROB
- if (in_traverse == 0 && random() % TRAVERSE_READ_PROB == 0) {
- in_traverse++;
- tdb_traverse_read(db, NULL, NULL);
- in_traverse--;
- goto next;
- }
-#endif
-
- data = tdb_fetch(db, key);
- if (data.dptr) free(data.dptr);
-
-next:
- free(k);
- free(d);
-}
-
-static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
- void *state)
-{
- tdb_delete(tdb, key);
- return 0;
-}
-
-static void usage(void)
-{
- printf("Usage: tdbtorture [-t] [-k] [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n");
- exit(0);
-}
-
-static void send_count_and_suicide(int sig)
-{
- /* This ensures our successor can continue where we left off. */
- write(count_pipe, &loopnum, sizeof(loopnum));
- /* This gives a unique signature. */
- kill(getpid(), SIGUSR2);
-}
-
-static int run_child(int i, int seed, unsigned num_loops, unsigned start)
-{
- db = tdb_open_ex("torture.tdb", hash_size, TDB_DEFAULT,
- O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
- if (!db) {
- fatal("db open failed");
- }
-
- srand(seed + i);
- srandom(seed + i);
-
- /* Set global, then we're ready to handle being killed. */
- loopnum = start;
- signal(SIGUSR1, send_count_and_suicide);
-
- for (;loopnum<num_loops && error_count == 0;loopnum++) {
- addrec_db();
- }
-
- if (error_count == 0) {
- tdb_traverse_read(db, NULL, NULL);
- if (always_transaction) {
- while (in_transaction) {
- tdb_transaction_cancel(db);
- in_transaction--;
- }
- if (tdb_transaction_start(db) != 0)
- fatal("tdb_transaction_start failed");
- }
- tdb_traverse(db, traverse_fn, NULL);
- tdb_traverse(db, traverse_fn, NULL);
- if (always_transaction) {
- if (tdb_transaction_commit(db) != 0)
- fatal("tdb_transaction_commit failed");
- }
- }
-
- tdb_close(db);
-
- return (error_count < 100 ? error_count : 100);
-}
-
-int main(int argc, char * const *argv)
-{
- int i, seed = -1;
- int num_loops = 5000;
- int num_procs = 3;
- int c, pfds[2];
- extern char *optarg;
- pid_t *pids;
- int kill_random = 0;
- int *done;
-
- log_ctx.log_fn = tdb_log;
-
- while ((c = getopt(argc, argv, "n:l:s:H:thk")) != -1) {
- switch (c) {
- case 'n':
- num_procs = strtol(optarg, NULL, 0);
- break;
- case 'l':
- num_loops = strtol(optarg, NULL, 0);
- break;
- case 'H':
- hash_size = strtol(optarg, NULL, 0);
- break;
- case 's':
- seed = strtol(optarg, NULL, 0);
- break;
- case 't':
- always_transaction = 1;
- break;
- case 'k':
- kill_random = 1;
- break;
- default:
- usage();
- }
- }
-
- unlink("torture.tdb");
-
- if (seed == -1) {
- seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
- }
-
- if (num_procs == 1 && !kill_random) {
- /* Don't fork for this case, makes debugging easier. */
- error_count = run_child(0, seed, num_loops, 0);
- goto done;
- }
-
- pids = (pid_t *)calloc(sizeof(pid_t), num_procs);
- done = (int *)calloc(sizeof(int), num_procs);
-
- if (pipe(pfds) != 0) {
- perror("Creating pipe");
- exit(1);
- }
- count_pipe = pfds[1];
-
- for (i=0;i<num_procs;i++) {
- if ((pids[i]=fork()) == 0) {
- close(pfds[0]);
- if (i == 0) {
- printf("testing with %d processes, %d loops, %d hash_size, seed=%d%s\n",
- num_procs, num_loops, hash_size, seed, always_transaction ? " (all within transactions)" : "");
- }
- exit(run_child(i, seed, num_loops, 0));
- }
- }
-
- while (num_procs) {
- int status, j;
- pid_t pid;
-
- if (error_count != 0) {
- /* try and stop the test on any failure */
- for (j=0;j<num_procs;j++) {
- if (pids[j] != 0) {
- kill(pids[j], SIGTERM);
- }
- }
- }
-
- pid = waitpid(-1, &status, kill_random ? WNOHANG : 0);
- if (pid == 0) {
- struct timespec ts;
-
- /* Sleep for 1/10 second. */
- ts.tv_sec = 0;
- ts.tv_nsec = 100000000;
- nanosleep(&ts, NULL);
-
- /* Kill someone. */
- kill(pids[random() % num_procs], SIGUSR1);
- continue;
- }
-
- if (pid == -1) {
- perror("failed to wait for child\n");
- exit(1);
- }
-
- for (j=0;j<num_procs;j++) {
- if (pids[j] == pid) break;
- }
- if (j == num_procs) {
- printf("unknown child %d exited!?\n", (int)pid);
- exit(1);
- }
- if (WIFSIGNALED(status)) {
- if (WTERMSIG(status) == SIGUSR2
- || WTERMSIG(status) == SIGUSR1) {
- /* SIGUSR2 means they wrote to pipe. */
- if (WTERMSIG(status) == SIGUSR2) {
- read(pfds[0], &done[j],
- sizeof(done[j]));
- }
- pids[j] = fork();
- if (pids[j] == 0)
- exit(run_child(j, seed, num_loops,
- done[j]));
- printf("Restarting child %i for %u-%u\n",
- j, done[j], num_loops);
- continue;
- }
- printf("child %d exited with signal %d\n",
- (int)pid, WTERMSIG(status));
- error_count++;
- } else {
- if (WEXITSTATUS(status) != 0) {
- printf("child %d exited with status %d\n",
- (int)pid, WEXITSTATUS(status));
- error_count++;
- }
- }
- memmove(&pids[j], &pids[j+1],
- (num_procs - j - 1)*sizeof(pids[0]));
- num_procs--;
- }
-
- free(pids);
-
-done:
- if (error_count == 0) {
- db = tdb_open_ex("torture.tdb", hash_size, TDB_DEFAULT,
- O_RDWR, 0, &log_ctx, NULL);
- if (!db) {
- fatal("db open failed");
- }
- if (tdb_check(db, NULL, NULL) == -1) {
- printf("db check failed");
- exit(1);
- }
- tdb_close(db);
- printf("OK\n");
- }
-
- return error_count;
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 2005
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-/*
- transaction design:
-
- - only allow a single transaction at a time per database. This makes
- using the transaction API simpler, as otherwise the caller would
- have to cope with temporary failures in transactions that conflict
- with other current transactions
-
- - keep the transaction recovery information in the same file as the
- database, using a special 'transaction recovery' record pointed at
- by the header. This removes the need for extra journal files as
- used by some other databases
-
- - dynamically allocated the transaction recover record, re-using it
- for subsequent transactions. If a larger record is needed then
- tdb_free() the old record to place it on the normal tdb freelist
- before allocating the new record
-
- - during transactions, keep a linked list of writes all that have
- been performed by intercepting all tdb_write() calls. The hooked
- transaction versions of tdb_read() and tdb_write() check this
- linked list and try to use the elements of the list in preference
- to the real database.
-
- - don't allow any locks to be held when a transaction starts,
- otherwise we can end up with deadlock (plus lack of lock nesting
- in posix locks would mean the lock is lost)
-
- - if the caller gains a lock during the transaction but doesn't
- release it then fail the commit
-
- - allow for nested calls to tdb_transaction_start(), re-using the
- existing transaction record. If the inner transaction is cancelled
- then a subsequent commit will fail
-
- - keep a mirrored copy of the tdb hash chain heads to allow for the
- fast hash heads scan on traverse, updating the mirrored copy in
- the transaction version of tdb_write
-
- - allow callers to mix transaction and non-transaction use of tdb,
- although once a transaction is started then an exclusive lock is
- gained until the transaction is committed or cancelled
-
- - the commit stategy involves first saving away all modified data
- into a linearised buffer in the transaction recovery area, then
- marking the transaction recovery area with a magic value to
- indicate a valid recovery record. In total 4 fsync/msync calls are
- needed per commit to prevent race conditions. It might be possible
- to reduce this to 3 or even 2 with some more work.
-
- - check for a valid recovery record on open of the tdb, while the
- open lock is held. Automatically recover from the transaction
- recovery area if needed, then continue with the open as
- usual. This allows for smooth crash recovery with no administrator
- intervention.
-
- - if TDB_NOSYNC is passed to flags in tdb_open then transactions are
- still available, but no transaction recovery area is used and no
- fsync/msync calls are made.
-
- - if TDB_ALLOW_NESTING is passed to flags in tdb open, or added using
- tdb_add_flags() transaction nesting is enabled.
- It resets the TDB_DISALLOW_NESTING flag, as both cannot be used together.
- The default is that transaction nesting is allowed.
- Note: this default may change in future versions of tdb.
-
- Beware. when transactions are nested a transaction successfully
- completed with tdb_transaction_commit() can be silently unrolled later.
-
- - if TDB_DISALLOW_NESTING is passed to flags in tdb open, or added using
- tdb_add_flags() transaction nesting is disabled.
- It resets the TDB_ALLOW_NESTING flag, as both cannot be used together.
- An attempt create a nested transaction will fail with TDB_ERR_NESTING.
- The default is that transaction nesting is allowed.
- Note: this default may change in future versions of tdb.
-*/
-
-
-/*
- hold the context of any current transaction
-*/
-struct tdb_transaction {
- /* we keep a mirrored copy of the tdb hash heads here so
- tdb_next_hash_chain() can operate efficiently */
- uint32_t *hash_heads;
-
- /* the original io methods - used to do IOs to the real db */
- const struct tdb_methods *io_methods;
-
- /* the list of transaction blocks. When a block is first
- written to, it gets created in this list */
- uint8_t **blocks;
- uint32_t num_blocks;
- uint32_t block_size; /* bytes in each block */
- uint32_t last_block_size; /* number of valid bytes in the last block */
-
- /* non-zero when an internal transaction error has
- occurred. All write operations will then fail until the
- transaction is ended */
- int transaction_error;
-
- /* when inside a transaction we need to keep track of any
- nested tdb_transaction_start() calls, as these are allowed,
- but don't create a new transaction */
- int nesting;
-
- /* set when a prepare has already occurred */
- bool prepared;
- tdb_off_t magic_offset;
-
- /* old file size before transaction */
- tdb_len_t old_map_size;
-
- /* we should re-pack on commit */
- bool need_repack;
-};
-
-
-/*
- read while in a transaction. We need to check first if the data is in our list
- of transaction elements, then if not do a real read
-*/
-static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
- tdb_len_t len, int cv)
-{
- uint32_t blk;
-
- /* break it down into block sized ops */
- while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) {
- tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size);
- if (transaction_read(tdb, off, buf, len2, cv) != 0) {
- return -1;
- }
- len -= len2;
- off += len2;
- buf = (void *)(len2 + (char *)buf);
- }
-
- if (len == 0) {
- return 0;
- }
-
- blk = off / tdb->transaction->block_size;
-
- /* see if we have it in the block list */
- if (tdb->transaction->num_blocks <= blk ||
- tdb->transaction->blocks[blk] == NULL) {
- /* nope, do a real read */
- if (tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv) != 0) {
- goto fail;
- }
- return 0;
- }
-
- /* it is in the block list. Now check for the last block */
- if (blk == tdb->transaction->num_blocks-1) {
- if (len > tdb->transaction->last_block_size) {
- goto fail;
- }
- }
-
- /* now copy it out of this block */
- memcpy(buf, tdb->transaction->blocks[blk] + (off % tdb->transaction->block_size), len);
- if (cv) {
- tdb_convert(buf, len);
- }
- return 0;
-
-fail:
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len));
- tdb->ecode = TDB_ERR_IO;
- tdb->transaction->transaction_error = 1;
- return -1;
-}
-
-
-/*
- write while in a transaction
-*/
-static int transaction_write(struct tdb_context *tdb, tdb_off_t off,
- const void *buf, tdb_len_t len)
-{
- uint32_t blk;
-
- /* Only a commit is allowed on a prepared transaction */
- if (tdb->transaction->prepared) {
- tdb->ecode = TDB_ERR_EINVAL;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: transaction already prepared, write not allowed\n"));
- tdb->transaction->transaction_error = 1;
- return -1;
- }
-
- /* if the write is to a hash head, then update the transaction
- hash heads */
- if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP &&
- off < FREELIST_TOP+TDB_HASHTABLE_SIZE(tdb)) {
- uint32_t chain = (off-FREELIST_TOP) / sizeof(tdb_off_t);
- memcpy(&tdb->transaction->hash_heads[chain], buf, len);
- }
-
- /* break it up into block sized chunks */
- while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) {
- tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size);
- if (transaction_write(tdb, off, buf, len2) != 0) {
- return -1;
- }
- len -= len2;
- off += len2;
- if (buf != NULL) {
- buf = (const void *)(len2 + (const char *)buf);
- }
- }
-
- if (len == 0) {
- return 0;
- }
-
- blk = off / tdb->transaction->block_size;
- off = off % tdb->transaction->block_size;
-
- if (tdb->transaction->num_blocks <= blk) {
- uint8_t **new_blocks;
- /* expand the blocks array */
- if (tdb->transaction->blocks == NULL) {
- new_blocks = (uint8_t **)malloc(
- (blk+1)*sizeof(uint8_t *));
- } else {
- new_blocks = (uint8_t **)realloc(
- tdb->transaction->blocks,
- (blk+1)*sizeof(uint8_t *));
- }
- if (new_blocks == NULL) {
- tdb->ecode = TDB_ERR_OOM;
- goto fail;
- }
- memset(&new_blocks[tdb->transaction->num_blocks], 0,
- (1+(blk - tdb->transaction->num_blocks))*sizeof(uint8_t *));
- tdb->transaction->blocks = new_blocks;
- tdb->transaction->num_blocks = blk+1;
- tdb->transaction->last_block_size = 0;
- }
-
- /* allocate and fill a block? */
- if (tdb->transaction->blocks[blk] == NULL) {
- tdb->transaction->blocks[blk] = (uint8_t *)calloc(tdb->transaction->block_size, 1);
- if (tdb->transaction->blocks[blk] == NULL) {
- tdb->ecode = TDB_ERR_OOM;
- tdb->transaction->transaction_error = 1;
- return -1;
- }
- if (tdb->transaction->old_map_size > blk * tdb->transaction->block_size) {
- tdb_len_t len2 = tdb->transaction->block_size;
- if (len2 + (blk * tdb->transaction->block_size) > tdb->transaction->old_map_size) {
- len2 = tdb->transaction->old_map_size - (blk * tdb->transaction->block_size);
- }
- if (tdb->transaction->io_methods->tdb_read(tdb, blk * tdb->transaction->block_size,
- tdb->transaction->blocks[blk],
- len2, 0) != 0) {
- SAFE_FREE(tdb->transaction->blocks[blk]);
- tdb->ecode = TDB_ERR_IO;
- goto fail;
- }
- if (blk == tdb->transaction->num_blocks-1) {
- tdb->transaction->last_block_size = len2;
- }
- }
- }
-
- /* overwrite part of an existing block */
- if (buf == NULL) {
- memset(tdb->transaction->blocks[blk] + off, 0, len);
- } else {
- memcpy(tdb->transaction->blocks[blk] + off, buf, len);
- }
- if (blk == tdb->transaction->num_blocks-1) {
- if (len + off > tdb->transaction->last_block_size) {
- tdb->transaction->last_block_size = len + off;
- }
- }
-
- return 0;
-
-fail:
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n",
- (blk*tdb->transaction->block_size) + off, len));
- tdb->transaction->transaction_error = 1;
- return -1;
-}
-
-
-/*
- write while in a transaction - this varient never expands the transaction blocks, it only
- updates existing blocks. This means it cannot change the recovery size
-*/
-static int transaction_write_existing(struct tdb_context *tdb, tdb_off_t off,
- const void *buf, tdb_len_t len)
-{
- uint32_t blk;
-
- /* break it up into block sized chunks */
- while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) {
- tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size);
- if (transaction_write_existing(tdb, off, buf, len2) != 0) {
- return -1;
- }
- len -= len2;
- off += len2;
- if (buf != NULL) {
- buf = (const void *)(len2 + (const char *)buf);
- }
- }
-
- if (len == 0) {
- return 0;
- }
-
- blk = off / tdb->transaction->block_size;
- off = off % tdb->transaction->block_size;
-
- if (tdb->transaction->num_blocks <= blk ||
- tdb->transaction->blocks[blk] == NULL) {
- return 0;
- }
-
- if (blk == tdb->transaction->num_blocks-1 &&
- off + len > tdb->transaction->last_block_size) {
- if (off >= tdb->transaction->last_block_size) {
- return 0;
- }
- len = tdb->transaction->last_block_size - off;
- }
-
- /* overwrite part of an existing block */
- memcpy(tdb->transaction->blocks[blk] + off, buf, len);
-
- return 0;
-}
-
-
-/*
- accelerated hash chain head search, using the cached hash heads
-*/
-static void transaction_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
-{
- uint32_t h = *chain;
- for (;h < tdb->header.hash_size;h++) {
- /* the +1 takes account of the freelist */
- if (0 != tdb->transaction->hash_heads[h+1]) {
- break;
- }
- }
- (*chain) = h;
-}
-
-/*
- out of bounds check during a transaction
-*/
-static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
-{
- if (len <= tdb->map_size) {
- return 0;
- }
- tdb->ecode = TDB_ERR_IO;
- return -1;
-}
-
-/*
- transaction version of tdb_expand().
-*/
-static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
- tdb_off_t addition)
-{
- /* add a write to the transaction elements, so subsequent
- reads see the zero data */
- if (transaction_write(tdb, size, NULL, addition) != 0) {
- return -1;
- }
-
- tdb->transaction->need_repack = true;
-
- return 0;
-}
-
-static const struct tdb_methods transaction_methods = {
- transaction_read,
- transaction_write,
- transaction_next_hash_chain,
- transaction_oob,
- transaction_expand_file,
-};
-
-/*
- sync to disk
-*/
-static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length)
-{
- if (tdb->flags & TDB_NOSYNC) {
- return 0;
- }
-
- if (fsync(tdb->fd) != 0) {
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
- return -1;
- }
-#ifdef MS_SYNC
- if (tdb->map_ptr) {
- tdb_off_t moffset = offset & ~(tdb->page_size-1);
- if (msync(moffset + (char *)tdb->map_ptr,
- length + (offset - moffset), MS_SYNC) != 0) {
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n",
- strerror(errno)));
- return -1;
- }
- }
-#endif
- return 0;
-}
-
-
-static int _tdb_transaction_cancel(struct tdb_context *tdb)
-{
- int i, ret = 0;
-
- if (tdb->transaction == NULL) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n"));
- return -1;
- }
-
- if (tdb->transaction->nesting != 0) {
- tdb->transaction->transaction_error = 1;
- tdb->transaction->nesting--;
- return 0;
- }
-
- tdb->map_size = tdb->transaction->old_map_size;
-
- /* free all the transaction blocks */
- for (i=0;i<tdb->transaction->num_blocks;i++) {
- if (tdb->transaction->blocks[i] != NULL) {
- free(tdb->transaction->blocks[i]);
- }
- }
- SAFE_FREE(tdb->transaction->blocks);
-
- if (tdb->transaction->magic_offset) {
- const struct tdb_methods *methods = tdb->transaction->io_methods;
- uint32_t invalid = TDB_RECOVERY_INVALID_MAGIC;
-
- /* remove the recovery marker */
- if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &invalid, 4) == -1 ||
- transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_cancel: failed to remove recovery magic\n"));
- ret = -1;
- }
- }
-
- /* This also removes the OPEN_LOCK, if we have it. */
- tdb_release_extra_locks(tdb);
-
- /* restore the normal io methods */
- tdb->methods = tdb->transaction->io_methods;
-
- tdb_transaction_unlock(tdb, F_WRLCK);
- SAFE_FREE(tdb->transaction->hash_heads);
- SAFE_FREE(tdb->transaction);
-
- return ret;
-}
-
-/*
- start a tdb transaction. No token is returned, as only a single
- transaction is allowed to be pending per tdb_context
-*/
-int tdb_transaction_start(struct tdb_context *tdb)
-{
- /* some sanity checks */
- if (tdb->read_only || (tdb->flags & TDB_INTERNAL) || tdb->traverse_read) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n"));
- tdb->ecode = TDB_ERR_EINVAL;
- return -1;
- }
-
- /* cope with nested tdb_transaction_start() calls */
- if (tdb->transaction != NULL) {
- if (!(tdb->flags & TDB_ALLOW_NESTING)) {
- tdb->ecode = TDB_ERR_NESTING;
- return -1;
- }
- tdb_trace(tdb, "tdb_transaction_start");
- tdb->transaction->nesting++;
- TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n",
- tdb->transaction->nesting));
- return 0;
- }
-
- if (tdb_have_extra_locks(tdb)) {
- /* the caller must not have any locks when starting a
- transaction as otherwise we'll be screwed by lack
- of nested locks in posix */
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction with locks held\n"));
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- if (tdb->travlocks.next != NULL) {
- /* you cannot use transactions inside a traverse (although you can use
- traverse inside a transaction) as otherwise you can end up with
- deadlock */
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction within a traverse\n"));
- tdb->ecode = TDB_ERR_LOCK;
- return -1;
- }
-
- tdb->transaction = (struct tdb_transaction *)
- calloc(sizeof(struct tdb_transaction), 1);
- if (tdb->transaction == NULL) {
- tdb->ecode = TDB_ERR_OOM;
- return -1;
- }
-
- /* a page at a time seems like a reasonable compromise between compactness and efficiency */
- tdb->transaction->block_size = tdb->page_size;
-
- /* get the transaction write lock. This is a blocking lock. As
- discussed with Volker, there are a number of ways we could
- make this async, which we will probably do in the future */
- if (tdb_transaction_lock(tdb, F_WRLCK) == -1) {
- SAFE_FREE(tdb->transaction->blocks);
- SAFE_FREE(tdb->transaction);
- return -1;
- }
-
- /* get a read lock from the freelist to the end of file. This
- is upgraded to a write lock during the commit */
- if (tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, true) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n"));
- goto fail_allrecord_lock;
- }
-
- /* setup a copy of the hash table heads so the hash scan in
- traverse can be fast */
- tdb->transaction->hash_heads = (uint32_t *)
- calloc(tdb->header.hash_size+1, sizeof(uint32_t));
- if (tdb->transaction->hash_heads == NULL) {
- tdb->ecode = TDB_ERR_OOM;
- goto fail;
- }
- if (tdb->methods->tdb_read(tdb, FREELIST_TOP, tdb->transaction->hash_heads,
- TDB_HASHTABLE_SIZE(tdb), 0) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to read hash heads\n"));
- tdb->ecode = TDB_ERR_IO;
- goto fail;
- }
-
- /* make sure we know about any file expansions already done by
- anyone else */
- tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
- tdb->transaction->old_map_size = tdb->map_size;
-
- /* finally hook the io methods, replacing them with
- transaction specific methods */
- tdb->transaction->io_methods = tdb->methods;
- tdb->methods = &transaction_methods;
-
- /* Trace at the end, so we get sequence number correct. */
- tdb_trace(tdb, "tdb_transaction_start");
- return 0;
-
-fail:
- tdb_allrecord_unlock(tdb, F_RDLCK, false);
-fail_allrecord_lock:
- tdb_transaction_unlock(tdb, F_WRLCK);
- SAFE_FREE(tdb->transaction->blocks);
- SAFE_FREE(tdb->transaction->hash_heads);
- SAFE_FREE(tdb->transaction);
- return -1;
-}
-
-
-/*
- cancel the current transaction
-*/
-int tdb_transaction_cancel(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_transaction_cancel");
- return _tdb_transaction_cancel(tdb);
-}
-
-/*
- work out how much space the linearised recovery data will consume
-*/
-static tdb_len_t tdb_recovery_size(struct tdb_context *tdb)
-{
- tdb_len_t recovery_size = 0;
- int i;
-
- recovery_size = sizeof(uint32_t);
- for (i=0;i<tdb->transaction->num_blocks;i++) {
- if (i * tdb->transaction->block_size >= tdb->transaction->old_map_size) {
- break;
- }
- if (tdb->transaction->blocks[i] == NULL) {
- continue;
- }
- recovery_size += 2*sizeof(tdb_off_t);
- if (i == tdb->transaction->num_blocks-1) {
- recovery_size += tdb->transaction->last_block_size;
- } else {
- recovery_size += tdb->transaction->block_size;
- }
- }
-
- return recovery_size;
-}
-
-/*
- allocate the recovery area, or use an existing recovery area if it is
- large enough
-*/
-static int tdb_recovery_allocate(struct tdb_context *tdb,
- tdb_len_t *recovery_size,
- tdb_off_t *recovery_offset,
- tdb_len_t *recovery_max_size)
-{
- struct tdb_record rec;
- const struct tdb_methods *methods = tdb->transaction->io_methods;
- tdb_off_t recovery_head, new_end;
-
- if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n"));
- return -1;
- }
-
- rec.rec_len = 0;
-
- if (recovery_head != 0) {
- if (methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));
- return -1;
- }
- /* ignore invalid recovery regions: can happen in crash */
- if (rec.magic != TDB_RECOVERY_MAGIC &&
- rec.magic != TDB_RECOVERY_INVALID_MAGIC) {
- recovery_head = 0;
- }
- }
-
- *recovery_size = tdb_recovery_size(tdb);
-
- /* Existing recovery area? */
- if (recovery_head != 0 && *recovery_size <= rec.rec_len) {
- /* it fits in the existing area */
- *recovery_max_size = rec.rec_len;
- *recovery_offset = recovery_head;
- return 0;
- }
-
- /* If recovery area in middle of file, we need a new one. */
- if (recovery_head == 0
- || recovery_head + sizeof(rec) + rec.rec_len != tdb->map_size) {
- /* we need to free up the old recovery area, then allocate a
- new one at the end of the file. Note that we cannot use
- tdb_allocate() to allocate the new one as that might return
- us an area that is being currently used (as of the start of
- the transaction) */
- if (recovery_head) {
- if (tdb_free(tdb, recovery_head, &rec) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL,
- "tdb_recovery_allocate: failed to"
- " free previous recovery area\n"));
- return -1;
- }
-
- /* the tdb_free() call might have increased
- * the recovery size */
- *recovery_size = tdb_recovery_size(tdb);
- }
-
- /* New head will be at end of file. */
- recovery_head = tdb->map_size;
- }
-
- /* Now we know where it will be. */
- *recovery_offset = recovery_head;
-
- /* Expand by more than we need, so we don't do it often. */
- *recovery_max_size = tdb_expand_adjust(tdb->map_size,
- *recovery_size,
- tdb->page_size)
- - sizeof(rec);
-
- new_end = recovery_head + sizeof(rec) + *recovery_max_size;
-
- if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size,
- new_end - tdb->transaction->old_map_size)
- == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n"));
- return -1;
- }
-
- /* remap the file (if using mmap) */
- methods->tdb_oob(tdb, tdb->map_size + 1, 1);
-
- /* we have to reset the old map size so that we don't try to expand the file
- again in the transaction commit, which would destroy the recovery area */
- tdb->transaction->old_map_size = tdb->map_size;
-
- /* write the recovery header offset and sync - we can sync without a race here
- as the magic ptr in the recovery record has not been set */
- CONVERT(recovery_head);
- if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD,
- &recovery_head, sizeof(tdb_off_t)) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n"));
- return -1;
- }
- if (transaction_write_existing(tdb, TDB_RECOVERY_HEAD, &recovery_head, sizeof(tdb_off_t)) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n"));
- return -1;
- }
-
- return 0;
-}
-
-
-/*
- setup the recovery data that will be used on a crash during commit
-*/
-static int transaction_setup_recovery(struct tdb_context *tdb,
- tdb_off_t *magic_offset)
-{
- tdb_len_t recovery_size;
- unsigned char *data, *p;
- const struct tdb_methods *methods = tdb->transaction->io_methods;
- struct tdb_record *rec;
- tdb_off_t recovery_offset, recovery_max_size;
- tdb_off_t old_map_size = tdb->transaction->old_map_size;
- uint32_t magic, tailer;
- int i;
-
- /*
- check that the recovery area has enough space
- */
- if (tdb_recovery_allocate(tdb, &recovery_size,
- &recovery_offset, &recovery_max_size) == -1) {
- return -1;
- }
-
- data = (unsigned char *)malloc(recovery_size + sizeof(*rec));
- if (data == NULL) {
- tdb->ecode = TDB_ERR_OOM;
- return -1;
- }
-
- rec = (struct tdb_record *)data;
- memset(rec, 0, sizeof(*rec));
-
- rec->magic = TDB_RECOVERY_INVALID_MAGIC;
- rec->data_len = recovery_size;
- rec->rec_len = recovery_max_size;
- rec->key_len = old_map_size;
- CONVERT(rec);
-
- /* build the recovery data into a single blob to allow us to do a single
- large write, which should be more efficient */
- p = data + sizeof(*rec);
- for (i=0;i<tdb->transaction->num_blocks;i++) {
- tdb_off_t offset;
- tdb_len_t length;
-
- if (tdb->transaction->blocks[i] == NULL) {
- continue;
- }
-
- offset = i * tdb->transaction->block_size;
- length = tdb->transaction->block_size;
- if (i == tdb->transaction->num_blocks-1) {
- length = tdb->transaction->last_block_size;
- }
-
- if (offset >= old_map_size) {
- continue;
- }
- if (offset + length > tdb->transaction->old_map_size) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n"));
- free(data);
- tdb->ecode = TDB_ERR_CORRUPT;
- return -1;
- }
- memcpy(p, &offset, 4);
- memcpy(p+4, &length, 4);
- if (DOCONV()) {
- tdb_convert(p, 8);
- }
- /* the recovery area contains the old data, not the
- new data, so we have to call the original tdb_read
- method to get it */
- if (methods->tdb_read(tdb, offset, p + 8, length, 0) != 0) {
- free(data);
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
- p += 8 + length;
- }
-
- /* and the tailer */
- tailer = sizeof(*rec) + recovery_max_size;
- memcpy(p, &tailer, 4);
- CONVERT(p);
-
- /* write the recovery data to the recovery area */
- if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery data\n"));
- free(data);
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
- if (transaction_write_existing(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write secondary recovery data\n"));
- free(data);
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- /* as we don't have ordered writes, we have to sync the recovery
- data before we update the magic to indicate that the recovery
- data is present */
- if (transaction_sync(tdb, recovery_offset, sizeof(*rec) + recovery_size) == -1) {
- free(data);
- return -1;
- }
-
- free(data);
-
- magic = TDB_RECOVERY_MAGIC;
- CONVERT(magic);
-
- *magic_offset = recovery_offset + offsetof(struct tdb_record, magic);
-
- if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
- if (transaction_write_existing(tdb, *magic_offset, &magic, sizeof(magic)) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write secondary recovery magic\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- /* ensure the recovery magic marker is on disk */
- if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) {
- return -1;
- }
-
- return 0;
-}
-
-static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
-{
- const struct tdb_methods *methods;
-
- if (tdb->transaction == NULL) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: no transaction\n"));
- return -1;
- }
-
- if (tdb->transaction->prepared) {
- tdb->ecode = TDB_ERR_EINVAL;
- _tdb_transaction_cancel(tdb);
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: transaction already prepared\n"));
- return -1;
- }
-
- if (tdb->transaction->transaction_error) {
- tdb->ecode = TDB_ERR_IO;
- _tdb_transaction_cancel(tdb);
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: transaction error pending\n"));
- return -1;
- }
-
-
- if (tdb->transaction->nesting != 0) {
- tdb->transaction->nesting--;
- return 0;
- }
-
- /* check for a null transaction */
- if (tdb->transaction->blocks == NULL) {
- return 0;
- }
-
- methods = tdb->transaction->io_methods;
-
- /* if there are any locks pending then the caller has not
- nested their locks properly, so fail the transaction */
- if (tdb_have_extra_locks(tdb)) {
- tdb->ecode = TDB_ERR_LOCK;
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: locks pending on commit\n"));
- _tdb_transaction_cancel(tdb);
- return -1;
- }
-
- /* upgrade the main transaction lock region to a write lock */
- if (tdb_allrecord_upgrade(tdb) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to upgrade hash locks\n"));
- _tdb_transaction_cancel(tdb);
- return -1;
- }
-
- /* get the open lock - this prevents new users attaching to the database
- during the commit */
- if (tdb_nest_lock(tdb, OPEN_LOCK, F_WRLCK, TDB_LOCK_WAIT) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to get open lock\n"));
- _tdb_transaction_cancel(tdb);
- return -1;
- }
-
- if (!(tdb->flags & TDB_NOSYNC)) {
- /* write the recovery data to the end of the file */
- if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n"));
- _tdb_transaction_cancel(tdb);
- return -1;
- }
- }
-
- tdb->transaction->prepared = true;
-
- /* expand the file to the new size if needed */
- if (tdb->map_size != tdb->transaction->old_map_size) {
- if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size,
- tdb->map_size -
- tdb->transaction->old_map_size) == -1) {
- tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: expansion failed\n"));
- _tdb_transaction_cancel(tdb);
- return -1;
- }
- tdb->map_size = tdb->transaction->old_map_size;
- methods->tdb_oob(tdb, tdb->map_size + 1, 1);
- }
-
- /* Keep the open lock until the actual commit */
-
- return 0;
-}
-
-/*
- prepare to commit the current transaction
-*/
-int tdb_transaction_prepare_commit(struct tdb_context *tdb)
-{
- tdb_trace(tdb, "tdb_transaction_prepare_commit");
- return _tdb_transaction_prepare_commit(tdb);
-}
-
-/*
- commit the current transaction
-*/
-int tdb_transaction_commit(struct tdb_context *tdb)
-{
- const struct tdb_methods *methods;
- int i;
- bool need_repack;
-
- if (tdb->transaction == NULL) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
- return -1;
- }
-
- tdb_trace(tdb, "tdb_transaction_commit");
-
- if (tdb->transaction->transaction_error) {
- tdb->ecode = TDB_ERR_IO;
- tdb_transaction_cancel(tdb);
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n"));
- return -1;
- }
-
-
- if (tdb->transaction->nesting != 0) {
- tdb->transaction->nesting--;
- return 0;
- }
-
- /* check for a null transaction */
- if (tdb->transaction->blocks == NULL) {
- _tdb_transaction_cancel(tdb);
- return 0;
- }
-
- if (!tdb->transaction->prepared) {
- int ret = _tdb_transaction_prepare_commit(tdb);
- if (ret)
- return ret;
- }
-
- methods = tdb->transaction->io_methods;
-
- /* perform all the writes */
- for (i=0;i<tdb->transaction->num_blocks;i++) {
- tdb_off_t offset;
- tdb_len_t length;
-
- if (tdb->transaction->blocks[i] == NULL) {
- continue;
- }
-
- offset = i * tdb->transaction->block_size;
- length = tdb->transaction->block_size;
- if (i == tdb->transaction->num_blocks-1) {
- length = tdb->transaction->last_block_size;
- }
-
- if (methods->tdb_write(tdb, offset, tdb->transaction->blocks[i], length) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n"));
-
- /* we've overwritten part of the data and
- possibly expanded the file, so we need to
- run the crash recovery code */
- tdb->methods = methods;
- tdb_transaction_recover(tdb);
-
- _tdb_transaction_cancel(tdb);
-
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n"));
- return -1;
- }
- SAFE_FREE(tdb->transaction->blocks[i]);
- }
-
- SAFE_FREE(tdb->transaction->blocks);
- tdb->transaction->num_blocks = 0;
-
- /* ensure the new data is on disk */
- if (transaction_sync(tdb, 0, tdb->map_size) == -1) {
- return -1;
- }
-
- /*
- TODO: maybe write to some dummy hdr field, or write to magic
- offset without mmap, before the last sync, instead of the
- utime() call
- */
-
- /* on some systems (like Linux 2.6.x) changes via mmap/msync
- don't change the mtime of the file, this means the file may
- not be backed up (as tdb rounding to block sizes means that
- file size changes are quite rare too). The following forces
- mtime changes when a transaction completes */
-#if HAVE_UTIME
- utime(tdb->name, NULL);
-#endif
-
- need_repack = tdb->transaction->need_repack;
-
- /* use a transaction cancel to free memory and remove the
- transaction locks */
- _tdb_transaction_cancel(tdb);
-
- if (need_repack) {
- return tdb_repack(tdb);
- }
-
- return 0;
-}
-
-
-/*
- recover from an aborted transaction. Must be called with exclusive
- database write access already established (including the open
- lock to prevent new processes attaching)
-*/
-int tdb_transaction_recover(struct tdb_context *tdb)
-{
- tdb_off_t recovery_head, recovery_eof;
- unsigned char *data, *p;
- uint32_t zero = 0;
- struct tdb_record rec;
-
- /* find the recovery area */
- if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery head\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- if (recovery_head == 0) {
- /* we have never allocated a recovery record */
- return 0;
- }
-
- /* read the recovery record */
- if (tdb->methods->tdb_read(tdb, recovery_head, &rec,
- sizeof(rec), DOCONV()) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- if (rec.magic != TDB_RECOVERY_MAGIC) {
- /* there is no valid recovery data */
- return 0;
- }
-
- if (tdb->read_only) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: attempt to recover read only database\n"));
- tdb->ecode = TDB_ERR_CORRUPT;
- return -1;
- }
-
- recovery_eof = rec.key_len;
-
- data = (unsigned char *)malloc(rec.data_len);
- if (data == NULL) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n"));
- tdb->ecode = TDB_ERR_OOM;
- return -1;
- }
-
- /* read the full recovery data */
- if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data,
- rec.data_len, 0) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- /* recover the file data */
- p = data;
- while (p+8 < data + rec.data_len) {
- uint32_t ofs, len;
- if (DOCONV()) {
- tdb_convert(p, 8);
- }
- memcpy(&ofs, p, 4);
- memcpy(&len, p+4, 4);
-
- if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) {
- free(data);
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
- p += 8 + len;
- }
-
- free(data);
-
- if (transaction_sync(tdb, 0, tdb->map_size) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync recovery\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- /* if the recovery area is after the recovered eof then remove it */
- if (recovery_eof <= recovery_head) {
- if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
- }
-
- /* remove the recovery magic */
- if (tdb_ofs_write(tdb, recovery_head + offsetof(struct tdb_record, magic),
- &zero) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- if (transaction_sync(tdb, 0, recovery_eof) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync2 recovery\n"));
- tdb->ecode = TDB_ERR_IO;
- return -1;
- }
-
- TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n",
- recovery_eof));
-
- /* all done */
- return 0;
-}
-
-/* Any I/O failures we say "needs recovery". */
-bool tdb_needs_recovery(struct tdb_context *tdb)
-{
- tdb_off_t recovery_head;
- struct tdb_record rec;
-
- /* find the recovery area */
- if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
- return true;
- }
-
- if (recovery_head == 0) {
- /* we have never allocated a recovery record */
- return false;
- }
-
- /* read the recovery record */
- if (tdb->methods->tdb_read(tdb, recovery_head, &rec,
- sizeof(rec), DOCONV()) == -1) {
- return true;
- }
-
- return (rec.magic == TDB_RECOVERY_MAGIC);
-}
+++ /dev/null
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2005
- Copyright (C) Paul `Rusty' Russell 2000
- Copyright (C) Jeremy Allison 2000-2003
-
- ** NOTE! The following LGPL license applies to the tdb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "tdb_private.h"
-
-#define TDB_NEXT_LOCK_ERR ((tdb_off_t)-1)
-
-/* Uses traverse lock: 0 = finish, TDB_NEXT_LOCK_ERR = error,
- other = record offset */
-static tdb_off_t tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tlock,
- struct tdb_record *rec)
-{
- int want_next = (tlock->off != 0);
-
- /* Lock each chain from the start one. */
- for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
- if (!tlock->off && tlock->hash != 0) {
- /* this is an optimization for the common case where
- the hash chain is empty, which is particularly
- common for the use of tdb with ldb, where large
- hashes are used. In that case we spend most of our
- time in tdb_brlock(), locking empty hash chains.
-
- To avoid this, we do an unlocked pre-check to see
- if the hash chain is empty before starting to look
- inside it. If it is empty then we can avoid that
- hash chain. If it isn't empty then we can't believe
- the value we get back, as we read it without a
- lock, so instead we get the lock and re-fetch the
- value below.
-
- Notice that not doing this optimization on the
- first hash chain is critical. We must guarantee
- that we have done at least one fcntl lock at the
- start of a search to guarantee that memory is
- coherent on SMP systems. If records are added by
- others during the search then thats OK, and we
- could possibly miss those with this trick, but we
- could miss them anyway without this trick, so the
- semantics don't change.
-
- With a non-indexed ldb search this trick gains us a
- factor of around 80 in speed on a linux 2.6.x
- system (testing using ldbtest).
- */
- tdb->methods->next_hash_chain(tdb, &tlock->hash);
- if (tlock->hash == tdb->header.hash_size) {
- continue;
- }
- }
-
- if (tdb_lock(tdb, tlock->hash, tlock->lock_rw) == -1)
- return TDB_NEXT_LOCK_ERR;
-
- /* No previous record? Start at top of chain. */
- if (!tlock->off) {
- if (tdb_ofs_read(tdb, TDB_HASH_TOP(tlock->hash),
- &tlock->off) == -1)
- goto fail;
- } else {
- /* Otherwise unlock the previous record. */
- if (tdb_unlock_record(tdb, tlock->off) != 0)
- goto fail;
- }
-
- if (want_next) {
- /* We have offset of old record: grab next */
- if (tdb_rec_read(tdb, tlock->off, rec) == -1)
- goto fail;
- tlock->off = rec->next;
- }
-
- /* Iterate through chain */
- while( tlock->off) {
- tdb_off_t current;
- if (tdb_rec_read(tdb, tlock->off, rec) == -1)
- goto fail;
-
- /* Detect infinite loops. From "Shlomi Yaakobovich" <Shlomi@exanet.com>. */
- if (tlock->off == rec->next) {
- tdb->ecode = TDB_ERR_CORRUPT;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: loop detected.\n"));
- goto fail;
- }
-
- if (!TDB_DEAD(rec)) {
- /* Woohoo: we found one! */
- if (tdb_lock_record(tdb, tlock->off) != 0)
- goto fail;
- return tlock->off;
- }
-
- /* Try to clean dead ones from old traverses */
- current = tlock->off;
- tlock->off = rec->next;
- if (!(tdb->read_only || tdb->traverse_read) &&
- tdb_do_delete(tdb, current, rec) != 0)
- goto fail;
- }
- tdb_unlock(tdb, tlock->hash, tlock->lock_rw);
- want_next = 0;
- }
- /* We finished iteration without finding anything */
- tdb->ecode = TDB_SUCCESS;
- return 0;
-
- fail:
- tlock->off = 0;
- if (tdb_unlock(tdb, tlock->hash, tlock->lock_rw) != 0)
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: On error unlock failed!\n"));
- return TDB_NEXT_LOCK_ERR;
-}
-
-/* traverse the entire database - calling fn(tdb, key, data) on each element.
- return -1 on error or the record count traversed
- if fn is NULL then it is not called
- a non-zero return value from fn() indicates that the traversal should stop
- */
-static int tdb_traverse_internal(struct tdb_context *tdb,
- tdb_traverse_func fn, void *private_data,
- struct tdb_traverse_lock *tl)
-{
- TDB_DATA key, dbuf;
- struct tdb_record rec;
- int ret = 0, count = 0;
- tdb_off_t off;
-
- /* This was in the initializaton, above, but the IRIX compiler
- * did not like it. crh
- */
- tl->next = tdb->travlocks.next;
-
- /* fcntl locks don't stack: beware traverse inside traverse */
- tdb->travlocks.next = tl;
-
- /* tdb_next_lock places locks on the record returned, and its chain */
- while ((off = tdb_next_lock(tdb, tl, &rec)) != 0) {
- if (off == TDB_NEXT_LOCK_ERR) {
- ret = -1;
- goto out;
- }
- count++;
- /* now read the full record */
- key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec),
- rec.key_len + rec.data_len);
- if (!key.dptr) {
- ret = -1;
- if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0)
- goto out;
- if (tdb_unlock_record(tdb, tl->off) != 0)
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
- goto out;
- }
- key.dsize = rec.key_len;
- dbuf.dptr = key.dptr + rec.key_len;
- dbuf.dsize = rec.data_len;
-
- tdb_trace_1rec_retrec(tdb, "traverse", key, dbuf);
-
- /* Drop chain lock, call out */
- if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) {
- ret = -1;
- SAFE_FREE(key.dptr);
- goto out;
- }
- if (fn && fn(tdb, key, dbuf, private_data)) {
- /* They want us to terminate traversal */
- tdb_trace_ret(tdb, "tdb_traverse_end", count);
- if (tdb_unlock_record(tdb, tl->off) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));;
- ret = -1;
- }
- SAFE_FREE(key.dptr);
- goto out;
- }
- SAFE_FREE(key.dptr);
- }
- tdb_trace(tdb, "tdb_traverse_end");
-out:
- tdb->travlocks.next = tl->next;
- if (ret < 0)
- return -1;
- else
- return count;
-}
-
-
-/*
- a write style traverse - temporarily marks the db read only
-*/
-int tdb_traverse_read(struct tdb_context *tdb,
- tdb_traverse_func fn, void *private_data)
-{
- struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK };
- int ret;
-
- /* we need to get a read lock on the transaction lock here to
- cope with the lock ordering semantics of solaris10 */
- if (tdb_transaction_lock(tdb, F_RDLCK)) {
- return -1;
- }
-
- tdb->traverse_read++;
- tdb_trace(tdb, "tdb_traverse_read_start");
- ret = tdb_traverse_internal(tdb, fn, private_data, &tl);
- tdb->traverse_read--;
-
- tdb_transaction_unlock(tdb, F_RDLCK);
-
- return ret;
-}
-
-/*
- a write style traverse - needs to get the transaction lock to
- prevent deadlocks
-
- WARNING: The data buffer given to the callback fn does NOT meet the
- alignment restrictions malloc gives you.
-*/
-int tdb_traverse(struct tdb_context *tdb,
- tdb_traverse_func fn, void *private_data)
-{
- struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK };
- int ret;
-
- if (tdb->read_only || tdb->traverse_read) {
- return tdb_traverse_read(tdb, fn, private_data);
- }
-
- if (tdb_transaction_lock(tdb, F_WRLCK)) {
- return -1;
- }
-
- tdb->traverse_write++;
- tdb_trace(tdb, "tdb_traverse_start");
- ret = tdb_traverse_internal(tdb, fn, private_data, &tl);
- tdb->traverse_write--;
-
- tdb_transaction_unlock(tdb, F_WRLCK);
-
- return ret;
-}
-
-
-/* find the first entry in the database and return its key */
-TDB_DATA tdb_firstkey(struct tdb_context *tdb)
-{
- TDB_DATA key;
- struct tdb_record rec;
- tdb_off_t off;
-
- /* release any old lock */
- if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0)
- return tdb_null;
- tdb->travlocks.off = tdb->travlocks.hash = 0;
- tdb->travlocks.lock_rw = F_RDLCK;
-
- /* Grab first record: locks chain and returned record. */
- off = tdb_next_lock(tdb, &tdb->travlocks, &rec);
- if (off == 0 || off == TDB_NEXT_LOCK_ERR) {
- tdb_trace_retrec(tdb, "tdb_firstkey", tdb_null);
- return tdb_null;
- }
- /* now read the key */
- key.dsize = rec.key_len;
- key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
-
- tdb_trace_retrec(tdb, "tdb_firstkey", key);
-
- /* Unlock the hash chain of the record we just read. */
- if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0)
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
- return key;
-}
-
-/* find the next entry in the database, returning its key */
-TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)
-{
- uint32_t oldhash;
- TDB_DATA key = tdb_null;
- struct tdb_record rec;
- unsigned char *k = NULL;
- tdb_off_t off;
-
- /* Is locked key the old key? If so, traverse will be reliable. */
- if (tdb->travlocks.off) {
- if (tdb_lock(tdb,tdb->travlocks.hash,tdb->travlocks.lock_rw))
- return tdb_null;
- if (tdb_rec_read(tdb, tdb->travlocks.off, &rec) == -1
- || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
- rec.key_len))
- || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
- /* No, it wasn't: unlock it and start from scratch */
- if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) {
- tdb_trace_1rec_retrec(tdb, "tdb_nextkey",
- oldkey, tdb_null);
- SAFE_FREE(k);
- return tdb_null;
- }
- if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) {
- SAFE_FREE(k);
- return tdb_null;
- }
- tdb->travlocks.off = 0;
- }
-
- SAFE_FREE(k);
- }
-
- if (!tdb->travlocks.off) {
- /* No previous element: do normal find, and lock record */
- tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec);
- if (!tdb->travlocks.off) {
- tdb_trace_1rec_retrec(tdb, "tdb_nextkey", oldkey, tdb_null);
- return tdb_null;
- }
- tdb->travlocks.hash = BUCKET(rec.full_hash);
- if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
- return tdb_null;
- }
- }
- oldhash = tdb->travlocks.hash;
-
- /* Grab next record: locks chain and returned record,
- unlocks old record */
- off = tdb_next_lock(tdb, &tdb->travlocks, &rec);
- if (off != TDB_NEXT_LOCK_ERR && off != 0) {
- key.dsize = rec.key_len;
- key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
- key.dsize);
- /* Unlock the chain of this new record */
- if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0)
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
- }
- /* Unlock the chain of old record */
- if (tdb_unlock(tdb, BUCKET(oldhash), tdb->travlocks.lock_rw) != 0)
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
- tdb_trace_1rec_retrec(tdb, "tdb_nextkey", oldkey, key);
- return key;
-}
-