summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/ntdb/ABI/ntdb-0.9.sigs39
-rw-r--r--lib/ntdb/LICENSE (renamed from lib/tdb2/LICENSE)0
-rw-r--r--lib/ntdb/Makefile (renamed from lib/tdb2/Makefile)0
-rw-r--r--lib/ntdb/check.c864
-rwxr-xr-xlib/ntdb/configure (renamed from lib/tdb2/configure)0
-rw-r--r--lib/ntdb/doc/TDB_porting.txt65
-rw-r--r--lib/ntdb/doc/design-1.3.txt (renamed from lib/tdb2/doc/design-1.3.txt)0
-rw-r--r--lib/ntdb/doc/design.lyx (renamed from lib/tdb2/doc/design.lyx)0
-rw-r--r--lib/ntdb/doc/design.lyx,v (renamed from lib/tdb2/doc/design.lyx,v)0
-rw-r--r--lib/ntdb/doc/design.pdf (renamed from lib/tdb2/doc/design.pdf)bin240440 -> 240440 bytes
-rw-r--r--lib/ntdb/doc/design.txt (renamed from lib/tdb2/doc/design.txt)0
-rw-r--r--lib/ntdb/free.c976
-rw-r--r--lib/ntdb/hash.c894
-rw-r--r--lib/ntdb/io.c650
-rw-r--r--lib/ntdb/lock.c883
-rw-r--r--lib/ntdb/ntdb.c605
-rw-r--r--lib/ntdb/ntdb.h901
-rw-r--r--lib/ntdb/ntdb.pc.in (renamed from lib/tdb2/tdb.pc.in)6
-rw-r--r--lib/ntdb/open.c768
-rw-r--r--lib/ntdb/private.h657
-rw-r--r--lib/ntdb/pyntdb.c591
-rw-r--r--lib/ntdb/summary.c (renamed from lib/tdb2/summary.c)178
-rw-r--r--lib/ntdb/test/api-12-store.c (renamed from lib/tdb2/test/api-12-store.c)34
-rw-r--r--lib/ntdb/test/api-13-delete.c205
-rw-r--r--lib/ntdb/test/api-14-exists.c54
-rw-r--r--lib/ntdb/test/api-16-wipe_all.c46
-rw-r--r--lib/ntdb/test/api-21-parse_record.c67
-rw-r--r--lib/ntdb/test/api-55-transaction.c73
-rw-r--r--lib/ntdb/test/api-80-tdb_fd.c (renamed from lib/tdb2/test/api-80-tdb_fd.c)22
-rw-r--r--lib/ntdb/test/api-81-seqnum.c69
-rw-r--r--lib/ntdb/test/api-82-lockattr.c (renamed from lib/tdb2/test/api-82-lockattr.c)116
-rw-r--r--lib/ntdb/test/api-83-openhook.c (renamed from lib/tdb2/test/api-83-openhook.c)50
-rw-r--r--lib/ntdb/test/api-91-get-stats.c (renamed from lib/tdb2/test/api-91-get-stats.c)32
-rw-r--r--lib/ntdb/test/api-92-get-set-readonly.c105
-rw-r--r--lib/ntdb/test/api-93-repack.c (renamed from lib/tdb2/test/api-93-repack.c)36
-rw-r--r--lib/ntdb/test/api-add-remove-flags.c89
-rw-r--r--lib/ntdb/test/api-check-callback.c (renamed from lib/tdb2/test/api-check-callback.c)44
-rw-r--r--lib/ntdb/test/api-firstkey-nextkey.c (renamed from lib/tdb2/test/api-firstkey-nextkey.c)86
-rw-r--r--lib/ntdb/test/api-fork-test.c (renamed from lib/tdb2/test/api-fork-test.c)96
-rw-r--r--lib/ntdb/test/api-locktimeout.c (renamed from lib/tdb2/test/api-locktimeout.c)64
-rw-r--r--lib/ntdb/test/api-missing-entries.c (renamed from lib/tdb2/test/api-missing-entries.c)20
-rw-r--r--lib/ntdb/test/api-open-multiple-times.c83
-rw-r--r--lib/ntdb/test/api-record-expand.c (renamed from lib/tdb2/test/api-record-expand.c)30
-rw-r--r--lib/ntdb/test/api-simple-delete.c39
-rw-r--r--lib/ntdb/test/api-summary.c (renamed from lib/tdb2/test/api-summary.c)34
-rw-r--r--lib/ntdb/test/external-agent.c (renamed from lib/tdb2/test/external-agent.c)70
-rw-r--r--lib/ntdb/test/external-agent.h (renamed from lib/tdb2/test/external-agent.h)16
-rw-r--r--lib/ntdb/test/failtest_helper.c (renamed from lib/tdb2/test/failtest_helper.c)6
-rw-r--r--lib/ntdb/test/failtest_helper.h (renamed from lib/tdb2/test/failtest_helper.h)8
-rw-r--r--lib/ntdb/test/helpapi-external-agent.c7
-rw-r--r--lib/ntdb/test/helprun-external-agent.c7
-rw-r--r--lib/ntdb/test/helprun-layout.c402
-rw-r--r--lib/ntdb/test/layout.h (renamed from lib/tdb2/test/layout.h)56
-rw-r--r--lib/ntdb/test/lock-tracking.c (renamed from lib/tdb2/test/lock-tracking.c)10
-rw-r--r--lib/ntdb/test/lock-tracking.h (renamed from lib/tdb2/test/lock-tracking.h)0
-rw-r--r--lib/ntdb/test/logging.c (renamed from lib/tdb2/test/logging.c)14
-rw-r--r--lib/ntdb/test/logging.h17
-rw-r--r--lib/ntdb/test/ntdb-source.h (renamed from lib/tdb2/test/tdb2-source.h)2
-rw-r--r--lib/ntdb/test/run-001-encode.c (renamed from lib/tdb2/test/run-001-encode.c)16
-rw-r--r--lib/ntdb/test/run-001-fls.c (renamed from lib/tdb2/test/run-001-fls.c)2
-rw-r--r--lib/ntdb/test/run-01-new_database.c (renamed from lib/tdb2/test/run-01-new_database.c)18
-rw-r--r--lib/ntdb/test/run-02-expand.c62
-rw-r--r--lib/ntdb/test/run-03-coalesce.c178
-rw-r--r--lib/ntdb/test/run-04-basichash.c260
-rw-r--r--lib/ntdb/test/run-05-readonly-open.c (renamed from lib/tdb2/test/run-05-readonly-open.c)46
-rw-r--r--lib/ntdb/test/run-10-simple-store.c (renamed from lib/tdb2/test/run-10-simple-store.c)38
-rw-r--r--lib/ntdb/test/run-11-simple-fetch.c (renamed from lib/tdb2/test/run-11-simple-fetch.c)40
-rw-r--r--lib/ntdb/test/run-12-check.c (renamed from lib/tdb2/test/run-12-check.c)28
-rw-r--r--lib/ntdb/test/run-15-append.c (renamed from lib/tdb2/test/run-15-append.c)90
-rw-r--r--lib/ntdb/test/run-20-growhash.c137
-rw-r--r--lib/ntdb/test/run-25-hashoverload.c113
-rw-r--r--lib/ntdb/test/run-30-exhaust-before-expand.c71
-rw-r--r--lib/ntdb/test/run-35-convert.c (renamed from lib/tdb2/test/run-35-convert.c)44
-rw-r--r--lib/ntdb/test/run-50-multiple-freelists.c70
-rw-r--r--lib/ntdb/test/run-56-open-during-transaction.c (renamed from lib/tdb2/test/run-56-open-during-transaction.c)34
-rw-r--r--lib/ntdb/test/run-57-die-during-transaction.c (renamed from lib/tdb2/test/run-57-die-during-transaction.c)31
-rw-r--r--lib/ntdb/test/run-64-bit-tdb.c72
-rw-r--r--lib/ntdb/test/run-90-get-set-attributes.c159
-rw-r--r--lib/ntdb/test/run-capabilities.c (renamed from lib/tdb2/test/run-capabilities.c)140
-rw-r--r--lib/ntdb/test/run-expand-in-transaction.c36
-rw-r--r--lib/ntdb/test/run-features.c (renamed from lib/tdb2/test/run-features.c)46
-rw-r--r--lib/ntdb/test/run-lockall.c (renamed from lib/tdb2/test/run-lockall.c)33
-rw-r--r--lib/ntdb/test/run-remap-in-read_traverse.c (renamed from lib/tdb2/test/run-remap-in-read_traverse.c)26
-rw-r--r--lib/ntdb/test/run-seed.c61
-rw-r--r--lib/ntdb/test/run-tdb_errorstr.c52
-rw-r--r--lib/ntdb/test/run-tdb_foreach.c (renamed from lib/tdb2/test/run-tdb_foreach.c)48
-rw-r--r--lib/ntdb/test/run-traverse.c (renamed from lib/tdb2/test/run-traverse.c)78
-rw-r--r--lib/ntdb/test/tap-interface.c (renamed from lib/tdb2/test/tap-interface.c)0
-rw-r--r--lib/ntdb/test/tap-interface.h (renamed from lib/tdb2/test/tap-interface.h)0
-rw-r--r--lib/ntdb/tools/Makefile16
-rw-r--r--lib/ntdb/tools/growtdb-bench.c114
-rw-r--r--lib/ntdb/tools/mkntdb.c (renamed from lib/tdb2/tools/mktdb2.c)12
-rw-r--r--lib/ntdb/tools/ntdbbackup.c (renamed from lib/tdb2/tools/tdb2backup.c)148
-rw-r--r--lib/ntdb/tools/ntdbdump.c (renamed from lib/tdb2/tools/tdb2dump.c)30
-rw-r--r--lib/ntdb/tools/ntdbrestore.c (renamed from lib/tdb2/tools/tdb2restore.c)36
-rw-r--r--lib/ntdb/tools/ntdbtool.c (renamed from lib/tdb2/tools/tdb2tool.c)272
-rw-r--r--lib/ntdb/tools/ntdbtorture.c (renamed from lib/tdb2/tools/tdb2torture.c)134
-rw-r--r--lib/ntdb/tools/speed.c (renamed from lib/tdb2/tools/speed.c)246
-rw-r--r--lib/ntdb/transaction.c1322
-rw-r--r--lib/ntdb/traverse.c99
-rw-r--r--lib/ntdb/wscript265
-rw-r--r--lib/tdb2/ABI/tdb-2.0.0.sigs40
-rw-r--r--lib/tdb2/ABI/tdb-2.0.1.sigs39
-rw-r--r--lib/tdb2/TODO4
-rw-r--r--lib/tdb2/_info91
-rw-r--r--lib/tdb2/check.c864
-rw-r--r--lib/tdb2/doc/TDB1_porting.txt72
-rw-r--r--lib/tdb2/free.c976
-rw-r--r--lib/tdb2/hash.c894
-rw-r--r--lib/tdb2/io.c650
-rw-r--r--lib/tdb2/lock.c883
-rw-r--r--lib/tdb2/open.c768
-rw-r--r--lib/tdb2/private.h657
-rw-r--r--lib/tdb2/pytdb.c591
-rw-r--r--lib/tdb2/tdb.c605
-rw-r--r--lib/tdb2/tdb2.h897
-rw-r--r--lib/tdb2/test/api-13-delete.c205
-rw-r--r--lib/tdb2/test/api-14-exists.c54
-rw-r--r--lib/tdb2/test/api-16-wipe_all.c46
-rw-r--r--lib/tdb2/test/api-21-parse_record.c67
-rw-r--r--lib/tdb2/test/api-55-transaction.c73
-rw-r--r--lib/tdb2/test/api-81-seqnum.c69
-rw-r--r--lib/tdb2/test/api-92-get-set-readonly.c105
-rw-r--r--lib/tdb2/test/api-add-remove-flags.c89
-rw-r--r--lib/tdb2/test/api-open-multiple-times.c83
-rw-r--r--lib/tdb2/test/api-simple-delete.c39
-rw-r--r--lib/tdb2/test/helpapi-external-agent.c7
-rw-r--r--lib/tdb2/test/helprun-external-agent.c7
-rw-r--r--lib/tdb2/test/helprun-layout.c402
-rw-r--r--lib/tdb2/test/logging.h17
-rw-r--r--lib/tdb2/test/run-02-expand.c62
-rw-r--r--lib/tdb2/test/run-03-coalesce.c178
-rw-r--r--lib/tdb2/test/run-04-basichash.c260
-rw-r--r--lib/tdb2/test/run-20-growhash.c137
-rw-r--r--lib/tdb2/test/run-25-hashoverload.c113
-rw-r--r--lib/tdb2/test/run-30-exhaust-before-expand.c71
-rw-r--r--lib/tdb2/test/run-50-multiple-freelists.c70
-rw-r--r--lib/tdb2/test/run-64-bit-tdb.c72
-rw-r--r--lib/tdb2/test/run-90-get-set-attributes.c159
-rw-r--r--lib/tdb2/test/run-expand-in-transaction.c36
-rw-r--r--lib/tdb2/test/run-seed.c61
-rw-r--r--lib/tdb2/test/run-tdb_errorstr.c52
-rw-r--r--lib/tdb2/tools/Makefile16
-rw-r--r--lib/tdb2/tools/growtdb-bench.c114
-rw-r--r--lib/tdb2/transaction.c1322
-rw-r--r--lib/tdb2/traverse.c99
-rw-r--r--lib/tdb2/wscript278
147 files changed, 13576 insertions, 13727 deletions
diff --git a/lib/ntdb/ABI/ntdb-0.9.sigs b/lib/ntdb/ABI/ntdb-0.9.sigs
new file mode 100644
index 0000000000..6dae18fb6c
--- /dev/null
+++ b/lib/ntdb/ABI/ntdb-0.9.sigs
@@ -0,0 +1,39 @@
+ntdb_add_flag: void (struct ntdb_context *, unsigned int)
+ntdb_append: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, NTDB_DATA)
+ntdb_chainlock: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA)
+ntdb_chainlock_read: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA)
+ntdb_chainunlock: void (struct ntdb_context *, NTDB_DATA)
+ntdb_chainunlock_read: void (struct ntdb_context *, NTDB_DATA)
+ntdb_check_: enum NTDB_ERROR (struct ntdb_context *, enum NTDB_ERROR (*)(NTDB_DATA, NTDB_DATA, void *), void *)
+ntdb_close: int (struct ntdb_context *)
+ntdb_delete: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA)
+ntdb_error: enum NTDB_ERROR (struct ntdb_context *)
+ntdb_errorstr: const char *(enum NTDB_ERROR)
+ntdb_exists: bool (struct ntdb_context *, NTDB_DATA)
+ntdb_fd: int (const struct ntdb_context *)
+ntdb_fetch: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, NTDB_DATA *)
+ntdb_firstkey: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA *)
+ntdb_foreach_: void (int (*)(struct ntdb_context *, void *), void *)
+ntdb_get_attribute: enum NTDB_ERROR (struct ntdb_context *, union ntdb_attribute *)
+ntdb_get_flags: unsigned int (struct ntdb_context *)
+ntdb_get_seqnum: int64_t (struct ntdb_context *)
+ntdb_lockall: enum NTDB_ERROR (struct ntdb_context *)
+ntdb_lockall_read: enum NTDB_ERROR (struct ntdb_context *)
+ntdb_name: const char *(const struct ntdb_context *)
+ntdb_nextkey: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA *)
+ntdb_open: struct ntdb_context *(const char *, int, int, mode_t, union ntdb_attribute *)
+ntdb_parse_record_: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, enum NTDB_ERROR (*)(NTDB_DATA, NTDB_DATA, void *), void *)
+ntdb_remove_flag: void (struct ntdb_context *, unsigned int)
+ntdb_repack: enum NTDB_ERROR (struct ntdb_context *)
+ntdb_set_attribute: enum NTDB_ERROR (struct ntdb_context *, const union ntdb_attribute *)
+ntdb_store: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, NTDB_DATA, int)
+ntdb_summary: enum NTDB_ERROR (struct ntdb_context *, enum ntdb_summary_flags, char **)
+ntdb_transaction_cancel: void (struct ntdb_context *)
+ntdb_transaction_commit: enum NTDB_ERROR (struct ntdb_context *)
+ntdb_transaction_prepare_commit: enum NTDB_ERROR (struct ntdb_context *)
+ntdb_transaction_start: enum NTDB_ERROR (struct ntdb_context *)
+ntdb_traverse_: int64_t (struct ntdb_context *, int (*)(struct ntdb_context *, NTDB_DATA, NTDB_DATA, void *), void *)
+ntdb_unlockall: void (struct ntdb_context *)
+ntdb_unlockall_read: void (struct ntdb_context *)
+ntdb_unset_attribute: void (struct ntdb_context *, enum ntdb_attribute_type)
+ntdb_wipe_all: enum NTDB_ERROR (struct ntdb_context *)
diff --git a/lib/tdb2/LICENSE b/lib/ntdb/LICENSE
index cca7fc278f..cca7fc278f 100644
--- a/lib/tdb2/LICENSE
+++ b/lib/ntdb/LICENSE
diff --git a/lib/tdb2/Makefile b/lib/ntdb/Makefile
index ddd439d503..ddd439d503 100644
--- a/lib/tdb2/Makefile
+++ b/lib/ntdb/Makefile
diff --git a/lib/ntdb/check.c b/lib/ntdb/check.c
new file mode 100644
index 0000000000..1c676c7d45
--- /dev/null
+++ b/lib/ntdb/check.c
@@ -0,0 +1,864 @@
+ /*
+ Trivial Database 2: free list/block handling
+ 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 "private.h"
+#include <ccan/likely/likely.h>
+#include <ccan/asearch/asearch.h>
+
+/* We keep an ordered array of offsets. */
+static bool append(ntdb_off_t **arr, size_t *num, ntdb_off_t off)
+{
+ ntdb_off_t *new = realloc(*arr, (*num + 1) * sizeof(ntdb_off_t));
+ if (!new)
+ return false;
+ new[(*num)++] = off;
+ *arr = new;
+ return true;
+}
+
+static enum NTDB_ERROR check_header(struct ntdb_context *ntdb, ntdb_off_t *recovery,
+ uint64_t *features, size_t *num_capabilities)
+{
+ uint64_t hash_test;
+ struct ntdb_header hdr;
+ enum NTDB_ERROR ecode;
+ ntdb_off_t off, next;
+
+ ecode = ntdb_read_convert(ntdb, 0, &hdr, sizeof(hdr));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ /* magic food should not be converted, so convert back. */
+ ntdb_convert(ntdb, hdr.magic_food, sizeof(hdr.magic_food));
+
+ hash_test = NTDB_HASH_MAGIC;
+ hash_test = ntdb_hash(ntdb, &hash_test, sizeof(hash_test));
+ if (hdr.hash_test != hash_test) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "check: hash test %llu should be %llu",
+ (long long)hdr.hash_test,
+ (long long)hash_test);
+ }
+
+ if (strcmp(hdr.magic_food, NTDB_MAGIC_FOOD) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "check: bad magic '%.*s'",
+ (unsigned)sizeof(hdr.magic_food),
+ hdr.magic_food);
+ }
+
+ /* Features which are used must be a subset of features offered. */
+ if (hdr.features_used & ~hdr.features_offered) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "check: features used (0x%llx) which"
+ " are not offered (0x%llx)",
+ (long long)hdr.features_used,
+ (long long)hdr.features_offered);
+ }
+
+ *features = hdr.features_offered;
+ *recovery = hdr.recovery;
+ if (*recovery) {
+ if (*recovery < sizeof(hdr)
+ || *recovery > ntdb->file->map_size) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check:"
+ " invalid recovery offset %zu",
+ (size_t)*recovery);
+ }
+ }
+
+ for (off = hdr.capabilities; off && ecode == NTDB_SUCCESS; off = next) {
+ const struct ntdb_capability *cap;
+ enum NTDB_ERROR e;
+
+ cap = ntdb_access_read(ntdb, off, sizeof(*cap), true);
+ if (NTDB_PTR_IS_ERR(cap)) {
+ return NTDB_PTR_ERR(cap);
+ }
+
+ /* All capabilities are unknown. */
+ e = unknown_capability(ntdb, "ntdb_check", cap->type);
+ next = cap->next;
+ ntdb_access_release(ntdb, cap);
+ if (e)
+ return e;
+ (*num_capabilities)++;
+ }
+
+ /* Don't check reserved: they *can* be used later. */
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR check_hash_tree(struct ntdb_context *ntdb,
+ ntdb_off_t off, unsigned int group_bits,
+ uint64_t hprefix,
+ unsigned hprefix_bits,
+ ntdb_off_t used[],
+ size_t num_used,
+ size_t *num_found,
+ enum NTDB_ERROR (*check)(NTDB_DATA,
+ NTDB_DATA, void *),
+ void *data);
+
+static enum NTDB_ERROR check_hash_chain(struct ntdb_context *ntdb,
+ ntdb_off_t off,
+ uint64_t hash,
+ ntdb_off_t used[],
+ size_t num_used,
+ size_t *num_found,
+ enum NTDB_ERROR (*check)(NTDB_DATA,
+ NTDB_DATA,
+ void *),
+ void *data)
+{
+ struct ntdb_used_record rec;
+ enum NTDB_ERROR ecode;
+
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (rec_magic(&rec) != NTDB_CHAIN_MAGIC) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Bad hash chain magic %llu",
+ (long long)rec_magic(&rec));
+ }
+
+ if (rec_data_length(&rec) != sizeof(struct ntdb_chain)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check:"
+ " Bad hash chain length %llu vs %zu",
+ (long long)rec_data_length(&rec),
+ sizeof(struct ntdb_chain));
+ }
+ if (rec_key_length(&rec) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Bad hash chain key length %llu",
+ (long long)rec_key_length(&rec));
+ }
+ if (rec_hash(&rec) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Bad hash chain hash value %llu",
+ (long long)rec_hash(&rec));
+ }
+
+ off += sizeof(rec);
+ ecode = check_hash_tree(ntdb, off, 0, hash, 64,
+ used, num_used, num_found, check, data);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ off = ntdb_read_off(ntdb, off + offsetof(struct ntdb_chain, next));
+ if (NTDB_OFF_IS_ERR(off)) {
+ return NTDB_OFF_TO_ERR(off);
+ }
+ if (off == 0)
+ return NTDB_SUCCESS;
+ (*num_found)++;
+ return check_hash_chain(ntdb, off, hash, used, num_used, num_found,
+ check, data);
+}
+
+static enum NTDB_ERROR check_hash_record(struct ntdb_context *ntdb,
+ ntdb_off_t off,
+ uint64_t hprefix,
+ unsigned hprefix_bits,
+ ntdb_off_t used[],
+ size_t num_used,
+ size_t *num_found,
+ enum NTDB_ERROR (*check)(NTDB_DATA,
+ NTDB_DATA,
+ void *),
+ void *data)
+{
+ struct ntdb_used_record rec;
+ enum NTDB_ERROR ecode;
+
+ if (hprefix_bits >= 64)
+ return check_hash_chain(ntdb, off, hprefix, used, num_used,
+ num_found, check, data);
+
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (rec_magic(&rec) != NTDB_HTABLE_MAGIC) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Bad hash table magic %llu",
+ (long long)rec_magic(&rec));
+ }
+ if (rec_data_length(&rec)
+ != sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check:"
+ " Bad hash table length %llu vs %llu",
+ (long long)rec_data_length(&rec),
+ (long long)sizeof(ntdb_off_t)
+ << NTDB_SUBLEVEL_HASH_BITS);
+ }
+ if (rec_key_length(&rec) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Bad hash table key length %llu",
+ (long long)rec_key_length(&rec));
+ }
+ if (rec_hash(&rec) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Bad hash table hash value %llu",
+ (long long)rec_hash(&rec));
+ }
+
+ off += sizeof(rec);
+ return check_hash_tree(ntdb, off,
+ NTDB_SUBLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS,
+ hprefix, hprefix_bits,
+ used, num_used, num_found, check, data);
+}
+
+static int off_cmp(const ntdb_off_t *a, const ntdb_off_t *b)
+{
+ /* Can overflow an int. */
+ return *a > *b ? 1
+ : *a < *b ? -1
+ : 0;
+}
+
+static uint64_t get_bits(uint64_t h, unsigned num, unsigned *used)
+{
+ *used += num;
+
+ return (h >> (64 - *used)) & ((1U << num) - 1);
+}
+
+static enum NTDB_ERROR check_hash_tree(struct ntdb_context *ntdb,
+ ntdb_off_t off, unsigned int group_bits,
+ uint64_t hprefix,
+ unsigned hprefix_bits,
+ ntdb_off_t used[],
+ size_t num_used,
+ size_t *num_found,
+ enum NTDB_ERROR (*check)(NTDB_DATA,
+ NTDB_DATA, void *),
+ void *data)
+{
+ unsigned int g, b;
+ const ntdb_off_t *hash;
+ struct ntdb_used_record rec;
+ enum NTDB_ERROR ecode;
+
+ hash = ntdb_access_read(ntdb, off,
+ sizeof(ntdb_off_t)
+ << (group_bits + NTDB_HASH_GROUP_BITS),
+ true);
+ if (NTDB_PTR_IS_ERR(hash)) {
+ return NTDB_PTR_ERR(hash);
+ }
+
+ for (g = 0; g < (1 << group_bits); g++) {
+ const ntdb_off_t *group = hash + (g << NTDB_HASH_GROUP_BITS);
+ for (b = 0; b < (1 << NTDB_HASH_GROUP_BITS); b++) {
+ unsigned int bucket, i, used_bits;
+ uint64_t h;
+ ntdb_off_t *p;
+ if (group[b] == 0)
+ continue;
+
+ off = group[b] & NTDB_OFF_MASK;
+ p = asearch(&off, used, num_used, off_cmp);
+ if (!p) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: Invalid offset"
+ " %llu in hash",
+ (long long)off);
+ goto fail;
+ }
+ /* Mark it invalid. */
+ *p ^= 1;
+ (*num_found)++;
+
+ if (hprefix_bits == 64) {
+ /* Chained entries are unordered. */
+ if (is_subhash(group[b])) {
+ ecode = NTDB_ERR_CORRUPT;
+ ntdb_logerr(ntdb, ecode,
+ NTDB_LOG_ERROR,
+ "ntdb_check: Invalid chain"
+ " entry subhash");
+ goto fail;
+ }
+ h = hash_record(ntdb, off);
+ if (h != hprefix) {
+ ecode = NTDB_ERR_CORRUPT;
+ ntdb_logerr(ntdb, ecode,
+ NTDB_LOG_ERROR,
+ "check: bad hash chain"
+ " placement"
+ " 0x%llx vs 0x%llx",
+ (long long)h,
+ (long long)hprefix);
+ goto fail;
+ }
+ ecode = ntdb_read_convert(ntdb, off, &rec,
+ sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ goto check;
+ }
+
+ if (is_subhash(group[b])) {
+ uint64_t subprefix;
+ subprefix = (hprefix
+ << (group_bits + NTDB_HASH_GROUP_BITS))
+ + g * (1 << NTDB_HASH_GROUP_BITS) + b;
+
+ ecode = check_hash_record(ntdb,
+ group[b] & NTDB_OFF_MASK,
+ subprefix,
+ hprefix_bits
+ + group_bits
+ + NTDB_HASH_GROUP_BITS,
+ used, num_used, num_found,
+ check, data);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ continue;
+ }
+ /* A normal entry */
+
+ /* Does it belong here at all? */
+ h = hash_record(ntdb, off);
+ used_bits = 0;
+ if (get_bits(h, hprefix_bits, &used_bits) != hprefix
+ && hprefix_bits) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "check: bad hash placement"
+ " 0x%llx vs 0x%llx",
+ (long long)h,
+ (long long)hprefix);
+ goto fail;
+ }
+
+ /* Does it belong in this group? */
+ if (get_bits(h, group_bits, &used_bits) != g) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "check: bad group %llu"
+ " vs %u",
+ (long long)h, g);
+ goto fail;
+ }
+
+ /* Are bucket bits correct? */
+ bucket = group[b] & NTDB_OFF_HASH_GROUP_MASK;
+ if (get_bits(h, NTDB_HASH_GROUP_BITS, &used_bits)
+ != bucket) {
+ used_bits -= NTDB_HASH_GROUP_BITS;
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "check: bad bucket %u vs %u",
+ (unsigned)get_bits(h,
+ NTDB_HASH_GROUP_BITS,
+ &used_bits),
+ bucket);
+ goto fail;
+ }
+
+ /* There must not be any zero entries between
+ * the bucket it belongs in and this one! */
+ for (i = bucket;
+ i != b;
+ i = (i + 1) % (1 << NTDB_HASH_GROUP_BITS)) {
+ if (group[i] == 0) {
+ ecode = NTDB_ERR_CORRUPT;
+ ntdb_logerr(ntdb, ecode,
+ NTDB_LOG_ERROR,
+ "check: bad group placement"
+ " %u vs %u",
+ b, bucket);
+ goto fail;
+ }
+ }
+
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+
+ /* Bottom bits must match header. */
+ if ((h & ((1 << 11)-1)) != rec_hash(&rec)) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: Bad hash magic"
+ " at offset %llu"
+ " (0x%llx vs 0x%llx)",
+ (long long)off,
+ (long long)h,
+ (long long)rec_hash(&rec));
+ goto fail;
+ }
+
+ check:
+ if (check) {
+ NTDB_DATA k, d;
+ const unsigned char *kptr;
+
+ kptr = ntdb_access_read(ntdb,
+ off + sizeof(rec),
+ rec_key_length(&rec)
+ + rec_data_length(&rec),
+ false);
+ if (NTDB_PTR_IS_ERR(kptr)) {
+ ecode = NTDB_PTR_ERR(kptr);
+ goto fail;
+ }
+
+ k = ntdb_mkdata(kptr, rec_key_length(&rec));
+ d = ntdb_mkdata(kptr + k.dsize,
+ rec_data_length(&rec));
+ ecode = check(k, d, data);
+ ntdb_access_release(ntdb, kptr);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ }
+ }
+ }
+ ntdb_access_release(ntdb, hash);
+ return NTDB_SUCCESS;
+
+fail:
+ ntdb_access_release(ntdb, hash);
+ return ecode;
+}
+
+static enum NTDB_ERROR check_hash(struct ntdb_context *ntdb,
+ ntdb_off_t used[],
+ size_t num_used, size_t num_other_used,
+ enum NTDB_ERROR (*check)(NTDB_DATA, NTDB_DATA, void *),
+ void *data)
+{
+ /* Free tables and capabilities also show up as used. */
+ size_t num_found = num_other_used;
+ enum NTDB_ERROR ecode;
+
+ ecode = check_hash_tree(ntdb, offsetof(struct ntdb_header, hashtable),
+ NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS,
+ 0, 0, used, num_used, &num_found,
+ check, data);
+ if (ecode == NTDB_SUCCESS) {
+ if (num_found != num_used) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Not all entries"
+ " are in hash");
+ }
+ }
+ return ecode;
+}
+
+static enum NTDB_ERROR check_free(struct ntdb_context *ntdb,
+ ntdb_off_t off,
+ const struct ntdb_free_record *frec,
+ ntdb_off_t prev, unsigned int ftable,
+ unsigned int bucket)
+{
+ enum NTDB_ERROR ecode;
+
+ if (frec_magic(frec) != NTDB_FREE_MAGIC) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: offset %llu bad magic 0x%llx",
+ (long long)off,
+ (long long)frec->magic_and_prev);
+ }
+ if (frec_ftable(frec) != ftable) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: offset %llu bad freetable %u",
+ (long long)off, frec_ftable(frec));
+
+ }
+
+ ecode = ntdb->io->oob(ntdb, off,
+ frec_len(frec)
+ + sizeof(struct ntdb_used_record),
+ false);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ if (size_to_bucket(frec_len(frec)) != bucket) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: offset %llu in wrong bucket"
+ " (%u vs %u)",
+ (long long)off,
+ bucket, size_to_bucket(frec_len(frec)));
+ }
+ if (prev && prev != frec_prev(frec)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: offset %llu bad prev"
+ " (%llu vs %llu)",
+ (long long)off,
+ (long long)prev, (long long)frec_len(frec));
+ }
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR check_free_table(struct ntdb_context *ntdb,
+ ntdb_off_t ftable_off,
+ unsigned ftable_num,
+ ntdb_off_t fr[],
+ size_t num_free,
+ size_t *num_found)
+{
+ struct ntdb_freetable ft;
+ ntdb_off_t h;
+ unsigned int i;
+ enum NTDB_ERROR ecode;
+
+ ecode = ntdb_read_convert(ntdb, ftable_off, &ft, sizeof(ft));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (rec_magic(&ft.hdr) != NTDB_FTABLE_MAGIC
+ || rec_key_length(&ft.hdr) != 0
+ || rec_data_length(&ft.hdr) != sizeof(ft) - sizeof(ft.hdr)
+ || rec_hash(&ft.hdr) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Invalid header on free table");
+ }
+
+ for (i = 0; i < NTDB_FREE_BUCKETS; i++) {
+ ntdb_off_t off, prev = 0, *p, first = 0;
+ struct ntdb_free_record f;
+
+ h = bucket_off(ftable_off, i);
+ for (off = ntdb_read_off(ntdb, h); off; off = f.next) {
+ if (NTDB_OFF_IS_ERR(off)) {
+ return NTDB_OFF_TO_ERR(off);
+ }
+ if (!first) {
+ off &= NTDB_OFF_MASK;
+ first = off;
+ }
+ ecode = ntdb_read_convert(ntdb, off, &f, sizeof(f));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ ecode = check_free(ntdb, off, &f, prev, ftable_num, i);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* FIXME: Check hash bits */
+ p = asearch(&off, fr, num_free, off_cmp);
+ if (!p) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: Invalid offset"
+ " %llu in free table",
+ (long long)off);
+ }
+ /* Mark it invalid. */
+ *p ^= 1;
+ (*num_found)++;
+ prev = off;
+ }
+
+ if (first) {
+ /* Now we can check first back pointer. */
+ ecode = ntdb_read_convert(ntdb, first, &f, sizeof(f));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ ecode = check_free(ntdb, first, &f, prev, ftable_num, i);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ }
+ }
+ return NTDB_SUCCESS;
+}
+
+/* Slow, but should be very rare. */
+ntdb_off_t dead_space(struct ntdb_context *ntdb, ntdb_off_t off)
+{
+ size_t len;
+ enum NTDB_ERROR ecode;
+
+ for (len = 0; off + len < ntdb->file->map_size; len++) {
+ char c;
+ ecode = ntdb->io->tread(ntdb, off, &c, 1);
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+ if (c != 0 && c != 0x43)
+ break;
+ }
+ return len;
+}
+
+static enum NTDB_ERROR check_linear(struct ntdb_context *ntdb,
+ ntdb_off_t **used, size_t *num_used,
+ ntdb_off_t **fr, size_t *num_free,
+ uint64_t features, ntdb_off_t recovery)
+{
+ ntdb_off_t off;
+ ntdb_len_t len;
+ enum NTDB_ERROR ecode;
+ bool found_recovery = false;
+
+ for (off = sizeof(struct ntdb_header);
+ off < ntdb->file->map_size;
+ off += len) {
+ union {
+ struct ntdb_used_record u;
+ struct ntdb_free_record f;
+ struct ntdb_recovery_record r;
+ } rec;
+ /* r is larger: only get that if we need to. */
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec.f));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* If we crash after ftruncate, we can get zeroes or fill. */
+ if (rec.r.magic == NTDB_RECOVERY_INVALID_MAGIC
+ || rec.r.magic == 0x4343434343434343ULL) {
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec.r));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ if (recovery == off) {
+ found_recovery = true;
+ len = sizeof(rec.r) + rec.r.max_len;
+ } else {
+ len = dead_space(ntdb, off);
+ if (NTDB_OFF_IS_ERR(len)) {
+ return NTDB_OFF_TO_ERR(len);
+ }
+ if (len < sizeof(rec.r)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: invalid"
+ " dead space at %zu",
+ (size_t)off);
+ }
+
+ ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
+ "Dead space at %zu-%zu (of %zu)",
+ (size_t)off, (size_t)(off + len),
+ (size_t)ntdb->file->map_size);
+ }
+ } else if (rec.r.magic == NTDB_RECOVERY_MAGIC) {
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec.r));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ if (recovery != off) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: unexpected"
+ " recovery record at offset"
+ " %zu",
+ (size_t)off);
+ }
+ if (rec.r.len > rec.r.max_len) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: invalid recovery"
+ " length %zu",
+ (size_t)rec.r.len);
+ }
+ if (rec.r.eof > ntdb->file->map_size) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: invalid old EOF"
+ " %zu", (size_t)rec.r.eof);
+ }
+ found_recovery = true;
+ len = sizeof(rec.r) + rec.r.max_len;
+ } else if (frec_magic(&rec.f) == NTDB_FREE_MAGIC) {
+ len = sizeof(rec.u) + frec_len(&rec.f);
+ if (off + len > ntdb->file->map_size) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: free overlength"
+ " %llu at offset %llu",
+ (long long)len,
+ (long long)off);
+ }
+ /* This record should be in free lists. */
+ if (frec_ftable(&rec.f) != NTDB_FTABLE_NONE
+ && !append(fr, num_free, off)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM,
+ NTDB_LOG_ERROR,
+ "ntdb_check: tracking %zu'th"
+ " free record.", *num_free);
+ }
+ } else if (rec_magic(&rec.u) == NTDB_USED_MAGIC
+ || rec_magic(&rec.u) == NTDB_CHAIN_MAGIC
+ || rec_magic(&rec.u) == NTDB_HTABLE_MAGIC
+ || rec_magic(&rec.u) == NTDB_FTABLE_MAGIC
+ || rec_magic(&rec.u) == NTDB_CAP_MAGIC) {
+ uint64_t klen, dlen, extra;
+
+ /* This record is used! */
+ if (!append(used, num_used, off)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM,
+ NTDB_LOG_ERROR,
+ "ntdb_check: tracking %zu'th"
+ " used record.", *num_used);
+ }
+
+ klen = rec_key_length(&rec.u);
+ dlen = rec_data_length(&rec.u);
+ extra = rec_extra_padding(&rec.u);
+
+ len = sizeof(rec.u) + klen + dlen + extra;
+ if (off + len > ntdb->file->map_size) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: used overlength"
+ " %llu at offset %llu",
+ (long long)len,
+ (long long)off);
+ }
+
+ if (len < sizeof(rec.f)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: too short record"
+ " %llu at %llu",
+ (long long)len,
+ (long long)off);
+ }
+
+ /* Check that records have correct 0 at end (but may
+ * not in future). */
+ if (extra && !features
+ && rec_magic(&rec.u) != NTDB_CAP_MAGIC) {
+ const char *p;
+ char c;
+ p = ntdb_access_read(ntdb, off + sizeof(rec.u)
+ + klen + dlen, 1, false);
+ if (NTDB_PTR_IS_ERR(p))
+ return NTDB_PTR_ERR(p);
+ c = *p;
+ ntdb_access_release(ntdb, p);
+
+ if (c != '\0') {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check:"
+ " non-zero extra"
+ " at %llu",
+ (long long)off);
+ }
+ }
+ } else {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "ntdb_check: Bad magic 0x%llx"
+ " at offset %zu",
+ (long long)rec_magic(&rec.u),
+ (size_t)off);
+ }
+ }
+
+ /* We must have found recovery area if there was one. */
+ if (recovery != 0 && !found_recovery) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: expected a recovery area at %zu",
+ (size_t)recovery);
+ }
+
+ return NTDB_SUCCESS;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_check_(struct ntdb_context *ntdb,
+ enum NTDB_ERROR (*check)(NTDB_DATA, NTDB_DATA, void *),
+ void *data)
+{
+ ntdb_off_t *fr = NULL, *used = NULL, ft, recovery;
+ size_t num_free = 0, num_used = 0, num_found = 0, num_ftables = 0,
+ num_capabilities = 0;
+ uint64_t features;
+ enum NTDB_ERROR ecode;
+
+ if (ntdb->flags & NTDB_CANT_CHECK) {
+ return ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
+ "ntdb_check: database has unknown capability,"
+ " cannot check.");
+ }
+
+ ecode = ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, false);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb->last_error = ecode;
+ }
+
+ ecode = ntdb_lock_expand(ntdb, F_RDLCK);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_allrecord_unlock(ntdb, F_RDLCK);
+ return ntdb->last_error = ecode;
+ }
+
+ ecode = check_header(ntdb, &recovery, &features, &num_capabilities);
+ if (ecode != NTDB_SUCCESS)
+ goto out;
+
+ /* First we do a linear scan, checking all records. */
+ ecode = check_linear(ntdb, &used, &num_used, &fr, &num_free, features,
+ recovery);
+ if (ecode != NTDB_SUCCESS)
+ goto out;
+
+ for (ft = first_ftable(ntdb); ft; ft = next_ftable(ntdb, ft)) {
+ if (NTDB_OFF_IS_ERR(ft)) {
+ ecode = NTDB_OFF_TO_ERR(ft);
+ goto out;
+ }
+ ecode = check_free_table(ntdb, ft, num_ftables, fr, num_free,
+ &num_found);
+ if (ecode != NTDB_SUCCESS)
+ goto out;
+ num_ftables++;
+ }
+
+ /* FIXME: Check key uniqueness? */
+ ecode = check_hash(ntdb, used, num_used, num_ftables + num_capabilities,
+ check, data);
+ if (ecode != NTDB_SUCCESS)
+ goto out;
+
+ if (num_found != num_free) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_check: Not all entries are in"
+ " free table");
+ }
+
+out:
+ ntdb_allrecord_unlock(ntdb, F_RDLCK);
+ ntdb_unlock_expand(ntdb, F_RDLCK);
+ free(fr);
+ free(used);
+ return ntdb->last_error = ecode;
+}
diff --git a/lib/tdb2/configure b/lib/ntdb/configure
index 6a9f875511..6a9f875511 100755
--- a/lib/tdb2/configure
+++ b/lib/ntdb/configure
diff --git a/lib/ntdb/doc/TDB_porting.txt b/lib/ntdb/doc/TDB_porting.txt
new file mode 100644
index 0000000000..8df137416d
--- /dev/null
+++ b/lib/ntdb/doc/TDB_porting.txt
@@ -0,0 +1,65 @@
+Interface differences between TDB and NTDB.
+
+- ntdb shares 'struct TDB_DATA' with tdb, but TDB defines the TDB_DATA
+ typedef, whereas ntdb defines NTDB_DATA (ie. both are compatible).
+ If you include both ntdb.h and tdb.h, #include tdb.h first,
+ otherwise you'll get a compile error when tdb.h re-defined struct
+ TDB_DATA.
+
+- ntdb functions return NTDB_SUCCESS (ie 0) on success, and a negative
+ error on failure, whereas tdb functions returned 0 on success, and
+ -1 on failure. tdb then used tdb_error() to determine the error;
+ this is also supported in ntdb to ease backwards compatibility,
+ though the other form is preferred.
+
+- ntdb's ntdb_fetch() returns an error, tdb's returned the data directly
+ (or tdb_null, and you were supposed to check tdb_error() to find out why).
+
+- ntdb's ntdb_nextkey() frees the old key's dptr, in tdb you needed to do
+ this manually.
+
+- tdb's tdb_open/tdb_open_ex took an explicit hash size. ntdb's hash table
+ resizes as required.
+
+- ntdb uses a linked list of attribute structures to implement logging and
+ alternate hashes. tdb used tdb_open_ex, which was not extensible.
+
+- ntdb does locking on read-only databases (ie. O_RDONLY passed to ntdb_open).
+ tdb did not: use the NTDB_NOLOCK flag if you want to suppress locking.
+
+- ntdb's log function is simpler than tdb's log function. The string is
+ already formatted, and it takes an enum ntdb_log_level not a tdb_debug_level,
+ and which has only three values: NTDB_LOG_ERROR, NTDB_LOG_USE_ERROR and
+ NTDB_LOG_WARNING.
+
+- ntdb provides ntdb_deq() for comparing two NTDB_DATA, and ntdb_mkdata() for
+ creating an NTDB_DATA.
+
+- ntdb's ntdb_name() returns a copy of the name even for NTDB_INTERNAL dbs.
+
+- ntdb does not need tdb_reopen() or tdb_reopen_all(). If you call
+ fork() after during certain operations the child should close the
+ tdb, or complete the operations before continuing to use the tdb:
+
+ ntdb_transaction_start(): child must ntdb_transaction_cancel()
+ ntdb_lockall(): child must call ntdb_unlockall()
+ ntdb_lockall_read(): child must call ntdb_unlockall_read()
+ ntdb_chainlock(): child must call ntdb_chainunlock()
+ ntdb_parse() callback: child must return from ntdb_parse()
+
+- ntdb will not open a non-tdb file, even if O_CREAT is specified.
+
+- There is no ntdb_traverse_read. For operating on TDB files, you can
+ simulate it by ntdb_add_flag(tdb, NTDB_RDONLY); ntdb_traverse();
+ ntdb_remove_flag(tdb, NTDB_RDONLY). This may be desirable because
+ traverse on TDB files use a write lock on the entire database
+ unless it's read-only.
+
+- Failure inside a transaction (such as a lock function failing) does
+ not implicitly cancel the transaction; you still need to call
+ ntdb_transaction_cancel().
+
+- There is no NTDB_CLEAR_IF_FIRST flag; it has severe scalability and
+ API problems. If necessary, you can emulate this by using the open
+ hook and placing a 1-byte lock at offset 4. If your program forks,
+ you will need to place this lock again in the child.
diff --git a/lib/tdb2/doc/design-1.3.txt b/lib/ntdb/doc/design-1.3.txt
index f81ecf7885..f81ecf7885 100644
--- a/lib/tdb2/doc/design-1.3.txt
+++ b/lib/ntdb/doc/design-1.3.txt
diff --git a/lib/tdb2/doc/design.lyx b/lib/ntdb/doc/design.lyx
index 0a1d6a14bc..0a1d6a14bc 100644
--- a/lib/tdb2/doc/design.lyx
+++ b/lib/ntdb/doc/design.lyx
diff --git a/lib/tdb2/doc/design.lyx,v b/lib/ntdb/doc/design.lyx,v
index 13e6387f7f..13e6387f7f 100644
--- a/lib/tdb2/doc/design.lyx,v
+++ b/lib/ntdb/doc/design.lyx,v
diff --git a/lib/tdb2/doc/design.pdf b/lib/ntdb/doc/design.pdf
index 558dc1f8c2..558dc1f8c2 100644
--- a/lib/tdb2/doc/design.pdf
+++ b/lib/ntdb/doc/design.pdf
Binary files differ
diff --git a/lib/tdb2/doc/design.txt b/lib/ntdb/doc/design.txt
index bd2ffde4db..bd2ffde4db 100644
--- a/lib/tdb2/doc/design.txt
+++ b/lib/ntdb/doc/design.txt
diff --git a/lib/ntdb/free.c b/lib/ntdb/free.c
new file mode 100644
index 0000000000..0fe6c73775
--- /dev/null
+++ b/lib/ntdb/free.c
@@ -0,0 +1,976 @@
+ /*
+ Trivial Database 2: free list/block handling
+ 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 "private.h"
+#include <ccan/likely/likely.h>
+#include <ccan/ilog/ilog.h>
+#include <time.h>
+#include <assert.h>
+#include <limits.h>
+
+static unsigned fls64(uint64_t val)
+{
+ return ilog64(val);
+}
+
+/* In which bucket would we find a particular record size? (ignoring header) */
+unsigned int size_to_bucket(ntdb_len_t data_len)
+{
+ unsigned int bucket;
+
+ /* We can't have records smaller than this. */
+ assert(data_len >= NTDB_MIN_DATA_LEN);
+
+ /* Ignoring the header... */
+ if (data_len - NTDB_MIN_DATA_LEN <= 64) {
+ /* 0 in bucket 0, 8 in bucket 1... 64 in bucket 8. */
+ bucket = (data_len - NTDB_MIN_DATA_LEN) / 8;
+ } else {
+ /* After that we go power of 2. */
+ bucket = fls64(data_len - NTDB_MIN_DATA_LEN) + 2;
+ }
+
+ if (unlikely(bucket >= NTDB_FREE_BUCKETS))
+ bucket = NTDB_FREE_BUCKETS - 1;
+ return bucket;
+}
+
+ntdb_off_t first_ftable(struct ntdb_context *ntdb)
+{
+ return ntdb_read_off(ntdb, offsetof(struct ntdb_header, free_table));
+}
+
+ntdb_off_t next_ftable(struct ntdb_context *ntdb, ntdb_off_t ftable)
+{
+ return ntdb_read_off(ntdb, ftable + offsetof(struct ntdb_freetable,next));
+}
+
+enum NTDB_ERROR ntdb_ftable_init(struct ntdb_context *ntdb)
+{
+ /* Use reservoir sampling algorithm to select a free list at random. */
+ unsigned int rnd, max = 0, count = 0;
+ ntdb_off_t off;
+
+ ntdb->ftable_off = off = first_ftable(ntdb);
+ ntdb->ftable = 0;
+
+ while (off) {
+ if (NTDB_OFF_IS_ERR(off)) {
+ return NTDB_OFF_TO_ERR(off);
+ }
+
+ rnd = random();
+ if (rnd >= max) {
+ ntdb->ftable_off = off;
+ ntdb->ftable = count;
+ max = rnd;
+ }
+
+ off = next_ftable(ntdb, off);
+ count++;
+ }
+ return NTDB_SUCCESS;
+}
+
+/* Offset of a given bucket. */
+ntdb_off_t bucket_off(ntdb_off_t ftable_off, unsigned bucket)
+{
+ return ftable_off + offsetof(struct ntdb_freetable, buckets)
+ + bucket * sizeof(ntdb_off_t);
+}
+
+/* Returns free_buckets + 1, or list number to search, or -ve error. */
+static ntdb_off_t find_free_head(struct ntdb_context *ntdb,
+ ntdb_off_t ftable_off,
+ ntdb_off_t bucket)
+{
+ /* Speculatively search for a non-zero bucket. */
+ return ntdb_find_nonzero_off(ntdb, bucket_off(ftable_off, 0),
+ bucket, NTDB_FREE_BUCKETS);
+}
+
+static void check_list(struct ntdb_context *ntdb, ntdb_off_t b_off)
+{
+#ifdef CCAN_NTDB_DEBUG
+ ntdb_off_t off, prev = 0, first;
+ struct ntdb_free_record r;
+
+ first = off = (ntdb_read_off(ntdb, b_off) & NTDB_OFF_MASK);
+ while (off != 0) {
+ ntdb_read_convert(ntdb, off, &r, sizeof(r));
+ if (frec_magic(&r) != NTDB_FREE_MAGIC)
+ abort();
+ if (prev && frec_prev(&r) != prev)
+ abort();
+ prev = off;
+ off = r.next;
+ }
+
+ if (first) {
+ ntdb_read_convert(ntdb, first, &r, sizeof(r));
+ if (frec_prev(&r) != prev)
+ abort();
+ }
+#endif
+}
+
+/* Remove from free bucket. */
+static enum NTDB_ERROR remove_from_list(struct ntdb_context *ntdb,
+ ntdb_off_t b_off, ntdb_off_t r_off,
+ const struct ntdb_free_record *r)
+{
+ ntdb_off_t off, prev_next, head;
+ enum NTDB_ERROR ecode;
+
+ /* Is this only element in list? Zero out bucket, and we're done. */
+ if (frec_prev(r) == r_off)
+ return ntdb_write_off(ntdb, b_off, 0);
+
+ /* off = &r->prev->next */
+ off = frec_prev(r) + offsetof(struct ntdb_free_record, next);
+
+ /* Get prev->next */
+ prev_next = ntdb_read_off(ntdb, off);
+ if (NTDB_OFF_IS_ERR(prev_next))
+ return NTDB_OFF_TO_ERR(prev_next);
+
+ /* If prev->next == 0, we were head: update bucket to point to next. */
+ if (prev_next == 0) {
+ /* We must preserve upper bits. */
+ head = ntdb_read_off(ntdb, b_off);
+ if (NTDB_OFF_IS_ERR(head))
+ return NTDB_OFF_TO_ERR(head);
+
+ if ((head & NTDB_OFF_MASK) != r_off) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "remove_from_list:"
+ " %llu head %llu on list %llu",
+ (long long)r_off,
+ (long long)head,
+ (long long)b_off);
+ }
+ head = ((head & ~NTDB_OFF_MASK) | r->next);
+ ecode = ntdb_write_off(ntdb, b_off, head);
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+ } else {
+ /* r->prev->next = r->next */
+ ecode = ntdb_write_off(ntdb, off, r->next);
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+ }
+
+ /* If we were the tail, off = &head->prev. */
+ if (r->next == 0) {
+ head = ntdb_read_off(ntdb, b_off);
+ if (NTDB_OFF_IS_ERR(head))
+ return NTDB_OFF_TO_ERR(head);
+ head &= NTDB_OFF_MASK;
+ off = head + offsetof(struct ntdb_free_record, magic_and_prev);
+ } else {
+ /* off = &r->next->prev */
+ off = r->next + offsetof(struct ntdb_free_record,
+ magic_and_prev);
+ }
+
+#ifdef CCAN_NTDB_DEBUG
+ /* *off == r */
+ if ((ntdb_read_off(ntdb, off) & NTDB_OFF_MASK) != r_off) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "remove_from_list:"
+ " %llu bad prev in list %llu",
+ (long long)r_off, (long long)b_off);
+ }
+#endif
+ /* r->next->prev = r->prev */
+ return ntdb_write_off(ntdb, off, r->magic_and_prev);
+}
+
+/* Enqueue in this free bucket: sets coalesce if we've added 128
+ * entries to it. */
+static enum NTDB_ERROR enqueue_in_free(struct ntdb_context *ntdb,
+ ntdb_off_t b_off,
+ ntdb_off_t off,
+ ntdb_len_t len,
+ bool *coalesce)
+{
+ struct ntdb_free_record new;
+ enum NTDB_ERROR ecode;
+ ntdb_off_t prev, head;
+ uint64_t magic = (NTDB_FREE_MAGIC << (64 - NTDB_OFF_UPPER_STEAL));
+
+ head = ntdb_read_off(ntdb, b_off);
+ if (NTDB_OFF_IS_ERR(head))
+ return NTDB_OFF_TO_ERR(head);
+
+ /* We only need to set ftable_and_len; rest is set in enqueue_in_free */
+ new.ftable_and_len = ((uint64_t)ntdb->ftable
+ << (64 - NTDB_OFF_UPPER_STEAL))
+ | len;
+
+ /* new->next = head. */
+ new.next = (head & NTDB_OFF_MASK);
+
+ /* First element? Prev points to ourselves. */
+ if (!new.next) {
+ new.magic_and_prev = (magic | off);
+ } else {
+ /* new->prev = next->prev */
+ prev = ntdb_read_off(ntdb,
+ new.next + offsetof(struct ntdb_free_record,
+ magic_and_prev));
+ new.magic_and_prev = prev;
+ if (frec_magic(&new) != NTDB_FREE_MAGIC) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "enqueue_in_free: %llu bad head"
+ " prev %llu",
+ (long long)new.next,
+ (long long)prev);
+ }
+ /* next->prev = new. */
+ ecode = ntdb_write_off(ntdb, new.next
+ + offsetof(struct ntdb_free_record,
+ magic_and_prev),
+ off | magic);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+#ifdef CCAN_NTDB_DEBUG
+ prev = ntdb_read_off(ntdb, frec_prev(&new)
+ + offsetof(struct ntdb_free_record, next));
+ if (prev != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "enqueue_in_free:"
+ " %llu bad tail next ptr %llu",
+ (long long)frec_prev(&new)
+ + offsetof(struct ntdb_free_record,
+ next),
+ (long long)prev);
+ }
+#endif
+ }
+
+ /* Update enqueue count, but don't set high bit: see NTDB_OFF_IS_ERR */
+ if (*coalesce)
+ head += (1ULL << (64 - NTDB_OFF_UPPER_STEAL));
+ head &= ~(NTDB_OFF_MASK | (1ULL << 63));
+ head |= off;
+
+ ecode = ntdb_write_off(ntdb, b_off, head);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* It's time to coalesce if counter wrapped. */
+ if (*coalesce)
+ *coalesce = ((head & ~NTDB_OFF_MASK) == 0);
+
+ return ntdb_write_convert(ntdb, off, &new, sizeof(new));
+}
+
+static ntdb_off_t ftable_offset(struct ntdb_context *ntdb, unsigned int ftable)
+{
+ ntdb_off_t off;
+ unsigned int i;
+
+ if (likely(ntdb->ftable == ftable))
+ return ntdb->ftable_off;
+
+ off = first_ftable(ntdb);
+ for (i = 0; i < ftable; i++) {
+ if (NTDB_OFF_IS_ERR(off)) {
+ break;
+ }
+ off = next_ftable(ntdb, off);
+ }
+ return off;
+}
+
+/* Note: we unlock the current bucket if fail (-ve), or coalesce (+ve) and
+ * need to blatt the *protect record (which is set to an error). */
+static ntdb_len_t coalesce(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_off_t b_off,
+ ntdb_len_t data_len,
+ ntdb_off_t *protect)
+{
+ ntdb_off_t end;
+ struct ntdb_free_record rec;
+ enum NTDB_ERROR ecode;
+
+ ntdb->stats.alloc_coalesce_tried++;
+ end = off + sizeof(struct ntdb_used_record) + data_len;
+
+ while (end < ntdb->file->map_size) {
+ const struct ntdb_free_record *r;
+ ntdb_off_t nb_off;
+ unsigned ftable, bucket;
+
+ r = ntdb_access_read(ntdb, end, sizeof(*r), true);
+ if (NTDB_PTR_IS_ERR(r)) {
+ ecode = NTDB_PTR_ERR(r);
+ goto err;
+ }
+
+ if (frec_magic(r) != NTDB_FREE_MAGIC
+ || frec_ftable(r) == NTDB_FTABLE_NONE) {
+ ntdb_access_release(ntdb, r);
+ break;
+ }
+
+ ftable = frec_ftable(r);
+ bucket = size_to_bucket(frec_len(r));
+ nb_off = ftable_offset(ntdb, ftable);
+ if (NTDB_OFF_IS_ERR(nb_off)) {
+ ntdb_access_release(ntdb, r);
+ ecode = NTDB_OFF_TO_ERR(nb_off);
+ goto err;
+ }
+ nb_off = bucket_off(nb_off, bucket);
+ ntdb_access_release(ntdb, r);
+
+ /* We may be violating lock order here, so best effort. */
+ if (ntdb_lock_free_bucket(ntdb, nb_off, NTDB_LOCK_NOWAIT)
+ != NTDB_SUCCESS) {
+ ntdb->stats.alloc_coalesce_lockfail++;
+ break;
+ }
+
+ /* Now we have lock, re-check. */
+ ecode = ntdb_read_convert(ntdb, end, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_unlock_free_bucket(ntdb, nb_off);
+ goto err;
+ }
+
+ if (unlikely(frec_magic(&rec) != NTDB_FREE_MAGIC)) {
+ ntdb->stats.alloc_coalesce_race++;
+ ntdb_unlock_free_bucket(ntdb, nb_off);
+ break;
+ }
+
+ if (unlikely(frec_ftable(&rec) != ftable)
+ || unlikely(size_to_bucket(frec_len(&rec)) != bucket)) {
+ ntdb->stats.alloc_coalesce_race++;
+ ntdb_unlock_free_bucket(ntdb, nb_off);
+ break;
+ }
+
+ /* Did we just mess up a record you were hoping to use? */
+ if (end == *protect) {
+ ntdb->stats.alloc_coalesce_iterate_clash++;
+ *protect = NTDB_ERR_TO_OFF(NTDB_ERR_NOEXIST);
+ }
+
+ ecode = remove_from_list(ntdb, nb_off, end, &rec);
+ check_list(ntdb, nb_off);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_unlock_free_bucket(ntdb, nb_off);
+ goto err;
+ }
+
+ end += sizeof(struct ntdb_used_record) + frec_len(&rec);
+ ntdb_unlock_free_bucket(ntdb, nb_off);
+ ntdb->stats.alloc_coalesce_num_merged++;
+ }
+
+ /* Didn't find any adjacent free? */
+ if (end == off + sizeof(struct ntdb_used_record) + data_len)
+ return 0;
+
+ /* Before we expand, check this isn't one you wanted protected? */
+ if (off == *protect) {
+ *protect = NTDB_ERR_TO_OFF(NTDB_ERR_EXISTS);
+ ntdb->stats.alloc_coalesce_iterate_clash++;
+ }
+
+ /* OK, expand initial record */
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ goto err;
+ }
+
+ if (frec_len(&rec) != data_len) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "coalesce: expected data len %zu not %zu",
+ (size_t)data_len, (size_t)frec_len(&rec));
+ goto err;
+ }
+
+ ecode = remove_from_list(ntdb, b_off, off, &rec);
+ check_list(ntdb, b_off);
+ if (ecode != NTDB_SUCCESS) {
+ goto err;
+ }
+
+ /* Try locking violation first. We don't allow coalesce recursion! */
+ ecode = add_free_record(ntdb, off, end - off, NTDB_LOCK_NOWAIT, false);
+ if (ecode != NTDB_SUCCESS) {
+ /* Need to drop lock. Can't rely on anything stable. */
+ ntdb->stats.alloc_coalesce_lockfail++;
+ *protect = NTDB_ERR_TO_OFF(NTDB_ERR_CORRUPT);
+
+ /* We have to drop this to avoid deadlocks, so make sure record
+ * doesn't get coalesced by someone else! */
+ rec.ftable_and_len = (NTDB_FTABLE_NONE
+ << (64 - NTDB_OFF_UPPER_STEAL))
+ | (end - off - sizeof(struct ntdb_used_record));
+ ecode = ntdb_write_off(ntdb,
+ off + offsetof(struct ntdb_free_record,
+ ftable_and_len),
+ rec.ftable_and_len);
+ if (ecode != NTDB_SUCCESS) {
+ goto err;
+ }
+
+ ntdb_unlock_free_bucket(ntdb, b_off);
+
+ ecode = add_free_record(ntdb, off, end - off, NTDB_LOCK_WAIT,
+ false);
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+ } else if (NTDB_OFF_IS_ERR(*protect)) {
+ /* For simplicity, we always drop lock if they can't continue */
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ }
+ ntdb->stats.alloc_coalesce_succeeded++;
+
+ /* Return usable length. */
+ return end - off - sizeof(struct ntdb_used_record);
+
+err:
+ /* To unify error paths, we *always* unlock bucket on error. */
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ return NTDB_ERR_TO_OFF(ecode);
+}
+
+/* List is locked: we unlock it. */
+static enum NTDB_ERROR coalesce_list(struct ntdb_context *ntdb,
+ ntdb_off_t ftable_off,
+ ntdb_off_t b_off,
+ unsigned int limit)
+{
+ enum NTDB_ERROR ecode;
+ ntdb_off_t off;
+
+ off = ntdb_read_off(ntdb, b_off);
+ if (NTDB_OFF_IS_ERR(off)) {
+ ecode = NTDB_OFF_TO_ERR(off);
+ goto unlock_err;
+ }
+ /* A little bit of paranoia: counter should be 0. */
+ off &= NTDB_OFF_MASK;
+
+ while (off && limit--) {
+ struct ntdb_free_record rec;
+ ntdb_len_t coal;
+ ntdb_off_t next;
+
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ next = rec.next;
+ coal = coalesce(ntdb, off, b_off, frec_len(&rec), &next);
+ if (NTDB_OFF_IS_ERR(coal)) {
+ /* This has already unlocked on error. */
+ return NTDB_OFF_TO_ERR(coal);
+ }
+ if (NTDB_OFF_IS_ERR(next)) {
+ /* Coalescing had to unlock, so stop. */
+ return NTDB_SUCCESS;
+ }
+ /* Keep going if we're doing well... */
+ limit += size_to_bucket(coal / 16 + NTDB_MIN_DATA_LEN);
+ off = next;
+ }
+
+ /* Now, move those elements to the tail of the list so we get something
+ * else next time. */
+ if (off) {
+ struct ntdb_free_record oldhrec, newhrec, oldtrec, newtrec;
+ ntdb_off_t oldhoff, oldtoff, newtoff;
+
+ /* The record we were up to is the new head. */
+ ecode = ntdb_read_convert(ntdb, off, &newhrec, sizeof(newhrec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ /* Get the new tail. */
+ newtoff = frec_prev(&newhrec);
+ ecode = ntdb_read_convert(ntdb, newtoff, &newtrec,
+ sizeof(newtrec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ /* Get the old head. */
+ oldhoff = ntdb_read_off(ntdb, b_off);
+ if (NTDB_OFF_IS_ERR(oldhoff)) {
+ ecode = NTDB_OFF_TO_ERR(oldhoff);
+ goto unlock_err;
+ }
+
+ /* This could happen if they all coalesced away. */
+ if (oldhoff == off)
+ goto out;
+
+ ecode = ntdb_read_convert(ntdb, oldhoff, &oldhrec,
+ sizeof(oldhrec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ /* Get the old tail. */
+ oldtoff = frec_prev(&oldhrec);
+ ecode = ntdb_read_convert(ntdb, oldtoff, &oldtrec,
+ sizeof(oldtrec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ /* Old tail's next points to old head. */
+ oldtrec.next = oldhoff;
+
+ /* Old head's prev points to old tail. */
+ oldhrec.magic_and_prev
+ = (NTDB_FREE_MAGIC << (64 - NTDB_OFF_UPPER_STEAL))
+ | oldtoff;
+
+ /* New tail's next is 0. */
+ newtrec.next = 0;
+
+ /* Write out the modified versions. */
+ ecode = ntdb_write_convert(ntdb, oldtoff, &oldtrec,
+ sizeof(oldtrec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ ecode = ntdb_write_convert(ntdb, oldhoff, &oldhrec,
+ sizeof(oldhrec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ ecode = ntdb_write_convert(ntdb, newtoff, &newtrec,
+ sizeof(newtrec));
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+
+ /* And finally link in new head. */
+ ecode = ntdb_write_off(ntdb, b_off, off);
+ if (ecode != NTDB_SUCCESS)
+ goto unlock_err;
+ }
+out:
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ return NTDB_SUCCESS;
+
+unlock_err:
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ return ecode;
+}
+
+/* List must not be locked if coalesce_ok is set. */
+enum NTDB_ERROR add_free_record(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len_with_header,
+ enum ntdb_lock_flags waitflag,
+ bool coalesce_ok)
+{
+ ntdb_off_t b_off;
+ ntdb_len_t len;
+ enum NTDB_ERROR ecode;
+
+ assert(len_with_header >= sizeof(struct ntdb_free_record));
+
+ len = len_with_header - sizeof(struct ntdb_used_record);
+
+ b_off = bucket_off(ntdb->ftable_off, size_to_bucket(len));
+ ecode = ntdb_lock_free_bucket(ntdb, b_off, waitflag);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ ecode = enqueue_in_free(ntdb, b_off, off, len, &coalesce_ok);
+ check_list(ntdb, b_off);
+
+ /* Coalescing unlocks free list. */
+ if (!ecode && coalesce_ok)
+ ecode = coalesce_list(ntdb, ntdb->ftable_off, b_off, 2);
+ else
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ return ecode;
+}
+
+static size_t adjust_size(size_t keylen, size_t datalen)
+{
+ size_t size = keylen + datalen;
+
+ if (size < NTDB_MIN_DATA_LEN)
+ size = NTDB_MIN_DATA_LEN;
+
+ /* Round to next uint64_t boundary. */
+ return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL);
+}
+
+/* If we have enough left over to be useful, split that off. */
+static size_t record_leftover(size_t keylen, size_t datalen,
+ bool want_extra, size_t total_len)
+{
+ ssize_t leftover;
+
+ if (want_extra)
+ datalen += datalen / 2;
+ leftover = total_len - adjust_size(keylen, datalen);
+
+ if (leftover < (ssize_t)sizeof(struct ntdb_free_record))
+ return 0;
+
+ return leftover;
+}
+
+/* We need size bytes to put our key and data in. */
+static ntdb_off_t lock_and_alloc(struct ntdb_context *ntdb,
+ ntdb_off_t ftable_off,
+ ntdb_off_t bucket,
+ size_t keylen, size_t datalen,
+ bool want_extra,
+ unsigned magic,
+ unsigned hashlow)
+{
+ ntdb_off_t off, b_off,best_off;
+ struct ntdb_free_record best = { 0 };
+ double multiplier;
+ size_t size = adjust_size(keylen, datalen);
+ enum NTDB_ERROR ecode;
+
+ ntdb->stats.allocs++;
+ b_off = bucket_off(ftable_off, bucket);
+
+ /* FIXME: Try non-blocking wait first, to measure contention. */
+ /* Lock this bucket. */
+ ecode = ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT);
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+
+ best.ftable_and_len = -1ULL;
+ best_off = 0;
+
+ /* Get slack if we're after extra. */
+ if (want_extra)
+ multiplier = 1.5;
+ else
+ multiplier = 1.0;
+
+ /* Walk the list to see if any are large enough, getting less fussy
+ * as we go. */
+ off = ntdb_read_off(ntdb, b_off);
+ if (NTDB_OFF_IS_ERR(off)) {
+ ecode = NTDB_OFF_TO_ERR(off);
+ goto unlock_err;
+ }
+ off &= NTDB_OFF_MASK;
+
+ while (off) {
+ const struct ntdb_free_record *r;
+ ntdb_len_t len;
+ ntdb_off_t next;
+
+ r = ntdb_access_read(ntdb, off, sizeof(*r), true);
+ if (NTDB_PTR_IS_ERR(r)) {
+ ecode = NTDB_PTR_ERR(r);
+ goto unlock_err;
+ }
+
+ if (frec_magic(r) != NTDB_FREE_MAGIC) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "lock_and_alloc:"
+ " %llu non-free 0x%llx",
+ (long long)off,
+ (long long)r->magic_and_prev);
+ ntdb_access_release(ntdb, r);
+ goto unlock_err;
+ }
+
+ if (frec_len(r) >= size && frec_len(r) < frec_len(&best)) {
+ best_off = off;
+ best = *r;
+ }
+
+ if (frec_len(&best) <= size * multiplier && best_off) {
+ ntdb_access_release(ntdb, r);
+ break;
+ }
+
+ multiplier *= 1.01;
+
+ next = r->next;
+ len = frec_len(r);
+ ntdb_access_release(ntdb, r);
+ off = next;
+ }
+
+ /* If we found anything at all, use it. */
+ if (best_off) {
+ struct ntdb_used_record rec;
+ size_t leftover;
+
+ /* We're happy with this size: take it. */
+ ecode = remove_from_list(ntdb, b_off, best_off, &best);
+ check_list(ntdb, b_off);
+ if (ecode != NTDB_SUCCESS) {
+ goto unlock_err;
+ }
+
+ leftover = record_leftover(keylen, datalen, want_extra,
+ frec_len(&best));
+
+ assert(keylen + datalen + leftover <= frec_len(&best));
+ /* We need to mark non-free before we drop lock, otherwise
+ * coalesce() could try to merge it! */
+ ecode = set_header(ntdb, &rec, magic, keylen, datalen,
+ frec_len(&best) - leftover, hashlow);
+ if (ecode != NTDB_SUCCESS) {
+ goto unlock_err;
+ }
+
+ ecode = ntdb_write_convert(ntdb, best_off, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ goto unlock_err;
+ }
+
+ /* For futureproofing, we put a 0 in any unused space. */
+ if (rec_extra_padding(&rec)) {
+ ecode = ntdb->io->twrite(ntdb, best_off + sizeof(rec)
+ + keylen + datalen, "", 1);
+ if (ecode != NTDB_SUCCESS) {
+ goto unlock_err;
+ }
+ }
+
+ /* Bucket of leftover will be <= current bucket, so nested
+ * locking is allowed. */
+ if (leftover) {
+ ntdb->stats.alloc_leftover++;
+ ecode = add_free_record(ntdb,
+ best_off + sizeof(rec)
+ + frec_len(&best) - leftover,
+ leftover, NTDB_LOCK_WAIT, false);
+ if (ecode != NTDB_SUCCESS) {
+ best_off = NTDB_ERR_TO_OFF(ecode);
+ }
+ }
+ ntdb_unlock_free_bucket(ntdb, b_off);
+
+ return best_off;
+ }
+
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ return 0;
+
+unlock_err:
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ return NTDB_ERR_TO_OFF(ecode);
+}
+
+/* Get a free block from current free list, or 0 if none, -ve on error. */
+static ntdb_off_t get_free(struct ntdb_context *ntdb,
+ size_t keylen, size_t datalen, bool want_extra,
+ unsigned magic, unsigned hashlow)
+{
+ ntdb_off_t off, ftable_off;
+ ntdb_off_t start_b, b, ftable;
+ bool wrapped = false;
+
+ /* If they are growing, add 50% to get to higher bucket. */
+ if (want_extra)
+ start_b = size_to_bucket(adjust_size(keylen,
+ datalen + datalen / 2));
+ else
+ start_b = size_to_bucket(adjust_size(keylen, datalen));
+
+ ftable_off = ntdb->ftable_off;
+ ftable = ntdb->ftable;
+ while (!wrapped || ftable_off != ntdb->ftable_off) {
+ /* Start at exact size bucket, and search up... */
+ for (b = find_free_head(ntdb, ftable_off, start_b);
+ b < NTDB_FREE_BUCKETS;
+ b = find_free_head(ntdb, ftable_off, b + 1)) {
+ /* Try getting one from list. */
+ off = lock_and_alloc(ntdb, ftable_off,
+ b, keylen, datalen, want_extra,
+ magic, hashlow);
+ if (NTDB_OFF_IS_ERR(off))
+ return off;
+ if (off != 0) {
+ if (b == start_b)
+ ntdb->stats.alloc_bucket_exact++;
+ if (b == NTDB_FREE_BUCKETS - 1)
+ ntdb->stats.alloc_bucket_max++;
+ /* Worked? Stay using this list. */
+ ntdb->ftable_off = ftable_off;
+ ntdb->ftable = ftable;
+ return off;
+ }
+ /* Didn't work. Try next bucket. */
+ }
+
+ if (NTDB_OFF_IS_ERR(b)) {
+ return b;
+ }
+
+ /* Hmm, try next table. */
+ ftable_off = next_ftable(ntdb, ftable_off);
+ if (NTDB_OFF_IS_ERR(ftable_off)) {
+ return ftable_off;
+ }
+ ftable++;
+
+ if (ftable_off == 0) {
+ wrapped = true;
+ ftable_off = first_ftable(ntdb);
+ if (NTDB_OFF_IS_ERR(ftable_off)) {
+ return ftable_off;
+ }
+ ftable = 0;
+ }
+ }
+
+ return 0;
+}
+
+enum NTDB_ERROR set_header(struct ntdb_context *ntdb,
+ struct ntdb_used_record *rec,
+ unsigned magic, uint64_t keylen, uint64_t datalen,
+ uint64_t actuallen, unsigned hashlow)
+{
+ uint64_t keybits = (fls64(keylen) + 1) / 2;
+
+ /* Use bottom bits of hash, so it's independent of hash table size. */
+ rec->magic_and_meta = (hashlow & ((1 << 11)-1))
+ | ((actuallen - (keylen + datalen)) << 11)
+ | (keybits << 43)
+ | ((uint64_t)magic << 48);
+ rec->key_and_data_len = (keylen | (datalen << (keybits*2)));
+
+ /* Encoding can fail on big values. */
+ if (rec_key_length(rec) != keylen
+ || rec_data_length(rec) != datalen
+ || rec_extra_padding(rec) != actuallen - (keylen + datalen)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "Could not encode k=%llu,d=%llu,a=%llu",
+ (long long)keylen, (long long)datalen,
+ (long long)actuallen);
+ }
+ return NTDB_SUCCESS;
+}
+
+/* You need 'size', this tells you how much you should expand by. */
+ntdb_off_t ntdb_expand_adjust(ntdb_off_t map_size, ntdb_off_t size)
+{
+ ntdb_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 */
+ if (new_size < top_size)
+ new_size = top_size;
+ return new_size - map_size;
+}
+
+/* Expand the database. */
+static enum NTDB_ERROR ntdb_expand(struct ntdb_context *ntdb, ntdb_len_t size)
+{
+ uint64_t old_size;
+ ntdb_len_t wanted;
+ enum NTDB_ERROR ecode;
+
+ /* Need to hold a hash lock to expand DB: transactions rely on it. */
+ if (!(ntdb->flags & NTDB_NOLOCK)
+ && !ntdb->file->allrecord_lock.count && !ntdb_has_hash_locks(ntdb)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_expand: must hold lock during expand");
+ }
+
+ /* Only one person can expand file at a time. */
+ ecode = ntdb_lock_expand(ntdb, F_WRLCK);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* Someone else may have expanded the file, so retry. */
+ old_size = ntdb->file->map_size;
+ ntdb->io->oob(ntdb, ntdb->file->map_size, 1, true);
+ if (ntdb->file->map_size != old_size) {
+ ntdb_unlock_expand(ntdb, F_WRLCK);
+ return NTDB_SUCCESS;
+ }
+
+ /* Overallocate. */
+ wanted = ntdb_expand_adjust(old_size, size);
+ /* We need room for the record header too. */
+ wanted = adjust_size(0, sizeof(struct ntdb_used_record) + wanted);
+
+ ecode = ntdb->io->expand_file(ntdb, wanted);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_unlock_expand(ntdb, F_WRLCK);
+ return ecode;
+ }
+
+ /* We need to drop this lock before adding free record. */
+ ntdb_unlock_expand(ntdb, F_WRLCK);
+
+ ntdb->stats.expands++;
+ return add_free_record(ntdb, old_size, wanted, NTDB_LOCK_WAIT, true);
+}
+
+/* This won't fail: it will expand the database if it has to. */
+ntdb_off_t alloc(struct ntdb_context *ntdb, size_t keylen, size_t datalen,
+ uint64_t hash, unsigned magic, bool growing)
+{
+ ntdb_off_t off;
+
+ /* We can't hold pointers during this: we could unmap! */
+ assert(!ntdb->direct_access);
+
+ for (;;) {
+ enum NTDB_ERROR ecode;
+ off = get_free(ntdb, keylen, datalen, growing, magic, hash);
+ if (likely(off != 0))
+ break;
+
+ ecode = ntdb_expand(ntdb, adjust_size(keylen, datalen));
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+ }
+
+ return off;
+}
diff --git a/lib/ntdb/hash.c b/lib/ntdb/hash.c
new file mode 100644
index 0000000000..95b98c0736
--- /dev/null
+++ b/lib/ntdb/hash.c
@@ -0,0 +1,894 @@
+ /*
+ Trivial Database 2: hash handling
+ 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 "private.h"
+#include <ccan/hash/hash.h>
+#include <assert.h>
+
+/* Default hash function. */
+uint64_t ntdb_jenkins_hash(const void *key, size_t length, uint64_t seed,
+ void *unused)
+{
+ uint64_t ret;
+ /* hash64_stable assumes lower bits are more important; they are a
+ * slightly better hash. We use the upper bits first, so swap them. */
+ ret = hash64_stable((const unsigned char *)key, length, seed);
+ return (ret >> 32) | (ret << 32);
+}
+
+uint64_t ntdb_hash(struct ntdb_context *ntdb, const void *ptr, size_t len)
+{
+ return ntdb->hash_fn(ptr, len, ntdb->hash_seed, ntdb->hash_data);
+}
+
+uint64_t hash_record(struct ntdb_context *ntdb, ntdb_off_t off)
+{
+ const struct ntdb_used_record *r;
+ const void *key;
+ uint64_t klen, hash;
+
+ r = ntdb_access_read(ntdb, off, sizeof(*r), true);
+ if (NTDB_PTR_IS_ERR(r)) {
+ /* FIXME */
+ return 0;
+ }
+
+ klen = rec_key_length(r);
+ ntdb_access_release(ntdb, r);
+
+ key = ntdb_access_read(ntdb, off + sizeof(*r), klen, false);
+ if (NTDB_PTR_IS_ERR(key)) {
+ return 0;
+ }
+
+ hash = ntdb_hash(ntdb, key, klen);
+ ntdb_access_release(ntdb, key);
+ return hash;
+}
+
+/* Get bits from a value. */
+static uint32_t bits_from(uint64_t val, unsigned start, unsigned num)
+{
+ assert(num <= 32);
+ return (val >> start) & ((1U << num) - 1);
+}
+
+/* We take bits from the top: that way we can lock whole sections of the hash
+ * by using lock ranges. */
+static uint32_t use_bits(struct hash_info *h, unsigned num)
+{
+ h->hash_used += num;
+ return bits_from(h->h, 64 - h->hash_used, num);
+}
+
+static ntdb_bool_err key_matches(struct ntdb_context *ntdb,
+ const struct ntdb_used_record *rec,
+ ntdb_off_t off,
+ const NTDB_DATA *key)
+{
+ ntdb_bool_err ret = false;
+ const char *rkey;
+
+ if (rec_key_length(rec) != key->dsize) {
+ ntdb->stats.compare_wrong_keylen++;
+ return ret;
+ }
+
+ rkey = ntdb_access_read(ntdb, off + sizeof(*rec), key->dsize, false);
+ if (NTDB_PTR_IS_ERR(rkey)) {
+ return (ntdb_bool_err)NTDB_PTR_ERR(rkey);
+ }
+ if (memcmp(rkey, key->dptr, key->dsize) == 0)
+ ret = true;
+ else
+ ntdb->stats.compare_wrong_keycmp++;
+ ntdb_access_release(ntdb, rkey);
+ return ret;
+}
+
+/* Does entry match? */
+static ntdb_bool_err match(struct ntdb_context *ntdb,
+ struct hash_info *h,
+ const NTDB_DATA *key,
+ ntdb_off_t val,
+ struct ntdb_used_record *rec)
+{
+ ntdb_off_t off;
+ enum NTDB_ERROR ecode;
+
+ ntdb->stats.compares++;
+ /* Desired bucket must match. */
+ if (h->home_bucket != (val & NTDB_OFF_HASH_GROUP_MASK)) {
+ ntdb->stats.compare_wrong_bucket++;
+ return false;
+ }
+
+ /* Top bits of offset == next bits of hash. */
+ if (bits_from(val, NTDB_OFF_HASH_EXTRA_BIT, NTDB_OFF_UPPER_STEAL_EXTRA)
+ != bits_from(h->h, 64 - h->hash_used - NTDB_OFF_UPPER_STEAL_EXTRA,
+ NTDB_OFF_UPPER_STEAL_EXTRA)) {
+ ntdb->stats.compare_wrong_offsetbits++;
+ return false;
+ }
+
+ off = val & NTDB_OFF_MASK;
+ ecode = ntdb_read_convert(ntdb, off, rec, sizeof(*rec));
+ if (ecode != NTDB_SUCCESS) {
+ return (ntdb_bool_err)ecode;
+ }
+
+ if ((h->h & ((1 << 11)-1)) != rec_hash(rec)) {
+ ntdb->stats.compare_wrong_rechash++;
+ return false;
+ }
+
+ return key_matches(ntdb, rec, off, key);
+}
+
+static ntdb_off_t hbucket_off(ntdb_off_t group_start, unsigned bucket)
+{
+ return group_start
+ + (bucket % (1 << NTDB_HASH_GROUP_BITS)) * sizeof(ntdb_off_t);
+}
+
+bool is_subhash(ntdb_off_t val)
+{
+ return (val >> NTDB_OFF_UPPER_STEAL_SUBHASH_BIT) & 1;
+}
+
+/* FIXME: Guess the depth, don't over-lock! */
+static ntdb_off_t hlock_range(ntdb_off_t group, ntdb_off_t *size)
+{
+ *size = 1ULL << (64 - (NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS));
+ return group << (64 - (NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS));
+}
+
+static ntdb_off_t COLD find_in_chain(struct ntdb_context *ntdb,
+ NTDB_DATA key,
+ ntdb_off_t chain,
+ struct hash_info *h,
+ struct ntdb_used_record *rec,
+ struct traverse_info *tinfo)
+{
+ ntdb_off_t off, next;
+ enum NTDB_ERROR ecode;
+
+ /* In case nothing is free, we set these to zero. */
+ h->home_bucket = h->found_bucket = 0;
+
+ for (off = chain; off; off = next) {
+ unsigned int i;
+
+ h->group_start = off;
+ ecode = ntdb_read_convert(ntdb, off, h->group, sizeof(h->group));
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+
+ for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
+ ntdb_off_t recoff;
+ if (!h->group[i]) {
+ /* Remember this empty bucket. */
+ h->home_bucket = h->found_bucket = i;
+ continue;
+ }
+
+ /* We can insert extra bits via add_to_hash
+ * empty bucket logic. */
+ recoff = h->group[i] & NTDB_OFF_MASK;
+ ecode = ntdb_read_convert(ntdb, recoff, rec,
+ sizeof(*rec));
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+
+ ecode = NTDB_OFF_TO_ERR(key_matches(ntdb, rec, recoff,
+ &key));
+ if (ecode < 0) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+ if (ecode == (enum NTDB_ERROR)1) {
+ h->home_bucket = h->found_bucket = i;
+
+ if (tinfo) {
+ tinfo->levels[tinfo->num_levels]
+ .hashtable = off;
+ tinfo->levels[tinfo->num_levels]
+ .total_buckets
+ = 1 << NTDB_HASH_GROUP_BITS;
+ tinfo->levels[tinfo->num_levels].entry
+ = i;
+ tinfo->num_levels++;
+ }
+ return recoff;
+ }
+ }
+ next = ntdb_read_off(ntdb, off
+ + offsetof(struct ntdb_chain, next));
+ if (NTDB_OFF_IS_ERR(next)) {
+ return next;
+ }
+ if (next)
+ next += sizeof(struct ntdb_used_record);
+ }
+ return 0;
+}
+
+/* This is the core routine which searches the hashtable for an entry.
+ * On error, no locks are held and -ve is returned.
+ * Otherwise, hinfo is filled in (and the optional tinfo).
+ * If not found, the return value is 0.
+ * If found, the return value is the offset, and *rec is the record. */
+ntdb_off_t find_and_lock(struct ntdb_context *ntdb,
+ NTDB_DATA key,
+ int ltype,
+ struct hash_info *h,
+ struct ntdb_used_record *rec,
+ struct traverse_info *tinfo)
+{
+ uint32_t i, group;
+ ntdb_off_t hashtable;
+ enum NTDB_ERROR ecode;
+
+ h->h = ntdb_hash(ntdb, key.dptr, key.dsize);
+ h->hash_used = 0;
+ group = use_bits(h, NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS);
+ h->home_bucket = use_bits(h, NTDB_HASH_GROUP_BITS);
+
+ h->hlock_start = hlock_range(group, &h->hlock_range);
+ ecode = ntdb_lock_hashes(ntdb, h->hlock_start, h->hlock_range, ltype,
+ NTDB_LOCK_WAIT);
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+
+ hashtable = offsetof(struct ntdb_header, hashtable);
+ if (tinfo) {
+ tinfo->toplevel_group = group;
+ tinfo->num_levels = 1;
+ tinfo->levels[0].entry = 0;
+ tinfo->levels[0].hashtable = hashtable
+ + (group << NTDB_HASH_GROUP_BITS) * sizeof(ntdb_off_t);
+ tinfo->levels[0].total_buckets = 1 << NTDB_HASH_GROUP_BITS;
+ }
+
+ while (h->hash_used <= 64) {
+ /* Read in the hash group. */
+ h->group_start = hashtable
+ + group * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
+
+ ecode = ntdb_read_convert(ntdb, h->group_start, &h->group,
+ sizeof(h->group));
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+
+ /* Pointer to another hash table? Go down... */
+ if (is_subhash(h->group[h->home_bucket])) {
+ hashtable = (h->group[h->home_bucket] & NTDB_OFF_MASK)
+ + sizeof(struct ntdb_used_record);
+ if (tinfo) {
+ /* When we come back, use *next* bucket */
+ tinfo->levels[tinfo->num_levels-1].entry
+ += h->home_bucket + 1;
+ }
+ group = use_bits(h, NTDB_SUBLEVEL_HASH_BITS
+ - NTDB_HASH_GROUP_BITS);
+ h->home_bucket = use_bits(h, NTDB_HASH_GROUP_BITS);
+ if (tinfo) {
+ tinfo->levels[tinfo->num_levels].hashtable
+ = hashtable;
+ tinfo->levels[tinfo->num_levels].total_buckets
+ = 1 << NTDB_SUBLEVEL_HASH_BITS;
+ tinfo->levels[tinfo->num_levels].entry
+ = group << NTDB_HASH_GROUP_BITS;
+ tinfo->num_levels++;
+ }
+ continue;
+ }
+
+ /* It's in this group: search (until 0 or all searched) */
+ for (i = 0, h->found_bucket = h->home_bucket;
+ i < (1 << NTDB_HASH_GROUP_BITS);
+ i++, h->found_bucket = ((h->found_bucket+1)
+ % (1 << NTDB_HASH_GROUP_BITS))) {
+ ntdb_bool_err berr;
+ if (is_subhash(h->group[h->found_bucket]))
+ continue;
+
+ if (!h->group[h->found_bucket])
+ break;
+
+ berr = match(ntdb, h, &key, h->group[h->found_bucket],
+ rec);
+ if (berr < 0) {
+ ecode = NTDB_OFF_TO_ERR(berr);
+ goto fail;
+ }
+ if (berr) {
+ if (tinfo) {
+ tinfo->levels[tinfo->num_levels-1].entry
+ += h->found_bucket;
+ }
+ return h->group[h->found_bucket] & NTDB_OFF_MASK;
+ }
+ }
+ /* Didn't find it: h indicates where it would go. */
+ return 0;
+ }
+
+ return find_in_chain(ntdb, key, hashtable, h, rec, tinfo);
+
+fail:
+ ntdb_unlock_hashes(ntdb, h->hlock_start, h->hlock_range, ltype);
+ return NTDB_ERR_TO_OFF(ecode);
+}
+
+/* I wrote a simple test, expanding a hash to 2GB, for the following
+ * cases:
+ * 1) Expanding all the buckets at once,
+ * 2) Expanding the bucket we wanted to place the new entry into.
+ * 3) Expanding the most-populated bucket,
+ *
+ * I measured the worst/average/best density during this process.
+ * 1) 3%/16%/30%
+ * 2) 4%/20%/38%
+ * 3) 6%/22%/41%
+ *
+ * So we figure out the busiest bucket for the moment.
+ */
+static unsigned fullest_bucket(struct ntdb_context *ntdb,
+ const ntdb_off_t *group,
+ unsigned new_bucket)
+{
+ unsigned counts[1 << NTDB_HASH_GROUP_BITS] = { 0 };
+ unsigned int i, best_bucket;
+
+ /* Count the new entry. */
+ counts[new_bucket]++;
+ best_bucket = new_bucket;
+
+ for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
+ unsigned this_bucket;
+
+ if (is_subhash(group[i]))
+ continue;
+ this_bucket = group[i] & NTDB_OFF_HASH_GROUP_MASK;
+ if (++counts[this_bucket] > counts[best_bucket])
+ best_bucket = this_bucket;
+ }
+
+ return best_bucket;
+}
+
+static bool put_into_group(ntdb_off_t *group,
+ unsigned bucket, ntdb_off_t encoded)
+{
+ unsigned int i;
+
+ for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
+ unsigned b = (bucket + i) % (1 << NTDB_HASH_GROUP_BITS);
+
+ if (group[b] == 0) {
+ group[b] = encoded;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void force_into_group(ntdb_off_t *group,
+ unsigned bucket, ntdb_off_t encoded)
+{
+ if (!put_into_group(group, bucket, encoded))
+ abort();
+}
+
+static ntdb_off_t encode_offset(ntdb_off_t new_off, struct hash_info *h)
+{
+ return h->home_bucket
+ | new_off
+ | ((uint64_t)bits_from(h->h,
+ 64 - h->hash_used - NTDB_OFF_UPPER_STEAL_EXTRA,
+ NTDB_OFF_UPPER_STEAL_EXTRA)
+ << NTDB_OFF_HASH_EXTRA_BIT);
+}
+
+/* Simply overwrite the hash entry we found before. */
+enum NTDB_ERROR replace_in_hash(struct ntdb_context *ntdb,
+ struct hash_info *h,
+ ntdb_off_t new_off)
+{
+ return ntdb_write_off(ntdb, hbucket_off(h->group_start, h->found_bucket),
+ encode_offset(new_off, h));
+}
+
+/* We slot in anywhere that's empty in the chain. */
+static enum NTDB_ERROR COLD add_to_chain(struct ntdb_context *ntdb,
+ ntdb_off_t subhash,
+ ntdb_off_t new_off)
+{
+ ntdb_off_t entry;
+ enum NTDB_ERROR ecode;
+
+ entry = ntdb_find_zero_off(ntdb, subhash, 1<<NTDB_HASH_GROUP_BITS);
+ if (NTDB_OFF_IS_ERR(entry)) {
+ return NTDB_OFF_TO_ERR(entry);
+ }
+
+ if (entry == 1 << NTDB_HASH_GROUP_BITS) {
+ ntdb_off_t next;
+
+ next = ntdb_read_off(ntdb, subhash
+ + offsetof(struct ntdb_chain, next));
+ if (NTDB_OFF_IS_ERR(next)) {
+ return NTDB_OFF_TO_ERR(next);
+ }
+
+ if (!next) {
+ next = alloc(ntdb, 0, sizeof(struct ntdb_chain), 0,
+ NTDB_CHAIN_MAGIC, false);
+ if (NTDB_OFF_IS_ERR(next))
+ return NTDB_OFF_TO_ERR(next);
+ ecode = zero_out(ntdb,
+ next+sizeof(struct ntdb_used_record),
+ sizeof(struct ntdb_chain));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ ecode = ntdb_write_off(ntdb, subhash
+ + offsetof(struct ntdb_chain,
+ next),
+ next);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ }
+ return add_to_chain(ntdb, next, new_off);
+ }
+
+ return ntdb_write_off(ntdb, subhash + entry * sizeof(ntdb_off_t),
+ new_off);
+}
+
+/* Add into a newly created subhash. */
+static enum NTDB_ERROR add_to_subhash(struct ntdb_context *ntdb, ntdb_off_t subhash,
+ unsigned hash_used, ntdb_off_t val)
+{
+ ntdb_off_t off = (val & NTDB_OFF_MASK), *group;
+ struct hash_info h;
+ unsigned int gnum;
+
+ h.hash_used = hash_used;
+
+ if (hash_used + NTDB_SUBLEVEL_HASH_BITS > 64)
+ return add_to_chain(ntdb, subhash, off);
+
+ h.h = hash_record(ntdb, off);
+ gnum = use_bits(&h, NTDB_SUBLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS);
+ h.group_start = subhash
+ + gnum * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
+ h.home_bucket = use_bits(&h, NTDB_HASH_GROUP_BITS);
+
+ group = ntdb_access_write(ntdb, h.group_start,
+ sizeof(*group) << NTDB_HASH_GROUP_BITS, true);
+ if (NTDB_PTR_IS_ERR(group)) {
+ return NTDB_PTR_ERR(group);
+ }
+ force_into_group(group, h.home_bucket, encode_offset(off, &h));
+ return ntdb_access_commit(ntdb, group);
+}
+
+static enum NTDB_ERROR expand_group(struct ntdb_context *ntdb, struct hash_info *h)
+{
+ unsigned bucket, num_vals, i, magic;
+ size_t subsize;
+ ntdb_off_t subhash;
+ ntdb_off_t vals[1 << NTDB_HASH_GROUP_BITS];
+ enum NTDB_ERROR ecode;
+
+ /* Attach new empty subhash under fullest bucket. */
+ bucket = fullest_bucket(ntdb, h->group, h->home_bucket);
+
+ if (h->hash_used == 64) {
+ ntdb->stats.alloc_chain++;
+ subsize = sizeof(struct ntdb_chain);
+ magic = NTDB_CHAIN_MAGIC;
+ } else {
+ ntdb->stats.alloc_subhash++;
+ subsize = (sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS);
+ magic = NTDB_HTABLE_MAGIC;
+ }
+
+ subhash = alloc(ntdb, 0, subsize, 0, magic, false);
+ if (NTDB_OFF_IS_ERR(subhash)) {
+ return NTDB_OFF_TO_ERR(subhash);
+ }
+
+ ecode = zero_out(ntdb, subhash + sizeof(struct ntdb_used_record),
+ subsize);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* Remove any which are destined for bucket or are in wrong place. */
+ num_vals = 0;
+ for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
+ unsigned home_bucket = h->group[i] & NTDB_OFF_HASH_GROUP_MASK;
+ if (!h->group[i] || is_subhash(h->group[i]))
+ continue;
+ if (home_bucket == bucket || home_bucket != i) {
+ vals[num_vals++] = h->group[i];
+ h->group[i] = 0;
+ }
+ }
+ /* FIXME: This assert is valid, but we do this during unit test :( */
+ /* assert(num_vals); */
+
+ /* Overwrite expanded bucket with subhash pointer. */
+ h->group[bucket] = subhash | (1ULL << NTDB_OFF_UPPER_STEAL_SUBHASH_BIT);
+
+ /* Point to actual contents of record. */
+ subhash += sizeof(struct ntdb_used_record);
+
+ /* Put values back. */
+ for (i = 0; i < num_vals; i++) {
+ unsigned this_bucket = vals[i] & NTDB_OFF_HASH_GROUP_MASK;
+
+ if (this_bucket == bucket) {
+ ecode = add_to_subhash(ntdb, subhash, h->hash_used,
+ vals[i]);
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+ } else {
+ /* There should be room to put this back. */
+ force_into_group(h->group, this_bucket, vals[i]);
+ }
+ }
+ return NTDB_SUCCESS;
+}
+
+enum NTDB_ERROR delete_from_hash(struct ntdb_context *ntdb, struct hash_info *h)
+{
+ unsigned int i, num_movers = 0;
+ ntdb_off_t movers[1 << NTDB_HASH_GROUP_BITS];
+
+ h->group[h->found_bucket] = 0;
+ for (i = 1; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
+ unsigned this_bucket;
+
+ this_bucket = (h->found_bucket+i) % (1 << NTDB_HASH_GROUP_BITS);
+ /* Empty bucket? We're done. */
+ if (!h->group[this_bucket])
+ break;
+
+ /* Ignore subhashes. */
+ if (is_subhash(h->group[this_bucket]))
+ continue;
+
+ /* If this one is not happy where it is, we'll move it. */
+ if ((h->group[this_bucket] & NTDB_OFF_HASH_GROUP_MASK)
+ != this_bucket) {
+ movers[num_movers++] = h->group[this_bucket];
+ h->group[this_bucket] = 0;
+ }
+ }
+
+ /* Put back the ones we erased. */
+ for (i = 0; i < num_movers; i++) {
+ force_into_group(h->group, movers[i] & NTDB_OFF_HASH_GROUP_MASK,
+ movers[i]);
+ }
+
+ /* Now we write back the hash group */
+ return ntdb_write_convert(ntdb, h->group_start,
+ h->group, sizeof(h->group));
+}
+
+enum NTDB_ERROR add_to_hash(struct ntdb_context *ntdb, struct hash_info *h,
+ ntdb_off_t new_off)
+{
+ enum NTDB_ERROR ecode;
+
+ /* We hit an empty bucket during search? That's where it goes. */
+ if (!h->group[h->found_bucket]) {
+ h->group[h->found_bucket] = encode_offset(new_off, h);
+ /* Write back the modified group. */
+ return ntdb_write_convert(ntdb, h->group_start,
+ h->group, sizeof(h->group));
+ }
+
+ if (h->hash_used > 64)
+ return add_to_chain(ntdb, h->group_start, new_off);
+
+ /* We're full. Expand. */
+ ecode = expand_group(ntdb, h);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (is_subhash(h->group[h->home_bucket])) {
+ /* We were expanded! */
+ ntdb_off_t hashtable;
+ unsigned int gnum;
+
+ /* Write back the modified group. */
+ ecode = ntdb_write_convert(ntdb, h->group_start, h->group,
+ sizeof(h->group));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* Move hashinfo down a level. */
+ hashtable = (h->group[h->home_bucket] & NTDB_OFF_MASK)
+ + sizeof(struct ntdb_used_record);
+ gnum = use_bits(h,NTDB_SUBLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS);
+ h->home_bucket = use_bits(h, NTDB_HASH_GROUP_BITS);
+ h->group_start = hashtable
+ + gnum * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
+ ecode = ntdb_read_convert(ntdb, h->group_start, &h->group,
+ sizeof(h->group));
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ }
+
+ /* Expanding the group must have made room if it didn't choose this
+ * bucket. */
+ if (put_into_group(h->group, h->home_bucket, encode_offset(new_off,h))){
+ return ntdb_write_convert(ntdb, h->group_start,
+ h->group, sizeof(h->group));
+ }
+
+ /* This can happen if all hashes in group (and us) dropped into same
+ * group in subhash. */
+ return add_to_hash(ntdb, h, new_off);
+}
+
+/* Traverse support: returns offset of record, or 0 or -ve error. */
+static ntdb_off_t iterate_hash(struct ntdb_context *ntdb,
+ struct traverse_info *tinfo)
+{
+ ntdb_off_t off, val, i;
+ struct traverse_level *tlevel;
+
+ tlevel = &tinfo->levels[tinfo->num_levels-1];
+
+again:
+ for (i = ntdb_find_nonzero_off(ntdb, tlevel->hashtable,
+ tlevel->entry, tlevel->total_buckets);
+ i != tlevel->total_buckets;
+ i = ntdb_find_nonzero_off(ntdb, tlevel->hashtable,
+ i+1, tlevel->total_buckets)) {
+ if (NTDB_OFF_IS_ERR(i)) {
+ return i;
+ }
+
+ val = ntdb_read_off(ntdb, tlevel->hashtable+sizeof(ntdb_off_t)*i);
+ if (NTDB_OFF_IS_ERR(val)) {
+ return val;
+ }
+
+ off = val & NTDB_OFF_MASK;
+
+ /* This makes the delete-all-in-traverse case work
+ * (and simplifies our logic a little). */
+ if (off == tinfo->prev)
+ continue;
+
+ tlevel->entry = i;
+
+ if (!is_subhash(val)) {
+ /* Found one. */
+ tinfo->prev = off;
+ return off;
+ }
+
+ /* When we come back, we want the next one */
+ tlevel->entry++;
+ tinfo->num_levels++;
+ tlevel++;
+ tlevel->hashtable = off + sizeof(struct ntdb_used_record);
+ tlevel->entry = 0;
+ /* Next level is a chain? */
+ if (unlikely(tinfo->num_levels == NTDB_MAX_LEVELS + 1))
+ tlevel->total_buckets = (1 << NTDB_HASH_GROUP_BITS);
+ else
+ tlevel->total_buckets = (1 << NTDB_SUBLEVEL_HASH_BITS);
+ goto again;
+ }
+
+ /* Nothing there? */
+ if (tinfo->num_levels == 1)
+ return 0;
+
+ /* Handle chained entries. */
+ if (unlikely(tinfo->num_levels == NTDB_MAX_LEVELS + 1)) {
+ tlevel->hashtable = ntdb_read_off(ntdb, tlevel->hashtable
+ + offsetof(struct ntdb_chain,
+ next));
+ if (NTDB_OFF_IS_ERR(tlevel->hashtable)) {
+ return tlevel->hashtable;
+ }
+ if (tlevel->hashtable) {
+ tlevel->hashtable += sizeof(struct ntdb_used_record);
+ tlevel->entry = 0;
+ goto again;
+ }
+ }
+
+ /* Go back up and keep searching. */
+ tinfo->num_levels--;
+ tlevel--;
+ goto again;
+}
+
+/* Return success if we find something, NTDB_ERR_NOEXIST if none. */
+enum NTDB_ERROR next_in_hash(struct ntdb_context *ntdb,
+ struct traverse_info *tinfo,
+ NTDB_DATA *kbuf, size_t *dlen)
+{
+ const unsigned group_bits = NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS;
+ ntdb_off_t hl_start, hl_range, off;
+ enum NTDB_ERROR ecode;
+
+ while (tinfo->toplevel_group < (1 << group_bits)) {
+ hl_start = (ntdb_off_t)tinfo->toplevel_group
+ << (64 - group_bits);
+ hl_range = 1ULL << group_bits;
+ ecode = ntdb_lock_hashes(ntdb, hl_start, hl_range, F_RDLCK,
+ NTDB_LOCK_WAIT);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ off = iterate_hash(ntdb, tinfo);
+ if (off) {
+ struct ntdb_used_record rec;
+
+ if (NTDB_OFF_IS_ERR(off)) {
+ ecode = NTDB_OFF_TO_ERR(off);
+ goto fail;
+ }
+
+ ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ if (rec_magic(&rec) != NTDB_USED_MAGIC) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
+ NTDB_LOG_ERROR,
+ "next_in_hash:"
+ " corrupt record at %llu",
+ (long long)off);
+ goto fail;
+ }
+
+ kbuf->dsize = rec_key_length(&rec);
+
+ /* They want data as well? */
+ if (dlen) {
+ *dlen = rec_data_length(&rec);
+ kbuf->dptr = ntdb_alloc_read(ntdb,
+ off + sizeof(rec),
+ kbuf->dsize
+ + *dlen);
+ } else {
+ kbuf->dptr = ntdb_alloc_read(ntdb,
+ off + sizeof(rec),
+ kbuf->dsize);
+ }
+ ntdb_unlock_hashes(ntdb, hl_start, hl_range, F_RDLCK);
+ if (NTDB_PTR_IS_ERR(kbuf->dptr)) {
+ return NTDB_PTR_ERR(kbuf->dptr);
+ }
+ return NTDB_SUCCESS;
+ }
+
+ ntdb_unlock_hashes(ntdb, hl_start, hl_range, F_RDLCK);
+
+ tinfo->toplevel_group++;
+ tinfo->levels[0].hashtable
+ += (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
+ tinfo->levels[0].entry = 0;
+ }
+ return NTDB_ERR_NOEXIST;
+
+fail:
+ ntdb_unlock_hashes(ntdb, hl_start, hl_range, F_RDLCK);
+ return ecode;
+
+}
+
+enum NTDB_ERROR first_in_hash(struct ntdb_context *ntdb,
+ struct traverse_info *tinfo,
+ NTDB_DATA *kbuf, size_t *dlen)
+{
+ tinfo->prev = 0;
+ tinfo->toplevel_group = 0;
+ tinfo->num_levels = 1;
+ tinfo->levels[0].hashtable = offsetof(struct ntdb_header, hashtable);
+ tinfo->levels[0].entry = 0;
+ tinfo->levels[0].total_buckets = (1 << NTDB_HASH_GROUP_BITS);
+
+ return next_in_hash(ntdb, tinfo, kbuf, dlen);
+}
+
+/* Even if the entry isn't in this hash bucket, you'd have to lock this
+ * bucket to find it. */
+static enum NTDB_ERROR chainlock(struct ntdb_context *ntdb, const NTDB_DATA *key,
+ int ltype, enum ntdb_lock_flags waitflag,
+ const char *func)
+{
+ enum NTDB_ERROR ecode;
+ uint64_t h = ntdb_hash(ntdb, key->dptr, key->dsize);
+ ntdb_off_t lockstart, locksize;
+ unsigned int group, gbits;
+
+ gbits = NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS;
+ group = bits_from(h, 64 - gbits, gbits);
+
+ lockstart = hlock_range(group, &locksize);
+
+ ecode = ntdb_lock_hashes(ntdb, lockstart, locksize, ltype, waitflag);
+ ntdb_trace_1rec(ntdb, func, *key);
+ return ecode;
+}
+
+/* lock/unlock one hash chain. This is meant to be used to reduce
+ contention - it cannot guarantee how many records will be locked */
+_PUBLIC_ enum NTDB_ERROR ntdb_chainlock(struct ntdb_context *ntdb, NTDB_DATA key)
+{
+ return ntdb->last_error = chainlock(ntdb, &key, F_WRLCK, NTDB_LOCK_WAIT,
+ "ntdb_chainlock");
+}
+
+_PUBLIC_ void ntdb_chainunlock(struct ntdb_context *ntdb, NTDB_DATA key)
+{
+ uint64_t h = ntdb_hash(ntdb, key.dptr, key.dsize);
+ ntdb_off_t lockstart, locksize;
+ unsigned int group, gbits;
+
+ gbits = NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS;
+ group = bits_from(h, 64 - gbits, gbits);
+
+ lockstart = hlock_range(group, &locksize);
+
+ ntdb_trace_1rec(ntdb, "ntdb_chainunlock", key);
+ ntdb_unlock_hashes(ntdb, lockstart, locksize, F_WRLCK);
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_chainlock_read(struct ntdb_context *ntdb, NTDB_DATA key)
+{
+ return ntdb->last_error = chainlock(ntdb, &key, F_RDLCK, NTDB_LOCK_WAIT,
+ "ntdb_chainlock_read");
+}
+
+_PUBLIC_ void ntdb_chainunlock_read(struct ntdb_context *ntdb, NTDB_DATA key)
+{
+ uint64_t h = ntdb_hash(ntdb, key.dptr, key.dsize);
+ ntdb_off_t lockstart, locksize;
+ unsigned int group, gbits;
+
+ gbits = NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS;
+ group = bits_from(h, 64 - gbits, gbits);
+
+ lockstart = hlock_range(group, &locksize);
+
+ ntdb_trace_1rec(ntdb, "ntdb_chainunlock_read", key);
+ ntdb_unlock_hashes(ntdb, lockstart, locksize, F_RDLCK);
+}
diff --git a/lib/ntdb/io.c b/lib/ntdb/io.c
new file mode 100644
index 0000000000..4580520fa2
--- /dev/null
+++ b/lib/ntdb/io.c
@@ -0,0 +1,650 @@
+ /*
+ 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
+ Copyright (C) Rusty Russell 2010
+
+ ** NOTE! The following LGPL license applies to the ntdb
+ ** 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 "private.h"
+#include <assert.h>
+#include <ccan/likely/likely.h>
+
+void ntdb_munmap(struct ntdb_file *file)
+{
+ if (file->fd == -1)
+ return;
+
+ if (file->map_ptr) {
+ munmap(file->map_ptr, file->map_size);
+ file->map_ptr = NULL;
+ }
+}
+
+enum NTDB_ERROR ntdb_mmap(struct ntdb_context *ntdb)
+{
+ int mmap_flags;
+
+ if (ntdb->flags & NTDB_INTERNAL)
+ return NTDB_SUCCESS;
+
+#ifndef HAVE_INCOHERENT_MMAP
+ if (ntdb->flags & NTDB_NOMMAP)
+ return NTDB_SUCCESS;
+#endif
+
+ if ((ntdb->open_flags & O_ACCMODE) == O_RDONLY)
+ mmap_flags = PROT_READ;
+ else
+ mmap_flags = PROT_READ | PROT_WRITE;
+
+ /* size_t can be smaller than off_t. */
+ if ((size_t)ntdb->file->map_size == ntdb->file->map_size) {
+ ntdb->file->map_ptr = mmap(NULL, ntdb->file->map_size,
+ mmap_flags,
+ MAP_SHARED, ntdb->file->fd, 0);
+ } else
+ ntdb->file->map_ptr = MAP_FAILED;
+
+ /*
+ * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
+ */
+ if (ntdb->file->map_ptr == MAP_FAILED) {
+ ntdb->file->map_ptr = NULL;
+#ifdef HAVE_INCOHERENT_MMAP
+ /* Incoherent mmap means everyone must mmap! */
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_mmap failed for size %lld (%s)",
+ (long long)ntdb->file->map_size,
+ strerror(errno));
+#else
+ ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
+ "ntdb_mmap failed for size %lld (%s)",
+ (long long)ntdb->file->map_size, strerror(errno));
+#endif
+ }
+ return NTDB_SUCCESS;
+}
+
+/* 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.
+
+ If probe is true, len being too large isn't a failure.
+*/
+static enum NTDB_ERROR ntdb_oob(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len, bool probe)
+{
+ struct stat st;
+ enum NTDB_ERROR ecode;
+
+ /* We can't hold pointers during this: we could unmap! */
+ assert(!ntdb->direct_access
+ || (ntdb->flags & NTDB_NOLOCK)
+ || ntdb_has_expansion_lock(ntdb));
+
+ if (len + off < len) {
+ if (probe)
+ return NTDB_SUCCESS;
+
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_oob off %llu len %llu wrap\n",
+ (long long)off, (long long)len);
+ }
+
+ if (len + off <= ntdb->file->map_size)
+ return NTDB_SUCCESS;
+ if (ntdb->flags & NTDB_INTERNAL) {
+ if (probe)
+ return NTDB_SUCCESS;
+
+ ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_oob len %lld beyond internal"
+ " malloc size %lld",
+ (long long)(off + len),
+ (long long)ntdb->file->map_size);
+ return NTDB_ERR_IO;
+ }
+
+ ecode = ntdb_lock_expand(ntdb, F_RDLCK);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (fstat(ntdb->file->fd, &st) != 0) {
+ ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "Failed to fstat file: %s", strerror(errno));
+ ntdb_unlock_expand(ntdb, F_RDLCK);
+ return NTDB_ERR_IO;
+ }
+
+ ntdb_unlock_expand(ntdb, F_RDLCK);
+
+ if (st.st_size < off + len) {
+ if (probe)
+ return NTDB_SUCCESS;
+
+ ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_oob len %llu beyond eof at %llu",
+ (long long)(off + len), (long long)st.st_size);
+ return NTDB_ERR_IO;
+ }
+
+ /* Unmap, update size, remap */
+ ntdb_munmap(ntdb->file);
+
+ ntdb->file->map_size = st.st_size;
+ return ntdb_mmap(ntdb);
+}
+
+/* Endian conversion: we only ever deal with 8 byte quantities */
+void *ntdb_convert(const struct ntdb_context *ntdb, void *buf, ntdb_len_t size)
+{
+ assert(size % 8 == 0);
+ if (unlikely((ntdb->flags & NTDB_CONVERT)) && buf) {
+ uint64_t i, *p = (uint64_t *)buf;
+ for (i = 0; i < size / 8; i++)
+ p[i] = bswap_64(p[i]);
+ }
+ return buf;
+}
+
+/* Return first non-zero offset in offset array, or end, or -ve error. */
+/* FIXME: Return the off? */
+uint64_t ntdb_find_nonzero_off(struct ntdb_context *ntdb,
+ ntdb_off_t base, uint64_t start, uint64_t end)
+{
+ uint64_t i;
+ const uint64_t *val;
+
+ /* Zero vs non-zero is the same unconverted: minor optimization. */
+ val = ntdb_access_read(ntdb, base + start * sizeof(ntdb_off_t),
+ (end - start) * sizeof(ntdb_off_t), false);
+ if (NTDB_PTR_IS_ERR(val)) {
+ return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
+ }
+
+ for (i = 0; i < (end - start); i++) {
+ if (val[i])
+ break;
+ }
+ ntdb_access_release(ntdb, val);
+ return start + i;
+}
+
+/* Return first zero offset in num offset array, or num, or -ve error. */
+uint64_t ntdb_find_zero_off(struct ntdb_context *ntdb, ntdb_off_t off,
+ uint64_t num)
+{
+ uint64_t i;
+ const uint64_t *val;
+
+ /* Zero vs non-zero is the same unconverted: minor optimization. */
+ val = ntdb_access_read(ntdb, off, num * sizeof(ntdb_off_t), false);
+ if (NTDB_PTR_IS_ERR(val)) {
+ return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
+ }
+
+ for (i = 0; i < num; i++) {
+ if (!val[i])
+ break;
+ }
+ ntdb_access_release(ntdb, val);
+ return i;
+}
+
+enum NTDB_ERROR zero_out(struct ntdb_context *ntdb, ntdb_off_t off, ntdb_len_t len)
+{
+ char buf[8192] = { 0 };
+ void *p = ntdb->io->direct(ntdb, off, len, true);
+ enum NTDB_ERROR ecode = NTDB_SUCCESS;
+
+ assert(!(ntdb->flags & NTDB_RDONLY));
+ if (NTDB_PTR_IS_ERR(p)) {
+ return NTDB_PTR_ERR(p);
+ }
+ if (p) {
+ memset(p, 0, len);
+ return ecode;
+ }
+ while (len) {
+ unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
+ ecode = ntdb->io->twrite(ntdb, off, buf, todo);
+ if (ecode != NTDB_SUCCESS) {
+ break;
+ }
+ len -= todo;
+ off += todo;
+ }
+ return ecode;
+}
+
+ntdb_off_t ntdb_read_off(struct ntdb_context *ntdb, ntdb_off_t off)
+{
+ ntdb_off_t ret;
+ enum NTDB_ERROR ecode;
+
+ if (likely(!(ntdb->flags & NTDB_CONVERT))) {
+ ntdb_off_t *p = ntdb->io->direct(ntdb, off, sizeof(*p), false);
+ if (NTDB_PTR_IS_ERR(p)) {
+ return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(p));
+ }
+ if (p)
+ return *p;
+ }
+
+ ecode = ntdb_read_convert(ntdb, off, &ret, sizeof(ret));
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+ return ret;
+}
+
+/* write a lump of data at a specified offset */
+static enum NTDB_ERROR ntdb_write(struct ntdb_context *ntdb, ntdb_off_t off,
+ const void *buf, ntdb_len_t len)
+{
+ enum NTDB_ERROR ecode;
+
+ if (ntdb->flags & NTDB_RDONLY) {
+ return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
+ "Write to read-only database");
+ }
+
+ ecode = ntdb->io->oob(ntdb, off, len, false);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (ntdb->file->map_ptr) {
+ memcpy(off + (char *)ntdb->file->map_ptr, buf, len);
+ } else {
+#ifdef HAVE_INCOHERENT_MMAP
+ return NTDB_ERR_IO;
+#else
+ ssize_t ret;
+ ret = pwrite(ntdb->file->fd, buf, len, off);
+ if (ret != len) {
+ /* This shouldn't happen: we avoid sparse files. */
+ if (ret >= 0)
+ errno = ENOSPC;
+
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_write: %zi at %zu len=%zu (%s)",
+ ret, (size_t)off, (size_t)len,
+ strerror(errno));
+ }
+#endif
+ }
+ return NTDB_SUCCESS;
+}
+
+/* read a lump of data at a specified offset */
+static enum NTDB_ERROR ntdb_read(struct ntdb_context *ntdb, ntdb_off_t off,
+ void *buf, ntdb_len_t len)
+{
+ enum NTDB_ERROR ecode;
+
+ ecode = ntdb->io->oob(ntdb, off, len, false);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (ntdb->file->map_ptr) {
+ memcpy(buf, off + (char *)ntdb->file->map_ptr, len);
+ } else {
+#ifdef HAVE_INCOHERENT_MMAP
+ return NTDB_ERR_IO;
+#else
+ ssize_t r = pread(ntdb->file->fd, buf, len, off);
+ if (r != len) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_read failed with %zi at %zu "
+ "len=%zu (%s) map_size=%zu",
+ r, (size_t)off, (size_t)len,
+ strerror(errno),
+ (size_t)ntdb->file->map_size);
+ }
+#endif
+ }
+ return NTDB_SUCCESS;
+}
+
+enum NTDB_ERROR ntdb_write_convert(struct ntdb_context *ntdb, ntdb_off_t off,
+ const void *rec, size_t len)
+{
+ enum NTDB_ERROR ecode;
+
+ if (unlikely((ntdb->flags & NTDB_CONVERT))) {
+ void *conv = malloc(len);
+ if (!conv) {
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_write: no memory converting"
+ " %zu bytes", len);
+ }
+ memcpy(conv, rec, len);
+ ecode = ntdb->io->twrite(ntdb, off,
+ ntdb_convert(ntdb, conv, len), len);
+ free(conv);
+ } else {
+ ecode = ntdb->io->twrite(ntdb, off, rec, len);
+ }
+ return ecode;
+}
+
+enum NTDB_ERROR ntdb_read_convert(struct ntdb_context *ntdb, ntdb_off_t off,
+ void *rec, size_t len)
+{
+ enum NTDB_ERROR ecode = ntdb->io->tread(ntdb, off, rec, len);
+ ntdb_convert(ntdb, rec, len);
+ return ecode;
+}
+
+enum NTDB_ERROR ntdb_write_off(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_off_t val)
+{
+ if (ntdb->flags & NTDB_RDONLY) {
+ return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
+ "Write to read-only database");
+ }
+
+ if (likely(!(ntdb->flags & NTDB_CONVERT))) {
+ ntdb_off_t *p = ntdb->io->direct(ntdb, off, sizeof(*p), true);
+ if (NTDB_PTR_IS_ERR(p)) {
+ return NTDB_PTR_ERR(p);
+ }
+ if (p) {
+ *p = val;
+ return NTDB_SUCCESS;
+ }
+ }
+ return ntdb_write_convert(ntdb, off, &val, sizeof(val));
+}
+
+static void *_ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset,
+ ntdb_len_t len, unsigned int prefix)
+{
+ unsigned char *buf;
+ enum NTDB_ERROR ecode;
+
+ /* some systems don't like zero length malloc */
+ buf = malloc(prefix + len ? prefix + len : 1);
+ if (!buf) {
+ ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_USE_ERROR,
+ "ntdb_alloc_read malloc failed len=%zu",
+ (size_t)(prefix + len));
+ return NTDB_ERR_PTR(NTDB_ERR_OOM);
+ } else {
+ ecode = ntdb->io->tread(ntdb, offset, buf+prefix, len);
+ if (unlikely(ecode != NTDB_SUCCESS)) {
+ free(buf);
+ return NTDB_ERR_PTR(ecode);
+ }
+ }
+ return buf;
+}
+
+/* read a lump of data, allocating the space for it */
+void *ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset, ntdb_len_t len)
+{
+ return _ntdb_alloc_read(ntdb, offset, len, 0);
+}
+
+static enum NTDB_ERROR fill(struct ntdb_context *ntdb,
+ const void *buf, size_t size,
+ ntdb_off_t off, ntdb_len_t len)
+{
+ while (len) {
+ size_t n = len > size ? size : len;
+ ssize_t ret = pwrite(ntdb->file->fd, buf, n, off);
+ if (ret != n) {
+ if (ret >= 0)
+ errno = ENOSPC;
+
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "fill failed:"
+ " %zi at %zu len=%zu (%s)",
+ ret, (size_t)off, (size_t)len,
+ strerror(errno));
+ }
+ len -= n;
+ off += n;
+ }
+ return NTDB_SUCCESS;
+}
+
+/* expand a file. we prefer to use ftruncate, as that is what posix
+ says to use for mmap expansion */
+static enum NTDB_ERROR ntdb_expand_file(struct ntdb_context *ntdb,
+ ntdb_len_t addition)
+{
+ char buf[8192];
+ enum NTDB_ERROR ecode;
+
+ if (ntdb->flags & NTDB_RDONLY) {
+ return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
+ "Expand on read-only database");
+ }
+
+ if (ntdb->flags & NTDB_INTERNAL) {
+ char *new = realloc(ntdb->file->map_ptr,
+ ntdb->file->map_size + addition);
+ if (!new) {
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "No memory to expand database");
+ }
+ ntdb->file->map_ptr = new;
+ ntdb->file->map_size += addition;
+ return NTDB_SUCCESS;
+ } else {
+ /* Unmap before trying to write; old NTDB claimed OpenBSD had
+ * problem with this otherwise. */
+ ntdb_munmap(ntdb->file);
+
+ /* If this fails, we try to fill anyway. */
+ if (ftruncate(ntdb->file->fd, ntdb->file->map_size + addition))
+ ;
+
+ /* 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, 0x43, sizeof(buf));
+ ecode = fill(ntdb, buf, sizeof(buf), ntdb->file->map_size,
+ addition);
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+ ntdb->file->map_size += addition;
+ return ntdb_mmap(ntdb);
+ }
+}
+
+const void *ntdb_access_read(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len, bool convert)
+{
+ void *ret = NULL;
+
+ if (likely(!(ntdb->flags & NTDB_CONVERT))) {
+ ret = ntdb->io->direct(ntdb, off, len, false);
+
+ if (NTDB_PTR_IS_ERR(ret)) {
+ return ret;
+ }
+ }
+ if (!ret) {
+ struct ntdb_access_hdr *hdr;
+ hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
+ if (NTDB_PTR_IS_ERR(hdr)) {
+ return hdr;
+ }
+ hdr->next = ntdb->access;
+ ntdb->access = hdr;
+ ret = hdr + 1;
+ if (convert) {
+ ntdb_convert(ntdb, (void *)ret, len);
+ }
+ } else
+ ntdb->direct_access++;
+
+ return ret;
+}
+
+void *ntdb_access_write(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len, bool convert)
+{
+ void *ret = NULL;
+
+ if (ntdb->flags & NTDB_RDONLY) {
+ ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
+ "Write to read-only database");
+ return NTDB_ERR_PTR(NTDB_ERR_RDONLY);
+ }
+
+ if (likely(!(ntdb->flags & NTDB_CONVERT))) {
+ ret = ntdb->io->direct(ntdb, off, len, true);
+
+ if (NTDB_PTR_IS_ERR(ret)) {
+ return ret;
+ }
+ }
+
+ if (!ret) {
+ struct ntdb_access_hdr *hdr;
+ hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
+ if (NTDB_PTR_IS_ERR(hdr)) {
+ return hdr;
+ }
+ hdr->next = ntdb->access;
+ ntdb->access = hdr;
+ hdr->off = off;
+ hdr->len = len;
+ hdr->convert = convert;
+ ret = hdr + 1;
+ if (convert)
+ ntdb_convert(ntdb, (void *)ret, len);
+ } else
+ ntdb->direct_access++;
+
+ return ret;
+}
+
+static struct ntdb_access_hdr **find_hdr(struct ntdb_context *ntdb, const void *p)
+{
+ struct ntdb_access_hdr **hp;
+
+ for (hp = &ntdb->access; *hp; hp = &(*hp)->next) {
+ if (*hp + 1 == p)
+ return hp;
+ }
+ return NULL;
+}
+
+void ntdb_access_release(struct ntdb_context *ntdb, const void *p)
+{
+ struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
+
+ if (hp) {
+ hdr = *hp;
+ *hp = hdr->next;
+ free(hdr);
+ } else
+ ntdb->direct_access--;
+}
+
+enum NTDB_ERROR ntdb_access_commit(struct ntdb_context *ntdb, void *p)
+{
+ struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
+ enum NTDB_ERROR ecode;
+
+ if (hp) {
+ hdr = *hp;
+ if (hdr->convert)
+ ecode = ntdb_write_convert(ntdb, hdr->off, p, hdr->len);
+ else
+ ecode = ntdb_write(ntdb, hdr->off, p, hdr->len);
+ *hp = hdr->next;
+ free(hdr);
+ } else {
+ ntdb->direct_access--;
+ ecode = NTDB_SUCCESS;
+ }
+
+ return ecode;
+}
+
+static void *ntdb_direct(struct ntdb_context *ntdb, ntdb_off_t off, size_t len,
+ bool write_mode)
+{
+ enum NTDB_ERROR ecode;
+
+ if (unlikely(!ntdb->file->map_ptr))
+ return NULL;
+
+ ecode = ntdb_oob(ntdb, off, len, false);
+ if (unlikely(ecode != NTDB_SUCCESS))
+ return NTDB_ERR_PTR(ecode);
+ return (char *)ntdb->file->map_ptr + off;
+}
+
+void ntdb_inc_seqnum(struct ntdb_context *ntdb)
+{
+ ntdb_off_t seq;
+
+ if (likely(!(ntdb->flags & NTDB_CONVERT))) {
+ int64_t *direct;
+
+ direct = ntdb->io->direct(ntdb,
+ offsetof(struct ntdb_header, seqnum),
+ sizeof(*direct), true);
+ if (likely(direct)) {
+ /* Don't let it go negative, even briefly */
+ if (unlikely((*direct) + 1) < 0)
+ *direct = 0;
+ (*direct)++;
+ return;
+ }
+ }
+
+ seq = ntdb_read_off(ntdb, offsetof(struct ntdb_header, seqnum));
+ if (!NTDB_OFF_IS_ERR(seq)) {
+ seq++;
+ if (unlikely((int64_t)seq < 0))
+ seq = 0;
+ ntdb_write_off(ntdb, offsetof(struct ntdb_header, seqnum), seq);
+ }
+}
+
+static const struct ntdb_methods io_methods = {
+ ntdb_read,
+ ntdb_write,
+ ntdb_oob,
+ ntdb_expand_file,
+ ntdb_direct,
+};
+
+/*
+ initialise the default methods table
+*/
+void ntdb_io_init(struct ntdb_context *ntdb)
+{
+ ntdb->io = &io_methods;
+}
diff --git a/lib/ntdb/lock.c b/lib/ntdb/lock.c
new file mode 100644
index 0000000000..167770d097
--- /dev/null
+++ b/lib/ntdb/lock.c
@@ -0,0 +1,883 @@
+ /*
+ 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 ntdb
+ ** 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 "private.h"
+#include <assert.h>
+#include <ccan/build_assert/build_assert.h>
+
+/* If we were threaded, we could wait for unlock, but we're not, so fail. */
+enum NTDB_ERROR owner_conflict(struct ntdb_context *ntdb, const char *call)
+{
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "%s: lock owned by another ntdb in this process.",
+ call);
+}
+
+/* If we fork, we no longer really own locks. */
+bool check_lock_pid(struct ntdb_context *ntdb, const char *call, bool log)
+{
+ /* No locks? No problem! */
+ if (ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0) {
+ return true;
+ }
+
+ /* No fork? No problem! */
+ if (ntdb->file->locker == getpid()) {
+ return true;
+ }
+
+ if (log) {
+ ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "%s: fork() detected after lock acquisition!"
+ " (%u vs %u)", call, ntdb->file->locker, getpid());
+ }
+ return false;
+}
+
+int ntdb_fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag,
+ void *unused)
+{
+ struct flock fl;
+ int ret;
+
+ do {
+ fl.l_type = rw;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = off;
+ fl.l_len = len;
+
+ if (waitflag)
+ ret = fcntl(fd, F_SETLKW, &fl);
+ else
+ ret = fcntl(fd, F_SETLK, &fl);
+ } while (ret != 0 && errno == EINTR);
+ return ret;
+}
+
+int ntdb_fcntl_unlock(int fd, int rw, off_t off, off_t len, void *unused)
+{
+ struct flock fl;
+ int ret;
+
+ do {
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = off;
+ fl.l_len = len;
+
+ ret = fcntl(fd, F_SETLKW, &fl);
+ } while (ret != 0 && errno == EINTR);
+ return ret;
+}
+
+static int lock(struct ntdb_context *ntdb,
+ int rw, off_t off, off_t len, bool waitflag)
+{
+ int ret;
+ if (ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0) {
+ ntdb->file->locker = getpid();
+ }
+
+ ntdb->stats.lock_lowlevel++;
+ ret = ntdb->lock_fn(ntdb->file->fd, rw, off, len, waitflag,
+ ntdb->lock_data);
+ if (!waitflag) {
+ ntdb->stats.lock_nonblock++;
+ if (ret != 0)
+ ntdb->stats.lock_nonblock_fail++;
+ }
+ return ret;
+}
+
+static int unlock(struct ntdb_context *ntdb, int rw, off_t off, off_t len)
+{
+#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!",
+ (int)off, (int)len);
+ abort();
+ }
+
+ fclose(locks);
+#endif
+
+ return ntdb->unlock_fn(ntdb->file->fd, rw, off, len, ntdb->lock_data);
+}
+
+/* a byte range locking function - return 0 on success
+ this functions locks len bytes at the specified offset.
+
+ note that a len of zero means lock to end of file
+*/
+static enum NTDB_ERROR ntdb_brlock(struct ntdb_context *ntdb,
+ int rw_type, ntdb_off_t offset, ntdb_off_t len,
+ enum ntdb_lock_flags flags)
+{
+ int ret;
+
+ if (ntdb->flags & NTDB_NOLOCK) {
+ return NTDB_SUCCESS;
+ }
+
+ if (rw_type == F_WRLCK && (ntdb->flags & NTDB_RDONLY)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
+ "Write lock attempted on read-only database");
+ }
+
+ /* A 32 bit system cannot open a 64-bit file, but it could have
+ * expanded since then: check here. */
+ if ((size_t)(offset + len) != offset + len) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_brlock: lock on giant offset %llu",
+ (long long)(offset + len));
+ }
+
+ ret = lock(ntdb, rw_type, offset, len, flags & NTDB_LOCK_WAIT);
+ if (ret != 0) {
+ /* Generic lock error. errno set by fcntl.
+ * EAGAIN is an expected return from non-blocking
+ * locks. */
+ if (!(flags & NTDB_LOCK_PROBE)
+ && (errno != EAGAIN && errno != EINTR)) {
+ ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_brlock failed (fd=%d) at"
+ " offset %zu rw_type=%d flags=%d len=%zu:"
+ " %s",
+ ntdb->file->fd, (size_t)offset, rw_type,
+ flags, (size_t)len, strerror(errno));
+ }
+ return NTDB_ERR_LOCK;
+ }
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR ntdb_brunlock(struct ntdb_context *ntdb,
+ int rw_type, ntdb_off_t offset, size_t len)
+{
+ if (ntdb->flags & NTDB_NOLOCK) {
+ return NTDB_SUCCESS;
+ }
+
+ if (!check_lock_pid(ntdb, "ntdb_brunlock", true))
+ return NTDB_ERR_LOCK;
+
+ if (unlock(ntdb, rw_type, offset, len) == -1) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_brunlock failed (fd=%d) at offset %zu"
+ " rw_type=%d len=%zu: %s",
+ ntdb->file->fd, (size_t)offset, rw_type,
+ (size_t)len, strerror(errno));
+ }
+ return NTDB_SUCCESS;
+}
+
+/*
+ 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.
+*/
+enum NTDB_ERROR ntdb_allrecord_upgrade(struct ntdb_context *ntdb, off_t start)
+{
+ int count = 1000;
+
+ if (!check_lock_pid(ntdb, "ntdb_transaction_prepare_commit", true))
+ return NTDB_ERR_LOCK;
+
+ if (ntdb->file->allrecord_lock.count != 1) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_allrecord_upgrade failed:"
+ " count %u too high",
+ ntdb->file->allrecord_lock.count);
+ }
+
+ if (ntdb->file->allrecord_lock.off != 1) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_allrecord_upgrade failed:"
+ " already upgraded?");
+ }
+
+ if (ntdb->file->allrecord_lock.owner != ntdb) {
+ return owner_conflict(ntdb, "ntdb_allrecord_upgrade");
+ }
+
+ while (count--) {
+ struct timeval tv;
+ if (ntdb_brlock(ntdb, F_WRLCK, start, 0,
+ NTDB_LOCK_WAIT|NTDB_LOCK_PROBE) == NTDB_SUCCESS) {
+ ntdb->file->allrecord_lock.ltype = F_WRLCK;
+ ntdb->file->allrecord_lock.off = 0;
+ return NTDB_SUCCESS;
+ }
+ 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);
+ }
+
+ if (errno != EAGAIN && errno != EINTR)
+ ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_allrecord_upgrade failed");
+ return NTDB_ERR_LOCK;
+}
+
+static struct ntdb_lock *find_nestlock(struct ntdb_context *ntdb, ntdb_off_t offset,
+ const struct ntdb_context *owner)
+{
+ unsigned int i;
+
+ for (i=0; i<ntdb->file->num_lockrecs; i++) {
+ if (ntdb->file->lockrecs[i].off == offset) {
+ if (owner && ntdb->file->lockrecs[i].owner != owner)
+ return NULL;
+ return &ntdb->file->lockrecs[i];
+ }
+ }
+ return NULL;
+}
+
+enum NTDB_ERROR ntdb_lock_and_recover(struct ntdb_context *ntdb)
+{
+ enum NTDB_ERROR ecode;
+
+ if (!check_lock_pid(ntdb, "ntdb_transaction_prepare_commit", true))
+ return NTDB_ERR_LOCK;
+
+ ecode = ntdb_allrecord_lock(ntdb, F_WRLCK, NTDB_LOCK_WAIT|NTDB_LOCK_NOCHECK,
+ false);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ ecode = ntdb_lock_open(ntdb, F_WRLCK, NTDB_LOCK_WAIT|NTDB_LOCK_NOCHECK);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_allrecord_unlock(ntdb, F_WRLCK);
+ return ecode;
+ }
+ ecode = ntdb_transaction_recover(ntdb);
+ ntdb_unlock_open(ntdb, F_WRLCK);
+ ntdb_allrecord_unlock(ntdb, F_WRLCK);
+
+ return ecode;
+}
+
+/* lock an offset in the database. */
+static enum NTDB_ERROR ntdb_nest_lock(struct ntdb_context *ntdb,
+ ntdb_off_t offset, int ltype,
+ enum ntdb_lock_flags flags)
+{
+ struct ntdb_lock *new_lck;
+ enum NTDB_ERROR ecode;
+
+ if (offset > (NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE
+ + ntdb->file->map_size / 8)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_nest_lock: invalid offset %zu ltype=%d",
+ (size_t)offset, ltype);
+ }
+
+ if (ntdb->flags & NTDB_NOLOCK)
+ return NTDB_SUCCESS;
+
+ if (!check_lock_pid(ntdb, "ntdb_nest_lock", true)) {
+ return NTDB_ERR_LOCK;
+ }
+
+ ntdb->stats.locks++;
+
+ new_lck = find_nestlock(ntdb, offset, NULL);
+ if (new_lck) {
+ if (new_lck->owner != ntdb) {
+ return owner_conflict(ntdb, "ntdb_nest_lock");
+ }
+
+ if (new_lck->ltype == F_RDLCK && ltype == F_WRLCK) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_nest_lock:"
+ " offset %zu has read lock",
+ (size_t)offset);
+ }
+ /* Just increment the struct, posix locks don't stack. */
+ new_lck->count++;
+ return NTDB_SUCCESS;
+ }
+
+#if 0
+ if (ntdb->file->num_lockrecs
+ && offset >= NTDB_HASH_LOCK_START
+ && offset < NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_nest_lock: already have a hash lock?");
+ }
+#endif
+
+ new_lck = (struct ntdb_lock *)realloc(
+ ntdb->file->lockrecs,
+ sizeof(*ntdb->file->lockrecs) * (ntdb->file->num_lockrecs+1));
+ if (new_lck == NULL) {
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_nest_lock:"
+ " unable to allocate %zu lock struct",
+ ntdb->file->num_lockrecs + 1);
+ }
+ ntdb->file->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 */
+ ecode = ntdb_brlock(ntdb, ltype, offset, 1, flags);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* First time we grab a lock, perhaps someone died in commit? */
+ if (!(flags & NTDB_LOCK_NOCHECK)
+ && ntdb->file->num_lockrecs == 0) {
+ ntdb_bool_err berr = ntdb_needs_recovery(ntdb);
+ if (berr != false) {
+ ntdb_brunlock(ntdb, ltype, offset, 1);
+
+ if (berr < 0)
+ return NTDB_OFF_TO_ERR(berr);
+ ecode = ntdb_lock_and_recover(ntdb);
+ if (ecode == NTDB_SUCCESS) {
+ ecode = ntdb_brlock(ntdb, ltype, offset, 1,
+ flags);
+ }
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ }
+ }
+
+ ntdb->file->lockrecs[ntdb->file->num_lockrecs].owner = ntdb;
+ ntdb->file->lockrecs[ntdb->file->num_lockrecs].off = offset;
+ ntdb->file->lockrecs[ntdb->file->num_lockrecs].count = 1;
+ ntdb->file->lockrecs[ntdb->file->num_lockrecs].ltype = ltype;
+ ntdb->file->num_lockrecs++;
+
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR ntdb_nest_unlock(struct ntdb_context *ntdb,
+ ntdb_off_t off, int ltype)
+{
+ struct ntdb_lock *lck;
+ enum NTDB_ERROR ecode;
+
+ if (ntdb->flags & NTDB_NOLOCK)
+ return NTDB_SUCCESS;
+
+ lck = find_nestlock(ntdb, off, ntdb);
+ if ((lck == NULL) || (lck->count == 0)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_nest_unlock: no lock for %zu",
+ (size_t)off);
+ }
+
+ if (lck->count > 1) {
+ lck->count--;
+ return NTDB_SUCCESS;
+ }
+
+ /*
+ * 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.
+ */
+ ecode = ntdb_brunlock(ntdb, ltype, off, 1);
+
+ /*
+ * Shrink the array by overwriting the element just unlocked with the
+ * last array element.
+ */
+ *lck = ntdb->file->lockrecs[--ntdb->file->num_lockrecs];
+
+ return ecode;
+}
+
+/*
+ get the transaction lock
+ */
+enum NTDB_ERROR ntdb_transaction_lock(struct ntdb_context *ntdb, int ltype)
+{
+ return ntdb_nest_lock(ntdb, NTDB_TRANSACTION_LOCK, ltype, NTDB_LOCK_WAIT);
+}
+
+/*
+ release the transaction lock
+ */
+void ntdb_transaction_unlock(struct ntdb_context *ntdb, int ltype)
+{
+ ntdb_nest_unlock(ntdb, NTDB_TRANSACTION_LOCK, ltype);
+}
+
+/* We only need to lock individual bytes, but Linux merges consecutive locks
+ * so we lock in contiguous ranges. */
+static enum NTDB_ERROR ntdb_lock_gradual(struct ntdb_context *ntdb,
+ int ltype, enum ntdb_lock_flags flags,
+ ntdb_off_t off, ntdb_off_t len)
+{
+ enum NTDB_ERROR ecode;
+ enum ntdb_lock_flags nb_flags = (flags & ~NTDB_LOCK_WAIT);
+
+ if (len <= 1) {
+ /* 0 would mean to end-of-file... */
+ assert(len != 0);
+ /* Single hash. Just do blocking lock. */
+ return ntdb_brlock(ntdb, ltype, off, len, flags);
+ }
+
+ /* First we try non-blocking. */
+ ecode = ntdb_brlock(ntdb, ltype, off, len, nb_flags);
+ if (ecode != NTDB_ERR_LOCK) {
+ return ecode;
+ }
+
+ /* Try locking first half, then second. */
+ ecode = ntdb_lock_gradual(ntdb, ltype, flags, off, len / 2);
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+
+ ecode = ntdb_lock_gradual(ntdb, ltype, flags,
+ off + len / 2, len - len / 2);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_brunlock(ntdb, ltype, off, len / 2);
+ }
+ return ecode;
+}
+
+/* lock/unlock entire database. It can only be upgradable if you have some
+ * other way of guaranteeing exclusivity (ie. transaction write lock). */
+enum NTDB_ERROR ntdb_allrecord_lock(struct ntdb_context *ntdb, int ltype,
+ enum ntdb_lock_flags flags, bool upgradable)
+{
+ enum NTDB_ERROR ecode;
+ ntdb_bool_err berr;
+
+ if (ntdb->flags & NTDB_NOLOCK)
+ return NTDB_SUCCESS;
+
+ if (!check_lock_pid(ntdb, "ntdb_allrecord_lock", true)) {
+ return NTDB_ERR_LOCK;
+ }
+
+ if (ntdb->file->allrecord_lock.count) {
+ if (ntdb->file->allrecord_lock.owner != ntdb) {
+ return owner_conflict(ntdb, "ntdb_allrecord_lock");
+ }
+
+ if (ltype == F_RDLCK
+ || ntdb->file->allrecord_lock.ltype == F_WRLCK) {
+ ntdb->file->allrecord_lock.count++;
+ return NTDB_SUCCESS;
+ }
+
+ /* a global lock of a different type exists */
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "ntdb_allrecord_lock: already have %s lock",
+ ntdb->file->allrecord_lock.ltype == F_RDLCK
+ ? "read" : "write");
+ }
+
+ if (ntdb_has_hash_locks(ntdb)) {
+ /* can't combine global and chain locks */
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "ntdb_allrecord_lock:"
+ " already have chain lock");
+ }
+
+ if (upgradable && ltype != F_RDLCK) {
+ /* ntdb error: you can't upgrade a write lock! */
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_allrecord_lock:"
+ " can't upgrade a write lock");
+ }
+
+ ntdb->stats.locks++;
+again:
+ /* Lock hashes, gradually. */
+ ecode = ntdb_lock_gradual(ntdb, ltype, flags, NTDB_HASH_LOCK_START,
+ NTDB_HASH_LOCK_RANGE);
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+
+ /* Lock free tables: there to end of file. */
+ ecode = ntdb_brlock(ntdb, ltype,
+ NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE,
+ 0, flags);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_brunlock(ntdb, ltype, NTDB_HASH_LOCK_START,
+ NTDB_HASH_LOCK_RANGE);
+ return ecode;
+ }
+
+ ntdb->file->allrecord_lock.owner = ntdb;
+ ntdb->file->allrecord_lock.count = 1;
+ /* If it's upgradable, it's actually exclusive so we can treat
+ * it as a write lock. */
+ ntdb->file->allrecord_lock.ltype = upgradable ? F_WRLCK : ltype;
+ ntdb->file->allrecord_lock.off = upgradable;
+
+ /* Now check for needing recovery. */
+ if (flags & NTDB_LOCK_NOCHECK)
+ return NTDB_SUCCESS;
+
+ berr = ntdb_needs_recovery(ntdb);
+ if (likely(berr == false))
+ return NTDB_SUCCESS;
+
+ ntdb_allrecord_unlock(ntdb, ltype);
+ if (berr < 0)
+ return NTDB_OFF_TO_ERR(berr);
+ ecode = ntdb_lock_and_recover(ntdb);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ goto again;
+}
+
+enum NTDB_ERROR ntdb_lock_open(struct ntdb_context *ntdb,
+ int ltype, enum ntdb_lock_flags flags)
+{
+ return ntdb_nest_lock(ntdb, NTDB_OPEN_LOCK, ltype, flags);
+}
+
+void ntdb_unlock_open(struct ntdb_context *ntdb, int ltype)
+{
+ ntdb_nest_unlock(ntdb, NTDB_OPEN_LOCK, ltype);
+}
+
+bool ntdb_has_open_lock(struct ntdb_context *ntdb)
+{
+ return !(ntdb->flags & NTDB_NOLOCK)
+ && find_nestlock(ntdb, NTDB_OPEN_LOCK, ntdb) != NULL;
+}
+
+enum NTDB_ERROR ntdb_lock_expand(struct ntdb_context *ntdb, int ltype)
+{
+ /* Lock doesn't protect data, so don't check (we recurse if we do!) */
+ return ntdb_nest_lock(ntdb, NTDB_EXPANSION_LOCK, ltype,
+ NTDB_LOCK_WAIT | NTDB_LOCK_NOCHECK);
+}
+
+void ntdb_unlock_expand(struct ntdb_context *ntdb, int ltype)
+{
+ ntdb_nest_unlock(ntdb, NTDB_EXPANSION_LOCK, ltype);
+}
+
+/* unlock entire db */
+void ntdb_allrecord_unlock(struct ntdb_context *ntdb, int ltype)
+{
+ if (ntdb->flags & NTDB_NOLOCK)
+ return;
+
+ if (ntdb->file->allrecord_lock.count == 0) {
+ ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "ntdb_allrecord_unlock: not locked!");
+ return;
+ }
+
+ if (ntdb->file->allrecord_lock.owner != ntdb) {
+ ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "ntdb_allrecord_unlock: not locked by us!");
+ return;
+ }
+
+ /* Upgradable locks are marked as write locks. */
+ if (ntdb->file->allrecord_lock.ltype != ltype
+ && (!ntdb->file->allrecord_lock.off || ltype != F_RDLCK)) {
+ ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_allrecord_unlock: have %s lock",
+ ntdb->file->allrecord_lock.ltype == F_RDLCK
+ ? "read" : "write");
+ return;
+ }
+
+ if (ntdb->file->allrecord_lock.count > 1) {
+ ntdb->file->allrecord_lock.count--;
+ return;
+ }
+
+ ntdb->file->allrecord_lock.count = 0;
+ ntdb->file->allrecord_lock.ltype = 0;
+
+ ntdb_brunlock(ntdb, ltype, NTDB_HASH_LOCK_START, 0);
+}
+
+bool ntdb_has_expansion_lock(struct ntdb_context *ntdb)
+{
+ return find_nestlock(ntdb, NTDB_EXPANSION_LOCK, ntdb) != NULL;
+}
+
+bool ntdb_has_hash_locks(struct ntdb_context *ntdb)
+{
+ unsigned int i;
+
+ for (i=0; i<ntdb->file->num_lockrecs; i++) {
+ if (ntdb->file->lockrecs[i].off >= NTDB_HASH_LOCK_START
+ && ntdb->file->lockrecs[i].off < (NTDB_HASH_LOCK_START
+ + NTDB_HASH_LOCK_RANGE))
+ return true;
+ }
+ return false;
+}
+
+static bool ntdb_has_free_lock(struct ntdb_context *ntdb)
+{
+ unsigned int i;
+
+ if (ntdb->flags & NTDB_NOLOCK)
+ return false;
+
+ for (i=0; i<ntdb->file->num_lockrecs; i++) {
+ if (ntdb->file->lockrecs[i].off
+ > NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE)
+ return true;
+ }
+ return false;
+}
+
+enum NTDB_ERROR ntdb_lock_hashes(struct ntdb_context *ntdb,
+ ntdb_off_t hash_lock,
+ ntdb_len_t hash_range,
+ int ltype, enum ntdb_lock_flags waitflag)
+{
+ /* FIXME: Do this properly, using hlock_range */
+ unsigned l = NTDB_HASH_LOCK_START
+ + (hash_lock >> (64 - NTDB_HASH_LOCK_RANGE_BITS));
+
+ /* a allrecord lock allows us to avoid per chain locks */
+ if (ntdb->file->allrecord_lock.count) {
+ if (!check_lock_pid(ntdb, "ntdb_lock_hashes", true))
+ return NTDB_ERR_LOCK;
+
+ if (ntdb->file->allrecord_lock.owner != ntdb)
+ return owner_conflict(ntdb, "ntdb_lock_hashes");
+ if (ltype == ntdb->file->allrecord_lock.ltype
+ || ltype == F_RDLCK) {
+ return NTDB_SUCCESS;
+ }
+
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "ntdb_lock_hashes:"
+ " already have %s allrecordlock",
+ ntdb->file->allrecord_lock.ltype == F_RDLCK
+ ? "read" : "write");
+ }
+
+ if (ntdb_has_free_lock(ntdb)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_lock_hashes: already have free lock");
+ }
+
+ if (ntdb_has_expansion_lock(ntdb)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_lock_hashes:"
+ " already have expansion lock");
+ }
+
+ return ntdb_nest_lock(ntdb, l, ltype, waitflag);
+}
+
+enum NTDB_ERROR ntdb_unlock_hashes(struct ntdb_context *ntdb,
+ ntdb_off_t hash_lock,
+ ntdb_len_t hash_range, int ltype)
+{
+ unsigned l = NTDB_HASH_LOCK_START
+ + (hash_lock >> (64 - NTDB_HASH_LOCK_RANGE_BITS));
+
+ if (ntdb->flags & NTDB_NOLOCK)
+ return 0;
+
+ /* a allrecord lock allows us to avoid per chain locks */
+ if (ntdb->file->allrecord_lock.count) {
+ if (ntdb->file->allrecord_lock.ltype == F_RDLCK
+ && ltype == F_WRLCK) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_unlock_hashes RO allrecord!");
+ }
+ if (ntdb->file->allrecord_lock.owner != ntdb) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
+ "ntdb_unlock_hashes:"
+ " not locked by us!");
+ }
+ return NTDB_SUCCESS;
+ }
+
+ return ntdb_nest_unlock(ntdb, l, ltype);
+}
+
+/* Hash locks use NTDB_HASH_LOCK_START + the next 30 bits.
+ * Then we begin; bucket offsets are sizeof(ntdb_len_t) apart, so we divide.
+ * The result is that on 32 bit systems we don't use lock values > 2^31 on
+ * files that are less than 4GB.
+ */
+static ntdb_off_t free_lock_off(ntdb_off_t b_off)
+{
+ return NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE
+ + b_off / sizeof(ntdb_off_t);
+}
+
+enum NTDB_ERROR ntdb_lock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off,
+ enum ntdb_lock_flags waitflag)
+{
+ assert(b_off >= sizeof(struct ntdb_header));
+
+ if (ntdb->flags & NTDB_NOLOCK)
+ return 0;
+
+ /* a allrecord lock allows us to avoid per chain locks */
+ if (ntdb->file->allrecord_lock.count) {
+ if (!check_lock_pid(ntdb, "ntdb_lock_free_bucket", true))
+ return NTDB_ERR_LOCK;
+
+ if (ntdb->file->allrecord_lock.owner != ntdb) {
+ return owner_conflict(ntdb, "ntdb_lock_free_bucket");
+ }
+
+ if (ntdb->file->allrecord_lock.ltype == F_WRLCK)
+ return 0;
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_lock_free_bucket with"
+ " read-only allrecordlock!");
+ }
+
+#if 0 /* FIXME */
+ if (ntdb_has_expansion_lock(ntdb)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
+ "ntdb_lock_free_bucket:"
+ " already have expansion lock");
+ }
+#endif
+
+ return ntdb_nest_lock(ntdb, free_lock_off(b_off), F_WRLCK, waitflag);
+}
+
+void ntdb_unlock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off)
+{
+ if (ntdb->file->allrecord_lock.count)
+ return;
+
+ ntdb_nest_unlock(ntdb, free_lock_off(b_off), F_WRLCK);
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_lockall(struct ntdb_context *ntdb)
+{
+ return ntdb_allrecord_lock(ntdb, F_WRLCK, NTDB_LOCK_WAIT, false);
+}
+
+_PUBLIC_ void ntdb_unlockall(struct ntdb_context *ntdb)
+{
+ ntdb_allrecord_unlock(ntdb, F_WRLCK);
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_lockall_read(struct ntdb_context *ntdb)
+{
+ return ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, false);
+}
+
+_PUBLIC_ void ntdb_unlockall_read(struct ntdb_context *ntdb)
+{
+ ntdb_allrecord_unlock(ntdb, F_RDLCK);
+}
+
+void ntdb_lock_cleanup(struct ntdb_context *ntdb)
+{
+ unsigned int i;
+
+ /* We don't want to warn: they're allowed to close ntdb after fork. */
+ if (!check_lock_pid(ntdb, "ntdb_close", false))
+ return;
+
+ while (ntdb->file->allrecord_lock.count
+ && ntdb->file->allrecord_lock.owner == ntdb) {
+ ntdb_allrecord_unlock(ntdb, ntdb->file->allrecord_lock.ltype);
+ }
+
+ for (i=0; i<ntdb->file->num_lockrecs; i++) {
+ if (ntdb->file->lockrecs[i].owner == ntdb) {
+ ntdb_nest_unlock(ntdb,
+ ntdb->file->lockrecs[i].off,
+ ntdb->file->lockrecs[i].ltype);
+ i--;
+ }
+ }
+}
diff --git a/lib/ntdb/ntdb.c b/lib/ntdb/ntdb.c
new file mode 100644
index 0000000000..9f1e32793a
--- /dev/null
+++ b/lib/ntdb/ntdb.c
@@ -0,0 +1,605 @@
+ /*
+ Trivial Database 2: fetch, store and misc routines.
+ 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 "private.h"
+#ifndef HAVE_LIBREPLACE
+#include <ccan/asprintf/asprintf.h>
+#include <stdarg.h>
+#endif
+
+static enum NTDB_ERROR update_rec_hdr(struct ntdb_context *ntdb,
+ ntdb_off_t off,
+ ntdb_len_t keylen,
+ ntdb_len_t datalen,
+ struct ntdb_used_record *rec,
+ uint64_t h)
+{
+ uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
+ enum NTDB_ERROR ecode;
+
+ ecode = set_header(ntdb, rec, NTDB_USED_MAGIC, keylen, datalen,
+ keylen + dataroom, h);
+ if (ecode == NTDB_SUCCESS) {
+ ecode = ntdb_write_convert(ntdb, off, rec, sizeof(*rec));
+ }
+ return ecode;
+}
+
+static enum NTDB_ERROR replace_data(struct ntdb_context *ntdb,
+ struct hash_info *h,
+ NTDB_DATA key, NTDB_DATA dbuf,
+ ntdb_off_t old_off, ntdb_len_t old_room,
+ bool growing)
+{
+ ntdb_off_t new_off;
+ enum NTDB_ERROR ecode;
+
+ /* Allocate a new record. */
+ new_off = alloc(ntdb, key.dsize, dbuf.dsize, h->h, NTDB_USED_MAGIC,
+ growing);
+ if (NTDB_OFF_IS_ERR(new_off)) {
+ return NTDB_OFF_TO_ERR(new_off);
+ }
+
+ /* We didn't like the existing one: remove it. */
+ if (old_off) {
+ ntdb->stats.frees++;
+ ecode = add_free_record(ntdb, old_off,
+ sizeof(struct ntdb_used_record)
+ + key.dsize + old_room,
+ NTDB_LOCK_WAIT, true);
+ if (ecode == NTDB_SUCCESS)
+ ecode = replace_in_hash(ntdb, h, new_off);
+ } else {
+ ecode = add_to_hash(ntdb, h, new_off);
+ }
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ new_off += sizeof(struct ntdb_used_record);
+ ecode = ntdb->io->twrite(ntdb, new_off, key.dptr, key.dsize);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ new_off += key.dsize;
+ ecode = ntdb->io->twrite(ntdb, new_off, dbuf.dptr, dbuf.dsize);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ if (ntdb->flags & NTDB_SEQNUM)
+ ntdb_inc_seqnum(ntdb);
+
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR update_data(struct ntdb_context *ntdb,
+ ntdb_off_t off,
+ NTDB_DATA dbuf,
+ ntdb_len_t extra)
+{
+ enum NTDB_ERROR ecode;
+
+ ecode = ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize);
+ if (ecode == NTDB_SUCCESS && extra) {
+ /* Put a zero in; future versions may append other data. */
+ ecode = ntdb->io->twrite(ntdb, off + dbuf.dsize, "", 1);
+ }
+ if (ntdb->flags & NTDB_SEQNUM)
+ ntdb_inc_seqnum(ntdb);
+
+ return ecode;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_store(struct ntdb_context *ntdb,
+ NTDB_DATA key, NTDB_DATA dbuf, int flag)
+{
+ struct hash_info h;
+ ntdb_off_t off;
+ ntdb_len_t old_room = 0;
+ struct ntdb_used_record rec;
+ enum NTDB_ERROR ecode;
+
+ off = find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL);
+ if (NTDB_OFF_IS_ERR(off)) {
+ return ntdb->last_error = NTDB_OFF_TO_ERR(off);
+ }
+
+ /* Now we have lock on this hash bucket. */
+ if (flag == NTDB_INSERT) {
+ if (off) {
+ ecode = NTDB_ERR_EXISTS;
+ goto out;
+ }
+ } else {
+ if (off) {
+ old_room = rec_data_length(&rec)
+ + rec_extra_padding(&rec);
+ if (old_room >= dbuf.dsize) {
+ /* Can modify in-place. Easy! */
+ ecode = update_rec_hdr(ntdb, off,
+ key.dsize, dbuf.dsize,
+ &rec, h.h);
+ if (ecode != NTDB_SUCCESS) {
+ goto out;
+ }
+ ecode = update_data(ntdb,
+ off + sizeof(rec)
+ + key.dsize, dbuf,
+ old_room - dbuf.dsize);
+ if (ecode != NTDB_SUCCESS) {
+ goto out;
+ }
+ ntdb_unlock_hashes(ntdb, h.hlock_start,
+ h.hlock_range, F_WRLCK);
+ return ntdb->last_error = NTDB_SUCCESS;
+ }
+ } else {
+ if (flag == NTDB_MODIFY) {
+ /* if the record doesn't exist and we
+ are in NTDB_MODIFY mode then we should fail
+ the store */
+ ecode = NTDB_ERR_NOEXIST;
+ goto out;
+ }
+ }
+ }
+
+ /* If we didn't use the old record, this implies we're growing. */
+ ecode = replace_data(ntdb, &h, key, dbuf, off, old_room, off);
+out:
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_WRLCK);
+ return ntdb->last_error = ecode;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_append(struct ntdb_context *ntdb,
+ NTDB_DATA key, NTDB_DATA dbuf)
+{
+ struct hash_info h;
+ ntdb_off_t off;
+ struct ntdb_used_record rec;
+ ntdb_len_t old_room = 0, old_dlen;
+ unsigned char *newdata;
+ NTDB_DATA new_dbuf;
+ enum NTDB_ERROR ecode;
+
+ off = find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL);
+ if (NTDB_OFF_IS_ERR(off)) {
+ return ntdb->last_error = NTDB_OFF_TO_ERR(off);
+ }
+
+ if (off) {
+ old_dlen = rec_data_length(&rec);
+ old_room = old_dlen + rec_extra_padding(&rec);
+
+ /* Fast path: can append in place. */
+ if (rec_extra_padding(&rec) >= dbuf.dsize) {
+ ecode = update_rec_hdr(ntdb, off, key.dsize,
+ old_dlen + dbuf.dsize, &rec,
+ h.h);
+ if (ecode != NTDB_SUCCESS) {
+ goto out;
+ }
+
+ off += sizeof(rec) + key.dsize + old_dlen;
+ ecode = update_data(ntdb, off, dbuf,
+ rec_extra_padding(&rec));
+ goto out;
+ }
+
+ /* Slow path. */
+ newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
+ if (!newdata) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_append:"
+ " failed to allocate %zu bytes",
+ (size_t)(key.dsize + old_dlen
+ + dbuf.dsize));
+ goto out;
+ }
+ ecode = ntdb->io->tread(ntdb, off + sizeof(rec) + key.dsize,
+ newdata, old_dlen);
+ if (ecode != NTDB_SUCCESS) {
+ goto out_free_newdata;
+ }
+ memcpy(newdata + old_dlen, dbuf.dptr, dbuf.dsize);
+ new_dbuf.dptr = newdata;
+ new_dbuf.dsize = old_dlen + dbuf.dsize;
+ } else {
+ newdata = NULL;
+ new_dbuf = dbuf;
+ }
+
+ /* If they're using ntdb_append(), it implies they're growing record. */
+ ecode = replace_data(ntdb, &h, key, new_dbuf, off, old_room, true);
+
+out_free_newdata:
+ free(newdata);
+out:
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_WRLCK);
+ return ntdb->last_error = ecode;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_fetch(struct ntdb_context *ntdb, NTDB_DATA key,
+ NTDB_DATA *data)
+{
+ ntdb_off_t off;
+ struct ntdb_used_record rec;
+ struct hash_info h;
+ enum NTDB_ERROR ecode;
+
+ off = find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL);
+ if (NTDB_OFF_IS_ERR(off)) {
+ return ntdb->last_error = NTDB_OFF_TO_ERR(off);
+ }
+
+ if (!off) {
+ ecode = NTDB_ERR_NOEXIST;
+ } else {
+ data->dsize = rec_data_length(&rec);
+ data->dptr = ntdb_alloc_read(ntdb, off + sizeof(rec) + key.dsize,
+ data->dsize);
+ if (NTDB_PTR_IS_ERR(data->dptr)) {
+ ecode = NTDB_PTR_ERR(data->dptr);
+ } else
+ ecode = NTDB_SUCCESS;
+ }
+
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
+ return ntdb->last_error = ecode;
+}
+
+_PUBLIC_ bool ntdb_exists(struct ntdb_context *ntdb, NTDB_DATA key)
+{
+ ntdb_off_t off;
+ struct ntdb_used_record rec;
+ struct hash_info h;
+
+ off = find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL);
+ if (NTDB_OFF_IS_ERR(off)) {
+ ntdb->last_error = NTDB_OFF_TO_ERR(off);
+ return false;
+ }
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
+
+ ntdb->last_error = NTDB_SUCCESS;
+ return off ? true : false;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_delete(struct ntdb_context *ntdb, NTDB_DATA key)
+{
+ ntdb_off_t off;
+ struct ntdb_used_record rec;
+ struct hash_info h;
+ enum NTDB_ERROR ecode;
+
+ off = find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL);
+ if (NTDB_OFF_IS_ERR(off)) {
+ return ntdb->last_error = NTDB_OFF_TO_ERR(off);
+ }
+
+ if (!off) {
+ ecode = NTDB_ERR_NOEXIST;
+ goto unlock;
+ }
+
+ ecode = delete_from_hash(ntdb, &h);
+ if (ecode != NTDB_SUCCESS) {
+ goto unlock;
+ }
+
+ /* Free the deleted entry. */
+ ntdb->stats.frees++;
+ ecode = add_free_record(ntdb, off,
+ sizeof(struct ntdb_used_record)
+ + rec_key_length(&rec)
+ + rec_data_length(&rec)
+ + rec_extra_padding(&rec),
+ NTDB_LOCK_WAIT, true);
+
+ if (ntdb->flags & NTDB_SEQNUM)
+ ntdb_inc_seqnum(ntdb);
+
+unlock:
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_WRLCK);
+ return ntdb->last_error = ecode;
+}
+
+_PUBLIC_ unsigned int ntdb_get_flags(struct ntdb_context *ntdb)
+{
+ return ntdb->flags;
+}
+
+static bool inside_transaction(const struct ntdb_context *ntdb)
+{
+ return ntdb->transaction != NULL;
+}
+
+static bool readonly_changable(struct ntdb_context *ntdb, const char *caller)
+{
+ if (inside_transaction(ntdb)) {
+ ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "%s: can't change"
+ " NTDB_RDONLY inside transaction",
+ caller);
+ return false;
+ }
+ return true;
+}
+
+_PUBLIC_ void ntdb_add_flag(struct ntdb_context *ntdb, unsigned flag)
+{
+ if (ntdb->flags & NTDB_INTERNAL) {
+ ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_add_flag: internal db");
+ return;
+ }
+ switch (flag) {
+ case NTDB_NOLOCK:
+ ntdb->flags |= NTDB_NOLOCK;
+ break;
+ case NTDB_NOMMAP:
+ ntdb->flags |= NTDB_NOMMAP;
+#ifndef HAVE_INCOHERENT_MMAP
+ ntdb_munmap(ntdb->file);
+#endif
+ break;
+ case NTDB_NOSYNC:
+ ntdb->flags |= NTDB_NOSYNC;
+ break;
+ case NTDB_SEQNUM:
+ ntdb->flags |= NTDB_SEQNUM;
+ break;
+ case NTDB_ALLOW_NESTING:
+ ntdb->flags |= NTDB_ALLOW_NESTING;
+ break;
+ case NTDB_RDONLY:
+ if (readonly_changable(ntdb, "ntdb_add_flag"))
+ ntdb->flags |= NTDB_RDONLY;
+ break;
+ default:
+ ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_add_flag: Unknown flag %u",
+ flag);
+ }
+}
+
+_PUBLIC_ void ntdb_remove_flag(struct ntdb_context *ntdb, unsigned flag)
+{
+ if (ntdb->flags & NTDB_INTERNAL) {
+ ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_remove_flag: internal db");
+ return;
+ }
+ switch (flag) {
+ case NTDB_NOLOCK:
+ ntdb->flags &= ~NTDB_NOLOCK;
+ break;
+ case NTDB_NOMMAP:
+ ntdb->flags &= ~NTDB_NOMMAP;
+#ifndef HAVE_INCOHERENT_MMAP
+ /* If mmap incoherent, we were mmaping anyway. */
+ ntdb_mmap(ntdb);
+#endif
+ break;
+ case NTDB_NOSYNC:
+ ntdb->flags &= ~NTDB_NOSYNC;
+ break;
+ case NTDB_SEQNUM:
+ ntdb->flags &= ~NTDB_SEQNUM;
+ break;
+ case NTDB_ALLOW_NESTING:
+ ntdb->flags &= ~NTDB_ALLOW_NESTING;
+ break;
+ case NTDB_RDONLY:
+ if ((ntdb->open_flags & O_ACCMODE) == O_RDONLY) {
+ ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_remove_flag: can't"
+ " remove NTDB_RDONLY on ntdb"
+ " opened with O_RDONLY");
+ break;
+ }
+ if (readonly_changable(ntdb, "ntdb_remove_flag"))
+ ntdb->flags &= ~NTDB_RDONLY;
+ break;
+ default:
+ ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_remove_flag: Unknown flag %u",
+ flag);
+ }
+}
+
+_PUBLIC_ const char *ntdb_errorstr(enum NTDB_ERROR ecode)
+{
+ /* Gcc warns if you miss a case in the switch, so use that. */
+ switch (NTDB_ERR_TO_OFF(ecode)) {
+ case NTDB_ERR_TO_OFF(NTDB_SUCCESS): return "Success";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_CORRUPT): return "Corrupt database";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_IO): return "IO Error";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_LOCK): return "Locking error";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_OOM): return "Out of memory";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_EXISTS): return "Record exists";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_EINVAL): return "Invalid parameter";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_NOEXIST): return "Record does not exist";
+ case NTDB_ERR_TO_OFF(NTDB_ERR_RDONLY): return "write not permitted";
+ }
+ return "Invalid error code";
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_error(struct ntdb_context *ntdb)
+{
+ return ntdb->last_error;
+}
+
+enum NTDB_ERROR COLD ntdb_logerr(struct ntdb_context *ntdb,
+ enum NTDB_ERROR ecode,
+ enum ntdb_log_level level,
+ const char *fmt, ...)
+{
+ char *message;
+ va_list ap;
+ size_t len;
+ /* ntdb_open paths care about errno, so save it. */
+ int saved_errno = errno;
+
+ if (!ntdb->log_fn)
+ return ecode;
+
+ va_start(ap, fmt);
+ len = vasprintf(&message, fmt, ap);
+ va_end(ap);
+
+ if (len < 0) {
+ ntdb->log_fn(ntdb, NTDB_LOG_ERROR, NTDB_ERR_OOM,
+ "out of memory formatting message:", ntdb->log_data);
+ ntdb->log_fn(ntdb, level, ecode, fmt, ntdb->log_data);
+ } else {
+ ntdb->log_fn(ntdb, level, ecode, message, ntdb->log_data);
+ free(message);
+ }
+ errno = saved_errno;
+ return ecode;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_parse_record_(struct ntdb_context *ntdb,
+ NTDB_DATA key,
+ enum NTDB_ERROR (*parse)(NTDB_DATA k,
+ NTDB_DATA d,
+ void *data),
+ void *data)
+{
+ ntdb_off_t off;
+ struct ntdb_used_record rec;
+ struct hash_info h;
+ enum NTDB_ERROR ecode;
+
+ off = find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL);
+ if (NTDB_OFF_IS_ERR(off)) {
+ return ntdb->last_error = NTDB_OFF_TO_ERR(off);
+ }
+
+ if (!off) {
+ ecode = NTDB_ERR_NOEXIST;
+ } else {
+ const void *dptr;
+ dptr = ntdb_access_read(ntdb, off + sizeof(rec) + key.dsize,
+ rec_data_length(&rec), false);
+ if (NTDB_PTR_IS_ERR(dptr)) {
+ ecode = NTDB_PTR_ERR(dptr);
+ } else {
+ NTDB_DATA d = ntdb_mkdata(dptr, rec_data_length(&rec));
+
+ ecode = parse(key, d, data);
+ ntdb_access_release(ntdb, dptr);
+ }
+ }
+
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
+ return ntdb->last_error = ecode;
+}
+
+_PUBLIC_ const char *ntdb_name(const struct ntdb_context *ntdb)
+{
+ return ntdb->name;
+}
+
+_PUBLIC_ int64_t ntdb_get_seqnum(struct ntdb_context *ntdb)
+{
+ ntdb_off_t off;
+
+ off = ntdb_read_off(ntdb, offsetof(struct ntdb_header, seqnum));
+ if (NTDB_OFF_IS_ERR(off))
+ ntdb->last_error = NTDB_OFF_TO_ERR(off);
+ else
+ ntdb->last_error = NTDB_SUCCESS;
+ return off;
+}
+
+
+_PUBLIC_ int ntdb_fd(const struct ntdb_context *ntdb)
+{
+ return ntdb->file->fd;
+}
+
+struct traverse_state {
+ enum NTDB_ERROR error;
+ struct ntdb_context *dest_db;
+};
+
+/*
+ traverse function for repacking
+ */
+static int repack_traverse(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA data,
+ struct traverse_state *state)
+{
+ state->error = ntdb_store(state->dest_db, key, data, NTDB_INSERT);
+ if (state->error != NTDB_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_repack(struct ntdb_context *ntdb)
+{
+ struct ntdb_context *tmp_db;
+ struct traverse_state state;
+
+ state.error = ntdb_transaction_start(ntdb);
+ if (state.error != NTDB_SUCCESS) {
+ return state.error;
+ }
+
+ tmp_db = ntdb_open("tmpdb", NTDB_INTERNAL, O_RDWR|O_CREAT, 0, NULL);
+ if (tmp_db == NULL) {
+ state.error = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ __location__
+ " Failed to create tmp_db");
+ ntdb_transaction_cancel(ntdb);
+ return ntdb->last_error = state.error;
+ }
+
+ state.dest_db = tmp_db;
+ if (ntdb_traverse(ntdb, repack_traverse, &state) < 0) {
+ goto fail;
+ }
+
+ state.error = ntdb_wipe_all(ntdb);
+ if (state.error != NTDB_SUCCESS) {
+ goto fail;
+ }
+
+ state.dest_db = ntdb;
+ if (ntdb_traverse(tmp_db, repack_traverse, &state) < 0) {
+ goto fail;
+ }
+
+ ntdb_close(tmp_db);
+ return ntdb_transaction_commit(ntdb);
+
+fail:
+ ntdb_transaction_cancel(ntdb);
+ ntdb_close(tmp_db);
+ return state.error;
+}
diff --git a/lib/ntdb/ntdb.h b/lib/ntdb/ntdb.h
new file mode 100644
index 0000000000..f0833b7261
--- /dev/null
+++ b/lib/ntdb/ntdb.h
@@ -0,0 +1,901 @@
+#ifndef CCAN_NTDB_H
+#define CCAN_NTDB_H
+
+/*
+ NTDB: trivial database library version 2
+
+ Copyright (C) Andrew Tridgell 1999-2004
+ Copyright (C) Rusty Russell 2010-2012
+
+ ** NOTE! The following LGPL license applies to the ntdb
+ ** 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
+
+#ifdef HAVE_LIBREPLACE
+#include <replace.h>
+#else
+#if HAVE_FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+/* For mode_t */
+#include <sys/types.h>
+/* For O_* flags. */
+#include <sys/stat.h>
+/* For sig_atomic_t. */
+#include <signal.h>
+/* For uint64_t */
+#include <stdint.h>
+/* For bool */
+#include <stdbool.h>
+/* For memcmp */
+#include <string.h>
+#endif
+
+#if HAVE_CCAN
+#include <ccan/compiler/compiler.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <ccan/cast/cast.h>
+#else
+#ifndef typesafe_cb_preargs
+/* Failing to have CCAN just mean less typesafe protection, etc. */
+#define typesafe_cb_preargs(rtype, atype, fn, arg, ...) \
+ ((rtype (*)(__VA_ARGS__, atype))(fn))
+#endif
+#ifndef cast_const
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+#define cast_const(type, expr) ((type)((intptr_t)(expr)))
+#else
+#define cast_const(type, expr) ((type *)(expr))
+#endif
+#endif
+#endif /* !HAVE_CCAN */
+
+union ntdb_attribute;
+struct ntdb_context;
+
+/**
+ * struct TDB_DATA - (n)tdb data blob
+ *
+ * To ease compatibility, we use 'struct TDB_DATA' from tdb.h, so if
+ * you want to include both tdb.h and ntdb.h, you need to #include
+ * tdb.h first.
+ */
+#ifndef __TDB_H__
+struct TDB_DATA {
+ unsigned char *dptr;
+ size_t dsize;
+};
+#endif
+
+typedef struct TDB_DATA NTDB_DATA;
+
+/**
+ * ntdb_open - open a database file
+ * @name: the file name (can be NULL if flags contains NTDB_INTERNAL)
+ * @ntdb_flags: options for this database
+ * @open_flags: flags argument for ntdb's open() call.
+ * @mode: mode argument for ntdb's open() call.
+ * @attributes: linked list of extra attributes for this ntdb.
+ *
+ * This call opens (and potentially creates) a database file.
+ * Multiple processes can have the NTDB file open at once.
+ *
+ * On failure it will return NULL, and set errno: it may also call
+ * any log attribute found in @attributes.
+ *
+ * See also:
+ * union ntdb_attribute
+ */
+struct ntdb_context *ntdb_open(const char *name, int ntdb_flags,
+ int open_flags, mode_t mode,
+ union ntdb_attribute *attributes);
+
+
+/* flags for ntdb_open() */
+#define NTDB_DEFAULT 0 /* just a readability place holder */
+#define NTDB_INTERNAL 2 /* don't store on disk */
+#define NTDB_NOLOCK 4 /* don't do any locking */
+#define NTDB_NOMMAP 8 /* don't use mmap */
+#define NTDB_CONVERT 16 /* convert endian */
+#define NTDB_NOSYNC 64 /* don't use synchronous transactions */
+#define NTDB_SEQNUM 128 /* maintain a sequence number */
+#define NTDB_ALLOW_NESTING 256 /* fake nested transactions */
+#define NTDB_RDONLY 512 /* implied by O_RDONLY */
+#define NTDB_CANT_CHECK 2048 /* has a feature which we don't understand */
+
+/**
+ * ntdb_close - close and free a ntdb.
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This always succeeds, in that @ntdb is unusable after this call. But if
+ * some unexpected error occurred while closing, it will return non-zero
+ * (the only clue as to cause will be via the log attribute).
+ */
+int ntdb_close(struct ntdb_context *ntdb);
+
+/**
+ * enum NTDB_ERROR - error returns for NTDB
+ *
+ * See Also:
+ * ntdb_errorstr()
+ */
+enum NTDB_ERROR {
+ NTDB_SUCCESS = 0, /* No error. */
+ NTDB_ERR_CORRUPT = -1, /* We read the db, and it was bogus. */
+ NTDB_ERR_IO = -2, /* We couldn't read/write the db. */
+ NTDB_ERR_LOCK = -3, /* Locking failed. */
+ NTDB_ERR_OOM = -4, /* Out of Memory. */
+ NTDB_ERR_EXISTS = -5, /* The key already exists. */
+ NTDB_ERR_NOEXIST = -6, /* The key does not exist. */
+ NTDB_ERR_EINVAL = -7, /* You're using it wrong. */
+ NTDB_ERR_RDONLY = -8, /* The database is read-only. */
+ NTDB_ERR_LAST = NTDB_ERR_RDONLY
+};
+
+/**
+ * ntdb_store - store a key/value pair in a ntdb.
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key
+ * @dbuf: the data to associate with the key.
+ * @flag: NTDB_REPLACE, NTDB_INSERT or NTDB_MODIFY.
+ *
+ * This inserts (or overwrites) a key/value pair in the NTDB. If flag
+ * is NTDB_REPLACE, it doesn't matter whether the key exists or not;
+ * NTDB_INSERT means it must not exist (returns NTDB_ERR_EXISTS otherwise),
+ * and NTDB_MODIFY means it must exist (returns NTDB_ERR_NOEXIST otherwise).
+ *
+ * On success, this returns NTDB_SUCCESS.
+ *
+ * See also:
+ * ntdb_fetch, ntdb_transaction_start, ntdb_append, ntdb_delete.
+ */
+enum NTDB_ERROR ntdb_store(struct ntdb_context *ntdb,
+ NTDB_DATA key,
+ NTDB_DATA dbuf,
+ int flag);
+
+/* flags to ntdb_store() */
+#define NTDB_REPLACE 1 /* A readability place holder */
+#define NTDB_INSERT 2 /* Don't overwrite an existing entry */
+#define NTDB_MODIFY 3 /* Don't create an existing entry */
+
+/**
+ * ntdb_fetch - fetch a value from a ntdb.
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key
+ * @data: pointer to data.
+ *
+ * This looks up a key in the database and sets it in @data.
+ *
+ * If it returns NTDB_SUCCESS, the key was found: it is your
+ * responsibility to call free() on @data->dptr.
+ *
+ * Otherwise, it returns an error (usually, NTDB_ERR_NOEXIST) and @data is
+ * undefined.
+ */
+enum NTDB_ERROR ntdb_fetch(struct ntdb_context *ntdb, NTDB_DATA key,
+ NTDB_DATA *data);
+
+/**
+ * ntdb_errorstr - map the ntdb error onto a constant readable string
+ * @ecode: the enum NTDB_ERROR to map.
+ *
+ * This is useful for displaying errors to users.
+ */
+const char *ntdb_errorstr(enum NTDB_ERROR ecode);
+
+/**
+ * ntdb_append - append a value to a key/value pair in a ntdb.
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key
+ * @dbuf: the data to append.
+ *
+ * This is equivalent to fetching a record, reallocating .dptr to add the
+ * data, and writing it back, only it's much more efficient. If the key
+ * doesn't exist, it's equivalent to ntdb_store (with an additional hint that
+ * you expect to expand the record in future).
+ *
+ * See Also:
+ * ntdb_fetch(), ntdb_store()
+ */
+enum NTDB_ERROR ntdb_append(struct ntdb_context *ntdb,
+ NTDB_DATA key, NTDB_DATA dbuf);
+
+/**
+ * ntdb_delete - delete a key from a ntdb.
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key to delete.
+ *
+ * Returns NTDB_SUCCESS on success, or an error (usually NTDB_ERR_NOEXIST).
+ *
+ * See Also:
+ * ntdb_fetch(), ntdb_store()
+ */
+enum NTDB_ERROR ntdb_delete(struct ntdb_context *ntdb, NTDB_DATA key);
+
+/**
+ * ntdb_exists - does a key exist in the database?
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key to search for.
+ *
+ * Returns true if it exists, or false if it doesn't or any other error.
+ */
+bool ntdb_exists(struct ntdb_context *ntdb, NTDB_DATA key);
+
+/**
+ * ntdb_deq - are NTDB_DATA equal?
+ * @a: one NTDB_DATA
+ * @b: another NTDB_DATA
+ */
+static inline bool ntdb_deq(NTDB_DATA a, NTDB_DATA b)
+{
+ return a.dsize == b.dsize && memcmp(a.dptr, b.dptr, a.dsize) == 0;
+}
+
+/**
+ * ntdb_mkdata - make a NTDB_DATA from const data
+ * @p: the constant pointer
+ * @len: the length
+ *
+ * As the dptr member of NTDB_DATA is not constant, you need to
+ * cast it. This function keeps thost casts in one place, as well as
+ * suppressing the warning some compilers give when casting away a
+ * qualifier (eg. gcc with -Wcast-qual)
+ */
+static inline NTDB_DATA ntdb_mkdata(const void *p, size_t len)
+{
+ NTDB_DATA d;
+ d.dptr = cast_const(void *, p);
+ d.dsize = len;
+ return d;
+}
+
+/**
+ * ntdb_transaction_start - start a transaction
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This begins a series of atomic operations. Other processes will be able
+ * to read the ntdb, but not alter it (they will block), nor will they see
+ * any changes until ntdb_transaction_commit() is called.
+ *
+ * Note that if the NTDB_ALLOW_NESTING flag is set, a ntdb_transaction_start()
+ * within a transaction will succeed, but it's not a real transaction:
+ * (1) An inner transaction which is committed is not actually committed until
+ * the outer transaction is; if the outer transaction is cancelled, the
+ * inner ones are discarded.
+ * (2) ntdb_transaction_cancel() marks the outer transaction as having an error,
+ * so the final ntdb_transaction_commit() will fail.
+ * (3) the outer transaction will see the results of the inner transaction.
+ *
+ * See Also:
+ * ntdb_transaction_cancel, ntdb_transaction_commit.
+ */
+enum NTDB_ERROR ntdb_transaction_start(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_transaction_cancel - abandon a transaction
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This aborts a transaction, discarding any changes which were made.
+ * ntdb_close() does this implicitly.
+ */
+void ntdb_transaction_cancel(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_transaction_commit - commit a transaction
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This completes a transaction, writing any changes which were made.
+ *
+ * fsync() is used to commit the transaction (unless NTDB_NOSYNC is set),
+ * making it robust against machine crashes, but very slow compared to
+ * other NTDB operations.
+ *
+ * A failure can only be caused by unexpected errors (eg. I/O or
+ * memory); this is no point looping on transaction failure.
+ *
+ * See Also:
+ * ntdb_transaction_prepare_commit()
+ */
+enum NTDB_ERROR ntdb_transaction_commit(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_transaction_prepare_commit - prepare to commit a transaction
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This ensures we have the resources to commit a transaction (using
+ * ntdb_transaction_commit): if this succeeds then a transaction will only
+ * fail if the write() or fsync() calls fail.
+ *
+ * If this fails you must still call ntdb_transaction_cancel() to cancel
+ * the transaction.
+ *
+ * See Also:
+ * ntdb_transaction_commit()
+ */
+enum NTDB_ERROR ntdb_transaction_prepare_commit(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_traverse - traverse a NTDB
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @fn: the function to call for every key/value pair (or NULL)
+ * @p: the pointer to hand to @f
+ *
+ * This walks the NTDB until all they keys have been traversed, or @fn
+ * returns non-zero. If the traverse function or other processes are
+ * changing data or adding or deleting keys, the traverse may be
+ * unreliable: keys may be skipped or (rarely) visited twice.
+ *
+ * There is one specific exception: the special case of deleting the
+ * current key does not undermine the reliability of the traversal.
+ *
+ * On success, returns the number of keys iterated. On error returns
+ * a negative enum NTDB_ERROR value.
+ */
+#define ntdb_traverse(ntdb, fn, p) \
+ ntdb_traverse_(ntdb, typesafe_cb_preargs(int, void *, (fn), (p), \
+ struct ntdb_context *, \
+ NTDB_DATA, NTDB_DATA), (p))
+
+int64_t ntdb_traverse_(struct ntdb_context *ntdb,
+ int (*fn)(struct ntdb_context *,
+ NTDB_DATA, NTDB_DATA, void *), void *p);
+
+/**
+ * ntdb_parse_record - operate directly on data in the database.
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key whose record we should hand to @parse
+ * @parse: the function to call for the data
+ * @data: the private pointer to hand to @parse (types must match).
+ *
+ * This avoids a copy for many cases, by handing you a pointer into
+ * the memory-mapped database. It also locks the record to prevent
+ * other accesses at the same time.
+ *
+ * Do not alter the data handed to parse()!
+ */
+#define ntdb_parse_record(ntdb, key, parse, data) \
+ ntdb_parse_record_((ntdb), (key), \
+ typesafe_cb_preargs(enum NTDB_ERROR, void *, \
+ (parse), (data), \
+ NTDB_DATA, NTDB_DATA), (data))
+
+enum NTDB_ERROR ntdb_parse_record_(struct ntdb_context *ntdb,
+ NTDB_DATA key,
+ enum NTDB_ERROR (*parse)(NTDB_DATA k,
+ NTDB_DATA d,
+ void *data),
+ void *data);
+
+/**
+ * ntdb_get_seqnum - get a database sequence number
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This returns a sequence number: any change to the database from a
+ * ntdb context opened with the NTDB_SEQNUM flag will cause that number
+ * to increment. Note that the incrementing is unreliable (it is done
+ * without locking), so this is only useful as an optimization.
+ *
+ * For example, you may have a regular database backup routine which
+ * does not operate if the sequence number is unchanged. In the
+ * unlikely event of a failed increment, it will be backed up next
+ * time any way.
+ *
+ * Returns an enum NTDB_ERROR (ie. negative) on error.
+ */
+int64_t ntdb_get_seqnum(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_firstkey - get the "first" key in a NTDB
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: pointer to key.
+ *
+ * This returns an arbitrary key in the database; with ntdb_nextkey() it allows
+ * open-coded traversal of the database, though it is slightly less efficient
+ * than ntdb_traverse.
+ *
+ * It is your responsibility to free @key->dptr on success.
+ *
+ * Returns NTDB_ERR_NOEXIST if the database is empty.
+ */
+enum NTDB_ERROR ntdb_firstkey(struct ntdb_context *ntdb, NTDB_DATA *key);
+
+/**
+ * ntdb_nextkey - get the "next" key in a NTDB
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: a key returned by ntdb_firstkey() or ntdb_nextkey().
+ *
+ * This returns another key in the database; it will free @key.dptr for
+ * your convenience.
+ *
+ * Returns NTDB_ERR_NOEXIST if there are no more keys.
+ */
+enum NTDB_ERROR ntdb_nextkey(struct ntdb_context *ntdb, NTDB_DATA *key);
+
+/**
+ * ntdb_chainlock - lock a record in the NTDB
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key to lock.
+ *
+ * This prevents any access occurring to a group of keys including @key,
+ * even if @key does not exist. This allows primitive atomic updates of
+ * records without using transactions.
+ *
+ * You cannot begin a transaction while holding a ntdb_chainlock(), nor can
+ * you do any operations on any other keys in the database. This also means
+ * that you cannot hold more than one ntdb_chainlock() at a time.
+ *
+ * See Also:
+ * ntdb_chainunlock()
+ */
+enum NTDB_ERROR ntdb_chainlock(struct ntdb_context *ntdb, NTDB_DATA key);
+
+/**
+ * ntdb_chainunlock - unlock a record in the NTDB
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key to unlock.
+ *
+ * The key must have previously been locked by ntdb_chainlock().
+ */
+void ntdb_chainunlock(struct ntdb_context *ntdb, NTDB_DATA key);
+
+/**
+ * ntdb_chainlock_read - lock a record in the NTDB, for reading
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key to lock.
+ *
+ * This prevents any changes from occurring to a group of keys including @key,
+ * even if @key does not exist. This allows primitive atomic updates of
+ * records without using transactions.
+ *
+ * You cannot begin a transaction while holding a ntdb_chainlock_read(), nor can
+ * you do any operations on any other keys in the database. This also means
+ * that you cannot hold more than one ntdb_chainlock()/read() at a time.
+ *
+ * See Also:
+ * ntdb_chainlock()
+ */
+enum NTDB_ERROR ntdb_chainlock_read(struct ntdb_context *ntdb, NTDB_DATA key);
+
+/**
+ * ntdb_chainunlock_read - unlock a record in the NTDB for reading
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @key: the key to unlock.
+ *
+ * The key must have previously been locked by ntdb_chainlock_read().
+ */
+void ntdb_chainunlock_read(struct ntdb_context *ntdb, NTDB_DATA key);
+
+/**
+ * ntdb_lockall - lock the entire NTDB
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * You cannot hold a ntdb_chainlock while calling this. It nests, so you
+ * must call ntdb_unlockall as many times as you call ntdb_lockall.
+ */
+enum NTDB_ERROR ntdb_lockall(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_unlockall - unlock the entire NTDB
+ * @ntdb: the ntdb context returned from ntdb_open()
+ */
+void ntdb_unlockall(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_lockall_read - lock the entire NTDB for reading
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This prevents others writing to the database, eg. ntdb_delete, ntdb_store,
+ * ntdb_append, but not ntdb_fetch.
+ *
+ * You cannot hold a ntdb_chainlock while calling this. It nests, so you
+ * must call ntdb_unlockall_read as many times as you call ntdb_lockall_read.
+ */
+enum NTDB_ERROR ntdb_lockall_read(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_unlockall_read - unlock the entire NTDB for reading
+ * @ntdb: the ntdb context returned from ntdb_open()
+ */
+void ntdb_unlockall_read(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_wipe_all - wipe the database clean
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * Completely erase the database. This is faster than iterating through
+ * each key and doing ntdb_delete.
+ */
+enum NTDB_ERROR ntdb_wipe_all(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_repack - repack the database
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This repacks the database; if it is suffering from a great deal of
+ * fragmentation this might help. However, it can take twice the
+ * memory of the existing NTDB.
+ */
+enum NTDB_ERROR ntdb_repack(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_check - check a NTDB for consistency
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @check: function to check each key/data pair (or NULL)
+ * @data: argument for @check, must match type.
+ *
+ * This performs a consistency check of the open database, optionally calling
+ * a check() function on each record so you can do your own data consistency
+ * checks as well. If check() returns an error, that is returned from
+ * ntdb_check().
+ *
+ * Note that the NTDB uses a feature which we don't understand which
+ * indicates we can't run ntdb_check(), this will log a warning to that
+ * effect and return NTDB_SUCCESS. You can detect this condition by
+ * looking for NTDB_CANT_CHECK in ntdb_get_flags().
+ *
+ * Returns NTDB_SUCCESS or an error.
+ */
+#define ntdb_check(ntdb, check, data) \
+ ntdb_check_((ntdb), typesafe_cb_preargs(enum NTDB_ERROR, void *, \
+ (check), (data), \
+ NTDB_DATA, \
+ NTDB_DATA), \
+ (data))
+
+enum NTDB_ERROR ntdb_check_(struct ntdb_context *ntdb,
+ enum NTDB_ERROR (*check)(NTDB_DATA k,
+ NTDB_DATA d,
+ void *data),
+ void *data);
+
+/**
+ * ntdb_error - get the last error (not threadsafe)
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * Returns the last error returned by a NTDB function.
+ *
+ * This makes porting from TDB easier, but note that the last error is not
+ * reliable in threaded programs.
+ */
+enum NTDB_ERROR ntdb_error(struct ntdb_context *ntdb);
+
+/**
+ * enum ntdb_summary_flags - flags for ntdb_summary.
+ */
+enum ntdb_summary_flags {
+ NTDB_SUMMARY_HISTOGRAMS = 1 /* Draw graphs in the summary. */
+};
+
+/**
+ * ntdb_summary - return a string describing the NTDB state
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @flags: flags to control the summary output.
+ * @summary: pointer to string to allocate.
+ *
+ * This returns a developer-readable string describing the overall
+ * state of the ntdb, such as the percentage used and sizes of records.
+ * It is designed to provide information about the ntdb at a glance
+ * without displaying any keys or data in the database.
+ *
+ * On success, sets @summary to point to a malloc()'ed nul-terminated
+ * multi-line string. It is your responsibility to free() it.
+ */
+enum NTDB_ERROR ntdb_summary(struct ntdb_context *ntdb,
+ enum ntdb_summary_flags flags,
+ char **summary);
+
+
+/**
+ * ntdb_get_flags - return the flags for a ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This returns the flags on the current ntdb. Some of these are caused by
+ * the flags argument to ntdb_open(), others (such as NTDB_CONVERT) are
+ * intuited.
+ */
+unsigned int ntdb_get_flags(struct ntdb_context *ntdb);
+
+/**
+ * ntdb_add_flag - set a flag for a ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @flag: one of NTDB_NOLOCK, NTDB_NOMMAP, NTDB_NOSYNC or NTDB_ALLOW_NESTING.
+ *
+ * You can use this to set a flag on the NTDB. You cannot set these flags
+ * on a NTDB_INTERNAL ntdb.
+ */
+void ntdb_add_flag(struct ntdb_context *ntdb, unsigned flag);
+
+/**
+ * ntdb_remove_flag - unset a flag for a ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @flag: one of NTDB_NOLOCK, NTDB_NOMMAP, NTDB_NOSYNC or NTDB_ALLOW_NESTING.
+ *
+ * You can use this to clear a flag on the NTDB. You cannot clear flags
+ * on a NTDB_INTERNAL ntdb.
+ */
+void ntdb_remove_flag(struct ntdb_context *ntdb, unsigned flag);
+
+/**
+ * enum ntdb_attribute_type - descriminator for union ntdb_attribute.
+ */
+enum ntdb_attribute_type {
+ NTDB_ATTRIBUTE_LOG = 0,
+ NTDB_ATTRIBUTE_HASH = 1,
+ NTDB_ATTRIBUTE_SEED = 2,
+ NTDB_ATTRIBUTE_STATS = 3,
+ NTDB_ATTRIBUTE_OPENHOOK = 4,
+ NTDB_ATTRIBUTE_FLOCK = 5,
+};
+
+/**
+ * ntdb_get_attribute - get an attribute for an existing ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @attr: the union ntdb_attribute to set.
+ *
+ * This gets an attribute from a NTDB which has previously been set (or
+ * may return the default values). Set @attr.base.attr to the
+ * attribute type you want get.
+ */
+enum NTDB_ERROR ntdb_get_attribute(struct ntdb_context *ntdb,
+ union ntdb_attribute *attr);
+
+/**
+ * ntdb_set_attribute - set an attribute for an existing ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @attr: the union ntdb_attribute to set.
+ *
+ * This sets an attribute on a NTDB, overriding any previous attribute
+ * of the same type. It returns NTDB_ERR_EINVAL if the attribute is
+ * unknown or invalid.
+ *
+ * Note that NTDB_ATTRIBUTE_HASH, NTDB_ATTRIBUTE_SEED, and
+ * NTDB_ATTRIBUTE_OPENHOOK cannot currently be set after ntdb_open.
+ */
+enum NTDB_ERROR ntdb_set_attribute(struct ntdb_context *ntdb,
+ const union ntdb_attribute *attr);
+
+/**
+ * ntdb_unset_attribute - reset an attribute for an existing ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ * @type: the attribute type to unset.
+ *
+ * This unsets an attribute on a NTDB, returning it to the defaults
+ * (where applicable).
+ *
+ * Note that it only makes sense for NTDB_ATTRIBUTE_LOG and NTDB_ATTRIBUTE_FLOCK
+ * to be unset.
+ */
+void ntdb_unset_attribute(struct ntdb_context *ntdb,
+ enum ntdb_attribute_type type);
+
+/**
+ * ntdb_name - get the name of a ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This returns a copy of the name string, made at ntdb_open() time. If that
+ * argument was NULL (possible for a NTDB_INTERNAL db) this will return NULL.
+ *
+ * This is mostly useful for logging.
+ */
+const char *ntdb_name(const struct ntdb_context *ntdb);
+
+/**
+ * ntdb_fd - get the file descriptor of a ntdb
+ * @ntdb: the ntdb context returned from ntdb_open()
+ *
+ * This returns the file descriptor for the underlying database file, or -1
+ * for NTDB_INTERNAL.
+ */
+int ntdb_fd(const struct ntdb_context *ntdb);
+
+/**
+ * ntdb_foreach - iterate through every open NTDB.
+ * @fn: the function to call for every NTDB
+ * @p: the pointer to hand to @fn
+ *
+ * NTDB internally keeps track of all open TDBs; this function allows you to
+ * iterate through them. If @fn returns non-zero, traversal stops.
+ */
+#define ntdb_foreach(fn, p) \
+ ntdb_foreach_(typesafe_cb_preargs(int, void *, (fn), (p), \
+ struct ntdb_context *), (p))
+
+void ntdb_foreach_(int (*fn)(struct ntdb_context *, void *), void *p);
+
+/**
+ * struct ntdb_attribute_base - common fields for all ntdb attributes.
+ */
+struct ntdb_attribute_base {
+ enum ntdb_attribute_type attr;
+ union ntdb_attribute *next;
+};
+
+/**
+ * enum ntdb_log_level - log levels for ntdb_attribute_log
+ * @NTDB_LOG_ERROR: used to log unrecoverable errors such as I/O errors
+ * or internal consistency failures.
+ * @NTDB_LOG_USE_ERROR: used to log usage errors such as invalid parameters
+ * or writing to a read-only database.
+ * @NTDB_LOG_WARNING: used for informational messages on issues which
+ * are unusual but handled by NTDB internally, such
+ * as a failure to mmap or failure to open /dev/urandom.
+ */
+enum ntdb_log_level {
+ NTDB_LOG_ERROR,
+ NTDB_LOG_USE_ERROR,
+ NTDB_LOG_WARNING
+};
+
+/**
+ * struct ntdb_attribute_log - log function attribute
+ *
+ * This attribute provides a hook for you to log errors.
+ */
+struct ntdb_attribute_log {
+ struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_LOG */
+ void (*fn)(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
+ const char *message,
+ void *data);
+ void *data;
+};
+
+/**
+ * struct ntdb_attribute_hash - hash function attribute
+ *
+ * This attribute allows you to provide an alternative hash function.
+ * This hash function will be handed keys from the database; it will also
+ * be handed the 8-byte NTDB_HASH_MAGIC value for checking the header (the
+ * ntdb_open() will fail if the hash value doesn't match the header).
+ *
+ * Note that if your hash function gives different results on
+ * different machine endians, your ntdb will no longer work across
+ * different architectures!
+ */
+struct ntdb_attribute_hash {
+ struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_HASH */
+ uint64_t (*fn)(const void *key, size_t len, uint64_t seed,
+ void *data);
+ void *data;
+};
+
+/**
+ * struct ntdb_attribute_seed - hash function seed attribute
+ *
+ * The hash function seed is normally taken from /dev/urandom (or equivalent)
+ * but can be set manually here. This is mainly for testing purposes.
+ */
+struct ntdb_attribute_seed {
+ struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_SEED */
+ uint64_t seed;
+};
+
+/**
+ * struct ntdb_attribute_stats - ntdb operational statistics
+ *
+ * This attribute records statistics of various low-level NTDB operations.
+ * This can be used to assist performance evaluation. This is only
+ * useful for ntdb_get_attribute().
+ *
+ * New fields will be added at the end, hence the "size" argument which
+ * indicates how large your structure is: it must be filled in before
+ * calling ntdb_get_attribute(), which will overwrite it with the size
+ * ntdb knows about.
+ */
+struct ntdb_attribute_stats {
+ struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_STATS */
+ size_t size; /* = sizeof(struct ntdb_attribute_stats) */
+ uint64_t allocs;
+ uint64_t alloc_subhash;
+ uint64_t alloc_chain;
+ uint64_t alloc_bucket_exact;
+ uint64_t alloc_bucket_max;
+ uint64_t alloc_leftover;
+ uint64_t alloc_coalesce_tried;
+ uint64_t alloc_coalesce_iterate_clash;
+ uint64_t alloc_coalesce_lockfail;
+ uint64_t alloc_coalesce_race;
+ uint64_t alloc_coalesce_succeeded;
+ uint64_t alloc_coalesce_num_merged;
+ uint64_t compares;
+ uint64_t compare_wrong_bucket;
+ uint64_t compare_wrong_offsetbits;
+ uint64_t compare_wrong_keylen;
+ uint64_t compare_wrong_rechash;
+ uint64_t compare_wrong_keycmp;
+ uint64_t transactions;
+ uint64_t transaction_cancel;
+ uint64_t transaction_nest;
+ uint64_t transaction_expand_file;
+ uint64_t transaction_read_direct;
+ uint64_t transaction_read_direct_fail;
+ uint64_t transaction_write_direct;
+ uint64_t transaction_write_direct_fail;
+ uint64_t expands;
+ uint64_t frees;
+ uint64_t locks;
+ uint64_t lock_lowlevel;
+ uint64_t lock_nonblock;
+ uint64_t lock_nonblock_fail;
+};
+
+/**
+ * struct ntdb_attribute_openhook - ntdb special effects hook for open
+ *
+ * This attribute contains a function to call once we have the OPEN_LOCK
+ * for the ntdb, but before we've examined its contents. If this succeeds,
+ * the ntdb will be populated if it's then zero-length.
+ *
+ * This is a hack to allow support for TDB-style TDB_CLEAR_IF_FIRST
+ * behaviour.
+ */
+struct ntdb_attribute_openhook {
+ struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_OPENHOOK */
+ enum NTDB_ERROR (*fn)(int fd, void *data);
+ void *data;
+};
+
+/**
+ * struct ntdb_attribute_flock - ntdb special effects hook for file locking
+ *
+ * This attribute contains function to call to place locks on a file; it can
+ * be used to support non-blocking operations or lock proxying.
+ *
+ * They should return 0 on success, -1 on failure and set errno.
+ *
+ * An error will be logged on error if errno is neither EAGAIN nor EINTR
+ * (normally it would only return EAGAIN if waitflag is false, and
+ * loop internally on EINTR).
+ */
+struct ntdb_attribute_flock {
+ struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_FLOCK */
+ int (*lock)(int fd,int rw, off_t off, off_t len, bool waitflag, void *);
+ int (*unlock)(int fd, int rw, off_t off, off_t len, void *);
+ void *data;
+};
+
+/**
+ * union ntdb_attribute - ntdb attributes.
+ *
+ * This represents all the known attributes.
+ *
+ * See also:
+ * struct ntdb_attribute_log, struct ntdb_attribute_hash,
+ * struct ntdb_attribute_seed, struct ntdb_attribute_stats,
+ * struct ntdb_attribute_openhook, struct ntdb_attribute_flock.
+ */
+union ntdb_attribute {
+ struct ntdb_attribute_base base;
+ struct ntdb_attribute_log log;
+ struct ntdb_attribute_hash hash;
+ struct ntdb_attribute_seed seed;
+ struct ntdb_attribute_stats stats;
+ struct ntdb_attribute_openhook openhook;
+ struct ntdb_attribute_flock flock;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ntdb.h */
diff --git a/lib/tdb2/tdb.pc.in b/lib/ntdb/ntdb.pc.in
index 75e69d7363..36a7d5136c 100644
--- a/lib/tdb2/tdb.pc.in
+++ b/lib/ntdb/ntdb.pc.in
@@ -3,9 +3,9 @@ exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
-Name: tdb
-Description: A trivial database
+Name: ntdb
+Description: A (not-so) trivial database
Version: @PACKAGE_VERSION@
-Libs: @LIB_RPATH@ -L${libdir} -ltdb
+Libs: @LIB_RPATH@ -L${libdir} -lntdb
Cflags: -I${includedir}
URL: http://tdb.samba.org/
diff --git a/lib/ntdb/open.c b/lib/ntdb/open.c
new file mode 100644
index 0000000000..338de8be8c
--- /dev/null
+++ b/lib/ntdb/open.c
@@ -0,0 +1,768 @@
+ /*
+ Trivial Database 2: opening and closing TDBs
+ 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 "private.h"
+#include <ccan/build_assert/build_assert.h>
+#include <assert.h>
+
+/* all tdbs, to detect double-opens (fcntl file don't nest!) */
+static struct ntdb_context *tdbs = NULL;
+
+static struct ntdb_file *find_file(dev_t device, ino_t ino)
+{
+ struct ntdb_context *i;
+
+ for (i = tdbs; i; i = i->next) {
+ if (i->file->device == device && i->file->inode == ino) {
+ i->file->refcnt++;
+ return i->file;
+ }
+ }
+ return NULL;
+}
+
+static bool read_all(int fd, void *buf, size_t len)
+{
+ while (len) {
+ ssize_t ret;
+ ret = read(fd, buf, len);
+ if (ret < 0)
+ return false;
+ if (ret == 0) {
+ /* ETOOSHORT? */
+ errno = EWOULDBLOCK;
+ return false;
+ }
+ buf = (char *)buf + ret;
+ len -= ret;
+ }
+ return true;
+}
+
+static uint64_t random_number(struct ntdb_context *ntdb)
+{
+ int fd;
+ uint64_t ret = 0;
+ struct timeval now;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd >= 0) {
+ if (read_all(fd, &ret, sizeof(ret))) {
+ close(fd);
+ return ret;
+ }
+ close(fd);
+ }
+ /* FIXME: Untested! Based on Wikipedia protocol description! */
+ fd = open("/dev/egd-pool", O_RDWR);
+ if (fd >= 0) {
+ /* Command is 1, next byte is size we want to read. */
+ char cmd[2] = { 1, sizeof(uint64_t) };
+ if (write(fd, cmd, sizeof(cmd)) == sizeof(cmd)) {
+ char reply[1 + sizeof(uint64_t)];
+ int r = read(fd, reply, sizeof(reply));
+ if (r > 1) {
+ /* Copy at least some bytes. */
+ memcpy(&ret, reply+1, r - 1);
+ if (reply[0] == sizeof(uint64_t)
+ && r == sizeof(reply)) {
+ close(fd);
+ return ret;
+ }
+ }
+ }
+ close(fd);
+ }
+
+ /* Fallback: pid and time. */
+ gettimeofday(&now, NULL);
+ ret = getpid() * 100132289ULL + now.tv_sec * 1000000ULL + now.tv_usec;
+ ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
+ "ntdb_open: random from getpid and time");
+ return ret;
+}
+
+static void ntdb_context_init(struct ntdb_context *ntdb)
+{
+ /* Initialize the NTDB fields here */
+ ntdb_io_init(ntdb);
+ ntdb->direct_access = 0;
+ ntdb->transaction = NULL;
+ ntdb->access = NULL;
+}
+
+struct new_database {
+ struct ntdb_header hdr;
+ struct ntdb_freetable ftable;
+};
+
+/* initialise a new database */
+static enum NTDB_ERROR ntdb_new_database(struct ntdb_context *ntdb,
+ struct ntdb_attribute_seed *seed,
+ struct ntdb_header *hdr)
+{
+ /* We make it up in memory, then write it out if not internal */
+ struct new_database newdb;
+ unsigned int magic_len;
+ ssize_t rlen;
+ enum NTDB_ERROR ecode;
+
+ /* Fill in the header */
+ newdb.hdr.version = NTDB_VERSION;
+ if (seed)
+ newdb.hdr.hash_seed = seed->seed;
+ else
+ newdb.hdr.hash_seed = random_number(ntdb);
+ newdb.hdr.hash_test = NTDB_HASH_MAGIC;
+ newdb.hdr.hash_test = ntdb->hash_fn(&newdb.hdr.hash_test,
+ sizeof(newdb.hdr.hash_test),
+ newdb.hdr.hash_seed,
+ ntdb->hash_data);
+ newdb.hdr.recovery = 0;
+ newdb.hdr.features_used = newdb.hdr.features_offered = NTDB_FEATURE_MASK;
+ newdb.hdr.seqnum = 0;
+ newdb.hdr.capabilities = 0;
+ memset(newdb.hdr.reserved, 0, sizeof(newdb.hdr.reserved));
+ /* Initial hashes are empty. */
+ memset(newdb.hdr.hashtable, 0, sizeof(newdb.hdr.hashtable));
+
+ /* Free is empty. */
+ newdb.hdr.free_table = offsetof(struct new_database, ftable);
+ memset(&newdb.ftable, 0, sizeof(newdb.ftable));
+ ecode = set_header(NULL, &newdb.ftable.hdr, NTDB_FTABLE_MAGIC, 0,
+ sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
+ sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
+ 0);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* Magic food */
+ memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
+ strcpy(newdb.hdr.magic_food, NTDB_MAGIC_FOOD);
+
+ /* This creates an endian-converted database, as if read from disk */
+ magic_len = sizeof(newdb.hdr.magic_food);
+ ntdb_convert(ntdb,
+ (char *)&newdb.hdr + magic_len, sizeof(newdb) - magic_len);
+
+ *hdr = newdb.hdr;
+
+ if (ntdb->flags & NTDB_INTERNAL) {
+ ntdb->file->map_size = sizeof(newdb);
+ ntdb->file->map_ptr = malloc(ntdb->file->map_size);
+ if (!ntdb->file->map_ptr) {
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_new_database:"
+ " failed to allocate");
+ }
+ memcpy(ntdb->file->map_ptr, &newdb, ntdb->file->map_size);
+ return NTDB_SUCCESS;
+ }
+ if (lseek(ntdb->file->fd, 0, SEEK_SET) == -1) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_new_database:"
+ " failed to seek: %s", strerror(errno));
+ }
+
+ if (ftruncate(ntdb->file->fd, 0) == -1) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_new_database:"
+ " failed to truncate: %s", strerror(errno));
+ }
+
+ rlen = write(ntdb->file->fd, &newdb, sizeof(newdb));
+ if (rlen != sizeof(newdb)) {
+ if (rlen >= 0)
+ errno = ENOSPC;
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_new_database: %zi writing header: %s",
+ rlen, strerror(errno));
+ }
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR ntdb_new_file(struct ntdb_context *ntdb)
+{
+ ntdb->file = malloc(sizeof(*ntdb->file));
+ if (!ntdb->file)
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_open: cannot alloc ntdb_file structure");
+ ntdb->file->num_lockrecs = 0;
+ ntdb->file->lockrecs = NULL;
+ ntdb->file->allrecord_lock.count = 0;
+ ntdb->file->refcnt = 1;
+ ntdb->file->map_ptr = NULL;
+ return NTDB_SUCCESS;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_set_attribute(struct ntdb_context *ntdb,
+ const union ntdb_attribute *attr)
+{
+ switch (attr->base.attr) {
+ case NTDB_ATTRIBUTE_LOG:
+ ntdb->log_fn = attr->log.fn;
+ ntdb->log_data = attr->log.data;
+ break;
+ case NTDB_ATTRIBUTE_HASH:
+ case NTDB_ATTRIBUTE_SEED:
+ case NTDB_ATTRIBUTE_OPENHOOK:
+ return ntdb->last_error
+ = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_set_attribute:"
+ " cannot set %s after opening",
+ attr->base.attr == NTDB_ATTRIBUTE_HASH
+ ? "NTDB_ATTRIBUTE_HASH"
+ : attr->base.attr == NTDB_ATTRIBUTE_SEED
+ ? "NTDB_ATTRIBUTE_SEED"
+ : "NTDB_ATTRIBUTE_OPENHOOK");
+ case NTDB_ATTRIBUTE_STATS:
+ return ntdb->last_error
+ = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_set_attribute:"
+ " cannot set NTDB_ATTRIBUTE_STATS");
+ case NTDB_ATTRIBUTE_FLOCK:
+ ntdb->lock_fn = attr->flock.lock;
+ ntdb->unlock_fn = attr->flock.unlock;
+ ntdb->lock_data = attr->flock.data;
+ break;
+ default:
+ return ntdb->last_error
+ = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_set_attribute:"
+ " unknown attribute type %u",
+ attr->base.attr);
+ }
+ return NTDB_SUCCESS;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_get_attribute(struct ntdb_context *ntdb,
+ union ntdb_attribute *attr)
+{
+ switch (attr->base.attr) {
+ case NTDB_ATTRIBUTE_LOG:
+ if (!ntdb->log_fn)
+ return ntdb->last_error = NTDB_ERR_NOEXIST;
+ attr->log.fn = ntdb->log_fn;
+ attr->log.data = ntdb->log_data;
+ break;
+ case NTDB_ATTRIBUTE_HASH:
+ attr->hash.fn = ntdb->hash_fn;
+ attr->hash.data = ntdb->hash_data;
+ break;
+ case NTDB_ATTRIBUTE_SEED:
+ attr->seed.seed = ntdb->hash_seed;
+ break;
+ case NTDB_ATTRIBUTE_OPENHOOK:
+ if (!ntdb->openhook)
+ return ntdb->last_error = NTDB_ERR_NOEXIST;
+ attr->openhook.fn = ntdb->openhook;
+ attr->openhook.data = ntdb->openhook_data;
+ break;
+ case NTDB_ATTRIBUTE_STATS: {
+ size_t size = attr->stats.size;
+ if (size > ntdb->stats.size)
+ size = ntdb->stats.size;
+ memcpy(&attr->stats, &ntdb->stats, size);
+ break;
+ }
+ case NTDB_ATTRIBUTE_FLOCK:
+ attr->flock.lock = ntdb->lock_fn;
+ attr->flock.unlock = ntdb->unlock_fn;
+ attr->flock.data = ntdb->lock_data;
+ break;
+ default:
+ return ntdb->last_error
+ = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_get_attribute:"
+ " unknown attribute type %u",
+ attr->base.attr);
+ }
+ attr->base.next = NULL;
+ return NTDB_SUCCESS;
+}
+
+_PUBLIC_ void ntdb_unset_attribute(struct ntdb_context *ntdb,
+ enum ntdb_attribute_type type)
+{
+ switch (type) {
+ case NTDB_ATTRIBUTE_LOG:
+ ntdb->log_fn = NULL;
+ break;
+ case NTDB_ATTRIBUTE_OPENHOOK:
+ ntdb->openhook = NULL;
+ break;
+ case NTDB_ATTRIBUTE_HASH:
+ case NTDB_ATTRIBUTE_SEED:
+ ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
+ "ntdb_unset_attribute: cannot unset %s after opening",
+ type == NTDB_ATTRIBUTE_HASH
+ ? "NTDB_ATTRIBUTE_HASH"
+ : "NTDB_ATTRIBUTE_SEED");
+ break;
+ case NTDB_ATTRIBUTE_STATS:
+ ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_unset_attribute:"
+ "cannot unset NTDB_ATTRIBUTE_STATS");
+ break;
+ case NTDB_ATTRIBUTE_FLOCK:
+ ntdb->lock_fn = ntdb_fcntl_lock;
+ ntdb->unlock_fn = ntdb_fcntl_unlock;
+ break;
+ default:
+ ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_unset_attribute: unknown attribute type %u",
+ type);
+ }
+}
+
+/* The top three bits of the capability tell us whether it matters. */
+enum NTDB_ERROR unknown_capability(struct ntdb_context *ntdb, const char *caller,
+ ntdb_off_t type)
+{
+ if (type & NTDB_CAP_NOOPEN) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "%s: file has unknown capability %llu",
+ caller, type & NTDB_CAP_NOOPEN);
+ }
+
+ if ((type & NTDB_CAP_NOWRITE) && !(ntdb->flags & NTDB_RDONLY)) {
+ return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_ERROR,
+ "%s: file has unknown capability %llu"
+ " (cannot write to it)",
+ caller, type & NTDB_CAP_NOOPEN);
+ }
+
+ if (type & NTDB_CAP_NOCHECK) {
+ ntdb->flags |= NTDB_CANT_CHECK;
+ }
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR capabilities_ok(struct ntdb_context *ntdb,
+ ntdb_off_t capabilities)
+{
+ ntdb_off_t off, next;
+ enum NTDB_ERROR ecode = NTDB_SUCCESS;
+ const struct ntdb_capability *cap;
+
+ /* Check capability list. */
+ for (off = capabilities; off && ecode == NTDB_SUCCESS; off = next) {
+ cap = ntdb_access_read(ntdb, off, sizeof(*cap), true);
+ if (NTDB_PTR_IS_ERR(cap)) {
+ return NTDB_PTR_ERR(cap);
+ }
+
+ switch (cap->type & NTDB_CAP_TYPE_MASK) {
+ /* We don't understand any capabilities (yet). */
+ default:
+ ecode = unknown_capability(ntdb, "ntdb_open", cap->type);
+ }
+ next = cap->next;
+ ntdb_access_release(ntdb, cap);
+ }
+ return ecode;
+}
+
+_PUBLIC_ struct ntdb_context *ntdb_open(const char *name, int ntdb_flags,
+ int open_flags, mode_t mode,
+ union ntdb_attribute *attr)
+{
+ struct ntdb_context *ntdb;
+ struct stat st;
+ int saved_errno = 0;
+ uint64_t hash_test;
+ unsigned v;
+ ssize_t rlen;
+ struct ntdb_header hdr;
+ struct ntdb_attribute_seed *seed = NULL;
+ ntdb_bool_err berr;
+ enum NTDB_ERROR ecode;
+ int openlock;
+
+ ntdb = malloc(sizeof(*ntdb) + (name ? strlen(name) + 1 : 0));
+ if (!ntdb) {
+ /* Can't log this */
+ errno = ENOMEM;
+ return NULL;
+ }
+ /* Set name immediately for logging functions. */
+ if (name) {
+ ntdb->name = strcpy((char *)(ntdb + 1), name);
+ } else {
+ ntdb->name = NULL;
+ }
+ ntdb->flags = ntdb_flags;
+ ntdb->log_fn = NULL;
+ ntdb->open_flags = open_flags;
+ ntdb->last_error = NTDB_SUCCESS;
+ ntdb->file = NULL;
+ ntdb->openhook = NULL;
+ ntdb->lock_fn = ntdb_fcntl_lock;
+ ntdb->unlock_fn = ntdb_fcntl_unlock;
+ ntdb->hash_fn = ntdb_jenkins_hash;
+ memset(&ntdb->stats, 0, sizeof(ntdb->stats));
+ ntdb->stats.base.attr = NTDB_ATTRIBUTE_STATS;
+ ntdb->stats.size = sizeof(ntdb->stats);
+
+ while (attr) {
+ switch (attr->base.attr) {
+ case NTDB_ATTRIBUTE_HASH:
+ ntdb->hash_fn = attr->hash.fn;
+ ntdb->hash_data = attr->hash.data;
+ break;
+ case NTDB_ATTRIBUTE_SEED:
+ seed = &attr->seed;
+ break;
+ case NTDB_ATTRIBUTE_OPENHOOK:
+ ntdb->openhook = attr->openhook.fn;
+ ntdb->openhook_data = attr->openhook.data;
+ break;
+ default:
+ /* These are set as normal. */
+ ecode = ntdb_set_attribute(ntdb, attr);
+ if (ecode != NTDB_SUCCESS)
+ goto fail;
+ }
+ attr = attr->base.next;
+ }
+
+ if (ntdb_flags & ~(NTDB_INTERNAL | NTDB_NOLOCK | NTDB_NOMMAP | NTDB_CONVERT
+ | NTDB_NOSYNC | NTDB_SEQNUM | NTDB_ALLOW_NESTING
+ | NTDB_RDONLY)) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
+ "ntdb_open: unknown flags %u", ntdb_flags);
+ goto fail;
+ }
+
+ if (seed) {
+ if (!(ntdb_flags & NTDB_INTERNAL) && !(open_flags & O_CREAT)) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_open:"
+ " cannot set NTDB_ATTRIBUTE_SEED"
+ " without O_CREAT.");
+ goto fail;
+ }
+ }
+
+ if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
+ "ntdb_open: can't open ntdb %s write-only",
+ name);
+ goto fail;
+ }
+
+ if ((open_flags & O_ACCMODE) == O_RDONLY) {
+ openlock = F_RDLCK;
+ ntdb->flags |= NTDB_RDONLY;
+ } else {
+ if (ntdb_flags & NTDB_RDONLY) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_open: can't use NTDB_RDONLY"
+ " without O_RDONLY");
+ goto fail;
+ }
+ openlock = F_WRLCK;
+ }
+
+ /* internal databases don't need any of the rest. */
+ if (ntdb->flags & NTDB_INTERNAL) {
+ ntdb->flags |= (NTDB_NOLOCK | NTDB_NOMMAP);
+ ecode = ntdb_new_file(ntdb);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ ntdb->file->fd = -1;
+ ecode = ntdb_new_database(ntdb, seed, &hdr);
+ if (ecode == NTDB_SUCCESS) {
+ ntdb_convert(ntdb, &hdr.hash_seed,
+ sizeof(hdr.hash_seed));
+ ntdb->hash_seed = hdr.hash_seed;
+ ntdb_context_init(ntdb);
+ ntdb_ftable_init(ntdb);
+ }
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ return ntdb;
+ }
+
+ if (stat(name, &st) != -1)
+ ntdb->file = find_file(st.st_dev, st.st_ino);
+
+ if (!ntdb->file) {
+ int fd;
+
+ if ((fd = open(name, open_flags, mode)) == -1) {
+ /* errno set by open(2) */
+ saved_errno = errno;
+ ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open: could not open file %s: %s",
+ name, strerror(errno));
+ goto fail_errno;
+ }
+
+ /* on exec, don't inherit the fd */
+ v = fcntl(fd, F_GETFD, 0);
+ fcntl(fd, F_SETFD, v | FD_CLOEXEC);
+
+ if (fstat(fd, &st) == -1) {
+ saved_errno = errno;
+ ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open: could not stat open %s: %s",
+ name, strerror(errno));
+ close(fd);
+ goto fail_errno;
+ }
+
+ ecode = ntdb_new_file(ntdb);
+ if (ecode != NTDB_SUCCESS) {
+ close(fd);
+ goto fail;
+ }
+
+ ntdb->file->fd = fd;
+ ntdb->file->device = st.st_dev;
+ ntdb->file->inode = st.st_ino;
+ ntdb->file->map_ptr = NULL;
+ ntdb->file->map_size = 0;
+ }
+
+ /* ensure there is only one process initialising at once */
+ ecode = ntdb_lock_open(ntdb, openlock, NTDB_LOCK_WAIT|NTDB_LOCK_NOCHECK);
+ if (ecode != NTDB_SUCCESS) {
+ saved_errno = errno;
+ goto fail_errno;
+ }
+
+ /* call their open hook if they gave us one. */
+ if (ntdb->openhook) {
+ ecode = ntdb->openhook(ntdb->file->fd, ntdb->openhook_data);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_open: open hook failed");
+ goto fail;
+ }
+ open_flags |= O_CREAT;
+ }
+
+ /* If they used O_TRUNC, read will return 0. */
+ rlen = pread(ntdb->file->fd, &hdr, sizeof(hdr), 0);
+ if (rlen == 0 && (open_flags & O_CREAT)) {
+ ecode = ntdb_new_database(ntdb, seed, &hdr);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ } else if (rlen < 0) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open: error %s reading %s",
+ strerror(errno), name);
+ goto fail;
+ } else if (rlen < sizeof(hdr)
+ || strcmp(hdr.magic_food, NTDB_MAGIC_FOOD) != 0) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open: %s is not a ntdb file", name);
+ goto fail;
+ }
+
+ if (hdr.version != NTDB_VERSION) {
+ if (hdr.version == bswap_64(NTDB_VERSION))
+ ntdb->flags |= NTDB_CONVERT;
+ else {
+ /* wrong version */
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open:"
+ " %s is unknown version 0x%llx",
+ name, (long long)hdr.version);
+ goto fail;
+ }
+ } else if (ntdb->flags & NTDB_CONVERT) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open:"
+ " %s does not need NTDB_CONVERT",
+ name);
+ goto fail;
+ }
+
+ ntdb_context_init(ntdb);
+
+ ntdb_convert(ntdb, &hdr, sizeof(hdr));
+ ntdb->hash_seed = hdr.hash_seed;
+ hash_test = NTDB_HASH_MAGIC;
+ hash_test = ntdb_hash(ntdb, &hash_test, sizeof(hash_test));
+ if (hdr.hash_test != hash_test) {
+ /* wrong hash variant */
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open:"
+ " %s uses a different hash function",
+ name);
+ goto fail;
+ }
+
+ ecode = capabilities_ok(ntdb, hdr.capabilities);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+
+ /* Clear any features we don't understand. */
+ if ((open_flags & O_ACCMODE) != O_RDONLY) {
+ hdr.features_used &= NTDB_FEATURE_MASK;
+ ecode = ntdb_write_convert(ntdb, offsetof(struct ntdb_header,
+ features_used),
+ &hdr.features_used,
+ sizeof(hdr.features_used));
+ if (ecode != NTDB_SUCCESS)
+ goto fail;
+ }
+
+ ntdb_unlock_open(ntdb, openlock);
+
+ /* This makes sure we have current map_size and mmap. */
+ ecode = ntdb->io->oob(ntdb, ntdb->file->map_size, 1, true);
+ if (unlikely(ecode != NTDB_SUCCESS))
+ goto fail;
+
+ /* Now it's fully formed, recover if necessary. */
+ berr = ntdb_needs_recovery(ntdb);
+ if (unlikely(berr != false)) {
+ if (berr < 0) {
+ ecode = NTDB_OFF_TO_ERR(berr);
+ goto fail;
+ }
+ ecode = ntdb_lock_and_recover(ntdb);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ }
+
+ ecode = ntdb_ftable_init(ntdb);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+
+ ntdb->next = tdbs;
+ tdbs = ntdb;
+ return ntdb;
+
+ fail:
+ /* Map ecode to some logical errno. */
+ switch (NTDB_ERR_TO_OFF(ecode)) {
+ case NTDB_ERR_TO_OFF(NTDB_ERR_CORRUPT):
+ case NTDB_ERR_TO_OFF(NTDB_ERR_IO):
+ saved_errno = EIO;
+ break;
+ case NTDB_ERR_TO_OFF(NTDB_ERR_LOCK):
+ saved_errno = EWOULDBLOCK;
+ break;
+ case NTDB_ERR_TO_OFF(NTDB_ERR_OOM):
+ saved_errno = ENOMEM;
+ break;
+ case NTDB_ERR_TO_OFF(NTDB_ERR_EINVAL):
+ saved_errno = EINVAL;
+ break;
+ default:
+ saved_errno = EINVAL;
+ break;
+ }
+
+fail_errno:
+#ifdef NTDB_TRACE
+ close(ntdb->tracefd);
+#endif
+ if (ntdb->file) {
+ ntdb_lock_cleanup(ntdb);
+ if (--ntdb->file->refcnt == 0) {
+ assert(ntdb->file->num_lockrecs == 0);
+ if (ntdb->file->map_ptr) {
+ if (ntdb->flags & NTDB_INTERNAL) {
+ free(ntdb->file->map_ptr);
+ } else
+ ntdb_munmap(ntdb->file);
+ }
+ if (close(ntdb->file->fd) != 0)
+ ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_open: failed to close ntdb fd"
+ " on error: %s", strerror(errno));
+ free(ntdb->file->lockrecs);
+ free(ntdb->file);
+ }
+ }
+
+ free(ntdb);
+ errno = saved_errno;
+ return NULL;
+}
+
+_PUBLIC_ int ntdb_close(struct ntdb_context *ntdb)
+{
+ int ret = 0;
+ struct ntdb_context **i;
+
+ ntdb_trace(ntdb, "ntdb_close");
+
+ if (ntdb->transaction) {
+ ntdb_transaction_cancel(ntdb);
+ }
+
+ if (ntdb->file->map_ptr) {
+ if (ntdb->flags & NTDB_INTERNAL)
+ free(ntdb->file->map_ptr);
+ else
+ ntdb_munmap(ntdb->file);
+ }
+ if (ntdb->file) {
+ ntdb_lock_cleanup(ntdb);
+ if (--ntdb->file->refcnt == 0) {
+ ret = close(ntdb->file->fd);
+ free(ntdb->file->lockrecs);
+ free(ntdb->file);
+ }
+ }
+
+ /* Remove from tdbs list */
+ for (i = &tdbs; *i; i = &(*i)->next) {
+ if (*i == ntdb) {
+ *i = ntdb->next;
+ break;
+ }
+ }
+
+#ifdef NTDB_TRACE
+ close(ntdb->tracefd);
+#endif
+ free(ntdb);
+
+ return ret;
+}
+
+_PUBLIC_ void ntdb_foreach_(int (*fn)(struct ntdb_context *, void *), void *p)
+{
+ struct ntdb_context *i;
+
+ for (i = tdbs; i; i = i->next) {
+ if (fn(i, p) != 0)
+ break;
+ }
+}
diff --git a/lib/ntdb/private.h b/lib/ntdb/private.h
new file mode 100644
index 0000000000..1cf9b7aca4
--- /dev/null
+++ b/lib/ntdb/private.h
@@ -0,0 +1,657 @@
+#ifndef NTDB_PRIVATE_H
+#define NTDB_PRIVATE_H
+/*
+ Trivial Database 2: private types and prototypes
+ 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 "config.h"
+#ifndef HAVE_CCAN
+#error You need ccan to build ntdb!
+#endif
+#include "ntdb.h"
+#include <ccan/compiler/compiler.h>
+#include <ccan/likely/likely.h>
+#include <ccan/endian/endian.h>
+
+#ifdef HAVE_LIBREPLACE
+#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 <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <utime.h>
+#include <unistd.h>
+#endif
+
+#ifndef TEST_IT
+#define TEST_IT(cond)
+#endif
+
+/* #define NTDB_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
+
+typedef uint64_t ntdb_len_t;
+typedef uint64_t ntdb_off_t;
+
+#define NTDB_MAGIC_FOOD "NTDB file\n"
+#define NTDB_VERSION ((uint64_t)(0x26011967 + 7))
+#define NTDB_USED_MAGIC ((uint64_t)0x1999)
+#define NTDB_HTABLE_MAGIC ((uint64_t)0x1888)
+#define NTDB_CHAIN_MAGIC ((uint64_t)0x1777)
+#define NTDB_FTABLE_MAGIC ((uint64_t)0x1666)
+#define NTDB_CAP_MAGIC ((uint64_t)0x1555)
+#define NTDB_FREE_MAGIC ((uint64_t)0xFE)
+#define NTDB_HASH_MAGIC (0xA1ABE11A01092008ULL)
+#define NTDB_RECOVERY_MAGIC (0xf53bc0e7ad124589ULL)
+#define NTDB_RECOVERY_INVALID_MAGIC (0x0ULL)
+
+/* Capability bits. */
+#define NTDB_CAP_TYPE_MASK 0x1FFFFFFFFFFFFFFFULL
+#define NTDB_CAP_NOCHECK 0x8000000000000000ULL
+#define NTDB_CAP_NOWRITE 0x4000000000000000ULL
+#define NTDB_CAP_NOOPEN 0x2000000000000000ULL
+
+#define NTDB_OFF_IS_ERR(off) unlikely(off >= (ntdb_off_t)(long)NTDB_ERR_LAST)
+#define NTDB_OFF_TO_ERR(off) ((enum NTDB_ERROR)(long)(off))
+#define NTDB_ERR_TO_OFF(ecode) ((ntdb_off_t)(long)(ecode))
+
+/* Packing errors into pointers and v.v. */
+#define NTDB_PTR_IS_ERR(ptr) \
+ unlikely((unsigned long)(ptr) >= (unsigned long)NTDB_ERR_LAST)
+#define NTDB_PTR_ERR(p) ((enum NTDB_ERROR)(long)(p))
+#define NTDB_ERR_PTR(err) ((void *)(long)(err))
+
+/* Common case of returning true, false or -ve error. */
+typedef int ntdb_bool_err;
+
+/* Prevent others from opening the file. */
+#define NTDB_OPEN_LOCK 0
+/* Expanding file. */
+#define NTDB_EXPANSION_LOCK 2
+/* Doing a transaction. */
+#define NTDB_TRANSACTION_LOCK 8
+/* Hash chain locks. */
+#define NTDB_HASH_LOCK_START 64
+
+/* Range for hash locks. */
+#define NTDB_HASH_LOCK_RANGE_BITS 30
+#define NTDB_HASH_LOCK_RANGE (1 << NTDB_HASH_LOCK_RANGE_BITS)
+
+/* We have 1024 entries in the top level. */
+#define NTDB_TOPLEVEL_HASH_BITS 10
+/* And 64 entries in each sub-level: thus 64 bits exactly after 9 levels. */
+#define NTDB_SUBLEVEL_HASH_BITS 6
+/* And 8 entries in each group, ie 8 groups per sublevel. */
+#define NTDB_HASH_GROUP_BITS 3
+/* This is currently 10: beyond this we chain. */
+#define NTDB_MAX_LEVELS (1+(64-NTDB_TOPLEVEL_HASH_BITS) / NTDB_SUBLEVEL_HASH_BITS)
+
+/* Extend file by least 100 times larger than needed. */
+#define NTDB_EXTENSION_FACTOR 100
+
+/* We steal bits from the offsets to store hash info. */
+#define NTDB_OFF_HASH_GROUP_MASK ((1ULL << NTDB_HASH_GROUP_BITS) - 1)
+/* We steal this many upper bits, giving a maximum offset of 64 exabytes. */
+#define NTDB_OFF_UPPER_STEAL 8
+#define NTDB_OFF_UPPER_STEAL_EXTRA 7
+/* The bit number where we store extra hash bits. */
+#define NTDB_OFF_HASH_EXTRA_BIT 57
+#define NTDB_OFF_UPPER_STEAL_SUBHASH_BIT 56
+
+/* Additional features we understand. Currently: none. */
+#define NTDB_FEATURE_MASK ((uint64_t)0)
+
+/* The bit number where we store the extra hash bits. */
+/* Convenience mask to get actual offset. */
+#define NTDB_OFF_MASK \
+ (((1ULL << (64 - NTDB_OFF_UPPER_STEAL)) - 1) - NTDB_OFF_HASH_GROUP_MASK)
+
+/* How many buckets in a free list: see size_to_bucket(). */
+#define NTDB_FREE_BUCKETS (64 - NTDB_OFF_UPPER_STEAL)
+
+/* We have to be able to fit a free record here. */
+#define NTDB_MIN_DATA_LEN \
+ (sizeof(struct ntdb_free_record) - sizeof(struct ntdb_used_record))
+
+/* Indicates this entry is not on an flist (can happen during coalescing) */
+#define NTDB_FTABLE_NONE ((1ULL << NTDB_OFF_UPPER_STEAL) - 1)
+
+struct ntdb_used_record {
+ /* For on-disk compatibility, we avoid bitfields:
+ magic: 16, (highest)
+ key_len_bits: 5,
+ extra_padding: 32
+ hash_bits: 11
+ */
+ uint64_t magic_and_meta;
+ /* The bottom key_len_bits*2 are key length, rest is data length. */
+ uint64_t key_and_data_len;
+};
+
+static inline unsigned rec_key_bits(const struct ntdb_used_record *r)
+{
+ return ((r->magic_and_meta >> 43) & ((1 << 5)-1)) * 2;
+}
+
+static inline uint64_t rec_key_length(const struct ntdb_used_record *r)
+{
+ return r->key_and_data_len & ((1ULL << rec_key_bits(r)) - 1);
+}
+
+static inline uint64_t rec_data_length(const struct ntdb_used_record *r)
+{
+ return r->key_and_data_len >> rec_key_bits(r);
+}
+
+static inline uint64_t rec_extra_padding(const struct ntdb_used_record *r)
+{
+ return (r->magic_and_meta >> 11) & 0xFFFFFFFF;
+}
+
+static inline uint32_t rec_hash(const struct ntdb_used_record *r)
+{
+ return r->magic_and_meta & ((1 << 11) - 1);
+}
+
+static inline uint16_t rec_magic(const struct ntdb_used_record *r)
+{
+ return (r->magic_and_meta >> 48);
+}
+
+struct ntdb_free_record {
+ uint64_t magic_and_prev; /* NTDB_OFF_UPPER_STEAL bits magic, then prev */
+ uint64_t ftable_and_len; /* Len not counting these two fields. */
+ /* This is why the minimum record size is 8 bytes. */
+ uint64_t next;
+};
+
+static inline uint64_t frec_prev(const struct ntdb_free_record *f)
+{
+ return f->magic_and_prev & ((1ULL << (64 - NTDB_OFF_UPPER_STEAL)) - 1);
+}
+
+static inline uint64_t frec_magic(const struct ntdb_free_record *f)
+{
+ return f->magic_and_prev >> (64 - NTDB_OFF_UPPER_STEAL);
+}
+
+static inline uint64_t frec_len(const struct ntdb_free_record *f)
+{
+ return f->ftable_and_len & ((1ULL << (64 - NTDB_OFF_UPPER_STEAL))-1);
+}
+
+static inline unsigned frec_ftable(const struct ntdb_free_record *f)
+{
+ return f->ftable_and_len >> (64 - NTDB_OFF_UPPER_STEAL);
+}
+
+struct ntdb_recovery_record {
+ uint64_t magic;
+ /* Length of record (add this header to get total length). */
+ uint64_t max_len;
+ /* Length used. */
+ uint64_t len;
+ /* Old length of file before transaction. */
+ uint64_t eof;
+};
+
+/* If we bottom out of the subhashes, we chain. */
+struct ntdb_chain {
+ ntdb_off_t rec[1 << NTDB_HASH_GROUP_BITS];
+ ntdb_off_t next;
+};
+
+/* this is stored at the front of every database */
+struct ntdb_header {
+ char magic_food[64]; /* for /etc/magic */
+ /* FIXME: Make me 32 bit? */
+ uint64_t version; /* version of the code */
+ uint64_t hash_test; /* result of hashing HASH_MAGIC. */
+ uint64_t hash_seed; /* "random" seed written at creation time. */
+ ntdb_off_t free_table; /* (First) free table. */
+ ntdb_off_t recovery; /* Transaction recovery area. */
+
+ uint64_t features_used; /* Features all writers understand */
+ uint64_t features_offered; /* Features offered */
+
+ uint64_t seqnum; /* Sequence number for NTDB_SEQNUM */
+
+ ntdb_off_t capabilities; /* Optional linked list of capabilities. */
+ ntdb_off_t reserved[22];
+
+ /* Top level hash table. */
+ ntdb_off_t hashtable[1ULL << NTDB_TOPLEVEL_HASH_BITS];
+};
+
+struct ntdb_freetable {
+ struct ntdb_used_record hdr;
+ ntdb_off_t next;
+ ntdb_off_t buckets[NTDB_FREE_BUCKETS];
+};
+
+struct ntdb_capability {
+ struct ntdb_used_record hdr;
+ ntdb_off_t type;
+ ntdb_off_t next;
+ /* ... */
+};
+
+/* Information about a particular (locked) hash entry. */
+struct hash_info {
+ /* Full hash value of entry. */
+ uint64_t h;
+ /* Start and length of lock acquired. */
+ ntdb_off_t hlock_start;
+ ntdb_len_t hlock_range;
+ /* Start of hash group. */
+ ntdb_off_t group_start;
+ /* Bucket we belong in. */
+ unsigned int home_bucket;
+ /* Bucket we (or an empty space) were found in. */
+ unsigned int found_bucket;
+ /* How many bits of the hash are already used. */
+ unsigned int hash_used;
+ /* Current working group. */
+ ntdb_off_t group[1 << NTDB_HASH_GROUP_BITS];
+};
+
+struct traverse_info {
+ struct traverse_level {
+ ntdb_off_t hashtable;
+ /* We ignore groups here, and treat it as a big array. */
+ unsigned entry;
+ unsigned int total_buckets;
+ } levels[NTDB_MAX_LEVELS + 1];
+ unsigned int num_levels;
+ unsigned int toplevel_group;
+ /* This makes delete-everything-inside-traverse work as expected. */
+ ntdb_off_t prev;
+};
+
+enum ntdb_lock_flags {
+ /* WAIT == F_SETLKW, NOWAIT == F_SETLK */
+ NTDB_LOCK_NOWAIT = 0,
+ NTDB_LOCK_WAIT = 1,
+ /* If set, don't log an error on failure. */
+ NTDB_LOCK_PROBE = 2,
+ /* If set, don't check for recovery (used by recovery code). */
+ NTDB_LOCK_NOCHECK = 4,
+};
+
+struct ntdb_lock {
+ struct ntdb_context *owner;
+ off_t off;
+ uint32_t count;
+ uint32_t ltype;
+};
+
+/* This is only needed for ntdb_access_commit, but used everywhere to
+ * simplify. */
+struct ntdb_access_hdr {
+ struct ntdb_access_hdr *next;
+ ntdb_off_t off;
+ ntdb_len_t len;
+ bool convert;
+};
+
+struct ntdb_file {
+ /* How many are sharing us? */
+ unsigned int refcnt;
+
+ /* Mmap (if any), or malloc (for NTDB_INTERNAL). */
+ void *map_ptr;
+
+ /* How much space has been mapped (<= current file size) */
+ ntdb_len_t map_size;
+
+ /* The file descriptor (-1 for NTDB_INTERNAL). */
+ int fd;
+
+ /* Lock information */
+ pid_t locker;
+ struct ntdb_lock allrecord_lock;
+ size_t num_lockrecs;
+ struct ntdb_lock *lockrecs;
+
+ /* Identity of this file. */
+ dev_t device;
+ ino_t inode;
+};
+
+struct ntdb_methods {
+ enum NTDB_ERROR (*tread)(struct ntdb_context *, ntdb_off_t, void *,
+ ntdb_len_t);
+ enum NTDB_ERROR (*twrite)(struct ntdb_context *, ntdb_off_t, const void *,
+ ntdb_len_t);
+ enum NTDB_ERROR (*oob)(struct ntdb_context *, ntdb_off_t, ntdb_len_t, bool);
+ enum NTDB_ERROR (*expand_file)(struct ntdb_context *, ntdb_len_t);
+ void *(*direct)(struct ntdb_context *, ntdb_off_t, size_t, bool);
+};
+
+/*
+ internal prototypes
+*/
+/* hash.c: */
+uint64_t ntdb_jenkins_hash(const void *key, size_t length, uint64_t seed,
+ void *unused);
+
+enum NTDB_ERROR first_in_hash(struct ntdb_context *ntdb,
+ struct traverse_info *tinfo,
+ NTDB_DATA *kbuf, size_t *dlen);
+
+enum NTDB_ERROR next_in_hash(struct ntdb_context *ntdb,
+ struct traverse_info *tinfo,
+ NTDB_DATA *kbuf, size_t *dlen);
+
+/* Hash random memory. */
+uint64_t ntdb_hash(struct ntdb_context *ntdb, const void *ptr, size_t len);
+
+/* Hash on disk. */
+uint64_t hash_record(struct ntdb_context *ntdb, ntdb_off_t off);
+
+/* Find and lock a hash entry (or where it would be). */
+ntdb_off_t find_and_lock(struct ntdb_context *ntdb,
+ NTDB_DATA key,
+ int ltype,
+ struct hash_info *h,
+ struct ntdb_used_record *rec,
+ struct traverse_info *tinfo);
+
+enum NTDB_ERROR replace_in_hash(struct ntdb_context *ntdb,
+ struct hash_info *h,
+ ntdb_off_t new_off);
+
+enum NTDB_ERROR add_to_hash(struct ntdb_context *ntdb, struct hash_info *h,
+ ntdb_off_t new_off);
+
+enum NTDB_ERROR delete_from_hash(struct ntdb_context *ntdb, struct hash_info *h);
+
+/* For ntdb_check */
+bool is_subhash(ntdb_off_t val);
+enum NTDB_ERROR unknown_capability(struct ntdb_context *ntdb, const char *caller,
+ ntdb_off_t type);
+
+/* free.c: */
+enum NTDB_ERROR ntdb_ftable_init(struct ntdb_context *ntdb);
+
+/* check.c needs these to iterate through free lists. */
+ntdb_off_t first_ftable(struct ntdb_context *ntdb);
+ntdb_off_t next_ftable(struct ntdb_context *ntdb, ntdb_off_t ftable);
+
+/* This returns space or -ve error number. */
+ntdb_off_t alloc(struct ntdb_context *ntdb, size_t keylen, size_t datalen,
+ uint64_t hash, unsigned magic, bool growing);
+
+/* Put this record in a free list. */
+enum NTDB_ERROR add_free_record(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len_with_header,
+ enum ntdb_lock_flags waitflag,
+ bool coalesce_ok);
+
+/* Set up header for a used/ftable/htable/chain/capability record. */
+enum NTDB_ERROR set_header(struct ntdb_context *ntdb,
+ struct ntdb_used_record *rec,
+ unsigned magic, uint64_t keylen, uint64_t datalen,
+ uint64_t actuallen, unsigned hashlow);
+
+/* Used by ntdb_check to verify. */
+unsigned int size_to_bucket(ntdb_len_t data_len);
+ntdb_off_t bucket_off(ntdb_off_t ftable_off, unsigned bucket);
+
+/* Used by ntdb_summary */
+ntdb_off_t dead_space(struct ntdb_context *ntdb, ntdb_off_t off);
+
+/* Adjust expansion, used by create_recovery_area */
+ntdb_off_t ntdb_expand_adjust(ntdb_off_t map_size, ntdb_off_t size);
+
+/* io.c: */
+/* Initialize ntdb->methods. */
+void ntdb_io_init(struct ntdb_context *ntdb);
+
+/* Convert endian of the buffer if required. */
+void *ntdb_convert(const struct ntdb_context *ntdb, void *buf, ntdb_len_t size);
+
+/* Unmap and try to map the ntdb. */
+void ntdb_munmap(struct ntdb_file *file);
+enum NTDB_ERROR ntdb_mmap(struct ntdb_context *ntdb);
+
+/* Either alloc a copy, or give direct access. Release frees or noop. */
+const void *ntdb_access_read(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len, bool convert);
+void *ntdb_access_write(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len, bool convert);
+
+/* Release result of ntdb_access_read/write. */
+void ntdb_access_release(struct ntdb_context *ntdb, const void *p);
+/* Commit result of ntdb_acces_write. */
+enum NTDB_ERROR ntdb_access_commit(struct ntdb_context *ntdb, void *p);
+
+/* Convenience routine to get an offset. */
+ntdb_off_t ntdb_read_off(struct ntdb_context *ntdb, ntdb_off_t off);
+
+/* Write an offset at an offset. */
+enum NTDB_ERROR ntdb_write_off(struct ntdb_context *ntdb, ntdb_off_t off,
+ ntdb_off_t val);
+
+/* Clear an ondisk area. */
+enum NTDB_ERROR zero_out(struct ntdb_context *ntdb, ntdb_off_t off, ntdb_len_t len);
+
+/* Return a non-zero offset between >= start < end in this array (or end). */
+ntdb_off_t ntdb_find_nonzero_off(struct ntdb_context *ntdb,
+ ntdb_off_t base,
+ uint64_t start,
+ uint64_t end);
+
+/* Return a zero offset in this array, or num. */
+ntdb_off_t ntdb_find_zero_off(struct ntdb_context *ntdb, ntdb_off_t off,
+ uint64_t num);
+
+/* Allocate and make a copy of some offset. */
+void *ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset, ntdb_len_t len);
+
+/* Writes a converted copy of a record. */
+enum NTDB_ERROR ntdb_write_convert(struct ntdb_context *ntdb, ntdb_off_t off,
+ const void *rec, size_t len);
+
+/* Reads record and converts it */
+enum NTDB_ERROR ntdb_read_convert(struct ntdb_context *ntdb, ntdb_off_t off,
+ void *rec, size_t len);
+
+/* Bump the seqnum (caller checks for ntdb->flags & NTDB_SEQNUM) */
+void ntdb_inc_seqnum(struct ntdb_context *ntdb);
+
+/* lock.c: */
+/* Print message because another ntdb owns a lock we want. */
+enum NTDB_ERROR owner_conflict(struct ntdb_context *ntdb, const char *call);
+
+/* If we fork, we no longer really own locks. */
+bool check_lock_pid(struct ntdb_context *ntdb, const char *call, bool log);
+
+/* Lock/unlock a range of hashes. */
+enum NTDB_ERROR ntdb_lock_hashes(struct ntdb_context *ntdb,
+ ntdb_off_t hash_lock, ntdb_len_t hash_range,
+ int ltype, enum ntdb_lock_flags waitflag);
+enum NTDB_ERROR ntdb_unlock_hashes(struct ntdb_context *ntdb,
+ ntdb_off_t hash_lock,
+ ntdb_len_t hash_range, int ltype);
+
+/* For closing the file. */
+void ntdb_lock_cleanup(struct ntdb_context *ntdb);
+
+/* Lock/unlock a particular free bucket. */
+enum NTDB_ERROR ntdb_lock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off,
+ enum ntdb_lock_flags waitflag);
+void ntdb_unlock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off);
+
+/* Serialize transaction start. */
+enum NTDB_ERROR ntdb_transaction_lock(struct ntdb_context *ntdb, int ltype);
+void ntdb_transaction_unlock(struct ntdb_context *ntdb, int ltype);
+
+/* Do we have any hash locks (ie. via ntdb_chainlock) ? */
+bool ntdb_has_hash_locks(struct ntdb_context *ntdb);
+
+/* Lock entire database. */
+enum NTDB_ERROR ntdb_allrecord_lock(struct ntdb_context *ntdb, int ltype,
+ enum ntdb_lock_flags flags, bool upgradable);
+void ntdb_allrecord_unlock(struct ntdb_context *ntdb, int ltype);
+enum NTDB_ERROR ntdb_allrecord_upgrade(struct ntdb_context *ntdb, off_t start);
+
+/* Serialize db open. */
+enum NTDB_ERROR ntdb_lock_open(struct ntdb_context *ntdb,
+ int ltype, enum ntdb_lock_flags flags);
+void ntdb_unlock_open(struct ntdb_context *ntdb, int ltype);
+bool ntdb_has_open_lock(struct ntdb_context *ntdb);
+
+/* Serialize db expand. */
+enum NTDB_ERROR ntdb_lock_expand(struct ntdb_context *ntdb, int ltype);
+void ntdb_unlock_expand(struct ntdb_context *ntdb, int ltype);
+bool ntdb_has_expansion_lock(struct ntdb_context *ntdb);
+
+/* If it needs recovery, grab all the locks and do it. */
+enum NTDB_ERROR ntdb_lock_and_recover(struct ntdb_context *ntdb);
+
+/* Default lock and unlock functions. */
+int ntdb_fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag, void *);
+int ntdb_fcntl_unlock(int fd, int rw, off_t off, off_t len, void *);
+
+/* transaction.c: */
+enum NTDB_ERROR ntdb_transaction_recover(struct ntdb_context *ntdb);
+ntdb_bool_err ntdb_needs_recovery(struct ntdb_context *ntdb);
+
+struct ntdb_context {
+ /* Single list of all TDBs, to detect multiple opens. */
+ struct ntdb_context *next;
+
+ /* Filename of the database. */
+ const char *name;
+
+ /* Logging function */
+ void (*log_fn)(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
+ const char *message,
+ void *data);
+ void *log_data;
+
+ /* Open flags passed to ntdb_open. */
+ int open_flags;
+
+ /* low level (fnctl) lock functions. */
+ int (*lock_fn)(int fd, int rw, off_t off, off_t len, bool w, void *);
+ int (*unlock_fn)(int fd, int rw, off_t off, off_t len, void *);
+ void *lock_data;
+
+ /* the ntdb flags passed to ntdb_open. */
+ uint32_t flags;
+
+ /* Our statistics. */
+ struct ntdb_attribute_stats stats;
+
+ /* The actual file information */
+ struct ntdb_file *file;
+
+ /* Hash function. */
+ uint64_t (*hash_fn)(const void *key, size_t len, uint64_t seed, void *);
+ void *hash_data;
+ uint64_t hash_seed;
+
+ /* Our open hook, if any. */
+ enum NTDB_ERROR (*openhook)(int fd, void *data);
+ void *openhook_data;
+
+ /* Last error we returned. */
+ enum NTDB_ERROR last_error;
+
+ /* Are we accessing directly? (debugging check). */
+ int direct_access;
+
+ /* Set if we are in a transaction. */
+ struct ntdb_transaction *transaction;
+
+ /* What free table are we using? */
+ ntdb_off_t ftable_off;
+ unsigned int ftable;
+
+ /* IO methods: changes for transactions. */
+ const struct ntdb_methods *io;
+
+ /* Direct access information */
+ struct ntdb_access_hdr *access;
+};
+
+/* ntdb.c: */
+enum NTDB_ERROR COLD PRINTF_FMT(4, 5)
+ ntdb_logerr(struct ntdb_context *ntdb,
+ enum NTDB_ERROR ecode,
+ enum ntdb_log_level level,
+ const char *fmt, ...);
+
+#ifdef NTDB_TRACE
+void ntdb_trace(struct ntdb_context *ntdb, const char *op);
+void ntdb_trace_seqnum(struct ntdb_context *ntdb, uint32_t seqnum, const char *op);
+void ntdb_trace_open(struct ntdb_context *ntdb, const char *op,
+ unsigned hash_size, unsigned ntdb_flags, unsigned open_flags);
+void ntdb_trace_ret(struct ntdb_context *ntdb, const char *op, int ret);
+void ntdb_trace_retrec(struct ntdb_context *ntdb, const char *op, NTDB_DATA ret);
+void ntdb_trace_1rec(struct ntdb_context *ntdb, const char *op,
+ NTDB_DATA rec);
+void ntdb_trace_1rec_ret(struct ntdb_context *ntdb, const char *op,
+ NTDB_DATA rec, int ret);
+void ntdb_trace_1rec_retrec(struct ntdb_context *ntdb, const char *op,
+ NTDB_DATA rec, NTDB_DATA ret);
+void ntdb_trace_2rec_flag_ret(struct ntdb_context *ntdb, const char *op,
+ NTDB_DATA rec1, NTDB_DATA rec2, unsigned flag,
+ int ret);
+void ntdb_trace_2rec_retrec(struct ntdb_context *ntdb, const char *op,
+ NTDB_DATA rec1, NTDB_DATA rec2, NTDB_DATA ret);
+#else
+#define ntdb_trace(ntdb, op)
+#define ntdb_trace_seqnum(ntdb, seqnum, op)
+#define ntdb_trace_open(ntdb, op, hash_size, ntdb_flags, open_flags)
+#define ntdb_trace_ret(ntdb, op, ret)
+#define ntdb_trace_retrec(ntdb, op, ret)
+#define ntdb_trace_1rec(ntdb, op, rec)
+#define ntdb_trace_1rec_ret(ntdb, op, rec, ret)
+#define ntdb_trace_1rec_retrec(ntdb, op, rec, ret)
+#define ntdb_trace_2rec_flag_ret(ntdb, op, rec1, rec2, flag, ret)
+#define ntdb_trace_2rec_retrec(ntdb, op, rec1, rec2, ret)
+#endif /* !NTDB_TRACE */
+
+#endif
diff --git a/lib/ntdb/pyntdb.c b/lib/ntdb/pyntdb.c
new file mode 100644
index 0000000000..1f80e4227b
--- /dev/null
+++ b/lib/ntdb/pyntdb.c
@@ -0,0 +1,591 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Python interface to ntdb. Simply modified from tdb version.
+
+ Copyright (C) 2004-2006 Tim Potter <tpot@samba.org>
+ Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
+ Copyright (C) 2011 Rusty Russell <rusty@rustcorp.com.au>
+
+ ** NOTE! The following LGPL license applies to the ntdb
+ ** 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 <Python.h>
+#include "replace.h"
+#include "system/filesys.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+/* Include ntdb headers */
+#include <ntdb.h>
+
+typedef struct {
+ PyObject_HEAD
+ struct ntdb_context *ctx;
+ bool closed;
+} PyNtdbObject;
+
+staticforward PyTypeObject PyNtdb;
+
+static void PyErr_SetTDBError(enum NTDB_ERROR e)
+{
+ PyErr_SetObject(PyExc_RuntimeError,
+ Py_BuildValue("(i,s)", e, ntdb_errorstr(e)));
+}
+
+static NTDB_DATA PyString_AsNtdb_Data(PyObject *data)
+{
+ NTDB_DATA ret;
+ ret.dptr = (unsigned char *)PyString_AsString(data);
+ ret.dsize = PyString_Size(data);
+ return ret;
+}
+
+static PyObject *PyString_FromNtdb_Data(NTDB_DATA data)
+{
+ PyObject *ret = PyString_FromStringAndSize((const char *)data.dptr,
+ data.dsize);
+ free(data.dptr);
+ return ret;
+}
+
+#define PyErr_NTDB_ERROR_IS_ERR_RAISE(ret) \
+ if (ret != NTDB_SUCCESS) { \
+ PyErr_SetTDBError(ret); \
+ return NULL; \
+ }
+
+static void stderr_log(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
+ const char *message,
+ void *data)
+{
+ fprintf(stderr, "%s:%s:%s\n",
+ ntdb_name(ntdb), ntdb_errorstr(ecode), message);
+}
+
+static PyObject *py_ntdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ char *name = NULL;
+ int ntdb_flags = NTDB_DEFAULT, flags = O_RDWR, mode = 0600;
+ struct ntdb_context *ctx;
+ PyNtdbObject *ret;
+ union ntdb_attribute logattr;
+ const char *kwnames[] = { "name", "ntdb_flags", "flags", "mode", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siii", cast_const2(char **, kwnames), &name, &ntdb_flags, &flags, &mode))
+ return NULL;
+
+ if (name == NULL) {
+ ntdb_flags |= NTDB_INTERNAL;
+ }
+
+ logattr.log.base.attr = NTDB_ATTRIBUTE_LOG;
+ logattr.log.base.next = NULL;
+ logattr.log.fn = stderr_log;
+ ctx = ntdb_open(name, ntdb_flags, flags, mode, &logattr);
+ if (ctx == NULL) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ ret = PyObject_New(PyNtdbObject, &PyNtdb);
+ if (!ret) {
+ ntdb_close(ctx);
+ return NULL;
+ }
+
+ ret->ctx = ctx;
+ ret->closed = false;
+ return (PyObject *)ret;
+}
+
+static PyObject *obj_transaction_cancel(PyNtdbObject *self)
+{
+ ntdb_transaction_cancel(self->ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_transaction_commit(PyNtdbObject *self)
+{
+ enum NTDB_ERROR ret = ntdb_transaction_commit(self->ctx);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_transaction_prepare_commit(PyNtdbObject *self)
+{
+ enum NTDB_ERROR ret = ntdb_transaction_prepare_commit(self->ctx);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_transaction_start(PyNtdbObject *self)
+{
+ enum NTDB_ERROR ret = ntdb_transaction_start(self->ctx);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_lockall(PyNtdbObject *self)
+{
+ enum NTDB_ERROR ret = ntdb_lockall(self->ctx);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_unlockall(PyNtdbObject *self)
+{
+ ntdb_unlockall(self->ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_lockall_read(PyNtdbObject *self)
+{
+ enum NTDB_ERROR ret = ntdb_lockall_read(self->ctx);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_unlockall_read(PyNtdbObject *self)
+{
+ ntdb_unlockall_read(self->ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_close(PyNtdbObject *self)
+{
+ int ret;
+ if (self->closed)
+ Py_RETURN_NONE;
+ ret = ntdb_close(self->ctx);
+ self->closed = true;
+ if (ret != 0) {
+ PyErr_SetTDBError(NTDB_ERR_IO);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_get(PyNtdbObject *self, PyObject *args)
+{
+ NTDB_DATA key, data;
+ PyObject *py_key;
+ enum NTDB_ERROR ret;
+ if (!PyArg_ParseTuple(args, "O", &py_key))
+ return NULL;
+
+ key = PyString_AsNtdb_Data(py_key);
+ ret = ntdb_fetch(self->ctx, key, &data);
+ if (ret == NTDB_ERR_NOEXIST)
+ Py_RETURN_NONE;
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ return PyString_FromNtdb_Data(data);
+}
+
+static PyObject *obj_append(PyNtdbObject *self, PyObject *args)
+{
+ NTDB_DATA key, data;
+ PyObject *py_key, *py_data;
+ enum NTDB_ERROR ret;
+ if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data))
+ return NULL;
+
+ key = PyString_AsNtdb_Data(py_key);
+ data = PyString_AsNtdb_Data(py_data);
+
+ ret = ntdb_append(self->ctx, key, data);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_firstkey(PyNtdbObject *self)
+{
+ enum NTDB_ERROR ret;
+ NTDB_DATA key;
+
+ ret = ntdb_firstkey(self->ctx, &key);
+ if (ret == NTDB_ERR_NOEXIST)
+ Py_RETURN_NONE;
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+
+ return PyString_FromNtdb_Data(key);
+}
+
+static PyObject *obj_nextkey(PyNtdbObject *self, PyObject *args)
+{
+ NTDB_DATA key;
+ PyObject *py_key;
+ enum NTDB_ERROR ret;
+ if (!PyArg_ParseTuple(args, "O", &py_key))
+ return NULL;
+
+ /* Malloc here, since ntdb_nextkey frees. */
+ key.dsize = PyString_Size(py_key);
+ key.dptr = malloc(key.dsize);
+ memcpy(key.dptr, PyString_AsString(py_key), key.dsize);
+
+ ret = ntdb_nextkey(self->ctx, &key);
+ if (ret == NTDB_ERR_NOEXIST)
+ Py_RETURN_NONE;
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+
+ return PyString_FromNtdb_Data(key);
+}
+
+static PyObject *obj_delete(PyNtdbObject *self, PyObject *args)
+{
+ NTDB_DATA key;
+ PyObject *py_key;
+ enum NTDB_ERROR ret;
+ if (!PyArg_ParseTuple(args, "O", &py_key))
+ return NULL;
+
+ key = PyString_AsNtdb_Data(py_key);
+ ret = ntdb_delete(self->ctx, key);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_has_key(PyNtdbObject *self, PyObject *args)
+{
+ NTDB_DATA key;
+ PyObject *py_key;
+ if (!PyArg_ParseTuple(args, "O", &py_key))
+ return NULL;
+
+ key = PyString_AsNtdb_Data(py_key);
+ if (ntdb_exists(self->ctx, key))
+ return Py_True;
+ if (ntdb_error(self->ctx) != NTDB_ERR_NOEXIST)
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ntdb_error(self->ctx));
+ return Py_False;
+}
+
+static PyObject *obj_store(PyNtdbObject *self, PyObject *args)
+{
+ NTDB_DATA key, value;
+ enum NTDB_ERROR ret;
+ int flag = NTDB_REPLACE;
+ PyObject *py_key, *py_value;
+
+ if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag))
+ return NULL;
+
+ key = PyString_AsNtdb_Data(py_key);
+ value = PyString_AsNtdb_Data(py_value);
+
+ ret = ntdb_store(self->ctx, key, value, flag);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_add_flag(PyNtdbObject *self, PyObject *args)
+{
+ unsigned flag;
+
+ if (!PyArg_ParseTuple(args, "I", &flag))
+ return NULL;
+
+ ntdb_add_flag(self->ctx, flag);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_remove_flag(PyNtdbObject *self, PyObject *args)
+{
+ unsigned flag;
+
+ if (!PyArg_ParseTuple(args, "I", &flag))
+ return NULL;
+
+ ntdb_remove_flag(self->ctx, flag);
+ Py_RETURN_NONE;
+}
+
+typedef struct {
+ PyObject_HEAD
+ NTDB_DATA current;
+ bool end;
+ PyNtdbObject *iteratee;
+} PyNtdbIteratorObject;
+
+static PyObject *ntdb_iter_next(PyNtdbIteratorObject *self)
+{
+ enum NTDB_ERROR e;
+ PyObject *ret;
+ if (self->end)
+ return NULL;
+ ret = PyString_FromStringAndSize((const char *)self->current.dptr,
+ self->current.dsize);
+ e = ntdb_nextkey(self->iteratee->ctx, &self->current);
+ if (e == NTDB_ERR_NOEXIST)
+ self->end = true;
+ else
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(e);
+ return ret;
+}
+
+static void ntdb_iter_dealloc(PyNtdbIteratorObject *self)
+{
+ Py_DECREF(self->iteratee);
+ PyObject_Del(self);
+}
+
+PyTypeObject PyNtdbIterator = {
+ .tp_name = "Iterator",
+ .tp_basicsize = sizeof(PyNtdbIteratorObject),
+ .tp_iternext = (iternextfunc)ntdb_iter_next,
+ .tp_dealloc = (destructor)ntdb_iter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_iter = PyObject_SelfIter,
+};
+
+static PyObject *ntdb_object_iter(PyNtdbObject *self)
+{
+ PyNtdbIteratorObject *ret;
+ enum NTDB_ERROR e;
+
+ ret = PyObject_New(PyNtdbIteratorObject, &PyNtdbIterator);
+ if (!ret)
+ return NULL;
+ e = ntdb_firstkey(self->ctx, &ret->current);
+ if (e == NTDB_ERR_NOEXIST) {
+ ret->end = true;
+ } else {
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(e);
+ ret->end = false;
+ }
+ ret->iteratee = self;
+ Py_INCREF(self);
+ return (PyObject *)ret;
+}
+
+static PyObject *obj_clear(PyNtdbObject *self)
+{
+ enum NTDB_ERROR ret = ntdb_wipe_all(self->ctx);
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_enable_seqnum(PyNtdbObject *self)
+{
+ ntdb_add_flag(self->ctx, NTDB_SEQNUM);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef ntdb_object_methods[] = {
+ { "transaction_cancel", (PyCFunction)obj_transaction_cancel, METH_NOARGS,
+ "S.transaction_cancel() -> None\n"
+ "Cancel the currently active transaction." },
+ { "transaction_commit", (PyCFunction)obj_transaction_commit, METH_NOARGS,
+ "S.transaction_commit() -> None\n"
+ "Commit the currently active transaction." },
+ { "transaction_prepare_commit", (PyCFunction)obj_transaction_prepare_commit, METH_NOARGS,
+ "S.transaction_prepare_commit() -> None\n"
+ "Prepare to commit the currently active transaction" },
+ { "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS,
+ "S.transaction_start() -> None\n"
+ "Start a new transaction." },
+ { "lock_all", (PyCFunction)obj_lockall, METH_NOARGS, NULL },
+ { "unlock_all", (PyCFunction)obj_unlockall, METH_NOARGS, NULL },
+ { "read_lock_all", (PyCFunction)obj_lockall_read, METH_NOARGS, NULL },
+ { "read_unlock_all", (PyCFunction)obj_unlockall_read, METH_NOARGS, NULL },
+ { "close", (PyCFunction)obj_close, METH_NOARGS, NULL },
+ { "get", (PyCFunction)obj_get, METH_VARARGS, "S.get(key) -> value\n"
+ "Fetch a value." },
+ { "append", (PyCFunction)obj_append, METH_VARARGS, "S.append(key, value) -> None\n"
+ "Append data to an existing key." },
+ { "firstkey", (PyCFunction)obj_firstkey, METH_NOARGS, "S.firstkey() -> data\n"
+ "Return the first key in this database." },
+ { "nextkey", (PyCFunction)obj_nextkey, METH_NOARGS, "S.nextkey(key) -> data\n"
+ "Return the next key in this database." },
+ { "delete", (PyCFunction)obj_delete, METH_VARARGS, "S.delete(key) -> None\n"
+ "Delete an entry." },
+ { "has_key", (PyCFunction)obj_has_key, METH_VARARGS, "S.has_key(key) -> None\n"
+ "Check whether key exists in this database." },
+ { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None"
+ "Store data." },
+ { "add_flag", (PyCFunction)obj_add_flag, METH_VARARGS, "S.add_flag(flag) -> None" },
+ { "remove_flag", (PyCFunction)obj_remove_flag, METH_VARARGS, "S.remove_flag(flag) -> None" },
+ { "iterkeys", (PyCFunction)ntdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" },
+ { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n"
+ "Wipe the entire database." },
+ { "enable_seqnum", (PyCFunction)obj_enable_seqnum, METH_NOARGS,
+ "S.enable_seqnum() -> None" },
+ { NULL }
+};
+
+static PyObject *obj_get_flags(PyNtdbObject *self, void *closure)
+{
+ return PyInt_FromLong(ntdb_get_flags(self->ctx));
+}
+
+static PyObject *obj_get_filename(PyNtdbObject *self, void *closure)
+{
+ return PyString_FromString(ntdb_name(self->ctx));
+}
+
+static PyObject *obj_get_seqnum(PyNtdbObject *self, void *closure)
+{
+ return PyInt_FromLong(ntdb_get_seqnum(self->ctx));
+}
+
+
+static PyGetSetDef ntdb_object_getsetters[] = {
+ { cast_const(char *, "flags"), (getter)obj_get_flags, NULL, NULL },
+ { cast_const(char *, "filename"), (getter)obj_get_filename, NULL,
+ cast_const(char *, "The filename of this NTDB file.")},
+ { cast_const(char *, "seqnum"), (getter)obj_get_seqnum, NULL, NULL },
+ { NULL }
+};
+
+static PyObject *ntdb_object_repr(PyNtdbObject *self)
+{
+ if (ntdb_get_flags(self->ctx) & NTDB_INTERNAL) {
+ return PyString_FromString("Ntdb(<internal>)");
+ } else {
+ return PyString_FromFormat("Ntdb('%s')", ntdb_name(self->ctx));
+ }
+}
+
+static void ntdb_object_dealloc(PyNtdbObject *self)
+{
+ if (!self->closed)
+ ntdb_close(self->ctx);
+ self->ob_type->tp_free(self);
+}
+
+static PyObject *obj_getitem(PyNtdbObject *self, PyObject *key)
+{
+ NTDB_DATA tkey, val;
+ enum NTDB_ERROR ret;
+
+ if (!PyString_Check(key)) {
+ PyErr_SetString(PyExc_TypeError, "Expected string as key");
+ return NULL;
+ }
+
+ tkey.dptr = (unsigned char *)PyString_AsString(key);
+ tkey.dsize = PyString_Size(key);
+
+ ret = ntdb_fetch(self->ctx, tkey, &val);
+ if (ret == NTDB_ERR_NOEXIST) {
+ PyErr_SetString(PyExc_KeyError, "No such NTDB entry");
+ return NULL;
+ } else {
+ PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
+ return PyString_FromNtdb_Data(val);
+ }
+}
+
+static int obj_setitem(PyNtdbObject *self, PyObject *key, PyObject *value)
+{
+ NTDB_DATA tkey, tval;
+ enum NTDB_ERROR ret;
+ if (!PyString_Check(key)) {
+ PyErr_SetString(PyExc_TypeError, "Expected string as key");
+ return -1;
+ }
+
+ tkey = PyString_AsNtdb_Data(key);
+
+ if (value == NULL) {
+ ret = ntdb_delete(self->ctx, tkey);
+ } else {
+ if (!PyString_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "Expected string as value");
+ return -1;
+ }
+
+ tval = PyString_AsNtdb_Data(value);
+
+ ret = ntdb_store(self->ctx, tkey, tval, NTDB_REPLACE);
+ }
+
+ if (ret != NTDB_SUCCESS) {
+ PyErr_SetTDBError(ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+static PyMappingMethods ntdb_object_mapping = {
+ .mp_subscript = (binaryfunc)obj_getitem,
+ .mp_ass_subscript = (objobjargproc)obj_setitem,
+};
+static PyTypeObject PyNtdb = {
+ .tp_name = "ntdb.Ntdb",
+ .tp_basicsize = sizeof(PyNtdbObject),
+ .tp_methods = ntdb_object_methods,
+ .tp_getset = ntdb_object_getsetters,
+ .tp_new = py_ntdb_open,
+ .tp_doc = "A NTDB file",
+ .tp_repr = (reprfunc)ntdb_object_repr,
+ .tp_dealloc = (destructor)ntdb_object_dealloc,
+ .tp_as_mapping = &ntdb_object_mapping,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER,
+ .tp_iter = (getiterfunc)ntdb_object_iter,
+};
+
+static PyMethodDef ntdb_methods[] = {
+ { "open", (PyCFunction)py_ntdb_open, METH_VARARGS|METH_KEYWORDS, "open(name, hash_size=0, ntdb_flags=NTDB_DEFAULT, flags=O_RDWR, mode=0600)\n"
+ "Open a NTDB file." },
+ { NULL }
+};
+
+void inittdb(void);
+void inittdb(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&PyNtdb) < 0)
+ return;
+
+ if (PyType_Ready(&PyNtdbIterator) < 0)
+ return;
+
+ m = Py_InitModule3("ntdb", ntdb_methods, "NTDB is a simple key-value database similar to GDBM that supports multiple writers.");
+ if (m == NULL)
+ return;
+
+ PyModule_AddObject(m, "REPLACE", PyInt_FromLong(NTDB_REPLACE));
+ PyModule_AddObject(m, "INSERT", PyInt_FromLong(NTDB_INSERT));
+ PyModule_AddObject(m, "MODIFY", PyInt_FromLong(NTDB_MODIFY));
+
+ PyModule_AddObject(m, "DEFAULT", PyInt_FromLong(NTDB_DEFAULT));
+ PyModule_AddObject(m, "INTERNAL", PyInt_FromLong(NTDB_INTERNAL));
+ PyModule_AddObject(m, "NOLOCK", PyInt_FromLong(NTDB_NOLOCK));
+ PyModule_AddObject(m, "NOMMAP", PyInt_FromLong(NTDB_NOMMAP));
+ PyModule_AddObject(m, "CONVERT", PyInt_FromLong(NTDB_CONVERT));
+ PyModule_AddObject(m, "NOSYNC", PyInt_FromLong(NTDB_NOSYNC));
+ PyModule_AddObject(m, "SEQNUM", PyInt_FromLong(NTDB_SEQNUM));
+ PyModule_AddObject(m, "ALLOW_NESTING", PyInt_FromLong(NTDB_ALLOW_NESTING));
+
+ PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
+
+ PyModule_AddObject(m, "__version__", PyString_FromString(PACKAGE_VERSION));
+
+ Py_INCREF(&PyNtdb);
+ PyModule_AddObject(m, "Ntdb", (PyObject *)&PyNtdb);
+
+ Py_INCREF(&PyNtdbIterator);
+}
diff --git a/lib/tdb2/summary.c b/lib/ntdb/summary.c
index c7e93284e0..28ffd61df9 100644
--- a/lib/tdb2/summary.c
+++ b/lib/ntdb/summary.c
@@ -47,25 +47,25 @@
#define HISTO_WIDTH 70
#define HISTO_HEIGHT 20
-static tdb_off_t count_hash(struct tdb_context *tdb,
- tdb_off_t hash_off, unsigned bits)
+static ntdb_off_t count_hash(struct ntdb_context *ntdb,
+ ntdb_off_t hash_off, unsigned bits)
{
- const tdb_off_t *h;
- tdb_off_t count = 0;
+ const ntdb_off_t *h;
+ ntdb_off_t count = 0;
unsigned int i;
- h = tdb_access_read(tdb, hash_off, sizeof(*h) << bits, true);
- if (TDB_PTR_IS_ERR(h)) {
- return TDB_ERR_TO_OFF(TDB_PTR_ERR(h));
+ h = ntdb_access_read(ntdb, hash_off, sizeof(*h) << bits, true);
+ if (NTDB_PTR_IS_ERR(h)) {
+ return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(h));
}
for (i = 0; i < (1 << bits); i++)
count += (h[i] != 0);
- tdb_access_release(tdb, h);
+ ntdb_access_release(ntdb, h);
return count;
}
-static enum TDB_ERROR summarize(struct tdb_context *tdb,
+static enum NTDB_ERROR summarize(struct ntdb_context *ntdb,
struct tally *hashes,
struct tally *ftables,
struct tally *fr,
@@ -76,39 +76,39 @@ static enum TDB_ERROR summarize(struct tdb_context *tdb,
struct tally *chains,
size_t *num_caps)
{
- tdb_off_t off;
- tdb_len_t len;
- tdb_len_t unc = 0;
+ ntdb_off_t off;
+ ntdb_len_t len;
+ ntdb_len_t unc = 0;
- for (off = sizeof(struct tdb_header);
- off < tdb->file->map_size;
+ for (off = sizeof(struct ntdb_header);
+ off < ntdb->file->map_size;
off += len) {
const union {
- struct tdb_used_record u;
- struct tdb_free_record f;
- struct tdb_recovery_record r;
+ struct ntdb_used_record u;
+ struct ntdb_free_record f;
+ struct ntdb_recovery_record r;
} *p;
/* We might not be able to get the whole thing. */
- p = tdb_access_read(tdb, off, sizeof(p->f), true);
- if (TDB_PTR_IS_ERR(p)) {
- return TDB_PTR_ERR(p);
+ p = ntdb_access_read(ntdb, off, sizeof(p->f), true);
+ if (NTDB_PTR_IS_ERR(p)) {
+ return NTDB_PTR_ERR(p);
}
- if (frec_magic(&p->f) != TDB_FREE_MAGIC) {
+ if (frec_magic(&p->f) != NTDB_FREE_MAGIC) {
if (unc > 1) {
tally_add(uncoal, unc);
unc = 0;
}
}
- if (p->r.magic == TDB_RECOVERY_INVALID_MAGIC
- || p->r.magic == TDB_RECOVERY_MAGIC) {
+ if (p->r.magic == NTDB_RECOVERY_INVALID_MAGIC
+ || p->r.magic == NTDB_RECOVERY_MAGIC) {
len = sizeof(p->r) + p->r.max_len;
- } else if (frec_magic(&p->f) == TDB_FREE_MAGIC) {
+ } else if (frec_magic(&p->f) == NTDB_FREE_MAGIC) {
len = frec_len(&p->f);
tally_add(fr, len);
len += sizeof(p->u);
unc++;
- } else if (rec_magic(&p->u) == TDB_USED_MAGIC) {
+ } else if (rec_magic(&p->u) == NTDB_USED_MAGIC) {
len = sizeof(p->u)
+ rec_key_length(&p->u)
+ rec_data_length(&p->u)
@@ -117,105 +117,105 @@ static enum TDB_ERROR summarize(struct tdb_context *tdb,
tally_add(keys, rec_key_length(&p->u));
tally_add(data, rec_data_length(&p->u));
tally_add(extra, rec_extra_padding(&p->u));
- } else if (rec_magic(&p->u) == TDB_HTABLE_MAGIC) {
- tdb_off_t count = count_hash(tdb,
+ } else if (rec_magic(&p->u) == NTDB_HTABLE_MAGIC) {
+ ntdb_off_t count = count_hash(ntdb,
off + sizeof(p->u),
- TDB_SUBLEVEL_HASH_BITS);
- if (TDB_OFF_IS_ERR(count)) {
- return TDB_OFF_TO_ERR(count);
+ NTDB_SUBLEVEL_HASH_BITS);
+ if (NTDB_OFF_IS_ERR(count)) {
+ return NTDB_OFF_TO_ERR(count);
}
tally_add(hashes, count);
tally_add(extra, rec_extra_padding(&p->u));
len = sizeof(p->u)
+ rec_data_length(&p->u)
+ rec_extra_padding(&p->u);
- } else if (rec_magic(&p->u) == TDB_FTABLE_MAGIC) {
+ } else if (rec_magic(&p->u) == NTDB_FTABLE_MAGIC) {
len = sizeof(p->u)
+ rec_data_length(&p->u)
+ rec_extra_padding(&p->u);
tally_add(ftables, rec_data_length(&p->u));
tally_add(extra, rec_extra_padding(&p->u));
- } else if (rec_magic(&p->u) == TDB_CHAIN_MAGIC) {
+ } else if (rec_magic(&p->u) == NTDB_CHAIN_MAGIC) {
len = sizeof(p->u)
+ rec_data_length(&p->u)
+ rec_extra_padding(&p->u);
tally_add(chains, 1);
tally_add(extra, rec_extra_padding(&p->u));
- } else if (rec_magic(&p->u) == TDB_CAP_MAGIC) {
+ } else if (rec_magic(&p->u) == NTDB_CAP_MAGIC) {
len = sizeof(p->u)
+ rec_data_length(&p->u)
+ rec_extra_padding(&p->u);
(*num_caps)++;
} else {
- len = dead_space(tdb, off);
- if (TDB_OFF_IS_ERR(len)) {
- return TDB_OFF_TO_ERR(len);
+ len = dead_space(ntdb, off);
+ if (NTDB_OFF_IS_ERR(len)) {
+ return NTDB_OFF_TO_ERR(len);
}
}
- tdb_access_release(tdb, p);
+ ntdb_access_release(ntdb, p);
}
if (unc)
tally_add(uncoal, unc);
- return TDB_SUCCESS;
+ return NTDB_SUCCESS;
}
-static void add_capabilities(struct tdb_context *tdb, char *summary)
+static void add_capabilities(struct ntdb_context *ntdb, char *summary)
{
- tdb_off_t off, next;
- const struct tdb_capability *cap;
+ ntdb_off_t off, next;
+ const struct ntdb_capability *cap;
size_t count = 0;
/* Append to summary. */
summary += strlen(summary);
- off = tdb_read_off(tdb, offsetof(struct tdb_header, capabilities));
- if (TDB_OFF_IS_ERR(off))
+ off = ntdb_read_off(ntdb, offsetof(struct ntdb_header, capabilities));
+ if (NTDB_OFF_IS_ERR(off))
return;
/* Walk capability list. */
for (; off; off = next) {
- cap = tdb_access_read(tdb, off, sizeof(*cap), true);
- if (TDB_PTR_IS_ERR(cap)) {
+ cap = ntdb_access_read(ntdb, off, sizeof(*cap), true);
+ if (NTDB_PTR_IS_ERR(cap)) {
break;
}
count++;
sprintf(summary, CAPABILITY_FORMAT,
- cap->type & TDB_CAP_TYPE_MASK,
+ cap->type & NTDB_CAP_TYPE_MASK,
/* Noopen? How did we get here? */
- (cap->type & TDB_CAP_NOOPEN) ? " (unopenable)"
- : ((cap->type & TDB_CAP_NOWRITE)
- && (cap->type & TDB_CAP_NOCHECK)) ? " (uncheckable,read-only)"
- : (cap->type & TDB_CAP_NOWRITE) ? " (read-only)"
- : (cap->type & TDB_CAP_NOCHECK) ? " (uncheckable)"
+ (cap->type & NTDB_CAP_NOOPEN) ? " (unopenable)"
+ : ((cap->type & NTDB_CAP_NOWRITE)
+ && (cap->type & NTDB_CAP_NOCHECK)) ? " (uncheckable,read-only)"
+ : (cap->type & NTDB_CAP_NOWRITE) ? " (read-only)"
+ : (cap->type & NTDB_CAP_NOCHECK) ? " (uncheckable)"
: "");
summary += strlen(summary);
next = cap->next;
- tdb_access_release(tdb, cap);
+ ntdb_access_release(ntdb, cap);
}
}
-_PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
- enum tdb_summary_flags flags,
+_PUBLIC_ enum NTDB_ERROR ntdb_summary(struct ntdb_context *ntdb,
+ enum ntdb_summary_flags flags,
char **summary)
{
- tdb_len_t len;
+ ntdb_len_t len;
size_t num_caps = 0;
struct tally *ftables, *hashes, *freet, *keys, *data, *extra, *uncoal,
*chains;
char *hashesg, *freeg, *keysg, *datag, *extrag, *uncoalg;
- enum TDB_ERROR ecode;
+ enum NTDB_ERROR ecode;
hashesg = freeg = keysg = datag = extrag = uncoalg = NULL;
- ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
- if (ecode != TDB_SUCCESS) {
- return tdb->last_error = ecode;
+ ecode = ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, false);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb->last_error = ecode;
}
- ecode = tdb_lock_expand(tdb, F_RDLCK);
- if (ecode != TDB_SUCCESS) {
- tdb_allrecord_unlock(tdb, F_RDLCK);
- return tdb->last_error = ecode;
+ ecode = ntdb_lock_expand(ntdb, F_RDLCK);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_allrecord_unlock(ntdb, F_RDLCK);
+ return ntdb->last_error = ecode;
}
/* Start stats off empty. */
@@ -229,19 +229,19 @@ _PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
chains = tally_new(HISTO_HEIGHT);
if (!ftables || !hashes || !freet || !keys || !data || !extra
|| !uncoal || !chains) {
- ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_summary: failed to allocate"
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_summary: failed to allocate"
" tally structures");
goto unlock;
}
- ecode = summarize(tdb, hashes, ftables, freet, keys, data, extra,
+ ecode = summarize(ntdb, hashes, ftables, freet, keys, data, extra,
uncoal, chains, &num_caps);
- if (ecode != TDB_SUCCESS) {
+ if (ecode != NTDB_SUCCESS) {
goto unlock;
}
- if (flags & TDB_SUMMARY_HISTOGRAMS) {
+ if (flags & NTDB_SUMMARY_HISTOGRAMS) {
hashesg = tally_histogram(hashes, HISTO_WIDTH, HISTO_HEIGHT);
freeg = tally_histogram(freet, HISTO_WIDTH, HISTO_HEIGHT);
keysg = tally_histogram(keys, HISTO_WIDTH, HISTO_HEIGHT);
@@ -263,13 +263,13 @@ _PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
*summary = malloc(len);
if (!*summary) {
- ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_summary: failed to allocate string");
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_summary: failed to allocate string");
goto unlock;
}
sprintf(*summary, SUMMARY_FORMAT,
- (size_t)tdb->file->map_size,
+ (size_t)ntdb->file->map_size,
tally_total(keys, NULL) + tally_total(data, NULL),
tally_num(keys),
tally_min(keys), tally_mean(keys), tally_max(keys),
@@ -284,29 +284,29 @@ _PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
tally_total(uncoal, NULL),
tally_min(uncoal), tally_mean(uncoal), tally_max(uncoal),
uncoalg ? uncoalg : "",
- (unsigned)count_hash(tdb, offsetof(struct tdb_header,
+ (unsigned)count_hash(ntdb, offsetof(struct ntdb_header,
hashtable),
- TDB_TOPLEVEL_HASH_BITS),
- 1 << TDB_TOPLEVEL_HASH_BITS,
+ NTDB_TOPLEVEL_HASH_BITS),
+ 1 << NTDB_TOPLEVEL_HASH_BITS,
tally_num(chains),
tally_num(hashes),
tally_min(hashes), tally_mean(hashes), tally_max(hashes),
hashesg ? hashesg : "",
- tally_total(keys, NULL) * 100.0 / tdb->file->map_size,
- tally_total(data, NULL) * 100.0 / tdb->file->map_size,
- tally_total(extra, NULL) * 100.0 / tdb->file->map_size,
- tally_total(freet, NULL) * 100.0 / tdb->file->map_size,
+ tally_total(keys, NULL) * 100.0 / ntdb->file->map_size,
+ tally_total(data, NULL) * 100.0 / ntdb->file->map_size,
+ tally_total(extra, NULL) * 100.0 / ntdb->file->map_size,
+ tally_total(freet, NULL) * 100.0 / ntdb->file->map_size,
(tally_num(keys) + tally_num(freet) + tally_num(hashes))
- * sizeof(struct tdb_used_record) * 100.0 / tdb->file->map_size,
- tally_num(ftables) * sizeof(struct tdb_freetable)
- * 100.0 / tdb->file->map_size,
+ * sizeof(struct ntdb_used_record) * 100.0 / ntdb->file->map_size,
+ tally_num(ftables) * sizeof(struct ntdb_freetable)
+ * 100.0 / ntdb->file->map_size,
(tally_num(hashes)
- * (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS)
- + (sizeof(tdb_off_t) << TDB_TOPLEVEL_HASH_BITS)
- + sizeof(struct tdb_chain) * tally_num(chains))
- * 100.0 / tdb->file->map_size);
+ * (sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS)
+ + (sizeof(ntdb_off_t) << NTDB_TOPLEVEL_HASH_BITS)
+ + sizeof(struct ntdb_chain) * tally_num(chains))
+ * 100.0 / ntdb->file->map_size);
- add_capabilities(tdb, *summary);
+ add_capabilities(ntdb, *summary);
unlock:
free(hashesg);
@@ -324,7 +324,7 @@ unlock:
free(ftables);
free(chains);
- tdb_allrecord_unlock(tdb, F_RDLCK);
- tdb_unlock_expand(tdb, F_RDLCK);
- return tdb->last_error = ecode;
+ ntdb_allrecord_unlock(ntdb, F_RDLCK);
+ ntdb_unlock_expand(ntdb, F_RDLCK);
+ return ntdb->last_error = ecode;
}
diff --git a/lib/tdb2/test/api-12-store.c b/lib/ntdb/test/api-12-store.c
index 6a9dd95f5f..24d9498755 100644
--- a/lib/tdb2/test/api-12-store.c
+++ b/lib/ntdb/test/api-12-store.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <ccan/hash/hash.h>
#include <sys/types.h>
@@ -18,38 +18,38 @@ static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
int main(int argc, char *argv[])
{
unsigned int i, j;
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
uint64_t seed = 16014841315512641303ULL;
- union tdb_attribute fixed_hattr
- = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
+ union ntdb_attribute fixed_hattr
+ = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
.fn = fixedhash,
.data = &seed } };
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = { (unsigned char *)&j, sizeof(j) };
- struct tdb_data data = { (unsigned char *)&j, sizeof(j) };
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
+ NTDB_DATA data = { (unsigned char *)&j, sizeof(j) };
fixed_hattr.base.next = &tap_log_attr;
plan_tests(sizeof(flags) / sizeof(flags[0]) * (1 + 500 * 3) + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-12-store.tdb", flags[i],
+ ntdb = ntdb_open("run-12-store.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &fixed_hattr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
/* We seemed to lose some keys.
* Insert and check they're in there! */
for (j = 0; j < 500; j++) {
- struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(tdb_deq(d, data));
+ NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(ntdb_deq(d, data));
free(d.dptr);
}
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/ntdb/test/api-13-delete.c b/lib/ntdb/test/api-13-delete.c
new file mode 100644
index 0000000000..182252b109
--- /dev/null
+++ b/lib/ntdb/test/api-13-delete.c
@@ -0,0 +1,205 @@
+#include "private.h" // For NTDB_TOPLEVEL_HASH_BITS
+#include <ccan/hash/hash.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "ntdb.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+/* We rig the hash so adjacent-numbered records always clash. */
+static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
+{
+ return ((uint64_t)*(const unsigned int *)key)
+ << (64 - NTDB_TOPLEVEL_HASH_BITS - 1);
+}
+
+/* We use the same seed which we saw a failure on. */
+static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
+{
+ return hash64_stable((const unsigned char *)key, len,
+ *(uint64_t *)p);
+}
+
+static bool store_records(struct ntdb_context *ntdb)
+{
+ int i;
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA d, data = { (unsigned char *)&i, sizeof(i) };
+
+ for (i = 0; i < 1000; i++) {
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
+ return false;
+ ntdb_fetch(ntdb, key, &d);
+ if (!ntdb_deq(d, data))
+ return false;
+ free(d.dptr);
+ }
+ return true;
+}
+
+static void test_val(struct ntdb_context *ntdb, uint64_t val)
+{
+ uint64_t v;
+ NTDB_DATA key = { (unsigned char *)&v, sizeof(v) };
+ NTDB_DATA d, data = { (unsigned char *)&v, sizeof(v) };
+
+ /* Insert an entry, then delete it. */
+ v = val;
+ /* Delete should fail. */
+ ok1(ntdb_delete(ntdb, key) == NTDB_ERR_NOEXIST);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Insert should succeed. */
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Delete should succeed. */
+ ok1(ntdb_delete(ntdb, key) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Re-add it, then add collision. */
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ v = val + 1;
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Can find both? */
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+ v = val;
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+
+ /* Delete second one. */
+ v = val + 1;
+ ok1(ntdb_delete(ntdb, key) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Re-add */
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Now, try deleting first one. */
+ v = val;
+ ok1(ntdb_delete(ntdb, key) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Can still find second? */
+ v = val + 1;
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+
+ /* Now, this will be ideally placed. */
+ v = val + 2;
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* This will collide with both. */
+ v = val;
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+
+ /* We can still find them all, right? */
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+ v = val + 1;
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+ v = val + 2;
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+
+ /* And if we delete val + 1, that val + 2 should not move! */
+ v = val + 1;
+ ok1(ntdb_delete(ntdb, key) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ v = val;
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+ v = val + 2;
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == data.dsize);
+ free(d.dptr);
+
+ /* Delete those two, so we are empty. */
+ ok1(ntdb_delete(ntdb, key) == 0);
+ v = val;
+ ok1(ntdb_delete(ntdb, key) == 0);
+
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j;
+ struct ntdb_context *ntdb;
+ uint64_t seed = 16014841315512641303ULL;
+ union ntdb_attribute clash_hattr
+ = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
+ .fn = clash } };
+ union ntdb_attribute fixed_hattr
+ = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
+ .fn = fixedhash,
+ .data = &seed } };
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ /* These two values gave trouble before. */
+ int vals[] = { 755, 837 };
+
+ clash_hattr.base.next = &tap_log_attr;
+ fixed_hattr.base.next = &tap_log_attr;
+
+ plan_tests(sizeof(flags) / sizeof(flags[0])
+ * (39 * 3 + 5 + sizeof(vals)/sizeof(vals[0])*2) + 1);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("run-13-delete.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &clash_hattr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ /* Check start of hash table. */
+ test_val(ntdb, 0);
+
+ /* Check end of hash table. */
+ test_val(ntdb, -1ULL);
+
+ /* Check mixed bitpattern. */
+ test_val(ntdb, 0x123456789ABCDEF0ULL);
+
+ ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0));
+ ntdb_close(ntdb);
+
+ /* Deleting these entries in the db gave problems. */
+ ntdb = ntdb_open("run-13-delete.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &fixed_hattr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ ok1(store_records(ntdb));
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ for (j = 0; j < sizeof(vals)/sizeof(vals[0]); j++) {
+ NTDB_DATA key;
+
+ key.dptr = (unsigned char *)&vals[j];
+ key.dsize = sizeof(vals[j]);
+ ok1(ntdb_delete(ntdb, key) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ }
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/ntdb/test/api-14-exists.c b/lib/ntdb/test/api-14-exists.c
new file mode 100644
index 0000000000..88663cad65
--- /dev/null
+++ b/lib/ntdb/test/api-14-exists.c
@@ -0,0 +1,54 @@
+#include "config.h"
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "logging.h"
+
+static bool test_records(struct ntdb_context *ntdb)
+{
+ int i;
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
+
+ for (i = 0; i < 1000; i++) {
+ if (ntdb_exists(ntdb, key))
+ return false;
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
+ return false;
+ if (!ntdb_exists(ntdb, key))
+ return false;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ if (!ntdb_exists(ntdb, key))
+ return false;
+ if (ntdb_delete(ntdb, key) != 0)
+ return false;
+ if (ntdb_exists(ntdb, key))
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 2 + 1);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("run-14-exists.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ if (ok1(ntdb))
+ ok1(test_records(ntdb));
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/ntdb/test/api-16-wipe_all.c b/lib/ntdb/test/api-16-wipe_all.c
new file mode 100644
index 0000000000..c1bda8e4f4
--- /dev/null
+++ b/lib/ntdb/test/api-16-wipe_all.c
@@ -0,0 +1,46 @@
+#include "config.h"
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "logging.h"
+
+static bool add_records(struct ntdb_context *ntdb)
+{
+ int i;
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
+
+ for (i = 0; i < 1000; i++) {
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
+ return false;
+ }
+ return true;
+}
+
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 1);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("run-16-wipe_all.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ if (ok1(ntdb)) {
+ NTDB_DATA key;
+ ok1(add_records(ntdb));
+ ok1(ntdb_wipe_all(ntdb) == NTDB_SUCCESS);
+ ok1(ntdb_firstkey(ntdb, &key) == NTDB_ERR_NOEXIST);
+ ntdb_close(ntdb);
+ }
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/ntdb/test/api-21-parse_record.c b/lib/ntdb/test/api-21-parse_record.c
new file mode 100644
index 0000000000..fa48562e17
--- /dev/null
+++ b/lib/ntdb/test/api-21-parse_record.c
@@ -0,0 +1,67 @@
+#include "config.h"
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "logging.h"
+
+static enum NTDB_ERROR parse(NTDB_DATA key, NTDB_DATA data, NTDB_DATA *expected)
+{
+ if (!ntdb_deq(data, *expected))
+ return NTDB_ERR_EINVAL;
+ return NTDB_SUCCESS;
+}
+
+static enum NTDB_ERROR parse_err(NTDB_DATA key, NTDB_DATA data, void *unused)
+{
+ return 100;
+}
+
+static bool test_records(struct ntdb_context *ntdb)
+{
+ int i;
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
+
+ for (i = 0; i < 1000; i++) {
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
+ return false;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ if (ntdb_parse_record(ntdb, key, parse, &data) != NTDB_SUCCESS)
+ return false;
+ }
+
+ if (ntdb_parse_record(ntdb, key, parse, &data) != NTDB_ERR_NOEXIST)
+ return false;
+
+ /* Test error return from parse function. */
+ i = 0;
+ if (ntdb_parse_record(ntdb, key, parse_err, NULL) != 100)
+ return false;
+
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 2 + 1);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("api-21-parse_record.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ if (ok1(ntdb))
+ ok1(test_records(ntdb));
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/ntdb/test/api-55-transaction.c b/lib/ntdb/test/api-55-transaction.c
new file mode 100644
index 0000000000..d51dd0b13e
--- /dev/null
+++ b/lib/ntdb/test/api-55-transaction.c
@@ -0,0 +1,73 @@
+#include "private.h" // struct ntdb_context
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ unsigned char *buffer;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data;
+
+ buffer = malloc(1000);
+ for (i = 0; i < 1000; i++)
+ buffer[i] = i;
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 20 + 1);
+
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("run-55-transaction.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ ok1(ntdb_transaction_start(ntdb) == 0);
+ data.dptr = buffer;
+ data.dsize = 1000;
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
+ ok1(data.dsize == 1000);
+ ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
+ free(data.dptr);
+
+ /* Cancelling a transaction means no store */
+ ntdb_transaction_cancel(ntdb);
+ ok1(ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb_fetch(ntdb, key, &data) == NTDB_ERR_NOEXIST);
+
+ /* Commit the transaction. */
+ ok1(ntdb_transaction_start(ntdb) == 0);
+ data.dptr = buffer;
+ data.dsize = 1000;
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
+ ok1(data.dsize == 1000);
+ ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
+ free(data.dptr);
+ ok1(ntdb_transaction_commit(ntdb) == 0);
+ ok1(ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
+ ok1(data.dsize == 1000);
+ ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
+ free(data.dptr);
+
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ free(buffer);
+ return exit_status();
+}
diff --git a/lib/tdb2/test/api-80-tdb_fd.c b/lib/ntdb/test/api-80-tdb_fd.c
index 63967b8aa6..39a9df414e 100644
--- a/lib/tdb2/test/api-80-tdb_fd.c
+++ b/lib/ntdb/test/api-80-tdb_fd.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -9,23 +9,23 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 3);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("api-80-tdb_fd.tdb", flags[i],
+ ntdb = ntdb_open("api-80-ntdb_fd.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
continue;
- if (flags[i] & TDB_INTERNAL)
- ok1(tdb_fd(tdb) == -1);
+ if (flags[i] & NTDB_INTERNAL)
+ ok1(ntdb_fd(ntdb) == -1);
else
- ok1(tdb_fd(tdb) > 2);
- tdb_close(tdb);
+ ok1(ntdb_fd(ntdb) > 2);
+ ntdb_close(ntdb);
ok1(tap_log_messages == 0);
}
return exit_status();
diff --git a/lib/ntdb/test/api-81-seqnum.c b/lib/ntdb/test/api-81-seqnum.c
new file mode 100644
index 0000000000..93ad53ab07
--- /dev/null
+++ b/lib/ntdb/test/api-81-seqnum.c
@@ -0,0 +1,69 @@
+#include "config.h"
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, seq;
+ struct ntdb_context *ntdb;
+ NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 15 + 4 * 13);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("api-81-seqnum.ntdb", flags[i]|NTDB_SEQNUM,
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ if (!ok1(ntdb))
+ continue;
+
+ seq = 0;
+ ok1(ntdb_get_seqnum(ntdb) == seq);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+ /* Fetch doesn't change seqnum */
+ if (ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS))
+ free(d.dptr);
+ ok1(ntdb_get_seqnum(ntdb) == seq);
+ ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+
+ ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+ /* Empty append works */
+ ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+
+ ok1(ntdb_wipe_all(ntdb) == NTDB_SUCCESS);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+
+ if (!(flags[i] & NTDB_INTERNAL)) {
+ ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+ ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+ ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
+ ok1(ntdb_get_seqnum(ntdb) == ++seq);
+ ok1(ntdb_transaction_commit(ntdb) == NTDB_SUCCESS);
+ ok1(ntdb_get_seqnum(ntdb) == seq);
+
+ ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_get_seqnum(ntdb) == seq + 1);
+ ntdb_transaction_cancel(ntdb);
+ ok1(ntdb_get_seqnum(ntdb) == seq);
+ }
+ ntdb_close(ntdb);
+ ok1(tap_log_messages == 0);
+ }
+ return exit_status();
+}
diff --git a/lib/tdb2/test/api-82-lockattr.c b/lib/ntdb/test/api-82-lockattr.c
index b229eab83c..51bb939f59 100644
--- a/lib/tdb2/test/api-82-lockattr.c
+++ b/lib/ntdb/test/api-82-lockattr.c
@@ -1,5 +1,5 @@
-#include "private.h" // for tdb_fcntl_unlock
-#include "tdb2.h"
+#include "private.h" // for ntdb_fcntl_unlock
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -35,7 +35,7 @@ static int mylock(int fd, int rw, off_t off, off_t len, bool waitflag,
}
static int trav_err;
-static int trav(struct tdb_context *tdb, TDB_DATA k, TDB_DATA d, int *terr)
+static int trav(struct ntdb_context *ntdb, NTDB_DATA k, NTDB_DATA d, int *terr)
{
*terr = trav_err;
return 0;
@@ -44,193 +44,193 @@ static int trav(struct tdb_context *tdb, TDB_DATA k, TDB_DATA d, int *terr)
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
- union tdb_attribute lock_attr;
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+ union ntdb_attribute lock_attr;
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
int lock_err;
- lock_attr.base.attr = TDB_ATTRIBUTE_FLOCK;
+ lock_attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
lock_attr.base.next = &tap_log_attr;
lock_attr.flock.lock = mylock;
- lock_attr.flock.unlock = tdb_fcntl_unlock;
+ lock_attr.flock.unlock = ntdb_fcntl_unlock;
lock_attr.flock.data = &lock_err;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 80);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- struct tdb_data d;
+ NTDB_DATA d;
/* Nonblocking open; expect no error message. */
lock_err = EAGAIN;
- tdb = tdb_open("run-82-lockattr.tdb", flags[i],
+ ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
ok(errno == lock_err, "Errno is %u", errno);
- ok1(!tdb);
+ ok1(!ntdb);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- tdb = tdb_open("run-82-lockattr.tdb", flags[i],
+ ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
ok(errno == lock_err, "Errno is %u", errno);
- ok1(!tdb);
+ ok1(!ntdb);
ok1(tap_log_messages == 0);
/* Forced fail open. */
lock_err = ENOMEM;
- tdb = tdb_open("run-82-lockattr.tdb", flags[i],
+ ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
ok1(errno == lock_err);
- ok1(!tdb);
+ ok1(!ntdb);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
lock_err = 0;
- tdb = tdb_open("run-82-lockattr.tdb", flags[i],
+ ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
continue;
ok1(tap_log_messages == 0);
/* Nonblocking store. */
lock_err = EAGAIN;
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_ERR_LOCK);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_ERR_LOCK);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_ERR_LOCK);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
/* Nonblocking fetch. */
lock_err = EAGAIN;
- ok1(!tdb_exists(tdb, key));
+ ok1(!ntdb_exists(ntdb, key));
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(!tdb_exists(tdb, key));
+ ok1(!ntdb_exists(ntdb, key));
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(!tdb_exists(tdb, key));
+ ok1(!ntdb_exists(ntdb, key));
ok1(tap_log_messages == 1);
tap_log_messages = 0;
lock_err = EAGAIN;
- ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_LOCK);
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_LOCK);
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_LOCK);
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
/* Nonblocking delete. */
lock_err = EAGAIN;
- ok1(tdb_delete(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_delete(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_delete(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_delete(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_delete(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_delete(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
/* Nonblocking locks. */
lock_err = EAGAIN;
- ok1(tdb_chainlock(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_chainlock(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_chainlock(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
lock_err = EAGAIN;
- ok1(tdb_chainlock_read(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_chainlock_read(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_chainlock_read(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_chainlock_read(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_chainlock_read(tdb, key) == TDB_ERR_LOCK);
+ ok1(ntdb_chainlock_read(ntdb, key) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
lock_err = EAGAIN;
- ok1(tdb_lockall(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_lockall(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_lockall(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_lockall(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_lockall(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_lockall(ntdb) == NTDB_ERR_LOCK);
/* This actually does divide and conquer. */
ok1(tap_log_messages > 0);
tap_log_messages = 0;
lock_err = EAGAIN;
- ok1(tdb_lockall_read(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_lockall_read(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_lockall_read(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_lockall_read(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_lockall_read(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_lockall_read(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages > 0);
tap_log_messages = 0;
/* Nonblocking traverse; go nonblock partway through. */
lock_err = 0;
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
trav_err = EAGAIN;
- ok1(tdb_traverse(tdb, trav, &lock_err) == TDB_ERR_LOCK);
+ ok1(ntdb_traverse(ntdb, trav, &lock_err) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
trav_err = EINTR;
lock_err = 0;
- ok1(tdb_traverse(tdb, trav, &lock_err) == TDB_ERR_LOCK);
+ ok1(ntdb_traverse(ntdb, trav, &lock_err) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
trav_err = ENOMEM;
lock_err = 0;
- ok1(tdb_traverse(tdb, trav, &lock_err) == TDB_ERR_LOCK);
+ ok1(ntdb_traverse(ntdb, trav, &lock_err) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
/* Nonblocking transactions. */
lock_err = EAGAIN;
- ok1(tdb_transaction_start(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = EINTR;
- ok1(tdb_transaction_start(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = ENOMEM;
- ok1(tdb_transaction_start(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 1);
tap_log_messages = 0;
/* Nonblocking transaction prepare. */
lock_err = 0;
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_delete(tdb, key) == 0);
+ ok1(ntdb_transaction_start(ntdb) == 0);
+ ok1(ntdb_delete(ntdb, key) == 0);
lock_err = EAGAIN;
- ok1(tdb_transaction_prepare_commit(tdb) == TDB_ERR_LOCK);
+ ok1(ntdb_transaction_prepare_commit(ntdb) == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
lock_err = 0;
- ok1(tdb_transaction_prepare_commit(tdb) == 0);
- ok1(tdb_transaction_commit(tdb) == 0);
+ ok1(ntdb_transaction_prepare_commit(ntdb) == 0);
+ ok1(ntdb_transaction_commit(ntdb) == 0);
/* And the transaction was committed, right? */
- ok1(!tdb_exists(tdb, key));
- tdb_close(tdb);
+ ok1(!ntdb_exists(ntdb, key));
+ ntdb_close(ntdb);
ok1(tap_log_messages == 0);
}
return exit_status();
diff --git a/lib/tdb2/test/api-83-openhook.c b/lib/ntdb/test/api-83-openhook.c
index 191cf068c1..9f474c9ab8 100644
--- a/lib/tdb2/test/api-83-openhook.c
+++ b/lib/ntdb/test/api-83-openhook.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -11,14 +11,14 @@
#include "external-agent.h"
#include "logging.h"
-static enum TDB_ERROR clear_if_first(int fd, void *arg)
+static enum NTDB_ERROR clear_if_first(int fd, void *arg)
{
/* We hold a lock offset 4 always, so we can tell if anyone is holding it.
- * (This is compatible with tdb1's TDB_CLEAR_IF_FIRST flag). */
+ * (This is compatible with tdb's TDB_CLEAR_IF_FIRST flag). */
struct flock fl;
if (arg != clear_if_first)
- return TDB_ERR_CORRUPT;
+ return NTDB_ERR_CORRUPT;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
@@ -29,27 +29,27 @@ static enum TDB_ERROR clear_if_first(int fd, void *arg)
/* We must be first ones to open it! */
diag("truncating file!");
if (ftruncate(fd, 0) != 0) {
- return TDB_ERR_IO;
+ return NTDB_ERR_IO;
}
}
fl.l_type = F_RDLCK;
if (fcntl(fd, F_SETLKW, &fl) != 0) {
- return TDB_ERR_IO;
+ return NTDB_ERR_IO;
}
- return TDB_SUCCESS;
+ return NTDB_SUCCESS;
}
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
struct agent *agent;
- union tdb_attribute cif;
- struct tdb_data key = tdb_mkdata("key", 3);
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+ union ntdb_attribute cif;
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
- cif.openhook.base.attr = TDB_ATTRIBUTE_OPENHOOK;
+ cif.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
cif.openhook.base.next = &tap_log_attr;
cif.openhook.fn = clear_if_first;
cif.openhook.data = clear_if_first;
@@ -58,33 +58,33 @@ int main(int argc, char *argv[])
plan_tests(sizeof(flags) / sizeof(flags[0]) * 13);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
/* Create it */
- tdb = tdb_open("run-83-openhook.tdb", flags[i],
+ ntdb = ntdb_open("run-83-openhook.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, NULL);
- ok1(tdb);
- ok1(tdb_store(tdb, key, key, TDB_REPLACE) == 0);
- tdb_close(tdb);
+ ok1(ntdb);
+ ok1(ntdb_store(ntdb, key, key, NTDB_REPLACE) == 0);
+ ntdb_close(ntdb);
/* Now, open with CIF, should clear it. */
- tdb = tdb_open("run-83-openhook.tdb", flags[i],
+ ntdb = ntdb_open("run-83-openhook.ntdb", flags[i],
O_RDWR, 0, &cif);
- ok1(tdb);
- ok1(!tdb_exists(tdb, key));
- ok1(tdb_store(tdb, key, key, TDB_REPLACE) == 0);
+ ok1(ntdb);
+ ok1(!ntdb_exists(ntdb, key));
+ ok1(ntdb_store(ntdb, key, key, NTDB_REPLACE) == 0);
/* Agent should not clear it, since it's still open. */
ok1(external_agent_operation(agent, OPEN_WITH_HOOK,
- "run-83-openhook.tdb") == SUCCESS);
+ "run-83-openhook.ntdb") == SUCCESS);
ok1(external_agent_operation(agent, FETCH, "key") == SUCCESS);
ok1(external_agent_operation(agent, CLOSE, "") == SUCCESS);
/* Still exists for us too. */
- ok1(tdb_exists(tdb, key));
+ ok1(ntdb_exists(ntdb, key));
/* Close it, now agent should clear it. */
- tdb_close(tdb);
+ ntdb_close(ntdb);
ok1(external_agent_operation(agent, OPEN_WITH_HOOK,
- "run-83-openhook.tdb") == SUCCESS);
+ "run-83-openhook.ntdb") == SUCCESS);
ok1(external_agent_operation(agent, FETCH, "key") == FAILED);
ok1(external_agent_operation(agent, CLOSE, "") == SUCCESS);
diff --git a/lib/tdb2/test/api-91-get-stats.c b/lib/ntdb/test/api-91-get-stats.c
index 395db3fb18..786885b44c 100644
--- a/lib/tdb2/test/api-91-get-stats.c
+++ b/lib/ntdb/test/api-91-get-stats.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -11,27 +11,27 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 11);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- union tdb_attribute *attr;
- struct tdb_data key = tdb_mkdata("key", 3);
+ union ntdb_attribute *attr;
+ NTDB_DATA key = ntdb_mkdata("key", 3);
- tdb = tdb_open("run-91-get-stats.tdb", flags[i],
+ ntdb = ntdb_open("run-91-get-stats.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- ok1(tdb_store(tdb, key, key, TDB_REPLACE) == 0);
+ ok1(ntdb);
+ ok1(ntdb_store(ntdb, key, key, NTDB_REPLACE) == 0);
/* Use malloc so valgrind will catch overruns. */
attr = malloc(sizeof *attr);
- attr->stats.base.attr = TDB_ATTRIBUTE_STATS;
+ attr->stats.base.attr = NTDB_ATTRIBUTE_STATS;
attr->stats.size = sizeof(*attr);
- ok1(tdb_get_attribute(tdb, attr) == 0);
+ ok1(ntdb_get_attribute(ntdb, attr) == 0);
ok1(attr->stats.size == sizeof(*attr));
ok1(attr->stats.allocs > 0);
ok1(attr->stats.expands > 0);
@@ -39,18 +39,18 @@ int main(int argc, char *argv[])
free(attr);
/* Try short one. */
- attr = malloc(offsetof(struct tdb_attribute_stats, allocs)
+ attr = malloc(offsetof(struct ntdb_attribute_stats, allocs)
+ sizeof(attr->stats.allocs));
- attr->stats.base.attr = TDB_ATTRIBUTE_STATS;
- attr->stats.size = offsetof(struct tdb_attribute_stats, allocs)
+ attr->stats.base.attr = NTDB_ATTRIBUTE_STATS;
+ attr->stats.size = offsetof(struct ntdb_attribute_stats, allocs)
+ sizeof(attr->stats.allocs);
- ok1(tdb_get_attribute(tdb, attr) == 0);
+ ok1(ntdb_get_attribute(ntdb, attr) == 0);
ok1(attr->stats.size == sizeof(*attr));
ok1(attr->stats.allocs > 0);
free(attr);
ok1(tap_log_messages == 0);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
return exit_status();
diff --git a/lib/ntdb/test/api-92-get-set-readonly.c b/lib/ntdb/test/api-92-get-set-readonly.c
new file mode 100644
index 0000000000..7abd304eef
--- /dev/null
+++ b/lib/ntdb/test/api-92-get-set-readonly.c
@@ -0,0 +1,105 @@
+#include "config.h"
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 48);
+
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ /* RW -> R0 */
+ ntdb = ntdb_open("run-92-get-set-readonly.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ ok1(!(ntdb_get_flags(ntdb) & NTDB_RDONLY));
+
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == NTDB_SUCCESS);
+
+ ntdb_add_flag(ntdb, NTDB_RDONLY);
+ ok1(ntdb_get_flags(ntdb) & NTDB_RDONLY);
+
+ /* Can't store, append, delete. */
+ ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 1);
+ ok1(ntdb_append(ntdb, key, data) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 2);
+ ok1(ntdb_delete(ntdb, key) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 3);
+
+ /* Can't start a transaction, or any write lock. */
+ ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 4);
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 5);
+ ok1(ntdb_lockall(ntdb) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 6);
+ ok1(ntdb_wipe_all(ntdb) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 7);
+
+ /* Back to RW. */
+ ntdb_remove_flag(ntdb, NTDB_RDONLY);
+ ok1(!(ntdb_get_flags(ntdb) & NTDB_RDONLY));
+
+ ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY) == NTDB_SUCCESS);
+ ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
+ ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
+
+ ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == NTDB_SUCCESS);
+ ok1(ntdb_transaction_commit(ntdb) == NTDB_SUCCESS);
+
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_SUCCESS);
+ ntdb_chainunlock(ntdb, key);
+ ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
+ ntdb_unlockall(ntdb);
+ ok1(ntdb_wipe_all(ntdb) == NTDB_SUCCESS);
+ ok1(tap_log_messages == 7);
+
+ ntdb_close(ntdb);
+
+ /* R0 -> RW */
+ ntdb = ntdb_open("run-92-get-set-readonly.ntdb", flags[i],
+ O_RDONLY, 0600, &tap_log_attr);
+ ok1(ntdb);
+ ok1(ntdb_get_flags(ntdb) & NTDB_RDONLY);
+
+ /* Can't store, append, delete. */
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 8);
+ ok1(ntdb_append(ntdb, key, data) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 9);
+ ok1(ntdb_delete(ntdb, key) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 10);
+
+ /* Can't start a transaction, or any write lock. */
+ ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 11);
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 12);
+ ok1(ntdb_lockall(ntdb) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 13);
+ ok1(ntdb_wipe_all(ntdb) == NTDB_ERR_RDONLY);
+ ok1(tap_log_messages == 14);
+
+ /* Can't remove NTDB_RDONLY since we opened with O_RDONLY */
+ ntdb_remove_flag(ntdb, NTDB_RDONLY);
+ ok1(tap_log_messages == 15);
+ ok1(ntdb_get_flags(ntdb) & NTDB_RDONLY);
+ ntdb_close(ntdb);
+
+ ok1(tap_log_messages == 15);
+ tap_log_messages = 0;
+ }
+ return exit_status();
+}
diff --git a/lib/tdb2/test/api-93-repack.c b/lib/ntdb/test/api-93-repack.c
index 910eb9b301..168bc24c0a 100644
--- a/lib/tdb2/test/api-93-repack.c
+++ b/lib/ntdb/test/api-93-repack.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -8,21 +8,21 @@
#define NUM_TESTS 1000
-static bool store_all(struct tdb_context *tdb)
+static bool store_all(struct ntdb_context *ntdb)
{
unsigned int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data dbuf = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA dbuf = { (unsigned char *)&i, sizeof(i) };
for (i = 0; i < NUM_TESTS; i++) {
- if (tdb_store(tdb, key, dbuf, TDB_INSERT) != TDB_SUCCESS)
+ if (ntdb_store(ntdb, key, dbuf, NTDB_INSERT) != NTDB_SUCCESS)
return false;
}
return true;
}
-static int mark_entry(struct tdb_context *tdb,
- TDB_DATA key, TDB_DATA data, bool found[])
+static int mark_entry(struct ntdb_context *ntdb,
+ NTDB_DATA key, NTDB_DATA data, bool found[])
{
unsigned int num;
@@ -51,28 +51,28 @@ int main(int argc, char *argv[])
{
unsigned int i;
bool found[NUM_TESTS];
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT
};
plan_tests(sizeof(flags) / sizeof(flags[0]) * 6 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-93-repack.tdb", flags[i],
+ ntdb = ntdb_open("run-93-repack.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
break;
- ok1(store_all(tdb));
+ ok1(store_all(ntdb));
- ok1(tdb_repack(tdb) == TDB_SUCCESS);
+ ok1(ntdb_repack(ntdb) == NTDB_SUCCESS);
memset(found, 0, sizeof(found));
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
- ok1(tdb_traverse(tdb, mark_entry, found) == NUM_TESTS);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
+ ok1(ntdb_traverse(ntdb, mark_entry, found) == NUM_TESTS);
ok1(is_all_set(found, NUM_TESTS));
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/ntdb/test/api-add-remove-flags.c b/lib/ntdb/test/api-add-remove-flags.c
new file mode 100644
index 0000000000..4888c32f06
--- /dev/null
+++ b/lib/ntdb/test/api-add-remove-flags.c
@@ -0,0 +1,89 @@
+#include "private.h" // for ntdb_context
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(87);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("run-add-remove-flags.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ ok1(ntdb_get_flags(ntdb) == ntdb->flags);
+ tap_log_messages = 0;
+ ntdb_add_flag(ntdb, NTDB_NOLOCK);
+ if (flags[i] & NTDB_INTERNAL)
+ ok1(tap_log_messages == 1);
+ else {
+ ok1(tap_log_messages == 0);
+ ok1(ntdb_get_flags(ntdb) & NTDB_NOLOCK);
+ }
+
+ tap_log_messages = 0;
+ ntdb_add_flag(ntdb, NTDB_NOMMAP);
+ if (flags[i] & NTDB_INTERNAL)
+ ok1(tap_log_messages == 1);
+ else {
+ ok1(tap_log_messages == 0);
+ ok1(ntdb_get_flags(ntdb) & NTDB_NOMMAP);
+ ok1(ntdb->file->map_ptr == NULL);
+ }
+
+ tap_log_messages = 0;
+ ntdb_add_flag(ntdb, NTDB_NOSYNC);
+ if (flags[i] & NTDB_INTERNAL)
+ ok1(tap_log_messages == 1);
+ else {
+ ok1(tap_log_messages == 0);
+ ok1(ntdb_get_flags(ntdb) & NTDB_NOSYNC);
+ }
+
+ ok1(ntdb_get_flags(ntdb) == ntdb->flags);
+
+ tap_log_messages = 0;
+ ntdb_remove_flag(ntdb, NTDB_NOLOCK);
+ if (flags[i] & NTDB_INTERNAL)
+ ok1(tap_log_messages == 1);
+ else {
+ ok1(tap_log_messages == 0);
+ ok1(!(ntdb_get_flags(ntdb) & NTDB_NOLOCK));
+ }
+
+ tap_log_messages = 0;
+ ntdb_remove_flag(ntdb, NTDB_NOMMAP);
+ if (flags[i] & NTDB_INTERNAL)
+ ok1(tap_log_messages == 1);
+ else {
+ ok1(tap_log_messages == 0);
+ ok1(!(ntdb_get_flags(ntdb) & NTDB_NOMMAP));
+ ok1(ntdb->file->map_ptr != NULL);
+ }
+
+ tap_log_messages = 0;
+ ntdb_remove_flag(ntdb, NTDB_NOSYNC);
+ if (flags[i] & NTDB_INTERNAL)
+ ok1(tap_log_messages == 1);
+ else {
+ ok1(tap_log_messages == 0);
+ ok1(!(ntdb_get_flags(ntdb) & NTDB_NOSYNC));
+ }
+
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/tdb2/test/api-check-callback.c b/lib/ntdb/test/api-check-callback.c
index 96ef09f3bd..f74f04b598 100644
--- a/lib/tdb2/test/api-check-callback.c
+++ b/lib/ntdb/test/api-check-callback.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -8,77 +8,77 @@
#define NUM_RECORDS 1000
-static bool store_records(struct tdb_context *tdb)
+static bool store_records(struct ntdb_context *ntdb)
{
int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
for (i = 0; i < NUM_RECORDS; i++)
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
return false;
return true;
}
-static enum TDB_ERROR check(struct tdb_data key,
- struct tdb_data data,
+static enum NTDB_ERROR check(NTDB_DATA key,
+ NTDB_DATA data,
bool *array)
{
int val;
if (key.dsize != sizeof(val)) {
diag("Wrong key size: %u\n", key.dsize);
- return TDB_ERR_CORRUPT;
+ return NTDB_ERR_CORRUPT;
}
if (key.dsize != data.dsize
|| memcmp(key.dptr, data.dptr, sizeof(val)) != 0) {
diag("Key and data differ\n");
- return TDB_ERR_CORRUPT;
+ return NTDB_ERR_CORRUPT;
}
memcpy(&val, key.dptr, sizeof(val));
if (val >= NUM_RECORDS || val < 0) {
diag("check value %i\n", val);
- return TDB_ERR_CORRUPT;
+ return NTDB_ERR_CORRUPT;
}
if (array[val]) {
diag("Value %i already seen\n", val);
- return TDB_ERR_CORRUPT;
+ return NTDB_ERR_CORRUPT;
}
array[val] = true;
- return TDB_SUCCESS;
+ return NTDB_SUCCESS;
}
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 };
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
bool array[NUM_RECORDS];
- tdb = tdb_open("run-check-callback.tdb", flags[i],
+ ntdb = ntdb_open("run-check-callback.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
- ok1(store_records(tdb));
+ ok1(store_records(ntdb));
for (j = 0; j < NUM_RECORDS; j++)
array[j] = false;
- ok1(tdb_check(tdb, check, array) == TDB_SUCCESS);
+ ok1(ntdb_check(ntdb, check, array) == NTDB_SUCCESS);
for (j = 0; j < NUM_RECORDS; j++)
if (!array[j])
break;
ok1(j == NUM_RECORDS);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/tdb2/test/api-firstkey-nextkey.c b/lib/ntdb/test/api-firstkey-nextkey.c
index e5a7c5f8b5..da1a68043b 100644
--- a/lib/tdb2/test/api-firstkey-nextkey.c
+++ b/lib/ntdb/test/api-firstkey-nextkey.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -9,14 +9,14 @@
#define NUM_RECORDS 1000
-static bool store_records(struct tdb_context *tdb)
+static bool store_records(struct ntdb_context *ntdb)
{
int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
for (i = 0; i < NUM_RECORDS; i++)
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
return false;
return true;
}
@@ -26,7 +26,7 @@ struct trav_data {
unsigned int calls;
};
-static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *p)
+static int trav(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *p)
{
struct trav_data *td = p;
int val;
@@ -36,8 +36,8 @@ static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *p)
return 0;
}
-/* Since tdb_nextkey frees dptr, we need to clone it. */
-static TDB_DATA dup_key(TDB_DATA key)
+/* Since ntdb_nextkey frees dptr, we need to clone it. */
+static NTDB_DATA dup_key(NTDB_DATA key)
{
void *p = malloc(key.dsize);
memcpy(p, key.dptr, key.dsize);
@@ -50,81 +50,81 @@ int main(int argc, char *argv[])
unsigned int i, j;
int num;
struct trav_data td;
- TDB_DATA k;
- struct tdb_context *tdb;
- union tdb_attribute seed_attr;
- enum TDB_ERROR ecode;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
+ NTDB_DATA k;
+ struct ntdb_context *ntdb;
+ union ntdb_attribute seed_attr;
+ enum NTDB_ERROR ecode;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
seed_attr.base.next = &tap_log_attr;
seed_attr.seed.seed = 6334326220117065685ULL;
plan_tests(sizeof(flags) / sizeof(flags[0])
* (NUM_RECORDS*6 + (NUM_RECORDS-1)*3 + 22) + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("api-firstkey-nextkey.tdb", flags[i],
+ ntdb = ntdb_open("api-firstkey-nextkey.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600,
&seed_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
- ok1(tdb_firstkey(tdb, &k) == TDB_ERR_NOEXIST);
+ ok1(ntdb_firstkey(ntdb, &k) == NTDB_ERR_NOEXIST);
/* One entry... */
k.dptr = (unsigned char *)&num;
k.dsize = sizeof(num);
num = 0;
- ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
- ok1(tdb_firstkey(tdb, &k) == TDB_SUCCESS);
+ ok1(ntdb_store(ntdb, k, k, NTDB_INSERT) == 0);
+ ok1(ntdb_firstkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(num));
ok1(memcmp(k.dptr, &num, sizeof(num)) == 0);
- ok1(tdb_nextkey(tdb, &k) == TDB_ERR_NOEXIST);
+ ok1(ntdb_nextkey(ntdb, &k) == NTDB_ERR_NOEXIST);
/* Two entries. */
k.dptr = (unsigned char *)&num;
k.dsize = sizeof(num);
num = 1;
- ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
- ok1(tdb_firstkey(tdb, &k) == TDB_SUCCESS);
+ ok1(ntdb_store(ntdb, k, k, NTDB_INSERT) == 0);
+ ok1(ntdb_firstkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(num));
memcpy(&num, k.dptr, sizeof(num));
ok1(num == 0 || num == 1);
- ok1(tdb_nextkey(tdb, &k) == TDB_SUCCESS);
+ ok1(ntdb_nextkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(j));
memcpy(&j, k.dptr, sizeof(j));
ok1(j == 0 || j == 1);
ok1(j != num);
- ok1(tdb_nextkey(tdb, &k) == TDB_ERR_NOEXIST);
+ ok1(ntdb_nextkey(ntdb, &k) == NTDB_ERR_NOEXIST);
/* Clean up. */
k.dptr = (unsigned char *)&num;
k.dsize = sizeof(num);
num = 0;
- ok1(tdb_delete(tdb, k) == 0);
+ ok1(ntdb_delete(ntdb, k) == 0);
num = 1;
- ok1(tdb_delete(tdb, k) == 0);
+ ok1(ntdb_delete(ntdb, k) == 0);
/* Now lots of records. */
- ok1(store_records(tdb));
+ ok1(store_records(ntdb));
td.calls = 0;
- num = tdb_traverse(tdb, trav, &td);
+ num = ntdb_traverse(ntdb, trav, &td);
ok1(num == NUM_RECORDS);
ok1(td.calls == NUM_RECORDS);
- /* Simple loop should match tdb_traverse */
- for (j = 0, ecode = tdb_firstkey(tdb, &k); j < td.calls; j++) {
+ /* Simple loop should match ntdb_traverse */
+ for (j = 0, ecode = ntdb_firstkey(ntdb, &k); j < td.calls; j++) {
int val;
- ok1(ecode == TDB_SUCCESS);
+ ok1(ecode == NTDB_SUCCESS);
ok1(k.dsize == sizeof(val));
memcpy(&val, k.dptr, k.dsize);
ok1(td.records[j] == val);
- ecode = tdb_nextkey(tdb, &k);
+ ecode = ntdb_nextkey(ntdb, &k);
}
/* But arbitrary orderings should work too. */
@@ -132,26 +132,26 @@ int main(int argc, char *argv[])
k.dptr = (unsigned char *)&td.records[j-1];
k.dsize = sizeof(td.records[j-1]);
k = dup_key(k);
- ok1(tdb_nextkey(tdb, &k) == TDB_SUCCESS);
+ ok1(ntdb_nextkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(td.records[j]));
ok1(memcmp(k.dptr, &td.records[j], k.dsize) == 0);
free(k.dptr);
}
/* Even delete should work. */
- for (j = 0, ecode = tdb_firstkey(tdb, &k);
- ecode != TDB_ERR_NOEXIST;
+ for (j = 0, ecode = ntdb_firstkey(ntdb, &k);
+ ecode != NTDB_ERR_NOEXIST;
j++) {
- ok1(ecode == TDB_SUCCESS);
+ ok1(ecode == NTDB_SUCCESS);
ok1(k.dsize == 4);
- ok1(tdb_delete(tdb, k) == 0);
- ecode = tdb_nextkey(tdb, &k);
+ ok1(ntdb_delete(ntdb, k) == 0);
+ ecode = ntdb_nextkey(ntdb, &k);
}
diag("delete using first/nextkey gave %u of %u records",
j, NUM_RECORDS);
ok1(j == NUM_RECORDS);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/tdb2/test/api-fork-test.c b/lib/ntdb/test/api-fork-test.c
index 934c71cbe8..57bd686282 100644
--- a/lib/tdb2/test/api-fork-test.c
+++ b/lib/ntdb/test/api-fork-test.c
@@ -1,17 +1,17 @@
/* Test forking while holding lock.
*
* There are only five ways to do this currently:
- * (1) grab a tdb_chainlock, then fork.
- * (2) grab a tdb_lockall, then fork.
- * (3) grab a tdb_lockall_read, then fork.
+ * (1) grab a ntdb_chainlock, then fork.
+ * (2) grab a ntdb_lockall, then fork.
+ * (3) grab a ntdb_lockall_read, then fork.
* (4) start a transaction, then fork.
- * (5) fork from inside a tdb_parse() callback.
+ * (5) fork from inside a ntdb_parse() callback.
*
- * Note that we don't hold a lock across tdb_traverse callbacks, so
+ * Note that we don't hold a lock across ntdb_traverse callbacks, so
* that doesn't matter.
*/
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -22,40 +22,40 @@
#include <stdlib.h>
#include "logging.h"
-static enum TDB_ERROR fork_in_parse(TDB_DATA key, TDB_DATA data,
- struct tdb_context *tdb)
+static enum NTDB_ERROR fork_in_parse(NTDB_DATA key, NTDB_DATA data,
+ struct ntdb_context *ntdb)
{
int status;
if (fork() == 0) {
/* We expect this to fail. */
- if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
exit(1);
- if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
+ if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
exit(1);
if (tap_log_messages != 2)
exit(2);
- tdb_close(tdb);
+ ntdb_close(ntdb);
if (tap_log_messages != 2)
exit(3);
exit(0);
}
wait(&status);
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
- return TDB_SUCCESS;
+ return NTDB_SUCCESS;
}
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
@@ -63,116 +63,116 @@ int main(int argc, char *argv[])
tap_log_messages = 0;
- tdb = tdb_open("run-fork-test.tdb", flags[i],
+ ntdb = ntdb_open("run-fork-test.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
continue;
/* Put a record in here. */
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_SUCCESS);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_SUCCESS);
- ok1(tdb_chainlock(tdb, key) == TDB_SUCCESS);
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_SUCCESS);
if (fork() == 0) {
/* We expect this to fail. */
- if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
return 1;
- if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
+ if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
return 1;
if (tap_log_messages != 2)
return 2;
- tdb_chainunlock(tdb, key);
+ ntdb_chainunlock(ntdb, key);
if (tap_log_messages != 3)
return 3;
- tdb_close(tdb);
+ ntdb_close(ntdb);
if (tap_log_messages != 3)
return 4;
return 0;
}
wait(&status);
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
- tdb_chainunlock(tdb, key);
+ ntdb_chainunlock(ntdb, key);
- ok1(tdb_lockall(tdb) == TDB_SUCCESS);
+ ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
if (fork() == 0) {
/* We expect this to fail. */
- if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
return 1;
- if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
+ if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
return 1;
if (tap_log_messages != 2)
return 2;
- tdb_unlockall(tdb);
+ ntdb_unlockall(ntdb);
if (tap_log_messages != 2)
return 3;
- tdb_close(tdb);
+ ntdb_close(ntdb);
if (tap_log_messages != 2)
return 4;
return 0;
}
wait(&status);
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
- tdb_unlockall(tdb);
+ ntdb_unlockall(ntdb);
- ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
+ ok1(ntdb_lockall_read(ntdb) == NTDB_SUCCESS);
if (fork() == 0) {
/* We expect this to fail. */
/* This would always fail anyway... */
- if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
return 1;
- if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
+ if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
return 1;
if (tap_log_messages != 2)
return 2;
- tdb_unlockall_read(tdb);
+ ntdb_unlockall_read(ntdb);
if (tap_log_messages != 2)
return 3;
- tdb_close(tdb);
+ ntdb_close(ntdb);
if (tap_log_messages != 2)
return 4;
return 0;
}
wait(&status);
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
- tdb_unlockall_read(tdb);
+ ntdb_unlockall_read(ntdb);
- ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
+ ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
/* If transactions is empty, noop "commit" succeeds. */
- ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
+ ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
if (fork() == 0) {
/* We expect this to fail. */
- if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
return 1;
- if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
+ if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
return 1;
if (tap_log_messages != 2)
return 2;
- if (tdb_transaction_commit(tdb) != TDB_ERR_LOCK)
+ if (ntdb_transaction_commit(ntdb) != NTDB_ERR_LOCK)
return 3;
- tdb_close(tdb);
+ ntdb_close(ntdb);
if (tap_log_messages < 3)
return 4;
return 0;
}
wait(&status);
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
- tdb_transaction_cancel(tdb);
+ ntdb_transaction_cancel(ntdb);
- ok1(tdb_parse_record(tdb, key, fork_in_parse, tdb)
- == TDB_SUCCESS);
- tdb_close(tdb);
+ ok1(ntdb_parse_record(ntdb, key, fork_in_parse, ntdb)
+ == NTDB_SUCCESS);
+ ntdb_close(ntdb);
ok1(tap_log_messages == 0);
}
return exit_status();
diff --git a/lib/tdb2/test/api-locktimeout.c b/lib/ntdb/test/api-locktimeout.c
index dabe262f25..cafe067d0b 100644
--- a/lib/tdb2/test/api-locktimeout.c
+++ b/lib/ntdb/test/api-locktimeout.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include "system/wait.h"
#include <sys/types.h>
@@ -94,35 +94,35 @@ static int timeout_lock(int fd, int rw, off_t off, off_t len, bool waitflag,
return ret;
}
-static int tdb_chainlock_with_timeout_internal(struct tdb_context *tdb,
- TDB_DATA key,
+static int ntdb_chainlock_with_timeout_internal(struct ntdb_context *ntdb,
+ NTDB_DATA key,
unsigned int timeout,
int rw_type)
{
- union tdb_attribute locking;
- enum TDB_ERROR ecode;
+ union ntdb_attribute locking;
+ enum NTDB_ERROR ecode;
if (timeout) {
- locking.base.attr = TDB_ATTRIBUTE_FLOCK;
- ecode = tdb_get_attribute(tdb, &locking);
- if (ecode != TDB_SUCCESS)
+ locking.base.attr = NTDB_ATTRIBUTE_FLOCK;
+ ecode = ntdb_get_attribute(ntdb, &locking);
+ if (ecode != NTDB_SUCCESS)
return ecode;
/* Replace locking function with our own. */
locking.flock.data = &timeout;
locking.flock.lock = timeout_lock;
- ecode = tdb_set_attribute(tdb, &locking);
- if (ecode != TDB_SUCCESS)
+ ecode = ntdb_set_attribute(ntdb, &locking);
+ if (ecode != NTDB_SUCCESS)
return ecode;
}
if (rw_type == F_RDLCK)
- ecode = tdb_chainlock_read(tdb, key);
+ ecode = ntdb_chainlock_read(ntdb, key);
else
- ecode = tdb_chainlock(tdb, key);
+ ecode = ntdb_chainlock(ntdb, key);
if (timeout) {
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
}
return ecode;
}
@@ -130,10 +130,10 @@ static int tdb_chainlock_with_timeout_internal(struct tdb_context *tdb,
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- TDB_DATA key = tdb_mkdata("hello", 5);
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+ struct ntdb_context *ntdb;
+ NTDB_DATA key = ntdb_mkdata("hello", 5);
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
struct agent *agent;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 15);
@@ -141,52 +141,52 @@ int main(int argc, char *argv[])
agent = prepare_external_agent();
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- enum TDB_ERROR ecode;
- tdb = tdb_open("run-locktimeout.tdb", flags[i],
+ enum NTDB_ERROR ecode;
+ ntdb = ntdb_open("run-locktimeout.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
break;
/* Simple cases: should succeed. */
- ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
+ ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
F_RDLCK);
- ok1(ecode == TDB_SUCCESS);
+ ok1(ecode == NTDB_SUCCESS);
ok1(tap_log_messages == 0);
- tdb_chainunlock_read(tdb, key);
+ ntdb_chainunlock_read(ntdb, key);
ok1(tap_log_messages == 0);
- ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
+ ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
F_WRLCK);
- ok1(ecode == TDB_SUCCESS);
+ ok1(ecode == NTDB_SUCCESS);
ok1(tap_log_messages == 0);
- tdb_chainunlock(tdb, key);
+ ntdb_chainunlock(ntdb, key);
ok1(tap_log_messages == 0);
/* OK, get agent to start transaction, then we should time out. */
- ok1(external_agent_operation(agent, OPEN, "run-locktimeout.tdb")
+ ok1(external_agent_operation(agent, OPEN, "run-locktimeout.ntdb")
== SUCCESS);
ok1(external_agent_operation(agent, TRANSACTION_START, "")
== SUCCESS);
- ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
+ ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
F_WRLCK);
- ok1(ecode == TDB_ERR_LOCK);
+ ok1(ecode == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
/* Even if we get a different signal, should be fine. */
CatchSignal(SIGUSR1, do_nothing);
external_agent_operation(agent, SEND_SIGNAL, "");
- ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
+ ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
F_WRLCK);
- ok1(ecode == TDB_ERR_LOCK);
+ ok1(ecode == NTDB_ERR_LOCK);
ok1(tap_log_messages == 0);
ok1(external_agent_operation(agent, TRANSACTION_COMMIT, "")
== SUCCESS);
ok1(external_agent_operation(agent, CLOSE, "")
== SUCCESS);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
free_external_agent(agent);
return exit_status();
diff --git a/lib/tdb2/test/api-missing-entries.c b/lib/ntdb/test/api-missing-entries.c
index c81839bc05..1c8064f945 100644
--- a/lib/tdb2/test/api-missing-entries.c
+++ b/lib/ntdb/test/api-missing-entries.c
@@ -1,6 +1,6 @@
/* Another test revealed that we lost an entry. This reproduces it. */
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include <ccan/hash/hash.h>
#include "tap-interface.h"
#include <sys/types.h>
@@ -20,23 +20,23 @@ static uint64_t failhash(const void *key, size_t len, uint64_t seed, void *p)
int main(int argc, char *argv[])
{
int i;
- struct tdb_context *tdb;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
- union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
+ struct ntdb_context *ntdb;
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
+ union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
.fn = failhash } };
hattr.base.next = &tap_log_attr;
plan_tests(1 + NUM_RECORDS + 2);
- tdb = tdb_open("run-missing-entries.tdb", TDB_INTERNAL,
+ ntdb = ntdb_open("run-missing-entries.ntdb", NTDB_INTERNAL,
O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
- if (ok1(tdb)) {
+ if (ok1(ntdb)) {
for (i = 0; i < NUM_RECORDS; i++) {
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
}
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/ntdb/test/api-open-multiple-times.c b/lib/ntdb/test/api-open-multiple-times.c
new file mode 100644
index 0000000000..70bad00568
--- /dev/null
+++ b/lib/ntdb/test/api-open-multiple-times.c
@@ -0,0 +1,83 @@
+#include "config.h"
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb, *ntdb2;
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 28);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("run-open-multiple-times.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ ntdb2 = ntdb_open("run-open-multiple-times.ntdb", flags[i],
+ O_RDWR|O_CREAT, 0600, &tap_log_attr);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb2, NULL, NULL) == 0);
+
+ /* Store in one, fetch in the other. */
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
+ ok1(ntdb_fetch(ntdb2, key, &d) == NTDB_SUCCESS);
+ ok1(ntdb_deq(d, data));
+ free(d.dptr);
+
+ /* Vice versa, with delete. */
+ ok1(ntdb_delete(ntdb2, key) == 0);
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_NOEXIST);
+
+ /* OK, now close first one, check second still good. */
+ ok1(ntdb_close(ntdb) == 0);
+
+ ok1(ntdb_store(ntdb2, key, data, NTDB_REPLACE) == 0);
+ ok1(ntdb_fetch(ntdb2, key, &d) == NTDB_SUCCESS);
+ ok1(ntdb_deq(d, data));
+ free(d.dptr);
+
+ /* Reopen */
+ ntdb = ntdb_open("run-open-multiple-times.ntdb", flags[i],
+ O_RDWR|O_CREAT, 0600, &tap_log_attr);
+ ok1(ntdb);
+
+ ok1(ntdb_transaction_start(ntdb2) == 0);
+
+ /* Anything in the other one should fail. */
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
+ ok1(tap_log_messages == 1);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
+ ok1(tap_log_messages == 2);
+ ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
+ ok1(tap_log_messages == 3);
+ ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
+ ok1(tap_log_messages == 4);
+
+ /* Transaciton should work as normal. */
+ ok1(ntdb_store(ntdb2, key, data, NTDB_REPLACE) == NTDB_SUCCESS);
+
+ /* Now... try closing with locks held. */
+ ok1(ntdb_close(ntdb2) == 0);
+
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(ntdb_deq(d, data));
+ free(d.dptr);
+ ok1(ntdb_close(ntdb) == 0);
+ ok1(tap_log_messages == 4);
+ tap_log_messages = 0;
+ }
+
+ return exit_status();
+}
diff --git a/lib/tdb2/test/api-record-expand.c b/lib/ntdb/test/api-record-expand.c
index 34799ebe5e..cea5a10bfb 100644
--- a/lib/tdb2/test/api-record-expand.c
+++ b/lib/ntdb/test/api-record-expand.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -13,12 +13,12 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data;
data.dptr = malloc(MAX_SIZE);
memset(data.dptr, 0x24, MAX_SIZE);
@@ -26,23 +26,23 @@ int main(int argc, char *argv[])
plan_tests(sizeof(flags) / sizeof(flags[0])
* (3 + (1 + (MAX_SIZE/SIZE_STEP)) * 2) + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-record-expand.tdb", flags[i],
+ ntdb = ntdb_open("run-record-expand.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
data.dsize = 0;
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
for (data.dsize = 0;
data.dsize < MAX_SIZE;
data.dsize += SIZE_STEP) {
memset(data.dptr, data.dsize, data.dsize);
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
}
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
free(data.dptr);
diff --git a/lib/ntdb/test/api-simple-delete.c b/lib/ntdb/test/api-simple-delete.c
new file mode 100644
index 0000000000..2b20e199ee
--- /dev/null
+++ b/lib/ntdb/test/api-simple-delete.c
@@ -0,0 +1,39 @@
+#include "config.h"
+#include "ntdb.h"
+#include "tap-interface.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ ntdb = ntdb_open("run-simple-delete.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ if (ntdb) {
+ /* Delete should fail. */
+ ok1(ntdb_delete(ntdb, key) == NTDB_ERR_NOEXIST);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ /* Insert should succeed. */
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ /* Delete should now work. */
+ ok1(ntdb_delete(ntdb, key) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ }
+ }
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/tdb2/test/api-summary.c b/lib/ntdb/test/api-summary.c
index e9dfd270e9..8060ef29be 100644
--- a/lib/tdb2/test/api-summary.c
+++ b/lib/ntdb/test/api-summary.c
@@ -1,5 +1,5 @@
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "tap-interface.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -10,38 +10,38 @@
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 };
- struct tdb_data key = { (unsigned char *)&j, sizeof(j) };
- struct tdb_data data = { (unsigned char *)&j, sizeof(j) };
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
+ NTDB_DATA data = { (unsigned char *)&j, sizeof(j) };
char *summary;
plan_tests(sizeof(flags) / sizeof(flags[0]) * (1 + 2 * 5) + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-summary.tdb", flags[i],
+ ntdb = ntdb_open("run-summary.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
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");
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
+ fail("Storing in ntdb");
}
for (j = 0;
- j <= TDB_SUMMARY_HISTOGRAMS;
- j += TDB_SUMMARY_HISTOGRAMS) {
- ok1(tdb_summary(tdb, j, &summary) == TDB_SUCCESS);
+ j <= NTDB_SUMMARY_HISTOGRAMS;
+ j += NTDB_SUMMARY_HISTOGRAMS) {
+ ok1(ntdb_summary(ntdb, j, &summary) == NTDB_SUCCESS);
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"));
- if (j == TDB_SUMMARY_HISTOGRAMS) {
+ if (j == NTDB_SUMMARY_HISTOGRAMS) {
ok1(strstr(summary, "|")
&& strstr(summary, "*"));
} else {
@@ -50,7 +50,7 @@ int main(int argc, char *argv[])
}
free(summary);
}
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/tdb2/test/external-agent.c b/lib/ntdb/test/external-agent.c
index e8cff95728..098d0cb595 100644
--- a/lib/tdb2/test/external-agent.c
+++ b/lib/ntdb/test/external-agent.c
@@ -14,14 +14,14 @@
#include <stdio.h>
#include <stdarg.h>
-static struct tdb_context *tdb;
+static struct ntdb_context *ntdb;
void (*external_agent_free)(void *) = free;
-static enum TDB_ERROR clear_if_first(int fd, void *arg)
+static enum NTDB_ERROR clear_if_first(int fd, void *arg)
{
/* We hold a lock offset 4 always, so we can tell if anyone is holding it.
- * (This is compatible with tdb1's TDB_CLEAR_IF_FIRST flag). */
+ * (This is compatible with tdb's TDB_CLEAR_IF_FIRST flag). */
struct flock fl;
fl.l_type = F_WRLCK;
@@ -33,73 +33,73 @@ static enum TDB_ERROR clear_if_first(int fd, void *arg)
/* We must be first ones to open it! */
diag("agent truncating file!");
if (ftruncate(fd, 0) != 0) {
- return TDB_ERR_IO;
+ return NTDB_ERR_IO;
}
}
fl.l_type = F_RDLCK;
if (fcntl(fd, F_SETLKW, &fl) != 0) {
- return TDB_ERR_IO;
+ return NTDB_ERR_IO;
}
- return TDB_SUCCESS;
+ return NTDB_SUCCESS;
}
static enum agent_return do_operation(enum operation op, const char *name)
{
- TDB_DATA k;
+ NTDB_DATA k;
enum agent_return ret;
- TDB_DATA data;
- enum TDB_ERROR ecode;
- union tdb_attribute cif;
+ NTDB_DATA data;
+ enum NTDB_ERROR ecode;
+ union ntdb_attribute cif;
- if (op != OPEN && op != OPEN_WITH_HOOK && !tdb) {
- diag("external: No tdb open!");
+ if (op != OPEN && op != OPEN_WITH_HOOK && !ntdb) {
+ diag("external: No ntdb open!");
return OTHER_FAILURE;
}
diag("external: %s", operation_name(op));
- k = tdb_mkdata(name, strlen(name));
+ k = ntdb_mkdata(name, strlen(name));
locking_would_block = 0;
switch (op) {
case OPEN:
- if (tdb) {
- diag("Already have tdb %s open", tdb_name(tdb));
+ if (ntdb) {
+ diag("Already have ntdb %s open", ntdb_name(ntdb));
return OTHER_FAILURE;
}
- tdb = tdb_open(name, TDB_DEFAULT, O_RDWR, 0, &tap_log_attr);
- if (!tdb) {
+ ntdb = ntdb_open(name, NTDB_DEFAULT, O_RDWR, 0, &tap_log_attr);
+ if (!ntdb) {
if (!locking_would_block)
- diag("Opening tdb gave %s", strerror(errno));
+ diag("Opening ntdb gave %s", strerror(errno));
forget_locking();
ret = OTHER_FAILURE;
} else
ret = SUCCESS;
break;
case OPEN_WITH_HOOK:
- if (tdb) {
- diag("Already have tdb %s open", tdb_name(tdb));
+ if (ntdb) {
+ diag("Already have ntdb %s open", ntdb_name(ntdb));
return OTHER_FAILURE;
}
- cif.openhook.base.attr = TDB_ATTRIBUTE_OPENHOOK;
+ cif.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
cif.openhook.base.next = &tap_log_attr;
cif.openhook.fn = clear_if_first;
- tdb = tdb_open(name, TDB_DEFAULT, O_RDWR, 0, &cif);
- if (!tdb) {
+ ntdb = ntdb_open(name, NTDB_DEFAULT, O_RDWR, 0, &cif);
+ if (!ntdb) {
if (!locking_would_block)
- diag("Opening tdb gave %s", strerror(errno));
+ diag("Opening ntdb gave %s", strerror(errno));
forget_locking();
ret = OTHER_FAILURE;
} else
ret = SUCCESS;
break;
case FETCH:
- ecode = tdb_fetch(tdb, k, &data);
- if (ecode == TDB_ERR_NOEXIST) {
+ ecode = ntdb_fetch(ntdb, k, &data);
+ if (ecode == NTDB_ERR_NOEXIST) {
ret = FAILED;
} else if (ecode < 0) {
ret = OTHER_FAILURE;
- } else if (!tdb_deq(data, k)) {
+ } else if (!ntdb_deq(data, k)) {
ret = OTHER_FAILURE;
external_agent_free(data.dptr);
} else {
@@ -108,23 +108,23 @@ static enum agent_return do_operation(enum operation op, const char *name)
}
break;
case STORE:
- ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
+ ret = ntdb_store(ntdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
break;
case TRANSACTION_START:
- ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
+ ret = ntdb_transaction_start(ntdb) == 0 ? SUCCESS : OTHER_FAILURE;
break;
case TRANSACTION_COMMIT:
- ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE;
+ ret = ntdb_transaction_commit(ntdb)==0 ? SUCCESS : OTHER_FAILURE;
break;
case NEEDS_RECOVERY:
- ret = external_agent_needs_rec(tdb);
+ ret = external_agent_needs_rec(ntdb);
break;
case CHECK:
- ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
+ ret = ntdb_check(ntdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
break;
case CLOSE:
- ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
- tdb = NULL;
+ ret = ntdb_close(ntdb) == 0 ? SUCCESS : OTHER_FAILURE;
+ ntdb = NULL;
break;
case SEND_SIGNAL:
/* We do this async */
@@ -144,7 +144,7 @@ struct agent {
int cmdfd, responsefd;
};
-/* Do this before doing any tdb stuff. Return handle, or NULL. */
+/* Do this before doing any ntdb stuff. Return handle, or NULL. */
struct agent *prepare_external_agent(void)
{
int pid, ret;
diff --git a/lib/tdb2/test/external-agent.h b/lib/ntdb/test/external-agent.h
index c4cd2b148d..c6b83d5b49 100644
--- a/lib/tdb2/test/external-agent.h
+++ b/lib/ntdb/test/external-agent.h
@@ -1,5 +1,5 @@
-#ifndef TDB2_TEST_EXTERNAL_AGENT_H
-#define TDB2_TEST_EXTERNAL_AGENT_H
+#ifndef NTDB_TEST_EXTERNAL_AGENT_H
+#define NTDB_TEST_EXTERNAL_AGENT_H
/* For locking tests, we need a different process to try things at
* various times. */
@@ -16,7 +16,7 @@ enum operation {
CLOSE,
};
-/* Do this before doing any tdb stuff. Return handle, or -1. */
+/* Do this before doing any ntdb stuff. Return handle, or -1. */
struct agent *prepare_external_agent(void);
enum agent_return {
@@ -28,14 +28,14 @@ enum agent_return {
};
/* Ask the external agent to try to do an operation.
- * name == tdb name for OPEN/OPEN_WITH_CLEAR_IF_FIRST,
+ * name == ntdb 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);
-/* Hook into free() on tdb_data in external agent. */
+/* Hook into free() on ntdb_data in external agent. */
extern void (*external_agent_free)(void *);
/* Mapping enum -> string. */
@@ -45,7 +45,7 @@ const char *operation_name(enum operation op);
void free_external_agent(struct agent *agent);
/* Internal use: */
-struct tdb_context;
-enum agent_return external_agent_needs_rec(struct tdb_context *tdb);
+struct ntdb_context;
+enum agent_return external_agent_needs_rec(struct ntdb_context *ntdb);
-#endif /* TDB2_TEST_EXTERNAL_AGENT_H */
+#endif /* NTDB_TEST_EXTERNAL_AGENT_H */
diff --git a/lib/tdb2/test/failtest_helper.c b/lib/ntdb/test/failtest_helper.c
index 386f1c2379..cc110919c3 100644
--- a/lib/tdb2/test/failtest_helper.c
+++ b/lib/ntdb/test/failtest_helper.c
@@ -51,8 +51,8 @@ bool exit_check_log(struct tlist_calls *history)
if (failmatch(i, URANDOM_READ))
continue;
- /* Initial allocation of tdb doesn't log. */
- if (failmatch(i, INITIAL_TDB_MALLOC))
+ /* Initial allocation of ntdb doesn't log. */
+ if (failmatch(i, INITIAL_NTDB_MALLOC))
continue;
/* We don't block "failures" on non-blocking locks. */
@@ -77,7 +77,7 @@ block_repeat_failures(struct tlist_calls *history)
if (failtest_suppress)
return FAIL_DONT_FAIL;
- if (failmatch(last, INITIAL_TDB_MALLOC)
+ if (failmatch(last, INITIAL_NTDB_MALLOC)
|| failmatch(last, URANDOM_OPEN)
|| failmatch(last, URANDOM_READ)) {
return FAIL_PROBE;
diff --git a/lib/tdb2/test/failtest_helper.h b/lib/ntdb/test/failtest_helper.h
index 3c509e7c38..e754636402 100644
--- a/lib/tdb2/test/failtest_helper.h
+++ b/lib/ntdb/test/failtest_helper.h
@@ -1,10 +1,10 @@
-#ifndef TDB2_TEST_FAILTEST_HELPER_H
-#define TDB2_TEST_FAILTEST_HELPER_H
+#ifndef NTDB_TEST_FAILTEST_HELPER_H
+#define NTDB_TEST_FAILTEST_HELPER_H
#include <ccan/failtest/failtest.h>
#include <stdbool.h>
/* FIXME: Check these! */
-#define INITIAL_TDB_MALLOC "open.c", 403, FAILTEST_MALLOC
+#define INITIAL_NTDB_MALLOC "open.c", 403, FAILTEST_MALLOC
#define URANDOM_OPEN "open.c", 62, FAILTEST_OPEN
#define URANDOM_READ "open.c", 42, FAILTEST_READ
@@ -16,4 +16,4 @@ enum failtest_result block_repeat_failures(struct tlist_calls *history);
/* Set this to suppress failure. */
extern bool failtest_suppress;
-#endif /* TDB2_TEST_LOGGING_H */
+#endif /* NTDB_TEST_LOGGING_H */
diff --git a/lib/ntdb/test/helpapi-external-agent.c b/lib/ntdb/test/helpapi-external-agent.c
new file mode 100644
index 0000000000..eb81399072
--- /dev/null
+++ b/lib/ntdb/test/helpapi-external-agent.c
@@ -0,0 +1,7 @@
+#include "external-agent.h"
+
+/* This isn't possible with via the ntdb API, but this makes it link. */
+enum agent_return external_agent_needs_rec(struct ntdb_context *ntdb)
+{
+ return FAILED;
+}
diff --git a/lib/ntdb/test/helprun-external-agent.c b/lib/ntdb/test/helprun-external-agent.c
new file mode 100644
index 0000000000..81a3fe881d
--- /dev/null
+++ b/lib/ntdb/test/helprun-external-agent.c
@@ -0,0 +1,7 @@
+#include "external-agent.h"
+#include "private.h"
+
+enum agent_return external_agent_needs_rec(struct ntdb_context *ntdb)
+{
+ return ntdb_needs_recovery(ntdb) ? SUCCESS : FAILED;
+}
diff --git a/lib/ntdb/test/helprun-layout.c b/lib/ntdb/test/helprun-layout.c
new file mode 100644
index 0000000000..c8f1fd03c4
--- /dev/null
+++ b/lib/ntdb/test/helprun-layout.c
@@ -0,0 +1,402 @@
+/* NTDB tools to create various canned database layouts. */
+#include "layout.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ccan/err/err.h>
+#include "logging.h"
+
+struct ntdb_layout *new_ntdb_layout(void)
+{
+ struct ntdb_layout *layout = malloc(sizeof(*layout));
+ layout->num_elems = 0;
+ layout->elem = NULL;
+ return layout;
+}
+
+static void add(struct ntdb_layout *layout, union ntdb_layout_elem elem)
+{
+ layout->elem = realloc(layout->elem,
+ sizeof(layout->elem[0])
+ * (layout->num_elems+1));
+ layout->elem[layout->num_elems++] = elem;
+}
+
+void ntdb_layout_add_freetable(struct ntdb_layout *layout)
+{
+ union ntdb_layout_elem elem;
+ elem.base.type = FREETABLE;
+ add(layout, elem);
+}
+
+void ntdb_layout_add_free(struct ntdb_layout *layout, ntdb_len_t len,
+ unsigned ftable)
+{
+ union ntdb_layout_elem elem;
+ elem.base.type = FREE;
+ elem.free.len = len;
+ elem.free.ftable_num = ftable;
+ add(layout, elem);
+}
+
+void ntdb_layout_add_capability(struct ntdb_layout *layout,
+ uint64_t type,
+ bool write_breaks,
+ bool check_breaks,
+ bool open_breaks,
+ ntdb_len_t extra)
+{
+ union ntdb_layout_elem elem;
+ elem.base.type = CAPABILITY;
+ elem.capability.type = type;
+ if (write_breaks)
+ elem.capability.type |= NTDB_CAP_NOWRITE;
+ if (open_breaks)
+ elem.capability.type |= NTDB_CAP_NOOPEN;
+ if (check_breaks)
+ elem.capability.type |= NTDB_CAP_NOCHECK;
+ elem.capability.extra = extra;
+ add(layout, elem);
+}
+
+static NTDB_DATA dup_key(NTDB_DATA key)
+{
+ NTDB_DATA ret;
+ ret.dsize = key.dsize;
+ ret.dptr = malloc(ret.dsize);
+ memcpy(ret.dptr, key.dptr, ret.dsize);
+ return ret;
+}
+
+void ntdb_layout_add_used(struct ntdb_layout *layout,
+ NTDB_DATA key, NTDB_DATA data,
+ ntdb_len_t extra)
+{
+ union ntdb_layout_elem elem;
+ elem.base.type = DATA;
+ elem.used.key = dup_key(key);
+ elem.used.data = dup_key(data);
+ elem.used.extra = extra;
+ add(layout, elem);
+}
+
+static ntdb_len_t free_record_len(ntdb_len_t len)
+{
+ return sizeof(struct ntdb_used_record) + len;
+}
+
+static ntdb_len_t data_record_len(struct tle_used *used)
+{
+ ntdb_len_t len;
+ len = sizeof(struct ntdb_used_record)
+ + used->key.dsize + used->data.dsize + used->extra;
+ assert(len >= sizeof(struct ntdb_free_record));
+ return len;
+}
+
+static ntdb_len_t hashtable_len(struct tle_hashtable *htable)
+{
+ return sizeof(struct ntdb_used_record)
+ + (sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS)
+ + htable->extra;
+}
+
+static ntdb_len_t capability_len(struct tle_capability *cap)
+{
+ return sizeof(struct ntdb_capability) + cap->extra;
+}
+
+static ntdb_len_t freetable_len(struct tle_freetable *ftable)
+{
+ return sizeof(struct ntdb_freetable);
+}
+
+static void set_free_record(void *mem, ntdb_len_t len)
+{
+ /* We do all the work in add_to_freetable */
+}
+
+static void add_zero_pad(struct ntdb_used_record *u, size_t len, size_t extra)
+{
+ if (extra)
+ ((char *)(u + 1))[len] = '\0';
+}
+
+static void set_data_record(void *mem, struct ntdb_context *ntdb,
+ struct tle_used *used)
+{
+ struct ntdb_used_record *u = mem;
+
+ set_header(ntdb, u, NTDB_USED_MAGIC, used->key.dsize, used->data.dsize,
+ used->key.dsize + used->data.dsize + used->extra,
+ ntdb_hash(ntdb, used->key.dptr, used->key.dsize));
+ memcpy(u + 1, used->key.dptr, used->key.dsize);
+ memcpy((char *)(u + 1) + used->key.dsize,
+ used->data.dptr, used->data.dsize);
+ add_zero_pad(u, used->key.dsize + used->data.dsize, used->extra);
+}
+
+static void set_hashtable(void *mem, struct ntdb_context *ntdb,
+ struct tle_hashtable *htable)
+{
+ struct ntdb_used_record *u = mem;
+ ntdb_len_t len = sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS;
+
+ set_header(ntdb, u, NTDB_HTABLE_MAGIC, 0, len, len + htable->extra, 0);
+ memset(u + 1, 0, len);
+ add_zero_pad(u, len, htable->extra);
+}
+
+static void set_capability(void *mem, struct ntdb_context *ntdb,
+ struct tle_capability *cap, struct ntdb_header *hdr,
+ ntdb_off_t last_cap)
+{
+ struct ntdb_capability *c = mem;
+ ntdb_len_t len = sizeof(*c) - sizeof(struct ntdb_used_record) + cap->extra;
+
+ c->type = cap->type;
+ c->next = 0;
+ set_header(ntdb, &c->hdr, NTDB_CAP_MAGIC, 0, len, len, 0);
+
+ /* Append to capability list. */
+ if (!last_cap) {
+ hdr->capabilities = cap->base.off;
+ } else {
+ c = (struct ntdb_capability *)((char *)hdr + last_cap);
+ c->next = cap->base.off;
+ }
+}
+
+static void set_freetable(void *mem, struct ntdb_context *ntdb,
+ struct tle_freetable *freetable, struct ntdb_header *hdr,
+ ntdb_off_t last_ftable)
+{
+ struct ntdb_freetable *ftable = mem;
+ memset(ftable, 0, sizeof(*ftable));
+ set_header(ntdb, &ftable->hdr, NTDB_FTABLE_MAGIC, 0,
+ sizeof(*ftable) - sizeof(ftable->hdr),
+ sizeof(*ftable) - sizeof(ftable->hdr), 0);
+
+ if (last_ftable) {
+ ftable = (struct ntdb_freetable *)((char *)hdr + last_ftable);
+ ftable->next = freetable->base.off;
+ } else {
+ hdr->free_table = freetable->base.off;
+ }
+}
+
+static void add_to_freetable(struct ntdb_context *ntdb,
+ ntdb_off_t eoff,
+ ntdb_off_t elen,
+ unsigned ftable,
+ struct tle_freetable *freetable)
+{
+ ntdb->ftable_off = freetable->base.off;
+ ntdb->ftable = ftable;
+ add_free_record(ntdb, eoff, sizeof(struct ntdb_used_record) + elen,
+ NTDB_LOCK_WAIT, false);
+}
+
+static ntdb_off_t hbucket_off(ntdb_off_t group_start, unsigned ingroup)
+{
+ return group_start
+ + (ingroup % (1 << NTDB_HASH_GROUP_BITS)) * sizeof(ntdb_off_t);
+}
+
+/* Get bits from a value. */
+static uint32_t bits(uint64_t val, unsigned start, unsigned num)
+{
+ assert(num <= 32);
+ return (val >> start) & ((1U << num) - 1);
+}
+
+/* We take bits from the top: that way we can lock whole sections of the hash
+ * by using lock ranges. */
+static uint32_t use_bits(uint64_t h, unsigned num, unsigned *used)
+{
+ *used += num;
+ return bits(h, 64 - *used, num);
+}
+
+static ntdb_off_t encode_offset(ntdb_off_t new_off, unsigned bucket,
+ uint64_t h)
+{
+ return bucket
+ | new_off
+ | ((uint64_t)bits(h, 64 - NTDB_OFF_UPPER_STEAL_EXTRA,
+ NTDB_OFF_UPPER_STEAL_EXTRA)
+ << NTDB_OFF_HASH_EXTRA_BIT);
+}
+
+/* FIXME: Our hash table handling here is primitive: we don't expand! */
+static void add_to_hashtable(struct ntdb_context *ntdb,
+ ntdb_off_t eoff,
+ NTDB_DATA key)
+{
+ uint64_t h = ntdb_hash(ntdb, key.dptr, key.dsize);
+ ntdb_off_t b_off, group_start;
+ unsigned i, group, in_group;
+ unsigned used = 0;
+
+ group = use_bits(h, NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS, &used);
+ in_group = use_bits(h, NTDB_HASH_GROUP_BITS, &used);
+
+ group_start = offsetof(struct ntdb_header, hashtable)
+ + group * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
+
+ for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
+ unsigned bucket = (in_group + i) % (1 << NTDB_HASH_GROUP_BITS);
+
+ b_off = hbucket_off(group_start, bucket);
+ if (ntdb_read_off(ntdb, b_off) == 0) {
+ ntdb_write_off(ntdb, b_off,
+ encode_offset(eoff, in_group, h));
+ return;
+ }
+ }
+ abort();
+}
+
+static struct tle_freetable *find_ftable(struct ntdb_layout *layout, unsigned num)
+{
+ unsigned i;
+
+ for (i = 0; i < layout->num_elems; i++) {
+ if (layout->elem[i].base.type != FREETABLE)
+ continue;
+ if (num == 0)
+ return &layout->elem[i].ftable;
+ num--;
+ }
+ abort();
+}
+
+/* FIXME: Support NTDB_CONVERT */
+struct ntdb_context *ntdb_layout_get(struct ntdb_layout *layout,
+ void (*freefn)(void *),
+ union ntdb_attribute *attr)
+{
+ unsigned int i;
+ ntdb_off_t off, len, last_ftable, last_cap;
+ char *mem;
+ struct ntdb_context *ntdb;
+
+ off = sizeof(struct ntdb_header);
+
+ /* First pass of layout: calc lengths */
+ for (i = 0; i < layout->num_elems; i++) {
+ union ntdb_layout_elem *e = &layout->elem[i];
+ e->base.off = off;
+ switch (e->base.type) {
+ case FREETABLE:
+ len = freetable_len(&e->ftable);
+ break;
+ case FREE:
+ len = free_record_len(e->free.len);
+ break;
+ case DATA:
+ len = data_record_len(&e->used);
+ break;
+ case HASHTABLE:
+ len = hashtable_len(&e->hashtable);
+ break;
+ case CAPABILITY:
+ len = capability_len(&e->capability);
+ break;
+ default:
+ abort();
+ }
+ off += len;
+ }
+
+ mem = malloc(off);
+ /* Fill with some weird pattern. */
+ memset(mem, 0x99, off);
+ /* Now populate our header, cribbing from a real NTDB header. */
+ ntdb = ntdb_open(NULL, NTDB_INTERNAL, O_RDWR, 0, attr);
+ memcpy(mem, ntdb->file->map_ptr, sizeof(struct ntdb_header));
+
+ /* Mug the ntdb we have to make it use this. */
+ freefn(ntdb->file->map_ptr);
+ ntdb->file->map_ptr = mem;
+ ntdb->file->map_size = off;
+
+ last_ftable = 0;
+ last_cap = 0;
+ for (i = 0; i < layout->num_elems; i++) {
+ union ntdb_layout_elem *e = &layout->elem[i];
+ switch (e->base.type) {
+ case FREETABLE:
+ set_freetable(mem + e->base.off, ntdb, &e->ftable,
+ (struct ntdb_header *)mem, last_ftable);
+ last_ftable = e->base.off;
+ break;
+ case FREE:
+ set_free_record(mem + e->base.off, e->free.len);
+ break;
+ case DATA:
+ set_data_record(mem + e->base.off, ntdb, &e->used);
+ break;
+ case HASHTABLE:
+ set_hashtable(mem + e->base.off, ntdb, &e->hashtable);
+ break;
+ case CAPABILITY:
+ set_capability(mem + e->base.off, ntdb, &e->capability,
+ (struct ntdb_header *)mem, last_cap);
+ last_cap = e->base.off;
+ break;
+ }
+ }
+ /* Must have a free table! */
+ assert(last_ftable);
+
+ /* Now fill the free and hash tables. */
+ for (i = 0; i < layout->num_elems; i++) {
+ union ntdb_layout_elem *e = &layout->elem[i];
+ switch (e->base.type) {
+ case FREE:
+ add_to_freetable(ntdb, e->base.off, e->free.len,
+ e->free.ftable_num,
+ find_ftable(layout, e->free.ftable_num));
+ break;
+ case DATA:
+ add_to_hashtable(ntdb, e->base.off, e->used.key);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ntdb->ftable_off = find_ftable(layout, 0)->base.off;
+ return ntdb;
+}
+
+void ntdb_layout_write(struct ntdb_layout *layout, void (*freefn)(void *),
+ union ntdb_attribute *attr, const char *filename)
+{
+ struct ntdb_context *ntdb = ntdb_layout_get(layout, freefn, attr);
+ int fd;
+
+ fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600);
+ if (fd < 0)
+ err(1, "opening %s for writing", filename);
+ if (write(fd, ntdb->file->map_ptr, ntdb->file->map_size)
+ != ntdb->file->map_size)
+ err(1, "writing %s", filename);
+ close(fd);
+ ntdb_close(ntdb);
+}
+
+void ntdb_layout_free(struct ntdb_layout *layout)
+{
+ unsigned int i;
+
+ for (i = 0; i < layout->num_elems; i++) {
+ if (layout->elem[i].base.type == DATA) {
+ free(layout->elem[i].used.key.dptr);
+ free(layout->elem[i].used.data.dptr);
+ }
+ }
+ free(layout->elem);
+ free(layout);
+}
diff --git a/lib/tdb2/test/layout.h b/lib/ntdb/test/layout.h
index 3aadf20ee2..bcd20b8965 100644
--- a/lib/tdb2/test/layout.h
+++ b/lib/ntdb/test/layout.h
@@ -1,35 +1,35 @@
-#ifndef TDB2_TEST_LAYOUT_H
-#define TDB2_TEST_LAYOUT_H
+#ifndef NTDB_TEST_LAYOUT_H
+#define NTDB_TEST_LAYOUT_H
#include "private.h"
-struct tdb_layout *new_tdb_layout(void);
-void tdb_layout_add_freetable(struct tdb_layout *layout);
-void tdb_layout_add_free(struct tdb_layout *layout, tdb_len_t len,
+struct ntdb_layout *new_ntdb_layout(void);
+void ntdb_layout_add_freetable(struct ntdb_layout *layout);
+void ntdb_layout_add_free(struct ntdb_layout *layout, ntdb_len_t len,
unsigned ftable);
-void tdb_layout_add_used(struct tdb_layout *layout,
- TDB_DATA key, TDB_DATA data,
- tdb_len_t extra);
-void tdb_layout_add_capability(struct tdb_layout *layout,
+void ntdb_layout_add_used(struct ntdb_layout *layout,
+ NTDB_DATA key, NTDB_DATA data,
+ ntdb_len_t extra);
+void ntdb_layout_add_capability(struct ntdb_layout *layout,
uint64_t type,
bool write_breaks,
bool check_breaks,
bool open_breaks,
- tdb_len_t extra);
+ ntdb_len_t extra);
#if 0 /* FIXME: Allow allocation of subtables */
-void tdb_layout_add_hashtable(struct tdb_layout *layout,
+void ntdb_layout_add_hashtable(struct ntdb_layout *layout,
int htable_parent, /* -1 == toplevel */
unsigned int bucket,
- tdb_len_t extra);
+ ntdb_len_t extra);
#endif
/* freefn is needed if we're using failtest_free. */
-struct tdb_context *tdb_layout_get(struct tdb_layout *layout,
+struct ntdb_context *ntdb_layout_get(struct ntdb_layout *layout,
void (*freefn)(void *),
- union tdb_attribute *attr);
-void tdb_layout_write(struct tdb_layout *layout, void (*freefn)(void *),
- union tdb_attribute *attr, const char *filename);
+ union ntdb_attribute *attr);
+void ntdb_layout_write(struct ntdb_layout *layout, void (*freefn)(void *),
+ union ntdb_attribute *attr, const char *filename);
-void tdb_layout_free(struct tdb_layout *layout);
+void ntdb_layout_free(struct ntdb_layout *layout);
enum layout_type {
FREETABLE, FREE, DATA, HASHTABLE, CAPABILITY
@@ -38,7 +38,7 @@ enum layout_type {
/* Shared by all union members. */
struct tle_base {
enum layout_type type;
- tdb_off_t off;
+ ntdb_off_t off;
};
struct tle_freetable {
@@ -47,31 +47,31 @@ struct tle_freetable {
struct tle_free {
struct tle_base base;
- tdb_len_t len;
+ ntdb_len_t len;
unsigned ftable_num;
};
struct tle_used {
struct tle_base base;
- TDB_DATA key;
- TDB_DATA data;
- tdb_len_t extra;
+ NTDB_DATA key;
+ NTDB_DATA data;
+ ntdb_len_t extra;
};
struct tle_hashtable {
struct tle_base base;
int parent;
unsigned int bucket;
- tdb_len_t extra;
+ ntdb_len_t extra;
};
struct tle_capability {
struct tle_base base;
uint64_t type;
- tdb_len_t extra;
+ ntdb_len_t extra;
};
-union tdb_layout_elem {
+union ntdb_layout_elem {
struct tle_base base;
struct tle_freetable ftable;
struct tle_free free;
@@ -80,8 +80,8 @@ union tdb_layout_elem {
struct tle_capability capability;
};
-struct tdb_layout {
+struct ntdb_layout {
unsigned int num_elems;
- union tdb_layout_elem *elem;
+ union ntdb_layout_elem *elem;
};
-#endif /* TDB2_TEST_LAYOUT_H */
+#endif /* NTDB_TEST_LAYOUT_H */
diff --git a/lib/tdb2/test/lock-tracking.c b/lib/ntdb/test/lock-tracking.c
index c7387ead99..525a5c4ca7 100644
--- a/lib/tdb2/test/lock-tracking.c
+++ b/lib/ntdb/test/lock-tracking.c
@@ -1,5 +1,5 @@
/* We save the locks so we can reaquire them. */
-#include "private.h" /* For TDB_HASH_LOCK_START, etc. */
+#include "private.h" /* For NTDB_HASH_LOCK_START, etc. */
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -88,8 +88,8 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
if (fl_end > i->off && fl_end < i_end)
break;
- /* tdb_allrecord_lock does this, handle adjacent: */
- if (fl->l_start > TDB_HASH_LOCK_START
+ /* ntdb_allrecord_lock does this, handle adjacent: */
+ if (fl->l_start > NTDB_HASH_LOCK_START
&& fl->l_start == i_end && fl->l_type == i->type) {
if (ret == 0) {
i->len = fl->l_len
@@ -102,8 +102,8 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
if (i) {
/* Special case: upgrade of allrecord lock. */
if (i->type == F_RDLCK && fl->l_type == F_WRLCK
- && i->off == TDB_HASH_LOCK_START
- && fl->l_start == TDB_HASH_LOCK_START
+ && i->off == NTDB_HASH_LOCK_START
+ && fl->l_start == NTDB_HASH_LOCK_START
&& i->len == 0
&& fl->l_len == 0) {
if (ret == 0)
diff --git a/lib/tdb2/test/lock-tracking.h b/lib/ntdb/test/lock-tracking.h
index f2c9c44653..f2c9c44653 100644
--- a/lib/tdb2/test/lock-tracking.h
+++ b/lib/ntdb/test/lock-tracking.h
diff --git a/lib/tdb2/test/logging.c b/lib/ntdb/test/logging.c
index 86fc152bab..2819dd7cad 100644
--- a/lib/tdb2/test/logging.c
+++ b/lib/ntdb/test/logging.c
@@ -8,21 +8,21 @@ const char *log_prefix = "";
char *log_last = NULL;
bool suppress_logging;
-union tdb_attribute tap_log_attr = {
- .log = { .base = { .attr = TDB_ATTRIBUTE_LOG },
+union ntdb_attribute tap_log_attr = {
+ .log = { .base = { .attr = NTDB_ATTRIBUTE_LOG },
.fn = tap_log_fn }
};
-void tap_log_fn(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
+void tap_log_fn(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
const char *message, void *priv)
{
if (suppress_logging)
return;
- diag("tdb log level %u: %s: %s%s",
- level, tdb_errorstr(ecode), log_prefix, message);
+ diag("ntdb log level %u: %s: %s%s",
+ level, ntdb_errorstr(ecode), log_prefix, message);
if (log_last)
free(log_last);
log_last = strdup(message);
diff --git a/lib/ntdb/test/logging.h b/lib/ntdb/test/logging.h
new file mode 100644
index 0000000000..0336ccaba3
--- /dev/null
+++ b/lib/ntdb/test/logging.h
@@ -0,0 +1,17 @@
+#ifndef NTDB_TEST_LOGGING_H
+#define NTDB_TEST_LOGGING_H
+#include "ntdb.h"
+#include <stdbool.h>
+#include <string.h>
+
+extern bool suppress_logging;
+extern const char *log_prefix;
+extern unsigned tap_log_messages;
+extern union ntdb_attribute tap_log_attr;
+extern char *log_last;
+
+void tap_log_fn(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
+ const char *message, void *priv);
+#endif /* NTDB_TEST_LOGGING_H */
diff --git a/lib/tdb2/test/tdb2-source.h b/lib/ntdb/test/ntdb-source.h
index d13d8b868c..52268440d2 100644
--- a/lib/tdb2/test/tdb2-source.h
+++ b/lib/ntdb/test/ntdb-source.h
@@ -6,6 +6,6 @@
#include "lock.c"
#include "open.c"
#include "summary.c"
-#include "tdb.c"
+#include "ntdb.c"
#include "transaction.c"
#include "traverse.c"
diff --git a/lib/tdb2/test/run-001-encode.c b/lib/ntdb/test/run-001-encode.c
index 9657eb79d0..12965676a2 100644
--- a/lib/tdb2/test/run-001-encode.c
+++ b/lib/ntdb/test/run-001-encode.c
@@ -1,24 +1,24 @@
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include "logging.h"
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_used_record rec;
- struct tdb_context tdb = { .log_fn = tap_log_fn };
+ struct ntdb_used_record rec;
+ struct ntdb_context ntdb = { .log_fn = tap_log_fn };
plan_tests(64 + 32 + 48*6 + 1);
/* We should be able to encode any data value. */
for (i = 0; i < 64; i++)
- ok1(set_header(&tdb, &rec, TDB_USED_MAGIC, 0, 1ULL << i,
+ ok1(set_header(&ntdb, &rec, NTDB_USED_MAGIC, 0, 1ULL << i,
1ULL << i, 0) == 0);
/* And any key and data with < 64 bits between them. */
for (i = 0; i < 32; i++) {
- tdb_len_t dlen = 1ULL >> (63 - i), klen = 1ULL << i;
- ok1(set_header(&tdb, &rec, TDB_USED_MAGIC, klen, dlen,
+ ntdb_len_t dlen = 1ULL >> (63 - i), klen = 1ULL << i;
+ ok1(set_header(&ntdb, &rec, NTDB_USED_MAGIC, klen, dlen,
klen + dlen, 0) == 0);
}
@@ -28,13 +28,13 @@ int main(int argc, char *argv[])
uint64_t klen = 1ULL << (i < 16 ? i : 15);
uint64_t dlen = 1ULL << i;
uint64_t xlen = 1ULL << (i < 32 ? i : 31);
- ok1(set_header(&tdb, &rec, TDB_USED_MAGIC, klen, dlen,
+ ok1(set_header(&ntdb, &rec, NTDB_USED_MAGIC, klen, dlen,
klen+dlen+xlen, h) == 0);
ok1(rec_key_length(&rec) == klen);
ok1(rec_data_length(&rec) == dlen);
ok1(rec_extra_padding(&rec) == xlen);
ok1((uint64_t)rec_hash(&rec) == h);
- ok1(rec_magic(&rec) == TDB_USED_MAGIC);
+ ok1(rec_magic(&rec) == NTDB_USED_MAGIC);
}
ok1(tap_log_messages == 0);
return exit_status();
diff --git a/lib/tdb2/test/run-001-fls.c b/lib/ntdb/test/run-001-fls.c
index 792adbf655..ec61294c6f 100644
--- a/lib/tdb2/test/run-001-fls.c
+++ b/lib/ntdb/test/run-001-fls.c
@@ -1,4 +1,4 @@
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
static unsigned int dumb_fls(uint64_t num)
diff --git a/lib/tdb2/test/run-01-new_database.c b/lib/ntdb/test/run-01-new_database.c
index 00c15140df..ae70e86e07 100644
--- a/lib/tdb2/test/run-01-new_database.c
+++ b/lib/ntdb/test/run-01-new_database.c
@@ -1,5 +1,5 @@
#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <ccan/failtest/failtest.h>
#include "logging.h"
@@ -8,25 +8,25 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
failtest_init(argc, argv);
failtest_hook = block_repeat_failures;
failtest_exit_check = exit_check_log;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 3);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-new_database.tdb", flags[i],
+ ntdb = ntdb_open("run-new_database.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
failtest_exit(exit_status());
failtest_suppress = true;
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
failtest_suppress = false;
- tdb_close(tdb);
+ ntdb_close(ntdb);
if (!ok1(tap_log_messages == 0))
break;
}
diff --git a/lib/ntdb/test/run-02-expand.c b/lib/ntdb/test/run-02-expand.c
new file mode 100644
index 0000000000..abf1569388
--- /dev/null
+++ b/lib/ntdb/test/run-02-expand.c
@@ -0,0 +1,62 @@
+#include <ccan/failtest/failtest_override.h>
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include <ccan/failtest/failtest.h>
+#include "logging.h"
+#include "failtest_helper.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ uint64_t val;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 11 + 1);
+
+ failtest_init(argc, argv);
+ failtest_hook = block_repeat_failures;
+ failtest_exit_check = exit_check_log;
+
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ failtest_suppress = true;
+ ntdb = ntdb_open("run-expand.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ if (!ok1(ntdb))
+ break;
+
+ val = ntdb->file->map_size;
+ /* Need some hash lock for expand. */
+ ok1(ntdb_lock_hashes(ntdb, 0, 1, F_WRLCK, NTDB_LOCK_WAIT) == 0);
+ failtest_suppress = false;
+ if (!ok1(ntdb_expand(ntdb, 1) == 0)) {
+ failtest_suppress = true;
+ ntdb_close(ntdb);
+ break;
+ }
+ failtest_suppress = true;
+
+ ok1(ntdb->file->map_size >= val + 1 * NTDB_EXTENSION_FACTOR);
+ ok1(ntdb_unlock_hashes(ntdb, 0, 1, F_WRLCK) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ val = ntdb->file->map_size;
+ ok1(ntdb_lock_hashes(ntdb, 0, 1, F_WRLCK, NTDB_LOCK_WAIT) == 0);
+ failtest_suppress = false;
+ if (!ok1(ntdb_expand(ntdb, 1024) == 0)) {
+ failtest_suppress = true;
+ ntdb_close(ntdb);
+ break;
+ }
+ failtest_suppress = true;
+ ok1(ntdb_unlock_hashes(ntdb, 0, 1, F_WRLCK) == 0);
+ ok1(ntdb->file->map_size >= val + 1024 * NTDB_EXTENSION_FACTOR);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ failtest_exit(exit_status());
+}
diff --git a/lib/ntdb/test/run-03-coalesce.c b/lib/ntdb/test/run-03-coalesce.c
new file mode 100644
index 0000000000..f93b33a1c3
--- /dev/null
+++ b/lib/ntdb/test/run-03-coalesce.c
@@ -0,0 +1,178 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+#include "layout.h"
+
+static ntdb_len_t free_record_length(struct ntdb_context *ntdb, ntdb_off_t off)
+{
+ struct ntdb_free_record f;
+ enum NTDB_ERROR ecode;
+
+ ecode = ntdb_read_convert(ntdb, off, &f, sizeof(f));
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+ if (frec_magic(&f) != NTDB_FREE_MAGIC)
+ return NTDB_ERR_CORRUPT;
+ return frec_len(&f);
+}
+
+int main(int argc, char *argv[])
+{
+ ntdb_off_t b_off, test;
+ struct ntdb_context *ntdb;
+ struct ntdb_layout *layout;
+ NTDB_DATA data, key;
+ ntdb_len_t len;
+
+ /* FIXME: Test NTDB_CONVERT */
+ /* FIXME: Test lock order fail. */
+
+ plan_tests(42);
+ data = ntdb_mkdata("world", 5);
+ key = ntdb_mkdata("hello", 5);
+
+ /* No coalescing can be done due to EOF */
+ layout = new_ntdb_layout();
+ ntdb_layout_add_freetable(layout);
+ len = 1024;
+ ntdb_layout_add_free(layout, len, 0);
+ ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
+ /* NOMMAP is for lockcheck. */
+ ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
+ &tap_log_attr);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off) == len);
+
+ /* Figure out which bucket free entry is. */
+ b_off = bucket_off(ntdb->ftable_off, size_to_bucket(len));
+ /* Lock and fail to coalesce. */
+ ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
+ test = layout->elem[1].base.off;
+ ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, len, &test)
+ == 0);
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off) == len);
+ ok1(test == layout->elem[1].base.off);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ ntdb_layout_free(layout);
+
+ /* No coalescing can be done due to used record */
+ layout = new_ntdb_layout();
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_free(layout, 1024, 0);
+ ntdb_layout_add_used(layout, key, data, 6);
+ ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
+ /* NOMMAP is for lockcheck. */
+ ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
+ &tap_log_attr);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Figure out which bucket free entry is. */
+ b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
+ /* Lock and fail to coalesce. */
+ ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
+ test = layout->elem[1].base.off;
+ ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
+ == 0);
+ ntdb_unlock_free_bucket(ntdb, b_off);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
+ ok1(test == layout->elem[1].base.off);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ ntdb_layout_free(layout);
+
+ /* Coalescing can be done due to two free records, then EOF */
+ layout = new_ntdb_layout();
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_free(layout, 1024, 0);
+ ntdb_layout_add_free(layout, 2048, 0);
+ ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
+ /* NOMMAP is for lockcheck. */
+ ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
+ &tap_log_attr);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
+ ok1(free_record_length(ntdb, layout->elem[2].base.off) == 2048);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Figure out which bucket (first) free entry is. */
+ b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
+ /* Lock and coalesce. */
+ ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
+ test = layout->elem[2].base.off;
+ ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
+ == 1024 + sizeof(struct ntdb_used_record) + 2048);
+ /* Should tell us it's erased this one... */
+ ok1(test == NTDB_ERR_NOEXIST);
+ ok1(ntdb->file->allrecord_lock.count == 0 && ntdb->file->num_lockrecs == 0);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off)
+ == 1024 + sizeof(struct ntdb_used_record) + 2048);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ ntdb_layout_free(layout);
+
+ /* Coalescing can be done due to two free records, then data */
+ layout = new_ntdb_layout();
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_free(layout, 1024, 0);
+ ntdb_layout_add_free(layout, 512, 0);
+ ntdb_layout_add_used(layout, key, data, 6);
+ ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
+ /* NOMMAP is for lockcheck. */
+ ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
+ &tap_log_attr);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
+ ok1(free_record_length(ntdb, layout->elem[2].base.off) == 512);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Figure out which bucket free entry is. */
+ b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
+ /* Lock and coalesce. */
+ ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
+ test = layout->elem[2].base.off;
+ ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
+ == 1024 + sizeof(struct ntdb_used_record) + 512);
+ ok1(ntdb->file->allrecord_lock.count == 0 && ntdb->file->num_lockrecs == 0);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off)
+ == 1024 + sizeof(struct ntdb_used_record) + 512);
+ ok1(test == NTDB_ERR_NOEXIST);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ ntdb_layout_free(layout);
+
+ /* Coalescing can be done due to three free records, then EOF */
+ layout = new_ntdb_layout();
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_free(layout, 1024, 0);
+ ntdb_layout_add_free(layout, 512, 0);
+ ntdb_layout_add_free(layout, 256, 0);
+ ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
+ /* NOMMAP is for lockcheck. */
+ ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
+ &tap_log_attr);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
+ ok1(free_record_length(ntdb, layout->elem[2].base.off) == 512);
+ ok1(free_record_length(ntdb, layout->elem[3].base.off) == 256);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Figure out which bucket free entry is. */
+ b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
+ /* Lock and coalesce. */
+ ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
+ test = layout->elem[2].base.off;
+ ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
+ == 1024 + sizeof(struct ntdb_used_record) + 512
+ + sizeof(struct ntdb_used_record) + 256);
+ ok1(ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0);
+ ok1(free_record_length(ntdb, layout->elem[1].base.off)
+ == 1024 + sizeof(struct ntdb_used_record) + 512
+ + sizeof(struct ntdb_used_record) + 256);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ ntdb_layout_free(layout);
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/ntdb/test/run-04-basichash.c b/lib/ntdb/test/run-04-basichash.c
new file mode 100644
index 0000000000..6e3bdc012d
--- /dev/null
+++ b/lib/ntdb/test/run-04-basichash.c
@@ -0,0 +1,260 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+/* We rig the hash so adjacent-numbered records always clash. */
+static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
+{
+ return ((uint64_t)*(const unsigned int *)key)
+ << (64 - NTDB_TOPLEVEL_HASH_BITS - 1);
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j;
+ struct ntdb_context *ntdb;
+ unsigned int v;
+ struct ntdb_used_record rec;
+ NTDB_DATA key = { (unsigned char *)&v, sizeof(v) };
+ NTDB_DATA dbuf = { (unsigned char *)&v, sizeof(v) };
+ union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
+ .fn = clash } };
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT,
+ };
+
+ hattr.base.next = &tap_log_attr;
+
+ plan_tests(sizeof(flags) / sizeof(flags[0])
+ * (91 + (2 * ((1 << NTDB_HASH_GROUP_BITS) - 1))) + 1);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ struct hash_info h;
+ ntdb_off_t new_off, off, subhash;
+
+ ntdb = ntdb_open("run-04-basichash.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ v = 0;
+ /* Should not find it. */
+ ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in group 0, bucket 0. */
+ ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 0);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
+
+ /* Should have lock on bucket 0 */
+ ok1(h.hlock_start == 0);
+ ok1(h.hlock_range ==
+ 1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
+ ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
+ ok1((ntdb->flags & NTDB_NOLOCK)
+ || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
+ /* FIXME: Check lock length */
+
+ /* Allocate a new record. */
+ new_off = alloc(ntdb, key.dsize, dbuf.dsize, h.h,
+ NTDB_USED_MAGIC, false);
+ ok1(!NTDB_OFF_IS_ERR(new_off));
+
+ /* We should be able to add it now. */
+ ok1(add_to_hash(ntdb, &h, new_off) == 0);
+
+ /* Make sure we fill it in for later finding. */
+ off = new_off + sizeof(struct ntdb_used_record);
+ ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
+ off += key.dsize;
+ ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
+
+ /* We should be able to unlock that OK. */
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_WRLCK) == 0);
+
+ /* Database should be consistent. */
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Now, this should give a successful lookup. */
+ ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL)
+ == new_off);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in group 0, bucket 0. */
+ ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 0);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
+
+ /* Should have lock on bucket 0 */
+ ok1(h.hlock_start == 0);
+ ok1(h.hlock_range ==
+ 1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
+ ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
+ ok1((ntdb->flags & NTDB_NOLOCK)
+ || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
+ /* FIXME: Check lock length */
+
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_WRLCK) == 0);
+
+ /* Database should be consistent. */
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Test expansion. */
+ v = 1;
+ ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in group 0, bucket 1. */
+ ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 1);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
+
+ /* Should have lock on bucket 0 */
+ ok1(h.hlock_start == 0);
+ ok1(h.hlock_range ==
+ 1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
+ ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
+ ok1((ntdb->flags & NTDB_NOLOCK)
+ || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
+ /* FIXME: Check lock length */
+
+ /* Make it expand 0'th bucket. */
+ ok1(expand_group(ntdb, &h) == 0);
+ /* First one should be subhash, next should be empty. */
+ ok1(is_subhash(h.group[0]));
+ subhash = (h.group[0] & NTDB_OFF_MASK);
+ for (j = 1; j < (1 << NTDB_HASH_GROUP_BITS); j++)
+ ok1(h.group[j] == 0);
+
+ ok1(ntdb_write_convert(ntdb, h.group_start,
+ h.group, sizeof(h.group)) == 0);
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_WRLCK) == 0);
+
+ /* Should be happy with expansion. */
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Should be able to find it. */
+ v = 0;
+ ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL)
+ == new_off);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in expanded group 0, bucket 0. */
+ ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 0);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
+ + NTDB_SUBLEVEL_HASH_BITS);
+
+ /* Should have lock on bucket 0 */
+ ok1(h.hlock_start == 0);
+ ok1(h.hlock_range ==
+ 1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
+ ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
+ ok1((ntdb->flags & NTDB_NOLOCK)
+ || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
+ /* FIXME: Check lock length */
+
+ /* Simple delete should work. */
+ ok1(delete_from_hash(ntdb, &h) == 0);
+ ok1(add_free_record(ntdb, new_off,
+ sizeof(struct ntdb_used_record)
+ + rec_key_length(&rec)
+ + rec_data_length(&rec)
+ + rec_extra_padding(&rec),
+ NTDB_LOCK_NOWAIT, false) == 0);
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_WRLCK) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Test second-level expansion: should expand 0th bucket. */
+ v = 0;
+ ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in group 0, bucket 0. */
+ ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 0);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS+NTDB_SUBLEVEL_HASH_BITS);
+
+ /* Should have lock on bucket 0 */
+ ok1(h.hlock_start == 0);
+ ok1(h.hlock_range ==
+ 1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
+ ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
+ ok1((ntdb->flags & NTDB_NOLOCK)
+ || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
+ /* FIXME: Check lock length */
+
+ ok1(expand_group(ntdb, &h) == 0);
+ /* First one should be subhash, next should be empty. */
+ ok1(is_subhash(h.group[0]));
+ subhash = (h.group[0] & NTDB_OFF_MASK);
+ for (j = 1; j < (1 << NTDB_HASH_GROUP_BITS); j++)
+ ok1(h.group[j] == 0);
+ ok1(ntdb_write_convert(ntdb, h.group_start,
+ h.group, sizeof(h.group)) == 0);
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_WRLCK) == 0);
+
+ /* Should be happy with expansion. */
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in group 0, bucket 0. */
+ ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 0);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
+ + NTDB_SUBLEVEL_HASH_BITS * 2);
+
+ /* We should be able to add it now. */
+ /* Allocate a new record. */
+ new_off = alloc(ntdb, key.dsize, dbuf.dsize, h.h,
+ NTDB_USED_MAGIC, false);
+ ok1(!NTDB_OFF_IS_ERR(new_off));
+ ok1(add_to_hash(ntdb, &h, new_off) == 0);
+
+ /* Make sure we fill it in for later finding. */
+ off = new_off + sizeof(struct ntdb_used_record);
+ ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
+ off += key.dsize;
+ ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
+
+ /* We should be able to unlock that OK. */
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_WRLCK) == 0);
+
+ /* Database should be consistent. */
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Should be able to find it. */
+ v = 0;
+ ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL)
+ == new_off);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in expanded group 0, bucket 0. */
+ ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 0);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
+ + NTDB_SUBLEVEL_HASH_BITS * 2);
+
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/tdb2/test/run-05-readonly-open.c b/lib/ntdb/test/run-05-readonly-open.c
index 1046a8b47e..dd5aa26d0d 100644
--- a/lib/tdb2/test/run-05-readonly-open.c
+++ b/lib/ntdb/test/run-05-readonly-open.c
@@ -1,5 +1,5 @@
#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <ccan/failtest/failtest.h>
#include "logging.h"
@@ -8,55 +8,55 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4), d;
- union tdb_attribute seed_attr;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4), d;
+ union ntdb_attribute seed_attr;
unsigned int msgs = 0;
failtest_init(argc, argv);
failtest_hook = block_repeat_failures;
failtest_exit_check = exit_check_log;
- seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
+ seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
seed_attr.base.next = &tap_log_attr;
seed_attr.seed.seed = 0;
failtest_suppress = true;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 11);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-05-readonly-open.tdb", flags[i],
+ ntdb = ntdb_open("run-05-readonly-open.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600,
&seed_attr);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- tdb_close(tdb);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ntdb_close(ntdb);
failtest_suppress = false;
- tdb = tdb_open("run-05-readonly-open.tdb", flags[i],
+ ntdb = ntdb_open("run-05-readonly-open.ntdb", flags[i],
O_RDONLY, 0600, &tap_log_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
break;
ok1(tap_log_messages == msgs);
/* Fetch should succeed, stores should fail. */
- if (!ok1(tdb_fetch(tdb, key, &d) == 0))
+ if (!ok1(ntdb_fetch(ntdb, key, &d) == 0))
goto fail;
- ok1(tdb_deq(d, data));
+ ok1(ntdb_deq(d, data));
free(d.dptr);
- if (!ok1(tdb_store(tdb, key, data, TDB_MODIFY)
- == TDB_ERR_RDONLY))
+ if (!ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY)
+ == NTDB_ERR_RDONLY))
goto fail;
ok1(tap_log_messages == ++msgs);
- if (!ok1(tdb_store(tdb, key, data, TDB_INSERT)
- == TDB_ERR_RDONLY))
+ if (!ok1(ntdb_store(ntdb, key, data, NTDB_INSERT)
+ == NTDB_ERR_RDONLY))
goto fail;
ok1(tap_log_messages == ++msgs);
failtest_suppress = true;
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
ok1(tap_log_messages == msgs);
- /* SIGH: failtest bug, it doesn't save the tdb file because
+ /* SIGH: failtest bug, it doesn't save the ntdb file because
* we have it read-only. If we go around again, it gets
* changed underneath us and things get screwy. */
if (failtest_has_failed())
@@ -66,6 +66,6 @@ int main(int argc, char *argv[])
fail:
failtest_suppress = true;
- tdb_close(tdb);
+ ntdb_close(ntdb);
failtest_exit(exit_status());
}
diff --git a/lib/tdb2/test/run-10-simple-store.c b/lib/ntdb/test/run-10-simple-store.c
index 66bf6a6a51..6e718bf61f 100644
--- a/lib/tdb2/test/run-10-simple-store.c
+++ b/lib/ntdb/test/run-10-simple-store.c
@@ -1,5 +1,5 @@
#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <ccan/failtest/failtest.h>
#include "logging.h"
@@ -8,12 +8,12 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
failtest_init(argc, argv);
failtest_hook = block_repeat_failures;
@@ -22,37 +22,37 @@ int main(int argc, char *argv[])
failtest_suppress = true;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-10-simple-store.tdb", flags[i],
+ ntdb = ntdb_open("run-10-simple-store.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
break;
/* Modify should fail. */
failtest_suppress = false;
- if (!ok1(tdb_store(tdb, key, data, TDB_MODIFY)
- == TDB_ERR_NOEXIST))
+ if (!ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY)
+ == NTDB_ERR_NOEXIST))
goto fail;
failtest_suppress = true;
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
/* Insert should succeed. */
failtest_suppress = false;
- if (!ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0))
+ if (!ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0))
goto fail;
failtest_suppress = true;
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
/* Second insert should fail. */
failtest_suppress = false;
- if (!ok1(tdb_store(tdb, key, data, TDB_INSERT)
- == TDB_ERR_EXISTS))
+ if (!ok1(ntdb_store(ntdb, key, data, NTDB_INSERT)
+ == NTDB_ERR_EXISTS))
goto fail;
failtest_suppress = true;
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
failtest_exit(exit_status());
fail:
failtest_suppress = true;
- tdb_close(tdb);
+ ntdb_close(ntdb);
failtest_exit(exit_status());
}
diff --git a/lib/tdb2/test/run-11-simple-fetch.c b/lib/ntdb/test/run-11-simple-fetch.c
index 4c41ceec6d..525cf46444 100644
--- a/lib/tdb2/test/run-11-simple-fetch.c
+++ b/lib/ntdb/test/run-11-simple-fetch.c
@@ -1,5 +1,5 @@
#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <ccan/failtest/failtest.h>
#include "logging.h"
@@ -8,12 +8,12 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
failtest_init(argc, argv);
failtest_hook = block_repeat_failures;
@@ -22,30 +22,30 @@ int main(int argc, char *argv[])
failtest_suppress = true;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 8 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-11-simple-fetch.tdb", flags[i],
+ ntdb = ntdb_open("run-11-simple-fetch.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (tdb) {
- struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */
+ ok1(ntdb);
+ if (ntdb) {
+ NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
/* fetch should fail. */
failtest_suppress = false;
- if (!ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_NOEXIST))
+ if (!ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_NOEXIST))
goto fail;
failtest_suppress = true;
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
/* Insert should succeed. */
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
/* Fetch should now work. */
failtest_suppress = false;
- if (!ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS))
+ if (!ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS))
goto fail;
failtest_suppress = true;
- ok1(tdb_deq(d, data));
+ ok1(ntdb_deq(d, data));
free(d.dptr);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
}
}
ok1(tap_log_messages == 0);
@@ -53,6 +53,6 @@ int main(int argc, char *argv[])
fail:
failtest_suppress = true;
- tdb_close(tdb);
+ ntdb_close(ntdb);
failtest_exit(exit_status());
}
diff --git a/lib/tdb2/test/run-12-check.c b/lib/ntdb/test/run-12-check.c
index cc57726f93..6040637048 100644
--- a/lib/tdb2/test/run-12-check.c
+++ b/lib/ntdb/test/run-12-check.c
@@ -1,6 +1,6 @@
#include "private.h"
#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <ccan/failtest/failtest.h>
#include "logging.h"
@@ -9,12 +9,12 @@
int main(int argc, char *argv[])
{
unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL,
- TDB_INTERNAL|TDB_CONVERT,
- TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL,
+ NTDB_INTERNAL|NTDB_CONVERT,
+ NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
failtest_init(argc, argv);
failtest_hook = block_repeat_failures;
@@ -23,24 +23,24 @@ int main(int argc, char *argv[])
failtest_suppress = true;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 3 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-12-check.tdb", flags[i],
+ ntdb = ntdb_open("run-12-check.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+ ok1(ntdb);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
- /* This is what we really want to test: tdb_check(). */
+ /* This is what we really want to test: ntdb_check(). */
failtest_suppress = false;
- if (!ok1(tdb_check(tdb, NULL, NULL) == 0))
+ if (!ok1(ntdb_check(ntdb, NULL, NULL) == 0))
goto fail;
failtest_suppress = true;
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
failtest_exit(exit_status());
fail:
failtest_suppress = true;
- tdb_close(tdb);
+ ntdb_close(ntdb);
failtest_exit(exit_status());
}
diff --git a/lib/tdb2/test/run-15-append.c b/lib/ntdb/test/run-15-append.c
index 6578b70734..3c208137f2 100644
--- a/lib/tdb2/test/run-15-append.c
+++ b/lib/ntdb/test/run-15-append.c
@@ -1,4 +1,4 @@
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <ccan/ilog/ilog.h>
#include "logging.h"
@@ -6,30 +6,30 @@
#define MAX_SIZE 13100
#define SIZE_STEP 131
-static tdb_off_t tdb_offset(struct tdb_context *tdb, struct tdb_data key)
+static ntdb_off_t ntdb_offset(struct ntdb_context *ntdb, NTDB_DATA key)
{
- tdb_off_t off;
- struct tdb_used_record urec;
+ ntdb_off_t off;
+ struct ntdb_used_record urec;
struct hash_info h;
- off = find_and_lock(tdb, key, F_RDLCK, &h, &urec, NULL);
- if (TDB_OFF_IS_ERR(off))
+ off = find_and_lock(ntdb, key, F_RDLCK, &h, &urec, NULL);
+ if (NTDB_OFF_IS_ERR(off))
return 0;
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
return off;
}
int main(int argc, char *argv[])
{
unsigned int i, j, moves;
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
unsigned char *buffer;
- tdb_off_t oldoff = 0, newoff;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data;
+ ntdb_off_t oldoff = 0, newoff;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data;
buffer = malloc(MAX_SIZE);
for (i = 0; i < MAX_SIZE; i++)
@@ -39,89 +39,89 @@ int main(int argc, char *argv[])
* ((3 + MAX_SIZE/SIZE_STEP * 5) * 2 + 7)
+ 1);
- /* Using tdb_store. */
+ /* Using ntdb_store. */
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-append.tdb", flags[i],
+ ntdb = ntdb_open("run-append.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
moves = 0;
for (j = 0; j < MAX_SIZE; j += SIZE_STEP) {
data.dptr = buffer;
data.dsize = j;
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
+ ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
ok1(data.dsize == j);
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
free(data.dptr);
- newoff = tdb_offset(tdb, key);
+ newoff = ntdb_offset(ntdb, key);
if (newoff != oldoff)
moves++;
oldoff = newoff;
}
- ok1(!tdb->file || (tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0));
+ ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0));
/* We should increase by 50% each time... */
ok(moves <= ilog64(j / SIZE_STEP)*2,
"Moved %u times", moves);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
- /* Using tdb_append. */
+ /* Using ntdb_append. */
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
size_t prev_len = 0;
- tdb = tdb_open("run-append.tdb", flags[i],
+ ntdb = ntdb_open("run-append.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
moves = 0;
for (j = 0; j < MAX_SIZE; j += SIZE_STEP) {
data.dptr = buffer + prev_len;
data.dsize = j - prev_len;
- ok1(tdb_append(tdb, key, data) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
+ ok1(ntdb_append(ntdb, key, data) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
ok1(data.dsize == j);
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
free(data.dptr);
prev_len = data.dsize;
- newoff = tdb_offset(tdb, key);
+ newoff = ntdb_offset(ntdb, key);
if (newoff != oldoff)
moves++;
oldoff = newoff;
}
- ok1(!tdb->file || (tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0));
+ ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0));
/* We should increase by 50% each time... */
ok(moves <= ilog64(j / SIZE_STEP)*2,
"Moved %u times", moves);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-append.tdb", flags[i],
+ ntdb = ntdb_open("run-append.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
/* Huge initial store. */
data.dptr = buffer;
data.dsize = MAX_SIZE;
- ok1(tdb_append(tdb, key, data) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
+ ok1(ntdb_append(ntdb, key, data) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
ok1(data.dsize == MAX_SIZE);
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
free(data.dptr);
- ok1(!tdb->file || (tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0));
- tdb_close(tdb);
+ ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
+ && ntdb->file->num_lockrecs == 0));
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/ntdb/test/run-20-growhash.c b/lib/ntdb/test/run-20-growhash.c
new file mode 100644
index 0000000000..5559370f2a
--- /dev/null
+++ b/lib/ntdb/test/run-20-growhash.c
@@ -0,0 +1,137 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+static uint64_t myhash(const void *key, size_t len, uint64_t seed, void *priv)
+{
+ return *(const uint64_t *)key;
+}
+
+static void add_bits(uint64_t *val, unsigned new, unsigned new_bits,
+ unsigned *done)
+{
+ *done += new_bits;
+ *val |= ((uint64_t)new << (64 - *done));
+}
+
+static uint64_t make_key(unsigned topgroup, unsigned topbucket,
+ unsigned subgroup1, unsigned subbucket1,
+ unsigned subgroup2, unsigned subbucket2)
+{
+ uint64_t key = 0;
+ unsigned done = 0;
+
+ add_bits(&key, topgroup, NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS,
+ &done);
+ add_bits(&key, topbucket, NTDB_HASH_GROUP_BITS, &done);
+ add_bits(&key, subgroup1, NTDB_SUBLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS,
+ &done);
+ add_bits(&key, subbucket1, NTDB_HASH_GROUP_BITS, &done);
+ add_bits(&key, subgroup2, NTDB_SUBLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS,
+ &done);
+ add_bits(&key, subbucket2, NTDB_HASH_GROUP_BITS, &done);
+ return key;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j;
+ struct ntdb_context *ntdb;
+ uint64_t kdata;
+ struct ntdb_used_record rec;
+ NTDB_DATA key = { (unsigned char *)&kdata, sizeof(kdata) };
+ NTDB_DATA dbuf = { (unsigned char *)&kdata, sizeof(kdata) };
+ union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
+ .fn = myhash } };
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT,
+ };
+
+ hattr.base.next = &tap_log_attr;
+
+ plan_tests(sizeof(flags) / sizeof(flags[0])
+ * (9 + (20 + 2 * ((1 << NTDB_HASH_GROUP_BITS) - 2))
+ * (1 << NTDB_HASH_GROUP_BITS)) + 1);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ struct hash_info h;
+
+ ntdb = ntdb_open("run-20-growhash.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ /* Fill a group. */
+ for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
+ kdata = make_key(0, j, 0, 0, 0, 0);
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ }
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Check first still exists. */
+ kdata = make_key(0, 0, 0, 0, 0, 0);
+ ok1(find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL) != 0);
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have located space in group 0, bucket 0. */
+ ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
+ ok1(h.home_bucket == 0);
+ ok1(h.found_bucket == 0);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
+ /* Entire group should be full! */
+ for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++)
+ ok1(h.group[j] != 0);
+
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_RDLCK) == 0);
+
+ /* Now, add one more to each should expand (that) bucket. */
+ for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
+ unsigned int k;
+ kdata = make_key(0, j, 0, 1, 0, 0);
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ ok1(find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL));
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have moved to subhash */
+ ok1(h.group_start >= sizeof(struct ntdb_header));
+ ok1(h.home_bucket == 1);
+ ok1(h.found_bucket == 1);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
+ + NTDB_SUBLEVEL_HASH_BITS);
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_RDLCK) == 0);
+
+ /* Keep adding, make it expand again. */
+ for (k = 2; k < (1 << NTDB_HASH_GROUP_BITS); k++) {
+ kdata = make_key(0, j, 0, k, 0, 0);
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ }
+
+ /* This should tip it over to sub-sub-hash. */
+ kdata = make_key(0, j, 0, 0, 0, 1);
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ ok1(find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL));
+ /* Should have created correct hash. */
+ ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
+ /* Should have moved to subhash */
+ ok1(h.group_start >= sizeof(struct ntdb_header));
+ ok1(h.home_bucket == 1);
+ ok1(h.found_bucket == 1);
+ ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
+ + NTDB_SUBLEVEL_HASH_BITS + NTDB_SUBLEVEL_HASH_BITS);
+ ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
+ F_RDLCK) == 0);
+ }
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/ntdb/test/run-25-hashoverload.c b/lib/ntdb/test/run-25-hashoverload.c
new file mode 100644
index 0000000000..611eb71bf6
--- /dev/null
+++ b/lib/ntdb/test/run-25-hashoverload.c
@@ -0,0 +1,113 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+static uint64_t badhash(const void *key, size_t len, uint64_t seed, void *priv)
+{
+ return 0;
+}
+
+static int trav(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *p)
+{
+ if (p)
+ return ntdb_delete(ntdb, key);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j;
+ struct ntdb_context *ntdb;
+ NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
+ NTDB_DATA dbuf = { (unsigned char *)&j, sizeof(j) };
+ union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
+ .fn = badhash } };
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT,
+ };
+
+ hattr.base.next = &tap_log_attr;
+
+ plan_tests(6883);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
+
+ ntdb = ntdb_open("run-25-hashoverload.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ /* Fill a group. */
+ for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ }
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Now store one last value: should form chain. */
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Check we can find them all. */
+ for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS) + 1; j++) {
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == sizeof(j));
+ ok1(d.dptr != NULL);
+ ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
+ free(d.dptr);
+ }
+
+ /* Now add a *lot* more. */
+ for (j = (1 << NTDB_HASH_GROUP_BITS) + 1;
+ j < (16 << NTDB_HASH_GROUP_BITS);
+ j++) {
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == sizeof(j));
+ ok1(d.dptr != NULL);
+ ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
+ free(d.dptr);
+ }
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Traverse through them. */
+ ok1(ntdb_traverse(ntdb, trav, NULL) == j);
+
+ /* Empty the first chain-worth. */
+ for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++)
+ ok1(ntdb_delete(ntdb, key) == 0);
+
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ for (j = (1 << NTDB_HASH_GROUP_BITS);
+ j < (16 << NTDB_HASH_GROUP_BITS);
+ j++) {
+ ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
+ ok1(d.dsize == sizeof(j));
+ ok1(d.dptr != NULL);
+ ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
+ free(d.dptr);
+ }
+
+ /* Traverse through them. */
+ ok1(ntdb_traverse(ntdb, trav, NULL)
+ == (15 << NTDB_HASH_GROUP_BITS));
+
+ /* Re-add */
+ for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
+ ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
+ }
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ /* Now try deleting as we go. */
+ ok1(ntdb_traverse(ntdb, trav, trav)
+ == (16 << NTDB_HASH_GROUP_BITS));
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb_traverse(ntdb, trav, NULL) == 0);
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/ntdb/test/run-30-exhaust-before-expand.c b/lib/ntdb/test/run-30-exhaust-before-expand.c
new file mode 100644
index 0000000000..b94bc01bff
--- /dev/null
+++ b/lib/ntdb/test/run-30-exhaust-before-expand.c
@@ -0,0 +1,71 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+static bool empty_freetable(struct ntdb_context *ntdb)
+{
+ struct ntdb_freetable ftab;
+ unsigned int i;
+
+ /* Now, free table should be completely exhausted in zone 0 */
+ if (ntdb_read_convert(ntdb, ntdb->ftable_off, &ftab, sizeof(ftab)) != 0)
+ abort();
+
+ for (i = 0; i < sizeof(ftab.buckets)/sizeof(ftab.buckets[0]); i++) {
+ if (ftab.buckets[i])
+ return false;
+ }
+ return true;
+}
+
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 9 + 1);
+
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ NTDB_DATA k;
+ uint64_t size;
+ bool was_empty = false;
+
+ k.dptr = (void *)&j;
+ k.dsize = sizeof(j);
+
+ ntdb = ntdb_open("run-30-exhaust-before-expand.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ ok1(empty_freetable(ntdb));
+ /* Need some hash lock for expand. */
+ ok1(ntdb_lock_hashes(ntdb, 0, 1, F_WRLCK, NTDB_LOCK_WAIT) == 0);
+ /* Create some free space. */
+ ok1(ntdb_expand(ntdb, 1) == 0);
+ ok1(ntdb_unlock_hashes(ntdb, 0, 1, F_WRLCK) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(!empty_freetable(ntdb));
+
+ size = ntdb->file->map_size;
+ /* Insert minimal-length records until we expand. */
+ for (j = 0; ntdb->file->map_size == size; j++) {
+ was_empty = empty_freetable(ntdb);
+ if (ntdb_store(ntdb, k, k, NTDB_INSERT) != 0)
+ err(1, "Failed to store record %i", j);
+ }
+
+ /* Would have been empty before expansion, but no longer. */
+ ok1(was_empty);
+ ok1(!empty_freetable(ntdb));
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/tdb2/test/run-35-convert.c b/lib/ntdb/test/run-35-convert.c
index ac7939591b..6a38d425cb 100644
--- a/lib/tdb2/test/run-35-convert.c
+++ b/lib/ntdb/test/run-35-convert.c
@@ -1,6 +1,6 @@
#include "private.h"
#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <ccan/failtest/failtest.h>
#include "logging.h"
@@ -9,46 +9,46 @@
int main(int argc, char *argv[])
{
unsigned int i, messages = 0;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
failtest_init(argc, argv);
failtest_hook = block_repeat_failures;
failtest_exit_check = exit_check_log;
plan_tests(sizeof(flags) / sizeof(flags[0]) * 4);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-35-convert.tdb", flags[i],
+ ntdb = ntdb_open("run-35-convert.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
+ if (!ok1(ntdb))
failtest_exit(exit_status());
- tdb_close(tdb);
- /* If we say TDB_CONVERT, it must be converted */
- tdb = tdb_open("run-35-convert.tdb",
- flags[i]|TDB_CONVERT,
+ ntdb_close(ntdb);
+ /* If we say NTDB_CONVERT, it must be converted */
+ ntdb = ntdb_open("run-35-convert.ntdb",
+ flags[i]|NTDB_CONVERT,
O_RDWR, 0600, &tap_log_attr);
- if (flags[i] & TDB_CONVERT) {
- if (!tdb)
+ if (flags[i] & NTDB_CONVERT) {
+ if (!ntdb)
failtest_exit(exit_status());
- ok1(tdb_get_flags(tdb) & TDB_CONVERT);
- tdb_close(tdb);
+ ok1(ntdb_get_flags(ntdb) & NTDB_CONVERT);
+ ntdb_close(ntdb);
} else {
- if (!ok1(!tdb && errno == EIO))
+ if (!ok1(!ntdb && errno == EIO))
failtest_exit(exit_status());
ok1(tap_log_messages == ++messages);
- if (!ok1(log_last && strstr(log_last, "TDB_CONVERT")))
+ if (!ok1(log_last && strstr(log_last, "NTDB_CONVERT")))
failtest_exit(exit_status());
}
- /* If don't say TDB_CONVERT, it *may* be converted */
- tdb = tdb_open("run-35-convert.tdb",
- flags[i] & ~TDB_CONVERT,
+ /* If don't say NTDB_CONVERT, it *may* be converted */
+ ntdb = ntdb_open("run-35-convert.ntdb",
+ flags[i] & ~NTDB_CONVERT,
O_RDWR, 0600, &tap_log_attr);
- if (!tdb)
+ if (!ntdb)
failtest_exit(exit_status());
- ok1(tdb_get_flags(tdb) == flags[i]);
- tdb_close(tdb);
+ ok1(ntdb_get_flags(ntdb) == flags[i]);
+ ntdb_close(ntdb);
}
failtest_exit(exit_status());
}
diff --git a/lib/ntdb/test/run-50-multiple-freelists.c b/lib/ntdb/test/run-50-multiple-freelists.c
new file mode 100644
index 0000000000..962462e5d4
--- /dev/null
+++ b/lib/ntdb/test/run-50-multiple-freelists.c
@@ -0,0 +1,70 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+#include "layout.h"
+
+int main(int argc, char *argv[])
+{
+ ntdb_off_t off;
+ struct ntdb_context *ntdb;
+ struct ntdb_layout *layout;
+ NTDB_DATA key, data;
+ union ntdb_attribute seed;
+
+ /* This seed value previously tickled a layout.c bug. */
+ seed.base.attr = NTDB_ATTRIBUTE_SEED;
+ seed.seed.seed = 0xb1142bc054d035b4ULL;
+ seed.base.next = &tap_log_attr;
+
+ plan_tests(11);
+ key = ntdb_mkdata("Hello", 5);
+ data = ntdb_mkdata("world", 5);
+
+ /* Create a NTDB with three free tables. */
+ layout = new_ntdb_layout();
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_free(layout, 80, 0);
+ /* Used record prevent coalescing. */
+ ntdb_layout_add_used(layout, key, data, 6);
+ ntdb_layout_add_free(layout, 160, 1);
+ key.dsize--;
+ ntdb_layout_add_used(layout, key, data, 7);
+ ntdb_layout_add_free(layout, 320, 2);
+ key.dsize--;
+ ntdb_layout_add_used(layout, key, data, 8);
+ ntdb_layout_add_free(layout, 40, 0);
+ ntdb = ntdb_layout_get(layout, free, &seed);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+
+ off = get_free(ntdb, 0, 80 - sizeof(struct ntdb_used_record), 0,
+ NTDB_USED_MAGIC, 0);
+ ok1(off == layout->elem[3].base.off);
+ ok1(ntdb->ftable_off == layout->elem[0].base.off);
+
+ off = get_free(ntdb, 0, 160 - sizeof(struct ntdb_used_record), 0,
+ NTDB_USED_MAGIC, 0);
+ ok1(off == layout->elem[5].base.off);
+ ok1(ntdb->ftable_off == layout->elem[1].base.off);
+
+ off = get_free(ntdb, 0, 320 - sizeof(struct ntdb_used_record), 0,
+ NTDB_USED_MAGIC, 0);
+ ok1(off == layout->elem[7].base.off);
+ ok1(ntdb->ftable_off == layout->elem[2].base.off);
+
+ off = get_free(ntdb, 0, 40 - sizeof(struct ntdb_used_record), 0,
+ NTDB_USED_MAGIC, 0);
+ ok1(off == layout->elem[9].base.off);
+ ok1(ntdb->ftable_off == layout->elem[0].base.off);
+
+ /* Now we fail. */
+ off = get_free(ntdb, 0, 0, 1, NTDB_USED_MAGIC, 0);
+ ok1(off == 0);
+
+ ntdb_close(ntdb);
+ ntdb_layout_free(layout);
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/tdb2/test/run-56-open-during-transaction.c b/lib/ntdb/test/run-56-open-during-transaction.c
index c514caa92b..f585aa13c8 100644
--- a/lib/tdb2/test/run-56-open-during-transaction.c
+++ b/lib/ntdb/test/run-56-open-during-transaction.c
@@ -11,7 +11,7 @@ static int ftruncate_check(int fd, off_t length);
#define fcntl fcntl_with_lockcheck
#define ftruncate ftruncate_check
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <stdlib.h>
#include <stdbool.h>
@@ -22,7 +22,7 @@ static int ftruncate_check(int fd, off_t length);
static struct agent *agent;
static bool opened;
static int errors = 0;
-#define TEST_DBNAME "run-56-open-during-transaction.tdb"
+#define TEST_DBNAME "run-56-open-during-transaction.ntdb"
#undef write
#undef pwrite
@@ -80,7 +80,7 @@ static void check_file_intact(int fd)
if (ret == SUCCESS) {
ret = external_agent_operation(agent, CLOSE, NULL);
if (ret != SUCCESS) {
- diag("Agent failed to close tdb: %s",
+ diag("Agent failed to close ntdb: %s",
agent_return_name(ret));
errors++;
}
@@ -127,11 +127,11 @@ static int ftruncate_check(int fd, off_t length)
int main(int argc, char *argv[])
{
- const int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+ const int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
int i;
- struct tdb_context *tdb;
- TDB_DATA key, data;
+ struct ntdb_context *ntdb;
+ NTDB_DATA key, data;
plan_tests(sizeof(flags)/sizeof(flags[0]) * 5);
agent = prepare_external_agent();
@@ -141,24 +141,24 @@ int main(int argc, char *argv[])
unlock_callback = after_unlock;
for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
diag("Test with %s and %s\n",
- (flags[i] & TDB_CONVERT) ? "CONVERT" : "DEFAULT",
- (flags[i] & TDB_NOMMAP) ? "no mmap" : "mmap");
+ (flags[i] & NTDB_CONVERT) ? "CONVERT" : "DEFAULT",
+ (flags[i] & NTDB_NOMMAP) ? "no mmap" : "mmap");
unlink(TEST_DBNAME);
- tdb = tdb_open(TEST_DBNAME, flags[i],
+ ntdb = ntdb_open(TEST_DBNAME, flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
+ ok1(ntdb);
opened = true;
- ok1(tdb_transaction_start(tdb) == 0);
- key = tdb_mkdata("hi", strlen("hi"));
- data = tdb_mkdata("world", strlen("world"));
+ ok1(ntdb_transaction_start(ntdb) == 0);
+ key = ntdb_mkdata("hi", strlen("hi"));
+ data = ntdb_mkdata("world", strlen("world"));
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_transaction_commit(tdb) == 0);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb_transaction_commit(ntdb) == 0);
ok(!errors, "We had %u open errors", errors);
opened = false;
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
return exit_status();
diff --git a/lib/tdb2/test/run-57-die-during-transaction.c b/lib/ntdb/test/run-57-die-during-transaction.c
index ee33a896ff..98ec9dd63a 100644
--- a/lib/tdb2/test/run-57-die-during-transaction.c
+++ b/lib/ntdb/test/run-57-die-during-transaction.c
@@ -81,7 +81,7 @@ static void free_all(void)
#define free free_noleak
#define realloc realloc_noleak
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#undef malloc
#undef free
@@ -93,6 +93,7 @@ static void free_all(void)
#include <stdbool.h>
#include <stdarg.h>
+#include <ccan/err/err.h>
#include <setjmp.h>
#include "external-agent.h"
#include "logging.h"
@@ -100,7 +101,7 @@ static void free_all(void)
static bool in_transaction;
static int target, current;
static jmp_buf jmpbuf;
-#define TEST_DBNAME "run-57-die-during-transaction.tdb"
+#define TEST_DBNAME "run-57-die-during-transaction.ntdb"
#define KEY_STRING "helloworld"
static void maybe_die(int fd)
@@ -153,24 +154,24 @@ static int ftruncate_check(int fd, off_t length)
static bool test_death(enum operation op, struct agent *agent)
{
- struct tdb_context *tdb = NULL;
- TDB_DATA key;
+ struct ntdb_context *ntdb = NULL;
+ NTDB_DATA key;
enum agent_return ret;
int needed_recovery = 0;
current = target = 0;
reset:
unlink(TEST_DBNAME);
- tdb = tdb_open(TEST_DBNAME, TDB_NOMMAP,
+ ntdb = ntdb_open(TEST_DBNAME, NTDB_NOMMAP,
O_CREAT|O_TRUNC|O_RDWR, 0600, &tap_log_attr);
- if (!tdb) {
- diag("Failed opening TDB: %s", strerror(errno));
+ if (!ntdb) {
+ diag("Failed opening NTDB: %s", strerror(errno));
return false;
}
if (setjmp(jmpbuf) != 0) {
/* We're partway through. Simulate our death. */
- close(tdb->file->fd);
+ close(ntdb->file->fd);
forget_locking();
in_transaction = false;
@@ -215,7 +216,7 @@ reset:
/* Suppress logging as this tries to use closed fd. */
suppress_logging = true;
suppress_lockcheck = true;
- tdb_close(tdb);
+ ntdb_close(ntdb);
suppress_logging = false;
suppress_lockcheck = false;
target++;
@@ -225,8 +226,8 @@ reset:
}
/* Put key for agent to fetch. */
- key = tdb_mkdata(KEY_STRING, strlen(KEY_STRING));
- if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
+ key = ntdb_mkdata(KEY_STRING, strlen(KEY_STRING));
+ if (ntdb_store(ntdb, key, key, NTDB_INSERT) != 0)
return false;
/* This is the key we insert in transaction. */
@@ -241,20 +242,20 @@ reset:
errx(1, "Agent failed find key: %s", agent_return_name(ret));
in_transaction = true;
- if (tdb_transaction_start(tdb) != 0)
+ if (ntdb_transaction_start(ntdb) != 0)
return false;
- if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
+ if (ntdb_store(ntdb, key, key, NTDB_INSERT) != 0)
return false;
- if (tdb_transaction_commit(tdb) != 0)
+ if (ntdb_transaction_commit(ntdb) != 0)
return false;
in_transaction = false;
/* We made it! */
diag("Completed %u runs", current);
- tdb_close(tdb);
+ ntdb_close(ntdb);
ret = external_agent_operation(agent, CLOSE, "");
if (ret != SUCCESS) {
diag("Step %u close failed = %s", current,
diff --git a/lib/ntdb/test/run-64-bit-tdb.c b/lib/ntdb/test/run-64-bit-tdb.c
new file mode 100644
index 0000000000..6a146cb1cf
--- /dev/null
+++ b/lib/ntdb/test/run-64-bit-tdb.c
@@ -0,0 +1,72 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ if (sizeof(off_t) <= 4) {
+ plan_tests(1);
+ pass("No 64 bit off_t");
+ return exit_status();
+ }
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ off_t old_size;
+ NTDB_DATA k, d;
+ struct hash_info h;
+ struct ntdb_used_record rec;
+ ntdb_off_t off;
+
+ ntdb = ntdb_open("run-64-bit-ntdb.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ old_size = ntdb->file->map_size;
+
+ /* This makes a sparse file */
+ ok1(ftruncate(ntdb->file->fd, 0xFFFFFFF0) == 0);
+ ok1(add_free_record(ntdb, old_size, 0xFFFFFFF0 - old_size,
+ NTDB_LOCK_WAIT, false) == NTDB_SUCCESS);
+
+ /* Now add a little record past the 4G barrier. */
+ ok1(ntdb_expand_file(ntdb, 100) == NTDB_SUCCESS);
+ ok1(add_free_record(ntdb, 0xFFFFFFF0, 100, NTDB_LOCK_WAIT, false)
+ == NTDB_SUCCESS);
+
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
+
+ /* Test allocation path. */
+ k = ntdb_mkdata("key", 4);
+ d = ntdb_mkdata("data", 5);
+ ok1(ntdb_store(ntdb, k, d, NTDB_INSERT) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
+
+ /* Make sure it put it at end as we expected. */
+ off = find_and_lock(ntdb, k, F_RDLCK, &h, &rec, NULL);
+ ok1(off >= 0xFFFFFFF0);
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
+
+ ok1(ntdb_fetch(ntdb, k, &d) == 0);
+ ok1(d.dsize == 5);
+ ok1(strcmp((char *)d.dptr, "data") == 0);
+ free(d.dptr);
+
+ ok1(ntdb_delete(ntdb, k) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
+
+ ntdb_close(ntdb);
+ }
+
+ /* We might get messages about mmap failing, so don't test
+ * tap_log_messages */
+ return exit_status();
+}
diff --git a/lib/ntdb/test/run-90-get-set-attributes.c b/lib/ntdb/test/run-90-get-set-attributes.c
new file mode 100644
index 0000000000..fc265b0729
--- /dev/null
+++ b/lib/ntdb/test/run-90-get-set-attributes.c
@@ -0,0 +1,159 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+static int mylock(int fd, int rw, off_t off, off_t len, bool waitflag,
+ void *unused)
+{
+ return 0;
+}
+
+static int myunlock(int fd, int rw, off_t off, off_t len, void *unused)
+{
+ return 0;
+}
+
+static uint64_t hash_fn(const void *key, size_t len, uint64_t seed,
+ void *priv)
+{
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+ union ntdb_attribute seed_attr;
+ union ntdb_attribute hash_attr;
+ union ntdb_attribute lock_attr;
+
+ seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
+ seed_attr.base.next = &hash_attr;
+ seed_attr.seed.seed = 100;
+
+ hash_attr.base.attr = NTDB_ATTRIBUTE_HASH;
+ hash_attr.base.next = &lock_attr;
+ hash_attr.hash.fn = hash_fn;
+ hash_attr.hash.data = &hash_attr;
+
+ lock_attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
+ lock_attr.base.next = &tap_log_attr;
+ lock_attr.flock.lock = mylock;
+ lock_attr.flock.unlock = myunlock;
+ lock_attr.flock.data = &lock_attr;
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 50);
+
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ union ntdb_attribute attr;
+
+ /* First open with no attributes. */
+ ntdb = ntdb_open("run-90-get-set-attributes.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, NULL);
+ ok1(ntdb);
+
+ /* Get log on no attributes will fail */
+ attr.base.attr = NTDB_ATTRIBUTE_LOG;
+ ok1(ntdb_get_attribute(ntdb, &attr) == NTDB_ERR_NOEXIST);
+ /* These always work. */
+ attr.base.attr = NTDB_ATTRIBUTE_HASH;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_HASH);
+ ok1(attr.hash.fn == ntdb_jenkins_hash);
+ attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_FLOCK);
+ ok1(attr.flock.lock == ntdb_fcntl_lock);
+ ok1(attr.flock.unlock == ntdb_fcntl_unlock);
+ attr.base.attr = NTDB_ATTRIBUTE_SEED;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_SEED);
+ /* This is possible, just astronomically unlikely. */
+ ok1(attr.seed.seed != 0);
+
+ /* Unset attributes. */
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_LOG);
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
+
+ /* Set them. */
+ ok1(ntdb_set_attribute(ntdb, &tap_log_attr) == 0);
+ ok1(ntdb_set_attribute(ntdb, &lock_attr) == 0);
+ /* These should fail. */
+ ok1(ntdb_set_attribute(ntdb, &seed_attr) == NTDB_ERR_EINVAL);
+ ok1(tap_log_messages == 1);
+ ok1(ntdb_set_attribute(ntdb, &hash_attr) == NTDB_ERR_EINVAL);
+ ok1(tap_log_messages == 2);
+ tap_log_messages = 0;
+
+ /* Getting them should work as expected. */
+ attr.base.attr = NTDB_ATTRIBUTE_LOG;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_LOG);
+ ok1(attr.log.fn == tap_log_attr.log.fn);
+ ok1(attr.log.data == tap_log_attr.log.data);
+
+ attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_FLOCK);
+ ok1(attr.flock.lock == mylock);
+ ok1(attr.flock.unlock == myunlock);
+ ok1(attr.flock.data == &lock_attr);
+
+ /* Unset them again. */
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
+ ok1(tap_log_messages == 0);
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_LOG);
+ ok1(tap_log_messages == 0);
+
+ ntdb_close(ntdb);
+ ok1(tap_log_messages == 0);
+
+ /* Now open with all attributes. */
+ ntdb = ntdb_open("run-90-get-set-attributes.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600,
+ &seed_attr);
+
+ ok1(ntdb);
+
+ /* Get will succeed */
+ attr.base.attr = NTDB_ATTRIBUTE_LOG;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_LOG);
+ ok1(attr.log.fn == tap_log_attr.log.fn);
+ ok1(attr.log.data == tap_log_attr.log.data);
+
+ attr.base.attr = NTDB_ATTRIBUTE_HASH;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_HASH);
+ ok1(attr.hash.fn == hash_fn);
+ ok1(attr.hash.data == &hash_attr);
+
+ attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_FLOCK);
+ ok1(attr.flock.lock == mylock);
+ ok1(attr.flock.unlock == myunlock);
+ ok1(attr.flock.data == &lock_attr);
+
+ attr.base.attr = NTDB_ATTRIBUTE_SEED;
+ ok1(ntdb_get_attribute(ntdb, &attr) == 0);
+ ok1(attr.base.attr == NTDB_ATTRIBUTE_SEED);
+ ok1(attr.seed.seed == seed_attr.seed.seed);
+
+ /* Unset attributes. */
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_HASH);
+ ok1(tap_log_messages == 1);
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_SEED);
+ ok1(tap_log_messages == 2);
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
+ ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_LOG);
+ ok1(tap_log_messages == 2);
+ tap_log_messages = 0;
+
+ ntdb_close(ntdb);
+
+ }
+ return exit_status();
+}
diff --git a/lib/tdb2/test/run-capabilities.c b/lib/ntdb/test/run-capabilities.c
index 1501abbe5c..c2c6aa15db 100644
--- a/lib/tdb2/test/run-capabilities.c
+++ b/lib/ntdb/test/run-capabilities.c
@@ -1,5 +1,5 @@
#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include "logging.h"
#include "layout.h"
@@ -18,28 +18,28 @@ static size_t len_of(bool breaks_check, bool breaks_write, bool breaks_open)
return len;
}
-/* Creates a TDB with various capabilities. */
-static void create_tdb(const char *name,
+/* Creates a NTDB with various capabilities. */
+static void create_ntdb(const char *name,
unsigned int cap,
bool breaks_check,
bool breaks_write,
bool breaks_open, ...)
{
- TDB_DATA key, data;
+ NTDB_DATA key, data;
va_list ap;
- struct tdb_layout *layout;
- struct tdb_context *tdb;
+ struct ntdb_layout *layout;
+ struct ntdb_context *ntdb;
int fd;
- key = tdb_mkdata("Hello", 5);
- data = tdb_mkdata("world", 5);
+ key = ntdb_mkdata("Hello", 5);
+ data = ntdb_mkdata("world", 5);
- /* Create a TDB with some data, and some capabilities */
- layout = new_tdb_layout();
- tdb_layout_add_freetable(layout);
- tdb_layout_add_used(layout, key, data, 6);
- tdb_layout_add_free(layout, 80, 0);
- tdb_layout_add_capability(layout, cap,
+ /* Create a NTDB with some data, and some capabilities */
+ layout = new_ntdb_layout();
+ ntdb_layout_add_freetable(layout);
+ ntdb_layout_add_used(layout, key, data, 6);
+ ntdb_layout_add_free(layout, 80, 0);
+ ntdb_layout_add_capability(layout, cap,
breaks_write, breaks_check, breaks_open,
len_of(breaks_check, breaks_write, breaks_open));
@@ -50,9 +50,9 @@ static void create_tdb(const char *name,
breaks_open = va_arg(ap, int);
key.dsize--;
- tdb_layout_add_used(layout, key, data, 11 - key.dsize);
- tdb_layout_add_free(layout, 80, 0);
- tdb_layout_add_capability(layout, cap,
+ ntdb_layout_add_used(layout, key, data, 11 - key.dsize);
+ ntdb_layout_add_free(layout, 80, 0);
+ ntdb_layout_add_capability(layout, cap,
breaks_write, breaks_check,
breaks_open,
len_of(breaks_check, breaks_write,
@@ -61,23 +61,23 @@ static void create_tdb(const char *name,
va_end(ap);
/* We open-code this, because we need to use the failtest write. */
- tdb = tdb_layout_get(layout, failtest_free, &tap_log_attr);
+ ntdb = ntdb_layout_get(layout, failtest_free, &tap_log_attr);
fd = open(name, O_RDWR|O_TRUNC|O_CREAT, 0600);
if (fd < 0)
err(1, "opening %s for writing", name);
- if (write(fd, tdb->file->map_ptr, tdb->file->map_size)
- != tdb->file->map_size)
+ if (write(fd, ntdb->file->map_ptr, ntdb->file->map_size)
+ != ntdb->file->map_size)
err(1, "writing %s", name);
close(fd);
- tdb_close(tdb);
- tdb_layout_free(layout);
+ ntdb_close(ntdb);
+ ntdb_layout_free(layout);
}
/* Note all the "goto out" early exits: they're to shorten failtest time. */
int main(int argc, char *argv[])
{
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
char *summary;
failtest_init(argc, argv);
@@ -87,72 +87,72 @@ int main(int argc, char *argv[])
failtest_suppress = true;
/* Capability says you can ignore it? */
- create_tdb("run-capabilities.tdb", 1, false, false, false, 0);
+ create_ntdb("run-capabilities.ntdb", 1, false, false, false, 0);
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
&tap_log_attr);
failtest_suppress = true;
- if (!ok1(tdb))
+ if (!ok1(ntdb))
goto out;
ok1(tap_log_messages == 0);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
ok1(tap_log_messages == 0);
- tdb_close(tdb);
+ ntdb_close(ntdb);
/* Two capabilitues say you can ignore them? */
- create_tdb("run-capabilities.tdb",
+ create_ntdb("run-capabilities.ntdb",
1, false, false, false,
2, false, false, false, 0);
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
&tap_log_attr);
failtest_suppress = true;
- if (!ok1(tdb))
+ if (!ok1(ntdb))
goto out;
ok1(tap_log_messages == 0);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
ok1(tap_log_messages == 0);
- ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
+ ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
ok1(strstr(summary, "Capability 1\n"));
free(summary);
- tdb_close(tdb);
+ ntdb_close(ntdb);
/* Capability says you can't check. */
- create_tdb("run-capabilities.tdb",
+ create_ntdb("run-capabilities.ntdb",
1, false, false, false,
2, true, false, false, 0);
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
&tap_log_attr);
failtest_suppress = true;
- if (!ok1(tdb))
+ if (!ok1(ntdb))
goto out;
ok1(tap_log_messages == 0);
- ok1(tdb_get_flags(tdb) & TDB_CANT_CHECK);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
+ ok1(ntdb_get_flags(ntdb) & NTDB_CANT_CHECK);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
/* We expect a warning! */
ok1(tap_log_messages == 1);
ok1(strstr(log_last, "capabilit"));
- ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
+ ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
ok1(strstr(summary, "Capability 1\n"));
ok1(strstr(summary, "Capability 2 (uncheckable)\n"));
free(summary);
- tdb_close(tdb);
+ ntdb_close(ntdb);
/* Capability says you can't write. */
- create_tdb("run-capabilities.tdb",
+ create_ntdb("run-capabilities.ntdb",
1, false, false, false,
2, false, true, false, 0);
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
&tap_log_attr);
failtest_suppress = true;
/* We expect a message. */
- ok1(!tdb);
+ ok1(!ntdb);
if (!ok1(tap_log_messages == 2))
goto out;
if (!ok1(strstr(log_last, "unknown")))
@@ -161,48 +161,48 @@ int main(int argc, char *argv[])
/* We can open it read-only though! */
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDONLY, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDONLY, 0,
&tap_log_attr);
failtest_suppress = true;
- if (!ok1(tdb))
+ if (!ok1(ntdb))
goto out;
ok1(tap_log_messages == 2);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
ok1(tap_log_messages == 2);
- ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
+ ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
ok1(strstr(summary, "Capability 1\n"));
ok1(strstr(summary, "Capability 2 (read-only)\n"));
free(summary);
- tdb_close(tdb);
+ ntdb_close(ntdb);
/* Capability says you can't open. */
- create_tdb("run-capabilities.tdb",
+ create_ntdb("run-capabilities.ntdb",
1, false, false, false,
2, false, false, true, 0);
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
&tap_log_attr);
failtest_suppress = true;
/* We expect a message. */
- ok1(!tdb);
+ ok1(!ntdb);
if (!ok1(tap_log_messages == 3))
goto out;
if (!ok1(strstr(log_last, "unknown")))
goto out;
/* Combine capabilities correctly. */
- create_tdb("run-capabilities.tdb",
+ create_ntdb("run-capabilities.ntdb",
1, false, false, false,
2, true, false, false,
3, false, true, false, 0);
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
&tap_log_attr);
failtest_suppress = true;
/* We expect a message. */
- ok1(!tdb);
+ ok1(!ntdb);
if (!ok1(tap_log_messages == 4))
goto out;
if (!ok1(strstr(log_last, "unknown")))
@@ -211,36 +211,36 @@ int main(int argc, char *argv[])
/* We can open it read-only though! */
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDONLY, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDONLY, 0,
&tap_log_attr);
failtest_suppress = true;
- if (!ok1(tdb))
+ if (!ok1(ntdb))
goto out;
ok1(tap_log_messages == 4);
- ok1(tdb_get_flags(tdb) & TDB_CANT_CHECK);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
+ ok1(ntdb_get_flags(ntdb) & NTDB_CANT_CHECK);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
/* We expect a warning! */
ok1(tap_log_messages == 5);
ok1(strstr(log_last, "unknown"));
- ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
+ ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
ok1(strstr(summary, "Capability 1\n"));
ok1(strstr(summary, "Capability 2 (uncheckable)\n"));
ok1(strstr(summary, "Capability 3 (read-only)\n"));
free(summary);
- tdb_close(tdb);
+ ntdb_close(ntdb);
/* Two capability flags in one. */
- create_tdb("run-capabilities.tdb",
+ create_ntdb("run-capabilities.ntdb",
1, false, false, false,
2, true, true, false,
0);
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
&tap_log_attr);
failtest_suppress = true;
/* We expect a message. */
- ok1(!tdb);
+ ok1(!ntdb);
if (!ok1(tap_log_messages == 6))
goto out;
if (!ok1(strstr(log_last, "unknown")))
@@ -249,22 +249,22 @@ int main(int argc, char *argv[])
/* We can open it read-only though! */
failtest_suppress = false;
- tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDONLY, 0,
+ ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDONLY, 0,
&tap_log_attr);
failtest_suppress = true;
- if (!ok1(tdb))
+ if (!ok1(ntdb))
goto out;
ok1(tap_log_messages == 6);
- ok1(tdb_get_flags(tdb) & TDB_CANT_CHECK);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
+ ok1(ntdb_get_flags(ntdb) & NTDB_CANT_CHECK);
+ ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
/* We expect a warning! */
ok1(tap_log_messages == 7);
ok1(strstr(log_last, "unknown"));
- ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
+ ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
ok1(strstr(summary, "Capability 1\n"));
ok1(strstr(summary, "Capability 2 (uncheckable,read-only)\n"));
free(summary);
- tdb_close(tdb);
+ ntdb_close(ntdb);
out:
failtest_exit(exit_status());
diff --git a/lib/ntdb/test/run-expand-in-transaction.c b/lib/ntdb/test/run-expand-in-transaction.c
new file mode 100644
index 0000000000..dadbec7922
--- /dev/null
+++ b/lib/ntdb/test/run-expand-in-transaction.c
@@ -0,0 +1,36 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = ntdb_mkdata("key", 3);
+ NTDB_DATA data = ntdb_mkdata("data", 4);
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
+
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ size_t size;
+ ntdb = ntdb_open("run-expand-in-transaction.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+
+ size = ntdb->file->map_size;
+ ok1(ntdb_transaction_start(ntdb) == 0);
+ ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
+ ok1(ntdb->file->map_size > size);
+ ok1(ntdb_transaction_commit(ntdb) == 0);
+ ok1(ntdb->file->map_size > size);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ntdb_close(ntdb);
+ }
+
+ ok1(tap_log_messages == 0);
+ return exit_status();
+}
diff --git a/lib/tdb2/test/run-features.c b/lib/ntdb/test/run-features.c
index f552fcfb58..0d6b3bce76 100644
--- a/lib/tdb2/test/run-features.c
+++ b/lib/ntdb/test/run-features.c
@@ -1,60 +1,60 @@
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include "logging.h"
int main(int argc, char *argv[])
{
unsigned int i, j;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = { (unsigned char *)&j, sizeof(j) };
- struct tdb_data data = { (unsigned char *)&j, sizeof(j) };
+ struct ntdb_context *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
+ NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
+ NTDB_DATA data = { (unsigned char *)&j, sizeof(j) };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 8 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
uint64_t features;
- tdb = tdb_open("run-features.tdb", flags[i],
+ ntdb = ntdb_open("run-features.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
/* Put some stuff in there. */
for (j = 0; j < 100; j++) {
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
- fail("Storing in tdb");
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
+ fail("Storing in ntdb");
}
/* Mess with features fields in hdr. */
- features = (~TDB_FEATURE_MASK ^ 1);
- ok1(tdb_write_convert(tdb, offsetof(struct tdb_header,
+ features = (~NTDB_FEATURE_MASK ^ 1);
+ ok1(ntdb_write_convert(ntdb, offsetof(struct ntdb_header,
features_used),
&features, sizeof(features)) == 0);
- ok1(tdb_write_convert(tdb, offsetof(struct tdb_header,
+ ok1(ntdb_write_convert(ntdb, offsetof(struct ntdb_header,
features_offered),
&features, sizeof(features)) == 0);
- tdb_close(tdb);
+ ntdb_close(ntdb);
- tdb = tdb_open("run-features.tdb", flags[i], O_RDWR, 0,
+ ntdb = ntdb_open("run-features.ntdb", flags[i], O_RDWR, 0,
&tap_log_attr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
/* Should not have changed features offered. */
- ok1(tdb_read_convert(tdb, offsetof(struct tdb_header,
+ ok1(ntdb_read_convert(ntdb, offsetof(struct ntdb_header,
features_offered),
&features, sizeof(features)) == 0);
- ok1(features == (~TDB_FEATURE_MASK ^ 1));
+ ok1(features == (~NTDB_FEATURE_MASK ^ 1));
/* Should have cleared unknown bits in features_used. */
- ok1(tdb_read_convert(tdb, offsetof(struct tdb_header,
+ ok1(ntdb_read_convert(ntdb, offsetof(struct ntdb_header,
features_used),
&features, sizeof(features)) == 0);
- ok1(features == (1 & TDB_FEATURE_MASK));
+ ok1(features == (1 & NTDB_FEATURE_MASK));
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/tdb2/test/run-lockall.c b/lib/ntdb/test/run-lockall.c
index 3ae0d14f65..964164e20b 100644
--- a/lib/tdb2/test/run-lockall.c
+++ b/lib/ntdb/test/run-lockall.c
@@ -3,25 +3,24 @@
#include "lock-tracking.h"
#define fcntl fcntl_with_lockcheck
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
-#include <ccan/err/err.h>
#include "external-agent.h"
#include "logging.h"
-#define TEST_DBNAME "run-lockall.tdb"
+#define TEST_DBNAME "run-lockall.ntdb"
#undef fcntl
int main(int argc, char *argv[])
{
struct agent *agent;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
int i;
plan_tests(13 * sizeof(flags)/sizeof(flags[0]) + 1);
@@ -31,38 +30,38 @@ int main(int argc, char *argv[])
for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
enum agent_return ret;
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
- tdb = tdb_open(TEST_DBNAME, flags[i],
+ ntdb = ntdb_open(TEST_DBNAME, flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
+ ok1(ntdb);
ret = external_agent_operation(agent, OPEN, TEST_DBNAME);
ok1(ret == SUCCESS);
- ok1(tdb_lockall(tdb) == TDB_SUCCESS);
+ ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
ok1(external_agent_operation(agent, STORE, "key")
== WOULD_HAVE_BLOCKED);
ok1(external_agent_operation(agent, FETCH, "key")
== WOULD_HAVE_BLOCKED);
/* Test nesting. */
- ok1(tdb_lockall(tdb) == TDB_SUCCESS);
- tdb_unlockall(tdb);
- tdb_unlockall(tdb);
+ ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
+ ntdb_unlockall(ntdb);
+ ntdb_unlockall(ntdb);
ok1(external_agent_operation(agent, STORE, "key") == SUCCESS);
- ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
+ ok1(ntdb_lockall_read(ntdb) == NTDB_SUCCESS);
ok1(external_agent_operation(agent, STORE, "key")
== WOULD_HAVE_BLOCKED);
ok1(external_agent_operation(agent, FETCH, "key") == SUCCESS);
- ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
- tdb_unlockall_read(tdb);
- tdb_unlockall_read(tdb);
+ ok1(ntdb_lockall_read(ntdb) == NTDB_SUCCESS);
+ ntdb_unlockall_read(ntdb);
+ ntdb_unlockall_read(ntdb);
ok1(external_agent_operation(agent, STORE, "key") == SUCCESS);
ok1(external_agent_operation(agent, CLOSE, NULL) == SUCCESS);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
free_external_agent(agent);
diff --git a/lib/tdb2/test/run-remap-in-read_traverse.c b/lib/ntdb/test/run-remap-in-read_traverse.c
index 16a1baab46..2d817c2d73 100644
--- a/lib/tdb2/test/run-remap-in-read_traverse.c
+++ b/lib/ntdb/test/run-remap-in-read_traverse.c
@@ -1,11 +1,11 @@
-#include "tdb2-source.h"
-/* We had a bug where we marked the tdb read-only for a tdb_traverse_read.
- * If we then expanded the tdb, we would remap read-only, and later SEGV. */
+#include "ntdb-source.h"
+/* We had a bug where we marked the ntdb read-only for a ntdb_traverse_read.
+ * If we then expanded the ntdb, we would remap read-only, and later SEGV. */
#include "tap-interface.h"
#include "external-agent.h"
#include "logging.h"
-static bool file_larger(int fd, tdb_len_t size)
+static bool file_larger(int fd, ntdb_len_t size)
{
struct stat st;
@@ -13,7 +13,7 @@ static bool file_larger(int fd, tdb_len_t size)
return st.st_size != size;
}
-static unsigned add_records_to_grow(struct agent *agent, int fd, tdb_len_t size)
+static unsigned add_records_to_grow(struct agent *agent, int fd, ntdb_len_t size)
{
unsigned int i;
@@ -31,27 +31,27 @@ int main(int argc, char *argv[])
{
unsigned int i;
struct agent *agent;
- struct tdb_context *tdb;
- struct tdb_data d = tdb_mkdata("hello", 5);
- const char filename[] = "run-remap-in-read_traverse.tdb";
+ struct ntdb_context *ntdb;
+ NTDB_DATA d = ntdb_mkdata("hello", 5);
+ const char filename[] = "run-remap-in-read_traverse.ntdb";
plan_tests(4);
agent = prepare_external_agent();
- tdb = tdb_open(filename, TDB_DEFAULT,
+ ntdb = ntdb_open(filename, NTDB_DEFAULT,
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
ok1(external_agent_operation(agent, OPEN, filename) == SUCCESS);
- i = add_records_to_grow(agent, tdb->file->fd, tdb->file->map_size);
+ i = add_records_to_grow(agent, ntdb->file->fd, ntdb->file->map_size);
/* Do a traverse. */
- ok1(tdb_traverse(tdb, NULL, NULL) == i);
+ ok1(ntdb_traverse(ntdb, NULL, NULL) == i);
/* Now store something! */
- ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
+ ok1(ntdb_store(ntdb, d, d, NTDB_INSERT) == 0);
ok1(tap_log_messages == 0);
- tdb_close(tdb);
+ ntdb_close(ntdb);
free_external_agent(agent);
return exit_status();
}
diff --git a/lib/ntdb/test/run-seed.c b/lib/ntdb/test/run-seed.c
new file mode 100644
index 0000000000..2514f728ac
--- /dev/null
+++ b/lib/ntdb/test/run-seed.c
@@ -0,0 +1,61 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+#include "logging.h"
+
+static int log_count = 0;
+
+/* Normally we get a log when setting random seed. */
+static void my_log_fn(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
+ const char *message, void *priv)
+{
+ log_count++;
+}
+
+static union ntdb_attribute log_attr = {
+ .log = { .base = { .attr = NTDB_ATTRIBUTE_LOG },
+ .fn = my_log_fn }
+};
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ struct ntdb_context *ntdb;
+ union ntdb_attribute attr;
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+
+ attr.seed.base.attr = NTDB_ATTRIBUTE_SEED;
+ attr.seed.base.next = &log_attr;
+ attr.seed.seed = 42;
+
+ plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 4 * 3);
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ struct ntdb_header hdr;
+ int fd;
+ ntdb = ntdb_open("run-seed.ntdb", flags[i],
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &attr);
+ ok1(ntdb);
+ if (!ntdb)
+ continue;
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
+ ok1(ntdb->hash_seed == 42);
+ ok1(log_count == 0);
+ ntdb_close(ntdb);
+
+ if (flags[i] & NTDB_INTERNAL)
+ continue;
+
+ fd = open("run-seed.ntdb", O_RDONLY);
+ ok1(fd >= 0);
+ ok1(read(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
+ if (flags[i] & NTDB_CONVERT)
+ ok1(bswap_64(hdr.hash_seed) == 42);
+ else
+ ok1(hdr.hash_seed == 42);
+ close(fd);
+ }
+ return exit_status();
+}
diff --git a/lib/ntdb/test/run-tdb_errorstr.c b/lib/ntdb/test/run-tdb_errorstr.c
new file mode 100644
index 0000000000..5b023140a7
--- /dev/null
+++ b/lib/ntdb/test/run-tdb_errorstr.c
@@ -0,0 +1,52 @@
+#include "ntdb-source.h"
+#include "tap-interface.h"
+
+int main(int argc, char *argv[])
+{
+ enum NTDB_ERROR e;
+ plan_tests(NTDB_ERR_RDONLY*-1 + 2);
+
+ for (e = NTDB_SUCCESS; e >= NTDB_ERR_RDONLY; e--) {
+ switch (e) {
+ case NTDB_SUCCESS:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "Success"));
+ break;
+ case NTDB_ERR_IO:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "IO Error"));
+ break;
+ case NTDB_ERR_LOCK:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "Locking error"));
+ break;
+ case NTDB_ERR_OOM:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "Out of memory"));
+ break;
+ case NTDB_ERR_EXISTS:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "Record exists"));
+ break;
+ case NTDB_ERR_EINVAL:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "Invalid parameter"));
+ break;
+ case NTDB_ERR_NOEXIST:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "Record does not exist"));
+ break;
+ case NTDB_ERR_RDONLY:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "write not permitted"));
+ break;
+ case NTDB_ERR_CORRUPT:
+ ok1(!strcmp(ntdb_errorstr(e),
+ "Corrupt database"));
+ break;
+ }
+ }
+ ok1(!strcmp(ntdb_errorstr(e), "Invalid error code"));
+
+ return exit_status();
+}
diff --git a/lib/tdb2/test/run-tdb_foreach.c b/lib/ntdb/test/run-tdb_foreach.c
index b1eb2de217..f1a2d00919 100644
--- a/lib/tdb2/test/run-tdb_foreach.c
+++ b/lib/ntdb/test/run-tdb_foreach.c
@@ -1,23 +1,23 @@
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include "logging.h"
-static int drop_count(struct tdb_context *tdb, unsigned int *count)
+static int drop_count(struct ntdb_context *ntdb, unsigned int *count)
{
if (--(*count) == 0)
return 1;
return 0;
}
-static int set_found(struct tdb_context *tdb, bool found[3])
+static int set_found(struct ntdb_context *ntdb, bool found[3])
{
unsigned int idx;
- if (strcmp(tdb_name(tdb), "run-tdb_foreach0.tdb") == 0)
+ if (strcmp(ntdb_name(ntdb), "run-ntdb_foreach0.ntdb") == 0)
idx = 0;
- else if (strcmp(tdb_name(tdb), "run-tdb_foreach1.tdb") == 0)
+ else if (strcmp(ntdb_name(ntdb), "run-ntdb_foreach1.ntdb") == 0)
idx = 1;
- else if (strcmp(tdb_name(tdb), "run-tdb_foreach2.tdb") == 0)
+ else if (strcmp(ntdb_name(ntdb), "run-ntdb_foreach2.ntdb") == 0)
idx = 2;
else
abort();
@@ -32,52 +32,52 @@ int main(int argc, char *argv[])
{
unsigned int i, count;
bool found[3];
- struct tdb_context *tdb0, *tdb1, *tdb2;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+ struct ntdb_context *ntdb0, *ntdb1, *ntdb;
+ int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 8);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb0 = tdb_open("run-tdb_foreach0.tdb", flags[i],
+ ntdb0 = ntdb_open("run-ntdb_foreach0.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- tdb1 = tdb_open("run-tdb_foreach1.tdb", flags[i],
+ ntdb1 = ntdb_open("run-ntdb_foreach1.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- tdb2 = tdb_open("run-tdb_foreach2.tdb", flags[i],
+ ntdb = ntdb_open("run-ntdb_foreach2.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
memset(found, 0, sizeof(found));
- tdb_foreach(set_found, found);
+ ntdb_foreach(set_found, found);
ok1(found[0] && found[1] && found[2]);
/* Test premature iteration termination */
count = 1;
- tdb_foreach(drop_count, &count);
+ ntdb_foreach(drop_count, &count);
ok1(count == 0);
- tdb_close(tdb1);
+ ntdb_close(ntdb1);
memset(found, 0, sizeof(found));
- tdb_foreach(set_found, found);
+ ntdb_foreach(set_found, found);
ok1(found[0] && !found[1] && found[2]);
- tdb_close(tdb2);
+ ntdb_close(ntdb);
memset(found, 0, sizeof(found));
- tdb_foreach(set_found, found);
+ ntdb_foreach(set_found, found);
ok1(found[0] && !found[1] && !found[2]);
- tdb1 = tdb_open("run-tdb_foreach1.tdb", flags[i],
+ ntdb1 = ntdb_open("run-ntdb_foreach1.ntdb", flags[i],
O_RDWR, 0600, &tap_log_attr);
memset(found, 0, sizeof(found));
- tdb_foreach(set_found, found);
+ ntdb_foreach(set_found, found);
ok1(found[0] && found[1] && !found[2]);
- tdb_close(tdb0);
+ ntdb_close(ntdb0);
memset(found, 0, sizeof(found));
- tdb_foreach(set_found, found);
+ ntdb_foreach(set_found, found);
ok1(!found[0] && found[1] && !found[2]);
- tdb_close(tdb1);
+ ntdb_close(ntdb1);
memset(found, 0, sizeof(found));
- tdb_foreach(set_found, found);
+ ntdb_foreach(set_found, found);
ok1(!found[0] && !found[1] && !found[2]);
ok1(tap_log_messages == 0);
}
diff --git a/lib/tdb2/test/run-traverse.c b/lib/ntdb/test/run-traverse.c
index 20d610fe66..9dfc94d3b3 100644
--- a/lib/tdb2/test/run-traverse.c
+++ b/lib/ntdb/test/run-traverse.c
@@ -1,4 +1,4 @@
-#include "tdb2-source.h"
+#include "ntdb-source.h"
#include "tap-interface.h"
#include "logging.h"
@@ -11,14 +11,14 @@ static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
*(uint64_t *)p);
}
-static bool store_records(struct tdb_context *tdb)
+static bool store_records(struct ntdb_context *ntdb)
{
int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
+ NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
for (i = 0; i < NUM_RECORDS; i++)
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
+ if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
return false;
return true;
}
@@ -28,10 +28,10 @@ struct trav_data {
int low, high;
bool mismatch;
bool delete;
- enum TDB_ERROR delete_error;
+ enum NTDB_ERROR delete_error;
};
-static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
+static int trav(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
struct trav_data *td)
{
int val;
@@ -49,8 +49,8 @@ static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
td->high = val;
if (td->delete) {
- td->delete_error = tdb_delete(tdb, key);
- if (td->delete_error != TDB_SUCCESS) {
+ td->delete_error = ntdb_delete(ntdb, key);
+ if (td->delete_error != NTDB_SUCCESS) {
return -1;
}
}
@@ -64,10 +64,10 @@ struct trav_grow_data {
unsigned int calls;
unsigned int num_large;
bool mismatch;
- enum TDB_ERROR error;
+ enum NTDB_ERROR error;
};
-static int trav_grow(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
+static int trav_grow(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
struct trav_grow_data *tgd)
{
int val;
@@ -87,8 +87,8 @@ static int trav_grow(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
/* Make a big difference to the database. */
dbuf.dptr = buffer;
dbuf.dsize = sizeof(buffer);
- tgd->error = tdb_append(tdb, key, dbuf);
- if (tgd->error != TDB_SUCCESS) {
+ tgd->error = ntdb_append(ntdb, key, dbuf);
+ if (tgd->error != NTDB_SUCCESS) {
return -1;
}
return 0;
@@ -100,12 +100,12 @@ int main(int argc, char *argv[])
int num;
struct trav_data td;
struct trav_grow_data tgd;
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
uint64_t seed = 16014841315512641303ULL;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
+ int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
+ NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
+ NTDB_NOMMAP|NTDB_CONVERT };
+ union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
.fn = fixedhash,
.data = &seed } };
@@ -113,16 +113,16 @@ int main(int argc, char *argv[])
plan_tests(sizeof(flags) / sizeof(flags[0]) * 32 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-traverse.tdb", flags[i],
+ ntdb = ntdb_open("run-traverse.ntdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
- ok1(tdb);
- if (!tdb)
+ ok1(ntdb);
+ if (!ntdb)
continue;
- ok1(tdb_traverse(tdb, NULL, NULL) == 0);
+ ok1(ntdb_traverse(ntdb, NULL, NULL) == 0);
- ok1(store_records(tdb));
- num = tdb_traverse(tdb, NULL, NULL);
+ ok1(store_records(ntdb));
+ num = ntdb_traverse(ntdb, NULL, NULL);
ok1(num == NUM_RECORDS);
/* Full traverse. */
@@ -133,7 +133,7 @@ int main(int argc, char *argv[])
td.mismatch = false;
td.delete = false;
- num = tdb_traverse(tdb, trav, &td);
+ num = ntdb_traverse(ntdb, trav, &td);
ok1(num == NUM_RECORDS);
ok1(!td.mismatch);
ok1(td.calls == NUM_RECORDS);
@@ -148,13 +148,13 @@ int main(int argc, char *argv[])
td.mismatch = false;
td.delete = false;
- num = tdb_traverse(tdb, trav, &td);
+ num = ntdb_traverse(ntdb, trav, &td);
ok1(num == NUM_RECORDS / 2);
ok1(!td.mismatch);
ok1(td.calls == NUM_RECORDS / 2);
ok1(td.low <= NUM_RECORDS / 2);
ok1(td.high > NUM_RECORDS / 2);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
ok1(tap_log_messages == 0);
/* Deleting traverse (delete everything). */
@@ -164,38 +164,38 @@ int main(int argc, char *argv[])
td.high = INT_MIN;
td.mismatch = false;
td.delete = true;
- td.delete_error = TDB_SUCCESS;
- num = tdb_traverse(tdb, trav, &td);
+ td.delete_error = NTDB_SUCCESS;
+ num = ntdb_traverse(ntdb, trav, &td);
ok1(num == NUM_RECORDS);
- ok1(td.delete_error == TDB_SUCCESS);
+ ok1(td.delete_error == NTDB_SUCCESS);
ok1(!td.mismatch);
ok1(td.calls == NUM_RECORDS);
ok1(td.low == 0);
ok1(td.high == NUM_RECORDS - 1);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
/* Now it's empty! */
- ok1(tdb_traverse(tdb, NULL, NULL) == 0);
+ ok1(ntdb_traverse(ntdb, NULL, NULL) == 0);
/* Re-add. */
- ok1(store_records(tdb));
- ok1(tdb_traverse(tdb, NULL, NULL) == NUM_RECORDS);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(store_records(ntdb));
+ ok1(ntdb_traverse(ntdb, NULL, NULL) == NUM_RECORDS);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
/* Grow. This will cause us to be reshuffled. */
tgd.calls = 0;
tgd.num_large = 0;
tgd.mismatch = false;
- tgd.error = TDB_SUCCESS;
- ok1(tdb_traverse(tdb, trav_grow, &tgd) > 1);
+ tgd.error = NTDB_SUCCESS;
+ ok1(ntdb_traverse(ntdb, trav_grow, &tgd) > 1);
ok1(tgd.error == 0);
ok1(!tgd.mismatch);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
+ ok1(ntdb_check(ntdb, NULL, NULL) == 0);
ok1(tgd.num_large < tgd.calls);
diag("growing db: %u calls, %u repeats",
tgd.calls, tgd.num_large);
- tdb_close(tdb);
+ ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);
diff --git a/lib/tdb2/test/tap-interface.c b/lib/ntdb/test/tap-interface.c
index 077ec2cd9a..077ec2cd9a 100644
--- a/lib/tdb2/test/tap-interface.c
+++ b/lib/ntdb/test/tap-interface.c
diff --git a/lib/tdb2/test/tap-interface.h b/lib/ntdb/test/tap-interface.h
index f3d4ec2545..f3d4ec2545 100644
--- a/lib/tdb2/test/tap-interface.h
+++ b/lib/ntdb/test/tap-interface.h
diff --git a/lib/ntdb/tools/Makefile b/lib/ntdb/tools/Makefile
new file mode 100644
index 0000000000..087c256d7f
--- /dev/null
+++ b/lib/ntdb/tools/Makefile
@@ -0,0 +1,16 @@
+OBJS:=../../ntdb.o ../../hash.o ../../tally.o
+CFLAGS:=-I../../.. -I.. -Wall -g -O3 #-g -pg
+LDFLAGS:=-L../../..
+
+default: ntdbtorture ntdbtool ntdbdump ntdbrestore mkntdb speed growtdb-bench
+
+ntdbdump: ntdbdump.c $(OBJS)
+ntdbrestore: ntdbrestore.c $(OBJS)
+ntdbtorture: ntdbtorture.c $(OBJS)
+ntdbtool: ntdbtool.c $(OBJS)
+mkntdb: mkntdb.c $(OBJS)
+speed: speed.c $(OBJS)
+growtdb-bench: growtdb-bench.c $(OBJS)
+
+clean:
+ rm -f ntdbtorture ntdbdump ntdbrestore ntdbtool mkntdb speed growtdb-bench
diff --git a/lib/ntdb/tools/growtdb-bench.c b/lib/ntdb/tools/growtdb-bench.c
new file mode 100644
index 0000000000..640f87af5a
--- /dev/null
+++ b/lib/ntdb/tools/growtdb-bench.c
@@ -0,0 +1,114 @@
+#include "ntdb.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ccan/err/err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static void logfn(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
+ const char *message,
+ void *data)
+{
+ fprintf(stderr, "ntdb:%s:%s:%s\n",
+ ntdb_name(ntdb), ntdb_errorstr(ecode), message);
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j, users, groups;
+ NTDB_DATA idxkey, idxdata;
+ NTDB_DATA k, d, gk;
+ char cmd[100];
+ struct ntdb_context *ntdb;
+ enum NTDB_ERROR ecode;
+ union ntdb_attribute log;
+
+ if (argc != 3) {
+ printf("Usage: growtdb-bench <users> <groups>\n");
+ exit(1);
+ }
+ users = atoi(argv[1]);
+ groups = atoi(argv[2]);
+
+ sprintf(cmd, "cat /proc/%i/statm", getpid());
+
+ log.base.attr = NTDB_ATTRIBUTE_LOG;
+ log.base.next = NULL;
+ log.log.fn = logfn;
+
+ ntdb = ntdb_open("/tmp/growtdb.ntdb", NTDB_DEFAULT,
+ O_RDWR|O_CREAT|O_TRUNC, 0600, &log);
+
+ idxkey.dptr = (unsigned char *)"User index";
+ idxkey.dsize = strlen("User index");
+ idxdata.dsize = 51;
+ idxdata.dptr = calloc(idxdata.dsize, 1);
+
+ /* Create users. */
+ k.dsize = 48;
+ k.dptr = calloc(k.dsize, 1);
+ d.dsize = 64;
+ d.dptr = calloc(d.dsize, 1);
+
+ ntdb_transaction_start(ntdb);
+ for (i = 0; i < users; i++) {
+ memcpy(k.dptr, &i, sizeof(i));
+ ecode = ntdb_store(ntdb, k, d, NTDB_INSERT);
+ if (ecode != NTDB_SUCCESS)
+ errx(1, "ntdb insert failed: %s", ntdb_errorstr(ecode));
+
+ /* This simulates a growing index record. */
+ ecode = ntdb_append(ntdb, idxkey, idxdata);
+ if (ecode != NTDB_SUCCESS)
+ errx(1, "ntdb append failed: %s", ntdb_errorstr(ecode));
+ }
+ if ((ecode = ntdb_transaction_commit(ntdb)) != 0)
+ errx(1, "ntdb commit1 failed: %s", ntdb_errorstr(ecode));
+
+ if ((ecode = ntdb_check(ntdb, NULL, NULL)) != 0)
+ errx(1, "ntdb_check failed after initial insert!");
+
+ system(cmd);
+
+ /* Now put them all in groups: add 32 bytes to each record for
+ * a group. */
+ gk.dsize = 48;
+ gk.dptr = calloc(k.dsize, 1);
+ gk.dptr[gk.dsize-1] = 1;
+
+ d.dsize = 32;
+ for (i = 0; i < groups; i++) {
+ ntdb_transaction_start(ntdb);
+ /* Create the "group". */
+ memcpy(gk.dptr, &i, sizeof(i));
+ ecode = ntdb_store(ntdb, gk, d, NTDB_INSERT);
+ if (ecode != NTDB_SUCCESS)
+ errx(1, "ntdb insert failed: %s", ntdb_errorstr(ecode));
+
+ /* Now populate it. */
+ for (j = 0; j < users; j++) {
+ /* Append to the user. */
+ memcpy(k.dptr, &j, sizeof(j));
+ if ((ecode = ntdb_append(ntdb, k, d)) != 0)
+ errx(1, "ntdb append failed: %s",
+ ntdb_errorstr(ecode));
+
+ /* Append to the group. */
+ if ((ecode = ntdb_append(ntdb, gk, d)) != 0)
+ errx(1, "ntdb append failed: %s",
+ ntdb_errorstr(ecode));
+ }
+ if ((ecode = ntdb_transaction_commit(ntdb)) != 0)
+ errx(1, "ntdb commit2 failed: %s", ntdb_errorstr(ecode));
+ if ((ecode = ntdb_check(ntdb, NULL, NULL)) != 0)
+ errx(1, "ntdb_check failed after iteration %i!", i);
+ system(cmd);
+ }
+
+ return 0;
+}
diff --git a/lib/tdb2/tools/mktdb2.c b/lib/ntdb/tools/mkntdb.c
index 35d7a07d0b..e728987a53 100644
--- a/lib/tdb2/tools/mktdb2.c
+++ b/lib/ntdb/tools/mkntdb.c
@@ -1,4 +1,4 @@
-#include "tdb2.h"
+#include "ntdb.h"
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
@@ -7,21 +7,21 @@
int main(int argc, char *argv[])
{
unsigned int i, num_recs;
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
if (argc != 3 || (num_recs = atoi(argv[2])) == 0)
errx(1, "Usage: mktdb <tdbfile> <numrecords>");
- tdb = tdb_open(argv[1], TDB_DEFAULT, O_CREAT|O_TRUNC|O_RDWR, 0600,NULL);
- if (!tdb)
+ ntdb = ntdb_open(argv[1], NTDB_DEFAULT, O_CREAT|O_TRUNC|O_RDWR, 0600,NULL);
+ if (!ntdb)
err(1, "Opening %s", argv[1]);
for (i = 0; i < num_recs; i++) {
- TDB_DATA d;
+ NTDB_DATA d;
d.dptr = (void *)&i;
d.dsize = sizeof(i);
- if (tdb_store(tdb, d, d, TDB_INSERT) != 0)
+ if (ntdb_store(ntdb, d, d, NTDB_INSERT) != 0)
err(1, "Failed to store record %i", i);
}
printf("Done\n");
diff --git a/lib/tdb2/tools/tdb2backup.c b/lib/ntdb/tools/ntdbbackup.c
index 37b301c548..a76f18491b 100644
--- a/lib/tdb2/tools/tdb2backup.c
+++ b/lib/ntdb/tools/ntdbbackup.c
@@ -1,6 +1,6 @@
/*
Unix SMB/CIFS implementation.
- low level tdb backup and restore utility
+ low level ntdb backup and restore utility
Copyright (C) Andrew Tridgell 2002
This program is free software; you can redistribute it and/or modify
@@ -19,11 +19,11 @@
/*
- This program is meant for backup/restore of tdb databases. Typical usage would be:
- tdbbackup *.tdb
+ This program is meant for backup/restore of ntdb databases. Typical usage would be:
+ tdbbackup *.ntdb
when Samba shuts down cleanly, which will make a backup of all the local databases
to *.bak files. Then on Samba startup you would use:
- tdbbackup -v *.tdb
+ tdbbackup -v *.ntdb
and this will check the databases for corruption and if corruption is detected then
the backup will be restored.
@@ -41,7 +41,7 @@
*/
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include "system/filesys.h"
#ifdef HAVE_GETOPT_H
@@ -50,13 +50,13 @@
static int failed;
-static void tdb_log(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
+static void ntdb_log(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
const char *message,
void *data)
{
- fprintf(stderr, "%s:%s\n", tdb_errorstr(ecode), message);
+ fprintf(stderr, "%s:%s\n", ntdb_errorstr(ecode), message);
}
static char *add_suffix(const char *name, const char *suffix)
@@ -72,15 +72,15 @@ static char *add_suffix(const char *name, const char *suffix)
return ret;
}
-static int copy_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+static int copy_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
{
- struct tdb_context *tdb_new = (struct tdb_context *)state;
- enum TDB_ERROR err;
+ struct ntdb_context *ntdb_new = (struct ntdb_context *)state;
+ enum NTDB_ERROR err;
- err = tdb_store(tdb_new, key, dbuf, TDB_INSERT);
+ err = ntdb_store(ntdb_new, key, dbuf, NTDB_INSERT);
if (err) {
fprintf(stderr,"Failed to insert into %s: %s\n",
- tdb_name(tdb_new), tdb_errorstr(err));
+ ntdb_name(ntdb_new), ntdb_errorstr(err));
failed = 1;
return 1;
}
@@ -88,75 +88,75 @@ static int copy_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *s
}
-static int test_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+static int test_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
{
return 0;
}
/*
- carefully backup a tdb, validating the contents and
+ carefully backup a ntdb, validating the contents and
only doing the backup if its OK
this function is also used for restore
*/
-static int backup_tdb(const char *old_name, const char *new_name)
+static int backup_ntdb(const char *old_name, const char *new_name)
{
- struct tdb_context *tdb;
- struct tdb_context *tdb_new;
+ struct ntdb_context *ntdb;
+ struct ntdb_context *ntdb_new;
char *tmp_name;
struct stat st;
int count1, count2;
- enum TDB_ERROR err;
- union tdb_attribute log_attr;
+ enum NTDB_ERROR err;
+ union ntdb_attribute log_attr;
tmp_name = add_suffix(new_name, ".tmp");
- /* stat the old tdb to find its permissions */
+ /* stat the old ntdb to find its permissions */
if (stat(old_name, &st) != 0) {
perror(old_name);
free(tmp_name);
return 1;
}
- log_attr.base.attr = TDB_ATTRIBUTE_LOG;
+ log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
log_attr.base.next = NULL;
- log_attr.log.fn = tdb_log;
+ log_attr.log.fn = ntdb_log;
- /* open the old tdb */
- tdb = tdb_open(old_name, TDB_DEFAULT, O_RDWR, 0, &log_attr);
- if (!tdb) {
+ /* open the old ntdb */
+ ntdb = ntdb_open(old_name, NTDB_DEFAULT, O_RDWR, 0, &log_attr);
+ if (!ntdb) {
printf("Failed to open %s\n", old_name);
free(tmp_name);
return 1;
}
unlink(tmp_name);
- tdb_new = tdb_open(tmp_name, TDB_DEFAULT,
+ ntdb_new = ntdb_open(tmp_name, NTDB_DEFAULT,
O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777,
&log_attr);
- if (!tdb_new) {
+ if (!ntdb_new) {
perror(tmp_name);
free(tmp_name);
return 1;
}
- err = tdb_transaction_start(tdb);
+ err = ntdb_transaction_start(ntdb);
if (err) {
- fprintf(stderr, "Failed to start transaction on old tdb: %s\n",
- tdb_errorstr(err));
- tdb_close(tdb);
- tdb_close(tdb_new);
+ fprintf(stderr, "Failed to start transaction on old ntdb: %s\n",
+ ntdb_errorstr(err));
+ ntdb_close(ntdb);
+ ntdb_close(ntdb_new);
unlink(tmp_name);
free(tmp_name);
return 1;
}
- /* lock the backup tdb so that nobody else can change it */
- err = tdb_lockall(tdb_new);
+ /* lock the backup ntdb so that nobody else can change it */
+ err = ntdb_lockall(ntdb_new);
if (err) {
- fprintf(stderr, "Failed to lock backup tdb: %s\n",
- tdb_errorstr(err));
- tdb_close(tdb);
- tdb_close(tdb_new);
+ fprintf(stderr, "Failed to lock backup ntdb: %s\n",
+ ntdb_errorstr(err));
+ ntdb_close(ntdb);
+ ntdb_close(ntdb_new);
unlink(tmp_name);
free(tmp_name);
return 1;
@@ -165,39 +165,39 @@ static int backup_tdb(const char *old_name, const char *new_name)
failed = 0;
/* traverse and copy */
- count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
+ count1 = ntdb_traverse(ntdb, copy_fn, (void *)ntdb_new);
if (count1 < 0 || failed) {
fprintf(stderr,"failed to copy %s\n", old_name);
- tdb_close(tdb);
- tdb_close(tdb_new);
+ ntdb_close(ntdb);
+ ntdb_close(ntdb_new);
unlink(tmp_name);
free(tmp_name);
return 1;
}
- /* close the old tdb */
- tdb_close(tdb);
+ /* close the old ntdb */
+ ntdb_close(ntdb);
- /* copy done, unlock the backup tdb */
- tdb_unlockall(tdb_new);
+ /* copy done, unlock the backup ntdb */
+ ntdb_unlockall(ntdb_new);
#ifdef HAVE_FDATASYNC
- if (fdatasync(tdb_fd(tdb_new)) != 0) {
+ if (fdatasync(ntdb_fd(ntdb_new)) != 0) {
#else
- if (fsync(tdb_fd(tdb_new)) != 0) {
+ if (fsync(ntdb_fd(ntdb_new)) != 0) {
#endif
/* not fatal */
fprintf(stderr, "failed to fsync backup file\n");
}
- /* close the new tdb and re-open read-only */
- tdb_close(tdb_new);
+ /* close the new ntdb and re-open read-only */
+ ntdb_close(ntdb_new);
/* we don't need the hash attr any more */
log_attr.base.next = NULL;
- tdb_new = tdb_open(tmp_name, TDB_DEFAULT, O_RDONLY, 0, &log_attr);
- if (!tdb_new) {
+ ntdb_new = ntdb_open(tmp_name, NTDB_DEFAULT, O_RDONLY, 0, &log_attr);
+ if (!ntdb_new) {
fprintf(stderr,"failed to reopen %s\n", tmp_name);
unlink(tmp_name);
perror(tmp_name);
@@ -205,18 +205,18 @@ static int backup_tdb(const char *old_name, const char *new_name)
return 1;
}
- /* traverse the new tdb to confirm */
- count2 = tdb_traverse(tdb_new, test_fn, NULL);
+ /* traverse the new ntdb to confirm */
+ count2 = ntdb_traverse(ntdb_new, test_fn, NULL);
if (count2 != count1) {
fprintf(stderr,"failed to copy %s\n", old_name);
- tdb_close(tdb_new);
+ ntdb_close(ntdb_new);
unlink(tmp_name);
free(tmp_name);
return 1;
}
- /* close the new tdb and rename it to .bak */
- tdb_close(tdb_new);
+ /* close the new ntdb and rename it to .bak */
+ ntdb_close(ntdb_new);
if (rename(tmp_name, new_name) != 0) {
perror(new_name);
free(tmp_name);
@@ -229,31 +229,31 @@ static int backup_tdb(const char *old_name, const char *new_name)
}
/*
- verify a tdb and if it is corrupt then restore from *.bak
+ verify a ntdb and if it is corrupt then restore from *.bak
*/
-static int verify_tdb(const char *fname, const char *bak_name)
+static int verify_ntdb(const char *fname, const char *bak_name)
{
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
int count = -1;
- union tdb_attribute log_attr;
+ union ntdb_attribute log_attr;
- log_attr.base.attr = TDB_ATTRIBUTE_LOG;
+ log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
log_attr.base.next = NULL;
- log_attr.log.fn = tdb_log;
+ log_attr.log.fn = ntdb_log;
- /* open the tdb */
- tdb = tdb_open(fname, TDB_DEFAULT, O_RDONLY, 0, &log_attr);
+ /* open the ntdb */
+ ntdb = ntdb_open(fname, NTDB_DEFAULT, O_RDONLY, 0, &log_attr);
- /* traverse the tdb, then close it */
- if (tdb) {
- count = tdb_traverse(tdb, test_fn, NULL);
- tdb_close(tdb);
+ /* traverse the ntdb, then close it */
+ if (ntdb) {
+ count = ntdb_traverse(ntdb, test_fn, NULL);
+ ntdb_close(ntdb);
}
/* count is < 0 means an error */
if (count < 0) {
printf("restoring %s\n", fname);
- return backup_tdb(bak_name, fname);
+ return backup_ntdb(bak_name, fname);
}
printf("%s : %d records\n", fname, count);
@@ -278,7 +278,7 @@ static int file_newer(const char *fname1, const char *fname2)
static void usage(void)
{
- printf("Usage: tdb2backup [options] <fname...>\n\n");
+ printf("Usage: ntdbbackup [options] <fname...>\n\n");
printf(" -h this help message\n");
printf(" -v verify mode (restore if corrupt)\n");
printf(" -s suffix set the backup suffix\n");
@@ -323,12 +323,12 @@ static void usage(void)
bak_name = add_suffix(fname, suffix);
if (verify) {
- if (verify_tdb(fname, bak_name) != 0) {
+ if (verify_ntdb(fname, bak_name) != 0) {
ret = 1;
}
} else {
if (file_newer(fname, bak_name) &&
- backup_tdb(fname, bak_name) != 0) {
+ backup_ntdb(fname, bak_name) != 0) {
ret = 1;
}
}
diff --git a/lib/tdb2/tools/tdb2dump.c b/lib/ntdb/tools/ntdbdump.c
index 40230a2643..1b1c59eae3 100644
--- a/lib/tdb2/tools/tdb2dump.c
+++ b/lib/ntdb/tools/ntdbdump.c
@@ -1,5 +1,5 @@
/*
- simple tdb2 dump util
+ simple ntdb dump util
Copyright (C) Andrew Tridgell 2001
Copyright (C) Rusty Russell 2011
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#ifdef HAVE_LIBREPLACE
#include <replace.h>
#include <system/filesys.h>
@@ -32,7 +32,7 @@
#include <unistd.h>
#endif
-static void print_data(TDB_DATA d)
+static void print_data(NTDB_DATA d)
{
unsigned char *p = (unsigned char *)d.dptr;
int len = d.dsize;
@@ -46,7 +46,7 @@ static void print_data(TDB_DATA d)
}
}
-static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+static int traverse_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
{
printf("{\n");
printf("key(%d) = \"", (int)key.dsize);
@@ -59,22 +59,22 @@ static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, voi
return 0;
}
-static int dump_tdb(const char *fname, const char *keyname)
+static int dump_ntdb(const char *fname, const char *keyname)
{
- struct tdb_context *tdb;
- TDB_DATA key, value;
+ struct ntdb_context *ntdb;
+ NTDB_DATA key, value;
- tdb = tdb_open(fname, 0, O_RDONLY, 0, NULL);
- if (!tdb) {
+ ntdb = ntdb_open(fname, 0, O_RDONLY, 0, NULL);
+ if (!ntdb) {
printf("Failed to open %s\n", fname);
return 1;
}
if (!keyname) {
- tdb_traverse(tdb, traverse_fn, NULL);
+ ntdb_traverse(ntdb, traverse_fn, NULL);
} else {
- key = tdb_mkdata(keyname, strlen(keyname));
- if (tdb_fetch(tdb, key, &value) != 0) {
+ key = ntdb_mkdata(keyname, strlen(keyname));
+ if (ntdb_fetch(ntdb, key, &value) != 0) {
return 1;
} else {
print_data(value);
@@ -87,7 +87,7 @@ static int dump_tdb(const char *fname, const char *keyname)
static void usage( void)
{
- printf( "Usage: tdb2dump [options] <filename>\n\n");
+ printf( "Usage: ntdbdump [options] <filename>\n\n");
printf( " -h this help message\n");
printf( " -k keyname dumps value of keyname\n");
}
@@ -98,7 +98,7 @@ static void usage( void)
int c;
if (argc < 2) {
- printf("Usage: tdb2dump <fname>\n");
+ printf("Usage: ntdbdump <fname>\n");
exit(1);
}
@@ -118,5 +118,5 @@ static void usage( void)
fname = argv[optind];
- return dump_tdb(fname, keyname);
+ return dump_ntdb(fname, keyname);
}
diff --git a/lib/tdb2/tools/tdb2restore.c b/lib/ntdb/tools/ntdbrestore.c
index 93c6c8bfe5..dad591d562 100644
--- a/lib/tdb2/tools/tdb2restore.c
+++ b/lib/ntdb/tools/ntdbrestore.c
@@ -1,5 +1,5 @@
/*
- tdb2restore -- construct a tdb from tdbdump output.
+ ntdbrestore -- construct a ntdb from tdbdump output.
Copyright (C) Volker Lendecke 2010
Copyright (C) Simon McVittie 2005
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include <assert.h>
#ifdef HAVE_LIBREPLACE
#include <replace.h>
@@ -88,7 +88,7 @@ static int read_hex(void) {
}
}
-static int read_data(FILE *f, struct tdb_data *d, size_t size) {
+static int read_data(FILE *f, NTDB_DATA *d, size_t size) {
int c, low, high;
int i;
@@ -141,12 +141,12 @@ static int swallow(FILE *f, const char *s, int *eof)
return 0;
}
-static bool read_rec(FILE *f, struct tdb_context *tdb, int *eof)
+static bool read_rec(FILE *f, struct ntdb_context *ntdb, int *eof)
{
int length;
- struct tdb_data key, data;
+ NTDB_DATA key, data;
bool ret = false;
- enum TDB_ERROR e;
+ enum NTDB_ERROR e;
key.dptr = NULL;
data.dptr = NULL;
@@ -175,9 +175,9 @@ static bool read_rec(FILE *f, struct tdb_context *tdb, int *eof)
|| (swallow(f, "}\n", NULL) == -1)) {
goto fail;
}
- e = tdb_store(tdb, key, data, TDB_INSERT);
- if (e != TDB_SUCCESS) {
- fprintf(stderr, "TDB error: %s\n", tdb_errorstr(e));
+ e = ntdb_store(ntdb, key, data, NTDB_INSERT);
+ if (e != NTDB_SUCCESS) {
+ fprintf(stderr, "NTDB error: %s\n", ntdb_errorstr(e));
goto fail;
}
@@ -188,28 +188,28 @@ fail:
return ret;
}
-static int restore_tdb(const char *fname)
+static int restore_ntdb(const char *fname)
{
- struct tdb_context *tdb;
+ struct ntdb_context *ntdb;
- tdb = tdb_open(fname, 0, O_RDWR|O_CREAT|O_EXCL, 0666, NULL);
- if (!tdb) {
- perror("tdb_open");
+ ntdb = ntdb_open(fname, 0, O_RDWR|O_CREAT|O_EXCL, 0666, NULL);
+ if (!ntdb) {
+ perror("ntdb_open");
fprintf(stderr, "Failed to open %s\n", fname);
return 1;
}
while (1) {
int eof = 0;
- if (!read_rec(stdin, tdb, &eof)) {
+ if (!read_rec(stdin, ntdb, &eof)) {
if (eof) {
break;
}
return 1;
}
}
- if (tdb_close(tdb)) {
- fprintf(stderr, "Error closing tdb\n");
+ if (ntdb_close(ntdb)) {
+ fprintf(stderr, "Error closing ntdb\n");
return 1;
}
fprintf(stderr, "EOF\n");
@@ -227,5 +227,5 @@ int main(int argc, char *argv[])
fname = argv[1];
- return restore_tdb(fname);
+ return restore_ntdb(fname);
}
diff --git a/lib/tdb2/tools/tdb2tool.c b/lib/ntdb/tools/ntdbtool.c
index ae20971143..7c1ef7df7a 100644
--- a/lib/tdb2/tools/tdb2tool.c
+++ b/lib/ntdb/tools/ntdbtool.c
@@ -21,7 +21,7 @@
*/
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#ifdef HAVE_LIBREPLACE
#include <replace.h>
#include <system/filesys.h>
@@ -46,13 +46,13 @@ char *arg1, *arg2;
size_t arg1len, arg2len;
int bIterate = 0;
char *line;
-TDB_DATA iterate_kbuf;
+NTDB_DATA iterate_kbuf;
char cmdline[1024];
static int disable_mmap;
enum commands {
- CMD_CREATE_TDB,
- CMD_OPEN_TDB,
+ CMD_CREATE_NTDB,
+ CMD_OPEN_NTDB,
CMD_TRANSACTION_START,
CMD_TRANSACTION_COMMIT,
CMD_TRANSACTION_CANCEL,
@@ -86,8 +86,8 @@ typedef struct {
} COMMAND_TABLE;
COMMAND_TABLE cmd_table[] = {
- {"create", CMD_CREATE_TDB},
- {"open", CMD_OPEN_TDB},
+ {"create", CMD_CREATE_NTDB},
+ {"open", CMD_OPEN_NTDB},
#if 0
{"transaction_start", CMD_TRANSACTION_START},
{"transaction_commit", CMD_TRANSACTION_COMMIT},
@@ -134,23 +134,23 @@ static double _end_timer(void)
(tp2.tv_usec - tp1.tv_usec)*1.0e-6);
}
-static void tdb_log(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
+static void ntdb_log(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
const char *message,
void *data)
{
- fprintf(stderr, "tdb:%s:%s:%s\n",
- tdb_name(tdb), tdb_errorstr(ecode), message);
+ fprintf(stderr, "ntdb:%s:%s:%s\n",
+ ntdb_name(ntdb), ntdb_errorstr(ecode), message);
}
-/* a tdb tool for manipulating a tdb database */
+/* a ntdb tool for manipulating a ntdb database */
-static struct tdb_context *tdb;
+static struct ntdb_context *ntdb;
-static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
-static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
-static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+static int print_rec(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
+static int print_key(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
+static int print_hexkey(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
static void print_asc(const char *buf,int len)
{
@@ -214,7 +214,7 @@ static void help(void)
" 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"
+" move key file : move a record to a destination ntdb\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"
@@ -232,51 +232,51 @@ static void help(void)
"\n");
}
-static void terror(enum TDB_ERROR err, const char *why)
+static void terror(enum NTDB_ERROR err, const char *why)
{
- if (err != TDB_SUCCESS)
- printf("%s:%s\n", tdb_errorstr(err), why);
+ if (err != NTDB_SUCCESS)
+ printf("%s:%s\n", ntdb_errorstr(err), why);
else
printf("%s\n", why);
}
-static void create_tdb(const char *tdbname)
+static void create_ntdb(const char *tdbname)
{
- union tdb_attribute log_attr;
- log_attr.base.attr = TDB_ATTRIBUTE_LOG;
+ union ntdb_attribute log_attr;
+ log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
log_attr.base.next = NULL;
- log_attr.log.fn = tdb_log;
+ log_attr.log.fn = ntdb_log;
- if (tdb) tdb_close(tdb);
- tdb = tdb_open(tdbname, (disable_mmap?TDB_NOMMAP:0),
+ if (ntdb) ntdb_close(ntdb);
+ ntdb = ntdb_open(tdbname, (disable_mmap?NTDB_NOMMAP:0),
O_RDWR | O_CREAT | O_TRUNC, 0600, &log_attr);
- if (!tdb) {
+ if (!ntdb) {
printf("Could not create %s: %s\n", tdbname, strerror(errno));
}
}
-static void open_tdb(const char *tdbname)
+static void open_ntdb(const char *tdbname)
{
- union tdb_attribute log_attr;
- log_attr.base.attr = TDB_ATTRIBUTE_LOG;
+ union ntdb_attribute log_attr;
+ log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
log_attr.base.next = NULL;
- log_attr.log.fn = tdb_log;
+ log_attr.log.fn = ntdb_log;
- if (tdb) tdb_close(tdb);
- tdb = tdb_open(tdbname, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600,
+ if (ntdb) ntdb_close(ntdb);
+ ntdb = ntdb_open(tdbname, disable_mmap?NTDB_NOMMAP:0, O_RDWR, 0600,
&log_attr);
- if (!tdb) {
+ if (!ntdb) {
printf("Could not open %s: %s\n", tdbname, strerror(errno));
}
}
-static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
+static void insert_ntdb(char *keyname, size_t keylen, char* data, size_t datalen)
{
- TDB_DATA key, dbuf;
- enum TDB_ERROR ecode;
+ NTDB_DATA key, dbuf;
+ enum NTDB_ERROR ecode;
if ((keyname == NULL) || (keylen == 0)) {
- terror(TDB_SUCCESS, "need key");
+ terror(NTDB_SUCCESS, "need key");
return;
}
@@ -285,24 +285,24 @@ static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
dbuf.dptr = (unsigned char *)data;
dbuf.dsize = datalen;
- ecode = tdb_store(tdb, key, dbuf, TDB_INSERT);
+ ecode = ntdb_store(ntdb, key, dbuf, NTDB_INSERT);
if (ecode) {
terror(ecode, "insert failed");
}
}
-static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
+static void store_ntdb(char *keyname, size_t keylen, char* data, size_t datalen)
{
- TDB_DATA key, dbuf;
- enum TDB_ERROR ecode;
+ NTDB_DATA key, dbuf;
+ enum NTDB_ERROR ecode;
if ((keyname == NULL) || (keylen == 0)) {
- terror(TDB_SUCCESS, "need key");
+ terror(NTDB_SUCCESS, "need key");
return;
}
if ((data == NULL) || (datalen == 0)) {
- terror(TDB_SUCCESS, "need data");
+ terror(NTDB_SUCCESS, "need data");
return;
}
@@ -312,52 +312,52 @@ static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
dbuf.dsize = datalen;
printf("Storing key:\n");
- print_rec(tdb, key, dbuf, NULL);
+ print_rec(ntdb, key, dbuf, NULL);
- ecode = tdb_store(tdb, key, dbuf, TDB_REPLACE);
+ ecode = ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
if (ecode) {
terror(ecode, "store failed");
}
}
-static void show_tdb(char *keyname, size_t keylen)
+static void show_ntdb(char *keyname, size_t keylen)
{
- TDB_DATA key, dbuf;
- enum TDB_ERROR ecode;
+ NTDB_DATA key, dbuf;
+ enum NTDB_ERROR ecode;
if ((keyname == NULL) || (keylen == 0)) {
- terror(TDB_SUCCESS, "need key");
+ terror(NTDB_SUCCESS, "need key");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
- ecode = tdb_fetch(tdb, key, &dbuf);
+ ecode = ntdb_fetch(ntdb, key, &dbuf);
if (ecode) {
terror(ecode, "fetch failed");
return;
}
- print_rec(tdb, key, dbuf, NULL);
+ print_rec(ntdb, key, dbuf, NULL);
free( dbuf.dptr );
}
-static void delete_tdb(char *keyname, size_t keylen)
+static void delete_ntdb(char *keyname, size_t keylen)
{
- TDB_DATA key;
- enum TDB_ERROR ecode;
+ NTDB_DATA key;
+ enum NTDB_ERROR ecode;
if ((keyname == NULL) || (keylen == 0)) {
- terror(TDB_SUCCESS, "need key");
+ terror(NTDB_SUCCESS, "need key");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
- ecode = tdb_delete(tdb, key);
+ ecode = ntdb_delete(ntdb, key);
if (ecode) {
terror(ecode, "delete failed");
}
@@ -365,47 +365,47 @@ static void delete_tdb(char *keyname, size_t keylen)
static void move_rec(char *keyname, size_t keylen, char* tdbname)
{
- TDB_DATA key, dbuf;
- struct tdb_context *dst_tdb;
- enum TDB_ERROR ecode;
+ NTDB_DATA key, dbuf;
+ struct ntdb_context *dst_ntdb;
+ enum NTDB_ERROR ecode;
if ((keyname == NULL) || (keylen == 0)) {
- terror(TDB_SUCCESS, "need key");
+ terror(NTDB_SUCCESS, "need key");
return;
}
if ( !tdbname ) {
- terror(TDB_SUCCESS, "need destination tdb name");
+ terror(NTDB_SUCCESS, "need destination ntdb name");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
- ecode = tdb_fetch(tdb, key, &dbuf);
+ ecode = ntdb_fetch(ntdb, key, &dbuf);
if (ecode) {
terror(ecode, "fetch failed");
return;
}
- print_rec(tdb, key, dbuf, NULL);
+ print_rec(ntdb, key, dbuf, NULL);
- dst_tdb = tdb_open(tdbname, 0, O_RDWR, 0600, NULL);
- if ( !dst_tdb ) {
- terror(TDB_SUCCESS, "unable to open destination tdb");
+ dst_ntdb = ntdb_open(tdbname, 0, O_RDWR, 0600, NULL);
+ if ( !dst_ntdb ) {
+ terror(NTDB_SUCCESS, "unable to open destination ntdb");
return;
}
- ecode = tdb_store( dst_tdb, key, dbuf, TDB_REPLACE);
+ ecode = ntdb_store( dst_ntdb, key, dbuf, NTDB_REPLACE);
if (ecode)
terror(ecode, "failed to move record");
else
printf("record moved\n");
- tdb_close( dst_tdb );
+ ntdb_close( dst_ntdb );
}
-static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+static int print_rec(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
{
printf("\nkey %d bytes\n", (int)key.dsize);
print_asc((const char *)key.dptr, key.dsize);
@@ -414,7 +414,7 @@ static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, v
return 0;
}
-static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+static int print_key(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
{
printf("key %d bytes: ", (int)key.dsize);
print_asc((const char *)key.dptr, key.dsize);
@@ -422,7 +422,7 @@ static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, v
return 0;
}
-static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+static int print_hexkey(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
{
printf("key %d bytes\n", (int)key.dsize);
print_data((const char *)key.dptr, key.dsize);
@@ -432,18 +432,18 @@ static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf
static int total_bytes;
-static int traverse_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+static int traverse_fn(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
{
total_bytes += dbuf.dsize;
return 0;
}
-static void info_tdb(void)
+static void info_ntdb(void)
{
- enum TDB_ERROR ecode;
+ enum NTDB_ERROR ecode;
char *summary;
- ecode = tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &summary);
+ ecode = ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &summary);
if (ecode) {
terror(ecode, "Getting summary");
@@ -453,7 +453,7 @@ static void info_tdb(void)
}
}
-static void speed_tdb(const char *tlimit)
+static void speed_ntdb(const char *tlimit)
{
unsigned timelimit = tlimit?atoi(tlimit):0;
double t;
@@ -465,11 +465,11 @@ static void speed_tdb(const char *tlimit)
_start_timer();
do {
long int r = random();
- TDB_DATA key, dbuf;
- key = tdb_mkdata("store test", strlen("store test"));
+ NTDB_DATA key, dbuf;
+ key = ntdb_mkdata("store test", strlen("store test"));
dbuf.dptr = (unsigned char *)&r;
dbuf.dsize = sizeof(r);
- tdb_store(tdb, key, dbuf, TDB_REPLACE);
+ ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
t = _end_timer();
ops++;
} while (t < timelimit);
@@ -480,11 +480,11 @@ static void speed_tdb(const char *tlimit)
_start_timer();
do {
long int r = random();
- TDB_DATA key, dbuf;
- key = tdb_mkdata("store test", strlen("store test"));
+ NTDB_DATA key, dbuf;
+ key = ntdb_mkdata("store test", strlen("store test"));
dbuf.dptr = (unsigned char *)&r;
dbuf.dsize = sizeof(r);
- tdb_fetch(tdb, key, &dbuf);
+ ntdb_fetch(ntdb, key, &dbuf);
t = _end_timer();
ops++;
} while (t < timelimit);
@@ -495,13 +495,13 @@ static void speed_tdb(const char *tlimit)
_start_timer();
do {
long int r = random();
- TDB_DATA key, dbuf;
- key = tdb_mkdata("transaction test", strlen("transaction test"));
+ NTDB_DATA key, dbuf;
+ key = ntdb_mkdata("transaction test", strlen("transaction test"));
dbuf.dptr = (unsigned char *)&r;
dbuf.dsize = sizeof(r);
- tdb_transaction_start(tdb);
- tdb_store(tdb, key, dbuf, TDB_REPLACE);
- tdb_transaction_commit(tdb);
+ ntdb_transaction_start(ntdb);
+ ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
+ ntdb_transaction_commit(ntdb);
t = _end_timer();
ops++;
} while (t < timelimit);
@@ -511,7 +511,7 @@ static void speed_tdb(const char *tlimit)
printf("Testing traverse speed for %u seconds\n", timelimit);
_start_timer();
do {
- tdb_traverse(tdb, traverse_fn, NULL);
+ ntdb_traverse(ntdb, traverse_fn, NULL);
t = _end_timer();
ops++;
} while (t < timelimit);
@@ -528,7 +528,7 @@ static void toggle_mmap(void)
}
}
-static char *tdb_getline(const char *prompt)
+static char *ntdb_getline(const char *prompt)
{
static char thisline[1024];
char *p;
@@ -540,45 +540,45 @@ static char *tdb_getline(const char *prompt)
return p?thisline:NULL;
}
-static int do_delete_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+static int do_delete_fn(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf,
void *state)
{
- return tdb_delete(the_tdb, key);
+ return ntdb_delete(the_ntdb, key);
}
-static void first_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
+static void first_record(struct ntdb_context *the_ntdb, NTDB_DATA *pkey)
{
- TDB_DATA dbuf;
- enum TDB_ERROR ecode;
- ecode = tdb_firstkey(the_tdb, pkey);
+ NTDB_DATA dbuf;
+ enum NTDB_ERROR ecode;
+ ecode = ntdb_firstkey(the_ntdb, pkey);
if (!ecode)
- ecode = tdb_fetch(the_tdb, *pkey, &dbuf);
+ ecode = ntdb_fetch(the_ntdb, *pkey, &dbuf);
if (ecode) terror(ecode, "fetch failed");
else {
- print_rec(the_tdb, *pkey, dbuf, NULL);
+ print_rec(the_ntdb, *pkey, dbuf, NULL);
}
}
-static void next_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
+static void next_record(struct ntdb_context *the_ntdb, NTDB_DATA *pkey)
{
- TDB_DATA dbuf;
- enum TDB_ERROR ecode;
- ecode = tdb_nextkey(the_tdb, pkey);
+ NTDB_DATA dbuf;
+ enum NTDB_ERROR ecode;
+ ecode = ntdb_nextkey(the_ntdb, pkey);
if (!ecode)
- ecode = tdb_fetch(the_tdb, *pkey, &dbuf);
+ ecode = ntdb_fetch(the_ntdb, *pkey, &dbuf);
if (ecode)
terror(ecode, "fetch failed");
else
- print_rec(the_tdb, *pkey, dbuf, NULL);
+ print_rec(the_ntdb, *pkey, dbuf, NULL);
}
-static void check_db(struct tdb_context *the_tdb)
+static void check_db(struct ntdb_context *the_ntdb)
{
- if (!the_tdb) {
+ if (!the_ntdb) {
printf("Error: No database opened!\n");
} else {
- if (tdb_check(the_tdb, NULL, NULL) != 0)
+ if (ntdb_check(the_ntdb, NULL, NULL) != 0)
printf("Integrity check for the opened database failed.\n");
else
printf("Database integrity is OK.\n");
@@ -605,54 +605,54 @@ static int do_command(void)
}
switch (mycmd) {
- case CMD_CREATE_TDB:
+ case CMD_CREATE_NTDB:
bIterate = 0;
- create_tdb(arg1);
+ create_ntdb(arg1);
return 0;
- case CMD_OPEN_TDB:
+ case CMD_OPEN_NTDB:
bIterate = 0;
- open_tdb(arg1);
+ open_ntdb(arg1);
return 0;
case CMD_SYSTEM:
/* Shell command */
if (system(arg1) == -1) {
- terror(TDB_SUCCESS, "system() call failed\n");
+ terror(NTDB_SUCCESS, "system() call failed\n");
}
return 0;
case CMD_QUIT:
return 1;
default:
/* all the rest require a open database */
- if (!tdb) {
+ if (!ntdb) {
bIterate = 0;
- terror(TDB_SUCCESS, "database not open");
+ terror(NTDB_SUCCESS, "database not open");
help();
return 0;
}
switch (mycmd) {
case CMD_TRANSACTION_START:
bIterate = 0;
- tdb_transaction_start(tdb);
+ ntdb_transaction_start(ntdb);
return 0;
case CMD_TRANSACTION_COMMIT:
bIterate = 0;
- tdb_transaction_commit(tdb);
+ ntdb_transaction_commit(ntdb);
return 0;
case CMD_TRANSACTION_CANCEL:
bIterate = 0;
- tdb_transaction_cancel(tdb);
+ ntdb_transaction_cancel(ntdb);
return 0;
case CMD_ERASE:
bIterate = 0;
- tdb_traverse(tdb, do_delete_fn, NULL);
+ ntdb_traverse(ntdb, do_delete_fn, NULL);
return 0;
case CMD_DUMP:
bIterate = 0;
- tdb_traverse(tdb, print_rec, NULL);
+ ntdb_traverse(ntdb, print_rec, NULL);
return 0;
case CMD_INSERT:
bIterate = 0;
- insert_tdb(arg1, arg1len,arg2,arg2len);
+ insert_ntdb(arg1, arg1len,arg2,arg2len);
return 0;
case CMD_MOVE:
bIterate = 0;
@@ -660,55 +660,55 @@ static int do_command(void)
return 0;
case CMD_STORE:
bIterate = 0;
- store_tdb(arg1,arg1len,arg2,arg2len);
+ store_ntdb(arg1,arg1len,arg2,arg2len);
return 0;
case CMD_SHOW:
bIterate = 0;
- show_tdb(arg1, arg1len);
+ show_ntdb(arg1, arg1len);
return 0;
case CMD_KEYS:
- tdb_traverse(tdb, print_key, NULL);
+ ntdb_traverse(ntdb, print_key, NULL);
return 0;
case CMD_HEXKEYS:
- tdb_traverse(tdb, print_hexkey, NULL);
+ ntdb_traverse(ntdb, print_hexkey, NULL);
return 0;
case CMD_DELETE:
bIterate = 0;
- delete_tdb(arg1,arg1len);
+ delete_ntdb(arg1,arg1len);
return 0;
#if 0
case CMD_LIST_HASH_FREE:
- tdb_dump_all(tdb);
+ ntdb_dump_all(ntdb);
return 0;
case CMD_LIST_FREE:
- tdb_printfreelist(tdb);
+ ntdb_printfreelist(ntdb);
return 0;
#endif
case CMD_INFO:
- info_tdb();
+ info_ntdb();
return 0;
case CMD_SPEED:
- speed_tdb(arg1);
+ speed_ntdb(arg1);
return 0;
case CMD_MMAP:
toggle_mmap();
return 0;
case CMD_FIRST:
bIterate = 1;
- first_record(tdb, &iterate_kbuf);
+ first_record(ntdb, &iterate_kbuf);
return 0;
case CMD_NEXT:
if (bIterate)
- next_record(tdb, &iterate_kbuf);
+ next_record(ntdb, &iterate_kbuf);
return 0;
case CMD_CHECK:
- check_db(tdb);
+ check_db(ntdb);
return 0;
case CMD_HELP:
help();
return 0;
- case CMD_CREATE_TDB:
- case CMD_OPEN_TDB:
+ case CMD_CREATE_NTDB:
+ case CMD_OPEN_NTDB:
case CMD_SYSTEM:
case CMD_QUIT:
/*
@@ -773,7 +773,7 @@ int main(int argc, char *argv[])
case 1:
case 2:
/* Interactive mode */
- while ((cmdname = tdb_getline("tdb> "))) {
+ while ((cmdname = ntdb_getline("ntdb> "))) {
arg2 = arg1 = NULL;
if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
arg1++;
@@ -804,7 +804,7 @@ int main(int argc, char *argv[])
break;
}
- if (tdb) tdb_close(tdb);
+ if (ntdb) ntdb_close(ntdb);
return 0;
}
diff --git a/lib/tdb2/tools/tdb2torture.c b/lib/ntdb/tools/ntdbtorture.c
index 73e2e29874..c7b249db06 100644
--- a/lib/tdb2/tools/tdb2torture.c
+++ b/lib/ntdb/tools/ntdbtorture.c
@@ -1,9 +1,9 @@
-/* this tests tdb by doing lots of ops from several simultaneous
+/* this tests ntdb by doing lots of ops from several simultaneous
writers - that stresses the locking code.
*/
#include "config.h"
-#include "tdb2.h"
+#include "ntdb.h"
#include <ccan/err/err.h>
#ifdef HAVE_LIBREPLACE
#include <replace.h>
@@ -35,7 +35,7 @@
#define KEYLEN 3
#define DATALEN 100
-static struct tdb_context *db;
+static struct ntdb_context *db;
static int in_transaction;
static int in_traverse;
static int error_count;
@@ -44,17 +44,17 @@ static int always_transaction = 0;
#endif
static int loopnum;
static int count_pipe;
-static union tdb_attribute log_attr;
-static union tdb_attribute seed_attr;
+static union ntdb_attribute log_attr;
+static union ntdb_attribute seed_attr;
-static void tdb_log(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
+static void ntdb_log(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
const char *message,
void *data)
{
- printf("tdb:%s:%s:%s\n",
- tdb_name(tdb), tdb_errorstr(ecode), message);
+ printf("ntdb:%s:%s:%s\n",
+ ntdb_name(ntdb), ntdb_errorstr(ecode), message);
fflush(stdout);
#if 0
{
@@ -80,10 +80,10 @@ static void segv_handler(int sig, siginfo_t *info, void *p)
_exit(11);
}
-static void fatal(struct tdb_context *tdb, const char *why)
+static void fatal(struct ntdb_context *ntdb, const char *why)
{
fprintf(stderr, "%u:%s:%s\n", getpid(), why,
- tdb ? tdb_errorstr(tdb_error(tdb)) : "(no tdb)");
+ ntdb ? ntdb_errorstr(ntdb_error(ntdb)) : "(no ntdb)");
error_count++;
}
@@ -101,12 +101,12 @@ static char *randbuf(int len)
}
static void addrec_db(void);
-static int modify_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
+static int modify_traverse(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
void *state)
{
#if CULL_PROB
if (random() % CULL_PROB == 0) {
- tdb_delete(tdb, key);
+ ntdb_delete(ntdb, key);
}
#endif
@@ -128,7 +128,7 @@ static void addrec_db(void)
{
int klen, dlen;
char *k, *d;
- TDB_DATA key, data;
+ NTDB_DATA key, data;
klen = 1 + (rand() % KEYLEN);
dlen = 1 + (rand() % DATALEN);
@@ -144,34 +144,34 @@ static void addrec_db(void)
#if REOPEN_PROB
if (in_traverse == 0 && in_transaction == 0 && random() % REOPEN_PROB == 0) {
- tdb_reopen_all(0);
+ ntdb_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(db, "tdb_transaction_start failed");
+ if (ntdb_transaction_start(db) != 0) {
+ fatal(db, "ntdb_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(db, "tdb_transaction_prepare_commit failed");
+ if (ntdb_transaction_prepare_commit(db) != 0) {
+ fatal(db, "ntdb_transaction_prepare_commit failed");
}
}
- if (tdb_transaction_commit(db) != 0) {
- fatal(db, "tdb_transaction_commit failed");
+ if (ntdb_transaction_commit(db) != 0) {
+ fatal(db, "ntdb_transaction_commit failed");
}
in_transaction--;
goto next;
}
if (in_traverse == 0 && in_transaction && random() % TRANSACTION_PROB == 0) {
- tdb_transaction_cancel(db);
+ ntdb_transaction_cancel(db);
in_transaction--;
goto next;
}
@@ -179,15 +179,15 @@ static void addrec_db(void)
#if DELETE_PROB
if (random() % DELETE_PROB == 0) {
- tdb_delete(db, key);
+ ntdb_delete(db, key);
goto next;
}
#endif
#if STORE_PROB
if (random() % STORE_PROB == 0) {
- if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
- fatal(db, "tdb_store failed");
+ if (ntdb_store(db, key, data, NTDB_REPLACE) != 0) {
+ fatal(db, "ntdb_store failed");
}
goto next;
}
@@ -195,8 +195,8 @@ static void addrec_db(void)
#if APPEND_PROB
if (random() % APPEND_PROB == 0) {
- if (tdb_append(db, key, data) != 0) {
- fatal(db, "tdb_append failed");
+ if (ntdb_append(db, key, data) != 0) {
+ fatal(db, "ntdb_append failed");
}
goto next;
}
@@ -204,16 +204,16 @@ static void addrec_db(void)
#if LOCKSTORE_PROB
if (random() % LOCKSTORE_PROB == 0) {
- tdb_chainlock(db, key);
- if (tdb_fetch(db, key, &data) != TDB_SUCCESS) {
+ ntdb_chainlock(db, key);
+ if (ntdb_fetch(db, key, &data) != NTDB_SUCCESS) {
data.dsize = 0;
data.dptr = NULL;
}
- if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
- fatal(db, "tdb_store failed");
+ if (ntdb_store(db, key, data, NTDB_REPLACE) != 0) {
+ fatal(db, "ntdb_store failed");
}
if (data.dptr) free(data.dptr);
- tdb_chainunlock(db, key);
+ ntdb_chainunlock(db, key);
goto next;
}
#endif
@@ -222,13 +222,13 @@ static void addrec_db(void)
/* FIXME: recursive traverses break transactions? */
if (in_traverse == 0 && random() % TRAVERSE_PROB == 0) {
in_traverse++;
- tdb_traverse(db, modify_traverse, NULL);
+ ntdb_traverse(db, modify_traverse, NULL);
in_traverse--;
goto next;
}
#endif
- if (tdb_fetch(db, key, &data) == TDB_SUCCESS)
+ if (ntdb_fetch(db, key, &data) == NTDB_SUCCESS)
free(data.dptr);
next:
@@ -236,16 +236,16 @@ next:
free(d);
}
-static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
+static int traverse_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
void *state)
{
- tdb_delete(tdb, key);
+ ntdb_delete(ntdb, key);
return 0;
}
static void usage(void)
{
- printf("Usage: tdb2torture"
+ printf("Usage: ntdbtorture"
#if TRANSACTION_PROB
" [-t]"
#endif
@@ -263,13 +263,13 @@ static void send_count_and_suicide(int sig)
}
static int run_child(const char *filename, int i, int seed, unsigned num_loops,
- unsigned start, int tdb_flags)
+ unsigned start, int ntdb_flags)
{
struct sigaction act = { .sa_sigaction = segv_handler,
.sa_flags = SA_SIGINFO };
sigaction(11, &act, NULL);
- db = tdb_open(filename, tdb_flags, O_RDWR | O_CREAT, 0600,
+ db = ntdb_open(filename, ntdb_flags, O_RDWR | O_CREAT, 0600,
&log_attr);
if (!db) {
fatal(NULL, "db open failed");
@@ -295,29 +295,29 @@ static int run_child(const char *filename, int i, int seed, unsigned num_loops,
}
if (error_count == 0) {
- tdb_traverse(db, NULL, NULL);
+ ntdb_traverse(db, NULL, NULL);
#if TRANSACTION_PROB
if (always_transaction) {
while (in_transaction) {
- tdb_transaction_cancel(db);
+ ntdb_transaction_cancel(db);
in_transaction--;
}
- if (tdb_transaction_start(db) != 0)
- fatal(db, "tdb_transaction_start failed");
+ if (ntdb_transaction_start(db) != 0)
+ fatal(db, "ntdb_transaction_start failed");
}
#endif
- tdb_traverse(db, traverse_fn, NULL);
- tdb_traverse(db, traverse_fn, NULL);
+ ntdb_traverse(db, traverse_fn, NULL);
+ ntdb_traverse(db, traverse_fn, NULL);
#if TRANSACTION_PROB
if (always_transaction) {
- if (tdb_transaction_commit(db) != 0)
- fatal(db, "tdb_transaction_commit failed");
+ if (ntdb_transaction_commit(db) != 0)
+ fatal(db, "ntdb_transaction_commit failed");
}
#endif
}
- tdb_close(db);
+ ntdb_close(db);
return (error_count < 100 ? error_count : 100);
}
@@ -350,13 +350,13 @@ int main(int argc, char * const *argv)
pid_t *pids;
int kill_random = 0;
int *done;
- int tdb_flags = TDB_DEFAULT;
- char *test_tdb;
+ int ntdb_flags = NTDB_DEFAULT;
+ char *test_ntdb;
- log_attr.base.attr = TDB_ATTRIBUTE_LOG;
+ log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
log_attr.base.next = &seed_attr;
- log_attr.log.fn = tdb_log;
- seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
+ log_attr.log.fn = ntdb_log;
+ seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
seed_attr.base.next = NULL;
while ((c = getopt(argc, argv, "n:l:s:thkS")) != -1) {
@@ -371,7 +371,7 @@ int main(int argc, char * const *argv)
seed = strtol(optarg, NULL, 0);
break;
case 'S':
- tdb_flags = TDB_NOSYNC;
+ ntdb_flags = NTDB_NOSYNC;
break;
case 't':
#if TRANSACTION_PROB
@@ -389,9 +389,9 @@ int main(int argc, char * const *argv)
}
}
- test_tdb = test_path("torture.tdb2");
+ test_ntdb = test_path("torture.ntdb");
- unlink(test_tdb);
+ unlink(test_ntdb);
if (seed == -1) {
seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
@@ -400,8 +400,8 @@ int main(int argc, char * const *argv)
if (num_procs == 1 && !kill_random) {
/* Don't fork for this case, makes debugging easier. */
- error_count = run_child(test_tdb, 0, seed, num_loops, 0,
- tdb_flags);
+ error_count = run_child(test_ntdb, 0, seed, num_loops, 0,
+ ntdb_flags);
goto done;
}
@@ -427,8 +427,8 @@ int main(int argc, char * const *argv)
#endif
);
}
- exit(run_child(test_tdb, i, seed, num_loops, 0,
- tdb_flags));
+ exit(run_child(test_ntdb, i, seed, num_loops, 0,
+ ntdb_flags));
}
}
@@ -484,9 +484,9 @@ int main(int argc, char * const *argv)
}
pids[j] = fork();
if (pids[j] == 0)
- exit(run_child(test_tdb, j, seed,
+ exit(run_child(test_ntdb, j, seed,
num_loops, done[j],
- tdb_flags));
+ ntdb_flags));
printf("Restarting child %i for %u-%u\n",
j, done[j], num_loops);
continue;
@@ -510,20 +510,20 @@ int main(int argc, char * const *argv)
done:
if (error_count == 0) {
- db = tdb_open(test_tdb, TDB_DEFAULT, O_RDWR | O_CREAT,
+ db = ntdb_open(test_ntdb, NTDB_DEFAULT, O_RDWR | O_CREAT,
0600, &log_attr);
if (!db) {
fatal(db, "db open failed");
exit(1);
}
- if (tdb_check(db, NULL, NULL) != 0) {
+ if (ntdb_check(db, NULL, NULL) != 0) {
fatal(db, "db check failed");
exit(1);
}
- tdb_close(db);
+ ntdb_close(db);
printf("OK\n");
}
- free(test_tdb);
+ free(test_ntdb);
return error_count;
}
diff --git a/lib/tdb2/tools/speed.c b/lib/ntdb/tools/speed.c
index 259d53f6c8..868494b898 100644
--- a/lib/tdb2/tools/speed.c
+++ b/lib/ntdb/tools/speed.c
@@ -1,4 +1,4 @@
-/* Simple speed test for TDB */
+/* Simple speed test for NTDB */
#include <ccan/err/err.h>
#include <time.h>
#include <sys/types.h>
@@ -10,7 +10,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
-#include "tdb2.h"
+#include "ntdb.h"
/* Nanoseconds per operation */
static size_t normalize(const struct timeval *start,
@@ -30,31 +30,31 @@ static size_t file_size(void)
{
struct stat st;
- if (stat("/tmp/speed.tdb", &st) != 0)
+ if (stat("/tmp/speed.ntdb", &st) != 0)
return -1;
return st.st_size;
}
-static int count_record(struct tdb_context *tdb,
- TDB_DATA key, TDB_DATA data, void *p)
+static int count_record(struct ntdb_context *ntdb,
+ NTDB_DATA key, NTDB_DATA data, void *p)
{
int *total = p;
*total += *(int *)data.dptr;
return 0;
}
-static void dump_and_clear_stats(struct tdb_context **tdb,
+static void dump_and_clear_stats(struct ntdb_context **ntdb,
int flags,
- union tdb_attribute *attr)
+ union ntdb_attribute *attr)
{
- union tdb_attribute stats;
- enum TDB_ERROR ecode;
+ union ntdb_attribute stats;
+ enum NTDB_ERROR ecode;
- stats.base.attr = TDB_ATTRIBUTE_STATS;
+ stats.base.attr = NTDB_ATTRIBUTE_STATS;
stats.stats.size = sizeof(stats.stats);
- ecode = tdb_get_attribute(*tdb, &stats);
- if (ecode != TDB_SUCCESS)
- errx(1, "Getting stats: %s", tdb_errorstr(ecode));
+ ecode = ntdb_get_attribute(*ntdb, &stats);
+ if (ecode != NTDB_SUCCESS)
+ errx(1, "Getting stats: %s", ntdb_errorstr(ecode));
printf("allocs = %llu\n",
(unsigned long long)stats.stats.allocs);
@@ -122,43 +122,43 @@ static void dump_and_clear_stats(struct tdb_context **tdb,
(unsigned long long)stats.stats.lock_nonblock_fail);
/* Now clear. */
- tdb_close(*tdb);
- *tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR, 0, attr);
+ ntdb_close(*ntdb);
+ *ntdb = ntdb_open("/tmp/speed.ntdb", flags, O_RDWR, 0, attr);
}
-static void tdb_log(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
+static void ntdb_log(struct ntdb_context *ntdb,
+ enum ntdb_log_level level,
+ enum NTDB_ERROR ecode,
const char *message,
void *data)
{
- fprintf(stderr, "tdb:%s:%s:%s\n",
- tdb_name(tdb), tdb_errorstr(ecode), message);
+ fprintf(stderr, "ntdb:%s:%s:%s\n",
+ ntdb_name(ntdb), ntdb_errorstr(ecode), message);
}
int main(int argc, char *argv[])
{
unsigned int i, j, num = 1000, stage = 0, stopat = -1;
- int flags = TDB_DEFAULT;
+ int flags = NTDB_DEFAULT;
bool transaction = false, summary = false;
- TDB_DATA key, data;
- struct tdb_context *tdb;
+ NTDB_DATA key, data;
+ struct ntdb_context *ntdb;
struct timeval start, stop;
- union tdb_attribute seed, log;
+ union ntdb_attribute seed, log;
bool do_stats = false;
- enum TDB_ERROR ecode;
+ enum NTDB_ERROR ecode;
/* Try to keep benchmarks even. */
- seed.base.attr = TDB_ATTRIBUTE_SEED;
+ seed.base.attr = NTDB_ATTRIBUTE_SEED;
seed.base.next = NULL;
seed.seed.seed = 0;
- log.base.attr = TDB_ATTRIBUTE_LOG;
+ log.base.attr = NTDB_ATTRIBUTE_LOG;
log.base.next = &seed;
- log.log.fn = tdb_log;
+ log.log.fn = ntdb_log;
if (argv[1] && strcmp(argv[1], "--internal") == 0) {
- flags = TDB_INTERNAL;
+ flags = NTDB_INTERNAL;
argc--;
argv++;
}
@@ -168,7 +168,7 @@ int main(int argc, char *argv[])
argv++;
}
if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
- flags |= TDB_NOSYNC;
+ flags |= NTDB_NOSYNC;
argc--;
argv++;
}
@@ -183,10 +183,10 @@ int main(int argc, char *argv[])
argv++;
}
- tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
+ ntdb = ntdb_open("/tmp/speed.ntdb", flags, O_RDWR|O_CREAT|O_TRUNC,
0600, &log);
- if (!tdb)
- err(1, "Opening /tmp/speed.tdb");
+ if (!ntdb)
+ err(1, "Opening /tmp/speed.ntdb");
key.dptr = (void *)&i;
key.dsize = sizeof(i);
@@ -206,199 +206,199 @@ int main(int argc, char *argv[])
/* Add 1000 records. */
printf("Adding %u records: ", num); fflush(stdout);
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
gettimeofday(&start, NULL);
for (i = 0; i < num; i++)
- if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
- errx(1, "Inserting key %u in tdb: %s",
- i, tdb_errorstr(ecode));
+ if ((ecode = ntdb_store(ntdb, key, data, NTDB_INSERT)) != 0)
+ errx(1, "Inserting key %u in ntdb: %s",
+ i, ntdb_errorstr(ecode));
gettimeofday(&stop, NULL);
- if (transaction && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
if (do_stats)
- dump_and_clear_stats(&tdb, flags, &log);
+ dump_and_clear_stats(&ntdb, flags, &log);
if (++stage == stopat)
exit(0);
/* Finding 1000 records. */
printf("Finding %u records: ", num); fflush(stdout);
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
gettimeofday(&start, NULL);
for (i = 0; i < num; i++) {
- struct tdb_data dbuf;
- if ((ecode = tdb_fetch(tdb, key, &dbuf)) != TDB_SUCCESS
+ NTDB_DATA dbuf;
+ if ((ecode = ntdb_fetch(ntdb, key, &dbuf)) != NTDB_SUCCESS
|| *(int *)dbuf.dptr != i) {
- errx(1, "Fetching key %u in tdb gave %u",
+ errx(1, "Fetching key %u in ntdb gave %u",
i, ecode ? ecode : *(int *)dbuf.dptr);
}
}
gettimeofday(&stop, NULL);
- if (transaction && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
if (do_stats)
- dump_and_clear_stats(&tdb, flags, &log);
+ dump_and_clear_stats(&ntdb, flags, &log);
if (++stage == stopat)
exit(0);
/* Missing 1000 records. */
printf("Missing %u records: ", num); fflush(stdout);
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
gettimeofday(&start, NULL);
for (i = num; i < num*2; i++) {
- struct tdb_data dbuf;
- ecode = tdb_fetch(tdb, key, &dbuf);
- if (ecode != TDB_ERR_NOEXIST)
- errx(1, "Fetching key %u in tdb gave %s",
- i, tdb_errorstr(ecode));
+ NTDB_DATA dbuf;
+ ecode = ntdb_fetch(ntdb, key, &dbuf);
+ if (ecode != NTDB_ERR_NOEXIST)
+ errx(1, "Fetching key %u in ntdb gave %s",
+ i, ntdb_errorstr(ecode));
}
gettimeofday(&stop, NULL);
- if (transaction && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
if (do_stats)
- dump_and_clear_stats(&tdb, flags, &log);
+ dump_and_clear_stats(&ntdb, flags, &log);
if (++stage == stopat)
exit(0);
/* Traverse 1000 records. */
printf("Traversing %u records: ", num); fflush(stdout);
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
i = 0;
gettimeofday(&start, NULL);
- if (tdb_traverse(tdb, count_record, &i) != num)
+ if (ntdb_traverse(ntdb, 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 && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
if (do_stats)
- dump_and_clear_stats(&tdb, flags, &log);
+ dump_and_clear_stats(&ntdb, flags, &log);
if (++stage == stopat)
exit(0);
/* Delete 1000 records (not in order). */
printf("Deleting %u records: ", num); fflush(stdout);
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
gettimeofday(&start, NULL);
for (j = 0; j < num; j++) {
i = (j + 100003) % num;
- if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
- errx(1, "Deleting key %u in tdb: %s",
- i, tdb_errorstr(ecode));
+ if ((ecode = ntdb_delete(ntdb, key)) != NTDB_SUCCESS)
+ errx(1, "Deleting key %u in ntdb: %s",
+ i, ntdb_errorstr(ecode));
}
gettimeofday(&stop, NULL);
- if (transaction && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
if (do_stats)
- dump_and_clear_stats(&tdb, flags, &log);
+ dump_and_clear_stats(&ntdb, flags, &log);
if (++stage == stopat)
exit(0);
/* Re-add 1000 records (not in order). */
printf("Re-adding %u records: ", num); fflush(stdout);
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
gettimeofday(&start, NULL);
for (j = 0; j < num; j++) {
i = (j + 100003) % num;
- if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
- errx(1, "Inserting key %u in tdb: %s",
- i, tdb_errorstr(ecode));
+ if ((ecode = ntdb_store(ntdb, key, data, NTDB_INSERT)) != 0)
+ errx(1, "Inserting key %u in ntdb: %s",
+ i, ntdb_errorstr(ecode));
}
gettimeofday(&stop, NULL);
- if (transaction && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
if (do_stats)
- dump_and_clear_stats(&tdb, flags, &log);
+ dump_and_clear_stats(&ntdb, flags, &log);
if (++stage == stopat)
exit(0);
/* Append 1000 records. */
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
printf("Appending %u records: ", num); fflush(stdout);
gettimeofday(&start, NULL);
for (i = 0; i < num; i++)
- if ((ecode = tdb_append(tdb, key, data)) != TDB_SUCCESS)
- errx(1, "Appending key %u in tdb: %s",
- i, tdb_errorstr(ecode));
+ if ((ecode = ntdb_append(ntdb, key, data)) != NTDB_SUCCESS)
+ errx(1, "Appending key %u in ntdb: %s",
+ i, ntdb_errorstr(ecode));
gettimeofday(&stop, NULL);
- if (transaction && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
@@ -406,36 +406,36 @@ int main(int argc, char *argv[])
exit(0);
/* Churn 1000 records: not in order! */
- if (transaction && (ecode = tdb_transaction_start(tdb)))
- errx(1, "starting transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_start(ntdb)))
+ errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
printf("Churning %u records: ", num); fflush(stdout);
gettimeofday(&start, NULL);
for (j = 0; j < num; j++) {
i = (j + 1000019) % num;
- if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
- errx(1, "Deleting key %u in tdb: %s",
- i, tdb_errorstr(ecode));
+ if ((ecode = ntdb_delete(ntdb, key)) != NTDB_SUCCESS)
+ errx(1, "Deleting key %u in ntdb: %s",
+ i, ntdb_errorstr(ecode));
i += num;
- if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
- errx(1, "Inserting key %u in tdb: %s",
- i, tdb_errorstr(ecode));
+ if ((ecode = ntdb_store(ntdb, key, data, NTDB_INSERT)) != 0)
+ errx(1, "Inserting key %u in ntdb: %s",
+ i, ntdb_errorstr(ecode));
}
gettimeofday(&stop, NULL);
- if (transaction && (ecode = tdb_transaction_commit(tdb)))
- errx(1, "committing transaction: %s", tdb_errorstr(ecode));
+ if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
+ errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
printf(" %zu ns (%zu bytes)\n",
normalize(&start, &stop, num), file_size());
- if (tdb_check(tdb, NULL, NULL))
- errx(1, "tdb_check failed!");
+ if (ntdb_check(ntdb, NULL, NULL))
+ errx(1, "ntdb_check failed!");
if (summary) {
char *sumstr = NULL;
- tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
+ ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
printf("%s\n", sumstr);
free(sumstr);
}
if (do_stats)
- dump_and_clear_stats(&tdb, flags, &log);
+ dump_and_clear_stats(&ntdb, flags, &log);
if (++stage == stopat)
exit(0);
diff --git a/lib/ntdb/transaction.c b/lib/ntdb/transaction.c
new file mode 100644
index 0000000000..76408c3022
--- /dev/null
+++ b/lib/ntdb/transaction.c
@@ -0,0 +1,1322 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ trivial database library
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Rusty Russell 2010
+
+ ** NOTE! The following LGPL license applies to the ntdb
+ ** 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 "private.h"
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((void *)x); (x)=NULL;} } while(0)
+
+/*
+ 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
+ ntdb_free() the old record to place it on the normal ntdb freelist
+ before allocating the new record
+
+ - during transactions, keep a linked list of writes all that have
+ been performed by intercepting all ntdb_write() calls. The hooked
+ transaction versions of ntdb_read() and ntdb_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 ntdb_transaction_start(), re-using the
+ existing transaction record. If the inner transaction is canceled
+ then a subsequent commit will fail
+
+ - keep a mirrored copy of the ntdb hash chain heads to allow for the
+ fast hash heads scan on traverse, updating the mirrored copy in
+ the transaction version of ntdb_write
+
+ - allow callers to mix transaction and non-transaction use of ntdb,
+ although once a transaction is started then an exclusive lock is
+ gained until the transaction is committed or canceled
+
+ - 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 ntdb, 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 NTDB_NOSYNC is passed to flags in ntdb_open then transactions are
+ still available, but no transaction recovery area is used and no
+ fsync/msync calls are made.
+*/
+
+/*
+ hold the context of any current transaction
+*/
+struct ntdb_transaction {
+ /* the original io methods - used to do IOs to the real db */
+ const struct ntdb_methods *io_methods;
+
+ /* the list of transaction blocks. When a block is first
+ written to, it gets created in this list */
+ uint8_t **blocks;
+ size_t num_blocks;
+ size_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 ntdb_transaction_start() calls, as these are allowed,
+ but don't create a new transaction */
+ unsigned int nesting;
+
+ /* set when a prepare has already occurred */
+ bool prepared;
+ ntdb_off_t magic_offset;
+
+ /* old file size before transaction */
+ ntdb_len_t old_map_size;
+};
+
+/* This doesn't really need to be pagesize, but we use it for similar reasons. */
+#define PAGESIZE 65536
+
+/*
+ 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 enum NTDB_ERROR transaction_read(struct ntdb_context *ntdb, ntdb_off_t off,
+ void *buf, ntdb_len_t len)
+{
+ size_t blk;
+ enum NTDB_ERROR ecode;
+
+ /* break it down into block sized ops */
+ while (len + (off % PAGESIZE) > PAGESIZE) {
+ ntdb_len_t len2 = PAGESIZE - (off % PAGESIZE);
+ ecode = transaction_read(ntdb, off, buf, len2);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ len -= len2;
+ off += len2;
+ buf = (void *)(len2 + (char *)buf);
+ }
+
+ if (len == 0) {
+ return NTDB_SUCCESS;
+ }
+
+ blk = off / PAGESIZE;
+
+ /* see if we have it in the block list */
+ if (ntdb->transaction->num_blocks <= blk ||
+ ntdb->transaction->blocks[blk] == NULL) {
+ /* nope, do a real read */
+ ecode = ntdb->transaction->io_methods->tread(ntdb, off, buf, len);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail;
+ }
+ return 0;
+ }
+
+ /* it is in the block list. Now check for the last block */
+ if (blk == ntdb->transaction->num_blocks-1) {
+ if (len > ntdb->transaction->last_block_size) {
+ ecode = NTDB_ERR_IO;
+ goto fail;
+ }
+ }
+
+ /* now copy it out of this block */
+ memcpy(buf, ntdb->transaction->blocks[blk] + (off % PAGESIZE), len);
+ return NTDB_SUCCESS;
+
+fail:
+ ntdb->transaction->transaction_error = 1;
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "transaction_read: failed at off=%zu len=%zu",
+ (size_t)off, (size_t)len);
+}
+
+
+/*
+ write while in a transaction
+*/
+static enum NTDB_ERROR transaction_write(struct ntdb_context *ntdb, ntdb_off_t off,
+ const void *buf, ntdb_len_t len)
+{
+ size_t blk;
+ enum NTDB_ERROR ecode;
+
+ /* Only a commit is allowed on a prepared transaction */
+ if (ntdb->transaction->prepared) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_ERROR,
+ "transaction_write: transaction already"
+ " prepared, write not allowed");
+ goto fail;
+ }
+
+ /* break it up into block sized chunks */
+ while (len + (off % PAGESIZE) > PAGESIZE) {
+ ntdb_len_t len2 = PAGESIZE - (off % PAGESIZE);
+ ecode = transaction_write(ntdb, off, buf, len2);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ len -= len2;
+ off += len2;
+ if (buf != NULL) {
+ buf = (const void *)(len2 + (const char *)buf);
+ }
+ }
+
+ if (len == 0) {
+ return NTDB_SUCCESS;
+ }
+
+ blk = off / PAGESIZE;
+ off = off % PAGESIZE;
+
+ if (ntdb->transaction->num_blocks <= blk) {
+ uint8_t **new_blocks;
+ /* expand the blocks array */
+ if (ntdb->transaction->blocks == NULL) {
+ new_blocks = (uint8_t **)malloc(
+ (blk+1)*sizeof(uint8_t *));
+ } else {
+ new_blocks = (uint8_t **)realloc(
+ ntdb->transaction->blocks,
+ (blk+1)*sizeof(uint8_t *));
+ }
+ if (new_blocks == NULL) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "transaction_write:"
+ " failed to allocate");
+ goto fail;
+ }
+ memset(&new_blocks[ntdb->transaction->num_blocks], 0,
+ (1+(blk - ntdb->transaction->num_blocks))*sizeof(uint8_t *));
+ ntdb->transaction->blocks = new_blocks;
+ ntdb->transaction->num_blocks = blk+1;
+ ntdb->transaction->last_block_size = 0;
+ }
+
+ /* allocate and fill a block? */
+ if (ntdb->transaction->blocks[blk] == NULL) {
+ ntdb->transaction->blocks[blk] = (uint8_t *)calloc(PAGESIZE, 1);
+ if (ntdb->transaction->blocks[blk] == NULL) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "transaction_write:"
+ " failed to allocate");
+ goto fail;
+ }
+ if (ntdb->transaction->old_map_size > blk * PAGESIZE) {
+ ntdb_len_t len2 = PAGESIZE;
+ if (len2 + (blk * PAGESIZE) > ntdb->transaction->old_map_size) {
+ len2 = ntdb->transaction->old_map_size - (blk * PAGESIZE);
+ }
+ ecode = ntdb->transaction->io_methods->tread(ntdb,
+ blk * PAGESIZE,
+ ntdb->transaction->blocks[blk],
+ len2);
+ if (ecode != NTDB_SUCCESS) {
+ ecode = ntdb_logerr(ntdb, ecode,
+ NTDB_LOG_ERROR,
+ "transaction_write:"
+ " failed to"
+ " read old block: %s",
+ strerror(errno));
+ SAFE_FREE(ntdb->transaction->blocks[blk]);
+ goto fail;
+ }
+ if (blk == ntdb->transaction->num_blocks-1) {
+ ntdb->transaction->last_block_size = len2;
+ }
+ }
+ }
+
+ /* overwrite part of an existing block */
+ if (buf == NULL) {
+ memset(ntdb->transaction->blocks[blk] + off, 0, len);
+ } else {
+ memcpy(ntdb->transaction->blocks[blk] + off, buf, len);
+ }
+ if (blk == ntdb->transaction->num_blocks-1) {
+ if (len + off > ntdb->transaction->last_block_size) {
+ ntdb->transaction->last_block_size = len + off;
+ }
+ }
+
+ return NTDB_SUCCESS;
+
+fail:
+ ntdb->transaction->transaction_error = 1;
+ return ecode;
+}
+
+
+/*
+ write while in a transaction - this variant never expands the transaction blocks, it only
+ updates existing blocks. This means it cannot change the recovery size
+*/
+static void transaction_write_existing(struct ntdb_context *ntdb, ntdb_off_t off,
+ const void *buf, ntdb_len_t len)
+{
+ size_t blk;
+
+ /* break it up into block sized chunks */
+ while (len + (off % PAGESIZE) > PAGESIZE) {
+ ntdb_len_t len2 = PAGESIZE - (off % PAGESIZE);
+ transaction_write_existing(ntdb, off, buf, len2);
+ len -= len2;
+ off += len2;
+ if (buf != NULL) {
+ buf = (const void *)(len2 + (const char *)buf);
+ }
+ }
+
+ if (len == 0) {
+ return;
+ }
+
+ blk = off / PAGESIZE;
+ off = off % PAGESIZE;
+
+ if (ntdb->transaction->num_blocks <= blk ||
+ ntdb->transaction->blocks[blk] == NULL) {
+ return;
+ }
+
+ if (blk == ntdb->transaction->num_blocks-1 &&
+ off + len > ntdb->transaction->last_block_size) {
+ if (off >= ntdb->transaction->last_block_size) {
+ return;
+ }
+ len = ntdb->transaction->last_block_size - off;
+ }
+
+ /* overwrite part of an existing block */
+ memcpy(ntdb->transaction->blocks[blk] + off, buf, len);
+}
+
+
+/*
+ out of bounds check during a transaction
+*/
+static enum NTDB_ERROR transaction_oob(struct ntdb_context *ntdb,
+ ntdb_off_t off, ntdb_len_t len, bool probe)
+{
+ if ((off + len >= off && off + len <= ntdb->file->map_size) || probe) {
+ return NTDB_SUCCESS;
+ }
+
+ ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_oob len %lld beyond transaction size %lld",
+ (long long)(off + len),
+ (long long)ntdb->file->map_size);
+ return NTDB_ERR_IO;
+}
+
+/*
+ transaction version of ntdb_expand().
+*/
+static enum NTDB_ERROR transaction_expand_file(struct ntdb_context *ntdb,
+ ntdb_off_t addition)
+{
+ enum NTDB_ERROR ecode;
+
+ /* add a write to the transaction elements, so subsequent
+ reads see the zero data */
+ ecode = transaction_write(ntdb, ntdb->file->map_size, NULL, addition);
+ if (ecode == NTDB_SUCCESS) {
+ ntdb->file->map_size += addition;
+ }
+ return ecode;
+}
+
+static void *transaction_direct(struct ntdb_context *ntdb, ntdb_off_t off,
+ size_t len, bool write_mode)
+{
+ size_t blk = off / PAGESIZE, end_blk;
+
+ /* This is wrong for zero-length blocks, but will fail gracefully */
+ end_blk = (off + len - 1) / PAGESIZE;
+
+ /* Can only do direct if in single block and we've already copied. */
+ if (write_mode) {
+ ntdb->stats.transaction_write_direct++;
+ if (blk != end_blk
+ || blk >= ntdb->transaction->num_blocks
+ || ntdb->transaction->blocks[blk] == NULL) {
+ ntdb->stats.transaction_write_direct_fail++;
+ return NULL;
+ }
+ return ntdb->transaction->blocks[blk] + off % PAGESIZE;
+ }
+
+ ntdb->stats.transaction_read_direct++;
+ /* Single which we have copied? */
+ if (blk == end_blk
+ && blk < ntdb->transaction->num_blocks
+ && ntdb->transaction->blocks[blk])
+ return ntdb->transaction->blocks[blk] + off % PAGESIZE;
+
+ /* Otherwise must be all not copied. */
+ while (blk <= end_blk) {
+ if (blk >= ntdb->transaction->num_blocks)
+ break;
+ if (ntdb->transaction->blocks[blk]) {
+ ntdb->stats.transaction_read_direct_fail++;
+ return NULL;
+ }
+ blk++;
+ }
+ return ntdb->transaction->io_methods->direct(ntdb, off, len, false);
+}
+
+static const struct ntdb_methods transaction_methods = {
+ transaction_read,
+ transaction_write,
+ transaction_oob,
+ transaction_expand_file,
+ transaction_direct,
+};
+
+/*
+ sync to disk
+*/
+static enum NTDB_ERROR transaction_sync(struct ntdb_context *ntdb,
+ ntdb_off_t offset, ntdb_len_t length)
+{
+ if (ntdb->flags & NTDB_NOSYNC) {
+ return NTDB_SUCCESS;
+ }
+
+ if (fsync(ntdb->file->fd) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_transaction: fsync failed: %s",
+ strerror(errno));
+ }
+#ifdef MS_SYNC
+ if (ntdb->file->map_ptr) {
+ ntdb_off_t moffset = offset & ~(getpagesize()-1);
+ if (msync(moffset + (char *)ntdb->file->map_ptr,
+ length + (offset - moffset), MS_SYNC) != 0) {
+ return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
+ "ntdb_transaction: msync failed: %s",
+ strerror(errno));
+ }
+ }
+#endif
+ return NTDB_SUCCESS;
+}
+
+
+static void _ntdb_transaction_cancel(struct ntdb_context *ntdb)
+{
+ int i;
+ enum NTDB_ERROR ecode;
+
+ if (ntdb->transaction == NULL) {
+ ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_cancel: no transaction");
+ return;
+ }
+
+ if (ntdb->transaction->nesting != 0) {
+ ntdb->transaction->transaction_error = 1;
+ ntdb->transaction->nesting--;
+ return;
+ }
+
+ ntdb->file->map_size = ntdb->transaction->old_map_size;
+
+ /* free all the transaction blocks */
+ for (i=0;i<ntdb->transaction->num_blocks;i++) {
+ if (ntdb->transaction->blocks[i] != NULL) {
+ free(ntdb->transaction->blocks[i]);
+ }
+ }
+ SAFE_FREE(ntdb->transaction->blocks);
+
+ if (ntdb->transaction->magic_offset) {
+ const struct ntdb_methods *methods = ntdb->transaction->io_methods;
+ uint64_t invalid = NTDB_RECOVERY_INVALID_MAGIC;
+
+ /* remove the recovery marker */
+ ecode = methods->twrite(ntdb, ntdb->transaction->magic_offset,
+ &invalid, sizeof(invalid));
+ if (ecode == NTDB_SUCCESS)
+ ecode = transaction_sync(ntdb,
+ ntdb->transaction->magic_offset,
+ sizeof(invalid));
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_cancel: failed to remove"
+ " recovery magic");
+ }
+ }
+
+ if (ntdb->file->allrecord_lock.count)
+ ntdb_allrecord_unlock(ntdb, ntdb->file->allrecord_lock.ltype);
+
+ /* restore the normal io methods */
+ ntdb->io = ntdb->transaction->io_methods;
+
+ ntdb_transaction_unlock(ntdb, F_WRLCK);
+
+ if (ntdb_has_open_lock(ntdb))
+ ntdb_unlock_open(ntdb, F_WRLCK);
+
+ SAFE_FREE(ntdb->transaction);
+}
+
+/*
+ start a ntdb transaction. No token is returned, as only a single
+ transaction is allowed to be pending per ntdb_context
+*/
+_PUBLIC_ enum NTDB_ERROR ntdb_transaction_start(struct ntdb_context *ntdb)
+{
+ enum NTDB_ERROR ecode;
+
+ ntdb->stats.transactions++;
+ /* some sanity checks */
+ if (ntdb->flags & NTDB_INTERNAL) {
+ return ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_start:"
+ " cannot start a"
+ " transaction on an"
+ " internal ntdb");
+ }
+
+ if (ntdb->flags & NTDB_RDONLY) {
+ return ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_RDONLY,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_start:"
+ " cannot start a"
+ " transaction on a "
+ " read-only ntdb");
+ }
+
+ /* cope with nested ntdb_transaction_start() calls */
+ if (ntdb->transaction != NULL) {
+ if (!(ntdb->flags & NTDB_ALLOW_NESTING)) {
+ return ntdb->last_error
+ = ntdb_logerr(ntdb, NTDB_ERR_IO,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_start:"
+ " already inside transaction");
+ }
+ ntdb->transaction->nesting++;
+ ntdb->stats.transaction_nest++;
+ return 0;
+ }
+
+ if (ntdb_has_hash_locks(ntdb)) {
+ /* the caller must not have any locks when starting a
+ transaction as otherwise we'll be screwed by lack
+ of nested locks in POSIX */
+ return ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_LOCK,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_start:"
+ " cannot start a"
+ " transaction with locks"
+ " held");
+ }
+
+ ntdb->transaction = (struct ntdb_transaction *)
+ calloc(sizeof(struct ntdb_transaction), 1);
+ if (ntdb->transaction == NULL) {
+ return ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_OOM,
+ NTDB_LOG_ERROR,
+ "ntdb_transaction_start:"
+ " cannot allocate");
+ }
+
+ /* 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 */
+ ecode = ntdb_transaction_lock(ntdb, F_WRLCK);
+ if (ecode != NTDB_SUCCESS) {
+ SAFE_FREE(ntdb->transaction->blocks);
+ SAFE_FREE(ntdb->transaction);
+ return ntdb->last_error = ecode;
+ }
+
+ /* get a read lock over entire file. This is upgraded to a write
+ lock during the commit */
+ ecode = ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, true);
+ if (ecode != NTDB_SUCCESS) {
+ goto fail_allrecord_lock;
+ }
+
+ /* make sure we know about any file expansions already done by
+ anyone else */
+ ntdb->io->oob(ntdb, ntdb->file->map_size, 1, true);
+ ntdb->transaction->old_map_size = ntdb->file->map_size;
+
+ /* finally hook the io methods, replacing them with
+ transaction specific methods */
+ ntdb->transaction->io_methods = ntdb->io;
+ ntdb->io = &transaction_methods;
+ return ntdb->last_error = NTDB_SUCCESS;
+
+fail_allrecord_lock:
+ ntdb_transaction_unlock(ntdb, F_WRLCK);
+ SAFE_FREE(ntdb->transaction->blocks);
+ SAFE_FREE(ntdb->transaction);
+ return ntdb->last_error = ecode;
+}
+
+
+/*
+ cancel the current transaction
+*/
+_PUBLIC_ void ntdb_transaction_cancel(struct ntdb_context *ntdb)
+{
+ ntdb->stats.transaction_cancel++;
+ _ntdb_transaction_cancel(ntdb);
+}
+
+/*
+ work out how much space the linearised recovery data will consume (worst case)
+*/
+static ntdb_len_t ntdb_recovery_size(struct ntdb_context *ntdb)
+{
+ ntdb_len_t recovery_size = 0;
+ int i;
+
+ recovery_size = 0;
+ for (i=0;i<ntdb->transaction->num_blocks;i++) {
+ if (i * PAGESIZE >= ntdb->transaction->old_map_size) {
+ break;
+ }
+ if (ntdb->transaction->blocks[i] == NULL) {
+ continue;
+ }
+ recovery_size += 2*sizeof(ntdb_off_t);
+ if (i == ntdb->transaction->num_blocks-1) {
+ recovery_size += ntdb->transaction->last_block_size;
+ } else {
+ recovery_size += PAGESIZE;
+ }
+ }
+
+ return recovery_size;
+}
+
+static enum NTDB_ERROR ntdb_recovery_area(struct ntdb_context *ntdb,
+ const struct ntdb_methods *methods,
+ ntdb_off_t *recovery_offset,
+ struct ntdb_recovery_record *rec)
+{
+ enum NTDB_ERROR ecode;
+
+ *recovery_offset = ntdb_read_off(ntdb,
+ offsetof(struct ntdb_header, recovery));
+ if (NTDB_OFF_IS_ERR(*recovery_offset)) {
+ return NTDB_OFF_TO_ERR(*recovery_offset);
+ }
+
+ if (*recovery_offset == 0) {
+ rec->max_len = 0;
+ return NTDB_SUCCESS;
+ }
+
+ ecode = methods->tread(ntdb, *recovery_offset, rec, sizeof(*rec));
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+
+ ntdb_convert(ntdb, rec, sizeof(*rec));
+ /* ignore invalid recovery regions: can happen in crash */
+ if (rec->magic != NTDB_RECOVERY_MAGIC &&
+ rec->magic != NTDB_RECOVERY_INVALID_MAGIC) {
+ *recovery_offset = 0;
+ rec->max_len = 0;
+ }
+ return NTDB_SUCCESS;
+}
+
+static unsigned int same(const unsigned char *new,
+ const unsigned char *old,
+ unsigned int length)
+{
+ unsigned int i;
+
+ for (i = 0; i < length; i++) {
+ if (new[i] != old[i])
+ break;
+ }
+ return i;
+}
+
+static unsigned int different(const unsigned char *new,
+ const unsigned char *old,
+ unsigned int length,
+ unsigned int min_same,
+ unsigned int *samelen)
+{
+ unsigned int i;
+
+ *samelen = 0;
+ for (i = 0; i < length; i++) {
+ if (new[i] == old[i]) {
+ (*samelen)++;
+ } else {
+ if (*samelen >= min_same) {
+ return i - *samelen;
+ }
+ *samelen = 0;
+ }
+ }
+
+ if (*samelen < min_same)
+ *samelen = 0;
+ return length - *samelen;
+}
+
+/* Allocates recovery blob, without ntdb_recovery_record at head set up. */
+static struct ntdb_recovery_record *alloc_recovery(struct ntdb_context *ntdb,
+ ntdb_len_t *len)
+{
+ struct ntdb_recovery_record *rec;
+ size_t i;
+ enum NTDB_ERROR ecode;
+ unsigned char *p;
+ const struct ntdb_methods *old_methods = ntdb->io;
+
+ rec = malloc(sizeof(*rec) + ntdb_recovery_size(ntdb));
+ if (!rec) {
+ ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "transaction_setup_recovery:"
+ " cannot allocate");
+ return NTDB_ERR_PTR(NTDB_ERR_OOM);
+ }
+
+ /* We temporarily revert to the old I/O methods, so we can use
+ * ntdb_access_read */
+ ntdb->io = ntdb->transaction->io_methods;
+
+ /* build the recovery data into a single blob to allow us to do a single
+ large write, which should be more efficient */
+ p = (unsigned char *)(rec + 1);
+ for (i=0;i<ntdb->transaction->num_blocks;i++) {
+ ntdb_off_t offset;
+ ntdb_len_t length;
+ unsigned int off;
+ const unsigned char *buffer;
+
+ if (ntdb->transaction->blocks[i] == NULL) {
+ continue;
+ }
+
+ offset = i * PAGESIZE;
+ length = PAGESIZE;
+ if (i == ntdb->transaction->num_blocks-1) {
+ length = ntdb->transaction->last_block_size;
+ }
+
+ if (offset >= ntdb->transaction->old_map_size) {
+ continue;
+ }
+
+ if (offset + length > ntdb->file->map_size) {
+ ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_transaction_setup_recovery:"
+ " transaction data over new region"
+ " boundary");
+ goto fail;
+ }
+ if (offset + length > ntdb->transaction->old_map_size) {
+ /* Short read at EOF. */
+ length = ntdb->transaction->old_map_size - offset;
+ }
+ buffer = ntdb_access_read(ntdb, offset, length, false);
+ if (NTDB_PTR_IS_ERR(buffer)) {
+ ecode = NTDB_PTR_ERR(buffer);
+ goto fail;
+ }
+
+ /* Skip over anything the same at the start. */
+ off = same(ntdb->transaction->blocks[i], buffer, length);
+ offset += off;
+
+ while (off < length) {
+ ntdb_len_t len1;
+ unsigned int samelen;
+
+ len1 = different(ntdb->transaction->blocks[i] + off,
+ buffer + off, length - off,
+ sizeof(offset) + sizeof(len1) + 1,
+ &samelen);
+
+ memcpy(p, &offset, sizeof(offset));
+ memcpy(p + sizeof(offset), &len1, sizeof(len1));
+ ntdb_convert(ntdb, p, sizeof(offset) + sizeof(len1));
+ p += sizeof(offset) + sizeof(len1);
+ memcpy(p, buffer + off, len1);
+ p += len1;
+ off += len1 + samelen;
+ offset += len1 + samelen;
+ }
+ ntdb_access_release(ntdb, buffer);
+ }
+
+ *len = p - (unsigned char *)(rec + 1);
+ ntdb->io = old_methods;
+ return rec;
+
+fail:
+ free(rec);
+ ntdb->io = old_methods;
+ return NTDB_ERR_PTR(ecode);
+}
+
+static ntdb_off_t create_recovery_area(struct ntdb_context *ntdb,
+ ntdb_len_t rec_length,
+ struct ntdb_recovery_record *rec)
+{
+ ntdb_off_t off, recovery_off;
+ ntdb_len_t addition;
+ enum NTDB_ERROR ecode;
+ const struct ntdb_methods *methods = ntdb->transaction->io_methods;
+
+ /* round up to a multiple of page size. Overallocate, since each
+ * such allocation forces us to expand the file. */
+ rec->max_len = ntdb_expand_adjust(ntdb->file->map_size, rec_length);
+
+ /* Round up to a page. */
+ rec->max_len = ((sizeof(*rec) + rec->max_len + PAGESIZE-1)
+ & ~(PAGESIZE-1))
+ - sizeof(*rec);
+
+ off = ntdb->file->map_size;
+
+ /* Restore ->map_size before calling underlying expand_file.
+ Also so that we don't try to expand the file again in the
+ transaction commit, which would destroy the recovery
+ area */
+ addition = (ntdb->file->map_size - ntdb->transaction->old_map_size) +
+ sizeof(*rec) + rec->max_len;
+ ntdb->file->map_size = ntdb->transaction->old_map_size;
+ ntdb->stats.transaction_expand_file++;
+ ecode = methods->expand_file(ntdb, addition);
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_recovery_allocate:"
+ " failed to create recovery area");
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+
+ /* 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 */
+ ntdb->transaction->old_map_size = ntdb->file->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 */
+ recovery_off = off;
+ ntdb_convert(ntdb, &recovery_off, sizeof(recovery_off));
+ ecode = methods->twrite(ntdb, offsetof(struct ntdb_header, recovery),
+ &recovery_off, sizeof(ntdb_off_t));
+ if (ecode != NTDB_SUCCESS) {
+ ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_recovery_allocate:"
+ " failed to write recovery head");
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+ transaction_write_existing(ntdb, offsetof(struct ntdb_header, recovery),
+ &recovery_off,
+ sizeof(ntdb_off_t));
+ return off;
+}
+
+/*
+ setup the recovery data that will be used on a crash during commit
+*/
+static enum NTDB_ERROR transaction_setup_recovery(struct ntdb_context *ntdb)
+{
+ ntdb_len_t recovery_size = 0;
+ ntdb_off_t recovery_off = 0;
+ ntdb_off_t old_map_size = ntdb->transaction->old_map_size;
+ struct ntdb_recovery_record *recovery;
+ const struct ntdb_methods *methods = ntdb->transaction->io_methods;
+ uint64_t magic;
+ enum NTDB_ERROR ecode;
+
+ recovery = alloc_recovery(ntdb, &recovery_size);
+ if (NTDB_PTR_IS_ERR(recovery))
+ return NTDB_PTR_ERR(recovery);
+
+ ecode = ntdb_recovery_area(ntdb, methods, &recovery_off, recovery);
+ if (ecode) {
+ free(recovery);
+ return ecode;
+ }
+
+ if (recovery->max_len < recovery_size) {
+ /* Not large enough. Free up old recovery area. */
+ if (recovery_off) {
+ ntdb->stats.frees++;
+ ecode = add_free_record(ntdb, recovery_off,
+ sizeof(*recovery)
+ + recovery->max_len,
+ NTDB_LOCK_WAIT, true);
+ free(recovery);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_recovery_allocate:"
+ " failed to free previous"
+ " recovery area");
+ }
+
+ /* Refresh recovery after add_free_record above. */
+ recovery = alloc_recovery(ntdb, &recovery_size);
+ if (NTDB_PTR_IS_ERR(recovery))
+ return NTDB_PTR_ERR(recovery);
+ }
+
+ recovery_off = create_recovery_area(ntdb, recovery_size,
+ recovery);
+ if (NTDB_OFF_IS_ERR(recovery_off)) {
+ free(recovery);
+ return NTDB_OFF_TO_ERR(recovery_off);
+ }
+ }
+
+ /* Now we know size, convert rec header. */
+ recovery->magic = NTDB_RECOVERY_INVALID_MAGIC;
+ recovery->len = recovery_size;
+ recovery->eof = old_map_size;
+ ntdb_convert(ntdb, recovery, sizeof(*recovery));
+
+ /* write the recovery data to the recovery area */
+ ecode = methods->twrite(ntdb, recovery_off, recovery, recovery_size);
+ if (ecode != NTDB_SUCCESS) {
+ free(recovery);
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_setup_recovery:"
+ " failed to write recovery data");
+ }
+ transaction_write_existing(ntdb, recovery_off, recovery, recovery_size);
+
+ free(recovery);
+
+ /* 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 */
+ ecode = transaction_sync(ntdb, recovery_off, recovery_size);
+ if (ecode != NTDB_SUCCESS)
+ return ecode;
+
+ magic = NTDB_RECOVERY_MAGIC;
+ ntdb_convert(ntdb, &magic, sizeof(magic));
+
+ ntdb->transaction->magic_offset
+ = recovery_off + offsetof(struct ntdb_recovery_record, magic);
+
+ ecode = methods->twrite(ntdb, ntdb->transaction->magic_offset,
+ &magic, sizeof(magic));
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_setup_recovery:"
+ " failed to write recovery magic");
+ }
+ transaction_write_existing(ntdb, ntdb->transaction->magic_offset,
+ &magic, sizeof(magic));
+
+ /* ensure the recovery magic marker is on disk */
+ return transaction_sync(ntdb, ntdb->transaction->magic_offset,
+ sizeof(magic));
+}
+
+static enum NTDB_ERROR _ntdb_transaction_prepare_commit(struct ntdb_context *ntdb)
+{
+ const struct ntdb_methods *methods;
+ enum NTDB_ERROR ecode;
+
+ if (ntdb->transaction == NULL) {
+ return ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_prepare_commit:"
+ " no transaction");
+ }
+
+ if (ntdb->transaction->prepared) {
+ _ntdb_transaction_cancel(ntdb);
+ return ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_prepare_commit:"
+ " transaction already prepared");
+ }
+
+ if (ntdb->transaction->transaction_error) {
+ _ntdb_transaction_cancel(ntdb);
+ return ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_ERROR,
+ "ntdb_transaction_prepare_commit:"
+ " transaction error pending");
+ }
+
+
+ if (ntdb->transaction->nesting != 0) {
+ return NTDB_SUCCESS;
+ }
+
+ /* check for a null transaction */
+ if (ntdb->transaction->blocks == NULL) {
+ return NTDB_SUCCESS;
+ }
+
+ methods = ntdb->transaction->io_methods;
+
+ /* upgrade the main transaction lock region to a write lock */
+ ecode = ntdb_allrecord_upgrade(ntdb, NTDB_HASH_LOCK_START);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* get the open lock - this prevents new users attaching to the database
+ during the commit */
+ ecode = ntdb_lock_open(ntdb, F_WRLCK, NTDB_LOCK_WAIT|NTDB_LOCK_NOCHECK);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+
+ /* Since we have whole db locked, we don't need the expansion lock. */
+ if (!(ntdb->flags & NTDB_NOSYNC)) {
+ /* Sets up ntdb->transaction->recovery and
+ * ntdb->transaction->magic_offset. */
+ ecode = transaction_setup_recovery(ntdb);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ }
+
+ ntdb->transaction->prepared = true;
+
+ /* expand the file to the new size if needed */
+ if (ntdb->file->map_size != ntdb->transaction->old_map_size) {
+ ntdb_len_t add;
+
+ add = ntdb->file->map_size - ntdb->transaction->old_map_size;
+ /* Restore original map size for ntdb_expand_file */
+ ntdb->file->map_size = ntdb->transaction->old_map_size;
+ ecode = methods->expand_file(ntdb, add);
+ if (ecode != NTDB_SUCCESS) {
+ return ecode;
+ }
+ }
+
+ /* Keep the open lock until the actual commit */
+ return NTDB_SUCCESS;
+}
+
+/*
+ prepare to commit the current transaction
+*/
+_PUBLIC_ enum NTDB_ERROR ntdb_transaction_prepare_commit(struct ntdb_context *ntdb)
+{
+ return ntdb->last_error = _ntdb_transaction_prepare_commit(ntdb);
+}
+
+/*
+ commit the current transaction
+*/
+_PUBLIC_ enum NTDB_ERROR ntdb_transaction_commit(struct ntdb_context *ntdb)
+{
+ const struct ntdb_methods *methods;
+ int i;
+ enum NTDB_ERROR ecode;
+
+ if (ntdb->transaction == NULL) {
+ return ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
+ NTDB_LOG_USE_ERROR,
+ "ntdb_transaction_commit:"
+ " no transaction");
+ }
+
+ ntdb_trace(ntdb, "ntdb_transaction_commit");
+
+ if (ntdb->transaction->nesting != 0) {
+ ntdb->transaction->nesting--;
+ return ntdb->last_error = NTDB_SUCCESS;
+ }
+
+ /* check for a null transaction */
+ if (ntdb->transaction->blocks == NULL) {
+ _ntdb_transaction_cancel(ntdb);
+ return ntdb->last_error = NTDB_SUCCESS;
+ }
+
+ if (!ntdb->transaction->prepared) {
+ ecode = _ntdb_transaction_prepare_commit(ntdb);
+ if (ecode != NTDB_SUCCESS) {
+ _ntdb_transaction_cancel(ntdb);
+ return ntdb->last_error = ecode;
+ }
+ }
+
+ methods = ntdb->transaction->io_methods;
+
+ /* perform all the writes */
+ for (i=0;i<ntdb->transaction->num_blocks;i++) {
+ ntdb_off_t offset;
+ ntdb_len_t length;
+
+ if (ntdb->transaction->blocks[i] == NULL) {
+ continue;
+ }
+
+ offset = i * PAGESIZE;
+ length = PAGESIZE;
+ if (i == ntdb->transaction->num_blocks-1) {
+ length = ntdb->transaction->last_block_size;
+ }
+
+ ecode = methods->twrite(ntdb, offset,
+ ntdb->transaction->blocks[i], length);
+ if (ecode != NTDB_SUCCESS) {
+ /* we've overwritten part of the data and
+ possibly expanded the file, so we need to
+ run the crash recovery code */
+ ntdb->io = methods;
+ ntdb_transaction_recover(ntdb);
+
+ _ntdb_transaction_cancel(ntdb);
+
+ return ntdb->last_error = ecode;
+ }
+ SAFE_FREE(ntdb->transaction->blocks[i]);
+ }
+
+ SAFE_FREE(ntdb->transaction->blocks);
+ ntdb->transaction->num_blocks = 0;
+
+ /* ensure the new data is on disk */
+ ecode = transaction_sync(ntdb, 0, ntdb->file->map_size);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb->last_error = ecode;
+ }
+
+ /*
+ 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 ntdb 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(ntdb->name, NULL);
+#endif
+
+ /* use a transaction cancel to free memory and remove the
+ transaction locks: it "restores" map_size, too. */
+ ntdb->transaction->old_map_size = ntdb->file->map_size;
+ _ntdb_transaction_cancel(ntdb);
+
+ return ntdb->last_error = NTDB_SUCCESS;
+}
+
+
+/*
+ recover from an aborted transaction. Must be called with exclusive
+ database write access already established (including the open
+ lock to prevent new processes attaching)
+*/
+enum NTDB_ERROR ntdb_transaction_recover(struct ntdb_context *ntdb)
+{
+ ntdb_off_t recovery_head, recovery_eof;
+ unsigned char *data, *p;
+ struct ntdb_recovery_record rec;
+ enum NTDB_ERROR ecode;
+
+ /* find the recovery area */
+ recovery_head = ntdb_read_off(ntdb, offsetof(struct ntdb_header,recovery));
+ if (NTDB_OFF_IS_ERR(recovery_head)) {
+ ecode = NTDB_OFF_TO_ERR(recovery_head);
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to read recovery head");
+ }
+
+ if (recovery_head == 0) {
+ /* we have never allocated a recovery record */
+ return NTDB_SUCCESS;
+ }
+
+ /* read the recovery record */
+ ecode = ntdb_read_convert(ntdb, recovery_head, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to read recovery record");
+ }
+
+ if (rec.magic != NTDB_RECOVERY_MAGIC) {
+ /* there is no valid recovery data */
+ return NTDB_SUCCESS;
+ }
+
+ if (ntdb->flags & NTDB_RDONLY) {
+ return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " attempt to recover read only database");
+ }
+
+ recovery_eof = rec.eof;
+
+ data = (unsigned char *)malloc(rec.len);
+ if (data == NULL) {
+ return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to allocate recovery data");
+ }
+
+ /* read the full recovery data */
+ ecode = ntdb->io->tread(ntdb, recovery_head + sizeof(rec), data,
+ rec.len);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to read recovery data");
+ }
+
+ /* recover the file data */
+ p = data;
+ while (p+sizeof(ntdb_off_t)+sizeof(ntdb_len_t) < data + rec.len) {
+ ntdb_off_t ofs;
+ ntdb_len_t len;
+ ntdb_convert(ntdb, p, sizeof(ofs) + sizeof(len));
+ memcpy(&ofs, p, sizeof(ofs));
+ memcpy(&len, p + sizeof(ofs), sizeof(len));
+ p += sizeof(ofs) + sizeof(len);
+
+ ecode = ntdb->io->twrite(ntdb, ofs, p, len);
+ if (ecode != NTDB_SUCCESS) {
+ free(data);
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to recover %zu bytes"
+ " at offset %zu",
+ (size_t)len, (size_t)ofs);
+ }
+ p += len;
+ }
+
+ free(data);
+
+ ecode = transaction_sync(ntdb, 0, ntdb->file->map_size);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to sync recovery");
+ }
+
+ /* if the recovery area is after the recovered eof then remove it */
+ if (recovery_eof <= recovery_head) {
+ ecode = ntdb_write_off(ntdb, offsetof(struct ntdb_header,
+ recovery),
+ 0);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to remove recovery head");
+ }
+ }
+
+ /* remove the recovery magic */
+ ecode = ntdb_write_off(ntdb,
+ recovery_head
+ + offsetof(struct ntdb_recovery_record, magic),
+ NTDB_RECOVERY_INVALID_MAGIC);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to remove recovery magic");
+ }
+
+ ecode = transaction_sync(ntdb, 0, recovery_eof);
+ if (ecode != NTDB_SUCCESS) {
+ return ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
+ "ntdb_transaction_recover:"
+ " failed to sync2 recovery");
+ }
+
+ ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
+ "ntdb_transaction_recover: recovered %zu byte database",
+ (size_t)recovery_eof);
+
+ /* all done */
+ return NTDB_SUCCESS;
+}
+
+ntdb_bool_err ntdb_needs_recovery(struct ntdb_context *ntdb)
+{
+ ntdb_off_t recovery_head;
+ struct ntdb_recovery_record rec;
+ enum NTDB_ERROR ecode;
+
+ /* find the recovery area */
+ recovery_head = ntdb_read_off(ntdb, offsetof(struct ntdb_header,recovery));
+ if (NTDB_OFF_IS_ERR(recovery_head)) {
+ return recovery_head;
+ }
+
+ if (recovery_head == 0) {
+ /* we have never allocated a recovery record */
+ return false;
+ }
+
+ /* read the recovery record */
+ ecode = ntdb_read_convert(ntdb, recovery_head, &rec, sizeof(rec));
+ if (ecode != NTDB_SUCCESS) {
+ return NTDB_ERR_TO_OFF(ecode);
+ }
+
+ return (rec.magic == NTDB_RECOVERY_MAGIC);
+}
diff --git a/lib/ntdb/traverse.c b/lib/ntdb/traverse.c
new file mode 100644
index 0000000000..52bf75c684
--- /dev/null
+++ b/lib/ntdb/traverse.c
@@ -0,0 +1,99 @@
+ /*
+ Trivial Database 2: traverse function.
+ 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 "private.h"
+#include <ccan/likely/likely.h>
+
+_PUBLIC_ int64_t ntdb_traverse_(struct ntdb_context *ntdb,
+ int (*fn)(struct ntdb_context *,
+ NTDB_DATA, NTDB_DATA, void *),
+ void *p)
+{
+ enum NTDB_ERROR ecode;
+ struct traverse_info tinfo;
+ NTDB_DATA k, d;
+ int64_t count = 0;
+
+ k.dptr = NULL;
+ for (ecode = first_in_hash(ntdb, &tinfo, &k, &d.dsize);
+ ecode == NTDB_SUCCESS;
+ ecode = next_in_hash(ntdb, &tinfo, &k, &d.dsize)) {
+ d.dptr = k.dptr + k.dsize;
+
+ count++;
+ if (fn && fn(ntdb, k, d, p)) {
+ free(k.dptr);
+ ntdb->last_error = NTDB_SUCCESS;
+ return count;
+ }
+ free(k.dptr);
+ }
+
+ if (ecode != NTDB_ERR_NOEXIST) {
+ return NTDB_ERR_TO_OFF(ntdb->last_error = ecode);
+ }
+ ntdb->last_error = NTDB_SUCCESS;
+ return count;
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_firstkey(struct ntdb_context *ntdb, NTDB_DATA *key)
+{
+ struct traverse_info tinfo;
+
+ return ntdb->last_error = first_in_hash(ntdb, &tinfo, key, NULL);
+}
+
+/* We lock twice, not very efficient. We could keep last key & tinfo cached. */
+_PUBLIC_ enum NTDB_ERROR ntdb_nextkey(struct ntdb_context *ntdb, NTDB_DATA *key)
+{
+ struct traverse_info tinfo;
+ struct hash_info h;
+ struct ntdb_used_record rec;
+
+ tinfo.prev = find_and_lock(ntdb, *key, F_RDLCK, &h, &rec, &tinfo);
+ free(key->dptr);
+ if (NTDB_OFF_IS_ERR(tinfo.prev)) {
+ return ntdb->last_error = NTDB_OFF_TO_ERR(tinfo.prev);
+ }
+ ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
+
+ return ntdb->last_error = next_in_hash(ntdb, &tinfo, key, NULL);
+}
+
+static int wipe_one(struct ntdb_context *ntdb,
+ NTDB_DATA key, NTDB_DATA data, enum NTDB_ERROR *ecode)
+{
+ *ecode = ntdb_delete(ntdb, key);
+ return (*ecode != NTDB_SUCCESS);
+}
+
+_PUBLIC_ enum NTDB_ERROR ntdb_wipe_all(struct ntdb_context *ntdb)
+{
+ enum NTDB_ERROR ecode;
+ int64_t count;
+
+ ecode = ntdb_allrecord_lock(ntdb, F_WRLCK, NTDB_LOCK_WAIT, false);
+ if (ecode != NTDB_SUCCESS)
+ return ntdb->last_error = ecode;
+
+ /* FIXME: Be smarter. */
+ count = ntdb_traverse(ntdb, wipe_one, &ecode);
+ if (count < 0)
+ ecode = NTDB_OFF_TO_ERR(count);
+ ntdb_allrecord_unlock(ntdb, F_WRLCK);
+ return ntdb->last_error = ecode;
+}
diff --git a/lib/ntdb/wscript b/lib/ntdb/wscript
new file mode 100644
index 0000000000..e6feb14f12
--- /dev/null
+++ b/lib/ntdb/wscript
@@ -0,0 +1,265 @@
+#!/usr/bin/env python
+
+APPNAME = 'ntdb'
+VERSION = '0.9'
+
+blddir = 'bin'
+
+import sys, os
+
+# find the buildtools directory
+srcdir = '.'
+while not os.path.exists(srcdir+'/buildtools') and len(srcdir.split('/')) < 5:
+ srcdir = '../' + srcdir
+sys.path.insert(0, srcdir + '/buildtools/wafsamba')
+
+import wafsamba, samba_dist, Options, Logs, glob
+
+samba_dist.DIST_DIRS('lib/ntdb:. lib/replace:lib/replace lib/ccan:lib/ccan buildtools:buildtools')
+
+def set_options(opt):
+ opt.BUILTIN_DEFAULT('replace,ccan')
+ opt.PRIVATE_EXTENSION_DEFAULT('ntdb', noextension='ntdb')
+ opt.RECURSE('lib/replace')
+ opt.add_option('--valgrind',
+ help=("use valgrind on tests programs"),
+ action="store_true", dest='VALGRIND', default=False)
+ opt.add_option('--valgrind-log',
+ help=("where to put the valgrind log"),
+ action="store", dest='VALGRINDLOG', default=None)
+ if opt.IN_LAUNCH_DIR():
+ opt.add_option('--disable-python',
+ help=("disable the pyntdb module"),
+ action="store_true", dest='disable_python', default=False)
+
+def configure(conf):
+ conf.RECURSE('lib/replace')
+ conf.RECURSE('lib/ccan')
+
+ conf.env.NTDB_TEST_RUN_SRC=['test/run-001-encode.c',
+ 'test/run-001-fls.c',
+ 'test/run-01-new_database.c',
+ 'test/run-02-expand.c',
+ 'test/run-03-coalesce.c',
+ 'test/run-04-basichash.c',
+ 'test/run-05-readonly-open.c',
+ 'test/run-10-simple-store.c',
+ 'test/run-11-simple-fetch.c',
+ 'test/run-12-check.c',
+ 'test/run-15-append.c',
+ 'test/run-20-growhash.c',
+ 'test/run-25-hashoverload.c',
+ 'test/run-30-exhaust-before-expand.c',
+ 'test/run-35-convert.c',
+ 'test/run-50-multiple-freelists.c',
+ 'test/run-56-open-during-transaction.c',
+ 'test/run-57-die-during-transaction.c',
+ 'test/run-64-bit-tdb.c',
+ 'test/run-90-get-set-attributes.c',
+ 'test/run-capabilities.c',
+ 'test/run-expand-in-transaction.c',
+ 'test/run-features.c',
+ 'test/run-lockall.c',
+ 'test/run-remap-in-read_traverse.c',
+ 'test/run-seed.c',
+ 'test/run-tdb_errorstr.c',
+ 'test/run-tdb_foreach.c',
+ 'test/run-traverse.c']
+ conf.env.NTDB_TEST_API_SRC=['test/api-12-store.c',
+ 'test/api-13-delete.c',
+ 'test/api-14-exists.c',
+ 'test/api-16-wipe_all.c',
+ 'test/api-21-parse_record.c',
+ 'test/api-55-transaction.c',
+ 'test/api-80-tdb_fd.c',
+ 'test/api-81-seqnum.c',
+ 'test/api-82-lockattr.c',
+ 'test/api-83-openhook.c',
+ 'test/api-91-get-stats.c',
+ 'test/api-92-get-set-readonly.c',
+ 'test/api-93-repack.c',
+ 'test/api-add-remove-flags.c',
+ 'test/api-check-callback.c',
+ 'test/api-firstkey-nextkey.c',
+ 'test/api-fork-test.c',
+ 'test/api-locktimeout.c',
+ 'test/api-missing-entries.c',
+ 'test/api-open-multiple-times.c',
+ 'test/api-record-expand.c',
+ 'test/api-simple-delete.c',
+ 'test/api-summary.c']
+ conf.env.NTDB_TEST_API_HELPER_SRC=['test/helpapi-external-agent.c']
+ conf.env.NTDB_TEST_RUN_HELPER_SRC=['test/helprun-external-agent.c',
+ 'test/helprun-layout.c']
+ conf.env.NTDB_TEST_HELPER_SRC=['test/external-agent.c',
+ 'test/failtest_helper.c',
+ 'test/lock-tracking.c',
+ 'test/logging.c',
+ 'test/tap-interface.c']
+
+ conf.env.standalone_ntdb = conf.IN_LAUNCH_DIR()
+ conf.env.disable_python = getattr(Options.options, 'disable_python', False)
+
+ if not conf.env.standalone_ntdb:
+ if conf.CHECK_BUNDLED_SYSTEM('ntdb', minversion=VERSION,
+ implied_deps='replace'):
+ conf.define('USING_SYSTEM_NTDB', 1)
+ if conf.CHECK_BUNDLED_SYSTEM_PYTHON('pyntdb', 'ntdb', minversion=VERSION):
+ conf.define('USING_SYSTEM_PYNTDB', 1)
+
+ if not conf.env.disable_python:
+ # also disable if we don't have the python libs installed
+ conf.find_program('python', var='PYTHON')
+ conf.check_tool('python')
+ conf.check_python_version((2,4,2))
+ conf.SAMBA_CHECK_PYTHON_HEADERS(mandatory=False)
+ if not conf.env.HAVE_PYTHON_H:
+ Logs.warn('Disabling pyntdb as python devel libs not found')
+ conf.env.disable_python = True
+
+ # This make #include <ccan/...> work.
+ conf.ADD_EXTRA_INCLUDES('''#lib''')
+
+ conf.SAMBA_CONFIG_H()
+
+def build(bld):
+ bld.RECURSE('lib/replace')
+ bld.RECURSE('lib/ccan')
+
+ if bld.env.standalone_ntdb:
+ bld.env.PKGCONFIGDIR = '${LIBDIR}/pkgconfig'
+ private_library = False
+ else:
+ private_library = True
+
+ SRC = '''check.c free.c hash.c io.c lock.c open.c
+ summary.c ntdb.c transaction.c traverse.c'''
+
+ if not bld.CONFIG_SET('USING_SYSTEM_NTDB'):
+ bld.SAMBA_LIBRARY('ntdb',
+ SRC,
+ deps='replace ccan',
+ includes='.',
+ abi_directory='ABI',
+ abi_match='ntdb_*',
+ hide_symbols=True,
+ vnum=VERSION,
+ public_headers='ntdb.h',
+ public_headers_install=not private_library,
+ pc_files='ntdb.pc',
+ private_library=private_library)
+
+ bld.SAMBA_BINARY('ntdbtorture',
+ 'tools/ntdbtorture.c',
+ deps='ntdb',
+ install=False)
+
+ bld.SAMBA_BINARY('ntdbtool',
+ 'tools/ntdbtool.c',
+ deps='ntdb')
+
+ bld.SAMBA_BINARY('ntdbdump',
+ 'tools/ntdbdump.c',
+ deps='ntdb')
+
+ bld.SAMBA_BINARY('ntdbrestore',
+ 'tools/ntdbrestore.c',
+ deps='ntdb')
+
+ bld.SAMBA_BINARY('ntdbbackup',
+ 'tools/ntdbbackup.c',
+ deps='ntdb')
+
+ if bld.env.DEVELOPER_MODE:
+ # FIXME: We need CCAN for some API tests, but waf thinks it's
+ # already available via ntdb. It is, but not publicly.
+ # Workaround is to build a private, non-hiding version.
+ bld.SAMBA_SUBSYSTEM('ntdb-testing',
+ SRC,
+ deps='replace ccan',
+ includes='.')
+
+ bld.SAMBA_SUBSYSTEM('ntdb-test-helpers',
+ bld.env.NTDB_TEST_HELPER_SRC,
+ deps='replace')
+ bld.SAMBA_SUBSYSTEM('ntdb-run-helpers',
+ bld.env.NTDB_TEST_RUN_HELPER_SRC,
+ deps='replace')
+ bld.SAMBA_SUBSYSTEM('ntdb-api-helpers',
+ bld.env.NTDB_TEST_API_HELPER_SRC,
+ deps='replace ntdb-testing')
+
+ for f in bld.env.NTDB_TEST_RUN_SRC:
+ base = os.path.splitext(os.path.basename(f))[0]
+ bld.SAMBA_BINARY('ntdb-' + base, f,
+ deps='ccan replace ntdb-test-helpers ntdb-run-helpers ccan-failtest',
+ install=False)
+
+ for f in bld.env.NTDB_TEST_API_SRC:
+ base = os.path.splitext(os.path.basename(f))[0]
+ bld.SAMBA_BINARY('ntdb-' + base, f,
+ deps='ccan replace ntdb-test-helpers ntdb-api-helpers',
+ install=False)
+
+ if not bld.CONFIG_SET('USING_SYSTEM_PYNTDB'):
+ bld.SAMBA_PYTHON('pyntdb',
+ source='pyntdb.c',
+ deps='ntdb',
+ enabled=not bld.env.disable_python,
+ realname='ntdb.so',
+ cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION)
+
+def testonly(ctx):
+ '''run ntdb testsuite'''
+ import Utils, samba_utils, shutil
+ ecode = 0;
+
+ env = samba_utils.LOAD_ENVIRONMENT()
+
+ if env.standalone_ntdb:
+ # FIXME: This is horrible :(
+ test_prefix = "%s/st" % (Utils.g_module.blddir)
+ shutil.rmtree(test_prefix, ignore_errors=True)
+ os.makedirs(test_prefix)
+
+ # Create scratch directory for tests.
+ testdir = os.path.join(test_prefix, 'ntdb-tests')
+ samba_utils.mkdir_p(testdir)
+ # Symlink back to source dir so it can find tests in test/
+ link = os.path.join(testdir, 'test')
+ if not os.path.exists(link):
+ os.symlink(os.path.abspath(os.path.join(env.cwd, 'test')), link)
+
+ if Options.options.VALGRIND:
+ os.environ['VALGRIND'] = 'valgrind -q --num-callers=30'
+ if Options.options.VALGRINDLOG is not None:
+ os.environ['VALGRIND'] += ' --log-file=%s' % Options.options.VALGRINDLOG
+
+ for f in env.NTDB_TEST_RUN_SRC + env.NTDB_TEST_API_SRC:
+ name = "ntdb-" + os.path.splitext(os.path.basename(f))[0]
+ cmd = "cd " + testdir + " && $VALGRIND " + os.path.abspath(os.path.join(Utils.g_module.blddir, name)) + " > test-output 2>&1"
+ print("..." + f)
+ ret = samba_utils.RUN_COMMAND(cmd)
+ if ret != 0:
+ print("%s (%s) failed:" % (name, f))
+ samba_utils.RUN_COMMAND("cat " + os.path.join(testdir, 'test-output'))
+ ecode = ret;
+ break;
+
+ sys.exit(ecode)
+
+# WAF doesn't build the unit tests for this, maybe because they don't link with ntdb?
+# This forces it
+def test(ctx):
+ import Scripting
+ Scripting.commands.append('build')
+ Scripting.commands.append('testonly')
+
+def dist():
+ '''makes a tarball for distribution'''
+ samba_dist.dist()
+
+def reconfigure(ctx):
+ '''reconfigure if config scripts have changed'''
+ import samba_utils
+ samba_utils.reconfigure(ctx)
diff --git a/lib/tdb2/ABI/tdb-2.0.0.sigs b/lib/tdb2/ABI/tdb-2.0.0.sigs
deleted file mode 100644
index 0e54b90895..0000000000
--- a/lib/tdb2/ABI/tdb-2.0.0.sigs
+++ /dev/null
@@ -1,40 +0,0 @@
-tdb1_incompatible_hash: uint64_t (const void *, size_t, uint64_t, void *)
-tdb_add_flag: void (struct tdb_context *, unsigned int)
-tdb_append: enum TDB_ERROR (struct tdb_context *, struct tdb_data, struct tdb_data)
-tdb_chainlock: enum TDB_ERROR (struct tdb_context *, TDB_DATA)
-tdb_chainlock_read: enum TDB_ERROR (struct tdb_context *, TDB_DATA)
-tdb_chainunlock: void (struct tdb_context *, TDB_DATA)
-tdb_chainunlock_read: void (struct tdb_context *, TDB_DATA)
-tdb_check_: enum TDB_ERROR (struct tdb_context *, enum TDB_ERROR (*)(TDB_DATA, TDB_DATA, void *), void *)
-tdb_close: int (struct tdb_context *)
-tdb_delete: enum TDB_ERROR (struct tdb_context *, struct tdb_data)
-tdb_error: enum TDB_ERROR (struct tdb_context *)
-tdb_errorstr: const char *(enum TDB_ERROR)
-tdb_exists: bool (struct tdb_context *, TDB_DATA)
-tdb_fd: int (const struct tdb_context *)
-tdb_fetch: enum TDB_ERROR (struct tdb_context *, struct tdb_data, struct tdb_data *)
-tdb_firstkey: enum TDB_ERROR (struct tdb_context *, struct tdb_data *)
-tdb_foreach_: void (int (*)(struct tdb_context *, void *), void *)
-tdb_get_attribute: enum TDB_ERROR (struct tdb_context *, union tdb_attribute *)
-tdb_get_flags: unsigned int (struct tdb_context *)
-tdb_get_seqnum: int64_t (struct tdb_context *)
-tdb_lockall: enum TDB_ERROR (struct tdb_context *)
-tdb_lockall_read: enum TDB_ERROR (struct tdb_context *)
-tdb_name: const char *(const struct tdb_context *)
-tdb_nextkey: enum TDB_ERROR (struct tdb_context *, struct tdb_data *)
-tdb_open: struct tdb_context *(const char *, int, int, mode_t, union tdb_attribute *)
-tdb_parse_record_: enum TDB_ERROR (struct tdb_context *, TDB_DATA, enum TDB_ERROR (*)(TDB_DATA, TDB_DATA, void *), void *)
-tdb_remove_flag: void (struct tdb_context *, unsigned int)
-tdb_repack: enum TDB_ERROR (struct tdb_context *)
-tdb_set_attribute: enum TDB_ERROR (struct tdb_context *, const union tdb_attribute *)
-tdb_store: enum TDB_ERROR (struct tdb_context *, struct tdb_data, struct tdb_data, int)
-tdb_summary: enum TDB_ERROR (struct tdb_context *, enum tdb_summary_flags, char **)
-tdb_transaction_cancel: void (struct tdb_context *)
-tdb_transaction_commit: enum TDB_ERROR (struct tdb_context *)
-tdb_transaction_prepare_commit: enum TDB_ERROR (struct tdb_context *)
-tdb_transaction_start: enum TDB_ERROR (struct tdb_context *)
-tdb_traverse_: int64_t (struct tdb_context *, int (*)(struct tdb_context *, TDB_DATA, TDB_DATA, void *), void *)
-tdb_unlockall: void (struct tdb_context *)
-tdb_unlockall_read: void (struct tdb_context *)
-tdb_unset_attribute: void (struct tdb_context *, enum tdb_attribute_type)
-tdb_wipe_all: enum TDB_ERROR (struct tdb_context *)
diff --git a/lib/tdb2/ABI/tdb-2.0.1.sigs b/lib/tdb2/ABI/tdb-2.0.1.sigs
deleted file mode 100644
index f9ee55f84a..0000000000
--- a/lib/tdb2/ABI/tdb-2.0.1.sigs
+++ /dev/null
@@ -1,39 +0,0 @@
-tdb_add_flag: void (struct tdb_context *, unsigned int)
-tdb_append: enum TDB_ERROR (struct tdb_context *, struct tdb_data, struct tdb_data)
-tdb_chainlock: enum TDB_ERROR (struct tdb_context *, TDB_DATA)
-tdb_chainlock_read: enum TDB_ERROR (struct tdb_context *, TDB_DATA)
-tdb_chainunlock: void (struct tdb_context *, TDB_DATA)
-tdb_chainunlock_read: void (struct tdb_context *, TDB_DATA)
-tdb_check_: enum TDB_ERROR (struct tdb_context *, enum TDB_ERROR (*)(TDB_DATA, TDB_DATA, void *), void *)
-tdb_close: int (struct tdb_context *)
-tdb_delete: enum TDB_ERROR (struct tdb_context *, struct tdb_data)
-tdb_error: enum TDB_ERROR (struct tdb_context *)
-tdb_errorstr: const char *(enum TDB_ERROR)
-tdb_exists: bool (struct tdb_context *, TDB_DATA)
-tdb_fd: int (const struct tdb_context *)
-tdb_fetch: enum TDB_ERROR (struct tdb_context *, struct tdb_data, struct tdb_data *)
-tdb_firstkey: enum TDB_ERROR (struct tdb_context *, struct tdb_data *)
-tdb_foreach_: void (int (*)(struct tdb_context *, void *), void *)
-tdb_get_attribute: enum TDB_ERROR (struct tdb_context *, union tdb_attribute *)
-tdb_get_flags: unsigned int (struct tdb_context *)
-tdb_get_seqnum: int64_t (struct tdb_context *)
-tdb_lockall: enum TDB_ERROR (struct tdb_context *)
-tdb_lockall_read: enum TDB_ERROR (struct tdb_context *)
-tdb_name: const char *(const struct tdb_context *)
-tdb_nextkey: enum TDB_ERROR (struct tdb_context *, struct tdb_data *)
-tdb_open: struct tdb_context *(const char *, int, int, mode_t, union tdb_attribute *)
-tdb_parse_record_: enum TDB_ERROR (struct tdb_context *, TDB_DATA, enum TDB_ERROR (*)(TDB_DATA, TDB_DATA, void *), void *)
-tdb_remove_flag: void (struct tdb_context *, unsigned int)
-tdb_repack: enum TDB_ERROR (struct tdb_context *)
-tdb_set_attribute: enum TDB_ERROR (struct tdb_context *, const union tdb_attribute *)
-tdb_store: enum TDB_ERROR (struct tdb_context *, struct tdb_data, struct tdb_data, int)
-tdb_summary: enum TDB_ERROR (struct tdb_context *, enum tdb_summary_flags, char **)
-tdb_transaction_cancel: void (struct tdb_context *)
-tdb_transaction_commit: enum TDB_ERROR (struct tdb_context *)
-tdb_transaction_prepare_commit: enum TDB_ERROR (struct tdb_context *)
-tdb_transaction_start: enum TDB_ERROR (struct tdb_context *)
-tdb_traverse_: int64_t (struct tdb_context *, int (*)(struct tdb_context *, TDB_DATA, TDB_DATA, void *), void *)
-tdb_unlockall: void (struct tdb_context *)
-tdb_unlockall_read: void (struct tdb_context *)
-tdb_unset_attribute: void (struct tdb_context *, enum tdb_attribute_type)
-tdb_wipe_all: enum TDB_ERROR (struct tdb_context *)
diff --git a/lib/tdb2/TODO b/lib/tdb2/TODO
deleted file mode 100644
index 0a9374f016..0000000000
--- a/lib/tdb2/TODO
+++ /dev/null
@@ -1,4 +0,0 @@
-- tdb2restore, tdb2dump, tdb2backup
-- tdb2tool man page
-- Integrate ccan testsuite
-- Integrate tdb2 testsuite
diff --git a/lib/tdb2/_info b/lib/tdb2/_info
deleted file mode 100644
index 37c0c29e99..0000000000
--- a/lib/tdb2/_info
+++ /dev/null
@@ -1,91 +0,0 @@
-#include <string.h>
-#include <stdio.h>
-
-/**
- * tdb2 - [[WORK IN PROGRESS!]] The trivial (64bit transactional) database
- *
- * The tdb2 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/tdb2/tdb2.h>
- * #include <ccan/str/str.h>
- * #include <ccan/err/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;
- * enum TDB_ERROR error;
- *
- * if (argc < 4)
- * usage(argv[0]);
- *
- * tdb = tdb_open(argv[2], TDB_DEFAULT, O_CREAT|O_RDWR,0600, NULL);
- * 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]);
- * error = tdb_fetch(tdb, key, &value);
- * if (error)
- * errx(1, "fetch %s: %s",
- * argv[3], tdb_errorstr(error));
- * 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]);
- * error = tdb_store(tdb, key, value, 0);
- * if (error)
- * errx(1, "store %s: %s",
- * argv[3], tdb_errorstr(error));
- * } else
- * usage(argv[0]);
- *
- * return 0;
- * }
- *
- * Maintainer: Rusty Russell <rusty@rustcorp.com.au>
- *
- * Author: Rusty Russell
- *
- * License: LGPLv3 (or later)
- */
-int main(int argc, char *argv[])
-{
- if (argc != 2)
- return 1;
-
- if (strcmp(argv[1], "depends") == 0) {
- printf("ccan/asprintf\n");
- printf("ccan/hash\n");
- printf("ccan/likely\n");
- printf("ccan/asearch\n");
- printf("ccan/compiler\n");
- printf("ccan/build_assert\n");
- printf("ccan/ilog\n");
- printf("ccan/failtest\n");
- printf("ccan/tally\n");
- printf("ccan/typesafe_cb\n");
- printf("ccan/cast\n");
- printf("ccan/endian\n");
- return 0;
- }
-
- return 1;
-}
diff --git a/lib/tdb2/check.c b/lib/tdb2/check.c
deleted file mode 100644
index 4b589b6ee1..0000000000
--- a/lib/tdb2/check.c
+++ /dev/null
@@ -1,864 +0,0 @@
- /*
- Trivial Database 2: free list/block handling
- 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 "private.h"
-#include <ccan/likely/likely.h>
-#include <ccan/asearch/asearch.h>
-
-/* We keep an ordered array of offsets. */
-static bool append(tdb_off_t **arr, size_t *num, tdb_off_t off)
-{
- tdb_off_t *new = realloc(*arr, (*num + 1) * sizeof(tdb_off_t));
- if (!new)
- return false;
- new[(*num)++] = off;
- *arr = new;
- return true;
-}
-
-static enum TDB_ERROR check_header(struct tdb_context *tdb, tdb_off_t *recovery,
- uint64_t *features, size_t *num_capabilities)
-{
- uint64_t hash_test;
- struct tdb_header hdr;
- enum TDB_ERROR ecode;
- tdb_off_t off, next;
-
- ecode = tdb_read_convert(tdb, 0, &hdr, sizeof(hdr));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- /* magic food should not be converted, so convert back. */
- tdb_convert(tdb, hdr.magic_food, sizeof(hdr.magic_food));
-
- hash_test = TDB_HASH_MAGIC;
- hash_test = tdb_hash(tdb, &hash_test, sizeof(hash_test));
- if (hdr.hash_test != hash_test) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "check: hash test %llu should be %llu",
- (long long)hdr.hash_test,
- (long long)hash_test);
- }
-
- if (strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "check: bad magic '%.*s'",
- (unsigned)sizeof(hdr.magic_food),
- hdr.magic_food);
- }
-
- /* Features which are used must be a subset of features offered. */
- if (hdr.features_used & ~hdr.features_offered) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "check: features used (0x%llx) which"
- " are not offered (0x%llx)",
- (long long)hdr.features_used,
- (long long)hdr.features_offered);
- }
-
- *features = hdr.features_offered;
- *recovery = hdr.recovery;
- if (*recovery) {
- if (*recovery < sizeof(hdr)
- || *recovery > tdb->file->map_size) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check:"
- " invalid recovery offset %zu",
- (size_t)*recovery);
- }
- }
-
- for (off = hdr.capabilities; off && ecode == TDB_SUCCESS; off = next) {
- const struct tdb_capability *cap;
- enum TDB_ERROR e;
-
- cap = tdb_access_read(tdb, off, sizeof(*cap), true);
- if (TDB_PTR_IS_ERR(cap)) {
- return TDB_PTR_ERR(cap);
- }
-
- /* All capabilities are unknown. */
- e = unknown_capability(tdb, "tdb_check", cap->type);
- next = cap->next;
- tdb_access_release(tdb, cap);
- if (e)
- return e;
- (*num_capabilities)++;
- }
-
- /* Don't check reserved: they *can* be used later. */
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR check_hash_tree(struct tdb_context *tdb,
- tdb_off_t off, unsigned int group_bits,
- uint64_t hprefix,
- unsigned hprefix_bits,
- tdb_off_t used[],
- size_t num_used,
- size_t *num_found,
- enum TDB_ERROR (*check)(TDB_DATA,
- TDB_DATA, void *),
- void *data);
-
-static enum TDB_ERROR check_hash_chain(struct tdb_context *tdb,
- tdb_off_t off,
- uint64_t hash,
- tdb_off_t used[],
- size_t num_used,
- size_t *num_found,
- enum TDB_ERROR (*check)(TDB_DATA,
- TDB_DATA,
- void *),
- void *data)
-{
- struct tdb_used_record rec;
- enum TDB_ERROR ecode;
-
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (rec_magic(&rec) != TDB_CHAIN_MAGIC) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Bad hash chain magic %llu",
- (long long)rec_magic(&rec));
- }
-
- if (rec_data_length(&rec) != sizeof(struct tdb_chain)) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check:"
- " Bad hash chain length %llu vs %zu",
- (long long)rec_data_length(&rec),
- sizeof(struct tdb_chain));
- }
- if (rec_key_length(&rec) != 0) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Bad hash chain key length %llu",
- (long long)rec_key_length(&rec));
- }
- if (rec_hash(&rec) != 0) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Bad hash chain hash value %llu",
- (long long)rec_hash(&rec));
- }
-
- off += sizeof(rec);
- ecode = check_hash_tree(tdb, off, 0, hash, 64,
- used, num_used, num_found, check, data);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- off = tdb_read_off(tdb, off + offsetof(struct tdb_chain, next));
- if (TDB_OFF_IS_ERR(off)) {
- return TDB_OFF_TO_ERR(off);
- }
- if (off == 0)
- return TDB_SUCCESS;
- (*num_found)++;
- return check_hash_chain(tdb, off, hash, used, num_used, num_found,
- check, data);
-}
-
-static enum TDB_ERROR check_hash_record(struct tdb_context *tdb,
- tdb_off_t off,
- uint64_t hprefix,
- unsigned hprefix_bits,
- tdb_off_t used[],
- size_t num_used,
- size_t *num_found,
- enum TDB_ERROR (*check)(TDB_DATA,
- TDB_DATA,
- void *),
- void *data)
-{
- struct tdb_used_record rec;
- enum TDB_ERROR ecode;
-
- if (hprefix_bits >= 64)
- return check_hash_chain(tdb, off, hprefix, used, num_used,
- num_found, check, data);
-
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (rec_magic(&rec) != TDB_HTABLE_MAGIC) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Bad hash table magic %llu",
- (long long)rec_magic(&rec));
- }
- if (rec_data_length(&rec)
- != sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check:"
- " Bad hash table length %llu vs %llu",
- (long long)rec_data_length(&rec),
- (long long)sizeof(tdb_off_t)
- << TDB_SUBLEVEL_HASH_BITS);
- }
- if (rec_key_length(&rec) != 0) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Bad hash table key length %llu",
- (long long)rec_key_length(&rec));
- }
- if (rec_hash(&rec) != 0) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Bad hash table hash value %llu",
- (long long)rec_hash(&rec));
- }
-
- off += sizeof(rec);
- return check_hash_tree(tdb, off,
- TDB_SUBLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
- hprefix, hprefix_bits,
- used, num_used, num_found, check, data);
-}
-
-static int off_cmp(const tdb_off_t *a, const tdb_off_t *b)
-{
- /* Can overflow an int. */
- return *a > *b ? 1
- : *a < *b ? -1
- : 0;
-}
-
-static uint64_t get_bits(uint64_t h, unsigned num, unsigned *used)
-{
- *used += num;
-
- return (h >> (64 - *used)) & ((1U << num) - 1);
-}
-
-static enum TDB_ERROR check_hash_tree(struct tdb_context *tdb,
- tdb_off_t off, unsigned int group_bits,
- uint64_t hprefix,
- unsigned hprefix_bits,
- tdb_off_t used[],
- size_t num_used,
- size_t *num_found,
- enum TDB_ERROR (*check)(TDB_DATA,
- TDB_DATA, void *),
- void *data)
-{
- unsigned int g, b;
- const tdb_off_t *hash;
- struct tdb_used_record rec;
- enum TDB_ERROR ecode;
-
- hash = tdb_access_read(tdb, off,
- sizeof(tdb_off_t)
- << (group_bits + TDB_HASH_GROUP_BITS),
- true);
- if (TDB_PTR_IS_ERR(hash)) {
- return TDB_PTR_ERR(hash);
- }
-
- for (g = 0; g < (1 << group_bits); g++) {
- const tdb_off_t *group = hash + (g << TDB_HASH_GROUP_BITS);
- for (b = 0; b < (1 << TDB_HASH_GROUP_BITS); b++) {
- unsigned int bucket, i, used_bits;
- uint64_t h;
- tdb_off_t *p;
- if (group[b] == 0)
- continue;
-
- off = group[b] & TDB_OFF_MASK;
- p = asearch(&off, used, num_used, off_cmp);
- if (!p) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: Invalid offset"
- " %llu in hash",
- (long long)off);
- goto fail;
- }
- /* Mark it invalid. */
- *p ^= 1;
- (*num_found)++;
-
- if (hprefix_bits == 64) {
- /* Chained entries are unordered. */
- if (is_subhash(group[b])) {
- ecode = TDB_ERR_CORRUPT;
- tdb_logerr(tdb, ecode,
- TDB_LOG_ERROR,
- "tdb_check: Invalid chain"
- " entry subhash");
- goto fail;
- }
- h = hash_record(tdb, off);
- if (h != hprefix) {
- ecode = TDB_ERR_CORRUPT;
- tdb_logerr(tdb, ecode,
- TDB_LOG_ERROR,
- "check: bad hash chain"
- " placement"
- " 0x%llx vs 0x%llx",
- (long long)h,
- (long long)hprefix);
- goto fail;
- }
- ecode = tdb_read_convert(tdb, off, &rec,
- sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- goto check;
- }
-
- if (is_subhash(group[b])) {
- uint64_t subprefix;
- subprefix = (hprefix
- << (group_bits + TDB_HASH_GROUP_BITS))
- + g * (1 << TDB_HASH_GROUP_BITS) + b;
-
- ecode = check_hash_record(tdb,
- group[b] & TDB_OFF_MASK,
- subprefix,
- hprefix_bits
- + group_bits
- + TDB_HASH_GROUP_BITS,
- used, num_used, num_found,
- check, data);
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- continue;
- }
- /* A normal entry */
-
- /* Does it belong here at all? */
- h = hash_record(tdb, off);
- used_bits = 0;
- if (get_bits(h, hprefix_bits, &used_bits) != hprefix
- && hprefix_bits) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "check: bad hash placement"
- " 0x%llx vs 0x%llx",
- (long long)h,
- (long long)hprefix);
- goto fail;
- }
-
- /* Does it belong in this group? */
- if (get_bits(h, group_bits, &used_bits) != g) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "check: bad group %llu"
- " vs %u",
- (long long)h, g);
- goto fail;
- }
-
- /* Are bucket bits correct? */
- bucket = group[b] & TDB_OFF_HASH_GROUP_MASK;
- if (get_bits(h, TDB_HASH_GROUP_BITS, &used_bits)
- != bucket) {
- used_bits -= TDB_HASH_GROUP_BITS;
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "check: bad bucket %u vs %u",
- (unsigned)get_bits(h,
- TDB_HASH_GROUP_BITS,
- &used_bits),
- bucket);
- goto fail;
- }
-
- /* There must not be any zero entries between
- * the bucket it belongs in and this one! */
- for (i = bucket;
- i != b;
- i = (i + 1) % (1 << TDB_HASH_GROUP_BITS)) {
- if (group[i] == 0) {
- ecode = TDB_ERR_CORRUPT;
- tdb_logerr(tdb, ecode,
- TDB_LOG_ERROR,
- "check: bad group placement"
- " %u vs %u",
- b, bucket);
- goto fail;
- }
- }
-
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
-
- /* Bottom bits must match header. */
- if ((h & ((1 << 11)-1)) != rec_hash(&rec)) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: Bad hash magic"
- " at offset %llu"
- " (0x%llx vs 0x%llx)",
- (long long)off,
- (long long)h,
- (long long)rec_hash(&rec));
- goto fail;
- }
-
- check:
- if (check) {
- TDB_DATA k, d;
- const unsigned char *kptr;
-
- kptr = tdb_access_read(tdb,
- off + sizeof(rec),
- rec_key_length(&rec)
- + rec_data_length(&rec),
- false);
- if (TDB_PTR_IS_ERR(kptr)) {
- ecode = TDB_PTR_ERR(kptr);
- goto fail;
- }
-
- k = tdb_mkdata(kptr, rec_key_length(&rec));
- d = tdb_mkdata(kptr + k.dsize,
- rec_data_length(&rec));
- ecode = check(k, d, data);
- tdb_access_release(tdb, kptr);
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- }
- }
- }
- tdb_access_release(tdb, hash);
- return TDB_SUCCESS;
-
-fail:
- tdb_access_release(tdb, hash);
- return ecode;
-}
-
-static enum TDB_ERROR check_hash(struct tdb_context *tdb,
- tdb_off_t used[],
- size_t num_used, size_t num_other_used,
- enum TDB_ERROR (*check)(TDB_DATA, TDB_DATA, void *),
- void *data)
-{
- /* Free tables and capabilities also show up as used. */
- size_t num_found = num_other_used;
- enum TDB_ERROR ecode;
-
- ecode = check_hash_tree(tdb, offsetof(struct tdb_header, hashtable),
- TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
- 0, 0, used, num_used, &num_found,
- check, data);
- if (ecode == TDB_SUCCESS) {
- if (num_found != num_used) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Not all entries"
- " are in hash");
- }
- }
- return ecode;
-}
-
-static enum TDB_ERROR check_free(struct tdb_context *tdb,
- tdb_off_t off,
- const struct tdb_free_record *frec,
- tdb_off_t prev, unsigned int ftable,
- unsigned int bucket)
-{
- enum TDB_ERROR ecode;
-
- if (frec_magic(frec) != TDB_FREE_MAGIC) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: offset %llu bad magic 0x%llx",
- (long long)off,
- (long long)frec->magic_and_prev);
- }
- if (frec_ftable(frec) != ftable) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: offset %llu bad freetable %u",
- (long long)off, frec_ftable(frec));
-
- }
-
- ecode = tdb->io->oob(tdb, off,
- frec_len(frec)
- + sizeof(struct tdb_used_record),
- false);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- if (size_to_bucket(frec_len(frec)) != bucket) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: offset %llu in wrong bucket"
- " (%u vs %u)",
- (long long)off,
- bucket, size_to_bucket(frec_len(frec)));
- }
- if (prev && prev != frec_prev(frec)) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: offset %llu bad prev"
- " (%llu vs %llu)",
- (long long)off,
- (long long)prev, (long long)frec_len(frec));
- }
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR check_free_table(struct tdb_context *tdb,
- tdb_off_t ftable_off,
- unsigned ftable_num,
- tdb_off_t fr[],
- size_t num_free,
- size_t *num_found)
-{
- struct tdb_freetable ft;
- tdb_off_t h;
- unsigned int i;
- enum TDB_ERROR ecode;
-
- ecode = tdb_read_convert(tdb, ftable_off, &ft, sizeof(ft));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (rec_magic(&ft.hdr) != TDB_FTABLE_MAGIC
- || rec_key_length(&ft.hdr) != 0
- || rec_data_length(&ft.hdr) != sizeof(ft) - sizeof(ft.hdr)
- || rec_hash(&ft.hdr) != 0) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Invalid header on free table");
- }
-
- for (i = 0; i < TDB_FREE_BUCKETS; i++) {
- tdb_off_t off, prev = 0, *p, first = 0;
- struct tdb_free_record f;
-
- h = bucket_off(ftable_off, i);
- for (off = tdb_read_off(tdb, h); off; off = f.next) {
- if (TDB_OFF_IS_ERR(off)) {
- return TDB_OFF_TO_ERR(off);
- }
- if (!first) {
- off &= TDB_OFF_MASK;
- first = off;
- }
- ecode = tdb_read_convert(tdb, off, &f, sizeof(f));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- ecode = check_free(tdb, off, &f, prev, ftable_num, i);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* FIXME: Check hash bits */
- p = asearch(&off, fr, num_free, off_cmp);
- if (!p) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: Invalid offset"
- " %llu in free table",
- (long long)off);
- }
- /* Mark it invalid. */
- *p ^= 1;
- (*num_found)++;
- prev = off;
- }
-
- if (first) {
- /* Now we can check first back pointer. */
- ecode = tdb_read_convert(tdb, first, &f, sizeof(f));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- ecode = check_free(tdb, first, &f, prev, ftable_num, i);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- }
- }
- return TDB_SUCCESS;
-}
-
-/* Slow, but should be very rare. */
-tdb_off_t dead_space(struct tdb_context *tdb, tdb_off_t off)
-{
- size_t len;
- enum TDB_ERROR ecode;
-
- for (len = 0; off + len < tdb->file->map_size; len++) {
- char c;
- ecode = tdb->io->tread(tdb, off, &c, 1);
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
- if (c != 0 && c != 0x43)
- break;
- }
- return len;
-}
-
-static enum TDB_ERROR check_linear(struct tdb_context *tdb,
- tdb_off_t **used, size_t *num_used,
- tdb_off_t **fr, size_t *num_free,
- uint64_t features, tdb_off_t recovery)
-{
- tdb_off_t off;
- tdb_len_t len;
- enum TDB_ERROR ecode;
- bool found_recovery = false;
-
- for (off = sizeof(struct tdb_header);
- off < tdb->file->map_size;
- off += len) {
- union {
- struct tdb_used_record u;
- struct tdb_free_record f;
- struct tdb_recovery_record r;
- } rec;
- /* r is larger: only get that if we need to. */
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec.f));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* If we crash after ftruncate, we can get zeroes or fill. */
- if (rec.r.magic == TDB_RECOVERY_INVALID_MAGIC
- || rec.r.magic == 0x4343434343434343ULL) {
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec.r));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- if (recovery == off) {
- found_recovery = true;
- len = sizeof(rec.r) + rec.r.max_len;
- } else {
- len = dead_space(tdb, off);
- if (TDB_OFF_IS_ERR(len)) {
- return TDB_OFF_TO_ERR(len);
- }
- if (len < sizeof(rec.r)) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: invalid"
- " dead space at %zu",
- (size_t)off);
- }
-
- tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
- "Dead space at %zu-%zu (of %zu)",
- (size_t)off, (size_t)(off + len),
- (size_t)tdb->file->map_size);
- }
- } else if (rec.r.magic == TDB_RECOVERY_MAGIC) {
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec.r));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- if (recovery != off) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: unexpected"
- " recovery record at offset"
- " %zu",
- (size_t)off);
- }
- if (rec.r.len > rec.r.max_len) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: invalid recovery"
- " length %zu",
- (size_t)rec.r.len);
- }
- if (rec.r.eof > tdb->file->map_size) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: invalid old EOF"
- " %zu", (size_t)rec.r.eof);
- }
- found_recovery = true;
- len = sizeof(rec.r) + rec.r.max_len;
- } else if (frec_magic(&rec.f) == TDB_FREE_MAGIC) {
- len = sizeof(rec.u) + frec_len(&rec.f);
- if (off + len > tdb->file->map_size) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: free overlength"
- " %llu at offset %llu",
- (long long)len,
- (long long)off);
- }
- /* This record should be in free lists. */
- if (frec_ftable(&rec.f) != TDB_FTABLE_NONE
- && !append(fr, num_free, off)) {
- return tdb_logerr(tdb, TDB_ERR_OOM,
- TDB_LOG_ERROR,
- "tdb_check: tracking %zu'th"
- " free record.", *num_free);
- }
- } else if (rec_magic(&rec.u) == TDB_USED_MAGIC
- || rec_magic(&rec.u) == TDB_CHAIN_MAGIC
- || rec_magic(&rec.u) == TDB_HTABLE_MAGIC
- || rec_magic(&rec.u) == TDB_FTABLE_MAGIC
- || rec_magic(&rec.u) == TDB_CAP_MAGIC) {
- uint64_t klen, dlen, extra;
-
- /* This record is used! */
- if (!append(used, num_used, off)) {
- return tdb_logerr(tdb, TDB_ERR_OOM,
- TDB_LOG_ERROR,
- "tdb_check: tracking %zu'th"
- " used record.", *num_used);
- }
-
- klen = rec_key_length(&rec.u);
- dlen = rec_data_length(&rec.u);
- extra = rec_extra_padding(&rec.u);
-
- len = sizeof(rec.u) + klen + dlen + extra;
- if (off + len > tdb->file->map_size) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: used overlength"
- " %llu at offset %llu",
- (long long)len,
- (long long)off);
- }
-
- if (len < sizeof(rec.f)) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: too short record"
- " %llu at %llu",
- (long long)len,
- (long long)off);
- }
-
- /* Check that records have correct 0 at end (but may
- * not in future). */
- if (extra && !features
- && rec_magic(&rec.u) != TDB_CAP_MAGIC) {
- const char *p;
- char c;
- p = tdb_access_read(tdb, off + sizeof(rec.u)
- + klen + dlen, 1, false);
- if (TDB_PTR_IS_ERR(p))
- return TDB_PTR_ERR(p);
- c = *p;
- tdb_access_release(tdb, p);
-
- if (c != '\0') {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check:"
- " non-zero extra"
- " at %llu",
- (long long)off);
- }
- }
- } else {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "tdb_check: Bad magic 0x%llx"
- " at offset %zu",
- (long long)rec_magic(&rec.u),
- (size_t)off);
- }
- }
-
- /* We must have found recovery area if there was one. */
- if (recovery != 0 && !found_recovery) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: expected a recovery area at %zu",
- (size_t)recovery);
- }
-
- return TDB_SUCCESS;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_check_(struct tdb_context *tdb,
- enum TDB_ERROR (*check)(TDB_DATA, TDB_DATA, void *),
- void *data)
-{
- tdb_off_t *fr = NULL, *used = NULL, ft, recovery;
- size_t num_free = 0, num_used = 0, num_found = 0, num_ftables = 0,
- num_capabilities = 0;
- uint64_t features;
- enum TDB_ERROR ecode;
-
- if (tdb->flags & TDB_CANT_CHECK) {
- return tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
- "tdb_check: database has unknown capability,"
- " cannot check.");
- }
-
- ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
- if (ecode != TDB_SUCCESS) {
- return tdb->last_error = ecode;
- }
-
- ecode = tdb_lock_expand(tdb, F_RDLCK);
- if (ecode != TDB_SUCCESS) {
- tdb_allrecord_unlock(tdb, F_RDLCK);
- return tdb->last_error = ecode;
- }
-
- ecode = check_header(tdb, &recovery, &features, &num_capabilities);
- if (ecode != TDB_SUCCESS)
- goto out;
-
- /* First we do a linear scan, checking all records. */
- ecode = check_linear(tdb, &used, &num_used, &fr, &num_free, features,
- recovery);
- if (ecode != TDB_SUCCESS)
- goto out;
-
- for (ft = first_ftable(tdb); ft; ft = next_ftable(tdb, ft)) {
- if (TDB_OFF_IS_ERR(ft)) {
- ecode = TDB_OFF_TO_ERR(ft);
- goto out;
- }
- ecode = check_free_table(tdb, ft, num_ftables, fr, num_free,
- &num_found);
- if (ecode != TDB_SUCCESS)
- goto out;
- num_ftables++;
- }
-
- /* FIXME: Check key uniqueness? */
- ecode = check_hash(tdb, used, num_used, num_ftables + num_capabilities,
- check, data);
- if (ecode != TDB_SUCCESS)
- goto out;
-
- if (num_found != num_free) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_check: Not all entries are in"
- " free table");
- }
-
-out:
- tdb_allrecord_unlock(tdb, F_RDLCK);
- tdb_unlock_expand(tdb, F_RDLCK);
- free(fr);
- free(used);
- return tdb->last_error = ecode;
-}
diff --git a/lib/tdb2/doc/TDB1_porting.txt b/lib/tdb2/doc/TDB1_porting.txt
deleted file mode 100644
index e59295c22f..0000000000
--- a/lib/tdb2/doc/TDB1_porting.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-Interface differences between TDB1 and TDB2.
-
-- tdb2 uses 'struct tdb_data', tdb1 uses 'struct TDB_DATA'. Use the
- TDB_DATA typedef if you want portability between the two.
-
-- tdb2 functions return 0 on success, and a negative error on failure,
- whereas tdb1 functions returned 0 on success, and -1 on failure.
- tdb1 then used tdb_error() to determine the error; this is also
- supported in tdb2 to ease backwards compatibility, though the other
- form is preferred.
-
-- tdb2's tdb_fetch() returns an error, tdb1's returned the data directly
- (or tdb_null, and you were supposed to check tdb_error() to find out why).
-
-- tdb2's tdb_nextkey() frees the old key's dptr, in tdb1 you needed to do
- this manually.
-
-- tdb1's tdb_open/tdb_open_ex took an explicit hash size. tdb2's hash table
- resizes as required.
-
-- tdb2 uses a linked list of attribute structures to implement logging and
- alternate hashes. tdb1 used tdb_open_ex, which was not extensible.
-
-- tdb2 does locking on read-only databases (ie. O_RDONLY passed to tdb_open).
- tdb1 did not: use the TDB_NOLOCK flag if you want to suppress locking.
-
-- tdb2's log function is simpler than tdb1's log function. The string is
- already formatted, and it takes an enum tdb_log_level not a tdb_debug_level,
- and which has only three values: TDB_LOG_ERROR, TDB_LOG_USE_ERROR and
- TDB_LOG_WARNING.
-
-- tdb2 provides tdb_deq() for comparing two struct tdb_data.
-
-- tdb2's tdb_name() returns a copy of the name even for TDB_INTERNAL dbs.
-
-- tdb2 does not need tdb_reopen() or tdb_reopen_all(). If you call
- fork() after during certain operations the child should close the
- tdb, or complete the operations before continuing to use the tdb:
-
- tdb_transaction_start(): child must tdb_transaction_cancel()
- tdb_lockall(): child must call tdb_unlockall()
- tdb_lockall_read(): child must call tdb_unlockall_read()
- tdb_chainlock(): child must call tdb_chainunlock()
- tdb_parse() callback: child must return from tdb_parse()
-
-- tdb2 will not open a non-tdb file, even if O_CREAT is specified.
-
-- There is no tdb_traverse_read. For operating on TDB1 files, you can
- simulate it by tdb_add_flag(tdb, TDB_RDONLY); tdb_traverse();
- tdb_remove_flag(tdb, TDB_RDONLY). This may be desirable because
- traverse on TDB1 files use a write lock on the entire database
- unless it's read-only.
-
-- Failure inside a transaction (such as a lock function failing) does
- not implicitly cancel the transaction; you still need to call
- tdb_transaction_cancel().
-
-TDB1 Compatibility:
-
-- tdb2's offers a tdb1_incompatible_hash function, which is the same
- as the default hash with the TDB_INCOMPATIBLE_HASH flag. There is
- no way of marking an old TDB incompatible with versions < 1.2.6
- while using any other hash.
-
-- The TDB_ATTRIBUTE_TDB1_HASHSIZE attribute can be used to control the
- hash size, but only when creating (ie. O_CREAT) a TDB1
- (ie. TDB_VERSION1).
-
-- There is no TDB_CLEAR_IF_FIRST flag; it has severe scalability and
- API problems. If necessary, you can emulate this by using the open
- hook and placing a 1-byte lock at offset 4. If your program forks,
- you will need to place this lock again in the child.
diff --git a/lib/tdb2/free.c b/lib/tdb2/free.c
deleted file mode 100644
index c4015a0f2a..0000000000
--- a/lib/tdb2/free.c
+++ /dev/null
@@ -1,976 +0,0 @@
- /*
- Trivial Database 2: free list/block handling
- 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 "private.h"
-#include <ccan/likely/likely.h>
-#include <ccan/ilog/ilog.h>
-#include <time.h>
-#include <assert.h>
-#include <limits.h>
-
-static unsigned fls64(uint64_t val)
-{
- return ilog64(val);
-}
-
-/* In which bucket would we find a particular record size? (ignoring header) */
-unsigned int size_to_bucket(tdb_len_t data_len)
-{
- unsigned int bucket;
-
- /* We can't have records smaller than this. */
- assert(data_len >= TDB_MIN_DATA_LEN);
-
- /* Ignoring the header... */
- if (data_len - TDB_MIN_DATA_LEN <= 64) {
- /* 0 in bucket 0, 8 in bucket 1... 64 in bucket 8. */
- bucket = (data_len - TDB_MIN_DATA_LEN) / 8;
- } else {
- /* After that we go power of 2. */
- bucket = fls64(data_len - TDB_MIN_DATA_LEN) + 2;
- }
-
- if (unlikely(bucket >= TDB_FREE_BUCKETS))
- bucket = TDB_FREE_BUCKETS - 1;
- return bucket;
-}
-
-tdb_off_t first_ftable(struct tdb_context *tdb)
-{
- return tdb_read_off(tdb, offsetof(struct tdb_header, free_table));
-}
-
-tdb_off_t next_ftable(struct tdb_context *tdb, tdb_off_t ftable)
-{
- return tdb_read_off(tdb, ftable + offsetof(struct tdb_freetable,next));
-}
-
-enum TDB_ERROR tdb_ftable_init(struct tdb_context *tdb)
-{
- /* Use reservoir sampling algorithm to select a free list at random. */
- unsigned int rnd, max = 0, count = 0;
- tdb_off_t off;
-
- tdb->ftable_off = off = first_ftable(tdb);
- tdb->ftable = 0;
-
- while (off) {
- if (TDB_OFF_IS_ERR(off)) {
- return TDB_OFF_TO_ERR(off);
- }
-
- rnd = random();
- if (rnd >= max) {
- tdb->ftable_off = off;
- tdb->ftable = count;
- max = rnd;
- }
-
- off = next_ftable(tdb, off);
- count++;
- }
- return TDB_SUCCESS;
-}
-
-/* Offset of a given bucket. */
-tdb_off_t bucket_off(tdb_off_t ftable_off, unsigned bucket)
-{
- return ftable_off + offsetof(struct tdb_freetable, buckets)
- + bucket * sizeof(tdb_off_t);
-}
-
-/* Returns free_buckets + 1, or list number to search, or -ve error. */
-static tdb_off_t find_free_head(struct tdb_context *tdb,
- tdb_off_t ftable_off,
- tdb_off_t bucket)
-{
- /* Speculatively search for a non-zero bucket. */
- return tdb_find_nonzero_off(tdb, bucket_off(ftable_off, 0),
- bucket, TDB_FREE_BUCKETS);
-}
-
-static void check_list(struct tdb_context *tdb, tdb_off_t b_off)
-{
-#ifdef CCAN_TDB2_DEBUG
- tdb_off_t off, prev = 0, first;
- struct tdb_free_record r;
-
- first = off = (tdb_read_off(tdb, b_off) & TDB_OFF_MASK);
- while (off != 0) {
- tdb_read_convert(tdb, off, &r, sizeof(r));
- if (frec_magic(&r) != TDB_FREE_MAGIC)
- abort();
- if (prev && frec_prev(&r) != prev)
- abort();
- prev = off;
- off = r.next;
- }
-
- if (first) {
- tdb_read_convert(tdb, first, &r, sizeof(r));
- if (frec_prev(&r) != prev)
- abort();
- }
-#endif
-}
-
-/* Remove from free bucket. */
-static enum TDB_ERROR remove_from_list(struct tdb_context *tdb,
- tdb_off_t b_off, tdb_off_t r_off,
- const struct tdb_free_record *r)
-{
- tdb_off_t off, prev_next, head;
- enum TDB_ERROR ecode;
-
- /* Is this only element in list? Zero out bucket, and we're done. */
- if (frec_prev(r) == r_off)
- return tdb_write_off(tdb, b_off, 0);
-
- /* off = &r->prev->next */
- off = frec_prev(r) + offsetof(struct tdb_free_record, next);
-
- /* Get prev->next */
- prev_next = tdb_read_off(tdb, off);
- if (TDB_OFF_IS_ERR(prev_next))
- return TDB_OFF_TO_ERR(prev_next);
-
- /* If prev->next == 0, we were head: update bucket to point to next. */
- if (prev_next == 0) {
- /* We must preserve upper bits. */
- head = tdb_read_off(tdb, b_off);
- if (TDB_OFF_IS_ERR(head))
- return TDB_OFF_TO_ERR(head);
-
- if ((head & TDB_OFF_MASK) != r_off) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "remove_from_list:"
- " %llu head %llu on list %llu",
- (long long)r_off,
- (long long)head,
- (long long)b_off);
- }
- head = ((head & ~TDB_OFF_MASK) | r->next);
- ecode = tdb_write_off(tdb, b_off, head);
- if (ecode != TDB_SUCCESS)
- return ecode;
- } else {
- /* r->prev->next = r->next */
- ecode = tdb_write_off(tdb, off, r->next);
- if (ecode != TDB_SUCCESS)
- return ecode;
- }
-
- /* If we were the tail, off = &head->prev. */
- if (r->next == 0) {
- head = tdb_read_off(tdb, b_off);
- if (TDB_OFF_IS_ERR(head))
- return TDB_OFF_TO_ERR(head);
- head &= TDB_OFF_MASK;
- off = head + offsetof(struct tdb_free_record, magic_and_prev);
- } else {
- /* off = &r->next->prev */
- off = r->next + offsetof(struct tdb_free_record,
- magic_and_prev);
- }
-
-#ifdef CCAN_TDB2_DEBUG
- /* *off == r */
- if ((tdb_read_off(tdb, off) & TDB_OFF_MASK) != r_off) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "remove_from_list:"
- " %llu bad prev in list %llu",
- (long long)r_off, (long long)b_off);
- }
-#endif
- /* r->next->prev = r->prev */
- return tdb_write_off(tdb, off, r->magic_and_prev);
-}
-
-/* Enqueue in this free bucket: sets coalesce if we've added 128
- * entries to it. */
-static enum TDB_ERROR enqueue_in_free(struct tdb_context *tdb,
- tdb_off_t b_off,
- tdb_off_t off,
- tdb_len_t len,
- bool *coalesce)
-{
- struct tdb_free_record new;
- enum TDB_ERROR ecode;
- tdb_off_t prev, head;
- uint64_t magic = (TDB_FREE_MAGIC << (64 - TDB_OFF_UPPER_STEAL));
-
- head = tdb_read_off(tdb, b_off);
- if (TDB_OFF_IS_ERR(head))
- return TDB_OFF_TO_ERR(head);
-
- /* We only need to set ftable_and_len; rest is set in enqueue_in_free */
- new.ftable_and_len = ((uint64_t)tdb->ftable
- << (64 - TDB_OFF_UPPER_STEAL))
- | len;
-
- /* new->next = head. */
- new.next = (head & TDB_OFF_MASK);
-
- /* First element? Prev points to ourselves. */
- if (!new.next) {
- new.magic_and_prev = (magic | off);
- } else {
- /* new->prev = next->prev */
- prev = tdb_read_off(tdb,
- new.next + offsetof(struct tdb_free_record,
- magic_and_prev));
- new.magic_and_prev = prev;
- if (frec_magic(&new) != TDB_FREE_MAGIC) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "enqueue_in_free: %llu bad head"
- " prev %llu",
- (long long)new.next,
- (long long)prev);
- }
- /* next->prev = new. */
- ecode = tdb_write_off(tdb, new.next
- + offsetof(struct tdb_free_record,
- magic_and_prev),
- off | magic);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
-#ifdef CCAN_TDB2_DEBUG
- prev = tdb_read_off(tdb, frec_prev(&new)
- + offsetof(struct tdb_free_record, next));
- if (prev != 0) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "enqueue_in_free:"
- " %llu bad tail next ptr %llu",
- (long long)frec_prev(&new)
- + offsetof(struct tdb_free_record,
- next),
- (long long)prev);
- }
-#endif
- }
-
- /* Update enqueue count, but don't set high bit: see TDB_OFF_IS_ERR */
- if (*coalesce)
- head += (1ULL << (64 - TDB_OFF_UPPER_STEAL));
- head &= ~(TDB_OFF_MASK | (1ULL << 63));
- head |= off;
-
- ecode = tdb_write_off(tdb, b_off, head);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* It's time to coalesce if counter wrapped. */
- if (*coalesce)
- *coalesce = ((head & ~TDB_OFF_MASK) == 0);
-
- return tdb_write_convert(tdb, off, &new, sizeof(new));
-}
-
-static tdb_off_t ftable_offset(struct tdb_context *tdb, unsigned int ftable)
-{
- tdb_off_t off;
- unsigned int i;
-
- if (likely(tdb->ftable == ftable))
- return tdb->ftable_off;
-
- off = first_ftable(tdb);
- for (i = 0; i < ftable; i++) {
- if (TDB_OFF_IS_ERR(off)) {
- break;
- }
- off = next_ftable(tdb, off);
- }
- return off;
-}
-
-/* Note: we unlock the current bucket if fail (-ve), or coalesce (+ve) and
- * need to blatt the *protect record (which is set to an error). */
-static tdb_len_t coalesce(struct tdb_context *tdb,
- tdb_off_t off, tdb_off_t b_off,
- tdb_len_t data_len,
- tdb_off_t *protect)
-{
- tdb_off_t end;
- struct tdb_free_record rec;
- enum TDB_ERROR ecode;
-
- tdb->stats.alloc_coalesce_tried++;
- end = off + sizeof(struct tdb_used_record) + data_len;
-
- while (end < tdb->file->map_size) {
- const struct tdb_free_record *r;
- tdb_off_t nb_off;
- unsigned ftable, bucket;
-
- r = tdb_access_read(tdb, end, sizeof(*r), true);
- if (TDB_PTR_IS_ERR(r)) {
- ecode = TDB_PTR_ERR(r);
- goto err;
- }
-
- if (frec_magic(r) != TDB_FREE_MAGIC
- || frec_ftable(r) == TDB_FTABLE_NONE) {
- tdb_access_release(tdb, r);
- break;
- }
-
- ftable = frec_ftable(r);
- bucket = size_to_bucket(frec_len(r));
- nb_off = ftable_offset(tdb, ftable);
- if (TDB_OFF_IS_ERR(nb_off)) {
- tdb_access_release(tdb, r);
- ecode = TDB_OFF_TO_ERR(nb_off);
- goto err;
- }
- nb_off = bucket_off(nb_off, bucket);
- tdb_access_release(tdb, r);
-
- /* We may be violating lock order here, so best effort. */
- if (tdb_lock_free_bucket(tdb, nb_off, TDB_LOCK_NOWAIT)
- != TDB_SUCCESS) {
- tdb->stats.alloc_coalesce_lockfail++;
- break;
- }
-
- /* Now we have lock, re-check. */
- ecode = tdb_read_convert(tdb, end, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- tdb_unlock_free_bucket(tdb, nb_off);
- goto err;
- }
-
- if (unlikely(frec_magic(&rec) != TDB_FREE_MAGIC)) {
- tdb->stats.alloc_coalesce_race++;
- tdb_unlock_free_bucket(tdb, nb_off);
- break;
- }
-
- if (unlikely(frec_ftable(&rec) != ftable)
- || unlikely(size_to_bucket(frec_len(&rec)) != bucket)) {
- tdb->stats.alloc_coalesce_race++;
- tdb_unlock_free_bucket(tdb, nb_off);
- break;
- }
-
- /* Did we just mess up a record you were hoping to use? */
- if (end == *protect) {
- tdb->stats.alloc_coalesce_iterate_clash++;
- *protect = TDB_ERR_TO_OFF(TDB_ERR_NOEXIST);
- }
-
- ecode = remove_from_list(tdb, nb_off, end, &rec);
- check_list(tdb, nb_off);
- if (ecode != TDB_SUCCESS) {
- tdb_unlock_free_bucket(tdb, nb_off);
- goto err;
- }
-
- end += sizeof(struct tdb_used_record) + frec_len(&rec);
- tdb_unlock_free_bucket(tdb, nb_off);
- tdb->stats.alloc_coalesce_num_merged++;
- }
-
- /* Didn't find any adjacent free? */
- if (end == off + sizeof(struct tdb_used_record) + data_len)
- return 0;
-
- /* Before we expand, check this isn't one you wanted protected? */
- if (off == *protect) {
- *protect = TDB_ERR_TO_OFF(TDB_ERR_EXISTS);
- tdb->stats.alloc_coalesce_iterate_clash++;
- }
-
- /* OK, expand initial record */
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- goto err;
- }
-
- if (frec_len(&rec) != data_len) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "coalesce: expected data len %zu not %zu",
- (size_t)data_len, (size_t)frec_len(&rec));
- goto err;
- }
-
- ecode = remove_from_list(tdb, b_off, off, &rec);
- check_list(tdb, b_off);
- if (ecode != TDB_SUCCESS) {
- goto err;
- }
-
- /* Try locking violation first. We don't allow coalesce recursion! */
- ecode = add_free_record(tdb, off, end - off, TDB_LOCK_NOWAIT, false);
- if (ecode != TDB_SUCCESS) {
- /* Need to drop lock. Can't rely on anything stable. */
- tdb->stats.alloc_coalesce_lockfail++;
- *protect = TDB_ERR_TO_OFF(TDB_ERR_CORRUPT);
-
- /* We have to drop this to avoid deadlocks, so make sure record
- * doesn't get coalesced by someone else! */
- rec.ftable_and_len = (TDB_FTABLE_NONE
- << (64 - TDB_OFF_UPPER_STEAL))
- | (end - off - sizeof(struct tdb_used_record));
- ecode = tdb_write_off(tdb,
- off + offsetof(struct tdb_free_record,
- ftable_and_len),
- rec.ftable_and_len);
- if (ecode != TDB_SUCCESS) {
- goto err;
- }
-
- tdb_unlock_free_bucket(tdb, b_off);
-
- ecode = add_free_record(tdb, off, end - off, TDB_LOCK_WAIT,
- false);
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
- } else if (TDB_OFF_IS_ERR(*protect)) {
- /* For simplicity, we always drop lock if they can't continue */
- tdb_unlock_free_bucket(tdb, b_off);
- }
- tdb->stats.alloc_coalesce_succeeded++;
-
- /* Return usable length. */
- return end - off - sizeof(struct tdb_used_record);
-
-err:
- /* To unify error paths, we *always* unlock bucket on error. */
- tdb_unlock_free_bucket(tdb, b_off);
- return TDB_ERR_TO_OFF(ecode);
-}
-
-/* List is locked: we unlock it. */
-static enum TDB_ERROR coalesce_list(struct tdb_context *tdb,
- tdb_off_t ftable_off,
- tdb_off_t b_off,
- unsigned int limit)
-{
- enum TDB_ERROR ecode;
- tdb_off_t off;
-
- off = tdb_read_off(tdb, b_off);
- if (TDB_OFF_IS_ERR(off)) {
- ecode = TDB_OFF_TO_ERR(off);
- goto unlock_err;
- }
- /* A little bit of paranoia: counter should be 0. */
- off &= TDB_OFF_MASK;
-
- while (off && limit--) {
- struct tdb_free_record rec;
- tdb_len_t coal;
- tdb_off_t next;
-
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- next = rec.next;
- coal = coalesce(tdb, off, b_off, frec_len(&rec), &next);
- if (TDB_OFF_IS_ERR(coal)) {
- /* This has already unlocked on error. */
- return TDB_OFF_TO_ERR(coal);
- }
- if (TDB_OFF_IS_ERR(next)) {
- /* Coalescing had to unlock, so stop. */
- return TDB_SUCCESS;
- }
- /* Keep going if we're doing well... */
- limit += size_to_bucket(coal / 16 + TDB_MIN_DATA_LEN);
- off = next;
- }
-
- /* Now, move those elements to the tail of the list so we get something
- * else next time. */
- if (off) {
- struct tdb_free_record oldhrec, newhrec, oldtrec, newtrec;
- tdb_off_t oldhoff, oldtoff, newtoff;
-
- /* The record we were up to is the new head. */
- ecode = tdb_read_convert(tdb, off, &newhrec, sizeof(newhrec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- /* Get the new tail. */
- newtoff = frec_prev(&newhrec);
- ecode = tdb_read_convert(tdb, newtoff, &newtrec,
- sizeof(newtrec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- /* Get the old head. */
- oldhoff = tdb_read_off(tdb, b_off);
- if (TDB_OFF_IS_ERR(oldhoff)) {
- ecode = TDB_OFF_TO_ERR(oldhoff);
- goto unlock_err;
- }
-
- /* This could happen if they all coalesced away. */
- if (oldhoff == off)
- goto out;
-
- ecode = tdb_read_convert(tdb, oldhoff, &oldhrec,
- sizeof(oldhrec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- /* Get the old tail. */
- oldtoff = frec_prev(&oldhrec);
- ecode = tdb_read_convert(tdb, oldtoff, &oldtrec,
- sizeof(oldtrec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- /* Old tail's next points to old head. */
- oldtrec.next = oldhoff;
-
- /* Old head's prev points to old tail. */
- oldhrec.magic_and_prev
- = (TDB_FREE_MAGIC << (64 - TDB_OFF_UPPER_STEAL))
- | oldtoff;
-
- /* New tail's next is 0. */
- newtrec.next = 0;
-
- /* Write out the modified versions. */
- ecode = tdb_write_convert(tdb, oldtoff, &oldtrec,
- sizeof(oldtrec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- ecode = tdb_write_convert(tdb, oldhoff, &oldhrec,
- sizeof(oldhrec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- ecode = tdb_write_convert(tdb, newtoff, &newtrec,
- sizeof(newtrec));
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
-
- /* And finally link in new head. */
- ecode = tdb_write_off(tdb, b_off, off);
- if (ecode != TDB_SUCCESS)
- goto unlock_err;
- }
-out:
- tdb_unlock_free_bucket(tdb, b_off);
- return TDB_SUCCESS;
-
-unlock_err:
- tdb_unlock_free_bucket(tdb, b_off);
- return ecode;
-}
-
-/* List must not be locked if coalesce_ok is set. */
-enum TDB_ERROR add_free_record(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len_with_header,
- enum tdb_lock_flags waitflag,
- bool coalesce_ok)
-{
- tdb_off_t b_off;
- tdb_len_t len;
- enum TDB_ERROR ecode;
-
- assert(len_with_header >= sizeof(struct tdb_free_record));
-
- len = len_with_header - sizeof(struct tdb_used_record);
-
- b_off = bucket_off(tdb->ftable_off, size_to_bucket(len));
- ecode = tdb_lock_free_bucket(tdb, b_off, waitflag);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- ecode = enqueue_in_free(tdb, b_off, off, len, &coalesce_ok);
- check_list(tdb, b_off);
-
- /* Coalescing unlocks free list. */
- if (!ecode && coalesce_ok)
- ecode = coalesce_list(tdb, tdb->ftable_off, b_off, 2);
- else
- tdb_unlock_free_bucket(tdb, b_off);
- return ecode;
-}
-
-static size_t adjust_size(size_t keylen, size_t datalen)
-{
- size_t size = keylen + datalen;
-
- if (size < TDB_MIN_DATA_LEN)
- size = TDB_MIN_DATA_LEN;
-
- /* Round to next uint64_t boundary. */
- return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL);
-}
-
-/* If we have enough left over to be useful, split that off. */
-static size_t record_leftover(size_t keylen, size_t datalen,
- bool want_extra, size_t total_len)
-{
- ssize_t leftover;
-
- if (want_extra)
- datalen += datalen / 2;
- leftover = total_len - adjust_size(keylen, datalen);
-
- if (leftover < (ssize_t)sizeof(struct tdb_free_record))
- return 0;
-
- return leftover;
-}
-
-/* We need size bytes to put our key and data in. */
-static tdb_off_t lock_and_alloc(struct tdb_context *tdb,
- tdb_off_t ftable_off,
- tdb_off_t bucket,
- size_t keylen, size_t datalen,
- bool want_extra,
- unsigned magic,
- unsigned hashlow)
-{
- tdb_off_t off, b_off,best_off;
- struct tdb_free_record best = { 0 };
- double multiplier;
- size_t size = adjust_size(keylen, datalen);
- enum TDB_ERROR ecode;
-
- tdb->stats.allocs++;
- b_off = bucket_off(ftable_off, bucket);
-
- /* FIXME: Try non-blocking wait first, to measure contention. */
- /* Lock this bucket. */
- ecode = tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT);
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
-
- best.ftable_and_len = -1ULL;
- best_off = 0;
-
- /* Get slack if we're after extra. */
- if (want_extra)
- multiplier = 1.5;
- else
- multiplier = 1.0;
-
- /* Walk the list to see if any are large enough, getting less fussy
- * as we go. */
- off = tdb_read_off(tdb, b_off);
- if (TDB_OFF_IS_ERR(off)) {
- ecode = TDB_OFF_TO_ERR(off);
- goto unlock_err;
- }
- off &= TDB_OFF_MASK;
-
- while (off) {
- const struct tdb_free_record *r;
- tdb_len_t len;
- tdb_off_t next;
-
- r = tdb_access_read(tdb, off, sizeof(*r), true);
- if (TDB_PTR_IS_ERR(r)) {
- ecode = TDB_PTR_ERR(r);
- goto unlock_err;
- }
-
- if (frec_magic(r) != TDB_FREE_MAGIC) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "lock_and_alloc:"
- " %llu non-free 0x%llx",
- (long long)off,
- (long long)r->magic_and_prev);
- tdb_access_release(tdb, r);
- goto unlock_err;
- }
-
- if (frec_len(r) >= size && frec_len(r) < frec_len(&best)) {
- best_off = off;
- best = *r;
- }
-
- if (frec_len(&best) <= size * multiplier && best_off) {
- tdb_access_release(tdb, r);
- break;
- }
-
- multiplier *= 1.01;
-
- next = r->next;
- len = frec_len(r);
- tdb_access_release(tdb, r);
- off = next;
- }
-
- /* If we found anything at all, use it. */
- if (best_off) {
- struct tdb_used_record rec;
- size_t leftover;
-
- /* We're happy with this size: take it. */
- ecode = remove_from_list(tdb, b_off, best_off, &best);
- check_list(tdb, b_off);
- if (ecode != TDB_SUCCESS) {
- goto unlock_err;
- }
-
- leftover = record_leftover(keylen, datalen, want_extra,
- frec_len(&best));
-
- assert(keylen + datalen + leftover <= frec_len(&best));
- /* We need to mark non-free before we drop lock, otherwise
- * coalesce() could try to merge it! */
- ecode = set_header(tdb, &rec, magic, keylen, datalen,
- frec_len(&best) - leftover, hashlow);
- if (ecode != TDB_SUCCESS) {
- goto unlock_err;
- }
-
- ecode = tdb_write_convert(tdb, best_off, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- goto unlock_err;
- }
-
- /* For futureproofing, we put a 0 in any unused space. */
- if (rec_extra_padding(&rec)) {
- ecode = tdb->io->twrite(tdb, best_off + sizeof(rec)
- + keylen + datalen, "", 1);
- if (ecode != TDB_SUCCESS) {
- goto unlock_err;
- }
- }
-
- /* Bucket of leftover will be <= current bucket, so nested
- * locking is allowed. */
- if (leftover) {
- tdb->stats.alloc_leftover++;
- ecode = add_free_record(tdb,
- best_off + sizeof(rec)
- + frec_len(&best) - leftover,
- leftover, TDB_LOCK_WAIT, false);
- if (ecode != TDB_SUCCESS) {
- best_off = TDB_ERR_TO_OFF(ecode);
- }
- }
- tdb_unlock_free_bucket(tdb, b_off);
-
- return best_off;
- }
-
- tdb_unlock_free_bucket(tdb, b_off);
- return 0;
-
-unlock_err:
- tdb_unlock_free_bucket(tdb, b_off);
- return TDB_ERR_TO_OFF(ecode);
-}
-
-/* Get a free block from current free list, or 0 if none, -ve on error. */
-static tdb_off_t get_free(struct tdb_context *tdb,
- size_t keylen, size_t datalen, bool want_extra,
- unsigned magic, unsigned hashlow)
-{
- tdb_off_t off, ftable_off;
- tdb_off_t start_b, b, ftable;
- bool wrapped = false;
-
- /* If they are growing, add 50% to get to higher bucket. */
- if (want_extra)
- start_b = size_to_bucket(adjust_size(keylen,
- datalen + datalen / 2));
- else
- start_b = size_to_bucket(adjust_size(keylen, datalen));
-
- ftable_off = tdb->ftable_off;
- ftable = tdb->ftable;
- while (!wrapped || ftable_off != tdb->ftable_off) {
- /* Start at exact size bucket, and search up... */
- for (b = find_free_head(tdb, ftable_off, start_b);
- b < TDB_FREE_BUCKETS;
- b = find_free_head(tdb, ftable_off, b + 1)) {
- /* Try getting one from list. */
- off = lock_and_alloc(tdb, ftable_off,
- b, keylen, datalen, want_extra,
- magic, hashlow);
- if (TDB_OFF_IS_ERR(off))
- return off;
- if (off != 0) {
- if (b == start_b)
- tdb->stats.alloc_bucket_exact++;
- if (b == TDB_FREE_BUCKETS - 1)
- tdb->stats.alloc_bucket_max++;
- /* Worked? Stay using this list. */
- tdb->ftable_off = ftable_off;
- tdb->ftable = ftable;
- return off;
- }
- /* Didn't work. Try next bucket. */
- }
-
- if (TDB_OFF_IS_ERR(b)) {
- return b;
- }
-
- /* Hmm, try next table. */
- ftable_off = next_ftable(tdb, ftable_off);
- if (TDB_OFF_IS_ERR(ftable_off)) {
- return ftable_off;
- }
- ftable++;
-
- if (ftable_off == 0) {
- wrapped = true;
- ftable_off = first_ftable(tdb);
- if (TDB_OFF_IS_ERR(ftable_off)) {
- return ftable_off;
- }
- ftable = 0;
- }
- }
-
- return 0;
-}
-
-enum TDB_ERROR set_header(struct tdb_context *tdb,
- struct tdb_used_record *rec,
- unsigned magic, uint64_t keylen, uint64_t datalen,
- uint64_t actuallen, unsigned hashlow)
-{
- uint64_t keybits = (fls64(keylen) + 1) / 2;
-
- /* Use bottom bits of hash, so it's independent of hash table size. */
- rec->magic_and_meta = (hashlow & ((1 << 11)-1))
- | ((actuallen - (keylen + datalen)) << 11)
- | (keybits << 43)
- | ((uint64_t)magic << 48);
- rec->key_and_data_len = (keylen | (datalen << (keybits*2)));
-
- /* Encoding can fail on big values. */
- if (rec_key_length(rec) != keylen
- || rec_data_length(rec) != datalen
- || rec_extra_padding(rec) != actuallen - (keylen + datalen)) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "Could not encode k=%llu,d=%llu,a=%llu",
- (long long)keylen, (long long)datalen,
- (long long)actuallen);
- }
- return TDB_SUCCESS;
-}
-
-/* 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)
-{
- 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 */
- if (new_size < top_size)
- new_size = top_size;
- return new_size - map_size;
-}
-
-/* Expand the database. */
-static enum TDB_ERROR tdb_expand(struct tdb_context *tdb, tdb_len_t size)
-{
- uint64_t old_size;
- tdb_len_t wanted;
- enum TDB_ERROR ecode;
-
- /* Need to hold a hash lock to expand DB: transactions rely on it. */
- if (!(tdb->flags & TDB_NOLOCK)
- && !tdb->file->allrecord_lock.count && !tdb_has_hash_locks(tdb)) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_expand: must hold lock during expand");
- }
-
- /* Only one person can expand file at a time. */
- ecode = tdb_lock_expand(tdb, F_WRLCK);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* Someone else may have expanded the file, so retry. */
- old_size = tdb->file->map_size;
- tdb->io->oob(tdb, tdb->file->map_size, 1, true);
- if (tdb->file->map_size != old_size) {
- tdb_unlock_expand(tdb, F_WRLCK);
- return TDB_SUCCESS;
- }
-
- /* Overallocate. */
- wanted = tdb_expand_adjust(old_size, size);
- /* We need room for the record header too. */
- wanted = adjust_size(0, sizeof(struct tdb_used_record) + wanted);
-
- ecode = tdb->io->expand_file(tdb, wanted);
- if (ecode != TDB_SUCCESS) {
- tdb_unlock_expand(tdb, F_WRLCK);
- return ecode;
- }
-
- /* We need to drop this lock before adding free record. */
- tdb_unlock_expand(tdb, F_WRLCK);
-
- tdb->stats.expands++;
- return add_free_record(tdb, old_size, wanted, TDB_LOCK_WAIT, true);
-}
-
-/* This won't fail: it will expand the database if it has to. */
-tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen,
- uint64_t hash, unsigned magic, bool growing)
-{
- tdb_off_t off;
-
- /* We can't hold pointers during this: we could unmap! */
- assert(!tdb->direct_access);
-
- for (;;) {
- enum TDB_ERROR ecode;
- off = get_free(tdb, keylen, datalen, growing, magic, hash);
- if (likely(off != 0))
- break;
-
- ecode = tdb_expand(tdb, adjust_size(keylen, datalen));
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
- }
-
- return off;
-}
diff --git a/lib/tdb2/hash.c b/lib/tdb2/hash.c
deleted file mode 100644
index 067884a74e..0000000000
--- a/lib/tdb2/hash.c
+++ /dev/null
@@ -1,894 +0,0 @@
- /*
- Trivial Database 2: hash handling
- 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 "private.h"
-#include <ccan/hash/hash.h>
-#include <assert.h>
-
-/* Default hash function. */
-uint64_t tdb_jenkins_hash(const void *key, size_t length, uint64_t seed,
- void *unused)
-{
- uint64_t ret;
- /* hash64_stable assumes lower bits are more important; they are a
- * slightly better hash. We use the upper bits first, so swap them. */
- ret = hash64_stable((const unsigned char *)key, length, seed);
- return (ret >> 32) | (ret << 32);
-}
-
-uint64_t tdb_hash(struct tdb_context *tdb, const void *ptr, size_t len)
-{
- return tdb->hash_fn(ptr, len, tdb->hash_seed, tdb->hash_data);
-}
-
-uint64_t hash_record(struct tdb_context *tdb, tdb_off_t off)
-{
- const struct tdb_used_record *r;
- const void *key;
- uint64_t klen, hash;
-
- r = tdb_access_read(tdb, off, sizeof(*r), true);
- if (TDB_PTR_IS_ERR(r)) {
- /* FIXME */
- return 0;
- }
-
- klen = rec_key_length(r);
- tdb_access_release(tdb, r);
-
- key = tdb_access_read(tdb, off + sizeof(*r), klen, false);
- if (TDB_PTR_IS_ERR(key)) {
- return 0;
- }
-
- hash = tdb_hash(tdb, key, klen);
- tdb_access_release(tdb, key);
- return hash;
-}
-
-/* Get bits from a value. */
-static uint32_t bits_from(uint64_t val, unsigned start, unsigned num)
-{
- assert(num <= 32);
- return (val >> start) & ((1U << num) - 1);
-}
-
-/* We take bits from the top: that way we can lock whole sections of the hash
- * by using lock ranges. */
-static uint32_t use_bits(struct hash_info *h, unsigned num)
-{
- h->hash_used += num;
- return bits_from(h->h, 64 - h->hash_used, num);
-}
-
-static tdb_bool_err key_matches(struct tdb_context *tdb,
- const struct tdb_used_record *rec,
- tdb_off_t off,
- const struct tdb_data *key)
-{
- tdb_bool_err ret = false;
- const char *rkey;
-
- if (rec_key_length(rec) != key->dsize) {
- tdb->stats.compare_wrong_keylen++;
- return ret;
- }
-
- rkey = tdb_access_read(tdb, off + sizeof(*rec), key->dsize, false);
- if (TDB_PTR_IS_ERR(rkey)) {
- return (tdb_bool_err)TDB_PTR_ERR(rkey);
- }
- if (memcmp(rkey, key->dptr, key->dsize) == 0)
- ret = true;
- else
- tdb->stats.compare_wrong_keycmp++;
- tdb_access_release(tdb, rkey);
- return ret;
-}
-
-/* Does entry match? */
-static tdb_bool_err match(struct tdb_context *tdb,
- struct hash_info *h,
- const struct tdb_data *key,
- tdb_off_t val,
- struct tdb_used_record *rec)
-{
- tdb_off_t off;
- enum TDB_ERROR ecode;
-
- tdb->stats.compares++;
- /* Desired bucket must match. */
- if (h->home_bucket != (val & TDB_OFF_HASH_GROUP_MASK)) {
- tdb->stats.compare_wrong_bucket++;
- return false;
- }
-
- /* Top bits of offset == next bits of hash. */
- if (bits_from(val, TDB_OFF_HASH_EXTRA_BIT, TDB_OFF_UPPER_STEAL_EXTRA)
- != bits_from(h->h, 64 - h->hash_used - TDB_OFF_UPPER_STEAL_EXTRA,
- TDB_OFF_UPPER_STEAL_EXTRA)) {
- tdb->stats.compare_wrong_offsetbits++;
- return false;
- }
-
- off = val & TDB_OFF_MASK;
- ecode = tdb_read_convert(tdb, off, rec, sizeof(*rec));
- if (ecode != TDB_SUCCESS) {
- return (tdb_bool_err)ecode;
- }
-
- if ((h->h & ((1 << 11)-1)) != rec_hash(rec)) {
- tdb->stats.compare_wrong_rechash++;
- return false;
- }
-
- return key_matches(tdb, rec, off, key);
-}
-
-static tdb_off_t hbucket_off(tdb_off_t group_start, unsigned bucket)
-{
- return group_start
- + (bucket % (1 << TDB_HASH_GROUP_BITS)) * sizeof(tdb_off_t);
-}
-
-bool is_subhash(tdb_off_t val)
-{
- return (val >> TDB_OFF_UPPER_STEAL_SUBHASH_BIT) & 1;
-}
-
-/* FIXME: Guess the depth, don't over-lock! */
-static tdb_off_t hlock_range(tdb_off_t group, tdb_off_t *size)
-{
- *size = 1ULL << (64 - (TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS));
- return group << (64 - (TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS));
-}
-
-static tdb_off_t COLD find_in_chain(struct tdb_context *tdb,
- struct tdb_data key,
- tdb_off_t chain,
- struct hash_info *h,
- struct tdb_used_record *rec,
- struct traverse_info *tinfo)
-{
- tdb_off_t off, next;
- enum TDB_ERROR ecode;
-
- /* In case nothing is free, we set these to zero. */
- h->home_bucket = h->found_bucket = 0;
-
- for (off = chain; off; off = next) {
- unsigned int i;
-
- h->group_start = off;
- ecode = tdb_read_convert(tdb, off, h->group, sizeof(h->group));
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
-
- for (i = 0; i < (1 << TDB_HASH_GROUP_BITS); i++) {
- tdb_off_t recoff;
- if (!h->group[i]) {
- /* Remember this empty bucket. */
- h->home_bucket = h->found_bucket = i;
- continue;
- }
-
- /* We can insert extra bits via add_to_hash
- * empty bucket logic. */
- recoff = h->group[i] & TDB_OFF_MASK;
- ecode = tdb_read_convert(tdb, recoff, rec,
- sizeof(*rec));
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
-
- ecode = TDB_OFF_TO_ERR(key_matches(tdb, rec, recoff,
- &key));
- if (ecode < 0) {
- return TDB_ERR_TO_OFF(ecode);
- }
- if (ecode == (enum TDB_ERROR)1) {
- h->home_bucket = h->found_bucket = i;
-
- if (tinfo) {
- tinfo->levels[tinfo->num_levels]
- .hashtable = off;
- tinfo->levels[tinfo->num_levels]
- .total_buckets
- = 1 << TDB_HASH_GROUP_BITS;
- tinfo->levels[tinfo->num_levels].entry
- = i;
- tinfo->num_levels++;
- }
- return recoff;
- }
- }
- next = tdb_read_off(tdb, off
- + offsetof(struct tdb_chain, next));
- if (TDB_OFF_IS_ERR(next)) {
- return next;
- }
- if (next)
- next += sizeof(struct tdb_used_record);
- }
- return 0;
-}
-
-/* This is the core routine which searches the hashtable for an entry.
- * On error, no locks are held and -ve is returned.
- * Otherwise, hinfo is filled in (and the optional tinfo).
- * If not found, the return value is 0.
- * If found, the return value is the offset, and *rec is the record. */
-tdb_off_t find_and_lock(struct tdb_context *tdb,
- struct tdb_data key,
- int ltype,
- struct hash_info *h,
- struct tdb_used_record *rec,
- struct traverse_info *tinfo)
-{
- uint32_t i, group;
- tdb_off_t hashtable;
- enum TDB_ERROR ecode;
-
- h->h = tdb_hash(tdb, key.dptr, key.dsize);
- h->hash_used = 0;
- group = use_bits(h, TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS);
- h->home_bucket = use_bits(h, TDB_HASH_GROUP_BITS);
-
- h->hlock_start = hlock_range(group, &h->hlock_range);
- ecode = tdb_lock_hashes(tdb, h->hlock_start, h->hlock_range, ltype,
- TDB_LOCK_WAIT);
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
-
- hashtable = offsetof(struct tdb_header, hashtable);
- if (tinfo) {
- tinfo->toplevel_group = group;
- tinfo->num_levels = 1;
- tinfo->levels[0].entry = 0;
- tinfo->levels[0].hashtable = hashtable
- + (group << TDB_HASH_GROUP_BITS) * sizeof(tdb_off_t);
- tinfo->levels[0].total_buckets = 1 << TDB_HASH_GROUP_BITS;
- }
-
- while (h->hash_used <= 64) {
- /* Read in the hash group. */
- h->group_start = hashtable
- + group * (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
-
- ecode = tdb_read_convert(tdb, h->group_start, &h->group,
- sizeof(h->group));
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
-
- /* Pointer to another hash table? Go down... */
- if (is_subhash(h->group[h->home_bucket])) {
- hashtable = (h->group[h->home_bucket] & TDB_OFF_MASK)
- + sizeof(struct tdb_used_record);
- if (tinfo) {
- /* When we come back, use *next* bucket */
- tinfo->levels[tinfo->num_levels-1].entry
- += h->home_bucket + 1;
- }
- group = use_bits(h, TDB_SUBLEVEL_HASH_BITS
- - TDB_HASH_GROUP_BITS);
- h->home_bucket = use_bits(h, TDB_HASH_GROUP_BITS);
- if (tinfo) {
- tinfo->levels[tinfo->num_levels].hashtable
- = hashtable;
- tinfo->levels[tinfo->num_levels].total_buckets
- = 1 << TDB_SUBLEVEL_HASH_BITS;
- tinfo->levels[tinfo->num_levels].entry
- = group << TDB_HASH_GROUP_BITS;
- tinfo->num_levels++;
- }
- continue;
- }
-
- /* It's in this group: search (until 0 or all searched) */
- for (i = 0, h->found_bucket = h->home_bucket;
- i < (1 << TDB_HASH_GROUP_BITS);
- i++, h->found_bucket = ((h->found_bucket+1)
- % (1 << TDB_HASH_GROUP_BITS))) {
- tdb_bool_err berr;
- if (is_subhash(h->group[h->found_bucket]))
- continue;
-
- if (!h->group[h->found_bucket])
- break;
-
- berr = match(tdb, h, &key, h->group[h->found_bucket],
- rec);
- if (berr < 0) {
- ecode = TDB_OFF_TO_ERR(berr);
- goto fail;
- }
- if (berr) {
- if (tinfo) {
- tinfo->levels[tinfo->num_levels-1].entry
- += h->found_bucket;
- }
- return h->group[h->found_bucket] & TDB_OFF_MASK;
- }
- }
- /* Didn't find it: h indicates where it would go. */
- return 0;
- }
-
- return find_in_chain(tdb, key, hashtable, h, rec, tinfo);
-
-fail:
- tdb_unlock_hashes(tdb, h->hlock_start, h->hlock_range, ltype);
- return TDB_ERR_TO_OFF(ecode);
-}
-
-/* I wrote a simple test, expanding a hash to 2GB, for the following
- * cases:
- * 1) Expanding all the buckets at once,
- * 2) Expanding the bucket we wanted to place the new entry into.
- * 3) Expanding the most-populated bucket,
- *
- * I measured the worst/average/best density during this process.
- * 1) 3%/16%/30%
- * 2) 4%/20%/38%
- * 3) 6%/22%/41%
- *
- * So we figure out the busiest bucket for the moment.
- */
-static unsigned fullest_bucket(struct tdb_context *tdb,
- const tdb_off_t *group,
- unsigned new_bucket)
-{
- unsigned counts[1 << TDB_HASH_GROUP_BITS] = { 0 };
- unsigned int i, best_bucket;
-
- /* Count the new entry. */
- counts[new_bucket]++;
- best_bucket = new_bucket;
-
- for (i = 0; i < (1 << TDB_HASH_GROUP_BITS); i++) {
- unsigned this_bucket;
-
- if (is_subhash(group[i]))
- continue;
- this_bucket = group[i] & TDB_OFF_HASH_GROUP_MASK;
- if (++counts[this_bucket] > counts[best_bucket])
- best_bucket = this_bucket;
- }
-
- return best_bucket;
-}
-
-static bool put_into_group(tdb_off_t *group,
- unsigned bucket, tdb_off_t encoded)
-{
- unsigned int i;
-
- for (i = 0; i < (1 << TDB_HASH_GROUP_BITS); i++) {
- unsigned b = (bucket + i) % (1 << TDB_HASH_GROUP_BITS);
-
- if (group[b] == 0) {
- group[b] = encoded;
- return true;
- }
- }
- return false;
-}
-
-static void force_into_group(tdb_off_t *group,
- unsigned bucket, tdb_off_t encoded)
-{
- if (!put_into_group(group, bucket, encoded))
- abort();
-}
-
-static tdb_off_t encode_offset(tdb_off_t new_off, struct hash_info *h)
-{
- return h->home_bucket
- | new_off
- | ((uint64_t)bits_from(h->h,
- 64 - h->hash_used - TDB_OFF_UPPER_STEAL_EXTRA,
- TDB_OFF_UPPER_STEAL_EXTRA)
- << TDB_OFF_HASH_EXTRA_BIT);
-}
-
-/* Simply overwrite the hash entry we found before. */
-enum TDB_ERROR replace_in_hash(struct tdb_context *tdb,
- struct hash_info *h,
- tdb_off_t new_off)
-{
- return tdb_write_off(tdb, hbucket_off(h->group_start, h->found_bucket),
- encode_offset(new_off, h));
-}
-
-/* We slot in anywhere that's empty in the chain. */
-static enum TDB_ERROR COLD add_to_chain(struct tdb_context *tdb,
- tdb_off_t subhash,
- tdb_off_t new_off)
-{
- tdb_off_t entry;
- enum TDB_ERROR ecode;
-
- entry = tdb_find_zero_off(tdb, subhash, 1<<TDB_HASH_GROUP_BITS);
- if (TDB_OFF_IS_ERR(entry)) {
- return TDB_OFF_TO_ERR(entry);
- }
-
- if (entry == 1 << TDB_HASH_GROUP_BITS) {
- tdb_off_t next;
-
- next = tdb_read_off(tdb, subhash
- + offsetof(struct tdb_chain, next));
- if (TDB_OFF_IS_ERR(next)) {
- return TDB_OFF_TO_ERR(next);
- }
-
- if (!next) {
- next = alloc(tdb, 0, sizeof(struct tdb_chain), 0,
- TDB_CHAIN_MAGIC, false);
- if (TDB_OFF_IS_ERR(next))
- return TDB_OFF_TO_ERR(next);
- ecode = zero_out(tdb,
- next+sizeof(struct tdb_used_record),
- sizeof(struct tdb_chain));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- ecode = tdb_write_off(tdb, subhash
- + offsetof(struct tdb_chain,
- next),
- next);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- }
- return add_to_chain(tdb, next, new_off);
- }
-
- return tdb_write_off(tdb, subhash + entry * sizeof(tdb_off_t),
- new_off);
-}
-
-/* Add into a newly created subhash. */
-static enum TDB_ERROR add_to_subhash(struct tdb_context *tdb, tdb_off_t subhash,
- unsigned hash_used, tdb_off_t val)
-{
- tdb_off_t off = (val & TDB_OFF_MASK), *group;
- struct hash_info h;
- unsigned int gnum;
-
- h.hash_used = hash_used;
-
- if (hash_used + TDB_SUBLEVEL_HASH_BITS > 64)
- return add_to_chain(tdb, subhash, off);
-
- h.h = hash_record(tdb, off);
- gnum = use_bits(&h, TDB_SUBLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS);
- h.group_start = subhash
- + gnum * (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
- h.home_bucket = use_bits(&h, TDB_HASH_GROUP_BITS);
-
- group = tdb_access_write(tdb, h.group_start,
- sizeof(*group) << TDB_HASH_GROUP_BITS, true);
- if (TDB_PTR_IS_ERR(group)) {
- return TDB_PTR_ERR(group);
- }
- force_into_group(group, h.home_bucket, encode_offset(off, &h));
- return tdb_access_commit(tdb, group);
-}
-
-static enum TDB_ERROR expand_group(struct tdb_context *tdb, struct hash_info *h)
-{
- unsigned bucket, num_vals, i, magic;
- size_t subsize;
- tdb_off_t subhash;
- tdb_off_t vals[1 << TDB_HASH_GROUP_BITS];
- enum TDB_ERROR ecode;
-
- /* Attach new empty subhash under fullest bucket. */
- bucket = fullest_bucket(tdb, h->group, h->home_bucket);
-
- if (h->hash_used == 64) {
- tdb->stats.alloc_chain++;
- subsize = sizeof(struct tdb_chain);
- magic = TDB_CHAIN_MAGIC;
- } else {
- tdb->stats.alloc_subhash++;
- subsize = (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS);
- magic = TDB_HTABLE_MAGIC;
- }
-
- subhash = alloc(tdb, 0, subsize, 0, magic, false);
- if (TDB_OFF_IS_ERR(subhash)) {
- return TDB_OFF_TO_ERR(subhash);
- }
-
- ecode = zero_out(tdb, subhash + sizeof(struct tdb_used_record),
- subsize);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* Remove any which are destined for bucket or are in wrong place. */
- num_vals = 0;
- for (i = 0; i < (1 << TDB_HASH_GROUP_BITS); i++) {
- unsigned home_bucket = h->group[i] & TDB_OFF_HASH_GROUP_MASK;
- if (!h->group[i] || is_subhash(h->group[i]))
- continue;
- if (home_bucket == bucket || home_bucket != i) {
- vals[num_vals++] = h->group[i];
- h->group[i] = 0;
- }
- }
- /* FIXME: This assert is valid, but we do this during unit test :( */
- /* assert(num_vals); */
-
- /* Overwrite expanded bucket with subhash pointer. */
- h->group[bucket] = subhash | (1ULL << TDB_OFF_UPPER_STEAL_SUBHASH_BIT);
-
- /* Point to actual contents of record. */
- subhash += sizeof(struct tdb_used_record);
-
- /* Put values back. */
- for (i = 0; i < num_vals; i++) {
- unsigned this_bucket = vals[i] & TDB_OFF_HASH_GROUP_MASK;
-
- if (this_bucket == bucket) {
- ecode = add_to_subhash(tdb, subhash, h->hash_used,
- vals[i]);
- if (ecode != TDB_SUCCESS)
- return ecode;
- } else {
- /* There should be room to put this back. */
- force_into_group(h->group, this_bucket, vals[i]);
- }
- }
- return TDB_SUCCESS;
-}
-
-enum TDB_ERROR delete_from_hash(struct tdb_context *tdb, struct hash_info *h)
-{
- unsigned int i, num_movers = 0;
- tdb_off_t movers[1 << TDB_HASH_GROUP_BITS];
-
- h->group[h->found_bucket] = 0;
- for (i = 1; i < (1 << TDB_HASH_GROUP_BITS); i++) {
- unsigned this_bucket;
-
- this_bucket = (h->found_bucket+i) % (1 << TDB_HASH_GROUP_BITS);
- /* Empty bucket? We're done. */
- if (!h->group[this_bucket])
- break;
-
- /* Ignore subhashes. */
- if (is_subhash(h->group[this_bucket]))
- continue;
-
- /* If this one is not happy where it is, we'll move it. */
- if ((h->group[this_bucket] & TDB_OFF_HASH_GROUP_MASK)
- != this_bucket) {
- movers[num_movers++] = h->group[this_bucket];
- h->group[this_bucket] = 0;
- }
- }
-
- /* Put back the ones we erased. */
- for (i = 0; i < num_movers; i++) {
- force_into_group(h->group, movers[i] & TDB_OFF_HASH_GROUP_MASK,
- movers[i]);
- }
-
- /* Now we write back the hash group */
- return tdb_write_convert(tdb, h->group_start,
- h->group, sizeof(h->group));
-}
-
-enum TDB_ERROR add_to_hash(struct tdb_context *tdb, struct hash_info *h,
- tdb_off_t new_off)
-{
- enum TDB_ERROR ecode;
-
- /* We hit an empty bucket during search? That's where it goes. */
- if (!h->group[h->found_bucket]) {
- h->group[h->found_bucket] = encode_offset(new_off, h);
- /* Write back the modified group. */
- return tdb_write_convert(tdb, h->group_start,
- h->group, sizeof(h->group));
- }
-
- if (h->hash_used > 64)
- return add_to_chain(tdb, h->group_start, new_off);
-
- /* We're full. Expand. */
- ecode = expand_group(tdb, h);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (is_subhash(h->group[h->home_bucket])) {
- /* We were expanded! */
- tdb_off_t hashtable;
- unsigned int gnum;
-
- /* Write back the modified group. */
- ecode = tdb_write_convert(tdb, h->group_start, h->group,
- sizeof(h->group));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* Move hashinfo down a level. */
- hashtable = (h->group[h->home_bucket] & TDB_OFF_MASK)
- + sizeof(struct tdb_used_record);
- gnum = use_bits(h,TDB_SUBLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS);
- h->home_bucket = use_bits(h, TDB_HASH_GROUP_BITS);
- h->group_start = hashtable
- + gnum * (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
- ecode = tdb_read_convert(tdb, h->group_start, &h->group,
- sizeof(h->group));
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- }
-
- /* Expanding the group must have made room if it didn't choose this
- * bucket. */
- if (put_into_group(h->group, h->home_bucket, encode_offset(new_off,h))){
- return tdb_write_convert(tdb, h->group_start,
- h->group, sizeof(h->group));
- }
-
- /* This can happen if all hashes in group (and us) dropped into same
- * group in subhash. */
- return add_to_hash(tdb, h, new_off);
-}
-
-/* Traverse support: returns offset of record, or 0 or -ve error. */
-static tdb_off_t iterate_hash(struct tdb_context *tdb,
- struct traverse_info *tinfo)
-{
- tdb_off_t off, val, i;
- struct traverse_level *tlevel;
-
- tlevel = &tinfo->levels[tinfo->num_levels-1];
-
-again:
- for (i = tdb_find_nonzero_off(tdb, tlevel->hashtable,
- tlevel->entry, tlevel->total_buckets);
- i != tlevel->total_buckets;
- i = tdb_find_nonzero_off(tdb, tlevel->hashtable,
- i+1, tlevel->total_buckets)) {
- if (TDB_OFF_IS_ERR(i)) {
- return i;
- }
-
- val = tdb_read_off(tdb, tlevel->hashtable+sizeof(tdb_off_t)*i);
- if (TDB_OFF_IS_ERR(val)) {
- return val;
- }
-
- off = val & TDB_OFF_MASK;
-
- /* This makes the delete-all-in-traverse case work
- * (and simplifies our logic a little). */
- if (off == tinfo->prev)
- continue;
-
- tlevel->entry = i;
-
- if (!is_subhash(val)) {
- /* Found one. */
- tinfo->prev = off;
- return off;
- }
-
- /* When we come back, we want the next one */
- tlevel->entry++;
- tinfo->num_levels++;
- tlevel++;
- tlevel->hashtable = off + sizeof(struct tdb_used_record);
- tlevel->entry = 0;
- /* Next level is a chain? */
- if (unlikely(tinfo->num_levels == TDB_MAX_LEVELS + 1))
- tlevel->total_buckets = (1 << TDB_HASH_GROUP_BITS);
- else
- tlevel->total_buckets = (1 << TDB_SUBLEVEL_HASH_BITS);
- goto again;
- }
-
- /* Nothing there? */
- if (tinfo->num_levels == 1)
- return 0;
-
- /* Handle chained entries. */
- if (unlikely(tinfo->num_levels == TDB_MAX_LEVELS + 1)) {
- tlevel->hashtable = tdb_read_off(tdb, tlevel->hashtable
- + offsetof(struct tdb_chain,
- next));
- if (TDB_OFF_IS_ERR(tlevel->hashtable)) {
- return tlevel->hashtable;
- }
- if (tlevel->hashtable) {
- tlevel->hashtable += sizeof(struct tdb_used_record);
- tlevel->entry = 0;
- goto again;
- }
- }
-
- /* Go back up and keep searching. */
- tinfo->num_levels--;
- tlevel--;
- goto again;
-}
-
-/* Return success if we find something, TDB_ERR_NOEXIST if none. */
-enum TDB_ERROR next_in_hash(struct tdb_context *tdb,
- struct traverse_info *tinfo,
- TDB_DATA *kbuf, size_t *dlen)
-{
- const unsigned group_bits = TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS;
- tdb_off_t hl_start, hl_range, off;
- enum TDB_ERROR ecode;
-
- while (tinfo->toplevel_group < (1 << group_bits)) {
- hl_start = (tdb_off_t)tinfo->toplevel_group
- << (64 - group_bits);
- hl_range = 1ULL << group_bits;
- ecode = tdb_lock_hashes(tdb, hl_start, hl_range, F_RDLCK,
- TDB_LOCK_WAIT);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- off = iterate_hash(tdb, tinfo);
- if (off) {
- struct tdb_used_record rec;
-
- if (TDB_OFF_IS_ERR(off)) {
- ecode = TDB_OFF_TO_ERR(off);
- goto fail;
- }
-
- ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- if (rec_magic(&rec) != TDB_USED_MAGIC) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_LOG_ERROR,
- "next_in_hash:"
- " corrupt record at %llu",
- (long long)off);
- goto fail;
- }
-
- kbuf->dsize = rec_key_length(&rec);
-
- /* They want data as well? */
- if (dlen) {
- *dlen = rec_data_length(&rec);
- kbuf->dptr = tdb_alloc_read(tdb,
- off + sizeof(rec),
- kbuf->dsize
- + *dlen);
- } else {
- kbuf->dptr = tdb_alloc_read(tdb,
- off + sizeof(rec),
- kbuf->dsize);
- }
- tdb_unlock_hashes(tdb, hl_start, hl_range, F_RDLCK);
- if (TDB_PTR_IS_ERR(kbuf->dptr)) {
- return TDB_PTR_ERR(kbuf->dptr);
- }
- return TDB_SUCCESS;
- }
-
- tdb_unlock_hashes(tdb, hl_start, hl_range, F_RDLCK);
-
- tinfo->toplevel_group++;
- tinfo->levels[0].hashtable
- += (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
- tinfo->levels[0].entry = 0;
- }
- return TDB_ERR_NOEXIST;
-
-fail:
- tdb_unlock_hashes(tdb, hl_start, hl_range, F_RDLCK);
- return ecode;
-
-}
-
-enum TDB_ERROR first_in_hash(struct tdb_context *tdb,
- struct traverse_info *tinfo,
- TDB_DATA *kbuf, size_t *dlen)
-{
- tinfo->prev = 0;
- tinfo->toplevel_group = 0;
- tinfo->num_levels = 1;
- tinfo->levels[0].hashtable = offsetof(struct tdb_header, hashtable);
- tinfo->levels[0].entry = 0;
- tinfo->levels[0].total_buckets = (1 << TDB_HASH_GROUP_BITS);
-
- return next_in_hash(tdb, tinfo, kbuf, dlen);
-}
-
-/* Even if the entry isn't in this hash bucket, you'd have to lock this
- * bucket to find it. */
-static enum TDB_ERROR chainlock(struct tdb_context *tdb, const TDB_DATA *key,
- int ltype, enum tdb_lock_flags waitflag,
- const char *func)
-{
- enum TDB_ERROR ecode;
- uint64_t h = tdb_hash(tdb, key->dptr, key->dsize);
- tdb_off_t lockstart, locksize;
- unsigned int group, gbits;
-
- gbits = TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS;
- group = bits_from(h, 64 - gbits, gbits);
-
- lockstart = hlock_range(group, &locksize);
-
- ecode = tdb_lock_hashes(tdb, lockstart, locksize, ltype, waitflag);
- tdb_trace_1rec(tdb, func, *key);
- return ecode;
-}
-
-/* lock/unlock one hash chain. This is meant to be used to reduce
- contention - it cannot guarantee how many records will be locked */
-_PUBLIC_ enum TDB_ERROR tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
-{
- return tdb->last_error = chainlock(tdb, &key, F_WRLCK, TDB_LOCK_WAIT,
- "tdb_chainlock");
-}
-
-_PUBLIC_ void tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
-{
- uint64_t h = tdb_hash(tdb, key.dptr, key.dsize);
- tdb_off_t lockstart, locksize;
- unsigned int group, gbits;
-
- gbits = TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS;
- group = bits_from(h, 64 - gbits, gbits);
-
- lockstart = hlock_range(group, &locksize);
-
- tdb_trace_1rec(tdb, "tdb_chainunlock", key);
- tdb_unlock_hashes(tdb, lockstart, locksize, F_WRLCK);
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
-{
- return tdb->last_error = chainlock(tdb, &key, F_RDLCK, TDB_LOCK_WAIT,
- "tdb_chainlock_read");
-}
-
-_PUBLIC_ void tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
-{
- uint64_t h = tdb_hash(tdb, key.dptr, key.dsize);
- tdb_off_t lockstart, locksize;
- unsigned int group, gbits;
-
- gbits = TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS;
- group = bits_from(h, 64 - gbits, gbits);
-
- lockstart = hlock_range(group, &locksize);
-
- tdb_trace_1rec(tdb, "tdb_chainunlock_read", key);
- tdb_unlock_hashes(tdb, lockstart, locksize, F_RDLCK);
-}
diff --git a/lib/tdb2/io.c b/lib/tdb2/io.c
deleted file mode 100644
index ca044ae361..0000000000
--- a/lib/tdb2/io.c
+++ /dev/null
@@ -1,650 +0,0 @@
- /*
- 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
- 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 "private.h"
-#include <assert.h>
-#include <ccan/likely/likely.h>
-
-void tdb_munmap(struct tdb_file *file)
-{
- if (file->fd == -1)
- return;
-
- if (file->map_ptr) {
- munmap(file->map_ptr, file->map_size);
- file->map_ptr = NULL;
- }
-}
-
-enum TDB_ERROR tdb_mmap(struct tdb_context *tdb)
-{
- int mmap_flags;
-
- if (tdb->flags & TDB_INTERNAL)
- return TDB_SUCCESS;
-
-#ifndef HAVE_INCOHERENT_MMAP
- if (tdb->flags & TDB_NOMMAP)
- return TDB_SUCCESS;
-#endif
-
- if ((tdb->open_flags & O_ACCMODE) == O_RDONLY)
- mmap_flags = PROT_READ;
- else
- mmap_flags = PROT_READ | PROT_WRITE;
-
- /* size_t can be smaller than off_t. */
- if ((size_t)tdb->file->map_size == tdb->file->map_size) {
- tdb->file->map_ptr = mmap(NULL, tdb->file->map_size,
- mmap_flags,
- MAP_SHARED, tdb->file->fd, 0);
- } else
- tdb->file->map_ptr = MAP_FAILED;
-
- /*
- * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
- */
- if (tdb->file->map_ptr == MAP_FAILED) {
- tdb->file->map_ptr = NULL;
-#ifdef HAVE_INCOHERENT_MMAP
- /* Incoherent mmap means everyone must mmap! */
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_mmap failed for size %lld (%s)",
- (long long)tdb->file->map_size,
- strerror(errno));
-#else
- tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
- "tdb_mmap failed for size %lld (%s)",
- (long long)tdb->file->map_size, strerror(errno));
-#endif
- }
- return TDB_SUCCESS;
-}
-
-/* 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.
-
- If probe is true, len being too large isn't a failure.
-*/
-static enum TDB_ERROR tdb_oob(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len, bool probe)
-{
- struct stat st;
- enum TDB_ERROR ecode;
-
- /* We can't hold pointers during this: we could unmap! */
- assert(!tdb->direct_access
- || (tdb->flags & TDB_NOLOCK)
- || tdb_has_expansion_lock(tdb));
-
- if (len + off < len) {
- if (probe)
- return TDB_SUCCESS;
-
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_oob off %llu len %llu wrap\n",
- (long long)off, (long long)len);
- }
-
- if (len + off <= tdb->file->map_size)
- return TDB_SUCCESS;
- if (tdb->flags & TDB_INTERNAL) {
- if (probe)
- return TDB_SUCCESS;
-
- tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_oob len %lld beyond internal"
- " malloc size %lld",
- (long long)(off + len),
- (long long)tdb->file->map_size);
- return TDB_ERR_IO;
- }
-
- ecode = tdb_lock_expand(tdb, F_RDLCK);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (fstat(tdb->file->fd, &st) != 0) {
- tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "Failed to fstat file: %s", strerror(errno));
- tdb_unlock_expand(tdb, F_RDLCK);
- return TDB_ERR_IO;
- }
-
- tdb_unlock_expand(tdb, F_RDLCK);
-
- if (st.st_size < off + len) {
- if (probe)
- return TDB_SUCCESS;
-
- tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_oob len %llu beyond eof at %llu",
- (long long)(off + len), (long long)st.st_size);
- return TDB_ERR_IO;
- }
-
- /* Unmap, update size, remap */
- tdb_munmap(tdb->file);
-
- tdb->file->map_size = st.st_size;
- return tdb_mmap(tdb);
-}
-
-/* Endian conversion: we only ever deal with 8 byte quantities */
-void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
-{
- assert(size % 8 == 0);
- if (unlikely((tdb->flags & TDB_CONVERT)) && buf) {
- uint64_t i, *p = (uint64_t *)buf;
- for (i = 0; i < size / 8; i++)
- p[i] = bswap_64(p[i]);
- }
- return buf;
-}
-
-/* Return first non-zero offset in offset array, or end, or -ve error. */
-/* FIXME: Return the off? */
-uint64_t tdb_find_nonzero_off(struct tdb_context *tdb,
- tdb_off_t base, uint64_t start, uint64_t end)
-{
- uint64_t i;
- const uint64_t *val;
-
- /* Zero vs non-zero is the same unconverted: minor optimization. */
- val = tdb_access_read(tdb, base + start * sizeof(tdb_off_t),
- (end - start) * sizeof(tdb_off_t), false);
- if (TDB_PTR_IS_ERR(val)) {
- return TDB_ERR_TO_OFF(TDB_PTR_ERR(val));
- }
-
- for (i = 0; i < (end - start); i++) {
- if (val[i])
- break;
- }
- tdb_access_release(tdb, val);
- return start + i;
-}
-
-/* Return first zero offset in num offset array, or num, or -ve error. */
-uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
- uint64_t num)
-{
- uint64_t i;
- const uint64_t *val;
-
- /* Zero vs non-zero is the same unconverted: minor optimization. */
- val = tdb_access_read(tdb, off, num * sizeof(tdb_off_t), false);
- if (TDB_PTR_IS_ERR(val)) {
- return TDB_ERR_TO_OFF(TDB_PTR_ERR(val));
- }
-
- for (i = 0; i < num; i++) {
- if (!val[i])
- break;
- }
- tdb_access_release(tdb, val);
- return i;
-}
-
-enum TDB_ERROR zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
-{
- char buf[8192] = { 0 };
- void *p = tdb->io->direct(tdb, off, len, true);
- enum TDB_ERROR ecode = TDB_SUCCESS;
-
- assert(!(tdb->flags & TDB_RDONLY));
- if (TDB_PTR_IS_ERR(p)) {
- return TDB_PTR_ERR(p);
- }
- if (p) {
- memset(p, 0, len);
- return ecode;
- }
- while (len) {
- unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
- ecode = tdb->io->twrite(tdb, off, buf, todo);
- if (ecode != TDB_SUCCESS) {
- break;
- }
- len -= todo;
- off += todo;
- }
- return ecode;
-}
-
-tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
-{
- tdb_off_t ret;
- enum TDB_ERROR ecode;
-
- if (likely(!(tdb->flags & TDB_CONVERT))) {
- tdb_off_t *p = tdb->io->direct(tdb, off, sizeof(*p), false);
- if (TDB_PTR_IS_ERR(p)) {
- return TDB_ERR_TO_OFF(TDB_PTR_ERR(p));
- }
- if (p)
- return *p;
- }
-
- ecode = tdb_read_convert(tdb, off, &ret, sizeof(ret));
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
- return ret;
-}
-
-/* write a lump of data at a specified offset */
-static enum TDB_ERROR tdb_write(struct tdb_context *tdb, tdb_off_t off,
- const void *buf, tdb_len_t len)
-{
- enum TDB_ERROR ecode;
-
- if (tdb->flags & TDB_RDONLY) {
- return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
- "Write to read-only database");
- }
-
- ecode = tdb->io->oob(tdb, off, len, false);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (tdb->file->map_ptr) {
- memcpy(off + (char *)tdb->file->map_ptr, buf, len);
- } else {
-#ifdef HAVE_INCOHERENT_MMAP
- return TDB_ERR_IO;
-#else
- ssize_t ret;
- ret = pwrite(tdb->file->fd, buf, len, off);
- if (ret != len) {
- /* This shouldn't happen: we avoid sparse files. */
- if (ret >= 0)
- errno = ENOSPC;
-
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_write: %zi at %zu len=%zu (%s)",
- ret, (size_t)off, (size_t)len,
- strerror(errno));
- }
-#endif
- }
- return TDB_SUCCESS;
-}
-
-/* read a lump of data at a specified offset */
-static enum TDB_ERROR tdb_read(struct tdb_context *tdb, tdb_off_t off,
- void *buf, tdb_len_t len)
-{
- enum TDB_ERROR ecode;
-
- ecode = tdb->io->oob(tdb, off, len, false);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (tdb->file->map_ptr) {
- memcpy(buf, off + (char *)tdb->file->map_ptr, len);
- } else {
-#ifdef HAVE_INCOHERENT_MMAP
- return TDB_ERR_IO;
-#else
- ssize_t r = pread(tdb->file->fd, buf, len, off);
- if (r != len) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_read failed with %zi at %zu "
- "len=%zu (%s) map_size=%zu",
- r, (size_t)off, (size_t)len,
- strerror(errno),
- (size_t)tdb->file->map_size);
- }
-#endif
- }
- return TDB_SUCCESS;
-}
-
-enum TDB_ERROR tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
- const void *rec, size_t len)
-{
- enum TDB_ERROR ecode;
-
- if (unlikely((tdb->flags & TDB_CONVERT))) {
- void *conv = malloc(len);
- if (!conv) {
- return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_write: no memory converting"
- " %zu bytes", len);
- }
- memcpy(conv, rec, len);
- ecode = tdb->io->twrite(tdb, off,
- tdb_convert(tdb, conv, len), len);
- free(conv);
- } else {
- ecode = tdb->io->twrite(tdb, off, rec, len);
- }
- return ecode;
-}
-
-enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
- void *rec, size_t len)
-{
- enum TDB_ERROR ecode = tdb->io->tread(tdb, off, rec, len);
- tdb_convert(tdb, rec, len);
- return ecode;
-}
-
-enum TDB_ERROR tdb_write_off(struct tdb_context *tdb,
- tdb_off_t off, tdb_off_t val)
-{
- if (tdb->flags & TDB_RDONLY) {
- return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
- "Write to read-only database");
- }
-
- if (likely(!(tdb->flags & TDB_CONVERT))) {
- tdb_off_t *p = tdb->io->direct(tdb, off, sizeof(*p), true);
- if (TDB_PTR_IS_ERR(p)) {
- return TDB_PTR_ERR(p);
- }
- if (p) {
- *p = val;
- return TDB_SUCCESS;
- }
- }
- return tdb_write_convert(tdb, off, &val, sizeof(val));
-}
-
-static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
- tdb_len_t len, unsigned int prefix)
-{
- unsigned char *buf;
- enum TDB_ERROR ecode;
-
- /* some systems don't like zero length malloc */
- buf = malloc(prefix + len ? prefix + len : 1);
- if (!buf) {
- tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_USE_ERROR,
- "tdb_alloc_read malloc failed len=%zu",
- (size_t)(prefix + len));
- return TDB_ERR_PTR(TDB_ERR_OOM);
- } else {
- ecode = tdb->io->tread(tdb, offset, buf+prefix, len);
- if (unlikely(ecode != TDB_SUCCESS)) {
- free(buf);
- return TDB_ERR_PTR(ecode);
- }
- }
- return buf;
-}
-
-/* read a lump of data, allocating the space for it */
-void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
-{
- return _tdb_alloc_read(tdb, offset, len, 0);
-}
-
-static enum TDB_ERROR fill(struct tdb_context *tdb,
- const void *buf, size_t size,
- tdb_off_t off, tdb_len_t len)
-{
- while (len) {
- size_t n = len > size ? size : len;
- ssize_t ret = pwrite(tdb->file->fd, buf, n, off);
- if (ret != n) {
- if (ret >= 0)
- errno = ENOSPC;
-
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "fill failed:"
- " %zi at %zu len=%zu (%s)",
- ret, (size_t)off, (size_t)len,
- strerror(errno));
- }
- len -= n;
- off += n;
- }
- return TDB_SUCCESS;
-}
-
-/* expand a file. we prefer to use ftruncate, as that is what posix
- says to use for mmap expansion */
-static enum TDB_ERROR tdb_expand_file(struct tdb_context *tdb,
- tdb_len_t addition)
-{
- char buf[8192];
- enum TDB_ERROR ecode;
-
- if (tdb->flags & TDB_RDONLY) {
- return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
- "Expand on read-only database");
- }
-
- if (tdb->flags & TDB_INTERNAL) {
- char *new = realloc(tdb->file->map_ptr,
- tdb->file->map_size + addition);
- if (!new) {
- return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "No memory to expand database");
- }
- tdb->file->map_ptr = new;
- tdb->file->map_size += addition;
- return TDB_SUCCESS;
- } else {
- /* Unmap before trying to write; old TDB claimed OpenBSD had
- * problem with this otherwise. */
- tdb_munmap(tdb->file);
-
- /* If this fails, we try to fill anyway. */
- if (ftruncate(tdb->file->fd, tdb->file->map_size + addition))
- ;
-
- /* 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, 0x43, sizeof(buf));
- ecode = fill(tdb, buf, sizeof(buf), tdb->file->map_size,
- addition);
- if (ecode != TDB_SUCCESS)
- return ecode;
- tdb->file->map_size += addition;
- return tdb_mmap(tdb);
- }
-}
-
-const void *tdb_access_read(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len, bool convert)
-{
- void *ret = NULL;
-
- if (likely(!(tdb->flags & TDB_CONVERT))) {
- ret = tdb->io->direct(tdb, off, len, false);
-
- if (TDB_PTR_IS_ERR(ret)) {
- return ret;
- }
- }
- if (!ret) {
- struct tdb_access_hdr *hdr;
- hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
- if (TDB_PTR_IS_ERR(hdr)) {
- return hdr;
- }
- hdr->next = tdb->access;
- tdb->access = hdr;
- ret = hdr + 1;
- if (convert) {
- tdb_convert(tdb, (void *)ret, len);
- }
- } else
- tdb->direct_access++;
-
- return ret;
-}
-
-void *tdb_access_write(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len, bool convert)
-{
- void *ret = NULL;
-
- if (tdb->flags & TDB_RDONLY) {
- tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
- "Write to read-only database");
- return TDB_ERR_PTR(TDB_ERR_RDONLY);
- }
-
- if (likely(!(tdb->flags & TDB_CONVERT))) {
- ret = tdb->io->direct(tdb, off, len, true);
-
- if (TDB_PTR_IS_ERR(ret)) {
- return ret;
- }
- }
-
- if (!ret) {
- struct tdb_access_hdr *hdr;
- hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
- if (TDB_PTR_IS_ERR(hdr)) {
- return hdr;
- }
- hdr->next = tdb->access;
- tdb->access = hdr;
- hdr->off = off;
- hdr->len = len;
- hdr->convert = convert;
- ret = hdr + 1;
- if (convert)
- tdb_convert(tdb, (void *)ret, len);
- } else
- tdb->direct_access++;
-
- return ret;
-}
-
-static struct tdb_access_hdr **find_hdr(struct tdb_context *tdb, const void *p)
-{
- struct tdb_access_hdr **hp;
-
- for (hp = &tdb->access; *hp; hp = &(*hp)->next) {
- if (*hp + 1 == p)
- return hp;
- }
- return NULL;
-}
-
-void tdb_access_release(struct tdb_context *tdb, const void *p)
-{
- struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
-
- if (hp) {
- hdr = *hp;
- *hp = hdr->next;
- free(hdr);
- } else
- tdb->direct_access--;
-}
-
-enum TDB_ERROR tdb_access_commit(struct tdb_context *tdb, void *p)
-{
- struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
- enum TDB_ERROR ecode;
-
- if (hp) {
- hdr = *hp;
- if (hdr->convert)
- ecode = tdb_write_convert(tdb, hdr->off, p, hdr->len);
- else
- ecode = tdb_write(tdb, hdr->off, p, hdr->len);
- *hp = hdr->next;
- free(hdr);
- } else {
- tdb->direct_access--;
- ecode = TDB_SUCCESS;
- }
-
- return ecode;
-}
-
-static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len,
- bool write_mode)
-{
- enum TDB_ERROR ecode;
-
- if (unlikely(!tdb->file->map_ptr))
- return NULL;
-
- ecode = tdb_oob(tdb, off, len, false);
- if (unlikely(ecode != TDB_SUCCESS))
- return TDB_ERR_PTR(ecode);
- return (char *)tdb->file->map_ptr + off;
-}
-
-void tdb_inc_seqnum(struct tdb_context *tdb)
-{
- tdb_off_t seq;
-
- if (likely(!(tdb->flags & TDB_CONVERT))) {
- int64_t *direct;
-
- direct = tdb->io->direct(tdb,
- offsetof(struct tdb_header, seqnum),
- sizeof(*direct), true);
- if (likely(direct)) {
- /* Don't let it go negative, even briefly */
- if (unlikely((*direct) + 1) < 0)
- *direct = 0;
- (*direct)++;
- return;
- }
- }
-
- seq = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
- if (!TDB_OFF_IS_ERR(seq)) {
- seq++;
- if (unlikely((int64_t)seq < 0))
- seq = 0;
- tdb_write_off(tdb, offsetof(struct tdb_header, seqnum), seq);
- }
-}
-
-static const struct tdb_methods io_methods = {
- tdb_read,
- tdb_write,
- tdb_oob,
- tdb_expand_file,
- tdb_direct,
-};
-
-/*
- initialise the default methods table
-*/
-void tdb_io_init(struct tdb_context *tdb)
-{
- tdb->io = &io_methods;
-}
diff --git a/lib/tdb2/lock.c b/lib/tdb2/lock.c
deleted file mode 100644
index b0583546fb..0000000000
--- a/lib/tdb2/lock.c
+++ /dev/null
@@ -1,883 +0,0 @@
- /*
- 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 "private.h"
-#include <assert.h>
-#include <ccan/build_assert/build_assert.h>
-
-/* If we were threaded, we could wait for unlock, but we're not, so fail. */
-enum TDB_ERROR owner_conflict(struct tdb_context *tdb, const char *call)
-{
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "%s: lock owned by another tdb in this process.",
- call);
-}
-
-/* If we fork, we no longer really own locks. */
-bool check_lock_pid(struct tdb_context *tdb, const char *call, bool log)
-{
- /* No locks? No problem! */
- if (tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0) {
- return true;
- }
-
- /* No fork? No problem! */
- if (tdb->file->locker == getpid()) {
- return true;
- }
-
- if (log) {
- tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "%s: fork() detected after lock acquisition!"
- " (%u vs %u)", call, tdb->file->locker, getpid());
- }
- return false;
-}
-
-int tdb_fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag,
- void *unused)
-{
- struct flock fl;
- int ret;
-
- do {
- fl.l_type = rw;
- fl.l_whence = SEEK_SET;
- fl.l_start = off;
- fl.l_len = len;
-
- if (waitflag)
- ret = fcntl(fd, F_SETLKW, &fl);
- else
- ret = fcntl(fd, F_SETLK, &fl);
- } while (ret != 0 && errno == EINTR);
- return ret;
-}
-
-int tdb_fcntl_unlock(int fd, int rw, off_t off, off_t len, void *unused)
-{
- struct flock fl;
- int ret;
-
- do {
- fl.l_type = F_UNLCK;
- fl.l_whence = SEEK_SET;
- fl.l_start = off;
- fl.l_len = len;
-
- ret = fcntl(fd, F_SETLKW, &fl);
- } while (ret != 0 && errno == EINTR);
- return ret;
-}
-
-static int lock(struct tdb_context *tdb,
- int rw, off_t off, off_t len, bool waitflag)
-{
- int ret;
- if (tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0) {
- tdb->file->locker = getpid();
- }
-
- tdb->stats.lock_lowlevel++;
- ret = tdb->lock_fn(tdb->file->fd, rw, off, len, waitflag,
- tdb->lock_data);
- if (!waitflag) {
- tdb->stats.lock_nonblock++;
- if (ret != 0)
- tdb->stats.lock_nonblock_fail++;
- }
- return ret;
-}
-
-static int unlock(struct tdb_context *tdb, int rw, off_t off, off_t len)
-{
-#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!",
- (int)off, (int)len);
- abort();
- }
-
- fclose(locks);
-#endif
-
- return tdb->unlock_fn(tdb->file->fd, rw, off, len, tdb->lock_data);
-}
-
-/* a byte range locking function - return 0 on success
- this functions locks len bytes at the specified offset.
-
- note that a len of zero means lock to end of file
-*/
-static enum TDB_ERROR tdb_brlock(struct tdb_context *tdb,
- int rw_type, tdb_off_t offset, tdb_off_t len,
- enum tdb_lock_flags flags)
-{
- int ret;
-
- if (tdb->flags & TDB_NOLOCK) {
- return TDB_SUCCESS;
- }
-
- if (rw_type == F_WRLCK && (tdb->flags & TDB_RDONLY)) {
- return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
- "Write lock attempted on read-only database");
- }
-
- /* A 32 bit system cannot open a 64-bit file, but it could have
- * expanded since then: check here. */
- if ((size_t)(offset + len) != offset + len) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_brlock: lock on giant offset %llu",
- (long long)(offset + len));
- }
-
- ret = lock(tdb, rw_type, offset, len, flags & TDB_LOCK_WAIT);
- if (ret != 0) {
- /* Generic lock error. errno set by fcntl.
- * EAGAIN is an expected return from non-blocking
- * locks. */
- if (!(flags & TDB_LOCK_PROBE)
- && (errno != EAGAIN && errno != EINTR)) {
- tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_brlock failed (fd=%d) at"
- " offset %zu rw_type=%d flags=%d len=%zu:"
- " %s",
- tdb->file->fd, (size_t)offset, rw_type,
- flags, (size_t)len, strerror(errno));
- }
- return TDB_ERR_LOCK;
- }
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR tdb_brunlock(struct tdb_context *tdb,
- int rw_type, tdb_off_t offset, size_t len)
-{
- if (tdb->flags & TDB_NOLOCK) {
- return TDB_SUCCESS;
- }
-
- if (!check_lock_pid(tdb, "tdb_brunlock", true))
- return TDB_ERR_LOCK;
-
- if (unlock(tdb, rw_type, offset, len) == -1) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_brunlock failed (fd=%d) at offset %zu"
- " rw_type=%d len=%zu: %s",
- tdb->file->fd, (size_t)offset, rw_type,
- (size_t)len, strerror(errno));
- }
- return TDB_SUCCESS;
-}
-
-/*
- 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.
-*/
-enum TDB_ERROR tdb_allrecord_upgrade(struct tdb_context *tdb, off_t start)
-{
- int count = 1000;
-
- if (!check_lock_pid(tdb, "tdb_transaction_prepare_commit", true))
- return TDB_ERR_LOCK;
-
- if (tdb->file->allrecord_lock.count != 1) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_allrecord_upgrade failed:"
- " count %u too high",
- tdb->file->allrecord_lock.count);
- }
-
- if (tdb->file->allrecord_lock.off != 1) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_allrecord_upgrade failed:"
- " already upgraded?");
- }
-
- if (tdb->file->allrecord_lock.owner != tdb) {
- return owner_conflict(tdb, "tdb_allrecord_upgrade");
- }
-
- while (count--) {
- struct timeval tv;
- if (tdb_brlock(tdb, F_WRLCK, start, 0,
- TDB_LOCK_WAIT|TDB_LOCK_PROBE) == TDB_SUCCESS) {
- tdb->file->allrecord_lock.ltype = F_WRLCK;
- tdb->file->allrecord_lock.off = 0;
- return TDB_SUCCESS;
- }
- 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);
- }
-
- if (errno != EAGAIN && errno != EINTR)
- tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_allrecord_upgrade failed");
- return TDB_ERR_LOCK;
-}
-
-static struct tdb_lock *find_nestlock(struct tdb_context *tdb, tdb_off_t offset,
- const struct tdb_context *owner)
-{
- unsigned int i;
-
- for (i=0; i<tdb->file->num_lockrecs; i++) {
- if (tdb->file->lockrecs[i].off == offset) {
- if (owner && tdb->file->lockrecs[i].owner != owner)
- return NULL;
- return &tdb->file->lockrecs[i];
- }
- }
- return NULL;
-}
-
-enum TDB_ERROR tdb_lock_and_recover(struct tdb_context *tdb)
-{
- enum TDB_ERROR ecode;
-
- if (!check_lock_pid(tdb, "tdb_transaction_prepare_commit", true))
- return TDB_ERR_LOCK;
-
- ecode = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK,
- false);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- ecode = tdb_lock_open(tdb, F_WRLCK, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK);
- if (ecode != TDB_SUCCESS) {
- tdb_allrecord_unlock(tdb, F_WRLCK);
- return ecode;
- }
- ecode = tdb_transaction_recover(tdb);
- tdb_unlock_open(tdb, F_WRLCK);
- tdb_allrecord_unlock(tdb, F_WRLCK);
-
- return ecode;
-}
-
-/* lock an offset in the database. */
-static enum TDB_ERROR tdb_nest_lock(struct tdb_context *tdb,
- tdb_off_t offset, int ltype,
- enum tdb_lock_flags flags)
-{
- struct tdb_lock *new_lck;
- enum TDB_ERROR ecode;
-
- if (offset > (TDB_HASH_LOCK_START + TDB_HASH_LOCK_RANGE
- + tdb->file->map_size / 8)) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_nest_lock: invalid offset %zu ltype=%d",
- (size_t)offset, ltype);
- }
-
- if (tdb->flags & TDB_NOLOCK)
- return TDB_SUCCESS;
-
- if (!check_lock_pid(tdb, "tdb_nest_lock", true)) {
- return TDB_ERR_LOCK;
- }
-
- tdb->stats.locks++;
-
- new_lck = find_nestlock(tdb, offset, NULL);
- if (new_lck) {
- if (new_lck->owner != tdb) {
- return owner_conflict(tdb, "tdb_nest_lock");
- }
-
- if (new_lck->ltype == F_RDLCK && ltype == F_WRLCK) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_nest_lock:"
- " offset %zu has read lock",
- (size_t)offset);
- }
- /* Just increment the struct, posix locks don't stack. */
- new_lck->count++;
- return TDB_SUCCESS;
- }
-
-#if 0
- if (tdb->file->num_lockrecs
- && offset >= TDB_HASH_LOCK_START
- && offset < TDB_HASH_LOCK_START + TDB_HASH_LOCK_RANGE) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_nest_lock: already have a hash lock?");
- }
-#endif
-
- new_lck = (struct tdb_lock *)realloc(
- tdb->file->lockrecs,
- sizeof(*tdb->file->lockrecs) * (tdb->file->num_lockrecs+1));
- if (new_lck == NULL) {
- return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_nest_lock:"
- " unable to allocate %zu lock struct",
- tdb->file->num_lockrecs + 1);
- }
- tdb->file->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 */
- ecode = tdb_brlock(tdb, ltype, offset, 1, flags);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* First time we grab a lock, perhaps someone died in commit? */
- if (!(flags & TDB_LOCK_NOCHECK)
- && tdb->file->num_lockrecs == 0) {
- tdb_bool_err berr = tdb_needs_recovery(tdb);
- if (berr != false) {
- tdb_brunlock(tdb, ltype, offset, 1);
-
- if (berr < 0)
- return TDB_OFF_TO_ERR(berr);
- ecode = tdb_lock_and_recover(tdb);
- if (ecode == TDB_SUCCESS) {
- ecode = tdb_brlock(tdb, ltype, offset, 1,
- flags);
- }
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- }
- }
-
- tdb->file->lockrecs[tdb->file->num_lockrecs].owner = tdb;
- tdb->file->lockrecs[tdb->file->num_lockrecs].off = offset;
- tdb->file->lockrecs[tdb->file->num_lockrecs].count = 1;
- tdb->file->lockrecs[tdb->file->num_lockrecs].ltype = ltype;
- tdb->file->num_lockrecs++;
-
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR tdb_nest_unlock(struct tdb_context *tdb,
- tdb_off_t off, int ltype)
-{
- struct tdb_lock *lck;
- enum TDB_ERROR ecode;
-
- if (tdb->flags & TDB_NOLOCK)
- return TDB_SUCCESS;
-
- lck = find_nestlock(tdb, off, tdb);
- if ((lck == NULL) || (lck->count == 0)) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_nest_unlock: no lock for %zu",
- (size_t)off);
- }
-
- if (lck->count > 1) {
- lck->count--;
- return TDB_SUCCESS;
- }
-
- /*
- * 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.
- */
- ecode = tdb_brunlock(tdb, ltype, off, 1);
-
- /*
- * Shrink the array by overwriting the element just unlocked with the
- * last array element.
- */
- *lck = tdb->file->lockrecs[--tdb->file->num_lockrecs];
-
- return ecode;
-}
-
-/*
- get the transaction lock
- */
-enum TDB_ERROR tdb_transaction_lock(struct tdb_context *tdb, int ltype)
-{
- return tdb_nest_lock(tdb, TDB_TRANSACTION_LOCK, ltype, TDB_LOCK_WAIT);
-}
-
-/*
- release the transaction lock
- */
-void tdb_transaction_unlock(struct tdb_context *tdb, int ltype)
-{
- tdb_nest_unlock(tdb, TDB_TRANSACTION_LOCK, ltype);
-}
-
-/* We only need to lock individual bytes, but Linux merges consecutive locks
- * so we lock in contiguous ranges. */
-static enum TDB_ERROR tdb_lock_gradual(struct tdb_context *tdb,
- int ltype, enum tdb_lock_flags flags,
- tdb_off_t off, tdb_off_t len)
-{
- enum TDB_ERROR ecode;
- enum tdb_lock_flags nb_flags = (flags & ~TDB_LOCK_WAIT);
-
- if (len <= 1) {
- /* 0 would mean to end-of-file... */
- assert(len != 0);
- /* Single hash. Just do blocking lock. */
- return tdb_brlock(tdb, ltype, off, len, flags);
- }
-
- /* First we try non-blocking. */
- ecode = tdb_brlock(tdb, ltype, off, len, nb_flags);
- if (ecode != TDB_ERR_LOCK) {
- return ecode;
- }
-
- /* Try locking first half, then second. */
- ecode = tdb_lock_gradual(tdb, ltype, flags, off, len / 2);
- if (ecode != TDB_SUCCESS)
- return ecode;
-
- ecode = tdb_lock_gradual(tdb, ltype, flags,
- off + len / 2, len - len / 2);
- if (ecode != TDB_SUCCESS) {
- tdb_brunlock(tdb, ltype, off, len / 2);
- }
- return ecode;
-}
-
-/* lock/unlock entire database. It can only be upgradable if you have some
- * other way of guaranteeing exclusivity (ie. transaction write lock). */
-enum TDB_ERROR tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
- enum tdb_lock_flags flags, bool upgradable)
-{
- enum TDB_ERROR ecode;
- tdb_bool_err berr;
-
- if (tdb->flags & TDB_NOLOCK)
- return TDB_SUCCESS;
-
- if (!check_lock_pid(tdb, "tdb_allrecord_lock", true)) {
- return TDB_ERR_LOCK;
- }
-
- if (tdb->file->allrecord_lock.count) {
- if (tdb->file->allrecord_lock.owner != tdb) {
- return owner_conflict(tdb, "tdb_allrecord_lock");
- }
-
- if (ltype == F_RDLCK
- || tdb->file->allrecord_lock.ltype == F_WRLCK) {
- tdb->file->allrecord_lock.count++;
- return TDB_SUCCESS;
- }
-
- /* a global lock of a different type exists */
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "tdb_allrecord_lock: already have %s lock",
- tdb->file->allrecord_lock.ltype == F_RDLCK
- ? "read" : "write");
- }
-
- if (tdb_has_hash_locks(tdb)) {
- /* can't combine global and chain locks */
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "tdb_allrecord_lock:"
- " already have chain lock");
- }
-
- if (upgradable && ltype != F_RDLCK) {
- /* tdb error: you can't upgrade a write lock! */
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_allrecord_lock:"
- " can't upgrade a write lock");
- }
-
- tdb->stats.locks++;
-again:
- /* Lock hashes, gradually. */
- ecode = tdb_lock_gradual(tdb, ltype, flags, TDB_HASH_LOCK_START,
- TDB_HASH_LOCK_RANGE);
- if (ecode != TDB_SUCCESS)
- return ecode;
-
- /* Lock free tables: there to end of file. */
- ecode = tdb_brlock(tdb, ltype,
- TDB_HASH_LOCK_START + TDB_HASH_LOCK_RANGE,
- 0, flags);
- if (ecode != TDB_SUCCESS) {
- tdb_brunlock(tdb, ltype, TDB_HASH_LOCK_START,
- TDB_HASH_LOCK_RANGE);
- return ecode;
- }
-
- tdb->file->allrecord_lock.owner = tdb;
- tdb->file->allrecord_lock.count = 1;
- /* If it's upgradable, it's actually exclusive so we can treat
- * it as a write lock. */
- tdb->file->allrecord_lock.ltype = upgradable ? F_WRLCK : ltype;
- tdb->file->allrecord_lock.off = upgradable;
-
- /* Now check for needing recovery. */
- if (flags & TDB_LOCK_NOCHECK)
- return TDB_SUCCESS;
-
- berr = tdb_needs_recovery(tdb);
- if (likely(berr == false))
- return TDB_SUCCESS;
-
- tdb_allrecord_unlock(tdb, ltype);
- if (berr < 0)
- return TDB_OFF_TO_ERR(berr);
- ecode = tdb_lock_and_recover(tdb);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- goto again;
-}
-
-enum TDB_ERROR tdb_lock_open(struct tdb_context *tdb,
- int ltype, enum tdb_lock_flags flags)
-{
- return tdb_nest_lock(tdb, TDB_OPEN_LOCK, ltype, flags);
-}
-
-void tdb_unlock_open(struct tdb_context *tdb, int ltype)
-{
- tdb_nest_unlock(tdb, TDB_OPEN_LOCK, ltype);
-}
-
-bool tdb_has_open_lock(struct tdb_context *tdb)
-{
- return !(tdb->flags & TDB_NOLOCK)
- && find_nestlock(tdb, TDB_OPEN_LOCK, tdb) != NULL;
-}
-
-enum TDB_ERROR tdb_lock_expand(struct tdb_context *tdb, int ltype)
-{
- /* Lock doesn't protect data, so don't check (we recurse if we do!) */
- return tdb_nest_lock(tdb, TDB_EXPANSION_LOCK, ltype,
- TDB_LOCK_WAIT | TDB_LOCK_NOCHECK);
-}
-
-void tdb_unlock_expand(struct tdb_context *tdb, int ltype)
-{
- tdb_nest_unlock(tdb, TDB_EXPANSION_LOCK, ltype);
-}
-
-/* unlock entire db */
-void tdb_allrecord_unlock(struct tdb_context *tdb, int ltype)
-{
- if (tdb->flags & TDB_NOLOCK)
- return;
-
- if (tdb->file->allrecord_lock.count == 0) {
- tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "tdb_allrecord_unlock: not locked!");
- return;
- }
-
- if (tdb->file->allrecord_lock.owner != tdb) {
- tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "tdb_allrecord_unlock: not locked by us!");
- return;
- }
-
- /* Upgradable locks are marked as write locks. */
- if (tdb->file->allrecord_lock.ltype != ltype
- && (!tdb->file->allrecord_lock.off || ltype != F_RDLCK)) {
- tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_allrecord_unlock: have %s lock",
- tdb->file->allrecord_lock.ltype == F_RDLCK
- ? "read" : "write");
- return;
- }
-
- if (tdb->file->allrecord_lock.count > 1) {
- tdb->file->allrecord_lock.count--;
- return;
- }
-
- tdb->file->allrecord_lock.count = 0;
- tdb->file->allrecord_lock.ltype = 0;
-
- tdb_brunlock(tdb, ltype, TDB_HASH_LOCK_START, 0);
-}
-
-bool tdb_has_expansion_lock(struct tdb_context *tdb)
-{
- return find_nestlock(tdb, TDB_EXPANSION_LOCK, tdb) != NULL;
-}
-
-bool tdb_has_hash_locks(struct tdb_context *tdb)
-{
- unsigned int i;
-
- for (i=0; i<tdb->file->num_lockrecs; i++) {
- if (tdb->file->lockrecs[i].off >= TDB_HASH_LOCK_START
- && tdb->file->lockrecs[i].off < (TDB_HASH_LOCK_START
- + TDB_HASH_LOCK_RANGE))
- return true;
- }
- return false;
-}
-
-static bool tdb_has_free_lock(struct tdb_context *tdb)
-{
- unsigned int i;
-
- if (tdb->flags & TDB_NOLOCK)
- return false;
-
- for (i=0; i<tdb->file->num_lockrecs; i++) {
- if (tdb->file->lockrecs[i].off
- > TDB_HASH_LOCK_START + TDB_HASH_LOCK_RANGE)
- return true;
- }
- return false;
-}
-
-enum TDB_ERROR tdb_lock_hashes(struct tdb_context *tdb,
- tdb_off_t hash_lock,
- tdb_len_t hash_range,
- int ltype, enum tdb_lock_flags waitflag)
-{
- /* FIXME: Do this properly, using hlock_range */
- unsigned l = TDB_HASH_LOCK_START
- + (hash_lock >> (64 - TDB_HASH_LOCK_RANGE_BITS));
-
- /* a allrecord lock allows us to avoid per chain locks */
- if (tdb->file->allrecord_lock.count) {
- if (!check_lock_pid(tdb, "tdb_lock_hashes", true))
- return TDB_ERR_LOCK;
-
- if (tdb->file->allrecord_lock.owner != tdb)
- return owner_conflict(tdb, "tdb_lock_hashes");
- if (ltype == tdb->file->allrecord_lock.ltype
- || ltype == F_RDLCK) {
- return TDB_SUCCESS;
- }
-
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "tdb_lock_hashes:"
- " already have %s allrecordlock",
- tdb->file->allrecord_lock.ltype == F_RDLCK
- ? "read" : "write");
- }
-
- if (tdb_has_free_lock(tdb)) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_lock_hashes: already have free lock");
- }
-
- if (tdb_has_expansion_lock(tdb)) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_lock_hashes:"
- " already have expansion lock");
- }
-
- return tdb_nest_lock(tdb, l, ltype, waitflag);
-}
-
-enum TDB_ERROR tdb_unlock_hashes(struct tdb_context *tdb,
- tdb_off_t hash_lock,
- tdb_len_t hash_range, int ltype)
-{
- unsigned l = TDB_HASH_LOCK_START
- + (hash_lock >> (64 - TDB_HASH_LOCK_RANGE_BITS));
-
- if (tdb->flags & TDB_NOLOCK)
- return 0;
-
- /* a allrecord lock allows us to avoid per chain locks */
- if (tdb->file->allrecord_lock.count) {
- if (tdb->file->allrecord_lock.ltype == F_RDLCK
- && ltype == F_WRLCK) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_unlock_hashes RO allrecord!");
- }
- if (tdb->file->allrecord_lock.owner != tdb) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "tdb_unlock_hashes:"
- " not locked by us!");
- }
- return TDB_SUCCESS;
- }
-
- return tdb_nest_unlock(tdb, l, ltype);
-}
-
-/* Hash locks use TDB_HASH_LOCK_START + the next 30 bits.
- * Then we begin; bucket offsets are sizeof(tdb_len_t) apart, so we divide.
- * The result is that on 32 bit systems we don't use lock values > 2^31 on
- * files that are less than 4GB.
- */
-static tdb_off_t free_lock_off(tdb_off_t b_off)
-{
- return TDB_HASH_LOCK_START + TDB_HASH_LOCK_RANGE
- + b_off / sizeof(tdb_off_t);
-}
-
-enum TDB_ERROR tdb_lock_free_bucket(struct tdb_context *tdb, tdb_off_t b_off,
- enum tdb_lock_flags waitflag)
-{
- assert(b_off >= sizeof(struct tdb_header));
-
- if (tdb->flags & TDB_NOLOCK)
- return 0;
-
- /* a allrecord lock allows us to avoid per chain locks */
- if (tdb->file->allrecord_lock.count) {
- if (!check_lock_pid(tdb, "tdb_lock_free_bucket", true))
- return TDB_ERR_LOCK;
-
- if (tdb->file->allrecord_lock.owner != tdb) {
- return owner_conflict(tdb, "tdb_lock_free_bucket");
- }
-
- if (tdb->file->allrecord_lock.ltype == F_WRLCK)
- return 0;
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_lock_free_bucket with"
- " read-only allrecordlock!");
- }
-
-#if 0 /* FIXME */
- if (tdb_has_expansion_lock(tdb)) {
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
- "tdb_lock_free_bucket:"
- " already have expansion lock");
- }
-#endif
-
- return tdb_nest_lock(tdb, free_lock_off(b_off), F_WRLCK, waitflag);
-}
-
-void tdb_unlock_free_bucket(struct tdb_context *tdb, tdb_off_t b_off)
-{
- if (tdb->file->allrecord_lock.count)
- return;
-
- tdb_nest_unlock(tdb, free_lock_off(b_off), F_WRLCK);
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_lockall(struct tdb_context *tdb)
-{
- return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
-}
-
-_PUBLIC_ void tdb_unlockall(struct tdb_context *tdb)
-{
- tdb_allrecord_unlock(tdb, F_WRLCK);
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_lockall_read(struct tdb_context *tdb)
-{
- return tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
-}
-
-_PUBLIC_ void tdb_unlockall_read(struct tdb_context *tdb)
-{
- tdb_allrecord_unlock(tdb, F_RDLCK);
-}
-
-void tdb_lock_cleanup(struct tdb_context *tdb)
-{
- unsigned int i;
-
- /* We don't want to warn: they're allowed to close tdb after fork. */
- if (!check_lock_pid(tdb, "tdb_close", false))
- return;
-
- while (tdb->file->allrecord_lock.count
- && tdb->file->allrecord_lock.owner == tdb) {
- tdb_allrecord_unlock(tdb, tdb->file->allrecord_lock.ltype);
- }
-
- for (i=0; i<tdb->file->num_lockrecs; i++) {
- if (tdb->file->lockrecs[i].owner == tdb) {
- tdb_nest_unlock(tdb,
- tdb->file->lockrecs[i].off,
- tdb->file->lockrecs[i].ltype);
- i--;
- }
- }
-}
diff --git a/lib/tdb2/open.c b/lib/tdb2/open.c
deleted file mode 100644
index fab855b6b8..0000000000
--- a/lib/tdb2/open.c
+++ /dev/null
@@ -1,768 +0,0 @@
- /*
- Trivial Database 2: opening and closing TDBs
- 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 "private.h"
-#include <ccan/build_assert/build_assert.h>
-#include <assert.h>
-
-/* all tdbs, to detect double-opens (fcntl file don't nest!) */
-static struct tdb_context *tdbs = NULL;
-
-static struct tdb_file *find_file(dev_t device, ino_t ino)
-{
- struct tdb_context *i;
-
- for (i = tdbs; i; i = i->next) {
- if (i->file->device == device && i->file->inode == ino) {
- i->file->refcnt++;
- return i->file;
- }
- }
- return NULL;
-}
-
-static bool read_all(int fd, void *buf, size_t len)
-{
- while (len) {
- ssize_t ret;
- ret = read(fd, buf, len);
- if (ret < 0)
- return false;
- if (ret == 0) {
- /* ETOOSHORT? */
- errno = EWOULDBLOCK;
- return false;
- }
- buf = (char *)buf + ret;
- len -= ret;
- }
- return true;
-}
-
-static uint64_t random_number(struct tdb_context *tdb)
-{
- int fd;
- uint64_t ret = 0;
- struct timeval now;
-
- fd = open("/dev/urandom", O_RDONLY);
- if (fd >= 0) {
- if (read_all(fd, &ret, sizeof(ret))) {
- close(fd);
- return ret;
- }
- close(fd);
- }
- /* FIXME: Untested! Based on Wikipedia protocol description! */
- fd = open("/dev/egd-pool", O_RDWR);
- if (fd >= 0) {
- /* Command is 1, next byte is size we want to read. */
- char cmd[2] = { 1, sizeof(uint64_t) };
- if (write(fd, cmd, sizeof(cmd)) == sizeof(cmd)) {
- char reply[1 + sizeof(uint64_t)];
- int r = read(fd, reply, sizeof(reply));
- if (r > 1) {
- /* Copy at least some bytes. */
- memcpy(&ret, reply+1, r - 1);
- if (reply[0] == sizeof(uint64_t)
- && r == sizeof(reply)) {
- close(fd);
- return ret;
- }
- }
- }
- close(fd);
- }
-
- /* Fallback: pid and time. */
- gettimeofday(&now, NULL);
- ret = getpid() * 100132289ULL + now.tv_sec * 1000000ULL + now.tv_usec;
- tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
- "tdb_open: random from getpid and time");
- return ret;
-}
-
-static void tdb2_context_init(struct tdb_context *tdb)
-{
- /* Initialize the TDB2 fields here */
- tdb_io_init(tdb);
- tdb->direct_access = 0;
- tdb->transaction = NULL;
- tdb->access = NULL;
-}
-
-struct new_database {
- struct tdb_header hdr;
- struct tdb_freetable ftable;
-};
-
-/* initialise a new database */
-static enum TDB_ERROR tdb_new_database(struct tdb_context *tdb,
- struct tdb_attribute_seed *seed,
- struct tdb_header *hdr)
-{
- /* We make it up in memory, then write it out if not internal */
- struct new_database newdb;
- unsigned int magic_len;
- ssize_t rlen;
- enum TDB_ERROR ecode;
-
- /* Fill in the header */
- newdb.hdr.version = TDB_VERSION;
- if (seed)
- newdb.hdr.hash_seed = seed->seed;
- else
- newdb.hdr.hash_seed = random_number(tdb);
- newdb.hdr.hash_test = TDB_HASH_MAGIC;
- newdb.hdr.hash_test = tdb->hash_fn(&newdb.hdr.hash_test,
- sizeof(newdb.hdr.hash_test),
- newdb.hdr.hash_seed,
- tdb->hash_data);
- newdb.hdr.recovery = 0;
- newdb.hdr.features_used = newdb.hdr.features_offered = TDB_FEATURE_MASK;
- newdb.hdr.seqnum = 0;
- newdb.hdr.capabilities = 0;
- memset(newdb.hdr.reserved, 0, sizeof(newdb.hdr.reserved));
- /* Initial hashes are empty. */
- memset(newdb.hdr.hashtable, 0, sizeof(newdb.hdr.hashtable));
-
- /* Free is empty. */
- newdb.hdr.free_table = offsetof(struct new_database, ftable);
- memset(&newdb.ftable, 0, sizeof(newdb.ftable));
- ecode = set_header(NULL, &newdb.ftable.hdr, TDB_FTABLE_MAGIC, 0,
- sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
- sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
- 0);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* Magic food */
- memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
- strcpy(newdb.hdr.magic_food, TDB_MAGIC_FOOD);
-
- /* This creates an endian-converted database, as if read from disk */
- magic_len = sizeof(newdb.hdr.magic_food);
- tdb_convert(tdb,
- (char *)&newdb.hdr + magic_len, sizeof(newdb) - magic_len);
-
- *hdr = newdb.hdr;
-
- if (tdb->flags & TDB_INTERNAL) {
- tdb->file->map_size = sizeof(newdb);
- tdb->file->map_ptr = malloc(tdb->file->map_size);
- if (!tdb->file->map_ptr) {
- return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_new_database:"
- " failed to allocate");
- }
- memcpy(tdb->file->map_ptr, &newdb, tdb->file->map_size);
- return TDB_SUCCESS;
- }
- if (lseek(tdb->file->fd, 0, SEEK_SET) == -1) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_new_database:"
- " failed to seek: %s", strerror(errno));
- }
-
- if (ftruncate(tdb->file->fd, 0) == -1) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_new_database:"
- " failed to truncate: %s", strerror(errno));
- }
-
- rlen = write(tdb->file->fd, &newdb, sizeof(newdb));
- if (rlen != sizeof(newdb)) {
- if (rlen >= 0)
- errno = ENOSPC;
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_new_database: %zi writing header: %s",
- rlen, strerror(errno));
- }
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR tdb_new_file(struct tdb_context *tdb)
-{
- tdb->file = malloc(sizeof(*tdb->file));
- if (!tdb->file)
- return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_open: cannot alloc tdb_file structure");
- tdb->file->num_lockrecs = 0;
- tdb->file->lockrecs = NULL;
- tdb->file->allrecord_lock.count = 0;
- tdb->file->refcnt = 1;
- tdb->file->map_ptr = NULL;
- return TDB_SUCCESS;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_set_attribute(struct tdb_context *tdb,
- const union tdb_attribute *attr)
-{
- switch (attr->base.attr) {
- case TDB_ATTRIBUTE_LOG:
- tdb->log_fn = attr->log.fn;
- tdb->log_data = attr->log.data;
- break;
- case TDB_ATTRIBUTE_HASH:
- case TDB_ATTRIBUTE_SEED:
- case TDB_ATTRIBUTE_OPENHOOK:
- return tdb->last_error
- = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_set_attribute:"
- " cannot set %s after opening",
- attr->base.attr == TDB_ATTRIBUTE_HASH
- ? "TDB_ATTRIBUTE_HASH"
- : attr->base.attr == TDB_ATTRIBUTE_SEED
- ? "TDB_ATTRIBUTE_SEED"
- : "TDB_ATTRIBUTE_OPENHOOK");
- case TDB_ATTRIBUTE_STATS:
- return tdb->last_error
- = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_set_attribute:"
- " cannot set TDB_ATTRIBUTE_STATS");
- case TDB_ATTRIBUTE_FLOCK:
- tdb->lock_fn = attr->flock.lock;
- tdb->unlock_fn = attr->flock.unlock;
- tdb->lock_data = attr->flock.data;
- break;
- default:
- return tdb->last_error
- = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_set_attribute:"
- " unknown attribute type %u",
- attr->base.attr);
- }
- return TDB_SUCCESS;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_get_attribute(struct tdb_context *tdb,
- union tdb_attribute *attr)
-{
- switch (attr->base.attr) {
- case TDB_ATTRIBUTE_LOG:
- if (!tdb->log_fn)
- return tdb->last_error = TDB_ERR_NOEXIST;
- attr->log.fn = tdb->log_fn;
- attr->log.data = tdb->log_data;
- break;
- case TDB_ATTRIBUTE_HASH:
- attr->hash.fn = tdb->hash_fn;
- attr->hash.data = tdb->hash_data;
- break;
- case TDB_ATTRIBUTE_SEED:
- attr->seed.seed = tdb->hash_seed;
- break;
- case TDB_ATTRIBUTE_OPENHOOK:
- if (!tdb->openhook)
- return tdb->last_error = TDB_ERR_NOEXIST;
- attr->openhook.fn = tdb->openhook;
- attr->openhook.data = tdb->openhook_data;
- break;
- case TDB_ATTRIBUTE_STATS: {
- size_t size = attr->stats.size;
- if (size > tdb->stats.size)
- size = tdb->stats.size;
- memcpy(&attr->stats, &tdb->stats, size);
- break;
- }
- case TDB_ATTRIBUTE_FLOCK:
- attr->flock.lock = tdb->lock_fn;
- attr->flock.unlock = tdb->unlock_fn;
- attr->flock.data = tdb->lock_data;
- break;
- default:
- return tdb->last_error
- = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_get_attribute:"
- " unknown attribute type %u",
- attr->base.attr);
- }
- attr->base.next = NULL;
- return TDB_SUCCESS;
-}
-
-_PUBLIC_ void tdb_unset_attribute(struct tdb_context *tdb,
- enum tdb_attribute_type type)
-{
- switch (type) {
- case TDB_ATTRIBUTE_LOG:
- tdb->log_fn = NULL;
- break;
- case TDB_ATTRIBUTE_OPENHOOK:
- tdb->openhook = NULL;
- break;
- case TDB_ATTRIBUTE_HASH:
- case TDB_ATTRIBUTE_SEED:
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_unset_attribute: cannot unset %s after opening",
- type == TDB_ATTRIBUTE_HASH
- ? "TDB_ATTRIBUTE_HASH"
- : "TDB_ATTRIBUTE_SEED");
- break;
- case TDB_ATTRIBUTE_STATS:
- tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_unset_attribute:"
- "cannot unset TDB_ATTRIBUTE_STATS");
- break;
- case TDB_ATTRIBUTE_FLOCK:
- tdb->lock_fn = tdb_fcntl_lock;
- tdb->unlock_fn = tdb_fcntl_unlock;
- break;
- default:
- tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_unset_attribute: unknown attribute type %u",
- type);
- }
-}
-
-/* The top three bits of the capability tell us whether it matters. */
-enum TDB_ERROR unknown_capability(struct tdb_context *tdb, const char *caller,
- tdb_off_t type)
-{
- if (type & TDB_CAP_NOOPEN) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "%s: file has unknown capability %llu",
- caller, type & TDB_CAP_NOOPEN);
- }
-
- if ((type & TDB_CAP_NOWRITE) && !(tdb->flags & TDB_RDONLY)) {
- return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_ERROR,
- "%s: file has unknown capability %llu"
- " (cannot write to it)",
- caller, type & TDB_CAP_NOOPEN);
- }
-
- if (type & TDB_CAP_NOCHECK) {
- tdb->flags |= TDB_CANT_CHECK;
- }
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR capabilities_ok(struct tdb_context *tdb,
- tdb_off_t capabilities)
-{
- tdb_off_t off, next;
- enum TDB_ERROR ecode = TDB_SUCCESS;
- const struct tdb_capability *cap;
-
- /* Check capability list. */
- for (off = capabilities; off && ecode == TDB_SUCCESS; off = next) {
- cap = tdb_access_read(tdb, off, sizeof(*cap), true);
- if (TDB_PTR_IS_ERR(cap)) {
- return TDB_PTR_ERR(cap);
- }
-
- switch (cap->type & TDB_CAP_TYPE_MASK) {
- /* We don't understand any capabilities (yet). */
- default:
- ecode = unknown_capability(tdb, "tdb_open", cap->type);
- }
- next = cap->next;
- tdb_access_release(tdb, cap);
- }
- return ecode;
-}
-
-_PUBLIC_ struct tdb_context *tdb_open(const char *name, int tdb_flags,
- int open_flags, mode_t mode,
- union tdb_attribute *attr)
-{
- struct tdb_context *tdb;
- struct stat st;
- int saved_errno = 0;
- uint64_t hash_test;
- unsigned v;
- ssize_t rlen;
- struct tdb_header hdr;
- struct tdb_attribute_seed *seed = NULL;
- tdb_bool_err berr;
- enum TDB_ERROR ecode;
- int openlock;
-
- tdb = malloc(sizeof(*tdb) + (name ? strlen(name) + 1 : 0));
- if (!tdb) {
- /* Can't log this */
- errno = ENOMEM;
- return NULL;
- }
- /* Set name immediately for logging functions. */
- if (name) {
- tdb->name = strcpy((char *)(tdb + 1), name);
- } else {
- tdb->name = NULL;
- }
- tdb->flags = tdb_flags;
- tdb->log_fn = NULL;
- tdb->open_flags = open_flags;
- tdb->last_error = TDB_SUCCESS;
- tdb->file = NULL;
- tdb->openhook = NULL;
- tdb->lock_fn = tdb_fcntl_lock;
- tdb->unlock_fn = tdb_fcntl_unlock;
- tdb->hash_fn = tdb_jenkins_hash;
- memset(&tdb->stats, 0, sizeof(tdb->stats));
- tdb->stats.base.attr = TDB_ATTRIBUTE_STATS;
- tdb->stats.size = sizeof(tdb->stats);
-
- while (attr) {
- switch (attr->base.attr) {
- case TDB_ATTRIBUTE_HASH:
- tdb->hash_fn = attr->hash.fn;
- tdb->hash_data = attr->hash.data;
- break;
- case TDB_ATTRIBUTE_SEED:
- seed = &attr->seed;
- break;
- case TDB_ATTRIBUTE_OPENHOOK:
- tdb->openhook = attr->openhook.fn;
- tdb->openhook_data = attr->openhook.data;
- break;
- default:
- /* These are set as normal. */
- ecode = tdb_set_attribute(tdb, attr);
- if (ecode != TDB_SUCCESS)
- goto fail;
- }
- attr = attr->base.next;
- }
-
- if (tdb_flags & ~(TDB_INTERNAL | TDB_NOLOCK | TDB_NOMMAP | TDB_CONVERT
- | TDB_NOSYNC | TDB_SEQNUM | TDB_ALLOW_NESTING
- | TDB_RDONLY)) {
- ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_open: unknown flags %u", tdb_flags);
- goto fail;
- }
-
- if (seed) {
- if (!(tdb_flags & TDB_INTERNAL) && !(open_flags & O_CREAT)) {
- ecode = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_open:"
- " cannot set TDB_ATTRIBUTE_SEED"
- " without O_CREAT.");
- goto fail;
- }
- }
-
- if ((open_flags & O_ACCMODE) == O_WRONLY) {
- ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_open: can't open tdb %s write-only",
- name);
- goto fail;
- }
-
- if ((open_flags & O_ACCMODE) == O_RDONLY) {
- openlock = F_RDLCK;
- tdb->flags |= TDB_RDONLY;
- } else {
- if (tdb_flags & TDB_RDONLY) {
- ecode = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_open: can't use TDB_RDONLY"
- " without O_RDONLY");
- goto fail;
- }
- openlock = F_WRLCK;
- }
-
- /* internal databases don't need any of the rest. */
- if (tdb->flags & TDB_INTERNAL) {
- tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
- ecode = tdb_new_file(tdb);
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- tdb->file->fd = -1;
- ecode = tdb_new_database(tdb, seed, &hdr);
- if (ecode == TDB_SUCCESS) {
- tdb_convert(tdb, &hdr.hash_seed,
- sizeof(hdr.hash_seed));
- tdb->hash_seed = hdr.hash_seed;
- tdb2_context_init(tdb);
- tdb_ftable_init(tdb);
- }
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- return tdb;
- }
-
- if (stat(name, &st) != -1)
- tdb->file = find_file(st.st_dev, st.st_ino);
-
- if (!tdb->file) {
- int fd;
-
- if ((fd = open(name, open_flags, mode)) == -1) {
- /* errno set by open(2) */
- saved_errno = errno;
- tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open: could not open file %s: %s",
- name, strerror(errno));
- goto fail_errno;
- }
-
- /* on exec, don't inherit the fd */
- v = fcntl(fd, F_GETFD, 0);
- fcntl(fd, F_SETFD, v | FD_CLOEXEC);
-
- if (fstat(fd, &st) == -1) {
- saved_errno = errno;
- tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open: could not stat open %s: %s",
- name, strerror(errno));
- close(fd);
- goto fail_errno;
- }
-
- ecode = tdb_new_file(tdb);
- if (ecode != TDB_SUCCESS) {
- close(fd);
- goto fail;
- }
-
- tdb->file->fd = fd;
- tdb->file->device = st.st_dev;
- tdb->file->inode = st.st_ino;
- tdb->file->map_ptr = NULL;
- tdb->file->map_size = 0;
- }
-
- /* ensure there is only one process initialising at once */
- ecode = tdb_lock_open(tdb, openlock, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK);
- if (ecode != TDB_SUCCESS) {
- saved_errno = errno;
- goto fail_errno;
- }
-
- /* call their open hook if they gave us one. */
- if (tdb->openhook) {
- ecode = tdb->openhook(tdb->file->fd, tdb->openhook_data);
- if (ecode != TDB_SUCCESS) {
- tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_open: open hook failed");
- goto fail;
- }
- open_flags |= O_CREAT;
- }
-
- /* If they used O_TRUNC, read will return 0. */
- rlen = pread(tdb->file->fd, &hdr, sizeof(hdr), 0);
- if (rlen == 0 && (open_flags & O_CREAT)) {
- ecode = tdb_new_database(tdb, seed, &hdr);
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- } else if (rlen < 0) {
- ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open: error %s reading %s",
- strerror(errno), name);
- goto fail;
- } else if (rlen < sizeof(hdr)
- || strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) {
- ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open: %s is not a tdb2 file", name);
- goto fail;
- }
-
- if (hdr.version != TDB_VERSION) {
- if (hdr.version == bswap_64(TDB_VERSION))
- tdb->flags |= TDB_CONVERT;
- else {
- /* wrong version */
- ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open:"
- " %s is unknown version 0x%llx",
- name, (long long)hdr.version);
- goto fail;
- }
- } else if (tdb->flags & TDB_CONVERT) {
- ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open:"
- " %s does not need TDB_CONVERT",
- name);
- goto fail;
- }
-
- tdb2_context_init(tdb);
-
- tdb_convert(tdb, &hdr, sizeof(hdr));
- tdb->hash_seed = hdr.hash_seed;
- hash_test = TDB_HASH_MAGIC;
- hash_test = tdb_hash(tdb, &hash_test, sizeof(hash_test));
- if (hdr.hash_test != hash_test) {
- /* wrong hash variant */
- ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open:"
- " %s uses a different hash function",
- name);
- goto fail;
- }
-
- ecode = capabilities_ok(tdb, hdr.capabilities);
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
-
- /* Clear any features we don't understand. */
- if ((open_flags & O_ACCMODE) != O_RDONLY) {
- hdr.features_used &= TDB_FEATURE_MASK;
- ecode = tdb_write_convert(tdb, offsetof(struct tdb_header,
- features_used),
- &hdr.features_used,
- sizeof(hdr.features_used));
- if (ecode != TDB_SUCCESS)
- goto fail;
- }
-
- tdb_unlock_open(tdb, openlock);
-
- /* This makes sure we have current map_size and mmap. */
- ecode = tdb->io->oob(tdb, tdb->file->map_size, 1, true);
- if (unlikely(ecode != TDB_SUCCESS))
- goto fail;
-
- /* Now it's fully formed, recover if necessary. */
- berr = tdb_needs_recovery(tdb);
- if (unlikely(berr != false)) {
- if (berr < 0) {
- ecode = TDB_OFF_TO_ERR(berr);
- goto fail;
- }
- ecode = tdb_lock_and_recover(tdb);
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
- }
-
- ecode = tdb_ftable_init(tdb);
- if (ecode != TDB_SUCCESS) {
- goto fail;
- }
-
- tdb->next = tdbs;
- tdbs = tdb;
- return tdb;
-
- fail:
- /* Map ecode to some logical errno. */
- switch (TDB_ERR_TO_OFF(ecode)) {
- case TDB_ERR_TO_OFF(TDB_ERR_CORRUPT):
- case TDB_ERR_TO_OFF(TDB_ERR_IO):
- saved_errno = EIO;
- break;
- case TDB_ERR_TO_OFF(TDB_ERR_LOCK):
- saved_errno = EWOULDBLOCK;
- break;
- case TDB_ERR_TO_OFF(TDB_ERR_OOM):
- saved_errno = ENOMEM;
- break;
- case TDB_ERR_TO_OFF(TDB_ERR_EINVAL):
- saved_errno = EINVAL;
- break;
- default:
- saved_errno = EINVAL;
- break;
- }
-
-fail_errno:
-#ifdef TDB_TRACE
- close(tdb->tracefd);
-#endif
- if (tdb->file) {
- tdb_lock_cleanup(tdb);
- if (--tdb->file->refcnt == 0) {
- assert(tdb->file->num_lockrecs == 0);
- if (tdb->file->map_ptr) {
- if (tdb->flags & TDB_INTERNAL) {
- free(tdb->file->map_ptr);
- } else
- tdb_munmap(tdb->file);
- }
- if (close(tdb->file->fd) != 0)
- tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_open: failed to close tdb fd"
- " on error: %s", strerror(errno));
- free(tdb->file->lockrecs);
- free(tdb->file);
- }
- }
-
- free(tdb);
- errno = saved_errno;
- return NULL;
-}
-
-_PUBLIC_ int tdb_close(struct tdb_context *tdb)
-{
- int ret = 0;
- struct tdb_context **i;
-
- tdb_trace(tdb, "tdb_close");
-
- if (tdb->transaction) {
- tdb_transaction_cancel(tdb);
- }
-
- if (tdb->file->map_ptr) {
- if (tdb->flags & TDB_INTERNAL)
- free(tdb->file->map_ptr);
- else
- tdb_munmap(tdb->file);
- }
- if (tdb->file) {
- tdb_lock_cleanup(tdb);
- if (--tdb->file->refcnt == 0) {
- ret = close(tdb->file->fd);
- free(tdb->file->lockrecs);
- free(tdb->file);
- }
- }
-
- /* Remove from tdbs list */
- for (i = &tdbs; *i; i = &(*i)->next) {
- if (*i == tdb) {
- *i = tdb->next;
- break;
- }
- }
-
-#ifdef TDB_TRACE
- close(tdb->tracefd);
-#endif
- free(tdb);
-
- return ret;
-}
-
-_PUBLIC_ void tdb_foreach_(int (*fn)(struct tdb_context *, void *), void *p)
-{
- struct tdb_context *i;
-
- for (i = tdbs; i; i = i->next) {
- if (fn(i, p) != 0)
- break;
- }
-}
diff --git a/lib/tdb2/private.h b/lib/tdb2/private.h
deleted file mode 100644
index 8c917a70b2..0000000000
--- a/lib/tdb2/private.h
+++ /dev/null
@@ -1,657 +0,0 @@
-#ifndef TDB_PRIVATE_H
-#define TDB_PRIVATE_H
- /*
- Trivial Database 2: private types and prototypes
- 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 "config.h"
-#ifndef HAVE_CCAN
-#error You need ccan to build tdb2!
-#endif
-#include "tdb2.h"
-#include <ccan/compiler/compiler.h>
-#include <ccan/likely/likely.h>
-#include <ccan/endian/endian.h>
-
-#ifdef HAVE_LIBREPLACE
-#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 <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <utime.h>
-#include <unistd.h>
-#endif
-
-#ifndef TEST_IT
-#define TEST_IT(cond)
-#endif
-
-/* #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
-
-typedef uint64_t tdb_len_t;
-typedef uint64_t tdb_off_t;
-
-#define TDB_MAGIC_FOOD "TDB file\n"
-#define TDB_VERSION ((uint64_t)(0x26011967 + 7))
-#define TDB_USED_MAGIC ((uint64_t)0x1999)
-#define TDB_HTABLE_MAGIC ((uint64_t)0x1888)
-#define TDB_CHAIN_MAGIC ((uint64_t)0x1777)
-#define TDB_FTABLE_MAGIC ((uint64_t)0x1666)
-#define TDB_CAP_MAGIC ((uint64_t)0x1555)
-#define TDB_FREE_MAGIC ((uint64_t)0xFE)
-#define TDB_HASH_MAGIC (0xA1ABE11A01092008ULL)
-#define TDB_RECOVERY_MAGIC (0xf53bc0e7ad124589ULL)
-#define TDB_RECOVERY_INVALID_MAGIC (0x0ULL)
-
-/* Capability bits. */
-#define TDB_CAP_TYPE_MASK 0x1FFFFFFFFFFFFFFFULL
-#define TDB_CAP_NOCHECK 0x8000000000000000ULL
-#define TDB_CAP_NOWRITE 0x4000000000000000ULL
-#define TDB_CAP_NOOPEN 0x2000000000000000ULL
-
-#define TDB_OFF_IS_ERR(off) unlikely(off >= (tdb_off_t)(long)TDB_ERR_LAST)
-#define TDB_OFF_TO_ERR(off) ((enum TDB_ERROR)(long)(off))
-#define TDB_ERR_TO_OFF(ecode) ((tdb_off_t)(long)(ecode))
-
-/* Packing errors into pointers and v.v. */
-#define TDB_PTR_IS_ERR(ptr) \
- unlikely((unsigned long)(ptr) >= (unsigned long)TDB_ERR_LAST)
-#define TDB_PTR_ERR(p) ((enum TDB_ERROR)(long)(p))
-#define TDB_ERR_PTR(err) ((void *)(long)(err))
-
-/* Common case of returning true, false or -ve error. */
-typedef int tdb_bool_err;
-
-/* Prevent others from opening the file. */
-#define TDB_OPEN_LOCK 0
-/* Expanding file. */
-#define TDB_EXPANSION_LOCK 2
-/* Doing a transaction. */
-#define TDB_TRANSACTION_LOCK 8
-/* Hash chain locks. */
-#define TDB_HASH_LOCK_START 64
-
-/* Range for hash locks. */
-#define TDB_HASH_LOCK_RANGE_BITS 30
-#define TDB_HASH_LOCK_RANGE (1 << TDB_HASH_LOCK_RANGE_BITS)
-
-/* We have 1024 entries in the top level. */
-#define TDB_TOPLEVEL_HASH_BITS 10
-/* And 64 entries in each sub-level: thus 64 bits exactly after 9 levels. */
-#define TDB_SUBLEVEL_HASH_BITS 6
-/* And 8 entries in each group, ie 8 groups per sublevel. */
-#define TDB_HASH_GROUP_BITS 3
-/* This is currently 10: beyond this we chain. */
-#define TDB_MAX_LEVELS (1+(64-TDB_TOPLEVEL_HASH_BITS) / TDB_SUBLEVEL_HASH_BITS)
-
-/* Extend file by least 100 times larger than needed. */
-#define TDB_EXTENSION_FACTOR 100
-
-/* We steal bits from the offsets to store hash info. */
-#define TDB_OFF_HASH_GROUP_MASK ((1ULL << TDB_HASH_GROUP_BITS) - 1)
-/* We steal this many upper bits, giving a maximum offset of 64 exabytes. */
-#define TDB_OFF_UPPER_STEAL 8
-#define TDB_OFF_UPPER_STEAL_EXTRA 7
-/* The bit number where we store extra hash bits. */
-#define TDB_OFF_HASH_EXTRA_BIT 57
-#define TDB_OFF_UPPER_STEAL_SUBHASH_BIT 56
-
-/* Additional features we understand. Currently: none. */
-#define TDB_FEATURE_MASK ((uint64_t)0)
-
-/* The bit number where we store the extra hash bits. */
-/* Convenience mask to get actual offset. */
-#define TDB_OFF_MASK \
- (((1ULL << (64 - TDB_OFF_UPPER_STEAL)) - 1) - TDB_OFF_HASH_GROUP_MASK)
-
-/* How many buckets in a free list: see size_to_bucket(). */
-#define TDB_FREE_BUCKETS (64 - TDB_OFF_UPPER_STEAL)
-
-/* We have to be able to fit a free record here. */
-#define TDB_MIN_DATA_LEN \
- (sizeof(struct tdb_free_record) - sizeof(struct tdb_used_record))
-
-/* Indicates this entry is not on an flist (can happen during coalescing) */
-#define TDB_FTABLE_NONE ((1ULL << TDB_OFF_UPPER_STEAL) - 1)
-
-struct tdb_used_record {
- /* For on-disk compatibility, we avoid bitfields:
- magic: 16, (highest)
- key_len_bits: 5,
- extra_padding: 32
- hash_bits: 11
- */
- uint64_t magic_and_meta;
- /* The bottom key_len_bits*2 are key length, rest is data length. */
- uint64_t key_and_data_len;
-};
-
-static inline unsigned rec_key_bits(const struct tdb_used_record *r)
-{
- return ((r->magic_and_meta >> 43) & ((1 << 5)-1)) * 2;
-}
-
-static inline uint64_t rec_key_length(const struct tdb_used_record *r)
-{
- return r->key_and_data_len & ((1ULL << rec_key_bits(r)) - 1);
-}
-
-static inline uint64_t rec_data_length(const struct tdb_used_record *r)
-{
- return r->key_and_data_len >> rec_key_bits(r);
-}
-
-static inline uint64_t rec_extra_padding(const struct tdb_used_record *r)
-{
- return (r->magic_and_meta >> 11) & 0xFFFFFFFF;
-}
-
-static inline uint32_t rec_hash(const struct tdb_used_record *r)
-{
- return r->magic_and_meta & ((1 << 11) - 1);
-}
-
-static inline uint16_t rec_magic(const struct tdb_used_record *r)
-{
- return (r->magic_and_meta >> 48);
-}
-
-struct tdb_free_record {
- uint64_t magic_and_prev; /* TDB_OFF_UPPER_STEAL bits magic, then prev */
- uint64_t ftable_and_len; /* Len not counting these two fields. */
- /* This is why the minimum record size is 8 bytes. */
- uint64_t next;
-};
-
-static inline uint64_t frec_prev(const struct tdb_free_record *f)
-{
- return f->magic_and_prev & ((1ULL << (64 - TDB_OFF_UPPER_STEAL)) - 1);
-}
-
-static inline uint64_t frec_magic(const struct tdb_free_record *f)
-{
- return f->magic_and_prev >> (64 - TDB_OFF_UPPER_STEAL);
-}
-
-static inline uint64_t frec_len(const struct tdb_free_record *f)
-{
- return f->ftable_and_len & ((1ULL << (64 - TDB_OFF_UPPER_STEAL))-1);
-}
-
-static inline unsigned frec_ftable(const struct tdb_free_record *f)
-{
- return f->ftable_and_len >> (64 - TDB_OFF_UPPER_STEAL);
-}
-
-struct tdb_recovery_record {
- uint64_t magic;
- /* Length of record (add this header to get total length). */
- uint64_t max_len;
- /* Length used. */
- uint64_t len;
- /* Old length of file before transaction. */
- uint64_t eof;
-};
-
-/* If we bottom out of the subhashes, we chain. */
-struct tdb_chain {
- tdb_off_t rec[1 << TDB_HASH_GROUP_BITS];
- tdb_off_t next;
-};
-
-/* this is stored at the front of every database */
-struct tdb_header {
- char magic_food[64]; /* for /etc/magic */
- /* FIXME: Make me 32 bit? */
- uint64_t version; /* version of the code */
- uint64_t hash_test; /* result of hashing HASH_MAGIC. */
- uint64_t hash_seed; /* "random" seed written at creation time. */
- tdb_off_t free_table; /* (First) free table. */
- tdb_off_t recovery; /* Transaction recovery area. */
-
- uint64_t features_used; /* Features all writers understand */
- uint64_t features_offered; /* Features offered */
-
- uint64_t seqnum; /* Sequence number for TDB_SEQNUM */
-
- tdb_off_t capabilities; /* Optional linked list of capabilities. */
- tdb_off_t reserved[22];
-
- /* Top level hash table. */
- tdb_off_t hashtable[1ULL << TDB_TOPLEVEL_HASH_BITS];
-};
-
-struct tdb_freetable {
- struct tdb_used_record hdr;
- tdb_off_t next;
- tdb_off_t buckets[TDB_FREE_BUCKETS];
-};
-
-struct tdb_capability {
- struct tdb_used_record hdr;
- tdb_off_t type;
- tdb_off_t next;
- /* ... */
-};
-
-/* Information about a particular (locked) hash entry. */
-struct hash_info {
- /* Full hash value of entry. */
- uint64_t h;
- /* Start and length of lock acquired. */
- tdb_off_t hlock_start;
- tdb_len_t hlock_range;
- /* Start of hash group. */
- tdb_off_t group_start;
- /* Bucket we belong in. */
- unsigned int home_bucket;
- /* Bucket we (or an empty space) were found in. */
- unsigned int found_bucket;
- /* How many bits of the hash are already used. */
- unsigned int hash_used;
- /* Current working group. */
- tdb_off_t group[1 << TDB_HASH_GROUP_BITS];
-};
-
-struct traverse_info {
- struct traverse_level {
- tdb_off_t hashtable;
- /* We ignore groups here, and treat it as a big array. */
- unsigned entry;
- unsigned int total_buckets;
- } levels[TDB_MAX_LEVELS + 1];
- unsigned int num_levels;
- unsigned int toplevel_group;
- /* This makes delete-everything-inside-traverse work as expected. */
- tdb_off_t prev;
-};
-
-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 check for recovery (used by recovery code). */
- TDB_LOCK_NOCHECK = 4,
-};
-
-struct tdb_lock {
- struct tdb_context *owner;
- off_t off;
- uint32_t count;
- uint32_t ltype;
-};
-
-/* This is only needed for tdb_access_commit, but used everywhere to
- * simplify. */
-struct tdb_access_hdr {
- struct tdb_access_hdr *next;
- tdb_off_t off;
- tdb_len_t len;
- bool convert;
-};
-
-struct tdb_file {
- /* How many are sharing us? */
- unsigned int refcnt;
-
- /* Mmap (if any), or malloc (for TDB_INTERNAL). */
- void *map_ptr;
-
- /* How much space has been mapped (<= current file size) */
- tdb_len_t map_size;
-
- /* The file descriptor (-1 for TDB_INTERNAL). */
- int fd;
-
- /* Lock information */
- pid_t locker;
- struct tdb_lock allrecord_lock;
- size_t num_lockrecs;
- struct tdb_lock *lockrecs;
-
- /* Identity of this file. */
- dev_t device;
- ino_t inode;
-};
-
-struct tdb_methods {
- enum TDB_ERROR (*tread)(struct tdb_context *, tdb_off_t, void *,
- tdb_len_t);
- enum TDB_ERROR (*twrite)(struct tdb_context *, tdb_off_t, const void *,
- tdb_len_t);
- enum TDB_ERROR (*oob)(struct tdb_context *, tdb_off_t, tdb_len_t, bool);
- enum TDB_ERROR (*expand_file)(struct tdb_context *, tdb_len_t);
- void *(*direct)(struct tdb_context *, tdb_off_t, size_t, bool);
-};
-
-/*
- internal prototypes
-*/
-/* hash.c: */
-uint64_t tdb_jenkins_hash(const void *key, size_t length, uint64_t seed,
- void *unused);
-
-enum TDB_ERROR first_in_hash(struct tdb_context *tdb,
- struct traverse_info *tinfo,
- TDB_DATA *kbuf, size_t *dlen);
-
-enum TDB_ERROR next_in_hash(struct tdb_context *tdb,
- struct traverse_info *tinfo,
- TDB_DATA *kbuf, size_t *dlen);
-
-/* Hash random memory. */
-uint64_t tdb_hash(struct tdb_context *tdb, const void *ptr, size_t len);
-
-/* Hash on disk. */
-uint64_t hash_record(struct tdb_context *tdb, tdb_off_t off);
-
-/* Find and lock a hash entry (or where it would be). */
-tdb_off_t find_and_lock(struct tdb_context *tdb,
- struct tdb_data key,
- int ltype,
- struct hash_info *h,
- struct tdb_used_record *rec,
- struct traverse_info *tinfo);
-
-enum TDB_ERROR replace_in_hash(struct tdb_context *tdb,
- struct hash_info *h,
- tdb_off_t new_off);
-
-enum TDB_ERROR add_to_hash(struct tdb_context *tdb, struct hash_info *h,
- tdb_off_t new_off);
-
-enum TDB_ERROR delete_from_hash(struct tdb_context *tdb, struct hash_info *h);
-
-/* For tdb_check */
-bool is_subhash(tdb_off_t val);
-enum TDB_ERROR unknown_capability(struct tdb_context *tdb, const char *caller,
- tdb_off_t type);
-
-/* free.c: */
-enum TDB_ERROR tdb_ftable_init(struct tdb_context *tdb);
-
-/* check.c needs these to iterate through free lists. */
-tdb_off_t first_ftable(struct tdb_context *tdb);
-tdb_off_t next_ftable(struct tdb_context *tdb, tdb_off_t ftable);
-
-/* This returns space or -ve error number. */
-tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen,
- uint64_t hash, unsigned magic, bool growing);
-
-/* Put this record in a free list. */
-enum TDB_ERROR add_free_record(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len_with_header,
- enum tdb_lock_flags waitflag,
- bool coalesce_ok);
-
-/* Set up header for a used/ftable/htable/chain/capability record. */
-enum TDB_ERROR set_header(struct tdb_context *tdb,
- struct tdb_used_record *rec,
- unsigned magic, uint64_t keylen, uint64_t datalen,
- uint64_t actuallen, unsigned hashlow);
-
-/* Used by tdb_check to verify. */
-unsigned int size_to_bucket(tdb_len_t data_len);
-tdb_off_t bucket_off(tdb_off_t ftable_off, unsigned bucket);
-
-/* Used by tdb_summary */
-tdb_off_t dead_space(struct tdb_context *tdb, tdb_off_t off);
-
-/* Adjust expansion, used by create_recovery_area */
-tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size);
-
-/* io.c: */
-/* Initialize tdb->methods. */
-void tdb_io_init(struct tdb_context *tdb);
-
-/* Convert endian of the buffer if required. */
-void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size);
-
-/* Unmap and try to map the tdb. */
-void tdb_munmap(struct tdb_file *file);
-enum TDB_ERROR tdb_mmap(struct tdb_context *tdb);
-
-/* Either alloc a copy, or give direct access. Release frees or noop. */
-const void *tdb_access_read(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len, bool convert);
-void *tdb_access_write(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len, bool convert);
-
-/* Release result of tdb_access_read/write. */
-void tdb_access_release(struct tdb_context *tdb, const void *p);
-/* Commit result of tdb_acces_write. */
-enum TDB_ERROR tdb_access_commit(struct tdb_context *tdb, void *p);
-
-/* Convenience routine to get an offset. */
-tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off);
-
-/* Write an offset at an offset. */
-enum TDB_ERROR tdb_write_off(struct tdb_context *tdb, tdb_off_t off,
- tdb_off_t val);
-
-/* Clear an ondisk area. */
-enum TDB_ERROR zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len);
-
-/* Return a non-zero offset between >= start < end in this array (or end). */
-tdb_off_t tdb_find_nonzero_off(struct tdb_context *tdb,
- tdb_off_t base,
- uint64_t start,
- uint64_t end);
-
-/* Return a zero offset in this array, or num. */
-tdb_off_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
- uint64_t num);
-
-/* Allocate and make a copy of some offset. */
-void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len);
-
-/* Writes a converted copy of a record. */
-enum TDB_ERROR tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
- const void *rec, size_t len);
-
-/* Reads record and converts it */
-enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
- void *rec, size_t len);
-
-/* Bump the seqnum (caller checks for tdb->flags & TDB_SEQNUM) */
-void tdb_inc_seqnum(struct tdb_context *tdb);
-
-/* lock.c: */
-/* Print message because another tdb owns a lock we want. */
-enum TDB_ERROR owner_conflict(struct tdb_context *tdb, const char *call);
-
-/* If we fork, we no longer really own locks. */
-bool check_lock_pid(struct tdb_context *tdb, const char *call, bool log);
-
-/* Lock/unlock a range of hashes. */
-enum TDB_ERROR tdb_lock_hashes(struct tdb_context *tdb,
- tdb_off_t hash_lock, tdb_len_t hash_range,
- int ltype, enum tdb_lock_flags waitflag);
-enum TDB_ERROR tdb_unlock_hashes(struct tdb_context *tdb,
- tdb_off_t hash_lock,
- tdb_len_t hash_range, int ltype);
-
-/* For closing the file. */
-void tdb_lock_cleanup(struct tdb_context *tdb);
-
-/* Lock/unlock a particular free bucket. */
-enum TDB_ERROR tdb_lock_free_bucket(struct tdb_context *tdb, tdb_off_t b_off,
- enum tdb_lock_flags waitflag);
-void tdb_unlock_free_bucket(struct tdb_context *tdb, tdb_off_t b_off);
-
-/* Serialize transaction start. */
-enum TDB_ERROR tdb_transaction_lock(struct tdb_context *tdb, int ltype);
-void tdb_transaction_unlock(struct tdb_context *tdb, int ltype);
-
-/* Do we have any hash locks (ie. via tdb_chainlock) ? */
-bool tdb_has_hash_locks(struct tdb_context *tdb);
-
-/* Lock entire database. */
-enum TDB_ERROR tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
- enum tdb_lock_flags flags, bool upgradable);
-void tdb_allrecord_unlock(struct tdb_context *tdb, int ltype);
-enum TDB_ERROR tdb_allrecord_upgrade(struct tdb_context *tdb, off_t start);
-
-/* Serialize db open. */
-enum TDB_ERROR tdb_lock_open(struct tdb_context *tdb,
- int ltype, enum tdb_lock_flags flags);
-void tdb_unlock_open(struct tdb_context *tdb, int ltype);
-bool tdb_has_open_lock(struct tdb_context *tdb);
-
-/* Serialize db expand. */
-enum TDB_ERROR tdb_lock_expand(struct tdb_context *tdb, int ltype);
-void tdb_unlock_expand(struct tdb_context *tdb, int ltype);
-bool tdb_has_expansion_lock(struct tdb_context *tdb);
-
-/* If it needs recovery, grab all the locks and do it. */
-enum TDB_ERROR tdb_lock_and_recover(struct tdb_context *tdb);
-
-/* Default lock and unlock functions. */
-int tdb_fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag, void *);
-int tdb_fcntl_unlock(int fd, int rw, off_t off, off_t len, void *);
-
-/* transaction.c: */
-enum TDB_ERROR tdb_transaction_recover(struct tdb_context *tdb);
-tdb_bool_err tdb_needs_recovery(struct tdb_context *tdb);
-
-struct tdb_context {
- /* Single list of all TDBs, to detect multiple opens. */
- struct tdb_context *next;
-
- /* Filename of the database. */
- const char *name;
-
- /* Logging function */
- void (*log_fn)(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
- const char *message,
- void *data);
- void *log_data;
-
- /* Open flags passed to tdb_open. */
- int open_flags;
-
- /* low level (fnctl) lock functions. */
- int (*lock_fn)(int fd, int rw, off_t off, off_t len, bool w, void *);
- int (*unlock_fn)(int fd, int rw, off_t off, off_t len, void *);
- void *lock_data;
-
- /* the tdb flags passed to tdb_open. */
- uint32_t flags;
-
- /* Our statistics. */
- struct tdb_attribute_stats stats;
-
- /* The actual file information */
- struct tdb_file *file;
-
- /* Hash function. */
- uint64_t (*hash_fn)(const void *key, size_t len, uint64_t seed, void *);
- void *hash_data;
- uint64_t hash_seed;
-
- /* Our open hook, if any. */
- enum TDB_ERROR (*openhook)(int fd, void *data);
- void *openhook_data;
-
- /* Last error we returned. */
- enum TDB_ERROR last_error;
-
- /* Are we accessing directly? (debugging check). */
- int direct_access;
-
- /* Set if we are in a transaction. */
- struct tdb_transaction *transaction;
-
- /* What free table are we using? */
- tdb_off_t ftable_off;
- unsigned int ftable;
-
- /* IO methods: changes for transactions. */
- const struct tdb_methods *io;
-
- /* Direct access information */
- struct tdb_access_hdr *access;
-};
-
-/* tdb.c: */
-enum TDB_ERROR COLD PRINTF_FMT(4, 5)
- tdb_logerr(struct tdb_context *tdb,
- enum TDB_ERROR ecode,
- enum tdb_log_level level,
- const char *fmt, ...);
-
-#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 */
-
-#endif
diff --git a/lib/tdb2/pytdb.c b/lib/tdb2/pytdb.c
deleted file mode 100644
index 1fa4e5828b..0000000000
--- a/lib/tdb2/pytdb.c
+++ /dev/null
@@ -1,591 +0,0 @@
-/*
- Unix SMB/CIFS implementation.
-
- Python interface to tdb2. Simply modified from tdb1 version.
-
- Copyright (C) 2004-2006 Tim Potter <tpot@samba.org>
- Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
- Copyright (C) 2011 Rusty Russell <rusty@rustcorp.com.au>
-
- ** 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 <Python.h>
-#include "replace.h"
-#include "system/filesys.h"
-
-#ifndef Py_RETURN_NONE
-#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
-#endif
-
-/* Include tdb headers */
-#include <tdb2.h>
-
-typedef struct {
- PyObject_HEAD
- struct tdb_context *ctx;
- bool closed;
-} PyTdbObject;
-
-staticforward PyTypeObject PyTdb;
-
-static void PyErr_SetTDBError(enum TDB_ERROR e)
-{
- PyErr_SetObject(PyExc_RuntimeError,
- Py_BuildValue("(i,s)", e, tdb_errorstr(e)));
-}
-
-static TDB_DATA PyString_AsTDB_DATA(PyObject *data)
-{
- TDB_DATA ret;
- ret.dptr = (unsigned char *)PyString_AsString(data);
- ret.dsize = PyString_Size(data);
- return ret;
-}
-
-static PyObject *PyString_FromTDB_DATA(TDB_DATA data)
-{
- PyObject *ret = PyString_FromStringAndSize((const char *)data.dptr,
- data.dsize);
- free(data.dptr);
- return ret;
-}
-
-#define PyErr_TDB_ERROR_IS_ERR_RAISE(ret) \
- if (ret != TDB_SUCCESS) { \
- PyErr_SetTDBError(ret); \
- return NULL; \
- }
-
-static void stderr_log(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
- const char *message,
- void *data)
-{
- fprintf(stderr, "%s:%s:%s\n",
- tdb_name(tdb), tdb_errorstr(ecode), message);
-}
-
-static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
-{
- char *name = NULL;
- int tdb_flags = TDB_DEFAULT, flags = O_RDWR, mode = 0600;
- struct tdb_context *ctx;
- PyTdbObject *ret;
- union tdb_attribute logattr;
- const char *kwnames[] = { "name", "tdb_flags", "flags", "mode", NULL };
-
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siii", cast_const2(char **, kwnames), &name, &tdb_flags, &flags, &mode))
- return NULL;
-
- if (name == NULL) {
- tdb_flags |= TDB_INTERNAL;
- }
-
- logattr.log.base.attr = TDB_ATTRIBUTE_LOG;
- logattr.log.base.next = NULL;
- logattr.log.fn = stderr_log;
- ctx = tdb_open(name, tdb_flags, flags, mode, &logattr);
- if (ctx == NULL) {
- PyErr_SetFromErrno(PyExc_IOError);
- return NULL;
- }
-
- ret = PyObject_New(PyTdbObject, &PyTdb);
- if (!ret) {
- tdb_close(ctx);
- return NULL;
- }
-
- ret->ctx = ctx;
- ret->closed = false;
- return (PyObject *)ret;
-}
-
-static PyObject *obj_transaction_cancel(PyTdbObject *self)
-{
- tdb_transaction_cancel(self->ctx);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_transaction_commit(PyTdbObject *self)
-{
- enum TDB_ERROR ret = tdb_transaction_commit(self->ctx);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_transaction_prepare_commit(PyTdbObject *self)
-{
- enum TDB_ERROR ret = tdb_transaction_prepare_commit(self->ctx);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_transaction_start(PyTdbObject *self)
-{
- enum TDB_ERROR ret = tdb_transaction_start(self->ctx);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_lockall(PyTdbObject *self)
-{
- enum TDB_ERROR ret = tdb_lockall(self->ctx);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_unlockall(PyTdbObject *self)
-{
- tdb_unlockall(self->ctx);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_lockall_read(PyTdbObject *self)
-{
- enum TDB_ERROR ret = tdb_lockall_read(self->ctx);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_unlockall_read(PyTdbObject *self)
-{
- tdb_unlockall_read(self->ctx);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_close(PyTdbObject *self)
-{
- int ret;
- if (self->closed)
- Py_RETURN_NONE;
- ret = tdb_close(self->ctx);
- self->closed = true;
- if (ret != 0) {
- PyErr_SetTDBError(TDB_ERR_IO);
- return NULL;
- }
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_get(PyTdbObject *self, PyObject *args)
-{
- TDB_DATA key, data;
- PyObject *py_key;
- enum TDB_ERROR ret;
- if (!PyArg_ParseTuple(args, "O", &py_key))
- return NULL;
-
- key = PyString_AsTDB_DATA(py_key);
- ret = tdb_fetch(self->ctx, key, &data);
- if (ret == TDB_ERR_NOEXIST)
- Py_RETURN_NONE;
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- return PyString_FromTDB_DATA(data);
-}
-
-static PyObject *obj_append(PyTdbObject *self, PyObject *args)
-{
- TDB_DATA key, data;
- PyObject *py_key, *py_data;
- enum TDB_ERROR ret;
- if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data))
- return NULL;
-
- key = PyString_AsTDB_DATA(py_key);
- data = PyString_AsTDB_DATA(py_data);
-
- ret = tdb_append(self->ctx, key, data);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_firstkey(PyTdbObject *self)
-{
- enum TDB_ERROR ret;
- TDB_DATA key;
-
- ret = tdb_firstkey(self->ctx, &key);
- if (ret == TDB_ERR_NOEXIST)
- Py_RETURN_NONE;
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
-
- return PyString_FromTDB_DATA(key);
-}
-
-static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args)
-{
- TDB_DATA key;
- PyObject *py_key;
- enum TDB_ERROR ret;
- if (!PyArg_ParseTuple(args, "O", &py_key))
- return NULL;
-
- /* Malloc here, since tdb_nextkey frees. */
- key.dsize = PyString_Size(py_key);
- key.dptr = malloc(key.dsize);
- memcpy(key.dptr, PyString_AsString(py_key), key.dsize);
-
- ret = tdb_nextkey(self->ctx, &key);
- if (ret == TDB_ERR_NOEXIST)
- Py_RETURN_NONE;
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
-
- return PyString_FromTDB_DATA(key);
-}
-
-static PyObject *obj_delete(PyTdbObject *self, PyObject *args)
-{
- TDB_DATA key;
- PyObject *py_key;
- enum TDB_ERROR ret;
- if (!PyArg_ParseTuple(args, "O", &py_key))
- return NULL;
-
- key = PyString_AsTDB_DATA(py_key);
- ret = tdb_delete(self->ctx, key);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_has_key(PyTdbObject *self, PyObject *args)
-{
- TDB_DATA key;
- PyObject *py_key;
- if (!PyArg_ParseTuple(args, "O", &py_key))
- return NULL;
-
- key = PyString_AsTDB_DATA(py_key);
- if (tdb_exists(self->ctx, key))
- return Py_True;
- if (tdb_error(self->ctx) != TDB_ERR_NOEXIST)
- PyErr_TDB_ERROR_IS_ERR_RAISE(tdb_error(self->ctx));
- return Py_False;
-}
-
-static PyObject *obj_store(PyTdbObject *self, PyObject *args)
-{
- TDB_DATA key, value;
- enum TDB_ERROR ret;
- int flag = TDB_REPLACE;
- PyObject *py_key, *py_value;
-
- if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag))
- return NULL;
-
- key = PyString_AsTDB_DATA(py_key);
- value = PyString_AsTDB_DATA(py_value);
-
- ret = tdb_store(self->ctx, key, value, flag);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_add_flag(PyTdbObject *self, PyObject *args)
-{
- unsigned flag;
-
- if (!PyArg_ParseTuple(args, "I", &flag))
- return NULL;
-
- tdb_add_flag(self->ctx, flag);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_remove_flag(PyTdbObject *self, PyObject *args)
-{
- unsigned flag;
-
- if (!PyArg_ParseTuple(args, "I", &flag))
- return NULL;
-
- tdb_remove_flag(self->ctx, flag);
- Py_RETURN_NONE;
-}
-
-typedef struct {
- PyObject_HEAD
- TDB_DATA current;
- bool end;
- PyTdbObject *iteratee;
-} PyTdbIteratorObject;
-
-static PyObject *tdb_iter_next(PyTdbIteratorObject *self)
-{
- enum TDB_ERROR e;
- PyObject *ret;
- if (self->end)
- return NULL;
- ret = PyString_FromStringAndSize((const char *)self->current.dptr,
- self->current.dsize);
- e = tdb_nextkey(self->iteratee->ctx, &self->current);
- if (e == TDB_ERR_NOEXIST)
- self->end = true;
- else
- PyErr_TDB_ERROR_IS_ERR_RAISE(e);
- return ret;
-}
-
-static void tdb_iter_dealloc(PyTdbIteratorObject *self)
-{
- Py_DECREF(self->iteratee);
- PyObject_Del(self);
-}
-
-PyTypeObject PyTdbIterator = {
- .tp_name = "Iterator",
- .tp_basicsize = sizeof(PyTdbIteratorObject),
- .tp_iternext = (iternextfunc)tdb_iter_next,
- .tp_dealloc = (destructor)tdb_iter_dealloc,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_iter = PyObject_SelfIter,
-};
-
-static PyObject *tdb_object_iter(PyTdbObject *self)
-{
- PyTdbIteratorObject *ret;
- enum TDB_ERROR e;
-
- ret = PyObject_New(PyTdbIteratorObject, &PyTdbIterator);
- if (!ret)
- return NULL;
- e = tdb_firstkey(self->ctx, &ret->current);
- if (e == TDB_ERR_NOEXIST) {
- ret->end = true;
- } else {
- PyErr_TDB_ERROR_IS_ERR_RAISE(e);
- ret->end = false;
- }
- ret->iteratee = self;
- Py_INCREF(self);
- return (PyObject *)ret;
-}
-
-static PyObject *obj_clear(PyTdbObject *self)
-{
- enum TDB_ERROR ret = tdb_wipe_all(self->ctx);
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- Py_RETURN_NONE;
-}
-
-static PyObject *obj_enable_seqnum(PyTdbObject *self)
-{
- tdb_add_flag(self->ctx, TDB_SEQNUM);
- Py_RETURN_NONE;
-}
-
-static PyMethodDef tdb_object_methods[] = {
- { "transaction_cancel", (PyCFunction)obj_transaction_cancel, METH_NOARGS,
- "S.transaction_cancel() -> None\n"
- "Cancel the currently active transaction." },
- { "transaction_commit", (PyCFunction)obj_transaction_commit, METH_NOARGS,
- "S.transaction_commit() -> None\n"
- "Commit the currently active transaction." },
- { "transaction_prepare_commit", (PyCFunction)obj_transaction_prepare_commit, METH_NOARGS,
- "S.transaction_prepare_commit() -> None\n"
- "Prepare to commit the currently active transaction" },
- { "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS,
- "S.transaction_start() -> None\n"
- "Start a new transaction." },
- { "lock_all", (PyCFunction)obj_lockall, METH_NOARGS, NULL },
- { "unlock_all", (PyCFunction)obj_unlockall, METH_NOARGS, NULL },
- { "read_lock_all", (PyCFunction)obj_lockall_read, METH_NOARGS, NULL },
- { "read_unlock_all", (PyCFunction)obj_unlockall_read, METH_NOARGS, NULL },
- { "close", (PyCFunction)obj_close, METH_NOARGS, NULL },
- { "get", (PyCFunction)obj_get, METH_VARARGS, "S.get(key) -> value\n"
- "Fetch a value." },
- { "append", (PyCFunction)obj_append, METH_VARARGS, "S.append(key, value) -> None\n"
- "Append data to an existing key." },
- { "firstkey", (PyCFunction)obj_firstkey, METH_NOARGS, "S.firstkey() -> data\n"
- "Return the first key in this database." },
- { "nextkey", (PyCFunction)obj_nextkey, METH_NOARGS, "S.nextkey(key) -> data\n"
- "Return the next key in this database." },
- { "delete", (PyCFunction)obj_delete, METH_VARARGS, "S.delete(key) -> None\n"
- "Delete an entry." },
- { "has_key", (PyCFunction)obj_has_key, METH_VARARGS, "S.has_key(key) -> None\n"
- "Check whether key exists in this database." },
- { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None"
- "Store data." },
- { "add_flag", (PyCFunction)obj_add_flag, METH_VARARGS, "S.add_flag(flag) -> None" },
- { "remove_flag", (PyCFunction)obj_remove_flag, METH_VARARGS, "S.remove_flag(flag) -> None" },
- { "iterkeys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" },
- { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n"
- "Wipe the entire database." },
- { "enable_seqnum", (PyCFunction)obj_enable_seqnum, METH_NOARGS,
- "S.enable_seqnum() -> None" },
- { NULL }
-};
-
-static PyObject *obj_get_flags(PyTdbObject *self, void *closure)
-{
- return PyInt_FromLong(tdb_get_flags(self->ctx));
-}
-
-static PyObject *obj_get_filename(PyTdbObject *self, void *closure)
-{
- return PyString_FromString(tdb_name(self->ctx));
-}
-
-static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure)
-{
- return PyInt_FromLong(tdb_get_seqnum(self->ctx));
-}
-
-
-static PyGetSetDef tdb_object_getsetters[] = {
- { cast_const(char *, "flags"), (getter)obj_get_flags, NULL, NULL },
- { cast_const(char *, "filename"), (getter)obj_get_filename, NULL,
- cast_const(char *, "The filename of this TDB file.")},
- { cast_const(char *, "seqnum"), (getter)obj_get_seqnum, NULL, NULL },
- { NULL }
-};
-
-static PyObject *tdb_object_repr(PyTdbObject *self)
-{
- if (tdb_get_flags(self->ctx) & TDB_INTERNAL) {
- return PyString_FromString("Tdb(<internal>)");
- } else {
- return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx));
- }
-}
-
-static void tdb_object_dealloc(PyTdbObject *self)
-{
- if (!self->closed)
- tdb_close(self->ctx);
- self->ob_type->tp_free(self);
-}
-
-static PyObject *obj_getitem(PyTdbObject *self, PyObject *key)
-{
- TDB_DATA tkey, val;
- enum TDB_ERROR ret;
-
- if (!PyString_Check(key)) {
- PyErr_SetString(PyExc_TypeError, "Expected string as key");
- return NULL;
- }
-
- tkey.dptr = (unsigned char *)PyString_AsString(key);
- tkey.dsize = PyString_Size(key);
-
- ret = tdb_fetch(self->ctx, tkey, &val);
- if (ret == TDB_ERR_NOEXIST) {
- PyErr_SetString(PyExc_KeyError, "No such TDB entry");
- return NULL;
- } else {
- PyErr_TDB_ERROR_IS_ERR_RAISE(ret);
- return PyString_FromTDB_DATA(val);
- }
-}
-
-static int obj_setitem(PyTdbObject *self, PyObject *key, PyObject *value)
-{
- TDB_DATA tkey, tval;
- enum TDB_ERROR ret;
- if (!PyString_Check(key)) {
- PyErr_SetString(PyExc_TypeError, "Expected string as key");
- return -1;
- }
-
- tkey = PyString_AsTDB_DATA(key);
-
- if (value == NULL) {
- ret = tdb_delete(self->ctx, tkey);
- } else {
- if (!PyString_Check(value)) {
- PyErr_SetString(PyExc_TypeError, "Expected string as value");
- return -1;
- }
-
- tval = PyString_AsTDB_DATA(value);
-
- ret = tdb_store(self->ctx, tkey, tval, TDB_REPLACE);
- }
-
- if (ret != TDB_SUCCESS) {
- PyErr_SetTDBError(ret);
- return -1;
- }
-
- return ret;
-}
-
-static PyMappingMethods tdb_object_mapping = {
- .mp_subscript = (binaryfunc)obj_getitem,
- .mp_ass_subscript = (objobjargproc)obj_setitem,
-};
-static PyTypeObject PyTdb = {
- .tp_name = "tdb.Tdb",
- .tp_basicsize = sizeof(PyTdbObject),
- .tp_methods = tdb_object_methods,
- .tp_getset = tdb_object_getsetters,
- .tp_new = py_tdb_open,
- .tp_doc = "A TDB file",
- .tp_repr = (reprfunc)tdb_object_repr,
- .tp_dealloc = (destructor)tdb_object_dealloc,
- .tp_as_mapping = &tdb_object_mapping,
- .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER,
- .tp_iter = (getiterfunc)tdb_object_iter,
-};
-
-static PyMethodDef tdb_methods[] = {
- { "open", (PyCFunction)py_tdb_open, METH_VARARGS|METH_KEYWORDS, "open(name, hash_size=0, tdb_flags=TDB_DEFAULT, flags=O_RDWR, mode=0600)\n"
- "Open a TDB file." },
- { NULL }
-};
-
-void inittdb(void);
-void inittdb(void)
-{
- PyObject *m;
-
- if (PyType_Ready(&PyTdb) < 0)
- return;
-
- if (PyType_Ready(&PyTdbIterator) < 0)
- return;
-
- m = Py_InitModule3("tdb", tdb_methods, "TDB is a simple key-value database similar to GDBM that supports multiple writers.");
- if (m == NULL)
- return;
-
- PyModule_AddObject(m, "REPLACE", PyInt_FromLong(TDB_REPLACE));
- PyModule_AddObject(m, "INSERT", PyInt_FromLong(TDB_INSERT));
- PyModule_AddObject(m, "MODIFY", PyInt_FromLong(TDB_MODIFY));
-
- PyModule_AddObject(m, "DEFAULT", PyInt_FromLong(TDB_DEFAULT));
- PyModule_AddObject(m, "INTERNAL", PyInt_FromLong(TDB_INTERNAL));
- PyModule_AddObject(m, "NOLOCK", PyInt_FromLong(TDB_NOLOCK));
- PyModule_AddObject(m, "NOMMAP", PyInt_FromLong(TDB_NOMMAP));
- PyModule_AddObject(m, "CONVERT", PyInt_FromLong(TDB_CONVERT));
- PyModule_AddObject(m, "NOSYNC", PyInt_FromLong(TDB_NOSYNC));
- PyModule_AddObject(m, "SEQNUM", PyInt_FromLong(TDB_SEQNUM));
- PyModule_AddObject(m, "ALLOW_NESTING", PyInt_FromLong(TDB_ALLOW_NESTING));
-
- PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
-
- PyModule_AddObject(m, "__version__", PyString_FromString(PACKAGE_VERSION));
-
- Py_INCREF(&PyTdb);
- PyModule_AddObject(m, "Tdb", (PyObject *)&PyTdb);
-
- Py_INCREF(&PyTdbIterator);
-}
diff --git a/lib/tdb2/tdb.c b/lib/tdb2/tdb.c
deleted file mode 100644
index 5257aa17e3..0000000000
--- a/lib/tdb2/tdb.c
+++ /dev/null
@@ -1,605 +0,0 @@
- /*
- Trivial Database 2: fetch, store and misc routines.
- 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 "private.h"
-#ifndef HAVE_LIBREPLACE
-#include <ccan/asprintf/asprintf.h>
-#include <stdarg.h>
-#endif
-
-static enum TDB_ERROR update_rec_hdr(struct tdb_context *tdb,
- tdb_off_t off,
- tdb_len_t keylen,
- tdb_len_t datalen,
- struct tdb_used_record *rec,
- uint64_t h)
-{
- uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
- enum TDB_ERROR ecode;
-
- ecode = set_header(tdb, rec, TDB_USED_MAGIC, keylen, datalen,
- keylen + dataroom, h);
- if (ecode == TDB_SUCCESS) {
- ecode = tdb_write_convert(tdb, off, rec, sizeof(*rec));
- }
- return ecode;
-}
-
-static enum TDB_ERROR replace_data(struct tdb_context *tdb,
- struct hash_info *h,
- struct tdb_data key, struct tdb_data dbuf,
- tdb_off_t old_off, tdb_len_t old_room,
- bool growing)
-{
- tdb_off_t new_off;
- enum TDB_ERROR ecode;
-
- /* Allocate a new record. */
- new_off = alloc(tdb, key.dsize, dbuf.dsize, h->h, TDB_USED_MAGIC,
- growing);
- if (TDB_OFF_IS_ERR(new_off)) {
- return TDB_OFF_TO_ERR(new_off);
- }
-
- /* We didn't like the existing one: remove it. */
- if (old_off) {
- tdb->stats.frees++;
- ecode = add_free_record(tdb, old_off,
- sizeof(struct tdb_used_record)
- + key.dsize + old_room,
- TDB_LOCK_WAIT, true);
- if (ecode == TDB_SUCCESS)
- ecode = replace_in_hash(tdb, h, new_off);
- } else {
- ecode = add_to_hash(tdb, h, new_off);
- }
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- new_off += sizeof(struct tdb_used_record);
- ecode = tdb->io->twrite(tdb, new_off, key.dptr, key.dsize);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- new_off += key.dsize;
- ecode = tdb->io->twrite(tdb, new_off, dbuf.dptr, dbuf.dsize);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- if (tdb->flags & TDB_SEQNUM)
- tdb_inc_seqnum(tdb);
-
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR update_data(struct tdb_context *tdb,
- tdb_off_t off,
- struct tdb_data dbuf,
- tdb_len_t extra)
-{
- enum TDB_ERROR ecode;
-
- ecode = tdb->io->twrite(tdb, off, dbuf.dptr, dbuf.dsize);
- if (ecode == TDB_SUCCESS && extra) {
- /* Put a zero in; future versions may append other data. */
- ecode = tdb->io->twrite(tdb, off + dbuf.dsize, "", 1);
- }
- if (tdb->flags & TDB_SEQNUM)
- tdb_inc_seqnum(tdb);
-
- return ecode;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_store(struct tdb_context *tdb,
- struct tdb_data key, struct tdb_data dbuf, int flag)
-{
- struct hash_info h;
- tdb_off_t off;
- tdb_len_t old_room = 0;
- struct tdb_used_record rec;
- enum TDB_ERROR ecode;
-
- off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
- if (TDB_OFF_IS_ERR(off)) {
- return tdb->last_error = TDB_OFF_TO_ERR(off);
- }
-
- /* Now we have lock on this hash bucket. */
- if (flag == TDB_INSERT) {
- if (off) {
- ecode = TDB_ERR_EXISTS;
- goto out;
- }
- } else {
- if (off) {
- old_room = rec_data_length(&rec)
- + rec_extra_padding(&rec);
- if (old_room >= dbuf.dsize) {
- /* Can modify in-place. Easy! */
- ecode = update_rec_hdr(tdb, off,
- key.dsize, dbuf.dsize,
- &rec, h.h);
- if (ecode != TDB_SUCCESS) {
- goto out;
- }
- ecode = update_data(tdb,
- off + sizeof(rec)
- + key.dsize, dbuf,
- old_room - dbuf.dsize);
- if (ecode != TDB_SUCCESS) {
- goto out;
- }
- tdb_unlock_hashes(tdb, h.hlock_start,
- h.hlock_range, F_WRLCK);
- return tdb->last_error = TDB_SUCCESS;
- }
- } else {
- if (flag == TDB_MODIFY) {
- /* if the record doesn't exist and we
- are in TDB_MODIFY mode then we should fail
- the store */
- ecode = TDB_ERR_NOEXIST;
- goto out;
- }
- }
- }
-
- /* If we didn't use the old record, this implies we're growing. */
- ecode = replace_data(tdb, &h, key, dbuf, off, old_room, off);
-out:
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
- return tdb->last_error = ecode;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_append(struct tdb_context *tdb,
- struct tdb_data key, struct tdb_data dbuf)
-{
- struct hash_info h;
- tdb_off_t off;
- struct tdb_used_record rec;
- tdb_len_t old_room = 0, old_dlen;
- unsigned char *newdata;
- struct tdb_data new_dbuf;
- enum TDB_ERROR ecode;
-
- off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
- if (TDB_OFF_IS_ERR(off)) {
- return tdb->last_error = TDB_OFF_TO_ERR(off);
- }
-
- if (off) {
- old_dlen = rec_data_length(&rec);
- old_room = old_dlen + rec_extra_padding(&rec);
-
- /* Fast path: can append in place. */
- if (rec_extra_padding(&rec) >= dbuf.dsize) {
- ecode = update_rec_hdr(tdb, off, key.dsize,
- old_dlen + dbuf.dsize, &rec,
- h.h);
- if (ecode != TDB_SUCCESS) {
- goto out;
- }
-
- off += sizeof(rec) + key.dsize + old_dlen;
- ecode = update_data(tdb, off, dbuf,
- rec_extra_padding(&rec));
- goto out;
- }
-
- /* Slow path. */
- newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
- if (!newdata) {
- ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_append:"
- " failed to allocate %zu bytes",
- (size_t)(key.dsize + old_dlen
- + dbuf.dsize));
- goto out;
- }
- ecode = tdb->io->tread(tdb, off + sizeof(rec) + key.dsize,
- newdata, old_dlen);
- if (ecode != TDB_SUCCESS) {
- goto out_free_newdata;
- }
- memcpy(newdata + old_dlen, dbuf.dptr, dbuf.dsize);
- new_dbuf.dptr = newdata;
- new_dbuf.dsize = old_dlen + dbuf.dsize;
- } else {
- newdata = NULL;
- new_dbuf = dbuf;
- }
-
- /* If they're using tdb_append(), it implies they're growing record. */
- ecode = replace_data(tdb, &h, key, new_dbuf, off, old_room, true);
-
-out_free_newdata:
- free(newdata);
-out:
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
- return tdb->last_error = ecode;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
- struct tdb_data *data)
-{
- tdb_off_t off;
- struct tdb_used_record rec;
- struct hash_info h;
- enum TDB_ERROR ecode;
-
- off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
- if (TDB_OFF_IS_ERR(off)) {
- return tdb->last_error = TDB_OFF_TO_ERR(off);
- }
-
- if (!off) {
- ecode = TDB_ERR_NOEXIST;
- } else {
- data->dsize = rec_data_length(&rec);
- data->dptr = tdb_alloc_read(tdb, off + sizeof(rec) + key.dsize,
- data->dsize);
- if (TDB_PTR_IS_ERR(data->dptr)) {
- ecode = TDB_PTR_ERR(data->dptr);
- } else
- ecode = TDB_SUCCESS;
- }
-
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
- return tdb->last_error = ecode;
-}
-
-_PUBLIC_ bool tdb_exists(struct tdb_context *tdb, TDB_DATA key)
-{
- tdb_off_t off;
- struct tdb_used_record rec;
- struct hash_info h;
-
- off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
- if (TDB_OFF_IS_ERR(off)) {
- tdb->last_error = TDB_OFF_TO_ERR(off);
- return false;
- }
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
-
- tdb->last_error = TDB_SUCCESS;
- return off ? true : false;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key)
-{
- tdb_off_t off;
- struct tdb_used_record rec;
- struct hash_info h;
- enum TDB_ERROR ecode;
-
- off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
- if (TDB_OFF_IS_ERR(off)) {
- return tdb->last_error = TDB_OFF_TO_ERR(off);
- }
-
- if (!off) {
- ecode = TDB_ERR_NOEXIST;
- goto unlock;
- }
-
- ecode = delete_from_hash(tdb, &h);
- if (ecode != TDB_SUCCESS) {
- goto unlock;
- }
-
- /* Free the deleted entry. */
- tdb->stats.frees++;
- ecode = add_free_record(tdb, off,
- sizeof(struct tdb_used_record)
- + rec_key_length(&rec)
- + rec_data_length(&rec)
- + rec_extra_padding(&rec),
- TDB_LOCK_WAIT, true);
-
- if (tdb->flags & TDB_SEQNUM)
- tdb_inc_seqnum(tdb);
-
-unlock:
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
- return tdb->last_error = ecode;
-}
-
-_PUBLIC_ unsigned int tdb_get_flags(struct tdb_context *tdb)
-{
- return tdb->flags;
-}
-
-static bool inside_transaction(const struct tdb_context *tdb)
-{
- return tdb->transaction != NULL;
-}
-
-static bool readonly_changable(struct tdb_context *tdb, const char *caller)
-{
- if (inside_transaction(tdb)) {
- tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "%s: can't change"
- " TDB_RDONLY inside transaction",
- caller);
- return false;
- }
- return true;
-}
-
-_PUBLIC_ void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
-{
- if (tdb->flags & TDB_INTERNAL) {
- tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_add_flag: internal db");
- return;
- }
- switch (flag) {
- case TDB_NOLOCK:
- tdb->flags |= TDB_NOLOCK;
- break;
- case TDB_NOMMAP:
- tdb->flags |= TDB_NOMMAP;
-#ifndef HAVE_INCOHERENT_MMAP
- tdb_munmap(tdb->file);
-#endif
- break;
- case TDB_NOSYNC:
- tdb->flags |= TDB_NOSYNC;
- break;
- case TDB_SEQNUM:
- tdb->flags |= TDB_SEQNUM;
- break;
- case TDB_ALLOW_NESTING:
- tdb->flags |= TDB_ALLOW_NESTING;
- break;
- case TDB_RDONLY:
- if (readonly_changable(tdb, "tdb_add_flag"))
- tdb->flags |= TDB_RDONLY;
- break;
- default:
- tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_add_flag: Unknown flag %u",
- flag);
- }
-}
-
-_PUBLIC_ void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
-{
- if (tdb->flags & TDB_INTERNAL) {
- tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_remove_flag: internal db");
- return;
- }
- switch (flag) {
- case TDB_NOLOCK:
- tdb->flags &= ~TDB_NOLOCK;
- break;
- case TDB_NOMMAP:
- tdb->flags &= ~TDB_NOMMAP;
-#ifndef HAVE_INCOHERENT_MMAP
- /* If mmap incoherent, we were mmaping anyway. */
- tdb_mmap(tdb);
-#endif
- break;
- case TDB_NOSYNC:
- tdb->flags &= ~TDB_NOSYNC;
- break;
- case TDB_SEQNUM:
- tdb->flags &= ~TDB_SEQNUM;
- break;
- case TDB_ALLOW_NESTING:
- tdb->flags &= ~TDB_ALLOW_NESTING;
- break;
- case TDB_RDONLY:
- if ((tdb->open_flags & O_ACCMODE) == O_RDONLY) {
- tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_remove_flag: can't"
- " remove TDB_RDONLY on tdb"
- " opened with O_RDONLY");
- break;
- }
- if (readonly_changable(tdb, "tdb_remove_flag"))
- tdb->flags &= ~TDB_RDONLY;
- break;
- default:
- tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_remove_flag: Unknown flag %u",
- flag);
- }
-}
-
-_PUBLIC_ const char *tdb_errorstr(enum TDB_ERROR ecode)
-{
- /* Gcc warns if you miss a case in the switch, so use that. */
- switch (TDB_ERR_TO_OFF(ecode)) {
- case TDB_ERR_TO_OFF(TDB_SUCCESS): return "Success";
- case TDB_ERR_TO_OFF(TDB_ERR_CORRUPT): return "Corrupt database";
- case TDB_ERR_TO_OFF(TDB_ERR_IO): return "IO Error";
- case TDB_ERR_TO_OFF(TDB_ERR_LOCK): return "Locking error";
- case TDB_ERR_TO_OFF(TDB_ERR_OOM): return "Out of memory";
- case TDB_ERR_TO_OFF(TDB_ERR_EXISTS): return "Record exists";
- case TDB_ERR_TO_OFF(TDB_ERR_EINVAL): return "Invalid parameter";
- case TDB_ERR_TO_OFF(TDB_ERR_NOEXIST): return "Record does not exist";
- case TDB_ERR_TO_OFF(TDB_ERR_RDONLY): return "write not permitted";
- }
- return "Invalid error code";
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_error(struct tdb_context *tdb)
-{
- return tdb->last_error;
-}
-
-enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb,
- enum TDB_ERROR ecode,
- enum tdb_log_level level,
- const char *fmt, ...)
-{
- char *message;
- va_list ap;
- size_t len;
- /* tdb_open paths care about errno, so save it. */
- int saved_errno = errno;
-
- if (!tdb->log_fn)
- return ecode;
-
- va_start(ap, fmt);
- len = vasprintf(&message, fmt, ap);
- va_end(ap);
-
- if (len < 0) {
- tdb->log_fn(tdb, TDB_LOG_ERROR, TDB_ERR_OOM,
- "out of memory formatting message:", tdb->log_data);
- tdb->log_fn(tdb, level, ecode, fmt, tdb->log_data);
- } else {
- tdb->log_fn(tdb, level, ecode, message, tdb->log_data);
- free(message);
- }
- errno = saved_errno;
- return ecode;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
- TDB_DATA key,
- enum TDB_ERROR (*parse)(TDB_DATA k,
- TDB_DATA d,
- void *data),
- void *data)
-{
- tdb_off_t off;
- struct tdb_used_record rec;
- struct hash_info h;
- enum TDB_ERROR ecode;
-
- off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
- if (TDB_OFF_IS_ERR(off)) {
- return tdb->last_error = TDB_OFF_TO_ERR(off);
- }
-
- if (!off) {
- ecode = TDB_ERR_NOEXIST;
- } else {
- const void *dptr;
- dptr = tdb_access_read(tdb, off + sizeof(rec) + key.dsize,
- rec_data_length(&rec), false);
- if (TDB_PTR_IS_ERR(dptr)) {
- ecode = TDB_PTR_ERR(dptr);
- } else {
- TDB_DATA d = tdb_mkdata(dptr, rec_data_length(&rec));
-
- ecode = parse(key, d, data);
- tdb_access_release(tdb, dptr);
- }
- }
-
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
- return tdb->last_error = ecode;
-}
-
-_PUBLIC_ const char *tdb_name(const struct tdb_context *tdb)
-{
- return tdb->name;
-}
-
-_PUBLIC_ int64_t tdb_get_seqnum(struct tdb_context *tdb)
-{
- tdb_off_t off;
-
- off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
- if (TDB_OFF_IS_ERR(off))
- tdb->last_error = TDB_OFF_TO_ERR(off);
- else
- tdb->last_error = TDB_SUCCESS;
- return off;
-}
-
-
-_PUBLIC_ int tdb_fd(const struct tdb_context *tdb)
-{
- return tdb->file->fd;
-}
-
-struct traverse_state {
- enum TDB_ERROR error;
- struct tdb_context *dest_db;
-};
-
-/*
- traverse function for repacking
- */
-static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
- struct traverse_state *state)
-{
- state->error = tdb_store(state->dest_db, key, data, TDB_INSERT);
- if (state->error != TDB_SUCCESS) {
- return -1;
- }
- return 0;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_repack(struct tdb_context *tdb)
-{
- struct tdb_context *tmp_db;
- struct traverse_state state;
-
- state.error = tdb_transaction_start(tdb);
- if (state.error != TDB_SUCCESS) {
- return state.error;
- }
-
- tmp_db = tdb_open("tmpdb", TDB_INTERNAL, O_RDWR|O_CREAT, 0, NULL);
- if (tmp_db == NULL) {
- state.error = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- __location__
- " Failed to create tmp_db");
- tdb_transaction_cancel(tdb);
- return tdb->last_error = state.error;
- }
-
- state.dest_db = tmp_db;
- if (tdb_traverse(tdb, repack_traverse, &state) < 0) {
- goto fail;
- }
-
- state.error = tdb_wipe_all(tdb);
- if (state.error != TDB_SUCCESS) {
- goto fail;
- }
-
- state.dest_db = tdb;
- if (tdb_traverse(tmp_db, repack_traverse, &state) < 0) {
- goto fail;
- }
-
- tdb_close(tmp_db);
- return tdb_transaction_commit(tdb);
-
-fail:
- tdb_transaction_cancel(tdb);
- tdb_close(tmp_db);
- return state.error;
-}
diff --git a/lib/tdb2/tdb2.h b/lib/tdb2/tdb2.h
deleted file mode 100644
index f7aa0cc310..0000000000
--- a/lib/tdb2/tdb2.h
+++ /dev/null
@@ -1,897 +0,0 @@
-#ifndef CCAN_TDB2_H
-#define CCAN_TDB2_H
-
-/*
- TDB version 2: trivial database library
-
- Copyright (C) Andrew Tridgell 1999-2004
- Copyright (C) Rusty Russell 2010-2011
-
- ** 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
-
-#ifdef HAVE_LIBREPLACE
-#include <replace.h>
-#else
-#if HAVE_FILE_OFFSET_BITS
-#define _FILE_OFFSET_BITS 64
-#endif
-/* For mode_t */
-#include <sys/types.h>
-/* For O_* flags. */
-#include <sys/stat.h>
-/* For sig_atomic_t. */
-#include <signal.h>
-/* For uint64_t */
-#include <stdint.h>
-/* For bool */
-#include <stdbool.h>
-/* For memcmp */
-#include <string.h>
-#endif
-
-#if HAVE_CCAN
-#include <ccan/compiler/compiler.h>
-#include <ccan/typesafe_cb/typesafe_cb.h>
-#include <ccan/cast/cast.h>
-#else
-#ifndef typesafe_cb_preargs
-/* Failing to have CCAN just mean less typesafe protection, etc. */
-#define typesafe_cb_preargs(rtype, atype, fn, arg, ...) \
- ((rtype (*)(__VA_ARGS__, atype))(fn))
-#endif
-#ifndef cast_const
-#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
-#define cast_const(type, expr) ((type)((intptr_t)(expr)))
-#else
-#define cast_const(type, expr) ((type *)(expr))
-#endif
-#endif
-#endif /* !HAVE_CCAN */
-
-union tdb_attribute;
-struct tdb_context;
-
-/**
- * tdb_open - open a database file
- * @name: the file name (can be NULL if flags contains TDB_INTERNAL)
- * @tdb_flags: options for this database
- * @open_flags: flags argument for tdb's open() call.
- * @mode: mode argument for tdb's open() call.
- * @attributes: linked list of extra attributes for this tdb.
- *
- * This call opens (and potentially creates) a database file.
- * Multiple processes can have the TDB file open at once.
- *
- * On failure it will return NULL, and set errno: it may also call
- * any log attribute found in @attributes.
- *
- * See also:
- * union tdb_attribute
- */
-struct tdb_context *tdb_open(const char *name, int tdb_flags,
- int open_flags, mode_t mode,
- union tdb_attribute *attributes);
-
-
-/* flags for tdb_open() */
-#define TDB_DEFAULT 0 /* just a readability place holder */
-#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 */
-#define TDB_NOSYNC 64 /* don't use synchronous transactions */
-#define TDB_SEQNUM 128 /* maintain a sequence number */
-#define TDB_ALLOW_NESTING 256 /* fake nested transactions */
-#define TDB_RDONLY 512 /* implied by O_RDONLY */
-#define TDB_CANT_CHECK 2048 /* has a feature which we don't understand */
-
-/**
- * tdb_close - close and free a tdb.
- * @tdb: the tdb context returned from tdb_open()
- *
- * This always succeeds, in that @tdb is unusable after this call. But if
- * some unexpected error occurred while closing, it will return non-zero
- * (the only clue as to cause will be via the log attribute).
- */
-int tdb_close(struct tdb_context *tdb);
-
-/**
- * struct tdb_data - representation of keys or values.
- * @dptr: the data pointer
- * @dsize: the size of the data pointed to by dptr.
- *
- * This is the "blob" representation of keys and data used by TDB.
- */
-typedef struct tdb_data {
- unsigned char *dptr;
- size_t dsize;
-} TDB_DATA;
-
-/**
- * enum TDB_ERROR - error returns for TDB
- *
- * See Also:
- * tdb_errorstr()
- */
-enum TDB_ERROR {
- TDB_SUCCESS = 0, /* No error. */
- TDB_ERR_CORRUPT = -1, /* We read the db, and it was bogus. */
- TDB_ERR_IO = -2, /* We couldn't read/write the db. */
- TDB_ERR_LOCK = -3, /* Locking failed. */
- TDB_ERR_OOM = -4, /* Out of Memory. */
- TDB_ERR_EXISTS = -5, /* The key already exists. */
- TDB_ERR_NOEXIST = -6, /* The key does not exist. */
- TDB_ERR_EINVAL = -7, /* You're using it wrong. */
- TDB_ERR_RDONLY = -8, /* The database is read-only. */
- TDB_ERR_LAST = TDB_ERR_RDONLY
-};
-
-/**
- * tdb_store - store a key/value pair in a tdb.
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key
- * @dbuf: the data to associate with the key.
- * @flag: TDB_REPLACE, TDB_INSERT or TDB_MODIFY.
- *
- * This inserts (or overwrites) a key/value pair in the TDB. If flag
- * is TDB_REPLACE, it doesn't matter whether the key exists or not;
- * TDB_INSERT means it must not exist (returns TDB_ERR_EXISTS otherwise),
- * and TDB_MODIFY means it must exist (returns TDB_ERR_NOEXIST otherwise).
- *
- * On success, this returns TDB_SUCCESS.
- *
- * See also:
- * tdb_fetch, tdb_transaction_start, tdb_append, tdb_delete.
- */
-enum TDB_ERROR tdb_store(struct tdb_context *tdb,
- struct tdb_data key,
- struct tdb_data dbuf,
- int flag);
-
-/* flags to tdb_store() */
-#define TDB_REPLACE 1 /* A readability place holder */
-#define TDB_INSERT 2 /* Don't overwrite an existing entry */
-#define TDB_MODIFY 3 /* Don't create an existing entry */
-
-/**
- * tdb_fetch - fetch a value from a tdb.
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key
- * @data: pointer to data.
- *
- * This looks up a key in the database and sets it in @data.
- *
- * If it returns TDB_SUCCESS, the key was found: it is your
- * responsibility to call free() on @data->dptr.
- *
- * Otherwise, it returns an error (usually, TDB_ERR_NOEXIST) and @data is
- * undefined.
- */
-enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
- struct tdb_data *data);
-
-/**
- * tdb_errorstr - map the tdb error onto a constant readable string
- * @ecode: the enum TDB_ERROR to map.
- *
- * This is useful for displaying errors to users.
- */
-const char *tdb_errorstr(enum TDB_ERROR ecode);
-
-/**
- * tdb_append - append a value to a key/value pair in a tdb.
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key
- * @dbuf: the data to append.
- *
- * This is equivalent to fetching a record, reallocating .dptr to add the
- * data, and writing it back, only it's much more efficient. If the key
- * doesn't exist, it's equivalent to tdb_store (with an additional hint that
- * you expect to expand the record in future).
- *
- * See Also:
- * tdb_fetch(), tdb_store()
- */
-enum TDB_ERROR tdb_append(struct tdb_context *tdb,
- struct tdb_data key, struct tdb_data dbuf);
-
-/**
- * tdb_delete - delete a key from a tdb.
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key to delete.
- *
- * Returns TDB_SUCCESS on success, or an error (usually TDB_ERR_NOEXIST).
- *
- * See Also:
- * tdb_fetch(), tdb_store()
- */
-enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key);
-
-/**
- * tdb_exists - does a key exist in the database?
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key to search for.
- *
- * Returns true if it exists, or false if it doesn't or any other error.
- */
-bool tdb_exists(struct tdb_context *tdb, TDB_DATA key);
-
-/**
- * tdb_deq - are struct tdb_data equal?
- * @a: one struct tdb_data
- * @b: another struct tdb_data
- */
-static inline bool tdb_deq(struct tdb_data a, struct tdb_data b)
-{
- return a.dsize == b.dsize && memcmp(a.dptr, b.dptr, a.dsize) == 0;
-}
-
-/**
- * tdb_mkdata - make a struct tdb_data from const data
- * @p: the constant pointer
- * @len: the length
- *
- * As the dptr member of struct tdb_data is not constant, you need to
- * cast it. This function keeps thost casts in one place, as well as
- * suppressing the warning some compilers give when casting away a
- * qualifier (eg. gcc with -Wcast-qual)
- */
-static inline struct tdb_data tdb_mkdata(const void *p, size_t len)
-{
- struct tdb_data d;
- d.dptr = cast_const(void *, p);
- d.dsize = len;
- return d;
-}
-
-/**
- * tdb_transaction_start - start a transaction
- * @tdb: the tdb context returned from tdb_open()
- *
- * This begins a series of atomic operations. Other processes will be able
- * to read the tdb, but not alter it (they will block), nor will they see
- * any changes until tdb_transaction_commit() is called.
- *
- * Note that if the TDB_ALLOW_NESTING flag is set, a tdb_transaction_start()
- * within a transaction will succeed, but it's not a real transaction:
- * (1) An inner transaction which is committed is not actually committed until
- * the outer transaction is; if the outer transaction is cancelled, the
- * inner ones are discarded.
- * (2) tdb_transaction_cancel() marks the outer transaction as having an error,
- * so the final tdb_transaction_commit() will fail.
- * (3) the outer transaction will see the results of the inner transaction.
- *
- * See Also:
- * tdb_transaction_cancel, tdb_transaction_commit.
- */
-enum TDB_ERROR tdb_transaction_start(struct tdb_context *tdb);
-
-/**
- * tdb_transaction_cancel - abandon a transaction
- * @tdb: the tdb context returned from tdb_open()
- *
- * This aborts a transaction, discarding any changes which were made.
- * tdb_close() does this implicitly.
- */
-void tdb_transaction_cancel(struct tdb_context *tdb);
-
-/**
- * tdb_transaction_commit - commit a transaction
- * @tdb: the tdb context returned from tdb_open()
- *
- * This completes a transaction, writing any changes which were made.
- *
- * fsync() is used to commit the transaction (unless TDB_NOSYNC is set),
- * making it robust against machine crashes, but very slow compared to
- * other TDB operations.
- *
- * A failure can only be caused by unexpected errors (eg. I/O or
- * memory); this is no point looping on transaction failure.
- *
- * See Also:
- * tdb_transaction_prepare_commit()
- */
-enum TDB_ERROR tdb_transaction_commit(struct tdb_context *tdb);
-
-/**
- * tdb_transaction_prepare_commit - prepare to commit a transaction
- * @tdb: the tdb context returned from tdb_open()
- *
- * This ensures we have the resources to commit a transaction (using
- * tdb_transaction_commit): if this succeeds then a transaction will only
- * fail if the write() or fsync() calls fail.
- *
- * If this fails you must still call tdb_transaction_cancel() to cancel
- * the transaction.
- *
- * See Also:
- * tdb_transaction_commit()
- */
-enum TDB_ERROR tdb_transaction_prepare_commit(struct tdb_context *tdb);
-
-/**
- * tdb_traverse - traverse a TDB
- * @tdb: the tdb context returned from tdb_open()
- * @fn: the function to call for every key/value pair (or NULL)
- * @p: the pointer to hand to @f
- *
- * This walks the TDB until all they keys have been traversed, or @fn
- * returns non-zero. If the traverse function or other processes are
- * changing data or adding or deleting keys, the traverse may be
- * unreliable: keys may be skipped or (rarely) visited twice.
- *
- * There is one specific exception: the special case of deleting the
- * current key does not undermine the reliability of the traversal.
- *
- * On success, returns the number of keys iterated. On error returns
- * a negative enum TDB_ERROR value.
- */
-#define tdb_traverse(tdb, fn, p) \
- tdb_traverse_(tdb, typesafe_cb_preargs(int, void *, (fn), (p), \
- struct tdb_context *, \
- TDB_DATA, TDB_DATA), (p))
-
-int64_t tdb_traverse_(struct tdb_context *tdb,
- int (*fn)(struct tdb_context *,
- TDB_DATA, TDB_DATA, void *), void *p);
-
-/**
- * tdb_parse_record - operate directly on data in the database.
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key whose record we should hand to @parse
- * @parse: the function to call for the data
- * @data: the private pointer to hand to @parse (types must match).
- *
- * This avoids a copy for many cases, by handing you a pointer into
- * the memory-mapped database. It also locks the record to prevent
- * other accesses at the same time.
- *
- * Do not alter the data handed to parse()!
- */
-#define tdb_parse_record(tdb, key, parse, data) \
- tdb_parse_record_((tdb), (key), \
- typesafe_cb_preargs(enum TDB_ERROR, void *, \
- (parse), (data), \
- TDB_DATA, TDB_DATA), (data))
-
-enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
- TDB_DATA key,
- enum TDB_ERROR (*parse)(TDB_DATA k,
- TDB_DATA d,
- void *data),
- void *data);
-
-/**
- * tdb_get_seqnum - get a database sequence number
- * @tdb: the tdb context returned from tdb_open()
- *
- * This returns a sequence number: any change to the database from a
- * tdb context opened with the TDB_SEQNUM flag will cause that number
- * to increment. Note that the incrementing is unreliable (it is done
- * without locking), so this is only useful as an optimization.
- *
- * For example, you may have a regular database backup routine which
- * does not operate if the sequence number is unchanged. In the
- * unlikely event of a failed increment, it will be backed up next
- * time any way.
- *
- * Returns an enum TDB_ERROR (ie. negative) on error.
- */
-int64_t tdb_get_seqnum(struct tdb_context *tdb);
-
-/**
- * tdb_firstkey - get the "first" key in a TDB
- * @tdb: the tdb context returned from tdb_open()
- * @key: pointer to key.
- *
- * This returns an arbitrary key in the database; with tdb_nextkey() it allows
- * open-coded traversal of the database, though it is slightly less efficient
- * than tdb_traverse.
- *
- * It is your responsibility to free @key->dptr on success.
- *
- * Returns TDB_ERR_NOEXIST if the database is empty.
- */
-enum TDB_ERROR tdb_firstkey(struct tdb_context *tdb, struct tdb_data *key);
-
-/**
- * tdb_nextkey - get the "next" key in a TDB
- * @tdb: the tdb context returned from tdb_open()
- * @key: a key returned by tdb_firstkey() or tdb_nextkey().
- *
- * This returns another key in the database; it will free @key.dptr for
- * your convenience.
- *
- * Returns TDB_ERR_NOEXIST if there are no more keys.
- */
-enum TDB_ERROR tdb_nextkey(struct tdb_context *tdb, struct tdb_data *key);
-
-/**
- * tdb_chainlock - lock a record in the TDB
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key to lock.
- *
- * This prevents any access occurring to a group of keys including @key,
- * even if @key does not exist. This allows primitive atomic updates of
- * records without using transactions.
- *
- * You cannot begin a transaction while holding a tdb_chainlock(), nor can
- * you do any operations on any other keys in the database. This also means
- * that you cannot hold more than one tdb_chainlock() at a time.
- *
- * See Also:
- * tdb_chainunlock()
- */
-enum TDB_ERROR tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);
-
-/**
- * tdb_chainunlock - unlock a record in the TDB
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key to unlock.
- *
- * The key must have previously been locked by tdb_chainlock().
- */
-void tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
-
-/**
- * tdb_chainlock_read - lock a record in the TDB, for reading
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key to lock.
- *
- * This prevents any changes from occurring to a group of keys including @key,
- * even if @key does not exist. This allows primitive atomic updates of
- * records without using transactions.
- *
- * You cannot begin a transaction while holding a tdb_chainlock_read(), nor can
- * you do any operations on any other keys in the database. This also means
- * that you cannot hold more than one tdb_chainlock()/read() at a time.
- *
- * See Also:
- * tdb_chainlock()
- */
-enum TDB_ERROR tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
-
-/**
- * tdb_chainunlock_read - unlock a record in the TDB for reading
- * @tdb: the tdb context returned from tdb_open()
- * @key: the key to unlock.
- *
- * The key must have previously been locked by tdb_chainlock_read().
- */
-void tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
-
-/**
- * tdb_lockall - lock the entire TDB
- * @tdb: the tdb context returned from tdb_open()
- *
- * You cannot hold a tdb_chainlock while calling this. It nests, so you
- * must call tdb_unlockall as many times as you call tdb_lockall.
- */
-enum TDB_ERROR tdb_lockall(struct tdb_context *tdb);
-
-/**
- * tdb_unlockall - unlock the entire TDB
- * @tdb: the tdb context returned from tdb_open()
- */
-void tdb_unlockall(struct tdb_context *tdb);
-
-/**
- * tdb_lockall_read - lock the entire TDB for reading
- * @tdb: the tdb context returned from tdb_open()
- *
- * This prevents others writing to the database, eg. tdb_delete, tdb_store,
- * tdb_append, but not tdb_fetch.
- *
- * You cannot hold a tdb_chainlock while calling this. It nests, so you
- * must call tdb_unlockall_read as many times as you call tdb_lockall_read.
- */
-enum TDB_ERROR tdb_lockall_read(struct tdb_context *tdb);
-
-/**
- * tdb_unlockall_read - unlock the entire TDB for reading
- * @tdb: the tdb context returned from tdb_open()
- */
-void tdb_unlockall_read(struct tdb_context *tdb);
-
-/**
- * tdb_wipe_all - wipe the database clean
- * @tdb: the tdb context returned from tdb_open()
- *
- * Completely erase the database. This is faster than iterating through
- * each key and doing tdb_delete.
- */
-enum TDB_ERROR tdb_wipe_all(struct tdb_context *tdb);
-
-/**
- * tdb_repack - repack the database
- * @tdb: the tdb context returned from tdb_open()
- *
- * This repacks the database; if it is suffering from a great deal of
- * fragmentation this might help. However, it can take twice the
- * memory of the existing TDB.
- */
-enum TDB_ERROR tdb_repack(struct tdb_context *tdb);
-
-/**
- * tdb_check - check a TDB for consistency
- * @tdb: the tdb context returned from tdb_open()
- * @check: function to check each key/data pair (or NULL)
- * @data: argument for @check, must match type.
- *
- * This performs a consistency check of the open database, optionally calling
- * a check() function on each record so you can do your own data consistency
- * checks as well. If check() returns an error, that is returned from
- * tdb_check().
- *
- * Note that the TDB uses a feature which we don't understand which
- * indicates we can't run tdb_check(), this will log a warning to that
- * effect and return TDB_SUCCESS. You can detect this condition by
- * looking for TDB_CANT_CHECK in tdb_get_flags().
- *
- * Returns TDB_SUCCESS or an error.
- */
-#define tdb_check(tdb, check, data) \
- tdb_check_((tdb), typesafe_cb_preargs(enum TDB_ERROR, void *, \
- (check), (data), \
- struct tdb_data, \
- struct tdb_data), \
- (data))
-
-enum TDB_ERROR tdb_check_(struct tdb_context *tdb,
- enum TDB_ERROR (*check)(struct tdb_data k,
- struct tdb_data d,
- void *data),
- void *data);
-
-/**
- * tdb_error - get the last error (not threadsafe)
- * @tdb: the tdb context returned from tdb_open()
- *
- * Returns the last error returned by a TDB function.
- *
- * This makes porting from TDB1 easier, but note that the last error is not
- * reliable in threaded programs.
- */
-enum TDB_ERROR tdb_error(struct tdb_context *tdb);
-
-/**
- * enum tdb_summary_flags - flags for tdb_summary.
- */
-enum tdb_summary_flags {
- TDB_SUMMARY_HISTOGRAMS = 1 /* Draw graphs in the summary. */
-};
-
-/**
- * tdb_summary - return a string describing the TDB state
- * @tdb: the tdb context returned from tdb_open()
- * @flags: flags to control the summary output.
- * @summary: pointer to string to allocate.
- *
- * This returns a developer-readable string describing the overall
- * state of the tdb, such as the percentage used and sizes of records.
- * It is designed to provide information about the tdb at a glance
- * without displaying any keys or data in the database.
- *
- * On success, sets @summary to point to a malloc()'ed nul-terminated
- * multi-line string. It is your responsibility to free() it.
- */
-enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
- enum tdb_summary_flags flags,
- char **summary);
-
-
-/**
- * tdb_get_flags - return the flags for a tdb
- * @tdb: the tdb context returned from tdb_open()
- *
- * This returns the flags on the current tdb. Some of these are caused by
- * the flags argument to tdb_open(), others (such as TDB_CONVERT) are
- * intuited.
- */
-unsigned int tdb_get_flags(struct tdb_context *tdb);
-
-/**
- * tdb_add_flag - set a flag for a tdb
- * @tdb: the tdb context returned from tdb_open()
- * @flag: one of TDB_NOLOCK, TDB_NOMMAP, TDB_NOSYNC or TDB_ALLOW_NESTING.
- *
- * You can use this to set a flag on the TDB. You cannot set these flags
- * on a TDB_INTERNAL tdb.
- */
-void tdb_add_flag(struct tdb_context *tdb, unsigned flag);
-
-/**
- * tdb_remove_flag - unset a flag for a tdb
- * @tdb: the tdb context returned from tdb_open()
- * @flag: one of TDB_NOLOCK, TDB_NOMMAP, TDB_NOSYNC or TDB_ALLOW_NESTING.
- *
- * You can use this to clear a flag on the TDB. You cannot clear flags
- * on a TDB_INTERNAL tdb.
- */
-void tdb_remove_flag(struct tdb_context *tdb, unsigned flag);
-
-/**
- * enum tdb_attribute_type - descriminator for union tdb_attribute.
- */
-enum tdb_attribute_type {
- TDB_ATTRIBUTE_LOG = 0,
- TDB_ATTRIBUTE_HASH = 1,
- TDB_ATTRIBUTE_SEED = 2,
- TDB_ATTRIBUTE_STATS = 3,
- TDB_ATTRIBUTE_OPENHOOK = 4,
- TDB_ATTRIBUTE_FLOCK = 5,
-};
-
-/**
- * tdb_get_attribute - get an attribute for an existing tdb
- * @tdb: the tdb context returned from tdb_open()
- * @attr: the union tdb_attribute to set.
- *
- * This gets an attribute from a TDB which has previously been set (or
- * may return the default values). Set @attr.base.attr to the
- * attribute type you want get.
- */
-enum TDB_ERROR tdb_get_attribute(struct tdb_context *tdb,
- union tdb_attribute *attr);
-
-/**
- * tdb_set_attribute - set an attribute for an existing tdb
- * @tdb: the tdb context returned from tdb_open()
- * @attr: the union tdb_attribute to set.
- *
- * This sets an attribute on a TDB, overriding any previous attribute
- * of the same type. It returns TDB_ERR_EINVAL if the attribute is
- * unknown or invalid.
- *
- * Note that TDB_ATTRIBUTE_HASH, TDB_ATTRIBUTE_SEED, and
- * TDB_ATTRIBUTE_OPENHOOK cannot currently be set after tdb_open.
- */
-enum TDB_ERROR tdb_set_attribute(struct tdb_context *tdb,
- const union tdb_attribute *attr);
-
-/**
- * tdb_unset_attribute - reset an attribute for an existing tdb
- * @tdb: the tdb context returned from tdb_open()
- * @type: the attribute type to unset.
- *
- * This unsets an attribute on a TDB, returning it to the defaults
- * (where applicable).
- *
- * Note that it only makes sense for TDB_ATTRIBUTE_LOG and TDB_ATTRIBUTE_FLOCK
- * to be unset.
- */
-void tdb_unset_attribute(struct tdb_context *tdb,
- enum tdb_attribute_type type);
-
-/**
- * tdb_name - get the name of a tdb
- * @tdb: the tdb context returned from tdb_open()
- *
- * This returns a copy of the name string, made at tdb_open() time. If that
- * argument was NULL (possible for a TDB_INTERNAL db) this will return NULL.
- *
- * This is mostly useful for logging.
- */
-const char *tdb_name(const struct tdb_context *tdb);
-
-/**
- * tdb_fd - get the file descriptor of a tdb
- * @tdb: the tdb context returned from tdb_open()
- *
- * This returns the file descriptor for the underlying database file, or -1
- * for TDB_INTERNAL.
- */
-int tdb_fd(const struct tdb_context *tdb);
-
-/**
- * tdb_foreach - iterate through every open TDB.
- * @fn: the function to call for every TDB
- * @p: the pointer to hand to @fn
- *
- * TDB internally keeps track of all open TDBs; this function allows you to
- * iterate through them. If @fn returns non-zero, traversal stops.
- */
-#define tdb_foreach(fn, p) \
- tdb_foreach_(typesafe_cb_preargs(int, void *, (fn), (p), \
- struct tdb_context *), (p))
-
-void tdb_foreach_(int (*fn)(struct tdb_context *, void *), void *p);
-
-/**
- * struct tdb_attribute_base - common fields for all tdb attributes.
- */
-struct tdb_attribute_base {
- enum tdb_attribute_type attr;
- union tdb_attribute *next;
-};
-
-/**
- * enum tdb_log_level - log levels for tdb_attribute_log
- * @TDB_LOG_ERROR: used to log unrecoverable errors such as I/O errors
- * or internal consistency failures.
- * @TDB_LOG_USE_ERROR: used to log usage errors such as invalid parameters
- * or writing to a read-only database.
- * @TDB_LOG_WARNING: used for informational messages on issues which
- * are unusual but handled by TDB internally, such
- * as a failure to mmap or failure to open /dev/urandom.
- */
-enum tdb_log_level {
- TDB_LOG_ERROR,
- TDB_LOG_USE_ERROR,
- TDB_LOG_WARNING
-};
-
-/**
- * struct tdb_attribute_log - log function attribute
- *
- * This attribute provides a hook for you to log errors.
- */
-struct tdb_attribute_log {
- struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_LOG */
- void (*fn)(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
- const char *message,
- void *data);
- void *data;
-};
-
-/**
- * struct tdb_attribute_hash - hash function attribute
- *
- * This attribute allows you to provide an alternative hash function.
- * This hash function will be handed keys from the database; it will also
- * be handed the 8-byte TDB_HASH_MAGIC value for checking the header (the
- * tdb_open() will fail if the hash value doesn't match the header).
- *
- * Note that if your hash function gives different results on
- * different machine endians, your tdb will no longer work across
- * different architectures!
- */
-struct tdb_attribute_hash {
- struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_HASH */
- uint64_t (*fn)(const void *key, size_t len, uint64_t seed,
- void *data);
- void *data;
-};
-
-/**
- * struct tdb_attribute_seed - hash function seed attribute
- *
- * The hash function seed is normally taken from /dev/urandom (or equivalent)
- * but can be set manually here. This is mainly for testing purposes.
- */
-struct tdb_attribute_seed {
- struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_SEED */
- uint64_t seed;
-};
-
-/**
- * struct tdb_attribute_stats - tdb operational statistics
- *
- * This attribute records statistics of various low-level TDB operations.
- * This can be used to assist performance evaluation. This is only
- * useful for tdb_get_attribute().
- *
- * New fields will be added at the end, hence the "size" argument which
- * indicates how large your structure is: it must be filled in before
- * calling tdb_get_attribute(), which will overwrite it with the size
- * tdb knows about.
- */
-struct tdb_attribute_stats {
- struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_STATS */
- size_t size; /* = sizeof(struct tdb_attribute_stats) */
- uint64_t allocs;
- uint64_t alloc_subhash;
- uint64_t alloc_chain;
- uint64_t alloc_bucket_exact;
- uint64_t alloc_bucket_max;
- uint64_t alloc_leftover;
- uint64_t alloc_coalesce_tried;
- uint64_t alloc_coalesce_iterate_clash;
- uint64_t alloc_coalesce_lockfail;
- uint64_t alloc_coalesce_race;
- uint64_t alloc_coalesce_succeeded;
- uint64_t alloc_coalesce_num_merged;
- uint64_t compares;
- uint64_t compare_wrong_bucket;
- uint64_t compare_wrong_offsetbits;
- uint64_t compare_wrong_keylen;
- uint64_t compare_wrong_rechash;
- uint64_t compare_wrong_keycmp;
- uint64_t transactions;
- uint64_t transaction_cancel;
- uint64_t transaction_nest;
- uint64_t transaction_expand_file;
- uint64_t transaction_read_direct;
- uint64_t transaction_read_direct_fail;
- uint64_t transaction_write_direct;
- uint64_t transaction_write_direct_fail;
- uint64_t expands;
- uint64_t frees;
- uint64_t locks;
- uint64_t lock_lowlevel;
- uint64_t lock_nonblock;
- uint64_t lock_nonblock_fail;
-};
-
-/**
- * struct tdb_attribute_openhook - tdb special effects hook for open
- *
- * This attribute contains a function to call once we have the OPEN_LOCK
- * for the tdb, but before we've examined its contents. If this succeeds,
- * the tdb will be populated if it's then zero-length.
- *
- * This is a hack to allow support for TDB1-style TDB_CLEAR_IF_FIRST
- * behaviour.
- */
-struct tdb_attribute_openhook {
- struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_OPENHOOK */
- enum TDB_ERROR (*fn)(int fd, void *data);
- void *data;
-};
-
-/**
- * struct tdb_attribute_flock - tdb special effects hook for file locking
- *
- * This attribute contains function to call to place locks on a file; it can
- * be used to support non-blocking operations or lock proxying.
- *
- * They should return 0 on success, -1 on failure and set errno.
- *
- * An error will be logged on error if errno is neither EAGAIN nor EINTR
- * (normally it would only return EAGAIN if waitflag is false, and
- * loop internally on EINTR).
- */
-struct tdb_attribute_flock {
- struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_FLOCK */
- int (*lock)(int fd,int rw, off_t off, off_t len, bool waitflag, void *);
- int (*unlock)(int fd, int rw, off_t off, off_t len, void *);
- void *data;
-};
-
-/**
- * union tdb_attribute - tdb attributes.
- *
- * This represents all the known attributes.
- *
- * See also:
- * struct tdb_attribute_log, struct tdb_attribute_hash,
- * struct tdb_attribute_seed, struct tdb_attribute_stats,
- * struct tdb_attribute_openhook, struct tdb_attribute_flock.
- */
-union tdb_attribute {
- struct tdb_attribute_base base;
- struct tdb_attribute_log log;
- struct tdb_attribute_hash hash;
- struct tdb_attribute_seed seed;
- struct tdb_attribute_stats stats;
- struct tdb_attribute_openhook openhook;
- struct tdb_attribute_flock flock;
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* tdb2.h */
diff --git a/lib/tdb2/test/api-13-delete.c b/lib/tdb2/test/api-13-delete.c
deleted file mode 100644
index 279b38645b..0000000000
--- a/lib/tdb2/test/api-13-delete.c
+++ /dev/null
@@ -1,205 +0,0 @@
-#include "private.h" // For TDB_TOPLEVEL_HASH_BITS
-#include <ccan/hash/hash.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "tdb2.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-/* We rig the hash so adjacent-numbered records always clash. */
-static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
-{
- return ((uint64_t)*(const unsigned int *)key)
- << (64 - TDB_TOPLEVEL_HASH_BITS - 1);
-}
-
-/* We use the same seed which we saw a failure on. */
-static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
-{
- return hash64_stable((const unsigned char *)key, len,
- *(uint64_t *)p);
-}
-
-static bool store_records(struct tdb_context *tdb)
-{
- int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data d, data = { (unsigned char *)&i, sizeof(i) };
-
- for (i = 0; i < 1000; i++) {
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
- return false;
- tdb_fetch(tdb, key, &d);
- if (!tdb_deq(d, data))
- return false;
- free(d.dptr);
- }
- return true;
-}
-
-static void test_val(struct tdb_context *tdb, uint64_t val)
-{
- uint64_t v;
- struct tdb_data key = { (unsigned char *)&v, sizeof(v) };
- struct tdb_data d, data = { (unsigned char *)&v, sizeof(v) };
-
- /* Insert an entry, then delete it. */
- v = val;
- /* Delete should fail. */
- ok1(tdb_delete(tdb, key) == TDB_ERR_NOEXIST);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Insert should succeed. */
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Delete should succeed. */
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Re-add it, then add collision. */
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- v = val + 1;
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Can find both? */
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
- v = val;
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
-
- /* Delete second one. */
- v = val + 1;
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Re-add */
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Now, try deleting first one. */
- v = val;
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Can still find second? */
- v = val + 1;
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
-
- /* Now, this will be ideally placed. */
- v = val + 2;
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* This will collide with both. */
- v = val;
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
-
- /* We can still find them all, right? */
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
- v = val + 1;
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
- v = val + 2;
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
-
- /* And if we delete val + 1, that val + 2 should not move! */
- v = val + 1;
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- v = val;
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
- v = val + 2;
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == data.dsize);
- free(d.dptr);
-
- /* Delete those two, so we are empty. */
- ok1(tdb_delete(tdb, key) == 0);
- v = val;
- ok1(tdb_delete(tdb, key) == 0);
-
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i, j;
- struct tdb_context *tdb;
- uint64_t seed = 16014841315512641303ULL;
- union tdb_attribute clash_hattr
- = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
- .fn = clash } };
- union tdb_attribute fixed_hattr
- = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
- .fn = fixedhash,
- .data = &seed } };
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- /* These two values gave trouble before. */
- int vals[] = { 755, 837 };
-
- clash_hattr.base.next = &tap_log_attr;
- fixed_hattr.base.next = &tap_log_attr;
-
- plan_tests(sizeof(flags) / sizeof(flags[0])
- * (39 * 3 + 5 + sizeof(vals)/sizeof(vals[0])*2) + 1);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-13-delete.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &clash_hattr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- /* Check start of hash table. */
- test_val(tdb, 0);
-
- /* Check end of hash table. */
- test_val(tdb, -1ULL);
-
- /* Check mixed bitpattern. */
- test_val(tdb, 0x123456789ABCDEF0ULL);
-
- ok1(!tdb->file || (tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0));
- tdb_close(tdb);
-
- /* Deleting these entries in the db gave problems. */
- tdb = tdb_open("run-13-delete.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &fixed_hattr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- ok1(store_records(tdb));
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- for (j = 0; j < sizeof(vals)/sizeof(vals[0]); j++) {
- struct tdb_data key;
-
- key.dptr = (unsigned char *)&vals[j];
- key.dsize = sizeof(vals[j]);
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- }
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-14-exists.c b/lib/tdb2/test/api-14-exists.c
deleted file mode 100644
index 801c295893..0000000000
--- a/lib/tdb2/test/api-14-exists.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "config.h"
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "logging.h"
-
-static bool test_records(struct tdb_context *tdb)
-{
- int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
-
- for (i = 0; i < 1000; i++) {
- if (tdb_exists(tdb, key))
- return false;
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
- return false;
- if (!tdb_exists(tdb, key))
- return false;
- }
-
- for (i = 0; i < 1000; i++) {
- if (!tdb_exists(tdb, key))
- return false;
- if (tdb_delete(tdb, key) != 0)
- return false;
- if (tdb_exists(tdb, key))
- return false;
- }
- return true;
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 2 + 1);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-14-exists.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (ok1(tdb))
- ok1(test_records(tdb));
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-16-wipe_all.c b/lib/tdb2/test/api-16-wipe_all.c
deleted file mode 100644
index 3dfcc7a419..0000000000
--- a/lib/tdb2/test/api-16-wipe_all.c
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "config.h"
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "logging.h"
-
-static bool add_records(struct tdb_context *tdb)
-{
- int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
-
- for (i = 0; i < 1000; i++) {
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
- return false;
- }
- return true;
-}
-
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 1);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-16-wipe_all.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (ok1(tdb)) {
- struct tdb_data key;
- ok1(add_records(tdb));
- ok1(tdb_wipe_all(tdb) == TDB_SUCCESS);
- ok1(tdb_firstkey(tdb, &key) == TDB_ERR_NOEXIST);
- tdb_close(tdb);
- }
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-21-parse_record.c b/lib/tdb2/test/api-21-parse_record.c
deleted file mode 100644
index 150e1c9dd0..0000000000
--- a/lib/tdb2/test/api-21-parse_record.c
+++ /dev/null
@@ -1,67 +0,0 @@
-#include "config.h"
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "logging.h"
-
-static enum TDB_ERROR parse(TDB_DATA key, TDB_DATA data, TDB_DATA *expected)
-{
- if (!tdb_deq(data, *expected))
- return TDB_ERR_EINVAL;
- return TDB_SUCCESS;
-}
-
-static enum TDB_ERROR parse_err(TDB_DATA key, TDB_DATA data, void *unused)
-{
- return 100;
-}
-
-static bool test_records(struct tdb_context *tdb)
-{
- int i;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
-
- for (i = 0; i < 1000; i++) {
- if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
- return false;
- }
-
- for (i = 0; i < 1000; i++) {
- if (tdb_parse_record(tdb, key, parse, &data) != TDB_SUCCESS)
- return false;
- }
-
- if (tdb_parse_record(tdb, key, parse, &data) != TDB_ERR_NOEXIST)
- return false;
-
- /* Test error return from parse function. */
- i = 0;
- if (tdb_parse_record(tdb, key, parse_err, NULL) != 100)
- return false;
-
- return true;
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 2 + 1);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("api-21-parse_record.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (ok1(tdb))
- ok1(test_records(tdb));
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-55-transaction.c b/lib/tdb2/test/api-55-transaction.c
deleted file mode 100644
index c474c6abc3..0000000000
--- a/lib/tdb2/test/api-55-transaction.c
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "private.h" // struct tdb_context
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- unsigned char *buffer;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data;
-
- buffer = malloc(1000);
- for (i = 0; i < 1000; i++)
- buffer[i] = i;
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 20 + 1);
-
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-55-transaction.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- ok1(tdb_transaction_start(tdb) == 0);
- data.dptr = buffer;
- data.dsize = 1000;
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
- ok1(data.dsize == 1000);
- ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
- free(data.dptr);
-
- /* Cancelling a transaction means no store */
- tdb_transaction_cancel(tdb);
- ok1(tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_fetch(tdb, key, &data) == TDB_ERR_NOEXIST);
-
- /* Commit the transaction. */
- ok1(tdb_transaction_start(tdb) == 0);
- data.dptr = buffer;
- data.dsize = 1000;
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
- ok1(data.dsize == 1000);
- ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
- free(data.dptr);
- ok1(tdb_transaction_commit(tdb) == 0);
- ok1(tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
- ok1(data.dsize == 1000);
- ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
- free(data.dptr);
-
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- free(buffer);
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-81-seqnum.c b/lib/tdb2/test/api-81-seqnum.c
deleted file mode 100644
index 8bf261d635..0000000000
--- a/lib/tdb2/test/api-81-seqnum.c
+++ /dev/null
@@ -1,69 +0,0 @@
-#include "config.h"
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i, seq;
- struct tdb_context *tdb;
- struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 15 + 4 * 13);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("api-81-seqnum.tdb", flags[i]|TDB_SEQNUM,
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
- continue;
-
- seq = 0;
- ok1(tdb_get_seqnum(tdb) == seq);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_get_seqnum(tdb) == ++seq);
- /* Fetch doesn't change seqnum */
- if (ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS))
- free(d.dptr);
- ok1(tdb_get_seqnum(tdb) == seq);
- ok1(tdb_append(tdb, key, data) == TDB_SUCCESS);
- ok1(tdb_get_seqnum(tdb) == ++seq);
-
- ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
- ok1(tdb_get_seqnum(tdb) == ++seq);
- /* Empty append works */
- ok1(tdb_append(tdb, key, data) == TDB_SUCCESS);
- ok1(tdb_get_seqnum(tdb) == ++seq);
-
- ok1(tdb_wipe_all(tdb) == TDB_SUCCESS);
- ok1(tdb_get_seqnum(tdb) == ++seq);
-
- if (!(flags[i] & TDB_INTERNAL)) {
- ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_get_seqnum(tdb) == ++seq);
- ok1(tdb_append(tdb, key, data) == TDB_SUCCESS);
- ok1(tdb_get_seqnum(tdb) == ++seq);
- ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
- ok1(tdb_get_seqnum(tdb) == ++seq);
- ok1(tdb_transaction_commit(tdb) == TDB_SUCCESS);
- ok1(tdb_get_seqnum(tdb) == seq);
-
- ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_get_seqnum(tdb) == seq + 1);
- tdb_transaction_cancel(tdb);
- ok1(tdb_get_seqnum(tdb) == seq);
- }
- tdb_close(tdb);
- ok1(tap_log_messages == 0);
- }
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-92-get-set-readonly.c b/lib/tdb2/test/api-92-get-set-readonly.c
deleted file mode 100644
index 46aea7ae0d..0000000000
--- a/lib/tdb2/test/api-92-get-set-readonly.c
+++ /dev/null
@@ -1,105 +0,0 @@
-#include "config.h"
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 48);
-
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- /* RW -> R0 */
- tdb = tdb_open("run-92-get-set-readonly.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- ok1(!(tdb_get_flags(tdb) & TDB_RDONLY));
-
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == TDB_SUCCESS);
-
- tdb_add_flag(tdb, TDB_RDONLY);
- ok1(tdb_get_flags(tdb) & TDB_RDONLY);
-
- /* Can't store, append, delete. */
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 1);
- ok1(tdb_append(tdb, key, data) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 2);
- ok1(tdb_delete(tdb, key) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 3);
-
- /* Can't start a transaction, or any write lock. */
- ok1(tdb_transaction_start(tdb) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 4);
- ok1(tdb_chainlock(tdb, key) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 5);
- ok1(tdb_lockall(tdb) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 6);
- ok1(tdb_wipe_all(tdb) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 7);
-
- /* Back to RW. */
- tdb_remove_flag(tdb, TDB_RDONLY);
- ok1(!(tdb_get_flags(tdb) & TDB_RDONLY));
-
- ok1(tdb_store(tdb, key, data, TDB_MODIFY) == TDB_SUCCESS);
- ok1(tdb_append(tdb, key, data) == TDB_SUCCESS);
- ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
-
- ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == TDB_SUCCESS);
- ok1(tdb_transaction_commit(tdb) == TDB_SUCCESS);
-
- ok1(tdb_chainlock(tdb, key) == TDB_SUCCESS);
- tdb_chainunlock(tdb, key);
- ok1(tdb_lockall(tdb) == TDB_SUCCESS);
- tdb_unlockall(tdb);
- ok1(tdb_wipe_all(tdb) == TDB_SUCCESS);
- ok1(tap_log_messages == 7);
-
- tdb_close(tdb);
-
- /* R0 -> RW */
- tdb = tdb_open("run-92-get-set-readonly.tdb", flags[i],
- O_RDONLY, 0600, &tap_log_attr);
- ok1(tdb);
- ok1(tdb_get_flags(tdb) & TDB_RDONLY);
-
- /* Can't store, append, delete. */
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 8);
- ok1(tdb_append(tdb, key, data) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 9);
- ok1(tdb_delete(tdb, key) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 10);
-
- /* Can't start a transaction, or any write lock. */
- ok1(tdb_transaction_start(tdb) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 11);
- ok1(tdb_chainlock(tdb, key) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 12);
- ok1(tdb_lockall(tdb) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 13);
- ok1(tdb_wipe_all(tdb) == TDB_ERR_RDONLY);
- ok1(tap_log_messages == 14);
-
- /* Can't remove TDB_RDONLY since we opened with O_RDONLY */
- tdb_remove_flag(tdb, TDB_RDONLY);
- ok1(tap_log_messages == 15);
- ok1(tdb_get_flags(tdb) & TDB_RDONLY);
- tdb_close(tdb);
-
- ok1(tap_log_messages == 15);
- tap_log_messages = 0;
- }
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-add-remove-flags.c b/lib/tdb2/test/api-add-remove-flags.c
deleted file mode 100644
index a72b609fcb..0000000000
--- a/lib/tdb2/test/api-add-remove-flags.c
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "private.h" // for tdb_context
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(87);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-add-remove-flags.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- ok1(tdb_get_flags(tdb) == tdb->flags);
- tap_log_messages = 0;
- tdb_add_flag(tdb, TDB_NOLOCK);
- if (flags[i] & TDB_INTERNAL)
- ok1(tap_log_messages == 1);
- else {
- ok1(tap_log_messages == 0);
- ok1(tdb_get_flags(tdb) & TDB_NOLOCK);
- }
-
- tap_log_messages = 0;
- tdb_add_flag(tdb, TDB_NOMMAP);
- if (flags[i] & TDB_INTERNAL)
- ok1(tap_log_messages == 1);
- else {
- ok1(tap_log_messages == 0);
- ok1(tdb_get_flags(tdb) & TDB_NOMMAP);
- ok1(tdb->file->map_ptr == NULL);
- }
-
- tap_log_messages = 0;
- tdb_add_flag(tdb, TDB_NOSYNC);
- if (flags[i] & TDB_INTERNAL)
- ok1(tap_log_messages == 1);
- else {
- ok1(tap_log_messages == 0);
- ok1(tdb_get_flags(tdb) & TDB_NOSYNC);
- }
-
- ok1(tdb_get_flags(tdb) == tdb->flags);
-
- tap_log_messages = 0;
- tdb_remove_flag(tdb, TDB_NOLOCK);
- if (flags[i] & TDB_INTERNAL)
- ok1(tap_log_messages == 1);
- else {
- ok1(tap_log_messages == 0);
- ok1(!(tdb_get_flags(tdb) & TDB_NOLOCK));
- }
-
- tap_log_messages = 0;
- tdb_remove_flag(tdb, TDB_NOMMAP);
- if (flags[i] & TDB_INTERNAL)
- ok1(tap_log_messages == 1);
- else {
- ok1(tap_log_messages == 0);
- ok1(!(tdb_get_flags(tdb) & TDB_NOMMAP));
- ok1(tdb->file->map_ptr != NULL);
- }
-
- tap_log_messages = 0;
- tdb_remove_flag(tdb, TDB_NOSYNC);
- if (flags[i] & TDB_INTERNAL)
- ok1(tap_log_messages == 1);
- else {
- ok1(tap_log_messages == 0);
- ok1(!(tdb_get_flags(tdb) & TDB_NOSYNC));
- }
-
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-open-multiple-times.c b/lib/tdb2/test/api-open-multiple-times.c
deleted file mode 100644
index 38aea135ac..0000000000
--- a/lib/tdb2/test/api-open-multiple-times.c
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "config.h"
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb, *tdb2;
- struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
- struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 28);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-open-multiple-times.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- tdb2 = tdb_open("run-open-multiple-times.tdb", flags[i],
- O_RDWR|O_CREAT, 0600, &tap_log_attr);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_check(tdb2, NULL, NULL) == 0);
-
- /* Store in one, fetch in the other. */
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
- ok1(tdb_fetch(tdb2, key, &d) == TDB_SUCCESS);
- ok1(tdb_deq(d, data));
- free(d.dptr);
-
- /* Vice versa, with delete. */
- ok1(tdb_delete(tdb2, key) == 0);
- ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_NOEXIST);
-
- /* OK, now close first one, check second still good. */
- ok1(tdb_close(tdb) == 0);
-
- ok1(tdb_store(tdb2, key, data, TDB_REPLACE) == 0);
- ok1(tdb_fetch(tdb2, key, &d) == TDB_SUCCESS);
- ok1(tdb_deq(d, data));
- free(d.dptr);
-
- /* Reopen */
- tdb = tdb_open("run-open-multiple-times.tdb", flags[i],
- O_RDWR|O_CREAT, 0600, &tap_log_attr);
- ok1(tdb);
-
- ok1(tdb_transaction_start(tdb2) == 0);
-
- /* Anything in the other one should fail. */
- ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_LOCK);
- ok1(tap_log_messages == 1);
- ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_ERR_LOCK);
- ok1(tap_log_messages == 2);
- ok1(tdb_transaction_start(tdb) == TDB_ERR_LOCK);
- ok1(tap_log_messages == 3);
- ok1(tdb_chainlock(tdb, key) == TDB_ERR_LOCK);
- ok1(tap_log_messages == 4);
-
- /* Transaciton should work as normal. */
- ok1(tdb_store(tdb2, key, data, TDB_REPLACE) == TDB_SUCCESS);
-
- /* Now... try closing with locks held. */
- ok1(tdb_close(tdb2) == 0);
-
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(tdb_deq(d, data));
- free(d.dptr);
- ok1(tdb_close(tdb) == 0);
- ok1(tap_log_messages == 4);
- tap_log_messages = 0;
- }
-
- return exit_status();
-}
diff --git a/lib/tdb2/test/api-simple-delete.c b/lib/tdb2/test/api-simple-delete.c
deleted file mode 100644
index 48b077a6db..0000000000
--- a/lib/tdb2/test/api-simple-delete.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "config.h"
-#include "tdb2.h"
-#include "tap-interface.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- tdb = tdb_open("run-simple-delete.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (tdb) {
- /* Delete should fail. */
- ok1(tdb_delete(tdb, key) == TDB_ERR_NOEXIST);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- /* Insert should succeed. */
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- /* Delete should now work. */
- ok1(tdb_delete(tdb, key) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- }
- }
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/helpapi-external-agent.c b/lib/tdb2/test/helpapi-external-agent.c
deleted file mode 100644
index 59e1c6cbee..0000000000
--- a/lib/tdb2/test/helpapi-external-agent.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "external-agent.h"
-
-/* This isn't possible with via the tdb2 API, but this makes it link. */
-enum agent_return external_agent_needs_rec(struct tdb_context *tdb)
-{
- return FAILED;
-}
diff --git a/lib/tdb2/test/helprun-external-agent.c b/lib/tdb2/test/helprun-external-agent.c
deleted file mode 100644
index 9f243824fd..0000000000
--- a/lib/tdb2/test/helprun-external-agent.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "external-agent.h"
-#include "private.h"
-
-enum agent_return external_agent_needs_rec(struct tdb_context *tdb)
-{
- return tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
-}
diff --git a/lib/tdb2/test/helprun-layout.c b/lib/tdb2/test/helprun-layout.c
deleted file mode 100644
index b9cd4a6432..0000000000
--- a/lib/tdb2/test/helprun-layout.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/* TDB tools to create various canned database layouts. */
-#include "layout.h"
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <ccan/err/err.h>
-#include "logging.h"
-
-struct tdb_layout *new_tdb_layout(void)
-{
- struct tdb_layout *layout = malloc(sizeof(*layout));
- layout->num_elems = 0;
- layout->elem = NULL;
- return layout;
-}
-
-static void add(struct tdb_layout *layout, union tdb_layout_elem elem)
-{
- layout->elem = realloc(layout->elem,
- sizeof(layout->elem[0])
- * (layout->num_elems+1));
- layout->elem[layout->num_elems++] = elem;
-}
-
-void tdb_layout_add_freetable(struct tdb_layout *layout)
-{
- union tdb_layout_elem elem;
- elem.base.type = FREETABLE;
- add(layout, elem);
-}
-
-void tdb_layout_add_free(struct tdb_layout *layout, tdb_len_t len,
- unsigned ftable)
-{
- union tdb_layout_elem elem;
- elem.base.type = FREE;
- elem.free.len = len;
- elem.free.ftable_num = ftable;
- add(layout, elem);
-}
-
-void tdb_layout_add_capability(struct tdb_layout *layout,
- uint64_t type,
- bool write_breaks,
- bool check_breaks,
- bool open_breaks,
- tdb_len_t extra)
-{
- union tdb_layout_elem elem;
- elem.base.type = CAPABILITY;
- elem.capability.type = type;
- if (write_breaks)
- elem.capability.type |= TDB_CAP_NOWRITE;
- if (open_breaks)
- elem.capability.type |= TDB_CAP_NOOPEN;
- if (check_breaks)
- elem.capability.type |= TDB_CAP_NOCHECK;
- elem.capability.extra = extra;
- add(layout, elem);
-}
-
-static struct tdb_data dup_key(struct tdb_data key)
-{
- struct tdb_data ret;
- ret.dsize = key.dsize;
- ret.dptr = malloc(ret.dsize);
- memcpy(ret.dptr, key.dptr, ret.dsize);
- return ret;
-}
-
-void tdb_layout_add_used(struct tdb_layout *layout,
- TDB_DATA key, TDB_DATA data,
- tdb_len_t extra)
-{
- union tdb_layout_elem elem;
- elem.base.type = DATA;
- elem.used.key = dup_key(key);
- elem.used.data = dup_key(data);
- elem.used.extra = extra;
- add(layout, elem);
-}
-
-static tdb_len_t free_record_len(tdb_len_t len)
-{
- return sizeof(struct tdb_used_record) + len;
-}
-
-static tdb_len_t data_record_len(struct tle_used *used)
-{
- tdb_len_t len;
- len = sizeof(struct tdb_used_record)
- + used->key.dsize + used->data.dsize + used->extra;
- assert(len >= sizeof(struct tdb_free_record));
- return len;
-}
-
-static tdb_len_t hashtable_len(struct tle_hashtable *htable)
-{
- return sizeof(struct tdb_used_record)
- + (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS)
- + htable->extra;
-}
-
-static tdb_len_t capability_len(struct tle_capability *cap)
-{
- return sizeof(struct tdb_capability) + cap->extra;
-}
-
-static tdb_len_t freetable_len(struct tle_freetable *ftable)
-{
- return sizeof(struct tdb_freetable);
-}
-
-static void set_free_record(void *mem, tdb_len_t len)
-{
- /* We do all the work in add_to_freetable */
-}
-
-static void add_zero_pad(struct tdb_used_record *u, size_t len, size_t extra)
-{
- if (extra)
- ((char *)(u + 1))[len] = '\0';
-}
-
-static void set_data_record(void *mem, struct tdb_context *tdb,
- struct tle_used *used)
-{
- struct tdb_used_record *u = mem;
-
- set_header(tdb, u, TDB_USED_MAGIC, used->key.dsize, used->data.dsize,
- used->key.dsize + used->data.dsize + used->extra,
- tdb_hash(tdb, used->key.dptr, used->key.dsize));
- memcpy(u + 1, used->key.dptr, used->key.dsize);
- memcpy((char *)(u + 1) + used->key.dsize,
- used->data.dptr, used->data.dsize);
- add_zero_pad(u, used->key.dsize + used->data.dsize, used->extra);
-}
-
-static void set_hashtable(void *mem, struct tdb_context *tdb,
- struct tle_hashtable *htable)
-{
- struct tdb_used_record *u = mem;
- tdb_len_t len = sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS;
-
- set_header(tdb, u, TDB_HTABLE_MAGIC, 0, len, len + htable->extra, 0);
- memset(u + 1, 0, len);
- add_zero_pad(u, len, htable->extra);
-}
-
-static void set_capability(void *mem, struct tdb_context *tdb,
- struct tle_capability *cap, struct tdb_header *hdr,
- tdb_off_t last_cap)
-{
- struct tdb_capability *c = mem;
- tdb_len_t len = sizeof(*c) - sizeof(struct tdb_used_record) + cap->extra;
-
- c->type = cap->type;
- c->next = 0;
- set_header(tdb, &c->hdr, TDB_CAP_MAGIC, 0, len, len, 0);
-
- /* Append to capability list. */
- if (!last_cap) {
- hdr->capabilities = cap->base.off;
- } else {
- c = (struct tdb_capability *)((char *)hdr + last_cap);
- c->next = cap->base.off;
- }
-}
-
-static void set_freetable(void *mem, struct tdb_context *tdb,
- struct tle_freetable *freetable, struct tdb_header *hdr,
- tdb_off_t last_ftable)
-{
- struct tdb_freetable *ftable = mem;
- memset(ftable, 0, sizeof(*ftable));
- set_header(tdb, &ftable->hdr, TDB_FTABLE_MAGIC, 0,
- sizeof(*ftable) - sizeof(ftable->hdr),
- sizeof(*ftable) - sizeof(ftable->hdr), 0);
-
- if (last_ftable) {
- ftable = (struct tdb_freetable *)((char *)hdr + last_ftable);
- ftable->next = freetable->base.off;
- } else {
- hdr->free_table = freetable->base.off;
- }
-}
-
-static void add_to_freetable(struct tdb_context *tdb,
- tdb_off_t eoff,
- tdb_off_t elen,
- unsigned ftable,
- struct tle_freetable *freetable)
-{
- tdb->ftable_off = freetable->base.off;
- tdb->ftable = ftable;
- add_free_record(tdb, eoff, sizeof(struct tdb_used_record) + elen,
- TDB_LOCK_WAIT, false);
-}
-
-static tdb_off_t hbucket_off(tdb_off_t group_start, unsigned ingroup)
-{
- return group_start
- + (ingroup % (1 << TDB_HASH_GROUP_BITS)) * sizeof(tdb_off_t);
-}
-
-/* Get bits from a value. */
-static uint32_t bits(uint64_t val, unsigned start, unsigned num)
-{
- assert(num <= 32);
- return (val >> start) & ((1U << num) - 1);
-}
-
-/* We take bits from the top: that way we can lock whole sections of the hash
- * by using lock ranges. */
-static uint32_t use_bits(uint64_t h, unsigned num, unsigned *used)
-{
- *used += num;
- return bits(h, 64 - *used, num);
-}
-
-static tdb_off_t encode_offset(tdb_off_t new_off, unsigned bucket,
- uint64_t h)
-{
- return bucket
- | new_off
- | ((uint64_t)bits(h, 64 - TDB_OFF_UPPER_STEAL_EXTRA,
- TDB_OFF_UPPER_STEAL_EXTRA)
- << TDB_OFF_HASH_EXTRA_BIT);
-}
-
-/* FIXME: Our hash table handling here is primitive: we don't expand! */
-static void add_to_hashtable(struct tdb_context *tdb,
- tdb_off_t eoff,
- struct tdb_data key)
-{
- uint64_t h = tdb_hash(tdb, key.dptr, key.dsize);
- tdb_off_t b_off, group_start;
- unsigned i, group, in_group;
- unsigned used = 0;
-
- group = use_bits(h, TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS, &used);
- in_group = use_bits(h, TDB_HASH_GROUP_BITS, &used);
-
- group_start = offsetof(struct tdb_header, hashtable)
- + group * (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
-
- for (i = 0; i < (1 << TDB_HASH_GROUP_BITS); i++) {
- unsigned bucket = (in_group + i) % (1 << TDB_HASH_GROUP_BITS);
-
- b_off = hbucket_off(group_start, bucket);
- if (tdb_read_off(tdb, b_off) == 0) {
- tdb_write_off(tdb, b_off,
- encode_offset(eoff, in_group, h));
- return;
- }
- }
- abort();
-}
-
-static struct tle_freetable *find_ftable(struct tdb_layout *layout, unsigned num)
-{
- unsigned i;
-
- for (i = 0; i < layout->num_elems; i++) {
- if (layout->elem[i].base.type != FREETABLE)
- continue;
- if (num == 0)
- return &layout->elem[i].ftable;
- num--;
- }
- abort();
-}
-
-/* FIXME: Support TDB_CONVERT */
-struct tdb_context *tdb_layout_get(struct tdb_layout *layout,
- void (*freefn)(void *),
- union tdb_attribute *attr)
-{
- unsigned int i;
- tdb_off_t off, len, last_ftable, last_cap;
- char *mem;
- struct tdb_context *tdb;
-
- off = sizeof(struct tdb_header);
-
- /* First pass of layout: calc lengths */
- for (i = 0; i < layout->num_elems; i++) {
- union tdb_layout_elem *e = &layout->elem[i];
- e->base.off = off;
- switch (e->base.type) {
- case FREETABLE:
- len = freetable_len(&e->ftable);
- break;
- case FREE:
- len = free_record_len(e->free.len);
- break;
- case DATA:
- len = data_record_len(&e->used);
- break;
- case HASHTABLE:
- len = hashtable_len(&e->hashtable);
- break;
- case CAPABILITY:
- len = capability_len(&e->capability);
- break;
- default:
- abort();
- }
- off += len;
- }
-
- mem = malloc(off);
- /* Fill with some weird pattern. */
- memset(mem, 0x99, off);
- /* Now populate our header, cribbing from a real TDB header. */
- tdb = tdb_open(NULL, TDB_INTERNAL, O_RDWR, 0, attr);
- memcpy(mem, tdb->file->map_ptr, sizeof(struct tdb_header));
-
- /* Mug the tdb we have to make it use this. */
- freefn(tdb->file->map_ptr);
- tdb->file->map_ptr = mem;
- tdb->file->map_size = off;
-
- last_ftable = 0;
- last_cap = 0;
- for (i = 0; i < layout->num_elems; i++) {
- union tdb_layout_elem *e = &layout->elem[i];
- switch (e->base.type) {
- case FREETABLE:
- set_freetable(mem + e->base.off, tdb, &e->ftable,
- (struct tdb_header *)mem, last_ftable);
- last_ftable = e->base.off;
- break;
- case FREE:
- set_free_record(mem + e->base.off, e->free.len);
- break;
- case DATA:
- set_data_record(mem + e->base.off, tdb, &e->used);
- break;
- case HASHTABLE:
- set_hashtable(mem + e->base.off, tdb, &e->hashtable);
- break;
- case CAPABILITY:
- set_capability(mem + e->base.off, tdb, &e->capability,
- (struct tdb_header *)mem, last_cap);
- last_cap = e->base.off;
- break;
- }
- }
- /* Must have a free table! */
- assert(last_ftable);
-
- /* Now fill the free and hash tables. */
- for (i = 0; i < layout->num_elems; i++) {
- union tdb_layout_elem *e = &layout->elem[i];
- switch (e->base.type) {
- case FREE:
- add_to_freetable(tdb, e->base.off, e->free.len,
- e->free.ftable_num,
- find_ftable(layout, e->free.ftable_num));
- break;
- case DATA:
- add_to_hashtable(tdb, e->base.off, e->used.key);
- break;
- default:
- break;
- }
- }
-
- tdb->ftable_off = find_ftable(layout, 0)->base.off;
- return tdb;
-}
-
-void tdb_layout_write(struct tdb_layout *layout, void (*freefn)(void *),
- union tdb_attribute *attr, const char *filename)
-{
- struct tdb_context *tdb = tdb_layout_get(layout, freefn, attr);
- int fd;
-
- fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600);
- if (fd < 0)
- err(1, "opening %s for writing", filename);
- if (write(fd, tdb->file->map_ptr, tdb->file->map_size)
- != tdb->file->map_size)
- err(1, "writing %s", filename);
- close(fd);
- tdb_close(tdb);
-}
-
-void tdb_layout_free(struct tdb_layout *layout)
-{
- unsigned int i;
-
- for (i = 0; i < layout->num_elems; i++) {
- if (layout->elem[i].base.type == DATA) {
- free(layout->elem[i].used.key.dptr);
- free(layout->elem[i].used.data.dptr);
- }
- }
- free(layout->elem);
- free(layout);
-}
diff --git a/lib/tdb2/test/logging.h b/lib/tdb2/test/logging.h
deleted file mode 100644
index 5f517dc592..0000000000
--- a/lib/tdb2/test/logging.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef TDB2_TEST_LOGGING_H
-#define TDB2_TEST_LOGGING_H
-#include "tdb2.h"
-#include <stdbool.h>
-#include <string.h>
-
-extern bool suppress_logging;
-extern const char *log_prefix;
-extern unsigned tap_log_messages;
-extern union tdb_attribute tap_log_attr;
-extern char *log_last;
-
-void tap_log_fn(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
- const char *message, void *priv);
-#endif /* TDB2_TEST_LOGGING_H */
diff --git a/lib/tdb2/test/run-02-expand.c b/lib/tdb2/test/run-02-expand.c
deleted file mode 100644
index fd1ae4be34..0000000000
--- a/lib/tdb2/test/run-02-expand.c
+++ /dev/null
@@ -1,62 +0,0 @@
-#include <ccan/failtest/failtest_override.h>
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include <ccan/failtest/failtest.h>
-#include "logging.h"
-#include "failtest_helper.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- uint64_t val;
- struct tdb_context *tdb;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 11 + 1);
-
- failtest_init(argc, argv);
- failtest_hook = block_repeat_failures;
- failtest_exit_check = exit_check_log;
-
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- failtest_suppress = true;
- tdb = tdb_open("run-expand.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- if (!ok1(tdb))
- break;
-
- val = tdb->file->map_size;
- /* Need some hash lock for expand. */
- ok1(tdb_lock_hashes(tdb, 0, 1, F_WRLCK, TDB_LOCK_WAIT) == 0);
- failtest_suppress = false;
- if (!ok1(tdb_expand(tdb, 1) == 0)) {
- failtest_suppress = true;
- tdb_close(tdb);
- break;
- }
- failtest_suppress = true;
-
- ok1(tdb->file->map_size >= val + 1 * TDB_EXTENSION_FACTOR);
- ok1(tdb_unlock_hashes(tdb, 0, 1, F_WRLCK) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- val = tdb->file->map_size;
- ok1(tdb_lock_hashes(tdb, 0, 1, F_WRLCK, TDB_LOCK_WAIT) == 0);
- failtest_suppress = false;
- if (!ok1(tdb_expand(tdb, 1024) == 0)) {
- failtest_suppress = true;
- tdb_close(tdb);
- break;
- }
- failtest_suppress = true;
- ok1(tdb_unlock_hashes(tdb, 0, 1, F_WRLCK) == 0);
- ok1(tdb->file->map_size >= val + 1024 * TDB_EXTENSION_FACTOR);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- failtest_exit(exit_status());
-}
diff --git a/lib/tdb2/test/run-03-coalesce.c b/lib/tdb2/test/run-03-coalesce.c
deleted file mode 100644
index ecc469fa32..0000000000
--- a/lib/tdb2/test/run-03-coalesce.c
+++ /dev/null
@@ -1,178 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-#include "layout.h"
-
-static tdb_len_t free_record_length(struct tdb_context *tdb, tdb_off_t off)
-{
- struct tdb_free_record f;
- enum TDB_ERROR ecode;
-
- ecode = tdb_read_convert(tdb, off, &f, sizeof(f));
- if (ecode != TDB_SUCCESS)
- return ecode;
- if (frec_magic(&f) != TDB_FREE_MAGIC)
- return TDB_ERR_CORRUPT;
- return frec_len(&f);
-}
-
-int main(int argc, char *argv[])
-{
- tdb_off_t b_off, test;
- struct tdb_context *tdb;
- struct tdb_layout *layout;
- struct tdb_data data, key;
- tdb_len_t len;
-
- /* FIXME: Test TDB_CONVERT */
- /* FIXME: Test lock order fail. */
-
- plan_tests(42);
- data = tdb_mkdata("world", 5);
- key = tdb_mkdata("hello", 5);
-
- /* No coalescing can be done due to EOF */
- layout = new_tdb_layout();
- tdb_layout_add_freetable(layout);
- len = 1024;
- tdb_layout_add_free(layout, len, 0);
- tdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.tdb");
- /* NOMMAP is for lockcheck. */
- tdb = tdb_open("run-03-coalesce.tdb", TDB_NOMMAP, O_RDWR, 0,
- &tap_log_attr);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(free_record_length(tdb, layout->elem[1].base.off) == len);
-
- /* Figure out which bucket free entry is. */
- b_off = bucket_off(tdb->ftable_off, size_to_bucket(len));
- /* Lock and fail to coalesce. */
- ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
- test = layout->elem[1].base.off;
- ok1(coalesce(tdb, layout->elem[1].base.off, b_off, len, &test)
- == 0);
- tdb_unlock_free_bucket(tdb, b_off);
- ok1(free_record_length(tdb, layout->elem[1].base.off) == len);
- ok1(test == layout->elem[1].base.off);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- tdb_layout_free(layout);
-
- /* No coalescing can be done due to used record */
- layout = new_tdb_layout();
- tdb_layout_add_freetable(layout);
- tdb_layout_add_free(layout, 1024, 0);
- tdb_layout_add_used(layout, key, data, 6);
- tdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.tdb");
- /* NOMMAP is for lockcheck. */
- tdb = tdb_open("run-03-coalesce.tdb", TDB_NOMMAP, O_RDWR, 0,
- &tap_log_attr);
- ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Figure out which bucket free entry is. */
- b_off = bucket_off(tdb->ftable_off, size_to_bucket(1024));
- /* Lock and fail to coalesce. */
- ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
- test = layout->elem[1].base.off;
- ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024, &test)
- == 0);
- tdb_unlock_free_bucket(tdb, b_off);
- ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
- ok1(test == layout->elem[1].base.off);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- tdb_layout_free(layout);
-
- /* Coalescing can be done due to two free records, then EOF */
- layout = new_tdb_layout();
- tdb_layout_add_freetable(layout);
- tdb_layout_add_free(layout, 1024, 0);
- tdb_layout_add_free(layout, 2048, 0);
- tdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.tdb");
- /* NOMMAP is for lockcheck. */
- tdb = tdb_open("run-03-coalesce.tdb", TDB_NOMMAP, O_RDWR, 0,
- &tap_log_attr);
- ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
- ok1(free_record_length(tdb, layout->elem[2].base.off) == 2048);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Figure out which bucket (first) free entry is. */
- b_off = bucket_off(tdb->ftable_off, size_to_bucket(1024));
- /* Lock and coalesce. */
- ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
- test = layout->elem[2].base.off;
- ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024, &test)
- == 1024 + sizeof(struct tdb_used_record) + 2048);
- /* Should tell us it's erased this one... */
- ok1(test == TDB_ERR_NOEXIST);
- ok1(tdb->file->allrecord_lock.count == 0 && tdb->file->num_lockrecs == 0);
- ok1(free_record_length(tdb, layout->elem[1].base.off)
- == 1024 + sizeof(struct tdb_used_record) + 2048);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- tdb_layout_free(layout);
-
- /* Coalescing can be done due to two free records, then data */
- layout = new_tdb_layout();
- tdb_layout_add_freetable(layout);
- tdb_layout_add_free(layout, 1024, 0);
- tdb_layout_add_free(layout, 512, 0);
- tdb_layout_add_used(layout, key, data, 6);
- tdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.tdb");
- /* NOMMAP is for lockcheck. */
- tdb = tdb_open("run-03-coalesce.tdb", TDB_NOMMAP, O_RDWR, 0,
- &tap_log_attr);
- ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
- ok1(free_record_length(tdb, layout->elem[2].base.off) == 512);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Figure out which bucket free entry is. */
- b_off = bucket_off(tdb->ftable_off, size_to_bucket(1024));
- /* Lock and coalesce. */
- ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
- test = layout->elem[2].base.off;
- ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024, &test)
- == 1024 + sizeof(struct tdb_used_record) + 512);
- ok1(tdb->file->allrecord_lock.count == 0 && tdb->file->num_lockrecs == 0);
- ok1(free_record_length(tdb, layout->elem[1].base.off)
- == 1024 + sizeof(struct tdb_used_record) + 512);
- ok1(test == TDB_ERR_NOEXIST);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- tdb_layout_free(layout);
-
- /* Coalescing can be done due to three free records, then EOF */
- layout = new_tdb_layout();
- tdb_layout_add_freetable(layout);
- tdb_layout_add_free(layout, 1024, 0);
- tdb_layout_add_free(layout, 512, 0);
- tdb_layout_add_free(layout, 256, 0);
- tdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.tdb");
- /* NOMMAP is for lockcheck. */
- tdb = tdb_open("run-03-coalesce.tdb", TDB_NOMMAP, O_RDWR, 0,
- &tap_log_attr);
- ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
- ok1(free_record_length(tdb, layout->elem[2].base.off) == 512);
- ok1(free_record_length(tdb, layout->elem[3].base.off) == 256);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Figure out which bucket free entry is. */
- b_off = bucket_off(tdb->ftable_off, size_to_bucket(1024));
- /* Lock and coalesce. */
- ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
- test = layout->elem[2].base.off;
- ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024, &test)
- == 1024 + sizeof(struct tdb_used_record) + 512
- + sizeof(struct tdb_used_record) + 256);
- ok1(tdb->file->allrecord_lock.count == 0
- && tdb->file->num_lockrecs == 0);
- ok1(free_record_length(tdb, layout->elem[1].base.off)
- == 1024 + sizeof(struct tdb_used_record) + 512
- + sizeof(struct tdb_used_record) + 256);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- tdb_layout_free(layout);
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-04-basichash.c b/lib/tdb2/test/run-04-basichash.c
deleted file mode 100644
index dc75fc72dc..0000000000
--- a/lib/tdb2/test/run-04-basichash.c
+++ /dev/null
@@ -1,260 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-/* We rig the hash so adjacent-numbered records always clash. */
-static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
-{
- return ((uint64_t)*(const unsigned int *)key)
- << (64 - TDB_TOPLEVEL_HASH_BITS - 1);
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i, j;
- struct tdb_context *tdb;
- unsigned int v;
- struct tdb_used_record rec;
- struct tdb_data key = { (unsigned char *)&v, sizeof(v) };
- struct tdb_data dbuf = { (unsigned char *)&v, sizeof(v) };
- union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
- .fn = clash } };
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT,
- };
-
- hattr.base.next = &tap_log_attr;
-
- plan_tests(sizeof(flags) / sizeof(flags[0])
- * (91 + (2 * ((1 << TDB_HASH_GROUP_BITS) - 1))) + 1);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- struct hash_info h;
- tdb_off_t new_off, off, subhash;
-
- tdb = tdb_open("run-04-basichash.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- v = 0;
- /* Should not find it. */
- ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in group 0, bucket 0. */
- ok1(h.group_start == offsetof(struct tdb_header, hashtable));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 0);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
-
- /* Should have lock on bucket 0 */
- ok1(h.hlock_start == 0);
- ok1(h.hlock_range ==
- 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
- ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
- ok1((tdb->flags & TDB_NOLOCK)
- || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
- /* FIXME: Check lock length */
-
- /* Allocate a new record. */
- new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h,
- TDB_USED_MAGIC, false);
- ok1(!TDB_OFF_IS_ERR(new_off));
-
- /* We should be able to add it now. */
- ok1(add_to_hash(tdb, &h, new_off) == 0);
-
- /* Make sure we fill it in for later finding. */
- off = new_off + sizeof(struct tdb_used_record);
- ok1(!tdb->io->twrite(tdb, off, key.dptr, key.dsize));
- off += key.dsize;
- ok1(!tdb->io->twrite(tdb, off, dbuf.dptr, dbuf.dsize));
-
- /* We should be able to unlock that OK. */
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_WRLCK) == 0);
-
- /* Database should be consistent. */
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Now, this should give a successful lookup. */
- ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
- == new_off);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in group 0, bucket 0. */
- ok1(h.group_start == offsetof(struct tdb_header, hashtable));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 0);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
-
- /* Should have lock on bucket 0 */
- ok1(h.hlock_start == 0);
- ok1(h.hlock_range ==
- 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
- ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
- ok1((tdb->flags & TDB_NOLOCK)
- || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
- /* FIXME: Check lock length */
-
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_WRLCK) == 0);
-
- /* Database should be consistent. */
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Test expansion. */
- v = 1;
- ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in group 0, bucket 1. */
- ok1(h.group_start == offsetof(struct tdb_header, hashtable));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 1);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
-
- /* Should have lock on bucket 0 */
- ok1(h.hlock_start == 0);
- ok1(h.hlock_range ==
- 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
- ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
- ok1((tdb->flags & TDB_NOLOCK)
- || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
- /* FIXME: Check lock length */
-
- /* Make it expand 0'th bucket. */
- ok1(expand_group(tdb, &h) == 0);
- /* First one should be subhash, next should be empty. */
- ok1(is_subhash(h.group[0]));
- subhash = (h.group[0] & TDB_OFF_MASK);
- for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
- ok1(h.group[j] == 0);
-
- ok1(tdb_write_convert(tdb, h.group_start,
- h.group, sizeof(h.group)) == 0);
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_WRLCK) == 0);
-
- /* Should be happy with expansion. */
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Should be able to find it. */
- v = 0;
- ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
- == new_off);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in expanded group 0, bucket 0. */
- ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 0);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
- + TDB_SUBLEVEL_HASH_BITS);
-
- /* Should have lock on bucket 0 */
- ok1(h.hlock_start == 0);
- ok1(h.hlock_range ==
- 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
- ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
- ok1((tdb->flags & TDB_NOLOCK)
- || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
- /* FIXME: Check lock length */
-
- /* Simple delete should work. */
- ok1(delete_from_hash(tdb, &h) == 0);
- ok1(add_free_record(tdb, new_off,
- sizeof(struct tdb_used_record)
- + rec_key_length(&rec)
- + rec_data_length(&rec)
- + rec_extra_padding(&rec),
- TDB_LOCK_NOWAIT, false) == 0);
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_WRLCK) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Test second-level expansion: should expand 0th bucket. */
- v = 0;
- ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in group 0, bucket 0. */
- ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 0);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS+TDB_SUBLEVEL_HASH_BITS);
-
- /* Should have lock on bucket 0 */
- ok1(h.hlock_start == 0);
- ok1(h.hlock_range ==
- 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
- ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
- ok1((tdb->flags & TDB_NOLOCK)
- || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
- /* FIXME: Check lock length */
-
- ok1(expand_group(tdb, &h) == 0);
- /* First one should be subhash, next should be empty. */
- ok1(is_subhash(h.group[0]));
- subhash = (h.group[0] & TDB_OFF_MASK);
- for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
- ok1(h.group[j] == 0);
- ok1(tdb_write_convert(tdb, h.group_start,
- h.group, sizeof(h.group)) == 0);
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_WRLCK) == 0);
-
- /* Should be happy with expansion. */
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in group 0, bucket 0. */
- ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 0);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
- + TDB_SUBLEVEL_HASH_BITS * 2);
-
- /* We should be able to add it now. */
- /* Allocate a new record. */
- new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h,
- TDB_USED_MAGIC, false);
- ok1(!TDB_OFF_IS_ERR(new_off));
- ok1(add_to_hash(tdb, &h, new_off) == 0);
-
- /* Make sure we fill it in for later finding. */
- off = new_off + sizeof(struct tdb_used_record);
- ok1(!tdb->io->twrite(tdb, off, key.dptr, key.dsize));
- off += key.dsize;
- ok1(!tdb->io->twrite(tdb, off, dbuf.dptr, dbuf.dsize));
-
- /* We should be able to unlock that OK. */
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_WRLCK) == 0);
-
- /* Database should be consistent. */
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Should be able to find it. */
- v = 0;
- ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
- == new_off);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in expanded group 0, bucket 0. */
- ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 0);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
- + TDB_SUBLEVEL_HASH_BITS * 2);
-
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-20-growhash.c b/lib/tdb2/test/run-20-growhash.c
deleted file mode 100644
index 2f634a27c0..0000000000
--- a/lib/tdb2/test/run-20-growhash.c
+++ /dev/null
@@ -1,137 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-static uint64_t myhash(const void *key, size_t len, uint64_t seed, void *priv)
-{
- return *(const uint64_t *)key;
-}
-
-static void add_bits(uint64_t *val, unsigned new, unsigned new_bits,
- unsigned *done)
-{
- *done += new_bits;
- *val |= ((uint64_t)new << (64 - *done));
-}
-
-static uint64_t make_key(unsigned topgroup, unsigned topbucket,
- unsigned subgroup1, unsigned subbucket1,
- unsigned subgroup2, unsigned subbucket2)
-{
- uint64_t key = 0;
- unsigned done = 0;
-
- add_bits(&key, topgroup, TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS,
- &done);
- add_bits(&key, topbucket, TDB_HASH_GROUP_BITS, &done);
- add_bits(&key, subgroup1, TDB_SUBLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS,
- &done);
- add_bits(&key, subbucket1, TDB_HASH_GROUP_BITS, &done);
- add_bits(&key, subgroup2, TDB_SUBLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS,
- &done);
- add_bits(&key, subbucket2, TDB_HASH_GROUP_BITS, &done);
- return key;
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i, j;
- struct tdb_context *tdb;
- uint64_t kdata;
- struct tdb_used_record rec;
- struct tdb_data key = { (unsigned char *)&kdata, sizeof(kdata) };
- struct tdb_data dbuf = { (unsigned char *)&kdata, sizeof(kdata) };
- union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
- .fn = myhash } };
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT,
- };
-
- hattr.base.next = &tap_log_attr;
-
- plan_tests(sizeof(flags) / sizeof(flags[0])
- * (9 + (20 + 2 * ((1 << TDB_HASH_GROUP_BITS) - 2))
- * (1 << TDB_HASH_GROUP_BITS)) + 1);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- struct hash_info h;
-
- tdb = tdb_open("run-20-growhash.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- /* Fill a group. */
- for (j = 0; j < (1 << TDB_HASH_GROUP_BITS); j++) {
- kdata = make_key(0, j, 0, 0, 0, 0);
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- }
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Check first still exists. */
- kdata = make_key(0, 0, 0, 0, 0, 0);
- ok1(find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL) != 0);
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have located space in group 0, bucket 0. */
- ok1(h.group_start == offsetof(struct tdb_header, hashtable));
- ok1(h.home_bucket == 0);
- ok1(h.found_bucket == 0);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
- /* Entire group should be full! */
- for (j = 0; j < (1 << TDB_HASH_GROUP_BITS); j++)
- ok1(h.group[j] != 0);
-
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_RDLCK) == 0);
-
- /* Now, add one more to each should expand (that) bucket. */
- for (j = 0; j < (1 << TDB_HASH_GROUP_BITS); j++) {
- unsigned int k;
- kdata = make_key(0, j, 0, 1, 0, 0);
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- ok1(find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL));
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have moved to subhash */
- ok1(h.group_start >= sizeof(struct tdb_header));
- ok1(h.home_bucket == 1);
- ok1(h.found_bucket == 1);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
- + TDB_SUBLEVEL_HASH_BITS);
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_RDLCK) == 0);
-
- /* Keep adding, make it expand again. */
- for (k = 2; k < (1 << TDB_HASH_GROUP_BITS); k++) {
- kdata = make_key(0, j, 0, k, 0, 0);
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- }
-
- /* This should tip it over to sub-sub-hash. */
- kdata = make_key(0, j, 0, 0, 0, 1);
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- ok1(find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL));
- /* Should have created correct hash. */
- ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
- /* Should have moved to subhash */
- ok1(h.group_start >= sizeof(struct tdb_header));
- ok1(h.home_bucket == 1);
- ok1(h.found_bucket == 1);
- ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
- + TDB_SUBLEVEL_HASH_BITS + TDB_SUBLEVEL_HASH_BITS);
- ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
- F_RDLCK) == 0);
- }
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-25-hashoverload.c b/lib/tdb2/test/run-25-hashoverload.c
deleted file mode 100644
index 850321554a..0000000000
--- a/lib/tdb2/test/run-25-hashoverload.c
+++ /dev/null
@@ -1,113 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-static uint64_t badhash(const void *key, size_t len, uint64_t seed, void *priv)
-{
- return 0;
-}
-
-static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *p)
-{
- if (p)
- return tdb_delete(tdb, key);
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i, j;
- struct tdb_context *tdb;
- struct tdb_data key = { (unsigned char *)&j, sizeof(j) };
- struct tdb_data dbuf = { (unsigned char *)&j, sizeof(j) };
- union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
- .fn = badhash } };
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT,
- };
-
- hattr.base.next = &tap_log_attr;
-
- plan_tests(6883);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */
-
- tdb = tdb_open("run-25-hashoverload.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- /* Fill a group. */
- for (j = 0; j < (1 << TDB_HASH_GROUP_BITS); j++) {
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- }
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Now store one last value: should form chain. */
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Check we can find them all. */
- for (j = 0; j < (1 << TDB_HASH_GROUP_BITS) + 1; j++) {
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == sizeof(j));
- ok1(d.dptr != NULL);
- ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
- free(d.dptr);
- }
-
- /* Now add a *lot* more. */
- for (j = (1 << TDB_HASH_GROUP_BITS) + 1;
- j < (16 << TDB_HASH_GROUP_BITS);
- j++) {
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == sizeof(j));
- ok1(d.dptr != NULL);
- ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
- free(d.dptr);
- }
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Traverse through them. */
- ok1(tdb_traverse(tdb, trav, NULL) == j);
-
- /* Empty the first chain-worth. */
- for (j = 0; j < (1 << TDB_HASH_GROUP_BITS); j++)
- ok1(tdb_delete(tdb, key) == 0);
-
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- for (j = (1 << TDB_HASH_GROUP_BITS);
- j < (16 << TDB_HASH_GROUP_BITS);
- j++) {
- ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
- ok1(d.dsize == sizeof(j));
- ok1(d.dptr != NULL);
- ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
- free(d.dptr);
- }
-
- /* Traverse through them. */
- ok1(tdb_traverse(tdb, trav, NULL)
- == (15 << TDB_HASH_GROUP_BITS));
-
- /* Re-add */
- for (j = 0; j < (1 << TDB_HASH_GROUP_BITS); j++) {
- ok1(tdb_store(tdb, key, dbuf, TDB_INSERT) == 0);
- }
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- /* Now try deleting as we go. */
- ok1(tdb_traverse(tdb, trav, trav)
- == (16 << TDB_HASH_GROUP_BITS));
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb_traverse(tdb, trav, NULL) == 0);
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-30-exhaust-before-expand.c b/lib/tdb2/test/run-30-exhaust-before-expand.c
deleted file mode 100644
index 13bb9461d4..0000000000
--- a/lib/tdb2/test/run-30-exhaust-before-expand.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-static bool empty_freetable(struct tdb_context *tdb)
-{
- struct tdb_freetable ftab;
- unsigned int i;
-
- /* Now, free table should be completely exhausted in zone 0 */
- if (tdb_read_convert(tdb, tdb->ftable_off, &ftab, sizeof(ftab)) != 0)
- abort();
-
- for (i = 0; i < sizeof(ftab.buckets)/sizeof(ftab.buckets[0]); i++) {
- if (ftab.buckets[i])
- return false;
- }
- return true;
-}
-
-
-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 };
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 9 + 1);
-
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- TDB_DATA k;
- uint64_t size;
- bool was_empty = false;
-
- k.dptr = (void *)&j;
- k.dsize = sizeof(j);
-
- tdb = tdb_open("run-30-exhaust-before-expand.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- ok1(empty_freetable(tdb));
- /* Need some hash lock for expand. */
- ok1(tdb_lock_hashes(tdb, 0, 1, F_WRLCK, TDB_LOCK_WAIT) == 0);
- /* Create some free space. */
- ok1(tdb_expand(tdb, 1) == 0);
- ok1(tdb_unlock_hashes(tdb, 0, 1, F_WRLCK) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(!empty_freetable(tdb));
-
- size = tdb->file->map_size;
- /* Insert minimal-length records until we expand. */
- for (j = 0; tdb->file->map_size == size; j++) {
- was_empty = empty_freetable(tdb);
- if (tdb_store(tdb, k, k, TDB_INSERT) != 0)
- err(1, "Failed to store record %i", j);
- }
-
- /* Would have been empty before expansion, but no longer. */
- ok1(was_empty);
- ok1(!empty_freetable(tdb));
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-50-multiple-freelists.c b/lib/tdb2/test/run-50-multiple-freelists.c
deleted file mode 100644
index b102876c8d..0000000000
--- a/lib/tdb2/test/run-50-multiple-freelists.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-#include "layout.h"
-
-int main(int argc, char *argv[])
-{
- tdb_off_t off;
- struct tdb_context *tdb;
- struct tdb_layout *layout;
- TDB_DATA key, data;
- union tdb_attribute seed;
-
- /* This seed value previously tickled a layout.c bug. */
- seed.base.attr = TDB_ATTRIBUTE_SEED;
- seed.seed.seed = 0xb1142bc054d035b4ULL;
- seed.base.next = &tap_log_attr;
-
- plan_tests(11);
- key = tdb_mkdata("Hello", 5);
- data = tdb_mkdata("world", 5);
-
- /* Create a TDB with three free tables. */
- layout = new_tdb_layout();
- tdb_layout_add_freetable(layout);
- tdb_layout_add_freetable(layout);
- tdb_layout_add_freetable(layout);
- tdb_layout_add_free(layout, 80, 0);
- /* Used record prevent coalescing. */
- tdb_layout_add_used(layout, key, data, 6);
- tdb_layout_add_free(layout, 160, 1);
- key.dsize--;
- tdb_layout_add_used(layout, key, data, 7);
- tdb_layout_add_free(layout, 320, 2);
- key.dsize--;
- tdb_layout_add_used(layout, key, data, 8);
- tdb_layout_add_free(layout, 40, 0);
- tdb = tdb_layout_get(layout, free, &seed);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
-
- off = get_free(tdb, 0, 80 - sizeof(struct tdb_used_record), 0,
- TDB_USED_MAGIC, 0);
- ok1(off == layout->elem[3].base.off);
- ok1(tdb->ftable_off == layout->elem[0].base.off);
-
- off = get_free(tdb, 0, 160 - sizeof(struct tdb_used_record), 0,
- TDB_USED_MAGIC, 0);
- ok1(off == layout->elem[5].base.off);
- ok1(tdb->ftable_off == layout->elem[1].base.off);
-
- off = get_free(tdb, 0, 320 - sizeof(struct tdb_used_record), 0,
- TDB_USED_MAGIC, 0);
- ok1(off == layout->elem[7].base.off);
- ok1(tdb->ftable_off == layout->elem[2].base.off);
-
- off = get_free(tdb, 0, 40 - sizeof(struct tdb_used_record), 0,
- TDB_USED_MAGIC, 0);
- ok1(off == layout->elem[9].base.off);
- ok1(tdb->ftable_off == layout->elem[0].base.off);
-
- /* Now we fail. */
- off = get_free(tdb, 0, 0, 1, TDB_USED_MAGIC, 0);
- ok1(off == 0);
-
- tdb_close(tdb);
- tdb_layout_free(layout);
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-64-bit-tdb.c b/lib/tdb2/test/run-64-bit-tdb.c
deleted file mode 100644
index ef6e243a05..0000000000
--- a/lib/tdb2/test/run-64-bit-tdb.c
+++ /dev/null
@@ -1,72 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- if (sizeof(off_t) <= 4) {
- plan_tests(1);
- pass("No 64 bit off_t");
- return exit_status();
- }
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- off_t old_size;
- TDB_DATA k, d;
- struct hash_info h;
- struct tdb_used_record rec;
- tdb_off_t off;
-
- tdb = tdb_open("run-64-bit-tdb.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- old_size = tdb->file->map_size;
-
- /* This makes a sparse file */
- ok1(ftruncate(tdb->file->fd, 0xFFFFFFF0) == 0);
- ok1(add_free_record(tdb, old_size, 0xFFFFFFF0 - old_size,
- TDB_LOCK_WAIT, false) == TDB_SUCCESS);
-
- /* Now add a little record past the 4G barrier. */
- ok1(tdb_expand_file(tdb, 100) == TDB_SUCCESS);
- ok1(add_free_record(tdb, 0xFFFFFFF0, 100, TDB_LOCK_WAIT, false)
- == TDB_SUCCESS);
-
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
-
- /* Test allocation path. */
- k = tdb_mkdata("key", 4);
- d = tdb_mkdata("data", 5);
- ok1(tdb_store(tdb, k, d, TDB_INSERT) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
-
- /* Make sure it put it at end as we expected. */
- off = find_and_lock(tdb, k, F_RDLCK, &h, &rec, NULL);
- ok1(off >= 0xFFFFFFF0);
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
-
- ok1(tdb_fetch(tdb, k, &d) == 0);
- ok1(d.dsize == 5);
- ok1(strcmp((char *)d.dptr, "data") == 0);
- free(d.dptr);
-
- ok1(tdb_delete(tdb, k) == 0);
- ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
-
- tdb_close(tdb);
- }
-
- /* We might get messages about mmap failing, so don't test
- * tap_log_messages */
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-90-get-set-attributes.c b/lib/tdb2/test/run-90-get-set-attributes.c
deleted file mode 100644
index edf0735013..0000000000
--- a/lib/tdb2/test/run-90-get-set-attributes.c
+++ /dev/null
@@ -1,159 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-static int mylock(int fd, int rw, off_t off, off_t len, bool waitflag,
- void *unused)
-{
- return 0;
-}
-
-static int myunlock(int fd, int rw, off_t off, off_t len, void *unused)
-{
- return 0;
-}
-
-static uint64_t hash_fn(const void *key, size_t len, uint64_t seed,
- void *priv)
-{
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
- union tdb_attribute seed_attr;
- union tdb_attribute hash_attr;
- union tdb_attribute lock_attr;
-
- seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
- seed_attr.base.next = &hash_attr;
- seed_attr.seed.seed = 100;
-
- hash_attr.base.attr = TDB_ATTRIBUTE_HASH;
- hash_attr.base.next = &lock_attr;
- hash_attr.hash.fn = hash_fn;
- hash_attr.hash.data = &hash_attr;
-
- lock_attr.base.attr = TDB_ATTRIBUTE_FLOCK;
- lock_attr.base.next = &tap_log_attr;
- lock_attr.flock.lock = mylock;
- lock_attr.flock.unlock = myunlock;
- lock_attr.flock.data = &lock_attr;
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 50);
-
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- union tdb_attribute attr;
-
- /* First open with no attributes. */
- tdb = tdb_open("run-90-get-set-attributes.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, NULL);
- ok1(tdb);
-
- /* Get log on no attributes will fail */
- attr.base.attr = TDB_ATTRIBUTE_LOG;
- ok1(tdb_get_attribute(tdb, &attr) == TDB_ERR_NOEXIST);
- /* These always work. */
- attr.base.attr = TDB_ATTRIBUTE_HASH;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_HASH);
- ok1(attr.hash.fn == tdb_jenkins_hash);
- attr.base.attr = TDB_ATTRIBUTE_FLOCK;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_FLOCK);
- ok1(attr.flock.lock == tdb_fcntl_lock);
- ok1(attr.flock.unlock == tdb_fcntl_unlock);
- attr.base.attr = TDB_ATTRIBUTE_SEED;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_SEED);
- /* This is possible, just astronomically unlikely. */
- ok1(attr.seed.seed != 0);
-
- /* Unset attributes. */
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_LOG);
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
-
- /* Set them. */
- ok1(tdb_set_attribute(tdb, &tap_log_attr) == 0);
- ok1(tdb_set_attribute(tdb, &lock_attr) == 0);
- /* These should fail. */
- ok1(tdb_set_attribute(tdb, &seed_attr) == TDB_ERR_EINVAL);
- ok1(tap_log_messages == 1);
- ok1(tdb_set_attribute(tdb, &hash_attr) == TDB_ERR_EINVAL);
- ok1(tap_log_messages == 2);
- tap_log_messages = 0;
-
- /* Getting them should work as expected. */
- attr.base.attr = TDB_ATTRIBUTE_LOG;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_LOG);
- ok1(attr.log.fn == tap_log_attr.log.fn);
- ok1(attr.log.data == tap_log_attr.log.data);
-
- attr.base.attr = TDB_ATTRIBUTE_FLOCK;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_FLOCK);
- ok1(attr.flock.lock == mylock);
- ok1(attr.flock.unlock == myunlock);
- ok1(attr.flock.data == &lock_attr);
-
- /* Unset them again. */
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
- ok1(tap_log_messages == 0);
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_LOG);
- ok1(tap_log_messages == 0);
-
- tdb_close(tdb);
- ok1(tap_log_messages == 0);
-
- /* Now open with all attributes. */
- tdb = tdb_open("run-90-get-set-attributes.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600,
- &seed_attr);
-
- ok1(tdb);
-
- /* Get will succeed */
- attr.base.attr = TDB_ATTRIBUTE_LOG;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_LOG);
- ok1(attr.log.fn == tap_log_attr.log.fn);
- ok1(attr.log.data == tap_log_attr.log.data);
-
- attr.base.attr = TDB_ATTRIBUTE_HASH;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_HASH);
- ok1(attr.hash.fn == hash_fn);
- ok1(attr.hash.data == &hash_attr);
-
- attr.base.attr = TDB_ATTRIBUTE_FLOCK;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_FLOCK);
- ok1(attr.flock.lock == mylock);
- ok1(attr.flock.unlock == myunlock);
- ok1(attr.flock.data == &lock_attr);
-
- attr.base.attr = TDB_ATTRIBUTE_SEED;
- ok1(tdb_get_attribute(tdb, &attr) == 0);
- ok1(attr.base.attr == TDB_ATTRIBUTE_SEED);
- ok1(attr.seed.seed == seed_attr.seed.seed);
-
- /* Unset attributes. */
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_HASH);
- ok1(tap_log_messages == 1);
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_SEED);
- ok1(tap_log_messages == 2);
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
- tdb_unset_attribute(tdb, TDB_ATTRIBUTE_LOG);
- ok1(tap_log_messages == 2);
- tap_log_messages = 0;
-
- tdb_close(tdb);
-
- }
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-expand-in-transaction.c b/lib/tdb2/test/run-expand-in-transaction.c
deleted file mode 100644
index 6b22d2ef46..0000000000
--- a/lib/tdb2/test/run-expand-in-transaction.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
- TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
- struct tdb_data key = tdb_mkdata("key", 3);
- struct tdb_data data = tdb_mkdata("data", 4);
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
-
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- size_t size;
- tdb = tdb_open("run-expand-in-transaction.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
- ok1(tdb);
- if (!tdb)
- continue;
-
- size = tdb->file->map_size;
- ok1(tdb_transaction_start(tdb) == 0);
- ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
- ok1(tdb->file->map_size > size);
- ok1(tdb_transaction_commit(tdb) == 0);
- ok1(tdb->file->map_size > size);
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- tdb_close(tdb);
- }
-
- ok1(tap_log_messages == 0);
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-seed.c b/lib/tdb2/test/run-seed.c
deleted file mode 100644
index 9c90833001..0000000000
--- a/lib/tdb2/test/run-seed.c
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-#include "logging.h"
-
-static int log_count = 0;
-
-/* Normally we get a log when setting random seed. */
-static void my_log_fn(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
- const char *message, void *priv)
-{
- log_count++;
-}
-
-static union tdb_attribute log_attr = {
- .log = { .base = { .attr = TDB_ATTRIBUTE_LOG },
- .fn = my_log_fn }
-};
-
-int main(int argc, char *argv[])
-{
- unsigned int i;
- struct tdb_context *tdb;
- union tdb_attribute attr;
- int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
- TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
- TDB_NOMMAP|TDB_CONVERT };
-
- attr.seed.base.attr = TDB_ATTRIBUTE_SEED;
- attr.seed.base.next = &log_attr;
- attr.seed.seed = 42;
-
- plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 4 * 3);
- for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- struct tdb_header hdr;
- int fd;
- tdb = tdb_open("run-seed.tdb", flags[i],
- O_RDWR|O_CREAT|O_TRUNC, 0600, &attr);
- ok1(tdb);
- if (!tdb)
- continue;
- ok1(tdb_check(tdb, NULL, NULL) == 0);
- ok1(tdb->hash_seed == 42);
- ok1(log_count == 0);
- tdb_close(tdb);
-
- if (flags[i] & TDB_INTERNAL)
- continue;
-
- fd = open("run-seed.tdb", O_RDONLY);
- ok1(fd >= 0);
- ok1(read(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
- if (flags[i] & TDB_CONVERT)
- ok1(bswap_64(hdr.hash_seed) == 42);
- else
- ok1(hdr.hash_seed == 42);
- close(fd);
- }
- return exit_status();
-}
diff --git a/lib/tdb2/test/run-tdb_errorstr.c b/lib/tdb2/test/run-tdb_errorstr.c
deleted file mode 100644
index 7a2da251aa..0000000000
--- a/lib/tdb2/test/run-tdb_errorstr.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include "tdb2-source.h"
-#include "tap-interface.h"
-
-int main(int argc, char *argv[])
-{
- enum TDB_ERROR e;
- plan_tests(TDB_ERR_RDONLY*-1 + 2);
-
- for (e = TDB_SUCCESS; e >= TDB_ERR_RDONLY; e--) {
- switch (e) {
- case TDB_SUCCESS:
- ok1(!strcmp(tdb_errorstr(e),
- "Success"));
- break;
- case TDB_ERR_IO:
- ok1(!strcmp(tdb_errorstr(e),
- "IO Error"));
- break;
- case TDB_ERR_LOCK:
- ok1(!strcmp(tdb_errorstr(e),
- "Locking error"));
- break;
- case TDB_ERR_OOM:
- ok1(!strcmp(tdb_errorstr(e),
- "Out of memory"));
- break;
- case TDB_ERR_EXISTS:
- ok1(!strcmp(tdb_errorstr(e),
- "Record exists"));
- break;
- case TDB_ERR_EINVAL:
- ok1(!strcmp(tdb_errorstr(e),
- "Invalid parameter"));
- break;
- case TDB_ERR_NOEXIST:
- ok1(!strcmp(tdb_errorstr(e),
- "Record does not exist"));
- break;
- case TDB_ERR_RDONLY:
- ok1(!strcmp(tdb_errorstr(e),
- "write not permitted"));
- break;
- case TDB_ERR_CORRUPT:
- ok1(!strcmp(tdb_errorstr(e),
- "Corrupt database"));
- break;
- }
- }
- ok1(!strcmp(tdb_errorstr(e), "Invalid error code"));
-
- return exit_status();
-}
diff --git a/lib/tdb2/tools/Makefile b/lib/tdb2/tools/Makefile
deleted file mode 100644
index 11188c3baf..0000000000
--- a/lib/tdb2/tools/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-OBJS:=../../tdb2.o ../../hash.o ../../tally.o
-CFLAGS:=-I../../.. -I.. -Wall -g -O3 #-g -pg
-LDFLAGS:=-L../../..
-
-default: tdb2torture tdb2tool tdb2dump tdb2restore mktdb2 speed growtdb-bench
-
-tdb2dump: tdb2dump.c $(OBJS)
-tdb2restore: tdb2restore.c $(OBJS)
-tdb2torture: tdb2torture.c $(OBJS)
-tdb2tool: tdb2tool.c $(OBJS)
-mktdb2: mktdb2.c $(OBJS)
-speed: speed.c $(OBJS)
-growtdb-bench: growtdb-bench.c $(OBJS)
-
-clean:
- rm -f tdb2torture tdb2dump tdb2restore tdb2tool mktdb2 speed growtdb-bench
diff --git a/lib/tdb2/tools/growtdb-bench.c b/lib/tdb2/tools/growtdb-bench.c
deleted file mode 100644
index 476e8be5da..0000000000
--- a/lib/tdb2/tools/growtdb-bench.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "tdb2.h"
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <ccan/err/err.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-static void logfn(struct tdb_context *tdb,
- enum tdb_log_level level,
- enum TDB_ERROR ecode,
- const char *message,
- void *data)
-{
- fprintf(stderr, "tdb:%s:%s:%s\n",
- tdb_name(tdb), tdb_errorstr(ecode), message);
-}
-
-int main(int argc, char *argv[])
-{
- unsigned int i, j, users, groups;
- TDB_DATA idxkey, idxdata;
- TDB_DATA k, d, gk;
- char cmd[100];
- struct tdb_context *tdb;
- enum TDB_ERROR ecode;
- union tdb_attribute log;
-
- if (argc != 3) {
- printf("Usage: growtdb-bench <users> <groups>\n");
- exit(1);
- }
- users = atoi(argv[1]);
- groups = atoi(argv[2]);
-
- sprintf(cmd, "cat /proc/%i/statm", getpid());
-
- log.base.attr = TDB_ATTRIBUTE_LOG;
- log.base.next = NULL;
- log.log.fn = logfn;
-
- tdb = tdb_open("/tmp/growtdb.tdb", TDB_DEFAULT,
- O_RDWR|O_CREAT|O_TRUNC, 0600, &log);
-
- idxkey.dptr = (unsigned char *)"User index";
- idxkey.dsize = strlen("User index");
- idxdata.dsize = 51;
- idxdata.dptr = calloc(idxdata.dsize, 1);
-
- /* Create users. */
- k.dsize = 48;
- k.dptr = calloc(k.dsize, 1);
- d.dsize = 64;
- d.dptr = calloc(d.dsize, 1);
-
- tdb_transaction_start(tdb);
- for (i = 0; i < users; i++) {
- memcpy(k.dptr, &i, sizeof(i));
- ecode = tdb_store(tdb, k, d, TDB_INSERT);
- if (ecode != TDB_SUCCESS)
- errx(1, "tdb insert failed: %s", tdb_errorstr(ecode));
-
- /* This simulates a growing index record. */
- ecode = tdb_append(tdb, idxkey, idxdata);
- if (ecode != TDB_SUCCESS)
- errx(1, "tdb append failed: %s", tdb_errorstr(ecode));
- }
- if ((ecode = tdb_transaction_commit(tdb)) != 0)
- errx(1, "tdb commit1 failed: %s", tdb_errorstr(ecode));
-
- if ((ecode = tdb_check(tdb, NULL, NULL)) != 0)
- errx(1, "tdb_check failed after initial insert!");
-
- system(cmd);
-
- /* Now put them all in groups: add 32 bytes to each record for
- * a group. */
- gk.dsize = 48;
- gk.dptr = calloc(k.dsize, 1);
- gk.dptr[gk.dsize-1] = 1;
-
- d.dsize = 32;
- for (i = 0; i < groups; i++) {
- tdb_transaction_start(tdb);
- /* Create the "group". */
- memcpy(gk.dptr, &i, sizeof(i));
- ecode = tdb_store(tdb, gk, d, TDB_INSERT);
- if (ecode != TDB_SUCCESS)
- errx(1, "tdb insert failed: %s", tdb_errorstr(ecode));
-
- /* Now populate it. */
- for (j = 0; j < users; j++) {
- /* Append to the user. */
- memcpy(k.dptr, &j, sizeof(j));
- if ((ecode = tdb_append(tdb, k, d)) != 0)
- errx(1, "tdb append failed: %s",
- tdb_errorstr(ecode));
-
- /* Append to the group. */
- if ((ecode = tdb_append(tdb, gk, d)) != 0)
- errx(1, "tdb append failed: %s",
- tdb_errorstr(ecode));
- }
- if ((ecode = tdb_transaction_commit(tdb)) != 0)
- errx(1, "tdb commit2 failed: %s", tdb_errorstr(ecode));
- if ((ecode = tdb_check(tdb, NULL, NULL)) != 0)
- errx(1, "tdb_check failed after iteration %i!", i);
- system(cmd);
- }
-
- return 0;
-}
diff --git a/lib/tdb2/transaction.c b/lib/tdb2/transaction.c
deleted file mode 100644
index 2b714714dc..0000000000
--- a/lib/tdb2/transaction.c
+++ /dev/null
@@ -1,1322 +0,0 @@
- /*
- Unix SMB/CIFS implementation.
-
- trivial database library
-
- Copyright (C) Andrew Tridgell 2005
- 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 "private.h"
-#define SAFE_FREE(x) do { if ((x) != NULL) {free((void *)x); (x)=NULL;} } while(0)
-
-/*
- 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 canceled
- 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 canceled
-
- - 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.
-*/
-
-/*
- hold the context of any current transaction
-*/
-struct tdb_transaction {
- /* 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;
- size_t num_blocks;
- size_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 */
- unsigned 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;
-};
-
-/* This doesn't really need to be pagesize, but we use it for similar reasons. */
-#define PAGESIZE 65536
-
-/*
- 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 enum TDB_ERROR transaction_read(struct tdb_context *tdb, tdb_off_t off,
- void *buf, tdb_len_t len)
-{
- size_t blk;
- enum TDB_ERROR ecode;
-
- /* break it down into block sized ops */
- while (len + (off % PAGESIZE) > PAGESIZE) {
- tdb_len_t len2 = PAGESIZE - (off % PAGESIZE);
- ecode = transaction_read(tdb, off, buf, len2);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- len -= len2;
- off += len2;
- buf = (void *)(len2 + (char *)buf);
- }
-
- if (len == 0) {
- return TDB_SUCCESS;
- }
-
- blk = off / PAGESIZE;
-
- /* 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 */
- ecode = tdb->transaction->io_methods->tread(tdb, off, buf, len);
- if (ecode != TDB_SUCCESS) {
- 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) {
- ecode = TDB_ERR_IO;
- goto fail;
- }
- }
-
- /* now copy it out of this block */
- memcpy(buf, tdb->transaction->blocks[blk] + (off % PAGESIZE), len);
- return TDB_SUCCESS;
-
-fail:
- tdb->transaction->transaction_error = 1;
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "transaction_read: failed at off=%zu len=%zu",
- (size_t)off, (size_t)len);
-}
-
-
-/*
- write while in a transaction
-*/
-static enum TDB_ERROR transaction_write(struct tdb_context *tdb, tdb_off_t off,
- const void *buf, tdb_len_t len)
-{
- size_t blk;
- enum TDB_ERROR ecode;
-
- /* Only a commit is allowed on a prepared transaction */
- if (tdb->transaction->prepared) {
- ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_ERROR,
- "transaction_write: transaction already"
- " prepared, write not allowed");
- goto fail;
- }
-
- /* break it up into block sized chunks */
- while (len + (off % PAGESIZE) > PAGESIZE) {
- tdb_len_t len2 = PAGESIZE - (off % PAGESIZE);
- ecode = transaction_write(tdb, off, buf, len2);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- len -= len2;
- off += len2;
- if (buf != NULL) {
- buf = (const void *)(len2 + (const char *)buf);
- }
- }
-
- if (len == 0) {
- return TDB_SUCCESS;
- }
-
- blk = off / PAGESIZE;
- off = off % PAGESIZE;
-
- 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) {
- ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "transaction_write:"
- " failed to allocate");
- 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(PAGESIZE, 1);
- if (tdb->transaction->blocks[blk] == NULL) {
- ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "transaction_write:"
- " failed to allocate");
- goto fail;
- }
- if (tdb->transaction->old_map_size > blk * PAGESIZE) {
- tdb_len_t len2 = PAGESIZE;
- if (len2 + (blk * PAGESIZE) > tdb->transaction->old_map_size) {
- len2 = tdb->transaction->old_map_size - (blk * PAGESIZE);
- }
- ecode = tdb->transaction->io_methods->tread(tdb,
- blk * PAGESIZE,
- tdb->transaction->blocks[blk],
- len2);
- if (ecode != TDB_SUCCESS) {
- ecode = tdb_logerr(tdb, ecode,
- TDB_LOG_ERROR,
- "transaction_write:"
- " failed to"
- " read old block: %s",
- strerror(errno));
- SAFE_FREE(tdb->transaction->blocks[blk]);
- 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 TDB_SUCCESS;
-
-fail:
- tdb->transaction->transaction_error = 1;
- return ecode;
-}
-
-
-/*
- write while in a transaction - this variant never expands the transaction blocks, it only
- updates existing blocks. This means it cannot change the recovery size
-*/
-static void transaction_write_existing(struct tdb_context *tdb, tdb_off_t off,
- const void *buf, tdb_len_t len)
-{
- size_t blk;
-
- /* break it up into block sized chunks */
- while (len + (off % PAGESIZE) > PAGESIZE) {
- tdb_len_t len2 = PAGESIZE - (off % PAGESIZE);
- transaction_write_existing(tdb, off, buf, len2);
- len -= len2;
- off += len2;
- if (buf != NULL) {
- buf = (const void *)(len2 + (const char *)buf);
- }
- }
-
- if (len == 0) {
- return;
- }
-
- blk = off / PAGESIZE;
- off = off % PAGESIZE;
-
- if (tdb->transaction->num_blocks <= blk ||
- tdb->transaction->blocks[blk] == NULL) {
- return;
- }
-
- if (blk == tdb->transaction->num_blocks-1 &&
- off + len > tdb->transaction->last_block_size) {
- if (off >= tdb->transaction->last_block_size) {
- return;
- }
- len = tdb->transaction->last_block_size - off;
- }
-
- /* overwrite part of an existing block */
- memcpy(tdb->transaction->blocks[blk] + off, buf, len);
-}
-
-
-/*
- out of bounds check during a transaction
-*/
-static enum TDB_ERROR transaction_oob(struct tdb_context *tdb,
- tdb_off_t off, tdb_len_t len, bool probe)
-{
- if ((off + len >= off && off + len <= tdb->file->map_size) || probe) {
- return TDB_SUCCESS;
- }
-
- tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_oob len %lld beyond transaction size %lld",
- (long long)(off + len),
- (long long)tdb->file->map_size);
- return TDB_ERR_IO;
-}
-
-/*
- transaction version of tdb_expand().
-*/
-static enum TDB_ERROR transaction_expand_file(struct tdb_context *tdb,
- tdb_off_t addition)
-{
- enum TDB_ERROR ecode;
-
- /* add a write to the transaction elements, so subsequent
- reads see the zero data */
- ecode = transaction_write(tdb, tdb->file->map_size, NULL, addition);
- if (ecode == TDB_SUCCESS) {
- tdb->file->map_size += addition;
- }
- return ecode;
-}
-
-static void *transaction_direct(struct tdb_context *tdb, tdb_off_t off,
- size_t len, bool write_mode)
-{
- size_t blk = off / PAGESIZE, end_blk;
-
- /* This is wrong for zero-length blocks, but will fail gracefully */
- end_blk = (off + len - 1) / PAGESIZE;
-
- /* Can only do direct if in single block and we've already copied. */
- if (write_mode) {
- tdb->stats.transaction_write_direct++;
- if (blk != end_blk
- || blk >= tdb->transaction->num_blocks
- || tdb->transaction->blocks[blk] == NULL) {
- tdb->stats.transaction_write_direct_fail++;
- return NULL;
- }
- return tdb->transaction->blocks[blk] + off % PAGESIZE;
- }
-
- tdb->stats.transaction_read_direct++;
- /* Single which we have copied? */
- if (blk == end_blk
- && blk < tdb->transaction->num_blocks
- && tdb->transaction->blocks[blk])
- return tdb->transaction->blocks[blk] + off % PAGESIZE;
-
- /* Otherwise must be all not copied. */
- while (blk <= end_blk) {
- if (blk >= tdb->transaction->num_blocks)
- break;
- if (tdb->transaction->blocks[blk]) {
- tdb->stats.transaction_read_direct_fail++;
- return NULL;
- }
- blk++;
- }
- return tdb->transaction->io_methods->direct(tdb, off, len, false);
-}
-
-static const struct tdb_methods transaction_methods = {
- transaction_read,
- transaction_write,
- transaction_oob,
- transaction_expand_file,
- transaction_direct,
-};
-
-/*
- sync to disk
-*/
-static enum TDB_ERROR transaction_sync(struct tdb_context *tdb,
- tdb_off_t offset, tdb_len_t length)
-{
- if (tdb->flags & TDB_NOSYNC) {
- return TDB_SUCCESS;
- }
-
- if (fsync(tdb->file->fd) != 0) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_transaction: fsync failed: %s",
- strerror(errno));
- }
-#ifdef MS_SYNC
- if (tdb->file->map_ptr) {
- tdb_off_t moffset = offset & ~(getpagesize()-1);
- if (msync(moffset + (char *)tdb->file->map_ptr,
- length + (offset - moffset), MS_SYNC) != 0) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
- "tdb_transaction: msync failed: %s",
- strerror(errno));
- }
- }
-#endif
- return TDB_SUCCESS;
-}
-
-
-static void _tdb_transaction_cancel(struct tdb_context *tdb)
-{
- int i;
- enum TDB_ERROR ecode;
-
- if (tdb->transaction == NULL) {
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_transaction_cancel: no transaction");
- return;
- }
-
- if (tdb->transaction->nesting != 0) {
- tdb->transaction->transaction_error = 1;
- tdb->transaction->nesting--;
- return;
- }
-
- tdb->file->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;
- uint64_t invalid = TDB_RECOVERY_INVALID_MAGIC;
-
- /* remove the recovery marker */
- ecode = methods->twrite(tdb, tdb->transaction->magic_offset,
- &invalid, sizeof(invalid));
- if (ecode == TDB_SUCCESS)
- ecode = transaction_sync(tdb,
- tdb->transaction->magic_offset,
- sizeof(invalid));
- if (ecode != TDB_SUCCESS) {
- tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_cancel: failed to remove"
- " recovery magic");
- }
- }
-
- if (tdb->file->allrecord_lock.count)
- tdb_allrecord_unlock(tdb, tdb->file->allrecord_lock.ltype);
-
- /* restore the normal io methods */
- tdb->io = tdb->transaction->io_methods;
-
- tdb_transaction_unlock(tdb, F_WRLCK);
-
- if (tdb_has_open_lock(tdb))
- tdb_unlock_open(tdb, F_WRLCK);
-
- SAFE_FREE(tdb->transaction);
-}
-
-/*
- start a tdb transaction. No token is returned, as only a single
- transaction is allowed to be pending per tdb_context
-*/
-_PUBLIC_ enum TDB_ERROR tdb_transaction_start(struct tdb_context *tdb)
-{
- enum TDB_ERROR ecode;
-
- tdb->stats.transactions++;
- /* some sanity checks */
- if (tdb->flags & TDB_INTERNAL) {
- return tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_transaction_start:"
- " cannot start a"
- " transaction on an"
- " internal tdb");
- }
-
- if (tdb->flags & TDB_RDONLY) {
- return tdb->last_error = tdb_logerr(tdb, TDB_ERR_RDONLY,
- TDB_LOG_USE_ERROR,
- "tdb_transaction_start:"
- " cannot start a"
- " transaction on a "
- " read-only tdb");
- }
-
- /* cope with nested tdb_transaction_start() calls */
- if (tdb->transaction != NULL) {
- if (!(tdb->flags & TDB_ALLOW_NESTING)) {
- return tdb->last_error
- = tdb_logerr(tdb, TDB_ERR_IO,
- TDB_LOG_USE_ERROR,
- "tdb_transaction_start:"
- " already inside transaction");
- }
- tdb->transaction->nesting++;
- tdb->stats.transaction_nest++;
- return 0;
- }
-
- if (tdb_has_hash_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 */
- return tdb->last_error = tdb_logerr(tdb, TDB_ERR_LOCK,
- TDB_LOG_USE_ERROR,
- "tdb_transaction_start:"
- " cannot start a"
- " transaction with locks"
- " held");
- }
-
- tdb->transaction = (struct tdb_transaction *)
- calloc(sizeof(struct tdb_transaction), 1);
- if (tdb->transaction == NULL) {
- return tdb->last_error = tdb_logerr(tdb, TDB_ERR_OOM,
- TDB_LOG_ERROR,
- "tdb_transaction_start:"
- " cannot allocate");
- }
-
- /* 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 */
- ecode = tdb_transaction_lock(tdb, F_WRLCK);
- if (ecode != TDB_SUCCESS) {
- SAFE_FREE(tdb->transaction->blocks);
- SAFE_FREE(tdb->transaction);
- return tdb->last_error = ecode;
- }
-
- /* get a read lock over entire file. This is upgraded to a write
- lock during the commit */
- ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, true);
- if (ecode != TDB_SUCCESS) {
- goto fail_allrecord_lock;
- }
-
- /* make sure we know about any file expansions already done by
- anyone else */
- tdb->io->oob(tdb, tdb->file->map_size, 1, true);
- tdb->transaction->old_map_size = tdb->file->map_size;
-
- /* finally hook the io methods, replacing them with
- transaction specific methods */
- tdb->transaction->io_methods = tdb->io;
- tdb->io = &transaction_methods;
- return tdb->last_error = TDB_SUCCESS;
-
-fail_allrecord_lock:
- tdb_transaction_unlock(tdb, F_WRLCK);
- SAFE_FREE(tdb->transaction->blocks);
- SAFE_FREE(tdb->transaction);
- return tdb->last_error = ecode;
-}
-
-
-/*
- cancel the current transaction
-*/
-_PUBLIC_ void tdb_transaction_cancel(struct tdb_context *tdb)
-{
- tdb->stats.transaction_cancel++;
- _tdb_transaction_cancel(tdb);
-}
-
-/*
- work out how much space the linearised recovery data will consume (worst case)
-*/
-static tdb_len_t tdb_recovery_size(struct tdb_context *tdb)
-{
- tdb_len_t recovery_size = 0;
- int i;
-
- recovery_size = 0;
- for (i=0;i<tdb->transaction->num_blocks;i++) {
- if (i * PAGESIZE >= 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 += PAGESIZE;
- }
- }
-
- return recovery_size;
-}
-
-static enum TDB_ERROR tdb_recovery_area(struct tdb_context *tdb,
- const struct tdb_methods *methods,
- tdb_off_t *recovery_offset,
- struct tdb_recovery_record *rec)
-{
- enum TDB_ERROR ecode;
-
- *recovery_offset = tdb_read_off(tdb,
- offsetof(struct tdb_header, recovery));
- if (TDB_OFF_IS_ERR(*recovery_offset)) {
- return TDB_OFF_TO_ERR(*recovery_offset);
- }
-
- if (*recovery_offset == 0) {
- rec->max_len = 0;
- return TDB_SUCCESS;
- }
-
- ecode = methods->tread(tdb, *recovery_offset, rec, sizeof(*rec));
- if (ecode != TDB_SUCCESS)
- return ecode;
-
- tdb_convert(tdb, rec, sizeof(*rec));
- /* ignore invalid recovery regions: can happen in crash */
- if (rec->magic != TDB_RECOVERY_MAGIC &&
- rec->magic != TDB_RECOVERY_INVALID_MAGIC) {
- *recovery_offset = 0;
- rec->max_len = 0;
- }
- return TDB_SUCCESS;
-}
-
-static unsigned int same(const unsigned char *new,
- const unsigned char *old,
- unsigned int length)
-{
- unsigned int i;
-
- for (i = 0; i < length; i++) {
- if (new[i] != old[i])
- break;
- }
- return i;
-}
-
-static unsigned int different(const unsigned char *new,
- const unsigned char *old,
- unsigned int length,
- unsigned int min_same,
- unsigned int *samelen)
-{
- unsigned int i;
-
- *samelen = 0;
- for (i = 0; i < length; i++) {
- if (new[i] == old[i]) {
- (*samelen)++;
- } else {
- if (*samelen >= min_same) {
- return i - *samelen;
- }
- *samelen = 0;
- }
- }
-
- if (*samelen < min_same)
- *samelen = 0;
- return length - *samelen;
-}
-
-/* Allocates recovery blob, without tdb_recovery_record at head set up. */
-static struct tdb_recovery_record *alloc_recovery(struct tdb_context *tdb,
- tdb_len_t *len)
-{
- struct tdb_recovery_record *rec;
- size_t i;
- enum TDB_ERROR ecode;
- unsigned char *p;
- const struct tdb_methods *old_methods = tdb->io;
-
- rec = malloc(sizeof(*rec) + tdb_recovery_size(tdb));
- if (!rec) {
- tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "transaction_setup_recovery:"
- " cannot allocate");
- return TDB_ERR_PTR(TDB_ERR_OOM);
- }
-
- /* We temporarily revert to the old I/O methods, so we can use
- * tdb_access_read */
- tdb->io = tdb->transaction->io_methods;
-
- /* build the recovery data into a single blob to allow us to do a single
- large write, which should be more efficient */
- p = (unsigned char *)(rec + 1);
- for (i=0;i<tdb->transaction->num_blocks;i++) {
- tdb_off_t offset;
- tdb_len_t length;
- unsigned int off;
- const unsigned char *buffer;
-
- if (tdb->transaction->blocks[i] == NULL) {
- continue;
- }
-
- offset = i * PAGESIZE;
- length = PAGESIZE;
- if (i == tdb->transaction->num_blocks-1) {
- length = tdb->transaction->last_block_size;
- }
-
- if (offset >= tdb->transaction->old_map_size) {
- continue;
- }
-
- if (offset + length > tdb->file->map_size) {
- ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_transaction_setup_recovery:"
- " transaction data over new region"
- " boundary");
- goto fail;
- }
- if (offset + length > tdb->transaction->old_map_size) {
- /* Short read at EOF. */
- length = tdb->transaction->old_map_size - offset;
- }
- buffer = tdb_access_read(tdb, offset, length, false);
- if (TDB_PTR_IS_ERR(buffer)) {
- ecode = TDB_PTR_ERR(buffer);
- goto fail;
- }
-
- /* Skip over anything the same at the start. */
- off = same(tdb->transaction->blocks[i], buffer, length);
- offset += off;
-
- while (off < length) {
- tdb_len_t len1;
- unsigned int samelen;
-
- len1 = different(tdb->transaction->blocks[i] + off,
- buffer + off, length - off,
- sizeof(offset) + sizeof(len1) + 1,
- &samelen);
-
- memcpy(p, &offset, sizeof(offset));
- memcpy(p + sizeof(offset), &len1, sizeof(len1));
- tdb_convert(tdb, p, sizeof(offset) + sizeof(len1));
- p += sizeof(offset) + sizeof(len1);
- memcpy(p, buffer + off, len1);
- p += len1;
- off += len1 + samelen;
- offset += len1 + samelen;
- }
- tdb_access_release(tdb, buffer);
- }
-
- *len = p - (unsigned char *)(rec + 1);
- tdb->io = old_methods;
- return rec;
-
-fail:
- free(rec);
- tdb->io = old_methods;
- return TDB_ERR_PTR(ecode);
-}
-
-static tdb_off_t create_recovery_area(struct tdb_context *tdb,
- tdb_len_t rec_length,
- struct tdb_recovery_record *rec)
-{
- tdb_off_t off, recovery_off;
- tdb_len_t addition;
- enum TDB_ERROR ecode;
- const struct tdb_methods *methods = tdb->transaction->io_methods;
-
- /* round up to a multiple of page size. Overallocate, since each
- * such allocation forces us to expand the file. */
- rec->max_len = tdb_expand_adjust(tdb->file->map_size, rec_length);
-
- /* Round up to a page. */
- rec->max_len = ((sizeof(*rec) + rec->max_len + PAGESIZE-1)
- & ~(PAGESIZE-1))
- - sizeof(*rec);
-
- off = tdb->file->map_size;
-
- /* Restore ->map_size before calling underlying expand_file.
- Also so that we don't try to expand the file again in the
- transaction commit, which would destroy the recovery
- area */
- addition = (tdb->file->map_size - tdb->transaction->old_map_size) +
- sizeof(*rec) + rec->max_len;
- tdb->file->map_size = tdb->transaction->old_map_size;
- tdb->stats.transaction_expand_file++;
- ecode = methods->expand_file(tdb, addition);
- if (ecode != TDB_SUCCESS) {
- tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_recovery_allocate:"
- " failed to create recovery area");
- return TDB_ERR_TO_OFF(ecode);
- }
-
- /* 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->file->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 */
- recovery_off = off;
- tdb_convert(tdb, &recovery_off, sizeof(recovery_off));
- ecode = methods->twrite(tdb, offsetof(struct tdb_header, recovery),
- &recovery_off, sizeof(tdb_off_t));
- if (ecode != TDB_SUCCESS) {
- tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_recovery_allocate:"
- " failed to write recovery head");
- return TDB_ERR_TO_OFF(ecode);
- }
- transaction_write_existing(tdb, offsetof(struct tdb_header, recovery),
- &recovery_off,
- sizeof(tdb_off_t));
- return off;
-}
-
-/*
- setup the recovery data that will be used on a crash during commit
-*/
-static enum TDB_ERROR transaction_setup_recovery(struct tdb_context *tdb)
-{
- tdb_len_t recovery_size = 0;
- tdb_off_t recovery_off = 0;
- tdb_off_t old_map_size = tdb->transaction->old_map_size;
- struct tdb_recovery_record *recovery;
- const struct tdb_methods *methods = tdb->transaction->io_methods;
- uint64_t magic;
- enum TDB_ERROR ecode;
-
- recovery = alloc_recovery(tdb, &recovery_size);
- if (TDB_PTR_IS_ERR(recovery))
- return TDB_PTR_ERR(recovery);
-
- ecode = tdb_recovery_area(tdb, methods, &recovery_off, recovery);
- if (ecode) {
- free(recovery);
- return ecode;
- }
-
- if (recovery->max_len < recovery_size) {
- /* Not large enough. Free up old recovery area. */
- if (recovery_off) {
- tdb->stats.frees++;
- ecode = add_free_record(tdb, recovery_off,
- sizeof(*recovery)
- + recovery->max_len,
- TDB_LOCK_WAIT, true);
- free(recovery);
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_recovery_allocate:"
- " failed to free previous"
- " recovery area");
- }
-
- /* Refresh recovery after add_free_record above. */
- recovery = alloc_recovery(tdb, &recovery_size);
- if (TDB_PTR_IS_ERR(recovery))
- return TDB_PTR_ERR(recovery);
- }
-
- recovery_off = create_recovery_area(tdb, recovery_size,
- recovery);
- if (TDB_OFF_IS_ERR(recovery_off)) {
- free(recovery);
- return TDB_OFF_TO_ERR(recovery_off);
- }
- }
-
- /* Now we know size, convert rec header. */
- recovery->magic = TDB_RECOVERY_INVALID_MAGIC;
- recovery->len = recovery_size;
- recovery->eof = old_map_size;
- tdb_convert(tdb, recovery, sizeof(*recovery));
-
- /* write the recovery data to the recovery area */
- ecode = methods->twrite(tdb, recovery_off, recovery, recovery_size);
- if (ecode != TDB_SUCCESS) {
- free(recovery);
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_setup_recovery:"
- " failed to write recovery data");
- }
- transaction_write_existing(tdb, recovery_off, recovery, recovery_size);
-
- free(recovery);
-
- /* 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 */
- ecode = transaction_sync(tdb, recovery_off, recovery_size);
- if (ecode != TDB_SUCCESS)
- return ecode;
-
- magic = TDB_RECOVERY_MAGIC;
- tdb_convert(tdb, &magic, sizeof(magic));
-
- tdb->transaction->magic_offset
- = recovery_off + offsetof(struct tdb_recovery_record, magic);
-
- ecode = methods->twrite(tdb, tdb->transaction->magic_offset,
- &magic, sizeof(magic));
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_setup_recovery:"
- " failed to write recovery magic");
- }
- transaction_write_existing(tdb, tdb->transaction->magic_offset,
- &magic, sizeof(magic));
-
- /* ensure the recovery magic marker is on disk */
- return transaction_sync(tdb, tdb->transaction->magic_offset,
- sizeof(magic));
-}
-
-static enum TDB_ERROR _tdb_transaction_prepare_commit(struct tdb_context *tdb)
-{
- const struct tdb_methods *methods;
- enum TDB_ERROR ecode;
-
- if (tdb->transaction == NULL) {
- return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_transaction_prepare_commit:"
- " no transaction");
- }
-
- if (tdb->transaction->prepared) {
- _tdb_transaction_cancel(tdb);
- return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_transaction_prepare_commit:"
- " transaction already prepared");
- }
-
- if (tdb->transaction->transaction_error) {
- _tdb_transaction_cancel(tdb);
- return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_ERROR,
- "tdb_transaction_prepare_commit:"
- " transaction error pending");
- }
-
-
- if (tdb->transaction->nesting != 0) {
- return TDB_SUCCESS;
- }
-
- /* check for a null transaction */
- if (tdb->transaction->blocks == NULL) {
- return TDB_SUCCESS;
- }
-
- methods = tdb->transaction->io_methods;
-
- /* upgrade the main transaction lock region to a write lock */
- ecode = tdb_allrecord_upgrade(tdb, TDB_HASH_LOCK_START);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* get the open lock - this prevents new users attaching to the database
- during the commit */
- ecode = tdb_lock_open(tdb, F_WRLCK, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
-
- /* Since we have whole db locked, we don't need the expansion lock. */
- if (!(tdb->flags & TDB_NOSYNC)) {
- /* Sets up tdb->transaction->recovery and
- * tdb->transaction->magic_offset. */
- ecode = transaction_setup_recovery(tdb);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- }
-
- tdb->transaction->prepared = true;
-
- /* expand the file to the new size if needed */
- if (tdb->file->map_size != tdb->transaction->old_map_size) {
- tdb_len_t add;
-
- add = tdb->file->map_size - tdb->transaction->old_map_size;
- /* Restore original map size for tdb_expand_file */
- tdb->file->map_size = tdb->transaction->old_map_size;
- ecode = methods->expand_file(tdb, add);
- if (ecode != TDB_SUCCESS) {
- return ecode;
- }
- }
-
- /* Keep the open lock until the actual commit */
- return TDB_SUCCESS;
-}
-
-/*
- prepare to commit the current transaction
-*/
-_PUBLIC_ enum TDB_ERROR tdb_transaction_prepare_commit(struct tdb_context *tdb)
-{
- return tdb->last_error = _tdb_transaction_prepare_commit(tdb);
-}
-
-/*
- commit the current transaction
-*/
-_PUBLIC_ enum TDB_ERROR tdb_transaction_commit(struct tdb_context *tdb)
-{
- const struct tdb_methods *methods;
- int i;
- enum TDB_ERROR ecode;
-
- if (tdb->transaction == NULL) {
- return tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
- TDB_LOG_USE_ERROR,
- "tdb_transaction_commit:"
- " no transaction");
- }
-
- tdb_trace(tdb, "tdb_transaction_commit");
-
- if (tdb->transaction->nesting != 0) {
- tdb->transaction->nesting--;
- return tdb->last_error = TDB_SUCCESS;
- }
-
- /* check for a null transaction */
- if (tdb->transaction->blocks == NULL) {
- _tdb_transaction_cancel(tdb);
- return tdb->last_error = TDB_SUCCESS;
- }
-
- if (!tdb->transaction->prepared) {
- ecode = _tdb_transaction_prepare_commit(tdb);
- if (ecode != TDB_SUCCESS) {
- _tdb_transaction_cancel(tdb);
- return tdb->last_error = ecode;
- }
- }
-
- 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 * PAGESIZE;
- length = PAGESIZE;
- if (i == tdb->transaction->num_blocks-1) {
- length = tdb->transaction->last_block_size;
- }
-
- ecode = methods->twrite(tdb, offset,
- tdb->transaction->blocks[i], length);
- if (ecode != TDB_SUCCESS) {
- /* we've overwritten part of the data and
- possibly expanded the file, so we need to
- run the crash recovery code */
- tdb->io = methods;
- tdb_transaction_recover(tdb);
-
- _tdb_transaction_cancel(tdb);
-
- return tdb->last_error = ecode;
- }
- SAFE_FREE(tdb->transaction->blocks[i]);
- }
-
- SAFE_FREE(tdb->transaction->blocks);
- tdb->transaction->num_blocks = 0;
-
- /* ensure the new data is on disk */
- ecode = transaction_sync(tdb, 0, tdb->file->map_size);
- if (ecode != TDB_SUCCESS) {
- return tdb->last_error = ecode;
- }
-
- /*
- 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
-
- /* use a transaction cancel to free memory and remove the
- transaction locks: it "restores" map_size, too. */
- tdb->transaction->old_map_size = tdb->file->map_size;
- _tdb_transaction_cancel(tdb);
-
- return tdb->last_error = TDB_SUCCESS;
-}
-
-
-/*
- recover from an aborted transaction. Must be called with exclusive
- database write access already established (including the open
- lock to prevent new processes attaching)
-*/
-enum TDB_ERROR tdb_transaction_recover(struct tdb_context *tdb)
-{
- tdb_off_t recovery_head, recovery_eof;
- unsigned char *data, *p;
- struct tdb_recovery_record rec;
- enum TDB_ERROR ecode;
-
- /* find the recovery area */
- recovery_head = tdb_read_off(tdb, offsetof(struct tdb_header,recovery));
- if (TDB_OFF_IS_ERR(recovery_head)) {
- ecode = TDB_OFF_TO_ERR(recovery_head);
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to read recovery head");
- }
-
- if (recovery_head == 0) {
- /* we have never allocated a recovery record */
- return TDB_SUCCESS;
- }
-
- /* read the recovery record */
- ecode = tdb_read_convert(tdb, recovery_head, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to read recovery record");
- }
-
- if (rec.magic != TDB_RECOVERY_MAGIC) {
- /* there is no valid recovery data */
- return TDB_SUCCESS;
- }
-
- if (tdb->flags & TDB_RDONLY) {
- return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " attempt to recover read only database");
- }
-
- recovery_eof = rec.eof;
-
- data = (unsigned char *)malloc(rec.len);
- if (data == NULL) {
- return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to allocate recovery data");
- }
-
- /* read the full recovery data */
- ecode = tdb->io->tread(tdb, recovery_head + sizeof(rec), data,
- rec.len);
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to read recovery data");
- }
-
- /* recover the file data */
- p = data;
- while (p+sizeof(tdb_off_t)+sizeof(tdb_len_t) < data + rec.len) {
- tdb_off_t ofs;
- tdb_len_t len;
- tdb_convert(tdb, p, sizeof(ofs) + sizeof(len));
- memcpy(&ofs, p, sizeof(ofs));
- memcpy(&len, p + sizeof(ofs), sizeof(len));
- p += sizeof(ofs) + sizeof(len);
-
- ecode = tdb->io->twrite(tdb, ofs, p, len);
- if (ecode != TDB_SUCCESS) {
- free(data);
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to recover %zu bytes"
- " at offset %zu",
- (size_t)len, (size_t)ofs);
- }
- p += len;
- }
-
- free(data);
-
- ecode = transaction_sync(tdb, 0, tdb->file->map_size);
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to sync recovery");
- }
-
- /* if the recovery area is after the recovered eof then remove it */
- if (recovery_eof <= recovery_head) {
- ecode = tdb_write_off(tdb, offsetof(struct tdb_header,
- recovery),
- 0);
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to remove recovery head");
- }
- }
-
- /* remove the recovery magic */
- ecode = tdb_write_off(tdb,
- recovery_head
- + offsetof(struct tdb_recovery_record, magic),
- TDB_RECOVERY_INVALID_MAGIC);
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to remove recovery magic");
- }
-
- ecode = transaction_sync(tdb, 0, recovery_eof);
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_transaction_recover:"
- " failed to sync2 recovery");
- }
-
- tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
- "tdb_transaction_recover: recovered %zu byte database",
- (size_t)recovery_eof);
-
- /* all done */
- return TDB_SUCCESS;
-}
-
-tdb_bool_err tdb_needs_recovery(struct tdb_context *tdb)
-{
- tdb_off_t recovery_head;
- struct tdb_recovery_record rec;
- enum TDB_ERROR ecode;
-
- /* find the recovery area */
- recovery_head = tdb_read_off(tdb, offsetof(struct tdb_header,recovery));
- if (TDB_OFF_IS_ERR(recovery_head)) {
- return recovery_head;
- }
-
- if (recovery_head == 0) {
- /* we have never allocated a recovery record */
- return false;
- }
-
- /* read the recovery record */
- ecode = tdb_read_convert(tdb, recovery_head, &rec, sizeof(rec));
- if (ecode != TDB_SUCCESS) {
- return TDB_ERR_TO_OFF(ecode);
- }
-
- return (rec.magic == TDB_RECOVERY_MAGIC);
-}
diff --git a/lib/tdb2/traverse.c b/lib/tdb2/traverse.c
deleted file mode 100644
index ed51a9ee72..0000000000
--- a/lib/tdb2/traverse.c
+++ /dev/null
@@ -1,99 +0,0 @@
- /*
- Trivial Database 2: traverse function.
- 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 "private.h"
-#include <ccan/likely/likely.h>
-
-_PUBLIC_ int64_t tdb_traverse_(struct tdb_context *tdb,
- int (*fn)(struct tdb_context *,
- TDB_DATA, TDB_DATA, void *),
- void *p)
-{
- enum TDB_ERROR ecode;
- struct traverse_info tinfo;
- struct tdb_data k, d;
- int64_t count = 0;
-
- k.dptr = NULL;
- for (ecode = first_in_hash(tdb, &tinfo, &k, &d.dsize);
- ecode == TDB_SUCCESS;
- ecode = next_in_hash(tdb, &tinfo, &k, &d.dsize)) {
- d.dptr = k.dptr + k.dsize;
-
- count++;
- if (fn && fn(tdb, k, d, p)) {
- free(k.dptr);
- tdb->last_error = TDB_SUCCESS;
- return count;
- }
- free(k.dptr);
- }
-
- if (ecode != TDB_ERR_NOEXIST) {
- return TDB_ERR_TO_OFF(tdb->last_error = ecode);
- }
- tdb->last_error = TDB_SUCCESS;
- return count;
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_firstkey(struct tdb_context *tdb, struct tdb_data *key)
-{
- struct traverse_info tinfo;
-
- return tdb->last_error = first_in_hash(tdb, &tinfo, key, NULL);
-}
-
-/* We lock twice, not very efficient. We could keep last key & tinfo cached. */
-_PUBLIC_ enum TDB_ERROR tdb_nextkey(struct tdb_context *tdb, struct tdb_data *key)
-{
- struct traverse_info tinfo;
- struct hash_info h;
- struct tdb_used_record rec;
-
- tinfo.prev = find_and_lock(tdb, *key, F_RDLCK, &h, &rec, &tinfo);
- free(key->dptr);
- if (TDB_OFF_IS_ERR(tinfo.prev)) {
- return tdb->last_error = TDB_OFF_TO_ERR(tinfo.prev);
- }
- tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
-
- return tdb->last_error = next_in_hash(tdb, &tinfo, key, NULL);
-}
-
-static int wipe_one(struct tdb_context *tdb,
- TDB_DATA key, TDB_DATA data, enum TDB_ERROR *ecode)
-{
- *ecode = tdb_delete(tdb, key);
- return (*ecode != TDB_SUCCESS);
-}
-
-_PUBLIC_ enum TDB_ERROR tdb_wipe_all(struct tdb_context *tdb)
-{
- enum TDB_ERROR ecode;
- int64_t count;
-
- ecode = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
- if (ecode != TDB_SUCCESS)
- return tdb->last_error = ecode;
-
- /* FIXME: Be smarter. */
- count = tdb_traverse(tdb, wipe_one, &ecode);
- if (count < 0)
- ecode = TDB_OFF_TO_ERR(count);
- tdb_allrecord_unlock(tdb, F_WRLCK);
- return tdb->last_error = ecode;
-}
diff --git a/lib/tdb2/wscript b/lib/tdb2/wscript
deleted file mode 100644
index ef30f1b778..0000000000
--- a/lib/tdb2/wscript
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/usr/bin/env python
-
-APPNAME = 'tdb'
-VERSION = '2.0.1'
-
-blddir = 'bin'
-
-import sys, os
-
-# find the buildtools directory
-srcdir = '.'
-while not os.path.exists(srcdir+'/buildtools') and len(srcdir.split('/')) < 5:
- srcdir = '../' + srcdir
-sys.path.insert(0, srcdir + '/buildtools/wafsamba')
-
-import wafsamba, samba_dist, Options, Logs, glob
-
-samba_dist.DIST_DIRS('lib/tdb2:. lib/replace:lib/replace lib/ccan:lib/ccan buildtools:buildtools')
-
-def set_options(opt):
- opt.BUILTIN_DEFAULT('replace,ccan')
- opt.PRIVATE_EXTENSION_DEFAULT('tdb2', noextension='tdb2')
- opt.RECURSE('lib/replace')
- opt.add_option('--enable-developer',
- help=("Turn on developer warnings and debugging"),
- action="store_true", dest='developer', default=False)
- opt.add_option('--enable-tdb2',
- help=("Use tdb2 API instead of tdb1 [True]"),
- action="store_true", dest='BUILD_TDB2', default=True)
- opt.add_option('--disable-tdb2',
- help=("Use old tdb1 API instead of tdb2"),
- action="store_false", dest='BUILD_TDB2')
- opt.add_option('--valgrind',
- help=("use valgrind on tests programs"),
- action="store_true", dest='VALGRIND', default=False)
- opt.add_option('--valgrind-log',
- help=("where to put the valgrind log"),
- action="store", dest='VALGRINDLOG', default=None)
- if opt.IN_LAUNCH_DIR():
- opt.add_option('--disable-python',
- help=("disable the pytdb module"),
- action="store_true", dest='disable_python', default=False)
-
-def configure(conf):
- if Options.options.developer:
- conf.env.DEVELOPER_MODE = True
-
- conf.env.TEST_RUN_SRC=['test/run-001-encode.c',
- 'test/run-001-fls.c',
- 'test/run-01-new_database.c',
- 'test/run-02-expand.c',
- 'test/run-03-coalesce.c',
- 'test/run-04-basichash.c',
- 'test/run-05-readonly-open.c',
- 'test/run-10-simple-store.c',
- 'test/run-11-simple-fetch.c',
- 'test/run-12-check.c',
- 'test/run-15-append.c',
- 'test/run-20-growhash.c',
- 'test/run-25-hashoverload.c',
- 'test/run-30-exhaust-before-expand.c',
- 'test/run-35-convert.c',
- 'test/run-50-multiple-freelists.c',
- 'test/run-56-open-during-transaction.c',
- 'test/run-57-die-during-transaction.c',
- 'test/run-64-bit-tdb.c',
- 'test/run-90-get-set-attributes.c',
- 'test/run-capabilities.c',
- 'test/run-expand-in-transaction.c',
- 'test/run-features.c',
- 'test/run-lockall.c',
- 'test/run-remap-in-read_traverse.c',
- 'test/run-seed.c',
- 'test/run-tdb_errorstr.c',
- 'test/run-tdb_foreach.c',
- 'test/run-traverse.c']
- conf.env.TEST_API_SRC=['test/api-12-store.c',
- 'test/api-13-delete.c',
- 'test/api-14-exists.c',
- 'test/api-16-wipe_all.c',
- 'test/api-21-parse_record.c',
- 'test/api-55-transaction.c',
- 'test/api-80-tdb_fd.c',
- 'test/api-81-seqnum.c',
- 'test/api-82-lockattr.c',
- 'test/api-83-openhook.c',
- 'test/api-91-get-stats.c',
- 'test/api-92-get-set-readonly.c',
- 'test/api-93-repack.c',
- 'test/api-add-remove-flags.c',
- 'test/api-check-callback.c',
- 'test/api-firstkey-nextkey.c',
- 'test/api-fork-test.c',
- 'test/api-locktimeout.c',
- 'test/api-missing-entries.c',
- 'test/api-open-multiple-times.c',
- 'test/api-record-expand.c',
- 'test/api-simple-delete.c',
- 'test/api-summary.c']
- conf.env.TEST_API_HELPER_SRC=['test/helpapi-external-agent.c']
- conf.env.TEST_RUN_HELPER_SRC=['test/helprun-external-agent.c',
- 'test/helprun-layout.c']
- conf.env.TEST_HELPER_SRC=['test/external-agent.c',
- 'test/failtest_helper.c',
- 'test/lock-tracking.c',
- 'test/logging.c',
- 'test/tap-interface.c']
-
- if Options.options.BUILD_TDB2:
- conf.DEFINE('BUILD_TDB2', 1)
- conf.RECURSE('lib/replace')
- conf.RECURSE('lib/ccan')
-
- conf.env.standalone_tdb2 = conf.IN_LAUNCH_DIR()
- conf.env.disable_python = getattr(Options.options, 'disable_python', False)
-
- if not conf.env.standalone_tdb2:
- if conf.CHECK_BUNDLED_SYSTEM('tdb', minversion=VERSION,
- implied_deps='replace'):
- conf.define('USING_SYSTEM_TDB2', 1)
- if conf.CHECK_BUNDLED_SYSTEM_PYTHON('pytdb', 'tdb', minversion=VERSION):
- conf.define('USING_SYSTEM_PYTDB', 1)
-
- if not conf.env.disable_python:
- # also disable if we don't have the python libs installed
- conf.find_program('python', var='PYTHON')
- conf.check_tool('python')
- conf.check_python_version((2,4,2))
- conf.SAMBA_CHECK_PYTHON_HEADERS(mandatory=False)
- if not conf.env.HAVE_PYTHON_H:
- Logs.warn('Disabling pytdb as python devel libs not found')
- conf.env.disable_python = True
-
- # This make #include <ccan/...> work.
- conf.ADD_EXTRA_INCLUDES('''#lib''')
-
- conf.SAMBA_CONFIG_H()
-
-def build(bld):
- if bld.env.BUILD_TDB2:
- bld.RECURSE('lib/replace')
- bld.RECURSE('lib/ccan')
-
- if bld.env.standalone_tdb2:
- bld.env.PKGCONFIGDIR = '${LIBDIR}/pkgconfig'
- private_library = False
- else:
- private_library = True
-
- SRC = '''check.c free.c hash.c io.c lock.c open.c
- summary.c tdb.c transaction.c traverse.c'''
-
- if not bld.CONFIG_SET('USING_SYSTEM_TDB2'):
- bld.SAMBA_LIBRARY('tdb',
- SRC,
- deps='replace ccan',
- includes='.',
- abi_directory='ABI',
- abi_match='tdb_*',
- hide_symbols=True,
- vnum=VERSION,
- public_headers='tdb2.h',
- public_headers_install=not private_library,
- pc_files='tdb.pc',
- private_library=private_library)
-
- bld.SAMBA_BINARY('tdbtorture',
- 'tools/tdb2torture.c',
- deps='tdb',
- install=False)
-
- bld.SAMBA_BINARY('tdbtool',
- 'tools/tdb2tool.c',
- deps='tdb')
-
- bld.SAMBA_BINARY('tdbdump',
- 'tools/tdb2dump.c',
- deps='tdb')
-
- bld.SAMBA_BINARY('tdbrestore',
- 'tools/tdb2restore.c',
- deps='tdb')
-
- bld.SAMBA_BINARY('tdbbackup',
- 'tools/tdb2backup.c',
- deps='tdb')
-
- if not bld.CONFIG_SET('USING_SYSTEM_PYTDB'):
- bld.SAMBA_PYTHON('pytdb',
- source='pytdb.c',
- deps='tdb',
- enabled=not bld.env.disable_python,
- realname='tdb.so',
- cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION)
-
- if bld.env.DEVELOPER_MODE:
- # FIXME: We need CCAN for some API tests, but waf thinks it's
- # already available via tdb2. It is, but not publicly.
- # Workaround is to build a private, non-hiding version.
- bld.SAMBA_SUBSYSTEM('tdb2-testing',
- SRC,
- deps='replace ccan',
- includes='.')
-
- bld.SAMBA_SUBSYSTEM('tdb2-test-helpers', bld.env.TEST_HELPER_SRC,
- deps='replace')
- bld.SAMBA_SUBSYSTEM('tdb2-run-helpers', bld.env.TEST_RUN_HELPER_SRC,
- deps='replace')
- bld.SAMBA_SUBSYSTEM('tdb2-api-helpers', bld.env.TEST_API_HELPER_SRC,
- deps='replace tdb2-testing')
-
- for f in bld.env.TEST_RUN_SRC:
- base = os.path.splitext(os.path.basename(f))[0]
- bld.SAMBA_BINARY('tdb2-' + base, f,
- deps='ccan replace tdb2-test-helpers tdb2-run-helpers ccan-failtest',
- install=False)
-
- for f in bld.env.TEST_API_SRC:
- base = os.path.splitext(os.path.basename(f))[0]
- bld.SAMBA_BINARY('tdb2-' + base, f,
- deps='ccan replace tdb2-test-helpers tdb2-api-helpers',
- install=False)
-
-def testonly(ctx):
- '''run tdb2 testsuite'''
- import Utils, samba_utils, shutil
- ecode = 0;
-
- env = samba_utils.LOAD_ENVIRONMENT()
-
- if env.BUILD_TDB2 and env.standalone_tdb2 and env.DEVELOPER_MODE:
-
- # FIXME: This is horrible :(
- test_prefix = "%s/st" % (Utils.g_module.blddir)
- shutil.rmtree(test_prefix, ignore_errors=True)
- os.makedirs(test_prefix)
-
- # Create scratch directory for tests.
- testdir = os.path.join(test_prefix, 'tdb2-tests')
- samba_utils.mkdir_p(testdir)
- # Symlink back to source dir so it can find tests in test/
- link = os.path.join(testdir, 'test')
- if not os.path.exists(link):
- os.symlink(os.path.abspath(os.path.join(env.cwd, 'test')), link)
-
- if Options.options.VALGRIND:
- os.environ['VALGRIND'] = 'valgrind -q --num-callers=30'
- if Options.options.VALGRINDLOG is not None:
- os.environ['VALGRIND'] += ' --log-file=%s' % Options.options.VALGRINDLOG
-
- for f in env.TEST_RUN_SRC + env.TEST_API_SRC:
- name = "tdb2-" + os.path.splitext(os.path.basename(f))[0]
- cmd = "cd " + testdir + " && $VALGRIND " + os.path.abspath(os.path.join(Utils.g_module.blddir, name)) + " > test-output 2>&1"
- print("..." + f)
- ret = samba_utils.RUN_COMMAND(cmd)
- if ret != 0:
- print("%s (%s) failed:" % (name, f))
- samba_utils.RUN_COMMAND("cat " + os.path.join(testdir, 'test-output'))
- ecode = ret;
- break;
-
- sys.exit(ecode)
-
-# WAF doesn't build the unit tests for this, maybe because they don't link with tdb?
-# This forces it
-def test(ctx):
- import Scripting
- Scripting.commands.append('build')
- Scripting.commands.append('testonly')
-
-def dist():
- '''makes a tarball for distribution'''
- samba_dist.dist()
-
-def reconfigure(ctx):
- '''reconfigure if config scripts have changed'''
- import samba_utils
- samba_utils.reconfigure(ctx)