From fab544c24c1ad6523f95893abcaec4e6cce6c2b4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Aug 2011 15:11:16 +0930 Subject: [PATCH] tdb2: test: import tdb1's tests. The main change is to s/tdb/tdb1_/ everywhere. --- ccan/tdb2/test/jenkins-be-hash.tdb1 | Bin 0 -> 696 bytes ccan/tdb2/test/jenkins-le-hash.tdb1 | Bin 0 -> 696 bytes ccan/tdb2/test/old-nohash-be.tdb1 | Bin 0 -> 696 bytes ccan/tdb2/test/old-nohash-le.tdb1 | Bin 0 -> 696 bytes ccan/tdb2/test/run-tdb1-3G-file.c | 118 ++++++++++ ccan/tdb2/test/run-tdb1-bad-tdb-header.c | 49 ++++ ccan/tdb2/test/run-tdb1-check.c | 55 +++++ ccan/tdb2/test/run-tdb1-corrupt.c | 118 ++++++++++ .../test/run-tdb1-die-during-transaction.c | 215 ++++++++++++++++++ ccan/tdb2/test/run-tdb1-endian.c | 54 +++++ ccan/tdb2/test/run-tdb1-incompatible.c | 178 +++++++++++++++ ccan/tdb2/test/run-tdb1-nested-transactions.c | 68 ++++++ ccan/tdb2/test/run-tdb1-nested-traverse.c | 80 +++++++ .../test/run-tdb1-no-lock-during-traverse.c | 106 +++++++++ ccan/tdb2/test/run-tdb1-oldhash.c | 40 ++++ .../test/run-tdb1-open-during-transaction.c | 176 ++++++++++++++ ccan/tdb2/test/run-tdb1-readonly-check.c | 43 ++++ ccan/tdb2/test/run-tdb1-rwlock-check.c | 36 +++ ccan/tdb2/test/run-tdb1-summary.c | 54 +++++ .../test/run-tdb1-traverse-in-transaction.c | 80 +++++++ ccan/tdb2/test/run-tdb1-wronghash-fail.c | 111 +++++++++ ccan/tdb2/test/run-tdb1-zero-append.c | 31 +++ ccan/tdb2/test/run-tdb1.c | 40 ++++ ccan/tdb2/test/rwlock-be.tdb1 | Bin 0 -> 696 bytes ccan/tdb2/test/rwlock-le.tdb1 | Bin 0 -> 696 bytes ccan/tdb2/test/tdb1-external-agent.c | 196 ++++++++++++++++ ccan/tdb2/test/tdb1-external-agent.h | 41 ++++ ccan/tdb2/test/tdb1-lock-tracking.c | 146 ++++++++++++ ccan/tdb2/test/tdb1-lock-tracking.h | 26 +++ ccan/tdb2/test/tdb1-logging.c | 30 +++ ccan/tdb2/test/tdb1-logging.h | 10 + ccan/tdb2/test/tdb1.corrupt | Bin 0 -> 192512 bytes ccan/tdb2/test/tdb2-source.h | 1 + 33 files changed, 2102 insertions(+) create mode 100644 ccan/tdb2/test/jenkins-be-hash.tdb1 create mode 100644 ccan/tdb2/test/jenkins-le-hash.tdb1 create mode 100644 ccan/tdb2/test/old-nohash-be.tdb1 create mode 100644 ccan/tdb2/test/old-nohash-le.tdb1 create mode 100644 ccan/tdb2/test/run-tdb1-3G-file.c create mode 100644 ccan/tdb2/test/run-tdb1-bad-tdb-header.c create mode 100644 ccan/tdb2/test/run-tdb1-check.c create mode 100644 ccan/tdb2/test/run-tdb1-corrupt.c create mode 100644 ccan/tdb2/test/run-tdb1-die-during-transaction.c create mode 100644 ccan/tdb2/test/run-tdb1-endian.c create mode 100644 ccan/tdb2/test/run-tdb1-incompatible.c create mode 100644 ccan/tdb2/test/run-tdb1-nested-transactions.c create mode 100644 ccan/tdb2/test/run-tdb1-nested-traverse.c create mode 100644 ccan/tdb2/test/run-tdb1-no-lock-during-traverse.c create mode 100644 ccan/tdb2/test/run-tdb1-oldhash.c create mode 100644 ccan/tdb2/test/run-tdb1-open-during-transaction.c create mode 100644 ccan/tdb2/test/run-tdb1-readonly-check.c create mode 100644 ccan/tdb2/test/run-tdb1-rwlock-check.c create mode 100644 ccan/tdb2/test/run-tdb1-summary.c create mode 100644 ccan/tdb2/test/run-tdb1-traverse-in-transaction.c create mode 100644 ccan/tdb2/test/run-tdb1-wronghash-fail.c create mode 100644 ccan/tdb2/test/run-tdb1-zero-append.c create mode 100644 ccan/tdb2/test/run-tdb1.c create mode 100644 ccan/tdb2/test/rwlock-be.tdb1 create mode 100644 ccan/tdb2/test/rwlock-le.tdb1 create mode 100644 ccan/tdb2/test/tdb1-external-agent.c create mode 100644 ccan/tdb2/test/tdb1-external-agent.h create mode 100644 ccan/tdb2/test/tdb1-lock-tracking.c create mode 100644 ccan/tdb2/test/tdb1-lock-tracking.h create mode 100644 ccan/tdb2/test/tdb1-logging.c create mode 100644 ccan/tdb2/test/tdb1-logging.h create mode 100644 ccan/tdb2/test/tdb1.corrupt diff --git a/ccan/tdb2/test/jenkins-be-hash.tdb1 b/ccan/tdb2/test/jenkins-be-hash.tdb1 new file mode 100644 index 0000000000000000000000000000000000000000..b652840414857969987a986c848ede159804166e GIT binary patch literal 696 ucmWG>aZ*Uj%t_^9zz)aZ*Uj%t_^9zz%XH8P%H6q@GUMcAd{aZ*Uj%t_^9zz)aZ*Uj%t_^9zz%XH8P%GxOO1+-hQNS@0020x1JwWk literal 0 HcmV?d00001 diff --git a/ccan/tdb2/test/run-tdb1-3G-file.c b/ccan/tdb2/test/run-tdb1-3G-file.c new file mode 100644 index 00000000..6509ca3a --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-3G-file.c @@ -0,0 +1,118 @@ +/* We need this otherwise fcntl locking fails. */ +#define _FILE_OFFSET_BITS 64 +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +static int tdb1_expand_file_sparse(struct tdb1_context *tdb, + tdb1_off_t size, + tdb1_off_t addition) +{ + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB1_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) { + TDB1_LOG((tdb, TDB1_DEBUG_FATAL, "expand_file to %d failed (%s)\n", + size+addition, strerror(errno))); + return -1; + } + } + + return 0; +} + +static const struct tdb1_methods large_io_methods = { + tdb1_read, + tdb1_write, + tdb1_next_hash_chain, + tdb1_oob, + tdb1_expand_file_sparse +}; + +static int test_traverse(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA data, + void *_data) +{ + TDB1_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 tdb1_context *tdb; + TDB1_DATA key, orig_data, data; + uint32_t hash; + tdb1_off_t rec_ptr; + struct tdb1_record rec; + + plan_tests(24); + tdb = tdb1_open_ex("run-36-file.tdb", 1024, TDB1_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 2). */ + ok1(tdb1_expand(tdb, 1500000000) == 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(tdb1_store(tdb, key, orig_data, TDB1_INSERT) == 0); + + data = tdb1_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 = tdb1_find_lock_hash(tdb, key, hash, F_RDLCK, &rec); + ok1(rec_ptr); + ok1(rec_ptr > 2U*1024*1024*1024); + tdb1_unlock(tdb, TDB1_BUCKET(rec.full_hash), F_RDLCK); + + /* Traverse must work. */ + ok1(tdb1_traverse(tdb, test_traverse, &orig_data) == 1); + + /* Delete should work. */ + ok1(tdb1_delete(tdb, key) == 0); + + ok1(tdb1_traverse(tdb, test_traverse, NULL) == 0); + + /* Transactions should work. */ + ok1(tdb1_transaction_start(tdb) == 0); + ok1(tdb1_store(tdb, key, orig_data, TDB1_INSERT) == 0); + + data = tdb1_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + ok1(tdb1_transaction_commit(tdb) == 0); + + ok1(tdb1_traverse(tdb, test_traverse, &orig_data) == 1); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-bad-tdb-header.c b/ccan/tdb2/test/run-tdb1-bad-tdb-header.c new file mode 100644 index 00000000..9e23e979 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-bad-tdb-header.c @@ -0,0 +1,49 @@ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + struct tdb1_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 = tdb1_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(!tdb); + tdb = tdb1_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_CREAT|O_RDWR, + 0600, &taplogctx, NULL); + ok1(tdb); + tdb1_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 == TDB1_VERSION); + hdr.version++; + lseek(fd, 0, SEEK_SET); + ok1(write(fd, &hdr, sizeof(hdr)) == sizeof(hdr)); + close(fd); + + tdb = tdb1_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 = tdb1_open_ex("run-bad-tdb-header.tdb", 1024, 0, + O_RDWR|O_CREAT|O_TRUNC, 0600, &taplogctx, NULL); + ok1(tdb); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-check.c b/ccan/tdb2/test/run-tdb1-check.c new file mode 100644 index 00000000..03b0191a --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-check.c @@ -0,0 +1,55 @@ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(13); + tdb = tdb1_open_ex("run-check.tdb", 1, TDB1_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dsize = strlen("world"); + data.dptr = (void *)"world"; + + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + tdb = tdb1_open_ex("run-check.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + tdb = tdb1_open_ex("test/tdb1.corrupt", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == -1); + ok1(tdb1_error(tdb) == TDB1_ERR_CORRUPT); + tdb1_close(tdb); + + /* Big and little endian should work! */ + tdb = tdb1_open_ex("test/old-nohash-le.tdb1", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + tdb = tdb1_open_ex("test/old-nohash-be.tdb1", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-corrupt.c b/ccan/tdb2/test/run-tdb1-corrupt.c new file mode 100644 index 00000000..b2dcafad --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-corrupt.c @@ -0,0 +1,118 @@ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +static int check(TDB1_DATA key, TDB1_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 tdb1_flip_bit(struct tdb1_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 tdb1_context *tdb) +{ + TDB1_DATA key, data; + unsigned int i, verifiable, corrupt, sizes[2], dsize, ksize; + + ok1(tdb1_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 (tdb1_store(tdb, key, data, TDB1_INSERT) != 0) + abort(); + } + + /* This is how many bytes we expect to be verifiable. */ + /* From the file header. */ + verifiable = strlen(TDB1_MAGIC_FOOD) + 1 + + 2 * sizeof(uint32_t) + 2 * sizeof(tdb1_off_t) + + 2 * sizeof(uint32_t); + /* From the free list chain and hash chains. */ + verifiable += 3 * sizeof(tdb1_off_t); + /* From the record headers & tailer */ + verifiable += 5 * (sizeof(struct tdb1_record) + sizeof(uint32_t)); + /* The free block: we ignore datalen, keylen, full_hash. */ + verifiable += sizeof(struct tdb1_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++) { + tdb1_flip_bit(tdb, i); + memset(sizes, 0, sizeof(sizes)); + if (tdb1_check(tdb, check, sizes) != 0) + corrupt++; + else if (sizes[0] != ksize || sizes[1] != dsize) + corrupt++; + tdb1_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 tdb1_context *tdb; + + plan_tests(4); + /* This should use mmap. */ + tdb = tdb1_open_ex("run-corrupt.tdb", 2, TDB1_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + if (!tdb) + abort(); + check_test(tdb); + tdb1_close(tdb); + + /* This should not. */ + tdb = tdb1_open_ex("run-corrupt.tdb", 2, TDB1_CLEAR_IF_FIRST|TDB1_NOMMAP, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + if (!tdb) + abort(); + check_test(tdb); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-die-during-transaction.c b/ccan/tdb2/test/run-tdb1-die-during-transaction.c new file mode 100644 index 00000000..ae03d5f8 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-die-during-transaction.c @@ -0,0 +1,215 @@ +#include +#include +#include "tdb1-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_lockcheck1 +#define ftruncate ftruncate_check + +#include "tdb2-source.h" +#include +#include +#include +#include +#include +#include +#include "tdb1-external-agent.h" +#include "tdb1-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 tdb1_context *tdb = NULL; + TDB1_DATA key; + enum agent_return ret; + int needed_recovery = 0; + + current = target = 0; +reset: + unlink(TEST_DBNAME); + tdb = tdb1_open_ex(TEST_DBNAME, 1024, TDB1_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_locking1(); + in_transaction = false; + + ret = external_agent_operation1(agent, NEEDS_RECOVERY, ""); + if (ret == SUCCESS) + needed_recovery++; + else if (ret != FAILED) { + diag("Step %u agent NEEDS_RECOVERY = %s", current, + agent_return_name1(ret)); + return false; + } + + ret = external_agent_operation1(agent, op, KEY_STRING); + if (ret != SUCCESS) { + diag("Step %u op %s failed = %s", current, + operation_name1(op), + agent_return_name1(ret)); + return false; + } + + ret = external_agent_operation1(agent, NEEDS_RECOVERY, ""); + if (ret != FAILED) { + diag("Still needs recovery after step %u = %s", + current, agent_return_name1(ret)); + return false; + } + + ret = external_agent_operation1(agent, CHECK, ""); + if (ret != SUCCESS) { + diag("Step %u check failed = %s", current, + agent_return_name1(ret)); + return false; + } + + ret = external_agent_operation1(agent, CLOSE, ""); + if (ret != SUCCESS) { + diag("Step %u close failed = %s", current, + agent_return_name1(ret)); + return false; + } + + /* Suppress logging as this tries to use closed fd. */ + suppress_logging = true; + suppress_lockcheck1 = true; + tdb1_close(tdb); + suppress_logging = false; + suppress_lockcheck1 = false; + target++; + current = 0; + goto reset; + } + + /* Put key for agent to fetch. */ + key.dsize = strlen(KEY_STRING); + key.dptr = (void *)KEY_STRING; + if (tdb1_store(tdb, key, key, TDB1_INSERT) != 0) + return false; + + /* This is the key we insert in transaction. */ + key.dsize--; + + ret = external_agent_operation1(agent, OPEN, TEST_DBNAME); + if (ret != SUCCESS) + errx(1, "Agent failed to open: %s", agent_return_name1(ret)); + + ret = external_agent_operation1(agent, FETCH, KEY_STRING); + if (ret != SUCCESS) + errx(1, "Agent failed find key: %s", agent_return_name1(ret)); + + in_transaction = true; + if (tdb1_transaction_start(tdb) != 0) + return false; + + if (tdb1_store(tdb, key, key, TDB1_INSERT) != 0) + return false; + + if (tdb1_transaction_commit(tdb) != 0) + return false; + + in_transaction = false; + + /* We made it! */ + diag("Completed %u runs", current); + tdb1_close(tdb); + ret = external_agent_operation1(agent, CLOSE, ""); + if (ret != SUCCESS) { + diag("Step %u close failed = %s", current, + agent_return_name1(ret)); + return false; + } + + ok1(needed_recovery); + ok1(locking_errors1 == 0); + ok1(forget_locking1() == 0); + locking_errors1 = 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_callback1 = maybe_die; + + agent = prepare_external_agent1(); + if (!agent) + err(1, "preparing agent"); + + for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) { + diag("Testing %s after death", operation_name1(ops[i])); + ok1(test_death(ops[i], agent)); + } + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-endian.c b/ccan/tdb2/test/run-tdb1-endian.c new file mode 100644 index 00000000..1a01de17 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-endian.c @@ -0,0 +1,54 @@ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(13); + tdb = tdb1_open_ex("run-endian.tdb", 1024, + TDB1_CLEAR_IF_FIRST|TDB1_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(tdb1_store(tdb, key, data, TDB1_MODIFY) < 0); + ok1(tdb1_error(tdb) == TDB1_ERR_NOEXIST); + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) < 0); + ok1(tdb1_error(tdb) == TDB1_ERR_EXISTS); + ok1(tdb1_store(tdb, key, data, TDB1_MODIFY) == 0); + + data = tdb1_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + + key.dsize++; + data = tdb1_fetch(tdb, key); + ok1(data.dptr == NULL); + tdb1_close(tdb); + + /* Reopen: should read it */ + tdb = tdb1_open_ex("run-endian.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data = tdb1_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-incompatible.c b/ccan/tdb2/test/run-tdb1-incompatible.c new file mode 100644 index 00000000..ed3181a9 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-incompatible.c @@ -0,0 +1,178 @@ +#include "tdb2-source.h" +#include +#include +#include + +static unsigned int tdb1_dumb_hash(TDB1_DATA *key) +{ + return key->dsize; +} + +static void log_fn(struct tdb1_context *tdb, enum tdb1_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb1_get_logging_private(tdb); + if (strstr(fmt, "hash")) + (*count)++; +} + +static unsigned int hdr_rwlocks(const char *fname) +{ + struct tdb1_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 tdb1_context *tdb; + unsigned int log_count, flags; + TDB1_DATA d; + struct tdb1_logging_context log_ctx = { log_fn, &log_count }; + + plan_tests(38 * 2); + + for (flags = 0; flags <= TDB1_CONVERT; flags += TDB1_CONVERT) { + unsigned int rwmagic = TDB1_HASH_RWLOCK_MAGIC; + + if (flags & TDB1_CONVERT) + tdb1_convert(&rwmagic, sizeof(rwmagic)); + + /* Create an old-style hash. */ + log_count = 0; + tdb = tdb1_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(tdb1_store(tdb, d, d, TDB1_INSERT) == 0); + tdb1_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 = tdb1_open_ex("run-incompatible.tdb", 0, + TDB1_INCOMPATIBLE_HASH, + O_RDWR, 0600, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + d = tdb1_fetch(tdb, d); + ok1(d.dsize == 5); + free(d.dptr); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDONLY, + 0, &log_ctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDONLY, + 0, &log_ctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + /* OK, now create with incompatible flag, default hash. */ + log_count = 0; + tdb = tdb1_open_ex("run-incompatible.tdb", 0, + flags|TDB1_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(tdb1_store(tdb, d, d, TDB1_INSERT) == 0); + tdb1_close(tdb); + + /* Should have marked rwlocks field. */ + ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic); + + /* Cannot open with old hash. */ + log_count = 0; + tdb = tdb1_open_ex("run-incompatible.tdb", 0, 0, + O_RDWR, 0600, &log_ctx, tdb1_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + /* Can open with jenkins hash. */ + log_count = 0; + tdb = tdb1_open_ex("run-incompatible.tdb", 0, 0, + O_RDWR, 0600, &log_ctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + d = tdb1_fetch(tdb, d); + ok1(d.dsize == 5); + free(d.dptr); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + /* Can open by letting it figure it out itself. */ + log_count = 0; + tdb = tdb1_open_ex("run-incompatible.tdb", 0, 0, + O_RDWR, 0600, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + d.dptr = (void *)"Hello"; + d.dsize = 5; + d = tdb1_fetch(tdb, d); + ok1(d.dsize == 5); + free(d.dptr); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + /* We can also use incompatible hash with other hashes. */ + log_count = 0; + tdb = tdb1_open_ex("run-incompatible.tdb", 0, + flags|TDB1_INCOMPATIBLE_HASH, + O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, + tdb1_dumb_hash); + ok1(tdb); + ok1(log_count == 0); + d.dptr = (void *)"Hello"; + d.dsize = 5; + ok1(tdb1_store(tdb, d, d, TDB1_INSERT) == 0); + tdb1_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 = tdb1_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 = tdb1_open_ex("run-incompatible.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb1_dumb_hash); + ok1(tdb); + ok1(log_count == 0); + d = tdb1_fetch(tdb, d); + ok1(d.dsize == 5); + free(d.dptr); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + } + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-nested-transactions.c b/ccan/tdb2/test/run-tdb1-nested-transactions.c new file mode 100644 index 00000000..2518003c --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-nested-transactions.c @@ -0,0 +1,68 @@ +#include "tdb2-source.h" +#include +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(27); + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + + tdb = tdb1_open_ex("run-nested-transactions.tdb", + 1024, TDB1_CLEAR_IF_FIRST|TDB1_DISALLOW_NESTING, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + ok1(tdb); + + ok1(tdb1_transaction_start(tdb) == 0); + data.dptr = (void *)"world"; + data.dsize = strlen("world"); + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + data = tdb1_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + ok1(tdb1_transaction_start(tdb) != 0); + ok1(tdb1_error(tdb) == TDB1_ERR_NESTING); + + data = tdb1_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + ok1(tdb1_transaction_commit(tdb) == 0); + data = tdb1_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + tdb1_close(tdb); + + /* Allow nesting by default. */ + tdb = tdb1_open_ex("run-nested-transactions.tdb", + 1024, TDB1_DEFAULT, O_RDWR, 0, &taplogctx, NULL); + ok1(tdb); + + ok1(tdb1_transaction_start(tdb) == 0); + ok1(tdb1_transaction_start(tdb) == 0); + ok1(tdb1_delete(tdb, key) == 0); + ok1(tdb1_transaction_commit(tdb) == 0); + ok1(!tdb1_exists(tdb, key)); + ok1(tdb1_transaction_cancel(tdb) == 0); + /* Surprise! Kills inner "committed" transaction. */ + ok1(tdb1_exists(tdb, key)); + + ok1(tdb1_transaction_start(tdb) == 0); + ok1(tdb1_transaction_start(tdb) == 0); + ok1(tdb1_delete(tdb, key) == 0); + ok1(tdb1_transaction_commit(tdb) == 0); + ok1(!tdb1_exists(tdb, key)); + ok1(tdb1_transaction_commit(tdb) == 0); + ok1(!tdb1_exists(tdb, key)); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-nested-traverse.c b/ccan/tdb2/test/run-tdb1-nested-traverse.c new file mode 100644 index 00000000..b6f6ac63 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-nested-traverse.c @@ -0,0 +1,80 @@ +#include "tdb1-lock-tracking.h" +#define fcntl fcntl_with_lockcheck1 +#include "tdb2-source.h" +#include +#undef fcntl +#include +#include +#include +#include "tdb1-external-agent.h" +#include "tdb1-logging.h" + +static struct agent *agent; + +static bool correct_key(TDB1_DATA key) +{ + return key.dsize == strlen("hi") + && memcmp(key.dptr, "hi", key.dsize) == 0; +} + +static bool correct_data(TDB1_DATA data) +{ + return data.dsize == strlen("world") + && memcmp(data.dptr, "world", data.dsize) == 0; +} + +static int traverse2(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA data, + void *p) +{ + ok1(correct_key(key)); + ok1(correct_data(data)); + return 0; +} + +static int traverse1(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA data, + void *p) +{ + ok1(correct_key(key)); + ok1(correct_data(data)); + ok1(external_agent_operation1(agent, TRANSACTION_START, tdb1_name(tdb)) + == WOULD_HAVE_BLOCKED); + tdb1_traverse(tdb, traverse2, NULL); + + /* That should *not* release the transaction lock! */ + ok1(external_agent_operation1(agent, TRANSACTION_START, tdb1_name(tdb)) + == WOULD_HAVE_BLOCKED); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(17); + agent = prepare_external_agent1(); + if (!agent) + err(1, "preparing agent"); + + tdb = tdb1_open_ex("run-nested-traverse.tdb", 1024, TDB1_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + ok1(tdb); + + ok1(external_agent_operation1(agent, OPEN, tdb1_name(tdb)) == SUCCESS); + ok1(external_agent_operation1(agent, TRANSACTION_START, tdb1_name(tdb)) + == SUCCESS); + ok1(external_agent_operation1(agent, TRANSACTION_COMMIT, tdb1_name(tdb)) + == SUCCESS); + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dptr = (void *)"world"; + data.dsize = strlen("world"); + + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + tdb1_traverse(tdb, traverse1, NULL); + tdb1_traverse_read(tdb, traverse1, NULL); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-no-lock-during-traverse.c b/ccan/tdb2/test/run-tdb1-no-lock-during-traverse.c new file mode 100644 index 00000000..57056299 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-no-lock-during-traverse.c @@ -0,0 +1,106 @@ +#include +#include +#include "tdb1-lock-tracking.h" + +#define fcntl fcntl_with_lockcheck1 + +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +#undef fcntl + +#define NUM_ENTRIES 10 + +static bool prepare_entries(struct tdb1_context *tdb) +{ + unsigned int i; + TDB1_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 (tdb1_store(tdb, key, data, 0) != 0) + return false; + } + return true; +} + +static void delete_entries(struct tdb1_context *tdb) +{ + unsigned int i; + TDB1_DATA key; + + for (i = 0; i < NUM_ENTRIES; i++) { + key.dsize = sizeof(i); + key.dptr = (void *)&i; + + ok1(tdb1_delete(tdb, key) == 0); + } +} + +/* We don't know how many times this will run. */ +static int delete_other(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA data, + void *private_data) +{ + unsigned int i; + memcpy(&i, key.dptr, 4); + i = (i + 1) % NUM_ENTRIES; + key.dptr = (void *)&i; + if (tdb1_delete(tdb, key) != 0) + (*(int *)private_data)++; + return 0; +} + +static int delete_self(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA data, + void *private_data) +{ + ok1(tdb1_delete(tdb, key) == 0); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + int errors = 0; + + plan_tests(41); + tdb = tdb1_open_ex("run-no-lock-during-traverse.tdb", + 1024, TDB1_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR, + 0600, &taplogctx, NULL); + + ok1(tdb); + ok1(prepare_entries(tdb)); + ok1(locking_errors1 == 0); + ok1(tdb1_lockall(tdb) == 0); + ok1(locking_errors1 == 0); + tdb1_traverse(tdb, delete_other, &errors); + ok1(errors == 0); + ok1(locking_errors1 == 0); + ok1(tdb1_unlockall(tdb) == 0); + + ok1(prepare_entries(tdb)); + ok1(locking_errors1 == 0); + ok1(tdb1_lockall(tdb) == 0); + ok1(locking_errors1 == 0); + tdb1_traverse(tdb, delete_self, NULL); + ok1(locking_errors1 == 0); + ok1(tdb1_unlockall(tdb) == 0); + + ok1(prepare_entries(tdb)); + ok1(locking_errors1 == 0); + ok1(tdb1_lockall(tdb) == 0); + ok1(locking_errors1 == 0); + delete_entries(tdb); + ok1(locking_errors1 == 0); + ok1(tdb1_unlockall(tdb) == 0); + + ok1(tdb1_close(tdb) == 0); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-oldhash.c b/ccan/tdb2/test/run-tdb1-oldhash.c new file mode 100644 index 00000000..32b4200d --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-oldhash.c @@ -0,0 +1,40 @@ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_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 = tdb1_open_ex("test/old-nohash-le.tdb1", 0, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + tdb = tdb1_open_ex("test/old-nohash-be.tdb1", 0, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + tdb = tdb1_open_ex("test/old-nohash-le.tdb1", 0, 0, O_RDWR, 0, + &taplogctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + tdb = tdb1_open_ex("test/old-nohash-be.tdb1", 0, 0, O_RDWR, 0, + &taplogctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-open-during-transaction.c b/ccan/tdb2/test/run-tdb1-open-during-transaction.c new file mode 100644 index 00000000..7b22320a --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-open-during-transaction.c @@ -0,0 +1,176 @@ +#include "config.h" +#include "tdb1-lock-tracking.h" +#include + +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_lockcheck1 +#define ftruncate ftruncate_check + +#include "tdb2-source.h" +#include +#include +#include +#include +#include +#include "tdb1-external-agent.h" +#include "tdb1-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_operation1(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_name1(ret)); + errors++; + } + + if (ret == SUCCESS) { + ret = external_agent_operation1(agent, CLOSE, NULL); + if (ret != SUCCESS) { + diag("Agent failed to close tdb: %s", + agent_return_name1(ret)); + errors++; + } + } else if (ret != WOULD_HAVE_BLOCKED) { + diag("Agent opening file gave %s", + agent_return_name1(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[] = { TDB1_DEFAULT, + TDB1_CLEAR_IF_FIRST, + TDB1_NOMMAP, + TDB1_CLEAR_IF_FIRST | TDB1_NOMMAP }; + int i; + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(20); + agent = prepare_external_agent1(); + if (!agent) + err(1, "preparing agent"); + + unlock_callback1 = after_unlock; + for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) { + clear_if_first = (flags[i] & TDB1_CLEAR_IF_FIRST); + diag("Test with %s and %s\n", + clear_if_first ? "CLEAR" : "DEFAULT", + (flags[i] & TDB1_NOMMAP) ? "no mmap" : "mmap"); + unlink(TEST_DBNAME); + tdb = tdb1_open_ex(TEST_DBNAME, 1024, flags[i], + O_CREAT|O_TRUNC|O_RDWR, 0600, + &taplogctx, NULL); + ok1(tdb); + + opened = true; + ok1(tdb1_transaction_start(tdb) == 0); + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dptr = (void *)"world"; + data.dsize = strlen("world"); + + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + ok1(tdb1_transaction_commit(tdb) == 0); + ok(!errors, "We had %u open errors", errors); + + opened = false; + tdb1_close(tdb); + } + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-readonly-check.c b/ccan/tdb2/test/run-tdb1-readonly-check.c new file mode 100644 index 00000000..2c06ca92 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-readonly-check.c @@ -0,0 +1,43 @@ +/* We should be able to tdb1_check a O_RDONLY tdb, and we were previously allowed + * to tdb1_check() inside a transaction (though that's paranoia!). */ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(11); + tdb = tdb1_open_ex("run-readonly-check.tdb", 1024, + TDB1_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(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + + /* We are also allowed to do a check inside a transaction. */ + ok1(tdb1_transaction_start(tdb) == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + ok1(tdb1_close(tdb) == 0); + + tdb = tdb1_open_ex("run-readonly-check.tdb", 1024, + TDB1_DEFAULT, O_RDONLY, 0, &taplogctx, NULL); + + ok1(tdb); + ok1(tdb1_store(tdb, key, data, TDB1_MODIFY) == -1); + ok1(tdb1_error(tdb) == TDB1_ERR_RDONLY); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + ok1(tdb1_close(tdb) == 0); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-rwlock-check.c b/ccan/tdb2/test/run-tdb1-rwlock-check.c new file mode 100644 index 00000000..5d438d38 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-rwlock-check.c @@ -0,0 +1,36 @@ +#include "tdb2-source.h" +#include +#include +#include + +static void log_fn(struct tdb1_context *tdb, enum tdb1_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb1_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 tdb1_context *tdb; + unsigned int log_count; + struct tdb1_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 = tdb1_open_ex("test/rwlock-le.tdb1", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb1_open_ex("test/rwlock-be.tdb1", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-summary.c b/ccan/tdb2/test/run-tdb1-summary.c new file mode 100644 index 00000000..ccfc0958 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-summary.c @@ -0,0 +1,54 @@ +#include "tdb2-source.h" +#include +#include +#include + +int main(int argc, char *argv[]) +{ + unsigned int i, j; + struct tdb1_context *tdb; + int flags[] = { TDB1_INTERNAL, TDB1_DEFAULT, TDB1_NOMMAP, + TDB1_INTERNAL|TDB1_CONVERT, TDB1_CONVERT, + TDB1_NOMMAP|TDB1_CONVERT }; + TDB1_DATA key = { (unsigned char *)&j, sizeof(j) }; + TDB1_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 = tdb1_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 (tdb1_store(tdb, key, data, TDB1_REPLACE) != 0) + fail("Storing in tdb"); + } + + summary = tdb1_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); + tdb1_close(tdb); + } + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-traverse-in-transaction.c b/ccan/tdb2/test/run-tdb1-traverse-in-transaction.c new file mode 100644 index 00000000..eb925be2 --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-traverse-in-transaction.c @@ -0,0 +1,80 @@ +#include "config.h" +#include "tdb1-lock-tracking.h" +#define fcntl fcntl_with_lockcheck1 +#include "tdb2-source.h" +#include +#undef fcntl_with_lockcheck +#include +#include +#include +#include "tdb1-external-agent.h" +#include "tdb1-logging.h" + +static struct agent *agent; + +static bool correct_key(TDB1_DATA key) +{ + return key.dsize == strlen("hi") + && memcmp(key.dptr, "hi", key.dsize) == 0; +} + +static bool correct_data(TDB1_DATA data) +{ + return data.dsize == strlen("world") + && memcmp(data.dptr, "world", data.dsize) == 0; +} + +static int traverse(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA data, + void *p) +{ + ok1(correct_key(key)); + ok1(correct_data(data)); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(13); + agent = prepare_external_agent1(); + if (!agent) + err(1, "preparing agent"); + + tdb = tdb1_open_ex("run-traverse-in-transaction.tdb", + 1024, TDB1_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(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + + ok1(external_agent_operation1(agent, OPEN, tdb1_name(tdb)) == SUCCESS); + + ok1(tdb1_transaction_start(tdb) == 0); + ok1(external_agent_operation1(agent, TRANSACTION_START, tdb1_name(tdb)) + == WOULD_HAVE_BLOCKED); + tdb1_traverse(tdb, traverse, NULL); + + /* That should *not* release the transaction lock! */ + ok1(external_agent_operation1(agent, TRANSACTION_START, tdb1_name(tdb)) + == WOULD_HAVE_BLOCKED); + tdb1_traverse_read(tdb, traverse, NULL); + + /* That should *not* release the transaction lock! */ + ok1(external_agent_operation1(agent, TRANSACTION_START, tdb1_name(tdb)) + == WOULD_HAVE_BLOCKED); + ok1(tdb1_transaction_commit(tdb) == 0); + /* Now we should be fine. */ + ok1(external_agent_operation1(agent, TRANSACTION_START, tdb1_name(tdb)) + == SUCCESS); + + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-wronghash-fail.c b/ccan/tdb2/test/run-tdb1-wronghash-fail.c new file mode 100644 index 00000000..5a7e311a --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-wronghash-fail.c @@ -0,0 +1,111 @@ +#include "tdb2-source.h" +#include +#include +#include + +static void log_fn(struct tdb1_context *tdb, enum tdb1_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb1_get_logging_private(tdb); + if (strstr(fmt, "hash")) + (*count)++; +} + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + unsigned int log_count; + TDB1_DATA d; + struct tdb1_logging_context log_ctx = { log_fn, &log_count }; + + plan_tests(28); + + /* Create with default hash. */ + log_count = 0; + tdb = tdb1_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(tdb1_store(tdb, d, d, TDB1_INSERT) == 0); + tdb1_close(tdb); + + /* Fail to open with different hash. */ + tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb1_jenkins_hash); + ok1(!tdb); + ok1(log_count == 1); + + /* Create with different hash. */ + log_count = 0; + tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, + O_CREAT|O_RDWR|O_TRUNC, + 0600, &log_ctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + tdb1_close(tdb); + + /* Endian should be no problem. */ + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDWR, 0, + &log_ctx, tdb1_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDWR, 0, + &log_ctx, tdb1_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + /* Fail to open with old default hash. */ + tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb1_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDONLY, + 0, &log_ctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDONLY, + 0, &log_ctx, tdb1_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + /* It should open with jenkins hash if we don't specify. */ + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-le-hash.tdb1", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + log_count = 0; + tdb = tdb1_open_ex("test/jenkins-be-hash.tdb1", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + log_count = 0; + tdb = tdb1_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb1_check(tdb, NULL, NULL) == 0); + tdb1_close(tdb); + + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1-zero-append.c b/ccan/tdb2/test/run-tdb1-zero-append.c new file mode 100644 index 00000000..e79ab60a --- /dev/null +++ b/ccan/tdb2/test/run-tdb1-zero-append.c @@ -0,0 +1,31 @@ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(4); + tdb = tdb1_open_ex(NULL, 1024, TDB1_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(tdb1_append(tdb, key, data) == 0); + ok1(tdb1_append(tdb, key, data) == 0); + data = tdb1_fetch(tdb, key); + ok1(data.dsize == 0); + free(data.dptr); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/run-tdb1.c b/ccan/tdb2/test/run-tdb1.c new file mode 100644 index 00000000..ebbfd77b --- /dev/null +++ b/ccan/tdb2/test/run-tdb1.c @@ -0,0 +1,40 @@ +#include "tdb2-source.h" +#include +#include +#include +#include "tdb1-logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb1_context *tdb; + TDB1_DATA key, data; + + plan_tests(10); + tdb = tdb1_open_ex("run.tdb", 1024, TDB1_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(tdb1_store(tdb, key, data, TDB1_MODIFY) < 0); + ok1(tdb1_error(tdb) == TDB1_ERR_NOEXIST); + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) == 0); + ok1(tdb1_store(tdb, key, data, TDB1_INSERT) < 0); + ok1(tdb1_error(tdb) == TDB1_ERR_EXISTS); + ok1(tdb1_store(tdb, key, data, TDB1_MODIFY) == 0); + + data = tdb1_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + + key.dsize++; + data = tdb1_fetch(tdb, key); + ok1(data.dptr == NULL); + tdb1_close(tdb); + + return exit_status(); +} diff --git a/ccan/tdb2/test/rwlock-be.tdb1 b/ccan/tdb2/test/rwlock-be.tdb1 new file mode 100644 index 0000000000000000000000000000000000000000..45b5f09a1b619b4f79201057dd811781d4151d33 GIT binary patch literal 696 ncmWG>aZ*Uj%t_^9zz%XH8P%GBQt3za#j&dx6&(!$`iB4j>yiaW literal 0 HcmV?d00001 diff --git a/ccan/tdb2/test/rwlock-le.tdb1 b/ccan/tdb2/test/rwlock-le.tdb1 new file mode 100644 index 0000000000000000000000000000000000000000..45b5f09a1b619b4f79201057dd811781d4151d33 GIT binary patch literal 696 ncmWG>aZ*Uj%t_^9zz%XH8P%GBQt3za#j&dx6&(!$`iB4j>yiaW literal 0 HcmV?d00001 diff --git a/ccan/tdb2/test/tdb1-external-agent.c b/ccan/tdb2/test/tdb1-external-agent.c new file mode 100644 index 00000000..3f73ae3a --- /dev/null +++ b/ccan/tdb2/test/tdb1-external-agent.c @@ -0,0 +1,196 @@ +#include "tdb1-external-agent.h" +#include "tdb1-lock-tracking.h" +#include "tdb1-logging.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct tdb1_context *tdb; + +static enum agent_return do_operation(enum operation op, const char *name) +{ + TDB1_DATA k; + enum agent_return ret; + TDB1_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_block1 = 0; + switch (op) { + case OPEN: + if (tdb) { + diag("Already have tdb %s open", tdb1_name(tdb)); + return OTHER_FAILURE; + } + tdb = tdb1_open_ex(name, 0, TDB1_DEFAULT, O_RDWR, 0, + &taplogctx, NULL); + if (!tdb) { + if (!locking_would_block1) + 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 = tdb1_open_ex(name, 0, TDB1_CLEAR_IF_FIRST, O_RDWR, 0, + &taplogctx, NULL); + ret = tdb ? SUCCESS : OTHER_FAILURE; + break; + case TRANSACTION_START: + ret = tdb1_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE; + break; + case FETCH: + data = tdb1_fetch(tdb, k); + if (data.dptr == NULL) { + if (tdb->ecode == TDB1_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 = tdb1_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE; + break; + case TRANSACTION_COMMIT: + ret = tdb1_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE; + break; + case CHECK: + ret = tdb1_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE; + break; + case NEEDS_RECOVERY: + ret = tdb1_needs_recovery(tdb) ? SUCCESS : FAILED; + break; + case CLOSE: + ret = tdb1_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE; + tdb = NULL; + break; + default: + ret = OTHER_FAILURE; + } + + if (locking_would_block1) + 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_agent1(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_locks1 = 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_operation1(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_name1(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_name1(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**"; +} diff --git a/ccan/tdb2/test/tdb1-external-agent.h b/ccan/tdb2/test/tdb1-external-agent.h new file mode 100644 index 00000000..8b0c1548 --- /dev/null +++ b/ccan/tdb2/test/tdb1-external-agent.h @@ -0,0 +1,41 @@ +#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_agent1(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_operation1(struct agent *handle, + enum operation op, + const char *name); + +/* Mapping enum -> string. */ +const char *agent_return_name1(enum agent_return ret); +const char *operation_name1(enum operation op); + +#endif /* TDB_TEST_EXTERNAL_AGENT_H */ diff --git a/ccan/tdb2/test/tdb1-lock-tracking.c b/ccan/tdb2/test/tdb1-lock-tracking.c new file mode 100644 index 00000000..197b1f07 --- /dev/null +++ b/ccan/tdb2/test/tdb1-lock-tracking.c @@ -0,0 +1,146 @@ +/* We save the locks so we can reaquire them. */ +#include +#include +#include +#include +#include +#include +#include "tdb1-lock-tracking.h" + +struct lock { + struct lock *next; + unsigned int off; + unsigned int len; + int type; +}; +static struct lock *locks; +int locking_errors1 = 0; +bool suppress_lockcheck1 = false; +bool nonblocking_locks1; +int locking_would_block1 = 0; +void (*unlock_callback1)(int fd); + +int fcntl_with_lockcheck1(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_locks1) { + 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_block1++; + } + + 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_lockcheck1) { + diag("Unknown unlock %u@%u - %i", + (int)fl->l_len, (int)fl->l_start, ret); + locking_errors1++; + } + } 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 == TDB1_FREELIST_TOP + && fl->l_start == TDB1_FREELIST_TOP + && i->len == 0 + && fl->l_len == 0) { + if (ret == 0) + i->type = F_WRLCK; + goto done; + } + if (!suppress_lockcheck1) { + 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_errors1++; + } + } + + 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_callback1) + unlock_callback1(fd); + return ret; +} + +unsigned int forget_locking1(void) +{ + unsigned int num = 0; + while (locks) { + struct lock *next = locks->next; + free(locks); + locks = next; + num++; + } + return num; +} diff --git a/ccan/tdb2/test/tdb1-lock-tracking.h b/ccan/tdb2/test/tdb1-lock-tracking.h new file mode 100644 index 00000000..cb8c2f12 --- /dev/null +++ b/ccan/tdb2/test/tdb1-lock-tracking.h @@ -0,0 +1,26 @@ +#ifndef TDB1_LOCK_TRACKING_H +#define TDB1_LOCK_TRACKING_H +#include +#include + +/* Set this if you want a callback after fnctl unlock. */ +extern void (*unlock_callback1)(int fd); + +/* Replacement fcntl. */ +int fcntl_with_lockcheck1(int fd, int cmd, ... /* arg */ ); + +/* Discard locking info: returns number of locks outstanding. */ +unsigned int forget_locking1(void); + +/* Number of errors in locking. */ +extern int locking_errors1; + +/* Suppress lock checking. */ +extern bool suppress_lockcheck1; + +/* Make all locks non-blocking. */ +extern bool nonblocking_locks1; + +/* Number of times we failed a lock because we made it non-blocking. */ +extern int locking_would_block1; +#endif /* LOCK_TRACKING_H */ diff --git a/ccan/tdb2/test/tdb1-logging.c b/ccan/tdb2/test/tdb1-logging.c new file mode 100644 index 00000000..43ce07b2 --- /dev/null +++ b/ccan/tdb2/test/tdb1-logging.c @@ -0,0 +1,30 @@ +#include "tdb1-logging.h" +#include +#include +#include +#include +#include + +/* Turn log messages into tap diag messages. */ +static void taplog(struct tdb1_context *tdb, + enum tdb1_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 tdb1_logging_context taplogctx = { taplog, NULL }; diff --git a/ccan/tdb2/test/tdb1-logging.h b/ccan/tdb2/test/tdb1-logging.h new file mode 100644 index 00000000..128ec7f8 --- /dev/null +++ b/ccan/tdb2/test/tdb1-logging.h @@ -0,0 +1,10 @@ +#ifndef TDB_TEST_LOGGING_H +#define TDB_TEST_LOGGING_H +#include +#include + +extern bool suppress_logging; +extern const char *log_prefix; +extern struct tdb1_logging_context taplogctx; + +#endif /* TDB_TEST_LOGGING_H */ diff --git a/ccan/tdb2/test/tdb1.corrupt b/ccan/tdb2/test/tdb1.corrupt new file mode 100644 index 0000000000000000000000000000000000000000..83d6677454300a5173f80dcbcfec15fc636aed24 GIT binary patch literal 192512 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
  • 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%0D;B{e9TTu0g|3*k?HPjWZaHd%ZIR!~gXDzVJoq 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 OTV%~k$Hy(v-~S&FX3gOM literal 0 HcmV?d00001 diff --git a/ccan/tdb2/test/tdb2-source.h b/ccan/tdb2/test/tdb2-source.h index 56765c8c..f289aeb0 100644 --- a/ccan/tdb2/test/tdb2-source.h +++ b/ccan/tdb2/test/tdb2-source.h @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include -- 2.39.2