tdb: Update TDB library to 1.2.12 from upstream
authorAmitay Isaacs <amitay@gmail.com>
Thu, 13 Jun 2013 02:52:38 +0000 (12:52 +1000)
committerAmitay Isaacs <amitay@gmail.com>
Fri, 14 Jun 2013 06:37:48 +0000 (16:37 +1000)
This comprises of following patches from Samba upstream.

d1feccb35e987545f4ae8e3f4eb0b4fc741e7e7e tdb: change version to tdb-1.2.12
1f269fcc6e2bb46b765d547eb1add2bc52272c47 tdb: Add another overflow check to tdb_expand_adjust
d9b4f19e73f241a1ccf64f04c3cc28d481550bb7 tdb: Make tdb_recovery_allocate overflow-safe
8b215df4454883b3733733af4f49f87eb0a2a46a tdb: Make tdb_recovery_size overflow-safe
7ae09a9695bcc5fad606441db3ab6e413b9d48ce tdb: add proper OOM/ENOSPC handling to tdb_expand()
854c5f0aac03c7c6d7e1b37997dcdc848fec1499 tdb: add overflow detection to tdb_expand_adjust()
e19d46f7e31a32e2b5ae3ec05e13f32b8ac2109d tdb: add overflow/ENOSPC handling to tdb_expand_file()
a07ba17e0c91d726416db946e6f65b064b2d17ec tdb: add a 'new_size' helper variable to tdb_expand_file()
4483bf143ddfee9ec07aed8f124559b00f757d9a tdb: Add overflow-checking tdb_add_off_t
3bd686c5ad4756af1033ac14ba09a40156cc6d47 tdb: fix logging of offets and lengths.
cd4b413cb0574c459c1c24cf07f8d6b44f5fc077 build: Remove autoconf build system
11f467d0bd8e2264f311d82f3299443b99526bb3 tdb: include information about hash function being used in tdbtool info output
c8c0bf74805c61b1379dab1d4529df0004872bb4 tdb: Fix blank line endings
a92c08e18bca2f1db671dc5e2d0db4adbf39752d tdb: Little format change
68698b4e64831d2fdf762b5f8577ff404f10a3cb tdb: Slightly simplify tdb_expand_file
2f4b21bb57c4f96c5f5b57a69d022c142d8088d5 ntdb: switch between secrets.tdb and secrets.ntdb depending on 'use ntdb'
a7fdd4f7c2e64eedf12cb46c3435edbec772a4ab tdb: Slightly simplify transaction_write
fcb345f5d6be9659a0f8a5afe62a937010a33d5c tdb: Make tdb_release_transaction_locks use tdb_allrecord_unlock
5929e38b6cdbd4f9721293a19f079ceae1af76b0 tdb: Don't segfault if tdb_open_ex for check failed
3534e4e8d5bebfaaaef5886dcea219a7e4047fc7 tdb: Factor out the retry loop from tdb_allrecord_upgrade
1f93f0836484ccc1abe335f5bd2cfd35df0b7631 tdb: Simplify fcntl_lock() a bit
542400a966039178effd6c8da932ed3a8d749131 tdb: Use tdb_null in freelistcheck
68559b787e7c9a83e055493bde638ec02e1097d1 tdb: Enhance lock tracking a bit
05235d5b444558f6d06ef12ea7d74850800425cf tdb: Fix a typo
72cd5d5ff664dc46afb3dd6a5ea45a28ef7b8591 tdb: Remove "header" from tdb_context
71247ec4bdefb3a1b16619f7ea7404bcbafb5b60 tdb: Pass argument "header" to check_header_hash
1436107b0769c88e7eb50057b5f05ad5b8573990 tdb: Pass argument "header" to tdb_new_database
80a6fe84271d15cc22caa3d08768ab5559ef9ed7 Remove some unused variables.
f2d67af7bc0b316f54d6cc1a44d07f1b24244378 tdb: Fix undefined prototype warnings
1beb4bc9d12fb124935e9e4710f48ad616dacc60 tdb: Fix \n in error messages
a444bb95a270ca5b331041c11a7e785c1e0559b7 tdb: Add a comment explaining the "check"
3109b541c9b2f0063e1ccb0cdaec0a8e388b29b4 tdb: Make tdb_new_database() follow a more conventional style
d972e6fa74b6499403d4c3d3c6a84cbda7eded39 tdb: Fix a typo
c04de8f3a4deba0062f8cdbcbe74d3735a80b735 tdb: Fix a typo
24755d75b0ee7170195bc26cf28bab4ffdb6f752 tdb: Use tdb_lock_covered_by_allrecord_lock in tdb_unlock
f8dafe5685008671f4f983a4defc90b4a05cf992 tdb: Factor out tdb_lock_covered_by_allrecord_lock from tdb_lock_list
26b8545df44df7e60ba0ba7336ffbdda8a14e423 tdb: Simplify logic in tdb_lock_list slightly
0f4e7a1401998746a6818b9469ab369d70418ac1 tdb: Slightly simplify tdb_lock_list
116ec13bb0718eb1de1ac1f4410d5c33f1db616f tdb: Fix blank line endings
7237fdd4ddc0b9c848b5936431b4f8731ce56dba tdb: Fix a comment
d2b852d79bd83754d8952a0e3dece00e513549f2 tdb: Fix a typo
2c3fd8a13e7dde23a23404cd88078a04c8b338ea tdb: Fix a missing CONVERT
c9053796b389758e8bacff4cd37d633fd65171f9 tdb: Improve the documentation of tdb_reopen() and tdb_close().
7f08365a28770fdcc1bb625d8a16d11d8f15c29a tdb: Fix possible crash bugs in the python tdb code.
ede2aaef281048123cacab9ae879f5c546787080 lib/tdb: Rename manpages/ to man/.
68c6dcb0942244f542eec7bbe5fba78ef7f66051 docs: man tdbtool: Add missing meta data.
c62f8baff878001ead921112dd653ff69d1cfe7d tdb: Make tdb robust against improper CLEAR_IF_FIRST restart
37fd93194db10fc832ed3fa1ec880ebc26be904b tdb: Make robust against shrinking tdbs
100d38d6e0fae1dc666ae43941570c9f106b73c2 tdb: add -e option to tdbdump (and docment it).
ffde8678910ae838588ab380b48a544333f81241 tdb: tdbdump should log errors, and fail in that case.
90f463b25f7bb0bc944732773c56e356834ea203 tdb: add tdb_rescue()
a168a7c791a4be1730a370d059b3a1073fbb0bdd tdb: Fix a typo
1f50b6c3aefe9a7ac64641b1e9c23e014459647f tdb/test: fix build on OSF/1
41cffa3c8b126570203e32c2024d5a8f439b529e doc: Remove build/ from doxygen config or it will not work in brew.
ea6b8ee026a4c53d9dfb5a42e4d9e485b89018e3 lib/tdb: Fix format string errors found by -Werror=format in tdb tests
c92a5670e3d60decbc13bd8680de37184bc12815 pytdb: Check if the database is closed before we touch it
a8e88332a394f4a4c3e43b496738008fba39d39f pytdb: Check for errors parsing strings into TDB_DATA
66f59f040984bef5023fc844097b85bebee88f09 tdb: finish weaning off err.h.
3c4263e7580143c69225729f5b67f09c00add2fd tdb: don't use err.h in tests.
1783fe34433f9bb4b939de3231a7c296390ec426 tdb: make TDB_NOSYNC merely disable sync.
f7f6992c1e6ee8ac4a55c2fddf169ac695362036 autobuild: always set TDB_NO_FSYNC.
bf5934ca1b80930d8fd2f19ef12e32092b34fa4d tdb/wscript: Remove unecessary semicolons.
e2caba054f977b631720f8dc2528ba03dc237122 tdb: remove unused debug_fprintf() macro that breaks the build
0688cf102d2a513875d1832ad0de6052b837a72a tdb:tests: fix use of a non-existent word (existant)
c8877d8f63ea367401fae4377cd28ee91b58d9e3 build: Remove unused release scripts for tdb
593e731097bc6f2fd50034f5e3ddac017894e584 lib/tdb: Update ABI
3fdeaa3992bb0599613e20d8e3248c478897531f lib/tdb: Add/expose lock functions to support CTDB
4442c0b2c92e4b2e88661e15022228c5f6547112 lib/tdb: fix transaction issue for HAVE_INCOHERENT_MMAP.
c12970cc91cb4da8976801e194e29e33e02b340a lib/tdb: fix test/run-die-during-transaction when HAVE_INCOHERENT_MMAP.
330e3e1b91ecbf99af3b598b324f21b5eff933fd lib/tdb: fix missing return 0 code.
fde694274e1e5a11d1473695e7ec7a97f95d39e4 lib/tdb: fix OpenBSD incoherent mmap.
eafd83736918bc5953e4a91cf2d893e68f2da2a2 lib/tdb: fix up run-die-during-transaction test cases on Solaris.
3272ba0d2d63e6a7d00972bc2c052aee84f073fd lib/tdb: remove unnecessary XOPEN and FILE_OFFSET_BITS defines in test/
583ffeae404cc632eebc43fed054391a59dffee2 lib/tdb: fix tests for standalone out-of-tree.
4d58d0fa8f936e7efdc02e31c053d42a47b3e62a tdb: build and run unit tests in tdb/test/
205242e1769f96e0e8fccd52378965d35dd02093 tdb/test: fix up tests for use in SAMBA tdb code.
8fa345d952328c5866f3a0f835f3599343c51b00 tdb: wean CCAN-style unit tests off of tap.
0802791081ba39298aa93f0e6860c3b62800df73 tdb: import unit tests from CCAN into tdb/test/
390b9a2dd8447ecd16e3957c02fa886781797733 tdb: make tdb_private.h idempotent.
eff69aa0f908f5cb44b3cb846c8a4ada874240fa Add "repack" command to tdbtool.
7b42ceb414d3e14c411dd95dcd0b200113fe1191 Fix compile when TDB_TRACE is enabled.
c1e9537ed0f58404fed96a7aa9e581b8ebb1fb60 tdb: Use tdb_parse_record in tdb_update_hash
5767224b7f4703c3195aa69eef4352d80980f95e tdb: don't free old recovery area when expanding if already at EOF.
3a2a755e3380a8f81374009d463cd06161352507 tdb: use same expansion factor logic when expanding for new recovery area.
664add17757836c5ee98618aef11371f412b6e44 tdb: Avoid a malloc/memcpy in _tdb_store
b64494535dc62f4073fc6302847593ed6e6ec38b tdb: be more careful on 4G files.
20789bfdde37ee3140422b5599d41a280c01d29f tdb: Fix python documentation for tdb module
3741cf999e0f05a831d88330ac6bfa7ad34c2ec7 Remove unused variable.
3e6e1aed949a4483fc38607e443b5c8b715aca3b Fix a bunch of "warning: variable ‘XXXX’ set but not used [-Wunused-but-set-variable]" warnings from the new gcc.
86afe83d867229b11fd4ec9cb6e29af698cacdef waf: Factor checking for undefined symbol flags out into separate method.
3585abcd4cc1b6ffeeb7f64abe3d21a12f9633f6 pytdb: Shorter description which fits on a single line.
774f85649b5d9f8872ebfdd359964330b4ff436a tdb: Only check for pkg-config file when checking for system tdb.
31912781ca84db9b27264b5182729d1097c0661d wafsamba: Only install .pc files if libraries are public.
a5025a3c2fa83c67e0a53611ad8fbe264888a590 tdb: Install pkg-config file.
ee720fc19cebf9108711429dfe25ccaf192e2c7e tdb: increment sequence number in tdb_wipe_all().
e01f3108ff447239fb3cb2f89b4749c5f7b88c3b tdb: remove 'EOF' print from tdbrestore
5eecc854236f0b943aaa89e0c3a46f9fbd208ca9 tdb2: create tdb2 versions of various testing TDBs.
6bc59d77b64d1ecbe5c906ed2fa80a7b8ca5d420 tdb_store: check returns for 0, not -1.
4fa51257b283c2e8bb415cc7f9c001d64c8a2669 tdb: enable VALGRIND to remove valgrind noise.
43ab5aa390769ee9b57918cf5b57aa4a22586805 lib/tdb/python/tests/simple.py: don't assume TDB ordering.
73c31f044e32103276558a194698ea6cf876b4f2 tdb: fix a build warning.
bf3b2e2aee284c85ecea6a3142bc1fa5344b430a Support the 'PYTHON' environment variable.
1804d9a64662d37f6c7c50bdd7b8edd80f42192b tdb_backup: avoid transaction on backup file, use lockall
36cfa7b79e36d880cdbf24d0769558be44d0edda tdb: make sure we skip over recovery area correctly.
cb884186a55c9ef8aca6ee48b16423b3c881e689 tdb_expand: limit the expansion with huge records
094ab60053bcc0bc3542af8144e394d83270053e tdb: tdb_repack() only when it's worthwhile.
6aa72dae8fc341de5b497f831ded1f8f519fa8fb tdb: fix transaction recovery area for converted tdbs.
0080f944b47f3afa676153e5da7093a8667fc005 tdb: Fix Coverity ID 2238: SECURE_CODING
25397de589e577e32bb291576b10c18978b5bc4e tdb: Fix Coverity ID 2192: NO_EFFECT
bfce962c8f5219e72a07810a663a14542355927d tdb: rename convert_string() to tdb_convert_string()
c56e3ccfc9eafbb69b03dc40346e3115bec42ef6 lib: don't install public headers if a private library
7b948a39e1783ff4732f9734830b0544d6a814b1 tdb: use public_headers to install header files
0a0ebd73fb98002f099544eca5deaf6763790277 tdb: use system include style for system headers
949427c208159f4ac580f547dd5465a70b4751b7 python: use os.environ[] instead of os.putenv()
91cad71390bd2a0330891083c65d3f9000b74657 tdb: Fix a C++ warning
8b8caac6d0ac980e59bc5bcbfb06502deebb9f42 build: removed the old autogen.sh and autogen-waf.sh scripts
b42afa0edf375c944d39a888f4db422e8d2b13cf tdb: Added doxygen documentation.
005c6370cdaab69d4228ecbf5e7369ebc61b86ae waf: ensure "make dist" works from a clean git tree for all libraries
24d5a7202ab521b92eb07c93647ae2d381e181a5 tdbrestore: Update to GPLv3+, remove old FSF address.
5792fa90ace06f736661d9924ec9a75c3a0a9771 s4-python: Only set BASETYPE flag if subclassing is supported.
51239bb26a714bf4c41fb15fde211df1255f9468 talloc/tdb/tevent: Remove obsolete signatures files.
b222615b5978aa78e82af79359b7cc3baec0bc87 tdb: add ABI/tdb-1.2.9.sigs
cac57328a6077dc428396402036636095f139569 tdb: tdb_summary() support.
7ea1b767977c8c201c0f5bfaeb01f96af4b51f7b tdb: setup TEST_DATA_PREFIX for make test
b83672b36c1ea8c35833c40c3919b63809f16624 tdb:tdbtorture: use TEST_DATA_PREFIX for files
d81ceeb9833ec80d7ab21b34e4b3af3d79aeb845 tdb:tdbtest: use TEST_DATA_PREFIX for files
9e8a04984327ffae611165244a159ff9c6ca30f4 tdb: Remove autotools support.
46ee6908be64c4405b3a8f7477abc119aa060020 tdb: add ABI/tdb-1.2.8.sigs
c754fad5712cc7c1912f27eb5595c12cf65e55c6 tdb: Bump version number after symbol versioning changes.
51e7244269e9c14a920f91a485cda6c785b2fc85 pytdb: Make PyTdb variable static.
87337383572324e3d1d00ed710614ebe217aa2b2 build: introduce SAMBA_CHECK_PYTHON_HEADERS
57f2f1d72a70a80e61a2ed6f1abc63a177a590ab waf: remove the restriction that private libraries must not have a vnum
ebe2867fc2c01fb5288d62eedb0e2f43788b9f27 waf-abi: auto-generate per-symbol versions from ABI files
735c1cd2da15167748e92ba6de48fdb5169db587 s4-pkgconfig: add @LIB_RPATH@ to our link flags
989d8803f28826e6541667127abad801c4fa4566 tdb:common/open.c - use "discard_const_p" for certain "tdb->name" assignments
d2560cd7dc106d7853442133f237001f68bcb971 tdb:tdbstore.c - remove an useless '\'
2ac5cedb719d220db412d0bdc69e34bad9ab26f1 Avoid the use of PyAPI_DATA, which is for internal Python API's.
dedd064aa825edd57f992b12218a184398db9586 tdb: set tdb->name early, as it's needed for tdb_name()
f0a472a2d678dd0374181f1a6ac0a3d35503636e waf: added reconfigure targets to our libraries
1aa8308c30962ac04a2997acaa7f2a7458729cc2 tdb: Use waf by default.
3deece559159150a0710d8160f39583ba7f2e582 s4: Remove the old perl/m4/make/mk-based build system.
50256c01d061c6d73bb2d8ee2c60785d58748e6c waf: Only specify vnum for non-private libraries.
49ef2888193dd7cc37c3fe0a980b7cc1abdac805 waf: Rename some BUNDLED_ functios to PRIVATE_.
dec00bf0974ea3b5079c32e2a6e6253954297253 tdb: Revert re-addition of tdb_set_logging_function.
ee913f45683e66d4391944e034217a56d42e7ab5 tdb: commit the version 1.2.7 signatures
c529317fe2b48e045b35a613cfd1ad3f03b68435 Lowercase socket_wrapper name.
62c4af99428abb2d4ac1b18454d72e0c8cbb67e8 tdb: Set _PUBLIC_ in C file rather than header files (Debian bug 600898)
7cba3cfac8781061e4114573517b30baedbf891a waf: replace the is_bundled option with private_library
713900b81297548c44a890c3bca1dde9019af8bc s4-build: fixed some formatting
05c1beb6b47e607dac9850e81cef775a1d9b00ae tdb: Bump version to 1.2.7 after addition of pytdb.__version__.
bb0017615d44b66828c98a408ca15b50956f3e91 waf: fixed exit status of test suites
20d39691a8eecd57b27cb709a70c50bf572b8114 tdb: Only use system pytdb when using system tdb.
e805bf52c9ed32bd53759996b5700c5d582a2a58 tdb: Support using system pytdb.

Signed-off-by: Amitay Isaacs <amitay@gmail.com>
81 files changed:
lib/tdb/ABI/tdb-1.2.1.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.10.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.11.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.12.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.2.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.3.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.4.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.5.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.7.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.8.sigs [new file with mode: 0644]
lib/tdb/ABI/tdb-1.2.9.sigs [new file with mode: 0644]
lib/tdb/common/check.c
lib/tdb/common/dump.c
lib/tdb/common/error.c
lib/tdb/common/freelist.c
lib/tdb/common/freelistcheck.c
lib/tdb/common/hash.c
lib/tdb/common/io.c
lib/tdb/common/lock.c
lib/tdb/common/open.c
lib/tdb/common/rescue.c [new file with mode: 0644]
lib/tdb/common/summary.c [new file with mode: 0644]
lib/tdb/common/tdb.c
lib/tdb/common/tdb_private.h
lib/tdb/common/transaction.c
lib/tdb/common/traverse.c
lib/tdb/configure.ac
lib/tdb/docs/mainpage.dox [new file with mode: 0644]
lib/tdb/doxy.config [new file with mode: 0644]
lib/tdb/include/tdb.h
lib/tdb/man/tdbbackup.8.xml [new file with mode: 0644]
lib/tdb/man/tdbdump.8.xml [new file with mode: 0644]
lib/tdb/man/tdbrestore.8.xml [new file with mode: 0644]
lib/tdb/man/tdbtool.8.xml [new file with mode: 0644]
lib/tdb/pytdb.c
lib/tdb/python/tests/simple.py
lib/tdb/tdb.pc.in
lib/tdb/test/external-agent.c [new file with mode: 0644]
lib/tdb/test/external-agent.h [new file with mode: 0644]
lib/tdb/test/jenkins-be-hash.tdb [new file with mode: 0644]
lib/tdb/test/jenkins-le-hash.tdb [new file with mode: 0644]
lib/tdb/test/lock-tracking.c [new file with mode: 0644]
lib/tdb/test/lock-tracking.h [new file with mode: 0644]
lib/tdb/test/logging.c [new file with mode: 0644]
lib/tdb/test/logging.h [new file with mode: 0644]
lib/tdb/test/old-nohash-be.tdb [new file with mode: 0644]
lib/tdb/test/old-nohash-le.tdb [new file with mode: 0644]
lib/tdb/test/run-3G-file.c [new file with mode: 0644]
lib/tdb/test/run-bad-tdb-header.c [new file with mode: 0644]
lib/tdb/test/run-check.c [new file with mode: 0644]
lib/tdb/test/run-corrupt.c [new file with mode: 0644]
lib/tdb/test/run-die-during-transaction.c [new file with mode: 0644]
lib/tdb/test/run-endian.c [new file with mode: 0644]
lib/tdb/test/run-incompatible.c [new file with mode: 0644]
lib/tdb/test/run-nested-transactions.c [new file with mode: 0644]
lib/tdb/test/run-nested-traverse.c [new file with mode: 0644]
lib/tdb/test/run-no-lock-during-traverse.c [new file with mode: 0644]
lib/tdb/test/run-oldhash.c [new file with mode: 0644]
lib/tdb/test/run-open-during-transaction.c [new file with mode: 0644]
lib/tdb/test/run-readonly-check.c [new file with mode: 0644]
lib/tdb/test/run-rescue-find_entry.c [new file with mode: 0644]
lib/tdb/test/run-rescue.c [new file with mode: 0644]
lib/tdb/test/run-rwlock-check.c [new file with mode: 0644]
lib/tdb/test/run-summary.c [new file with mode: 0644]
lib/tdb/test/run-transaction-expand.c [new file with mode: 0644]
lib/tdb/test/run-traverse-in-transaction.c [new file with mode: 0644]
lib/tdb/test/run-wronghash-fail.c [new file with mode: 0644]
lib/tdb/test/run-zero-append.c [new file with mode: 0644]
lib/tdb/test/run.c [new file with mode: 0644]
lib/tdb/test/rwlock-be.tdb [new file with mode: 0644]
lib/tdb/test/rwlock-le.tdb [new file with mode: 0644]
lib/tdb/test/tap-interface.h [new file with mode: 0644]
lib/tdb/test/tap-to-subunit.h [new file with mode: 0644]
lib/tdb/test/tdb.corrupt [new file with mode: 0644]
lib/tdb/tools/tdbbackup.c
lib/tdb/tools/tdbdump.c
lib/tdb/tools/tdbrestore.c
lib/tdb/tools/tdbtest.c
lib/tdb/tools/tdbtool.c
lib/tdb/tools/tdbtorture.c
lib/tdb/wscript [new file with mode: 0644]

diff --git a/lib/tdb/ABI/tdb-1.2.1.sigs b/lib/tdb/ABI/tdb-1.2.1.sigs
new file mode 100644 (file)
index 0000000..84f2007
--- /dev/null
@@ -0,0 +1,95 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_allrecord_lock: int (struct tdb_context *, int, enum tdb_lock_flags, bool)
+tdb_allrecord_unlock: int (struct tdb_context *, int, bool)
+tdb_allrecord_upgrade: int (struct tdb_context *)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, int, tdb_off_t, size_t, enum tdb_lock_flags)
+tdb_brunlock: int (struct tdb_context *, int, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_have_extra_locks: bool (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_needs_recovery: bool (struct tdb_context *)
+tdb_nest_lock: int (struct tdb_context *, uint32_t, int, enum tdb_lock_flags)
+tdb_nest_unlock: int (struct tdb_context *, uint32_t, int, bool)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_release_transaction_locks: void (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int, enum tdb_lock_flags)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *, int)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
diff --git a/lib/tdb/ABI/tdb-1.2.10.sigs b/lib/tdb/ABI/tdb-1.2.10.sigs
new file mode 100644 (file)
index 0000000..61f6c19
--- /dev/null
@@ -0,0 +1,66 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_summary: char *(struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_transaction_write_lock_mark: int (struct tdb_context *)
+tdb_transaction_write_lock_unmark: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.11.sigs b/lib/tdb/ABI/tdb-1.2.11.sigs
new file mode 100644 (file)
index 0000000..d727f21
--- /dev/null
@@ -0,0 +1,67 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_rescue: int (struct tdb_context *, void (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_summary: char *(struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_transaction_write_lock_mark: int (struct tdb_context *)
+tdb_transaction_write_lock_unmark: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.12.sigs b/lib/tdb/ABI/tdb-1.2.12.sigs
new file mode 100644 (file)
index 0000000..d727f21
--- /dev/null
@@ -0,0 +1,67 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_rescue: int (struct tdb_context *, void (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_summary: char *(struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_transaction_write_lock_mark: int (struct tdb_context *)
+tdb_transaction_write_lock_unmark: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.2.sigs b/lib/tdb/ABI/tdb-1.2.2.sigs
new file mode 100644 (file)
index 0000000..043790d
--- /dev/null
@@ -0,0 +1,60 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.3.sigs b/lib/tdb/ABI/tdb-1.2.3.sigs
new file mode 100644 (file)
index 0000000..043790d
--- /dev/null
@@ -0,0 +1,60 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.4.sigs b/lib/tdb/ABI/tdb-1.2.4.sigs
new file mode 100644 (file)
index 0000000..043790d
--- /dev/null
@@ -0,0 +1,60 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.5.sigs b/lib/tdb/ABI/tdb-1.2.5.sigs
new file mode 100644 (file)
index 0000000..1e01f3b
--- /dev/null
@@ -0,0 +1,61 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.7.sigs b/lib/tdb/ABI/tdb-1.2.7.sigs
new file mode 100644 (file)
index 0000000..1e01f3b
--- /dev/null
@@ -0,0 +1,61 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.8.sigs b/lib/tdb/ABI/tdb-1.2.8.sigs
new file mode 100644 (file)
index 0000000..1e01f3b
--- /dev/null
@@ -0,0 +1,61 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
diff --git a/lib/tdb/ABI/tdb-1.2.9.sigs b/lib/tdb/ABI/tdb-1.2.9.sigs
new file mode 100644 (file)
index 0000000..9e4149b
--- /dev/null
@@ -0,0 +1,62 @@
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: dptr = 0xXXXX, dsize = 0
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_summary: char *(struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
index 58c9c26540dcfb29d292e0c00bdb22bc9afc3c20..9f9d8708c1e26944f254d385e70a52c940ec23e3 100644 (file)
@@ -50,11 +50,11 @@ static bool tdb_check_header(struct tdb_context *tdb, tdb_off_t *recovery)
        if (hdr.hash_size == 0)
                goto corrupt;
 
-       if (hdr.hash_size != tdb->header.hash_size)
+       if (hdr.hash_size != tdb->hash_size)
                goto corrupt;
 
        if (hdr.recovery_start != 0 &&
-           hdr.recovery_start < TDB_DATA_START(tdb->header.hash_size))
+           hdr.recovery_start < TDB_DATA_START(tdb->hash_size))
                goto corrupt;
 
        *recovery = hdr.recovery_start;
@@ -74,43 +74,43 @@ static bool tdb_check_record(struct tdb_context *tdb,
        tdb_off_t tailer;
 
        /* Check rec->next: 0 or points to record offset, aligned. */
-       if (rec->next > 0 && rec->next < TDB_DATA_START(tdb->header.hash_size)){
+       if (rec->next > 0 && rec->next < TDB_DATA_START(tdb->hash_size)){
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d too small next %d\n",
+                        "Record offset %u too small next %u\n",
                         off, rec->next));
                goto corrupt;
        }
        if (rec->next + sizeof(*rec) < rec->next) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d too large next %d\n",
+                        "Record offset %u too large next %u\n",
                         off, rec->next));
                goto corrupt;
        }
        if ((rec->next % TDB_ALIGNMENT) != 0) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d misaligned next %d\n",
+                        "Record offset %u misaligned next %u\n",
                         off, rec->next));
                goto corrupt;
        }
-       if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0))
+       if (tdb->methods->tdb_oob(tdb, rec->nextsizeof(*rec), 0))
                goto corrupt;
 
        /* Check rec_len: similar to rec->next, implies next record. */
        if ((rec->rec_len % TDB_ALIGNMENT) != 0) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d misaligned length %d\n",
+                        "Record offset %u misaligned length %u\n",
                         off, rec->rec_len));
                goto corrupt;
        }
        /* Must fit tailer. */
        if (rec->rec_len < sizeof(tailer)) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d too short length %d\n",
+                        "Record offset %u too short length %u\n",
                         off, rec->rec_len));
                goto corrupt;
        }
        /* OOB allows "right at the end" access, so this works for last rec. */
-       if (tdb->methods->tdb_oob(tdb, off+sizeof(*rec)+rec->rec_len, 0))
+       if (tdb->methods->tdb_oob(tdb, offsizeof(*rec)+rec->rec_len, 0))
                goto corrupt;
 
        /* Check tailer. */
@@ -119,7 +119,7 @@ static bool tdb_check_record(struct tdb_context *tdb,
                goto corrupt;
        if (tailer != sizeof(*rec) + rec->rec_len) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d invalid tailer\n", off));
+                        "Record offset %u invalid tailer\n", off));
                goto corrupt;
        }
 
@@ -247,7 +247,7 @@ static bool tdb_check_used_record(struct tdb_context *tdb,
        /* key + data + tailer must fit in record */
        if (rec->key_len + rec->data_len + sizeof(tdb_off_t) > rec->rec_len) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d too short for contents\n", off));
+                        "Record offset %u too short for contents\n", off));
                return false;
        }
 
@@ -257,7 +257,7 @@ static bool tdb_check_used_record(struct tdb_context *tdb,
 
        if (tdb->hash_fn(&key) != rec->full_hash) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                        "Record offset %d has incorrect hash\n", off));
+                        "Record offset %u has incorrect hash\n", off));
                goto fail_put_key;
        }
 
@@ -308,7 +308,7 @@ static bool tdb_check_free_record(struct tdb_context *tdb,
 }
 
 /* Slow, but should be very rare. */
-static size_t dead_space(struct tdb_context *tdb, tdb_off_t off)
+size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off)
 {
        size_t len;
 
@@ -322,7 +322,7 @@ static size_t dead_space(struct tdb_context *tdb, tdb_off_t off)
        return len;
 }
 
-int tdb_check(struct tdb_context *tdb,
+_PUBLIC_ int tdb_check(struct tdb_context *tdb,
              int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
              void *private_data)
 {
@@ -345,14 +345,14 @@ int tdb_check(struct tdb_context *tdb,
        }
 
        /* Make sure we know true size of the underlying file. */
-       tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+       tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
 
        /* Header must be OK: also gets us the recovery ptr, if any. */
        if (!tdb_check_header(tdb, &recovery_start))
                goto unlock;
 
        /* We should have the whole header, too. */
-       if (tdb->map_size < TDB_DATA_START(tdb->header.hash_size)) {
+       if (tdb->map_size < TDB_DATA_START(tdb->hash_size)) {
                tdb->ecode = TDB_ERR_CORRUPT;
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "File too short for hashes\n"));
                goto unlock;
@@ -360,20 +360,20 @@ int tdb_check(struct tdb_context *tdb,
 
        /* One big malloc: pointers then bit arrays. */
        hashes = (unsigned char **)calloc(
-                       1, sizeof(hashes[0]) * (1+tdb->header.hash_size)
-                       + BITMAP_BITS / CHAR_BIT * (1+tdb->header.hash_size));
+                       1, sizeof(hashes[0]) * (1+tdb->hash_size)
+                       + BITMAP_BITS / CHAR_BIT * (1+tdb->hash_size));
        if (!hashes) {
                tdb->ecode = TDB_ERR_OOM;
                goto unlock;
        }
 
        /* Initialize pointers */
-       hashes[0] = (unsigned char *)(&hashes[1+tdb->header.hash_size]);
-       for (h = 1; h < 1+tdb->header.hash_size; h++)
+       hashes[0] = (unsigned char *)(&hashes[1+tdb->hash_size]);
+       for (h = 1; h < 1+tdb->hash_size; h++)
                hashes[h] = hashes[h-1] + BITMAP_BITS / CHAR_BIT;
 
        /* Freelist and hash headers are all in a row: read them. */
-       for (h = 0; h < 1+tdb->header.hash_size; h++) {
+       for (h = 0; h < 1+tdb->hash_size; h++) {
                if (tdb_ofs_read(tdb, FREELIST_TOP + h*sizeof(tdb_off_t),
                                 &off) == -1)
                        goto free;
@@ -382,7 +382,7 @@ int tdb_check(struct tdb_context *tdb,
        }
 
        /* For each record, read it in and check it's ok. */
-       for (off = TDB_DATA_START(tdb->header.hash_size);
+       for (off = TDB_DATA_START(tdb->hash_size);
             off < tdb->map_size;
             off += sizeof(rec) + rec.rec_len) {
                if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
@@ -406,19 +406,19 @@ int tdb_check(struct tdb_context *tdb,
                                found_recovery = true;
                                break;
                        }
-                       dead = dead_space(tdb, off);
+                       dead = tdb_dead_space(tdb, off);
                        if (dead < sizeof(rec))
                                goto corrupt;
 
                        TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                                "Dead space at %d-%d (of %u)\n",
+                                "Dead space at %u-%u (of %u)\n",
                                 off, off + dead, tdb->map_size));
                        rec.rec_len = dead - sizeof(rec);
                        break;
                case TDB_RECOVERY_MAGIC:
                        if (recovery_start != off) {
                                TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                                        "Unexpected recovery record at offset %d\n",
+                                        "Unexpected recovery record at offset %u\n",
                                         off));
                                goto free;
                        }
@@ -428,7 +428,7 @@ int tdb_check(struct tdb_context *tdb,
                corrupt:
                        tdb->ecode = TDB_ERR_CORRUPT;
                        TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                                "Bad magic 0x%x at offset %d\n",
+                                "Bad magic 0x%x at offset %u\n",
                                 rec.magic, off));
                        goto free;
                }
@@ -436,7 +436,7 @@ int tdb_check(struct tdb_context *tdb,
 
        /* Now, hashes should all be empty: each record exists and is referred
         * to by one other. */
-       for (h = 0; h < 1+tdb->header.hash_size; h++) {
+       for (h = 0; h < 1+tdb->hash_size; h++) {
                unsigned int i;
                for (i = 0; i < BITMAP_BITS / CHAR_BIT; i++) {
                        if (hashes[h][i] != 0) {
index 9f770f81a52381d13d339bfff1b8701ca862cc80..7193c1e110e7fe1533bdf3abf69484b17757758e 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
@@ -33,14 +33,14 @@ static tdb_off_t tdb_dump_record(struct tdb_context *tdb, int hash,
        struct tdb_record rec;
        tdb_off_t tailer_ofs, tailer;
 
-       if (tdb->methods->tdb_read(tdb, offset, (char *)&rec, 
+       if (tdb->methods->tdb_read(tdb, offset, (char *)&rec,
                                   sizeof(rec), DOCONV()) == -1) {
                printf("ERROR: failed to read record at %u\n", offset);
                return 0;
        }
 
-       printf(" rec: hash=%d offset=0x%08x next=0x%08x rec_len=%d "
-              "key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
+       printf(" rec: hash=%d offset=0x%08x next=0x%08x rec_len=%u "
+              "key_len=%u data_len=%u full_hash=0x%x magic=0x%x\n",
               hash, offset, rec.next, rec.rec_len, rec.key_len, rec.data_len,
               rec.full_hash, rec.magic);
 
@@ -80,17 +80,17 @@ static int tdb_dump_chain(struct tdb_context *tdb, int i)
        return tdb_unlock(tdb, i, F_WRLCK);
 }
 
-void tdb_dump_all(struct tdb_context *tdb)
+_PUBLIC_ void tdb_dump_all(struct tdb_context *tdb)
 {
        int i;
-       for (i=0;i<tdb->header.hash_size;i++) {
+       for (i=0;i<tdb->hash_size;i++) {
                tdb_dump_chain(tdb, i);
        }
        printf("freelist:\n");
        tdb_dump_chain(tdb, -1);
 }
 
-int tdb_printfreelist(struct tdb_context *tdb)
+_PUBLIC_ int tdb_printfreelist(struct tdb_context *tdb)
 {
        int ret;
        long total_free = 0;
@@ -110,7 +110,7 @@ int tdb_printfreelist(struct tdb_context *tdb)
 
        printf("freelist top=[0x%08x]\n", rec_ptr );
        while (rec_ptr) {
-               if (tdb->methods->tdb_read(tdb, rec_ptr, (char *)&rec, 
+               if (tdb->methods->tdb_read(tdb, rec_ptr, (char *)&rec,
                                           sizeof(rec), DOCONV()) == -1) {
                        tdb_unlock(tdb, -1, F_WRLCK);
                        return -1;
@@ -122,15 +122,14 @@ int tdb_printfreelist(struct tdb_context *tdb)
                        return -1;
                }
 
-               printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)] (end = 0x%08x)\n", 
+               printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%u)] (end = 0x%08x)\n",
                       rec_ptr, rec.rec_len, rec.rec_len, rec_ptr + rec.rec_len);
                total_free += rec.rec_len;
 
                /* move to the next record */
                rec_ptr = rec.next;
        }
-       printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, 
-               (int)total_free);
+       printf("total rec_len = [0x%08lx (%lu)]\n", total_free, total_free);
 
        return tdb_unlock(tdb, -1, F_WRLCK);
 }
index 9197918ddeaa048f56c1ebbaa611989add0a6bf4..478eb887ffdd4a08a5bfc39084a811a4559c3883 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
@@ -27,7 +27,7 @@
 
 #include "tdb_private.h"
 
-enum TDB_ERROR tdb_error(struct tdb_context *tdb)
+_PUBLIC_ enum TDB_ERROR tdb_error(struct tdb_context *tdb)
 {
        return tdb->ecode;
 }
@@ -46,7 +46,7 @@ static struct tdb_errname {
             {TDB_ERR_RDONLY, "write not permitted"} };
 
 /* Error string for the last tdb error */
-const char *tdb_errorstr(struct tdb_context *tdb)
+_PUBLIC_ const char *tdb_errorstr(struct tdb_context *tdb)
 {
        uint32_t i;
        for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
index 79e3c344b8bb0f93a4dd85d4a42807afc1820f35..ea14dd02ab97e5279235611e887f167689628608 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
@@ -28,7 +28,7 @@
 #include "tdb_private.h"
 
 /* 'right' merges can involve O(n^2) cost when combined with a
-   traverse, so they are disabled until we find a way to do them in 
+   traverse, so they are disabled until we find a way to do them in
    O(1) time
 */
 #define USE_RIGHT_MERGES 0
@@ -42,21 +42,21 @@ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct tdb_record
        if (rec->magic == TDB_MAGIC) {
                /* this happens when a app is showdown while deleting a record - we should
                   not completely fail when this happens */
-               TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read non-free magic 0x%x at offset=%d - fixing\n", 
+               TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read non-free magic 0x%x at offset=%u - fixing\n",
                         rec->magic, off));
                rec->magic = TDB_FREE_MAGIC;
-               if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
+               if (tdb_rec_write(tdb, off, rec) == -1)
                        return -1;
        }
 
        if (rec->magic != TDB_FREE_MAGIC) {
                /* Ensure ecode is set for log fn. */
                tdb->ecode = TDB_ERR_CORRUPT;
-               TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%d\n", 
+               TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%u\n",
                           rec->magic, off));
                return -1;
        }
-       if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+       if (tdb->methods->tdb_oob(tdb, rec->nextsizeof(*rec), 0) != 0)
                return -1;
        return 0;
 }
@@ -79,7 +79,7 @@ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_
                last_ptr = i;
        }
        tdb->ecode = TDB_ERR_CORRUPT;
-       TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off));
+       TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%u\n", off));
        return -1;
 }
 #endif
@@ -139,7 +139,7 @@ left:
 #endif
 
        /* Look left */
-       if (offset - sizeof(tdb_off_t) > TDB_DATA_START(tdb->header.hash_size)) {
+       if (offset - sizeof(tdb_off_t) > TDB_DATA_START(tdb->hash_size)) {
                tdb_off_t left = offset - sizeof(tdb_off_t);
                struct tdb_record l;
                tdb_off_t leftsize;
@@ -158,7 +158,7 @@ left:
                left = offset - leftsize;
 
                if (leftsize > offset ||
-                   left < TDB_DATA_START(tdb->header.hash_size)) {
+                   left < TDB_DATA_START(tdb->hash_size)) {
                        goto update;
                }
 
@@ -170,7 +170,7 @@ left:
 
                /* If it's free, expand to include it. */
                if (l.magic == TDB_FREE_MAGIC) {
-                       /* we now merge the new record into the left record, rather than the other 
+                       /* we now merge the new record into the left record, rather than the other
                           way around. This makes the operation O(1) instead of O(n). This change
                           prevents traverse from being O(n^2) after a lot of deletes */
                        l.rec_len += sizeof(*rec) + rec->rec_len;
@@ -195,7 +195,7 @@ update:
        if (tdb_ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
            tdb_rec_write(tdb, offset, rec) == -1 ||
            tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
-               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%d\n", offset));
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%u\n", offset));
                goto fail;
        }
 
@@ -210,7 +210,7 @@ update:
 
 
 
-/* 
+/*
    the core of tdb_allocate - called when we have decided which
    free list entry to use
 
@@ -218,7 +218,7 @@ update:
    not the beginning. This is so the left merge in a free is more likely to be
    able to free up the record without fragmentation
  */
-static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, 
+static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb,
                                  tdb_len_t length, tdb_off_t rec_ptr,
                                  struct tdb_record *rec, tdb_off_t last_ptr)
 {
@@ -250,7 +250,7 @@ static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb,
        }
 
        /* and setup the new record */
-       rec_ptr += sizeof(*rec) + rec->rec_len; 
+       rec_ptr += sizeof(*rec) + rec->rec_len;
 
        memset(rec, '\0', sizeof(*rec));
        rec->rec_len = length;
@@ -303,7 +303,7 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_rec
        bestfit.last_ptr = 0;
        bestfit.rec_len = 0;
 
-       /* 
+       /*
           this is a best fit allocation strategy. Originally we used
           a first fit strategy, but it suffered from massive fragmentation
           issues when faced with a slowly increasing record size.
@@ -347,7 +347,7 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_rec
                        goto fail;
                }
 
-               newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, 
+               newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr,
                                              rec, bestfit.last_ptr);
                tdb_unlock(tdb, -1, F_WRLCK);
                return newrec_ptr;
@@ -364,10 +364,10 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_rec
 
 
 
-/* 
-   return the size of the freelist - used to decide if we should repack 
+/*
+   return the size of the freelist - used to decide if we should repack
 */
-int tdb_freelist_size(struct tdb_context *tdb)
+_PUBLIC_ int tdb_freelist_size(struct tdb_context *tdb)
 {
        tdb_off_t ptr;
        int count=0;
index 8d1ebabe04e85c9c3650797bde2a83f187b9ce2b..c6bfeaa41bf4af9e03267884c17f958f0c60d24d 100644 (file)
 
 static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr)
 {
-       TDB_DATA key, data;
+       TDB_DATA key;
 
-       memset(&data, '\0', sizeof(data));
        key.dptr = (unsigned char *)&rec_ptr;
        key.dsize = sizeof(rec_ptr);
-       return tdb_store(mem_tdb, key, data, TDB_INSERT);
+       return tdb_store(mem_tdb, key, tdb_null, TDB_INSERT);
 }
 
-int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries)
+_PUBLIC_ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries)
 {
        struct tdb_context *mem_tdb = NULL;
        struct tdb_record rec;
@@ -52,7 +51,7 @@ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries)
 
        *pnum_entries = 0;
 
-       mem_tdb = tdb_open("flval", tdb->header.hash_size,
+       mem_tdb = tdb_open("flval", tdb->hash_size,
                                TDB_INTERNAL, O_RDWR, 0600);
        if (!mem_tdb) {
                return -1;
index e8f7555a3cbbd13a70bc6e9410e7fb219b1733b1..1eed7221d2ab3216cae88e7bb8a51baccb2ec6ae 100644 (file)
@@ -23,7 +23,6 @@
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 #include "tdb_private.h"
-#define VALGRIND
 
 /* This is based on the hash algorithm from gdbm */
 unsigned int tdb_old_hash(TDB_DATA *key)
@@ -215,9 +214,7 @@ static uint32_t hashlittle( const void *key, size_t length )
   u.ptr = key;
   if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
     const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
-#ifdef VALGRIND
     const uint8_t  *k8;
-#endif
 
     /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
     while (length > 12)
@@ -231,36 +228,6 @@ static uint32_t hashlittle( const void *key, size_t length )
     }
 
     /*----------------------------- handle the last (probably partial) block */
-    /*
-     * "k[2]&0xffffff" actually reads beyond the end of the string, but
-     * then masks off the part it's not allowed to read.  Because the
-     * string is aligned, the masked-off tail is in the same word as the
-     * rest of the string.  Every machine with memory protection I've seen
-     * does it on word boundaries, so is OK with this.  But VALGRIND will
-     * still catch it and complain.  The masking trick does make the hash
-     * noticably faster for short strings (like English words).
-     */
-#ifndef VALGRIND
-
-    switch(length)
-    {
-    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
-    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
-    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
-    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
-    case 8 : b+=k[1]; a+=k[0]; break;
-    case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
-    case 6 : b+=k[1]&0xffff; a+=k[0]; break;
-    case 5 : b+=k[1]&0xff; a+=k[0]; break;
-    case 4 : a+=k[0]; break;
-    case 3 : a+=k[0]&0xffffff; break;
-    case 2 : a+=k[0]&0xffff; break;
-    case 1 : a+=k[0]&0xff; break;
-    case 0 : return c;              /* zero length strings require no mixing */
-    }
-
-#else /* make valgrind happy */
-
     k8 = (const uint8_t *)k;
     switch(length)
     {
@@ -278,9 +245,6 @@ static uint32_t hashlittle( const void *key, size_t length )
     case 1 : a+=k8[0]; break;
     case 0 : return c;
     }
-
-#endif /* !valgrind */
-
   } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
     const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */
     const uint8_t  *k8;
@@ -375,7 +339,7 @@ static uint32_t hashlittle( const void *key, size_t length )
   return c;
 }
 
-unsigned int tdb_jenkins_hash(TDB_DATA *key)
+_PUBLIC_ unsigned int tdb_jenkins_hash(TDB_DATA *key)
 {
        return hashlittle(key->dptr, key->dsize);
 }
index 058ca6c6b5194691fe872e3faf35fc0a92dffd97..11dfefd102b68cdb924680c5c0d94ebe8bf0d734 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
 
 /* 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 necessary
 */
-static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
+static int tdb_oob(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len,
+                  int probe)
 {
        struct stat st;
-       if (len <= tdb->map_size)
+       if (len + off < len) {
+               if (!probe) {
+                       /* Ensure ecode is set for log fn. */
+                       tdb->ecode = TDB_ERR_IO;
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob off %u len %u wrap\n",
+                                off, len));
+               }
+               return -1;
+       }
+
+       if (off + len <= tdb->map_size)
                return 0;
        if (tdb->flags & TDB_INTERNAL) {
                if (!probe) {
                        /* Ensure ecode is set for log fn. */
                        tdb->ecode = TDB_ERR_IO;
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n",
-                                (int)len, (int)tdb->map_size));
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond internal malloc size %u\n",
+                                (int)(off + len), (int)tdb->map_size));
                }
                return -1;
        }
@@ -53,28 +63,44 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
                return -1;
        }
 
-       if (st.st_size < (size_t)len) {
-               if (!probe) {
-                       /* Ensure ecode is set for log fn. */
-                       tdb->ecode = TDB_ERR_IO;
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n",
-                                (int)len, (int)st.st_size));
-               }
+       /* Beware >4G files! */
+       if ((tdb_off_t)st.st_size != st.st_size) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_oob len %llu too large!\n",
+                        (long long)st.st_size));
                return -1;
        }
 
-       /* Unmap, update size, remap */
+       /* Unmap, update size, remap.  We do this unconditionally, to handle
+        * the unusual case where the db is truncated.
+        *
+        * This can happen to a child using tdb_reopen_all(true) on a
+        * TDB_CLEAR_IF_FIRST tdb whose parent crashes: the next
+        * opener will truncate the database. */
        if (tdb_munmap(tdb) == -1) {
                tdb->ecode = TDB_ERR_IO;
                return -1;
        }
        tdb->map_size = st.st_size;
-       tdb_mmap(tdb);
+       if (tdb_mmap(tdb) != 0) {
+               return -1;
+       }
+
+       if (st.st_size < (size_t)off + len) {
+               if (!probe) {
+                       /* Ensure ecode is set for log fn. */
+                       tdb->ecode = TDB_ERR_IO;
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond eof at %u\n",
+                                (int)(off + len), (int)st.st_size));
+               }
+               return -1;
+       }
        return 0;
 }
 
 /* write a lump of data at a specified offset */
-static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
+static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
                     const void *buf, tdb_len_t len)
 {
        if (len == 0) {
@@ -86,19 +112,23 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
                return -1;
        }
 
-       if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
+       if (tdb->methods->tdb_oob(tdb, off, len, 0) != 0)
                return -1;
 
        if (tdb->map_ptr) {
                memcpy(off + (char *)tdb->map_ptr, buf, len);
        } else {
+#ifdef HAVE_INCOHERENT_MMAP
+               tdb->ecode = TDB_ERR_IO;
+               return -1;
+#else
                ssize_t written = pwrite(tdb->fd, buf, len, off);
                if ((written != (ssize_t)len) && (written != -1)) {
                        /* try once more */
                        tdb->ecode = TDB_ERR_IO;
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
-                                "%d of %d bytes at %d, trying once more\n",
-                                (int)written, len, off));
+                                "%zi of %u bytes at %u, trying once more\n",
+                                written, len, off));
                        written = pwrite(tdb->fd, (const char *)buf+written,
                                         len-written,
                                         off+written);
@@ -106,16 +136,17 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
                if (written == -1) {
                        /* Ensure ecode is set for log fn. */
                        tdb->ecode = TDB_ERR_IO;
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
-                                "len=%d (%s)\n", off, len, strerror(errno)));
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %u "
+                                "len=%u (%s)\n", off, len, strerror(errno)));
                        return -1;
                } else if (written != (ssize_t)len) {
                        tdb->ecode = TDB_ERR_IO;
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
-                                "write %d bytes at %d in two attempts\n",
+                                "write %u bytes at %u in two attempts\n",
                                 len, off));
                        return -1;
                }
+#endif
        }
        return 0;
 }
@@ -131,26 +162,31 @@ void *tdb_convert(void *buf, uint32_t size)
 
 
 /* read a lump of data at a specified offset, maybe convert */
-static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, 
+static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
                    tdb_len_t len, int cv)
 {
-       if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) {
+       if (tdb->methods->tdb_oob(tdb, off, len, 0) != 0) {
                return -1;
        }
 
        if (tdb->map_ptr) {
                memcpy(buf, off + (char *)tdb->map_ptr, len);
        } else {
+#ifdef HAVE_INCOHERENT_MMAP
+               tdb->ecode = TDB_ERR_IO;
+               return -1;
+#else
                ssize_t ret = pread(tdb->fd, buf, len, off);
                if (ret != (ssize_t)len) {
                        /* Ensure ecode is set for log fn. */
                        tdb->ecode = TDB_ERR_IO;
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d "
-                                "len=%d ret=%d (%s) map_size=%d\n",
-                                (int)off, (int)len, (int)ret, strerror(errno),
-                                (int)tdb->map_size));
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %u "
+                                "len=%u ret=%zi (%s) map_size=%u\n",
+                                off, len, ret, strerror(errno),
+                                tdb->map_size));
                        return -1;
                }
+#endif
        }
        if (cv) {
                tdb_convert(buf, len);
@@ -163,19 +199,19 @@ static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
 /*
   do an unlocked scan of the hash table heads to find the next non-zero head. The value
   will then be confirmed with the lock held
-*/             
+*/
 static void tdb_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
 {
        uint32_t h = *chain;
        if (tdb->map_ptr) {
-               for (;h < tdb->header.hash_size;h++) {
+               for (;h < tdb->hash_size;h++) {
                        if (0 != *(uint32_t *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
                                break;
                        }
                }
        } else {
                uint32_t off=0;
-               for (;h < tdb->header.hash_size;h++) {
+               for (;h < tdb->hash_size;h++) {
                        if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) {
                                break;
                        }
@@ -203,15 +239,25 @@ int tdb_munmap(struct tdb_context *tdb)
        return 0;
 }
 
-void tdb_mmap(struct tdb_context *tdb)
+/* If mmap isn't coherent, *everyone* must always mmap. */
+static bool should_mmap(const struct tdb_context *tdb)
+{
+#ifdef HAVE_INCOHERENT_MMAP
+       return true;
+#else
+       return !(tdb->flags & TDB_NOMMAP);
+#endif
+}
+
+int tdb_mmap(struct tdb_context *tdb)
 {
        if (tdb->flags & TDB_INTERNAL)
-               return;
+               return 0;
 
 #ifdef HAVE_MMAP
-       if (!(tdb->flags & TDB_NOMMAP)) {
-               tdb->map_ptr = mmap(NULL, tdb->map_size, 
-                                   PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
+       if (should_mmap(tdb)) {
+               tdb->map_ptr = mmap(NULL, tdb->map_size,
+                                   PROT_READ|(tdb->read_only? 0:PROT_WRITE),
                                    MAP_SHARED|MAP_FILE, tdb->fd, 0);
 
                /*
@@ -220,8 +266,12 @@ void tdb_mmap(struct tdb_context *tdb)
 
                if (tdb->map_ptr == MAP_FAILED) {
                        tdb->map_ptr = NULL;
-                       TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", 
+                       TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %u (%s)\n",
                                 tdb->map_size, strerror(errno)));
+#ifdef HAVE_INCOHERENT_MMAP
+                       tdb->ecode = TDB_ERR_IO;
+                       return -1;
+#endif
                }
        } else {
                tdb->map_ptr = NULL;
@@ -229,6 +279,7 @@ void tdb_mmap(struct tdb_context *tdb)
 #else
        tdb->map_ptr = NULL;
 #endif
+       return 0;
 }
 
 /* expand a file.  we prefer to use ftruncate, as that is what posix
@@ -236,26 +287,37 @@ void tdb_mmap(struct tdb_context *tdb)
 static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
 {
        char buf[8192];
+       tdb_off_t new_size;
 
        if (tdb->read_only || tdb->traverse_read) {
                tdb->ecode = TDB_ERR_RDONLY;
                return -1;
        }
 
-       if (ftruncate(tdb->fd, size+addition) == -1) {
+       if (!tdb_add_off_t(size, addition, &new_size)) {
+               tdb->ecode = TDB_ERR_OOM;
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
+                       "overflow detected current size[%u] addition[%u]!\n",
+                       (unsigned)size, (unsigned)addition));
+               errno = ENOSPC;
+               return -1;
+       }
+
+       if (ftruncate(tdb->fd, new_size) == -1) {
                char b = 0;
-               ssize_t written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
+               ssize_t written = pwrite(tdb->fd,  &b, 1, new_size - 1);
                if (written == 0) {
                        /* try once more, potentially revealing errno */
-                       written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
+                       written = pwrite(tdb->fd,  &b, 1, new_size - 1);
                }
                if (written == 0) {
                        /* again - give up, guessing errno */
                        errno = ENOSPC;
                }
                if (written != 1) {
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", 
-                                size+addition, strerror(errno)));
+                       tdb->ecode = TDB_ERR_OOM;
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %u failed (%s)\n",
+                                (unsigned)new_size, strerror(errno)));
                        return -1;
                }
        }
@@ -273,19 +335,23 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
                }
                if (written == 0) {
                        /* give up, trying to provide a useful errno */
+                       tdb->ecode = TDB_ERR_OOM;
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
                                "returned 0 twice: giving up!\n"));
                        errno = ENOSPC;
                        return -1;
-               } else if (written == -1) {
+               }
+               if (written == -1) {
+                       tdb->ecode = TDB_ERR_OOM;
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
-                                "%d bytes failed (%s)\n", (int)n,
+                                "%u bytes failed (%s)\n", (int)n,
                                 strerror(errno)));
                        return -1;
-               } else if (written != n) {
+               }
+               if (written != n) {
                        TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: wrote "
-                                "only %d of %d bytes - retrying\n", (int)written,
-                                (int)n));
+                                "only %zu of %zi bytes - retrying\n", written,
+                                n));
                }
                addition -= written;
                size += written;
@@ -294,12 +360,74 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
 }
 
 
+/* You need 'size', this tells you how much you should expand by. */
+tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size, int page_size)
+{
+       tdb_off_t new_size, top_size, increment;
+       tdb_off_t max_size = UINT32_MAX - map_size;
+
+       if (size > max_size) {
+               /*
+                * We can't round up anymore, just give back
+                * what we're asked for.
+                *
+                * The caller has to take care of the ENOSPC handling.
+                */
+               return 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) {
+               increment = size * 2;
+       } else {
+               increment = size * 100;
+       }
+       if (increment < size) {
+               goto overflow;
+       }
+
+       if (!tdb_add_off_t(map_size, increment, &top_size)) {
+               goto overflow;
+       }
+
+       /* 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;
+       }
+       if (new_size < map_size) {
+               goto overflow;
+       }
+
+       /* Round the database up to a multiple of the page size */
+       new_size = MAX(top_size, new_size);
+
+       if (new_size + page_size < new_size) {
+               /* There's a "+" in TDB_ALIGN that might overflow... */
+               goto overflow;
+       }
+
+       return TDB_ALIGN(new_size, page_size) - map_size;
+
+overflow:
+       /*
+        * Somewhere in between we went over 4GB. Make one big jump to
+        * exactly 4GB database size.
+        */
+       return max_size;
+}
+
 /* expand the database at least size bytes by expanding the underlying
    file and doing the mmap again if necessary */
 int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
 {
        struct tdb_record rec;
-       tdb_off_t offset, new_size;     
+       tdb_off_t offset;
+       tdb_off_t new_size;
 
        if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
@@ -307,56 +435,54 @@ int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
        }
 
        /* must know about any previous expansions by another process */
-       tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+       tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
 
-       /* always make room for at least 100 more records, and at
-           least 25% more space. Round the database up to a multiple
-           of the page size */
-       new_size = MAX(tdb->map_size + size*100, tdb->map_size * 1.25);
-       size = TDB_ALIGN(new_size, tdb->page_size) - tdb->map_size;
+       size = tdb_expand_adjust(tdb->map_size, size, tdb->page_size);
 
-       if (!(tdb->flags & TDB_INTERNAL))
-               tdb_munmap(tdb);
-
-       /*
-        * We must ensure the file is unmapped before doing this
-        * to ensure consistency with systems like OpenBSD where
-        * writes and mmaps are not consistent.
-        */
-
-       /* expand the file itself */
-       if (!(tdb->flags & TDB_INTERNAL)) {
-               if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0)
-                       goto fail;
+       if (!tdb_add_off_t(tdb->map_size, size, &new_size)) {
+               tdb->ecode = TDB_ERR_OOM;
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_expand "
+                       "overflow detected current map_size[%u] size[%u]!\n",
+                       (unsigned)tdb->map_size, (unsigned)size));
+               goto fail;
        }
 
-       tdb->map_size += size;
+       /* form a new freelist record */
+       offset = tdb->map_size;
+       memset(&rec,'\0',sizeof(rec));
+       rec.rec_len = size - sizeof(rec);
 
        if (tdb->flags & TDB_INTERNAL) {
-               char *new_map_ptr = (char *)realloc(tdb->map_ptr,
-                                                   tdb->map_size);
+               char *new_map_ptr;
+
+               new_map_ptr = (char *)realloc(tdb->map_ptr, new_size);
                if (!new_map_ptr) {
-                       tdb->map_size -= size;
+                       tdb->ecode = TDB_ERR_OOM;
                        goto fail;
                }
                tdb->map_ptr = new_map_ptr;
+               tdb->map_size = new_size;
        } else {
+               int ret;
+
                /*
-                * We must ensure the file is remapped before adding the space
-                * to ensure consistency with systems like OpenBSD where
-                * writes and mmaps are not consistent.
+                * expand the file itself
                 */
+               ret = tdb->methods->tdb_expand_file(tdb, tdb->map_size, size);
+               if (ret != 0) {
+                       goto fail;
+               }
 
-               /* We're ok if the mmap fails as we'll fallback to read/write */
-               tdb_mmap(tdb);
+               /* Explicitly remap: if we're in a transaction, this won't
+                * happen automatically! */
+               tdb_munmap(tdb);
+               tdb->map_size = new_size;
+               if (tdb_mmap(tdb) != 0) {
+                       goto fail;
+               }
        }
 
-       /* form a new freelist record */
-       memset(&rec,'\0',sizeof(rec));
-       rec.rec_len = size - sizeof(rec);
-
        /* link it into the free list */
-       offset = tdb->map_size - size;
        if (tdb_free(tdb, offset, &rec) == -1)
                goto fail;
 
@@ -390,7 +516,7 @@ unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len
        if (!(buf = (unsigned char *)malloc(len ? len : 1))) {
                /* Ensure ecode is set for log fn. */
                tdb->ecode = TDB_ERR_OOM;
-               TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n",
+               TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%u (%s)\n",
                           len, strerror(errno)));
                return NULL;
        }
@@ -419,7 +545,7 @@ int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
                 * Optimize by avoiding the malloc/memcpy/free, point the
                 * parser directly at the mmap area.
                 */
-               if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) {
+               if (tdb->methods->tdb_oob(tdb, offsetlen, 0) != 0) {
                        return -1;
                }
                data.dptr = offset + (unsigned char *)tdb->map_ptr;
@@ -443,10 +569,10 @@ int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *r
        if (TDB_BAD_MAGIC(rec)) {
                /* Ensure ecode is set for log fn. */
                tdb->ecode = TDB_ERR_CORRUPT;
-               TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
+               TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%u\n", rec->magic, offset));
                return -1;
        }
-       return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+       return tdb->methods->tdb_oob(tdb, rec->nextsizeof(*rec), 0);
 }
 
 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
index f0da8818d190247be700c69204ec4f0c9327d299..4dfefd563dcb0e53281a5d23302781912e3f75bb 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
@@ -27,7 +27,7 @@
 
 #include "tdb_private.h"
 
-void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
+_PUBLIC_ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
 {
        tdb->interrupt_sig_ptr = ptr;
 }
@@ -36,6 +36,7 @@ static int fcntl_lock(struct tdb_context *tdb,
                      int rw, off_t off, off_t len, bool waitflag)
 {
        struct flock fl;
+       int cmd;
 
        fl.l_type = rw;
        fl.l_whence = SEEK_SET;
@@ -43,10 +44,9 @@ static int fcntl_lock(struct tdb_context *tdb,
        fl.l_len = len;
        fl.l_pid = 0;
 
-       if (waitflag)
-               return fcntl(tdb->fd, F_SETLKW, &fl);
-       else
-               return fcntl(tdb->fd, F_SETLK, &fl);
+       cmd = waitflag ? F_SETLKW : F_SETLK;
+
+       return fcntl(tdb->fd, cmd, &fl);
 }
 
 static int fcntl_unlock(struct tdb_context *tdb, int rw, off_t off, off_t len)
@@ -126,10 +126,10 @@ static tdb_off_t lock_offset(int list)
 }
 
 /* a byte range locking function - return 0 on success
-   this functions locks/unlocks 1 byte at the specified offset.
+   this functions locks/unlocks "len" byte at the specified offset.
 
    On error, errno is also set so that errors are passed back properly
-   through tdb_open(). 
+   through tdb_open().
 
    note that a len of zero means lock to end of file
 */
@@ -169,8 +169,8 @@ int tdb_brlock(struct tdb_context *tdb,
                 * EAGAIN is an expected return from non-blocking
                 * locks. */
                if (!(flags & TDB_LOCK_PROBE) && errno != EAGAIN) {
-                       TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d flags=%d len=%d\n",
-                                tdb->fd, offset, rw_type, flags, (int)len));
+                       TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %u rw_type=%d flags=%d len=%zu\n",
+                                tdb->fd, offset, rw_type, flags, len));
                }
                return -1;
        }
@@ -191,21 +191,49 @@ int tdb_brunlock(struct tdb_context *tdb,
        } while (ret == -1 && errno == EINTR);
 
        if (ret == -1) {
-               TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brunlock failed (fd=%d) at offset %d rw_type=%d len=%d\n",
-                        tdb->fd, offset, rw_type, (int)len));
+               TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brunlock failed (fd=%d) at offset %u rw_type=%u len=%zu\n",
+                        tdb->fd, offset, rw_type, len));
        }
        return ret;
 }
 
 /*
-  upgrade a read lock to a write lock. This needs to be handled in a
-  special way as some OSes (such as solaris) have too conservative
-  deadlock detection and claim a deadlock when progress can be
-  made. For those OSes we may loop for a while.  
+ * Do a tdb_brlock in a loop. 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.
+ */
+
+static int tdb_brlock_retry(struct tdb_context *tdb,
+                           int rw_type, tdb_off_t offset, size_t len,
+                           enum tdb_lock_flags flags)
+{
+       int count = 1000;
+
+       while (count--) {
+               struct timeval tv;
+               int ret;
+
+               ret = tdb_brlock(tdb, rw_type, offset, len, flags);
+               if (ret == 0) {
+                       return 0;
+               }
+               if (errno != EDEADLK) {
+                       break;
+               }
+               /* sleep for as short a time as we can - more portable than usleep() */
+               tv.tv_sec = 0;
+               tv.tv_usec = 1;
+               select(0, NULL, NULL, NULL, &tv);
+       }
+       return -1;
+}
+
+/*
+  upgrade a read lock to a write lock.
 */
 int tdb_allrecord_upgrade(struct tdb_context *tdb)
 {
-       int count = 1000;
+       int ret;
 
        if (tdb->allrecord_lock.count != 1) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR,
@@ -220,21 +248,12 @@ int tdb_allrecord_upgrade(struct tdb_context *tdb)
                return -1;
        }
 
-       while (count--) {
-               struct timeval tv;
-               if (tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0,
-                              TDB_LOCK_WAIT|TDB_LOCK_PROBE) == 0) {
-                       tdb->allrecord_lock.ltype = F_WRLCK;
-                       tdb->allrecord_lock.off = 0;
-                       return 0;
-               }
-               if (errno != EDEADLK) {
-                       break;
-               }
-               /* sleep for as short a time as we can - more portable than usleep() */
-               tv.tv_sec = 0;
-               tv.tv_usec = 1;
-               select(0, NULL, NULL, NULL, &tv);
+       ret = tdb_brlock_retry(tdb, F_WRLCK, FREELIST_TOP, 0,
+                              TDB_LOCK_WAIT|TDB_LOCK_PROBE);
+       if (ret == 0) {
+               tdb->allrecord_lock.ltype = F_WRLCK;
+               tdb->allrecord_lock.off = 0;
+               return 0;
        }
        TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_allrecord_upgrade failed\n"));
        return -1;
@@ -259,7 +278,7 @@ int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype,
 {
        struct tdb_lock_type *new_lck;
 
-       if (offset >= lock_offset(tdb->header.hash_size)) {
+       if (offset >= lock_offset(tdb->hash_size)) {
                tdb->ecode = TDB_ERR_LOCK;
                TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid offset %u for ltype=%d\n",
                         offset, ltype));
@@ -334,34 +353,60 @@ static bool have_data_locks(const struct tdb_context *tdb)
        return false;
 }
 
+/*
+ * A allrecord lock allows us to avoid per chain locks. Check if the allrecord
+ * lock is strong enough.
+ */
+static int tdb_lock_covered_by_allrecord_lock(struct tdb_context *tdb,
+                                             int ltype)
+{
+       if (ltype == F_RDLCK) {
+               /*
+                * The allrecord_lock is equal (F_RDLCK) or stronger
+                * (F_WRLCK). Pass.
+                */
+               return 0;
+       }
+
+       if (tdb->allrecord_lock.ltype == F_RDLCK) {
+               /*
+                * We ask for ltype==F_WRLCK, but the allrecord_lock
+                * is too weak. We can't upgrade here, so fail.
+                */
+               tdb->ecode = TDB_ERR_LOCK;
+               return -1;
+       }
+
+       /*
+        * Asking for F_WRLCK, allrecord is F_WRLCK as well. Pass.
+        */
+       return 0;
+}
+
 static int tdb_lock_list(struct tdb_context *tdb, int list, int ltype,
                         enum tdb_lock_flags waitflag)
 {
        int ret;
        bool check = false;
 
-       /* a allrecord lock allows us to avoid per chain locks */
-       if (tdb->allrecord_lock.count &&
-           (ltype == tdb->allrecord_lock.ltype || ltype == F_RDLCK)) {
-               return 0;
+       if (tdb->allrecord_lock.count) {
+               return tdb_lock_covered_by_allrecord_lock(tdb, ltype);
        }
 
-       if (tdb->allrecord_lock.count) {
-               tdb->ecode = TDB_ERR_LOCK;
-               ret = -1;
-       } else {
-               /* Only check when we grab first data lock. */
-               check = !have_data_locks(tdb);
-               ret = tdb_nest_lock(tdb, lock_offset(list), ltype, waitflag);
+       /*
+        * Check for recoveries: Someone might have kill -9'ed a process
+        * during a commit.
+        */
+       check = !have_data_locks(tdb);
+       ret = tdb_nest_lock(tdb, lock_offset(list), ltype, waitflag);
 
-               if (ret == 0 && check && tdb_needs_recovery(tdb)) {
-                       tdb_nest_unlock(tdb, lock_offset(list), ltype, false);
+       if (ret == 0 && check && tdb_needs_recovery(tdb)) {
+               tdb_nest_unlock(tdb, lock_offset(list), ltype, false);
 
-                       if (tdb_lock_and_recover(tdb) == -1) {
-                               return -1;
-                       }
-                       return tdb_lock_list(tdb, list, ltype, waitflag);
+               if (tdb_lock_and_recover(tdb) == -1) {
+                       return -1;
                }
+               return tdb_lock_list(tdb, list, ltype, waitflag);
        }
        return ret;
 }
@@ -380,7 +425,7 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
 }
 
 /* lock a list in the database. list -1 is the alloc list. non-blocking lock */
-int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
+_PUBLIC_ int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
 {
        return tdb_lock_list(tdb, list, ltype, TDB_LOCK_NOWAIT);
 }
@@ -396,8 +441,8 @@ int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype,
                return 0;
 
        /* Sanity checks */
-       if (offset >= lock_offset(tdb->header.hash_size)) {
-               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: offset %u invalid (%d)\n", offset, tdb->header.hash_size));
+       if (offset >= lock_offset(tdb->hash_size)) {
+               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: offset %u invalid (%d)\n", offset, tdb->hash_size));
                return ret;
        }
 
@@ -441,21 +486,15 @@ int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype,
        }
 
        if (ret)
-               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n")); 
+               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
        return ret;
 }
 
-int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
+_PUBLIC_ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
 {
        /* a global lock allows us to avoid per chain locks */
-       if (tdb->allrecord_lock.count &&
-           (ltype == tdb->allrecord_lock.ltype || ltype == F_RDLCK)) {
-               return 0;
-       }
-
        if (tdb->allrecord_lock.count) {
-               tdb->ecode = TDB_ERR_LOCK;
-               return -1;
+               return tdb_lock_covered_by_allrecord_lock(tdb, ltype);
        }
 
        return tdb_nest_unlock(tdb, lock_offset(list), ltype, false);
@@ -562,21 +601,21 @@ int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
 
        /* We cover two kinds of locks:
         * 1) Normal chain locks.  Taken for almost all operations.
-        * 3) Individual records locks.  Taken after normal or free
+        * 2) Individual records locks.  Taken after normal or free
         *    chain locks.
         *
         * It is (1) which cause the starvation problem, so we're only
         * gradual for that. */
        if (tdb_chainlock_gradual(tdb, ltype, flags, FREELIST_TOP,
-                                 tdb->header.hash_size * 4) == -1) {
+                                 tdb->hash_size * 4) == -1) {
                return -1;
        }
 
        /* Grab individual record locks. */
-       if (tdb_brlock(tdb, ltype, lock_offset(tdb->header.hash_size), 0,
+       if (tdb_brlock(tdb, ltype, lock_offset(tdb->hash_size), 0,
                       flags) == -1) {
                tdb_brunlock(tdb, ltype, FREELIST_TOP,
-                            tdb->header.hash_size * 4);
+                            tdb->hash_size * 4);
                return -1;
        }
 
@@ -644,28 +683,28 @@ int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock)
 }
 
 /* lock entire database with write lock */
-int tdb_lockall(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall");
        return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
 }
 
 /* lock entire database with write lock - mark only */
-int tdb_lockall_mark(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_mark(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall_mark");
        return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY, false);
 }
 
 /* unlock entire database with write lock - unmark only */
-int tdb_lockall_unmark(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_unmark(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall_unmark");
        return tdb_allrecord_unlock(tdb, F_WRLCK, true);
 }
 
 /* lock entire database with write lock - nonblocking varient */
-int tdb_lockall_nonblock(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_nonblock(struct tdb_context *tdb)
 {
        int ret = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_NOWAIT, false);
        tdb_trace_ret(tdb, "tdb_lockall_nonblock", ret);
@@ -673,21 +712,21 @@ int tdb_lockall_nonblock(struct tdb_context *tdb)
 }
 
 /* unlock entire database with write lock */
-int tdb_unlockall(struct tdb_context *tdb)
+_PUBLIC_ int tdb_unlockall(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_unlockall");
        return tdb_allrecord_unlock(tdb, F_WRLCK, false);
 }
 
 /* lock entire database with read lock */
-int tdb_lockall_read(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_read(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall_read");
        return tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
 }
 
 /* lock entire database with read lock - nonblock varient */
-int tdb_lockall_read_nonblock(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_read_nonblock(struct tdb_context *tdb)
 {
        int ret = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_NOWAIT, false);
        tdb_trace_ret(tdb, "tdb_lockall_read_nonblock", ret);
@@ -695,7 +734,7 @@ int tdb_lockall_read_nonblock(struct tdb_context *tdb)
 }
 
 /* unlock entire database with read lock */
-int tdb_unlockall_read(struct tdb_context *tdb)
+_PUBLIC_ int tdb_unlockall_read(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_unlockall_read");
        return tdb_allrecord_unlock(tdb, F_RDLCK, false);
@@ -703,7 +742,7 @@ int tdb_unlockall_read(struct tdb_context *tdb)
 
 /* lock/unlock one hash chain. This is meant to be used to reduce
    contention - it cannot guarantee how many records will be locked */
-int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
        tdb_trace_1rec(tdb, "tdb_chainlock", key);
@@ -713,7 +752,7 @@ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
 /* lock/unlock one hash chain, non-blocking. This is meant to be used
    to reduce contention - it cannot guarantee how many records will be
    locked */
-int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret = tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
        tdb_trace_1rec_ret(tdb, "tdb_chainlock_nonblock", key, ret);
@@ -721,7 +760,7 @@ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
 }
 
 /* mark a chain as locked without actually locking it. Warning! use with great caution! */
-int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret = tdb_nest_lock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))),
                                F_WRLCK, TDB_LOCK_MARK_ONLY);
@@ -730,20 +769,20 @@ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
 }
 
 /* unmark a chain as locked without actually locking it. Warning! use with great caution! */
-int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
 {
        tdb_trace_1rec(tdb, "tdb_chainlock_unmark", key);
        return tdb_nest_unlock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))),
                               F_WRLCK, true);
 }
 
-int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
 {
        tdb_trace_1rec(tdb, "tdb_chainunlock", key);
        return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
 }
 
-int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret;
        ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
@@ -751,14 +790,12 @@ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
        return ret;
 }
 
-int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
 {
        tdb_trace_1rec(tdb, "tdb_chainunlock_read", key);
        return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
 }
 
-
-
 /* record lock stops delete underneath */
 int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
 {
@@ -842,7 +879,7 @@ void tdb_release_transaction_locks(struct tdb_context *tdb)
        unsigned int i, active = 0;
 
        if (tdb->allrecord_lock.count != 0) {
-               tdb_brunlock(tdb, tdb->allrecord_lock.ltype, FREELIST_TOP, 0);
+               tdb_allrecord_unlock(tdb, tdb->allrecord_lock.ltype, false);
                tdb->allrecord_lock.count = 0;
        }
 
@@ -862,22 +899,18 @@ void tdb_release_transaction_locks(struct tdb_context *tdb)
        }
 }
 
-int tdb_transaction_write_lock_mark(struct tdb_context *tdb)
-{
-       return tdb_transaction_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY);
-}
-
-int tdb_transaction_write_lock(struct tdb_context *tdb)
-{
-       return tdb_transaction_lock(tdb, F_WRLCK, 0);
-}
+/* Following functions are added specifically to support CTDB. */
 
-int tdb_transaction_write_unlock(struct tdb_context *tdb)
+/* Don't do actual fcntl locking, just mark tdb locked */
+int tdb_transaction_write_lock_mark(struct tdb_context *tdb);
+_PUBLIC_ int tdb_transaction_write_lock_mark(struct tdb_context *tdb)
 {
-       return tdb_transaction_unlock(tdb, F_WRLCK);
+       return tdb_transaction_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY);
 }
 
-int tdb_transaction_write_lock_unmark(struct tdb_context *tdb)
+/* Don't do actual fcntl unlocking, just mark tdb unlocked */
+int tdb_transaction_write_lock_unmark(struct tdb_context *tdb);
+_PUBLIC_ int tdb_transaction_write_lock_unmark(struct tdb_context *tdb)
 {
        return tdb_nest_unlock(tdb, TRANSACTION_LOCK, F_WRLCK, true);
 }
index 66539c3f6c5dd9fece0010a3822355ec07c9af34..05d7cae6beb64a629de5854a9478cb7f878e4ee5 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
@@ -51,7 +51,8 @@ void tdb_header_hash(struct tdb_context *tdb,
 }
 
 /* initialise a new database with a specified hash size */
-static int tdb_new_database(struct tdb_context *tdb, int hash_size)
+static int tdb_new_database(struct tdb_context *tdb, struct tdb_header *header,
+                           int hash_size)
 {
        struct tdb_header *newdb;
        size_t size;
@@ -78,7 +79,7 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size)
        if (tdb->flags & TDB_INTERNAL) {
                tdb->map_size = size;
                tdb->map_ptr = (char *)newdb;
-               memcpy(&tdb->header, newdb, sizeof(tdb->header));
+               memcpy(header, newdb, sizeof(*header));
                /* Convert the `ondisk' version if asked. */
                CONVERT(*newdb);
                return 0;
@@ -91,13 +92,14 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size)
 
        /* This creates an endian-converted header, as if read from disk */
        CONVERT(*newdb);
-       memcpy(&tdb->header, newdb, sizeof(tdb->header));
+       memcpy(header, newdb, sizeof(*header));
        /* Don't endian-convert the magic food! */
        memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
-       /* we still have "ret == -1" here */
-       if (tdb_write_all(tdb->fd, newdb, size))
-               ret = 0;
 
+       if (!tdb_write_all(tdb->fd, newdb, size))
+               goto fail;
+
+       ret = 0;
   fail:
        SAFE_FREE(newdb);
        return ret;
@@ -119,17 +121,17 @@ static int tdb_already_open(dev_t device,
        return 0;
 }
 
-/* open the database, creating it if necessary 
+/* open the database, creating it if necessary
 
    The open_flags and mode are passed straight to the open call on the
    database file. A flags value of O_WRONLY is invalid. The hash size
    is advisory, use zero for a default value.
 
-   Return is NULL on error, in which case errno is also set.  Don't 
+   Return is NULL on error, in which case errno is also set.  Don't
    try to call tdb_error or tdb_errname, just do strerror(errno).
 
    @param name may be NULL for internal databases. */
-struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
+_PUBLIC_ struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
                      int open_flags, mode_t mode)
 {
        return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
@@ -142,11 +144,12 @@ static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, con
 }
 
 static bool check_header_hash(struct tdb_context *tdb,
+                             struct tdb_header *header,
                              bool default_hash, uint32_t *m1, uint32_t *m2)
 {
        tdb_header_hash(tdb, m1, m2);
-       if (tdb->header.magic1_hash == *m1 &&
-           tdb->header.magic2_hash == *m2) {
+       if (header->magic1_hash == *m1 &&
+           header->magic2_hash == *m2) {
                return true;
        }
 
@@ -159,14 +162,15 @@ static bool check_header_hash(struct tdb_context *tdb,
                tdb->hash_fn = tdb_jenkins_hash;
        else
                tdb->hash_fn = tdb_old_hash;
-       return check_header_hash(tdb, false, m1, m2);
+       return check_header_hash(tdb, header, false, m1, m2);
 }
 
-struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+_PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                                int open_flags, mode_t mode,
                                const struct tdb_logging_context *log_ctx,
                                tdb_hash_func hash_fn)
 {
+       struct tdb_header header;
        struct tdb_context *tdb;
        struct stat st;
        int rev = 0, locked = 0;
@@ -176,6 +180,8 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
        const char *hash_alg;
        uint32_t magic1, magic2;
 
+       ZERO_STRUCT(header);
+
        if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
                /* Can't log this */
                errno = ENOMEM;
@@ -197,6 +203,32 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                tdb->log.log_private = NULL;
        }
 
+       if (name == NULL && (tdb_flags & TDB_INTERNAL)) {
+               name = "__TDB_INTERNAL__";
+       }
+
+       if (name == NULL) {
+               tdb->name = discard_const_p(char, "__NULL__");
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: called with name == NULL\n"));
+               tdb->name = NULL;
+               errno = EINVAL;
+               goto fail;
+       }
+
+       /* now make a copy of the name, as the caller memory might go away */
+       if (!(tdb->name = (char *)strdup(name))) {
+               /*
+                * set the name as the given string, so that tdb_name() will
+                * work in case of an error.
+                */
+               tdb->name = discard_const_p(char, name);
+               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't strdup(%s)\n",
+                        name));
+               tdb->name = NULL;
+               errno = ENOMEM;
+               goto fail;
+       }
+
        if (hash_fn) {
                tdb->hash_fn = hash_fn;
                hash_alg = "the user defined";
@@ -259,10 +291,11 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
        if (tdb->flags & TDB_INTERNAL) {
                tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
                tdb->flags &= ~TDB_CLEAR_IF_FIRST;
-               if (tdb_new_database(tdb, hash_size) != 0) {
+               if (tdb_new_database(tdb, &header, hash_size) != 0) {
                        TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: tdb_new_database failed!"));
                        goto fail;
                }
+               tdb->hash_size = hash_size;
                goto internal;
        }
 
@@ -287,32 +320,57 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
        if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
            (!tdb->read_only) &&
            (locked = (tdb_nest_lock(tdb, ACTIVE_LOCK, F_WRLCK, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE) == 0))) {
-               open_flags |= O_CREAT;
-               if (ftruncate(tdb->fd, 0) == -1) {
+               int ret;
+               ret = tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0,
+                                TDB_LOCK_WAIT);
+               if (ret == -1) {
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
-                                "failed to truncate %s: %s\n",
+                                "tdb_brlock failed for %s: %s\n",
                                 name, strerror(errno)));
-                       goto fail; /* errno set by ftruncate */
+                       goto fail;
+               }
+               ret = tdb_new_database(tdb, &header, hash_size);
+               if (ret == -1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                                "tdb_new_database failed for %s: %s\n",
+                                name, strerror(errno)));
+                       tdb_unlockall(tdb);
+                       goto fail;
+               }
+               ret = tdb_brunlock(tdb, F_WRLCK, FREELIST_TOP, 0);
+               if (ret == -1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                                "tdb_unlockall failed for %s: %s\n",
+                                name, strerror(errno)));
+                       goto fail;
+               }
+               ret = lseek(tdb->fd, 0, SEEK_SET);
+               if (ret == -1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                                "lseek failed for %s: %s\n",
+                                name, strerror(errno)));
+                       goto fail;
                }
        }
 
        errno = 0;
-       if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
-           || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0) {
-               if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+       if (read(tdb->fd, &header, sizeof(header)) != sizeof(header)
+           || strcmp(header.magic_food, TDB_MAGIC_FOOD) != 0) {
+               if (!(open_flags & O_CREAT) ||
+                   tdb_new_database(tdb, &header, hash_size) == -1) {
                        if (errno == 0) {
                                errno = EIO; /* ie bad format or something */
                        }
                        goto fail;
                }
                rev = (tdb->flags & TDB_CONVERT);
-       } else if (tdb->header.version != TDB_VERSION
-                  && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION)))) {
+       } else if (header.version != TDB_VERSION
+                  && !(rev = (header.version==TDB_BYTEREV(TDB_VERSION)))) {
                /* wrong version */
                errno = EIO;
                goto fail;
        }
-       vp = (unsigned char *)&tdb->header.version;
+       vp = (unsigned char *)&header.version;
        vertest = (((uint32_t)vp[0]) << 24) | (((uint32_t)vp[1]) << 16) |
                  (((uint32_t)vp[2]) << 8) | (uint32_t)vp[3];
        tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
@@ -320,31 +378,33 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                tdb->flags &= ~TDB_CONVERT;
        else {
                tdb->flags |= TDB_CONVERT;
-               tdb_convert(&tdb->header, sizeof(tdb->header));
+               tdb_convert(&header, sizeof(header));
        }
        if (fstat(tdb->fd, &st) == -1)
                goto fail;
 
-       if (tdb->header.rwlocks != 0 &&
-           tdb->header.rwlocks != TDB_HASH_RWLOCK_MAGIC) {
+       if (header.rwlocks != 0 &&
+           header.rwlocks != TDB_HASH_RWLOCK_MAGIC) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n"));
                goto fail;
        }
+       tdb->hash_size = header.hash_size;
 
-       if ((tdb->header.magic1_hash == 0) && (tdb->header.magic2_hash == 0)) {
+       if ((header.magic1_hash == 0) && (header.magic2_hash == 0)) {
                /* older TDB without magic hash references */
                tdb->hash_fn = tdb_old_hash;
-       } else if (!check_header_hash(tdb, !hash_fn, &magic1, &magic2)) {
+       } else if (!check_header_hash(tdb, &header, !hash_fn,
+                                     &magic1, &magic2)) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
                         "%s was not created with %s hash function we are using\n"
                         "magic1_hash[0x%08X %s 0x%08X] "
                         "magic2_hash[0x%08X %s 0x%08X]\n",
                         name, hash_alg,
-                        tdb->header.magic1_hash,
-                        (tdb->header.magic1_hash == magic1) ? "==" : "!=",
+                        header.magic1_hash,
+                        (header.magic1_hash == magic1) ? "==" : "!=",
                         magic1,
-                        tdb->header.magic2_hash,
-                        (tdb->header.magic2_hash == magic2) ? "==" : "!=",
+                        header.magic2_hash,
+                        (header.magic2_hash == magic2) ? "==" : "!=",
                         magic2));
                errno = EINVAL;
                goto fail;
@@ -359,12 +419,17 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                goto fail;
        }
 
-       if (!(tdb->name = (char *)strdup(name))) {
-               errno = ENOMEM;
+       /* Beware truncation! */
+       tdb->map_size = st.st_size;
+       if (tdb->map_size != st.st_size) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                        "len %llu too large!\n", (long long)st.st_size));
+               errno = EIO;
                goto fail;
        }
 
-       tdb->map_size = st.st_size;
        tdb->device = st.st_dev;
        tdb->inode = st.st_ino;
        tdb_mmap(tdb);
@@ -436,11 +501,11 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                else
                        tdb_munmap(tdb);
        }
-       SAFE_FREE(tdb->name);
        if (tdb->fd != -1)
                if (close(tdb->fd) != 0)
                        TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
        SAFE_FREE(tdb->lockrecs);
+       SAFE_FREE(tdb->name);
        SAFE_FREE(tdb);
        errno = save_errno;
        return NULL;
@@ -451,7 +516,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
  * Set the maximum number of dead records per hash chain
  */
 
-void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
+_PUBLIC_ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
 {
        tdb->max_dead_records = max_dead;
 }
@@ -461,7 +526,7 @@ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
  *
  * @returns -1 for error; 0 for success.
  **/
-int tdb_close(struct tdb_context *tdb)
+_PUBLIC_ int tdb_close(struct tdb_context *tdb)
 {
        struct tdb_context **i;
        int ret = 0;
@@ -502,13 +567,13 @@ int tdb_close(struct tdb_context *tdb)
 }
 
 /* register a loging function */
-void tdb_set_logging_function(struct tdb_context *tdb,
-                              const struct tdb_logging_context *log_ctx)
+_PUBLIC_ void tdb_set_logging_function(struct tdb_context *tdb,
+                                       const struct tdb_logging_context *log_ctx)
 {
         tdb->log = *log_ctx;
 }
 
-void *tdb_get_logging_private(struct tdb_context *tdb)
+_PUBLIC_ void *tdb_get_logging_private(struct tdb_context *tdb)
 {
        return tdb->log.log_private;
 }
@@ -556,7 +621,9 @@ static int tdb_reopen_internal(struct tdb_context *tdb, bool active_lock)
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n"));
                goto fail;
        }
-       tdb_mmap(tdb);
+       if (tdb_mmap(tdb) != 0) {
+               goto fail;
+       }
 #endif /* fake pread or pwrite */
 
        /* We may still think we hold the active lock. */
@@ -577,13 +644,13 @@ fail:
 
 /* reopen a tdb - this can be used after a fork to ensure that we have an independent
    seek pointer from our parent and to re-establish locks */
-int tdb_reopen(struct tdb_context *tdb)
+_PUBLIC_ int tdb_reopen(struct tdb_context *tdb)
 {
        return tdb_reopen_internal(tdb, tdb->flags & TDB_CLEAR_IF_FIRST);
 }
 
 /* reopen all tdb's */
-int tdb_reopen_all(int parent_longlived)
+_PUBLIC_ int tdb_reopen_all(int parent_longlived)
 {
        struct tdb_context *tdb;
 
diff --git a/lib/tdb/common/rescue.c b/lib/tdb/common/rescue.c
new file mode 100644 (file)
index 0000000..17e7ed8
--- /dev/null
@@ -0,0 +1,349 @@
+ /*
+   Unix SMB/CIFS implementation.
+
+   trivial database library, rescue attempt code.
+
+   Copyright (C) Rusty Russell            2012
+
+     ** NOTE! The following LGPL license applies to the tdb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include "tdb_private.h"
+#include <assert.h>
+
+
+struct found {
+       tdb_off_t head; /* 0 -> invalid. */
+       struct tdb_record rec;
+       TDB_DATA key;
+       bool in_hash;
+       bool in_free;
+};
+
+struct found_table {
+       /* As an ordered array (by head offset). */
+       struct found *arr;
+       unsigned int num, max;
+};
+
+static bool looks_like_valid_record(struct tdb_context *tdb,
+                                   tdb_off_t off,
+                                   const struct tdb_record *rec,
+                                   TDB_DATA *key)
+{
+       unsigned int hval;
+
+       if (rec->magic != TDB_MAGIC)
+               return false;
+
+       if (rec->key_len + rec->data_len > rec->rec_len)
+               return false;
+
+       if (rec->rec_len % TDB_ALIGNMENT)
+               return false;
+
+       /* Next pointer must make some sense. */
+       if (rec->next > 0 && rec->next < TDB_DATA_START(tdb->hash_size))
+               return false;
+
+       if (tdb->methods->tdb_oob(tdb, rec->next, sizeof(*rec), 1))
+               return false;
+
+       key->dsize = rec->key_len;
+       key->dptr = tdb_alloc_read(tdb, off + sizeof(*rec), key->dsize);
+       if (!key->dptr)
+               return false;
+
+       hval = tdb->hash_fn(key);
+       if (hval != rec->full_hash) {
+               free(key->dptr);
+               return false;
+       }
+
+       /* Caller frees up key->dptr */
+       return true;
+}
+
+static bool add_to_table(struct found_table *found,
+                        tdb_off_t off,
+                        struct tdb_record *rec,
+                        TDB_DATA key)
+{
+       if (found->num + 1 > found->max) {
+               struct found *new;
+               found->max = (found->max ? found->max * 2 : 128);
+               new = realloc(found->arr, found->max * sizeof(found->arr[0]));
+               if (!new)
+                       return false;
+               found->arr = new;
+       }
+
+       found->arr[found->num].head = off;
+       found->arr[found->num].rec = *rec;
+       found->arr[found->num].key = key;
+       found->arr[found->num].in_hash = false;
+       found->arr[found->num].in_free = false;
+
+       found->num++;
+       return true;
+}
+
+static bool walk_record(struct tdb_context *tdb,
+                       const struct found *f,
+                       void (*walk)(TDB_DATA, TDB_DATA, void *private_data),
+                       void *private_data)
+{
+       TDB_DATA data;
+
+       data.dsize = f->rec.data_len;
+       data.dptr = tdb_alloc_read(tdb,
+                                  f->head + sizeof(f->rec) + f->rec.key_len,
+                                  data.dsize);
+       if (!data.dptr) {
+               if (tdb->ecode == TDB_ERR_OOM)
+                       return false;
+               /* I/O errors are expected. */
+               return true;
+       }
+
+       walk(f->key, data, private_data);
+       free(data.dptr);
+       return true;
+}
+
+/* First entry which has offset >= this one. */
+static unsigned int find_entry(struct found_table *found, tdb_off_t off)
+{
+       unsigned int start = 0, end = found->num;
+
+       while (start < end) {
+               /* We can't overflow here. */
+               unsigned int mid = (start + end) / 2;
+
+               if (off < found->arr[mid].head) {
+                       end = mid;
+               } else if (off > found->arr[mid].head) {
+                       start = mid + 1;
+               } else {
+                       return mid;
+               }
+       }
+
+       assert(start == end);
+       return end;
+}
+
+static void found_in_hashchain(struct found_table *found, tdb_off_t head)
+{
+       unsigned int match;
+
+       match = find_entry(found, head);
+       if (match < found->num && found->arr[match].head == head) {
+               found->arr[match].in_hash = true;
+       }
+}
+
+static void mark_free_area(struct found_table *found, tdb_off_t head,
+                          tdb_len_t len)
+{
+       unsigned int match;
+
+       match = find_entry(found, head);
+       /* Mark everything within this free entry. */
+       while (match < found->num) {
+               if (found->arr[match].head >= head + len) {
+                       break;
+               }
+               found->arr[match].in_free = true;
+               match++;
+       }
+}
+
+static int cmp_key(const void *a, const void *b)
+{
+       const struct found *fa = a, *fb = b;
+
+       if (fa->key.dsize < fb->key.dsize) {
+               return -1;
+       } else if (fa->key.dsize > fb->key.dsize) {
+               return 1;
+       }
+       return memcmp(fa->key.dptr, fb->key.dptr, fa->key.dsize);
+}
+
+static bool key_eq(TDB_DATA a, TDB_DATA b)
+{
+       return a.dsize == b.dsize
+               && memcmp(a.dptr, b.dptr, a.dsize) == 0;
+}
+
+static void free_table(struct found_table *found)
+{
+       unsigned int i;
+
+       for (i = 0; i < found->num; i++) {
+               free(found->arr[i].key.dptr);
+       }
+       free(found->arr);
+}
+
+static void logging_suppressed(struct tdb_context *tdb,
+                              enum tdb_debug_level level, const char *fmt, ...)
+{
+}
+
+_PUBLIC_ int tdb_rescue(struct tdb_context *tdb,
+                       void (*walk)(TDB_DATA, TDB_DATA, void *private_data),
+                       void *private_data)
+{
+       struct found_table found = { NULL, 0, 0 };
+       tdb_off_t h, off, i;
+       tdb_log_func oldlog = tdb->log.log_fn;
+       struct tdb_record rec;
+       TDB_DATA key;
+       bool locked;
+
+       /* Read-only databases use no locking at all: it's best-effort.
+        * We may have a write lock already, so skip that case too. */
+       if (tdb->read_only || tdb->allrecord_lock.count != 0) {
+               locked = false;
+       } else {
+               if (tdb_lockall_read(tdb) == -1)
+                       return -1;
+               locked = true;
+       }
+
+       /* Make sure we know true size of the underlying file. */
+       tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
+
+       /* Suppress logging, since we anticipate errors. */
+       tdb->log.log_fn = logging_suppressed;
+
+       /* Now walk entire db looking for records. */
+       for (off = TDB_DATA_START(tdb->hash_size);
+            off < tdb->map_size;
+            off += TDB_ALIGNMENT) {
+               if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
+                                          DOCONV()) == -1)
+                       continue;
+
+               if (looks_like_valid_record(tdb, off, &rec, &key)) {
+                       if (!add_to_table(&found, off, &rec, key)) {
+                               goto oom;
+                       }
+               }
+       }
+
+       /* Walk hash chains to positive vet. */
+       for (h = 0; h < 1+tdb->hash_size; h++) {
+               bool slow_chase = false;
+               tdb_off_t slow_off = FREELIST_TOP + h*sizeof(tdb_off_t);
+
+               if (tdb_ofs_read(tdb, FREELIST_TOP + h*sizeof(tdb_off_t),
+                                &off) == -1)
+                       continue;
+
+               while (off && off != slow_off) {
+                       if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
+                                                  DOCONV()) != 0) {
+                               break;
+                       }
+
+                       /* 0 is the free list, rest are hash chains. */
+                       if (h == 0) {
+                               /* Don't mark garbage as free. */
+                               if (rec.magic != TDB_FREE_MAGIC) {
+                                       break;
+                               }
+                               mark_free_area(&found, off,
+                                              sizeof(rec) + rec.rec_len);
+                       } else {
+                               found_in_hashchain(&found, off);
+                       }
+
+                       off = rec.next;
+
+                       /* Loop detection using second pointer at half-speed */
+                       if (slow_chase) {
+                               /* First entry happens to be next ptr */
+                               tdb_ofs_read(tdb, slow_off, &slow_off);
+                       }
+                       slow_chase = !slow_chase;
+               }
+       }
+
+       /* Recovery area: must be marked as free, since it often has old
+        * records in there! */
+       if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off) == 0 && off != 0) {
+               if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
+                                          DOCONV()) == 0) {
+                       mark_free_area(&found, off, sizeof(rec) + rec.rec_len);
+               }
+       }
+
+       /* Now sort by key! */
+       qsort(found.arr, found.num, sizeof(found.arr[0]), cmp_key);
+
+       for (i = 0; i < found.num; ) {
+               unsigned int num, num_in_hash = 0;
+
+               /* How many are identical? */
+               for (num = 0; num < found.num - i; num++) {
+                       if (!key_eq(found.arr[i].key, found.arr[i+num].key)) {
+                               break;
+                       }
+                       if (found.arr[i+num].in_hash) {
+                               if (!walk_record(tdb, &found.arr[i+num],
+                                                walk, private_data))
+                                       goto oom;
+                               num_in_hash++;
+                       }
+               }
+               assert(num);
+
+               /* If none were in the hash, we print any not in free list. */
+               if (num_in_hash == 0) {
+                       unsigned int j;
+
+                       for (j = i; j < i + num; j++) {
+                               if (!found.arr[j].in_free) {
+                                       if (!walk_record(tdb, &found.arr[j],
+                                                        walk, private_data))
+                                               goto oom;
+                               }
+                       }
+               }
+
+               i += num;
+       }
+
+       tdb->log.log_fn = oldlog;
+       if (locked) {
+               tdb_unlockall_read(tdb);
+       }
+       return 0;
+
+oom:
+       tdb->log.log_fn = oldlog;
+       tdb->ecode = TDB_ERR_OOM;
+       TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_rescue: failed allocating\n"));
+       free_table(&found);
+       if (locked) {
+               tdb_unlockall_read(tdb);
+       }
+       return -1;
+}
diff --git a/lib/tdb/common/summary.c b/lib/tdb/common/summary.c
new file mode 100644 (file)
index 0000000..3c6f755
--- /dev/null
@@ -0,0 +1,203 @@
+ /*
+   Trivial Database: human-readable summary code
+   Copyright (C) Rusty Russell 2010
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include "tdb_private.h"
+
+#define SUMMARY_FORMAT \
+       "Size of file/data: %u/%zu\n" \
+       "Number of records: %zu\n" \
+       "Incompatible hash: %s\n" \
+       "Smallest/average/largest keys: %zu/%zu/%zu\n" \
+       "Smallest/average/largest data: %zu/%zu/%zu\n" \
+       "Smallest/average/largest padding: %zu/%zu/%zu\n" \
+       "Number of dead records: %zu\n" \
+       "Smallest/average/largest dead records: %zu/%zu/%zu\n" \
+       "Number of free records: %zu\n" \
+       "Smallest/average/largest free records: %zu/%zu/%zu\n" \
+       "Number of hash chains: %zu\n" \
+       "Smallest/average/largest hash chains: %zu/%zu/%zu\n" \
+       "Number of uncoalesced records: %zu\n" \
+       "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n" \
+       "Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
+
+/* We don't use tally module, to keep upstream happy. */
+struct tally {
+       size_t min, max, total;
+       size_t num;
+};
+
+static void tally_init(struct tally *tally)
+{
+       tally->total = 0;
+       tally->num = 0;
+       tally->min = tally->max = 0;
+}
+
+static void tally_add(struct tally *tally, size_t len)
+{
+       if (tally->num == 0)
+               tally->max = tally->min = len;
+       else if (len > tally->max)
+               tally->max = len;
+       else if (len < tally->min)
+               tally->min = len;
+       tally->num++;
+       tally->total += len;
+}
+
+static size_t tally_mean(const struct tally *tally)
+{
+       if (!tally->num)
+               return 0;
+       return tally->total / tally->num;
+}
+
+static size_t get_hash_length(struct tdb_context *tdb, unsigned int i)
+{
+       tdb_off_t rec_ptr;
+       size_t count = 0;
+
+       if (tdb_ofs_read(tdb, TDB_HASH_TOP(i), &rec_ptr) == -1)
+               return 0;
+
+       /* keep looking until we find the right record */
+       while (rec_ptr) {
+               struct tdb_record r;
+               ++count;
+               if (tdb_rec_read(tdb, rec_ptr, &r) == -1)
+                       return 0;
+               rec_ptr = r.next;
+       }
+       return count;
+}
+
+_PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
+{
+       tdb_off_t off, rec_off;
+       struct tally freet, keys, data, dead, extra, hash, uncoal;
+       struct tdb_record rec;
+       char *ret = NULL;
+       bool locked;
+       size_t len, unc = 0;
+       struct tdb_record recovery;
+
+       /* Read-only databases use no locking at all: it's best-effort.
+        * We may have a write lock already, so skip that case too. */
+       if (tdb->read_only || tdb->allrecord_lock.count != 0) {
+               locked = false;
+       } else {
+               if (tdb_lockall_read(tdb) == -1)
+                       return NULL;
+               locked = true;
+       }
+
+       if (tdb_recovery_area(tdb, tdb->methods, &rec_off, &recovery) != 0) {
+               goto unlock;
+       }
+
+       tally_init(&freet);
+       tally_init(&keys);
+       tally_init(&data);
+       tally_init(&dead);
+       tally_init(&extra);
+       tally_init(&hash);
+       tally_init(&uncoal);
+
+       for (off = TDB_DATA_START(tdb->hash_size);
+            off < tdb->map_size - 1;
+            off += sizeof(rec) + rec.rec_len) {
+               if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
+                                          DOCONV()) == -1)
+                       goto unlock;
+               switch (rec.magic) {
+               case TDB_MAGIC:
+                       tally_add(&keys, rec.key_len);
+                       tally_add(&data, rec.data_len);
+                       tally_add(&extra, rec.rec_len - (rec.key_len
+                                                        + rec.data_len));
+                       if (unc > 1)
+                               tally_add(&uncoal, unc - 1);
+                       unc = 0;
+                       break;
+               case TDB_FREE_MAGIC:
+                       tally_add(&freet, rec.rec_len);
+                       unc++;
+                       break;
+               /* If we crash after ftruncate, we can get zeroes or fill. */
+               case TDB_RECOVERY_INVALID_MAGIC:
+               case 0x42424242:
+                       unc++;
+                       /* If it's a valid recovery, we can trust rec_len. */
+                       if (off != rec_off) {
+                               rec.rec_len = tdb_dead_space(tdb, off)
+                                       - sizeof(rec);
+                       }
+                       /* Fall through */
+               case TDB_DEAD_MAGIC:
+                       tally_add(&dead, rec.rec_len);
+                       break;
+               default:
+                       TDB_LOG((tdb, TDB_DEBUG_ERROR,
+                                "Unexpected record magic 0x%x at offset %u\n",
+                                rec.magic, off));
+                       goto unlock;
+               }
+       }
+       if (unc > 1)
+               tally_add(&uncoal, unc - 1);
+
+       for (off = 0; off < tdb->hash_size; off++)
+               tally_add(&hash, get_hash_length(tdb, off));
+
+       /* 20 is max length of a %zu. */
+       len = strlen(SUMMARY_FORMAT) + 35*20 + 1;
+       ret = (char *)malloc(len);
+       if (!ret)
+               goto unlock;
+
+       snprintf(ret, len, SUMMARY_FORMAT,
+                tdb->map_size, keys.total+data.total,
+                keys.num,
+                (tdb->hash_fn == tdb_jenkins_hash)?"yes":"no",
+                keys.min, tally_mean(&keys), keys.max,
+                data.min, tally_mean(&data), data.max,
+                extra.min, tally_mean(&extra), extra.max,
+                dead.num,
+                dead.min, tally_mean(&dead), dead.max,
+                freet.num,
+                freet.min, tally_mean(&freet), freet.max,
+                hash.num,
+                hash.min, tally_mean(&hash), hash.max,
+                uncoal.total,
+                uncoal.min, tally_mean(&uncoal), uncoal.max,
+                keys.total * 100.0 / tdb->map_size,
+                data.total * 100.0 / tdb->map_size,
+                extra.total * 100.0 / tdb->map_size,
+                freet.total * 100.0 / tdb->map_size,
+                dead.total * 100.0 / tdb->map_size,
+                (keys.num + freet.num + dead.num)
+                * (sizeof(struct tdb_record) + sizeof(uint32_t))
+                * 100.0 / tdb->map_size,
+                tdb->hash_size * sizeof(tdb_off_t)
+                * 100.0 / tdb->map_size);
+
+unlock:
+       if (locked) {
+               tdb_unlockall_read(tdb);
+       }
+       return ret;
+}
index 4d8c5fc59c9952056fa177c75ce93922276b45d8..6256a05d04ebf53f7cd9913abaf713802fe95530 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
 
 #include "tdb_private.h"
 
-TDB_DATA tdb_null;
+_PUBLIC_ TDB_DATA tdb_null;
 
 /*
   non-blocking increment of the tdb sequence number if the tdb has been opened using
   the TDB_SEQNUM flag
 */
-void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
+_PUBLIC_ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
 {
        tdb_off_t seqnum=0;
 
@@ -124,6 +124,19 @@ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t has
 
 static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
 
+static int tdb_update_hash_cmp(TDB_DATA key, TDB_DATA data, void *private_data)
+{
+       TDB_DATA *dbuf = (TDB_DATA *)private_data;
+
+       if (dbuf->dsize != data.dsize) {
+               return -1;
+       }
+       if (memcmp(dbuf->dptr, data.dptr, data.dsize) != 0) {
+               return -1;
+       }
+       return 0;
+}
+
 /* update an entry in place - this only works if the new data size
    is <= the old data size and the key exists.
    on failure return -1.
@@ -139,20 +152,11 @@ static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
 
        /* it could be an exact duplicate of what is there - this is
         * surprisingly common (eg. with a ldb re-index). */
-       if (rec.key_len == key.dsize && 
+       if (rec.key_len == key.dsize &&
            rec.data_len == dbuf.dsize &&
-           rec.full_hash == hash) {
-               TDB_DATA data = _tdb_fetch(tdb, key);
-               if (data.dsize == dbuf.dsize &&
-                   memcmp(data.dptr, dbuf.dptr, data.dsize) == 0) {
-                       if (data.dptr) {
-                               free(data.dptr);
-                       }
-                       return 0;
-               }
-               if (data.dptr) {
-                       free(data.dptr);
-               }
+           rec.full_hash == hash &&
+           tdb_parse_record(tdb, key, tdb_update_hash_cmp, &dbuf) == 0) {
+               return 0;
        }
 
        /* must be long enough key, data and tailer */
@@ -199,7 +203,7 @@ static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
        return ret;
 }
 
-TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
 {
        TDB_DATA ret = _tdb_fetch(tdb, key);
 
@@ -225,7 +229,7 @@ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
  * Return -1 if the record was not found.
  */
 
-int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
+_PUBLIC_ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
                     int (*parser)(TDB_DATA key, TDB_DATA data,
                                   void *private_data),
                     void *private_data)
@@ -254,7 +258,7 @@ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
        return ret;
 }
 
-/* check if an entry in the database exists 
+/* check if an entry in the database exists
 
    note that 1 is returned if the key is found and 0 is returned if not found
    this doesn't match the conventions in the rest of this module, but is
@@ -270,7 +274,7 @@ static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
        return 1;
 }
 
-int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
 {
        uint32_t hash = tdb->hash_fn(&key);
        int ret;
@@ -429,7 +433,7 @@ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
        return ret;
 }
 
-int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
 {
        uint32_t hash = tdb->hash_fn(&key);
        int ret;
@@ -473,7 +477,6 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
 {
        struct tdb_record rec;
        tdb_off_t rec_ptr;
-       char *p = NULL;
        int ret = -1;
 
        /* check for it existing, on insert. */
@@ -503,18 +506,6 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
        if (flag != TDB_INSERT)
                tdb_delete_hash(tdb, key, hash);
 
-       /* Copy key+value *before* allocating free space in case malloc
-          fails and we are left with a dead spot in the tdb. */
-
-       if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
-               tdb->ecode = TDB_ERR_OOM;
-               goto fail;
-       }
-
-       memcpy(p, key.dptr, key.dsize);
-       if (dbuf.dsize)
-               memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
-
        if (tdb->max_dead_records != 0) {
                /*
                 * Allow for some dead records per hash chain, look if we can
@@ -534,7 +525,10 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
                        if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
                            || tdb->methods->tdb_write(
                                    tdb, rec_ptr + sizeof(rec),
-                                   p, key.dsize + dbuf.dsize) == -1) {
+                                   key.dptr, key.dsize) == -1
+                           || tdb->methods->tdb_write(
+                                   tdb, rec_ptr + sizeof(rec) + key.dsize,
+                                   dbuf.dptr, dbuf.dsize) == -1) {
                                goto fail;
                        }
                        goto done;
@@ -577,7 +571,10 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
 
        /* write out and point the top of the hash chain at it */
        if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
-           || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+           || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec),
+                                      key.dptr, key.dsize) == -1
+           || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec)+key.dsize,
+                                      dbuf.dptr, dbuf.dsize) == -1
            || tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
                /* Need to tdb_unallocate() here */
                goto fail;
@@ -589,8 +586,6 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
        if (ret == 0) {
                tdb_increment_seqnum(tdb);
        }
-
-       SAFE_FREE(p); 
        return ret;
 }
 
@@ -599,7 +594,7 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
 
    return 0 on success, -1 on failure
 */
-int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+_PUBLIC_ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
 {
        uint32_t hash;
        int ret;
@@ -622,7 +617,7 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
 }
 
 /* Append to an entry. Create if not exist. */
-int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+_PUBLIC_ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
 {
        uint32_t hash;
        TDB_DATA dbuf;
@@ -673,7 +668,7 @@ failed:
   return the name of the current tdb file
   useful for external logging functions
 */
-const char *tdb_name(struct tdb_context *tdb)
+_PUBLIC_ const char *tdb_name(struct tdb_context *tdb)
 {
        return tdb->name;
 }
@@ -683,7 +678,7 @@ const char *tdb_name(struct tdb_context *tdb)
   useful for external routines that want to check the device/inode
   of the fd
 */
-int tdb_fd(struct tdb_context *tdb)
+_PUBLIC_ int tdb_fd(struct tdb_context *tdb)
 {
        return tdb->fd;
 }
@@ -692,7 +687,7 @@ int tdb_fd(struct tdb_context *tdb)
   return the current logging function
   useful for external tdb routines that wish to log tdb errors
 */
-tdb_log_func tdb_log_fn(struct tdb_context *tdb)
+_PUBLIC_ tdb_log_func tdb_log_fn(struct tdb_context *tdb)
 {
        return tdb->log.log_fn;
 }
@@ -708,7 +703,7 @@ tdb_log_func tdb_log_fn(struct tdb_context *tdb)
   The aim of this sequence number is to allow for a very lightweight
   test of a possible tdb change.
 */
-int tdb_get_seqnum(struct tdb_context *tdb)
+_PUBLIC_ int tdb_get_seqnum(struct tdb_context *tdb)
 {
        tdb_off_t seqnum=0;
 
@@ -716,22 +711,22 @@ int tdb_get_seqnum(struct tdb_context *tdb)
        return seqnum;
 }
 
-int tdb_hash_size(struct tdb_context *tdb)
+_PUBLIC_ int tdb_hash_size(struct tdb_context *tdb)
 {
-       return tdb->header.hash_size;
+       return tdb->hash_size;
 }
 
-size_t tdb_map_size(struct tdb_context *tdb)
+_PUBLIC_ size_t tdb_map_size(struct tdb_context *tdb)
 {
        return tdb->map_size;
 }
 
-int tdb_get_flags(struct tdb_context *tdb)
+_PUBLIC_ int tdb_get_flags(struct tdb_context *tdb)
 {
        return tdb->flags;
 }
 
-void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
+_PUBLIC_ void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
 {
        if ((flags & TDB_ALLOW_NESTING) &&
            (flags & TDB_DISALLOW_NESTING)) {
@@ -751,7 +746,7 @@ void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
        tdb->flags |= flags;
 }
 
-void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
+_PUBLIC_ void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
 {
        if ((flags & TDB_ALLOW_NESTING) &&
            (flags & TDB_DISALLOW_NESTING)) {
@@ -775,14 +770,14 @@ void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
 /*
   enable sequence number handling on an open tdb
 */
-void tdb_enable_seqnum(struct tdb_context *tdb)
+_PUBLIC_ void tdb_enable_seqnum(struct tdb_context *tdb)
 {
        tdb->flags |= TDB_SEQNUM;
 }
 
 
 /*
-  add a region of the file to the freelist. Length is the size of the region in bytes, 
+  add a region of the file to the freelist. Length is the size of the region in bytes,
   which includes the free list header that needs to be added
  */
 static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t length)
@@ -794,7 +789,7 @@ static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t le
        }
        if (length + offset > tdb->map_size) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: adding region beyond end of file\n"));
-               return -1;              
+               return -1;
        }
        memset(&rec,'\0',sizeof(rec));
        rec.rec_len = length - sizeof(rec);
@@ -812,7 +807,7 @@ static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t le
 
   This code carefully steps around the recovery area, leaving it alone
  */
-int tdb_wipe_all(struct tdb_context *tdb)
+_PUBLIC_ int tdb_wipe_all(struct tdb_context *tdb)
 {
        int i;
        tdb_off_t offset = 0;
@@ -840,12 +835,12 @@ int tdb_wipe_all(struct tdb_context *tdb)
                if (tdb->methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery record\n"));
                        return -1;
-               }       
+               }
                recovery_size = rec.rec_len + sizeof(rec);
        }
 
        /* wipe the hashes */
-       for (i=0;i<tdb->header.hash_size;i++) {
+       for (i=0;i<tdb->hash_size;i++) {
                if (tdb_ofs_write(tdb, TDB_HASH_TOP(i), &offset) == -1) {
                        TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write hash %d\n", i));
                        goto failed;
@@ -858,25 +853,25 @@ int tdb_wipe_all(struct tdb_context *tdb)
                goto failed;
        }
 
-       /* add all the rest of the file to the freelist, possibly leaving a gap 
+       /* add all the rest of the file to the freelist, possibly leaving a gap
           for the recovery area */
        if (recovery_size == 0) {
                /* the simple case - the whole file can be used as a freelist */
-               data_len = (tdb->map_size - TDB_DATA_START(tdb->header.hash_size));
-               if (tdb_free_region(tdb, TDB_DATA_START(tdb->header.hash_size), data_len) != 0) {
+               data_len = (tdb->map_size - TDB_DATA_START(tdb->hash_size));
+               if (tdb_free_region(tdb, TDB_DATA_START(tdb->hash_size), data_len) != 0) {
                        goto failed;
                }
        } else {
                /* we need to add two freelist entries - one on either
-                  side of the recovery area 
+                  side of the recovery area
 
                   Note that we cannot shift the recovery area during
                   this operation. Only the transaction.c code may
                   move the recovery area or we risk subtle data
                   corruption
                */
-               data_len = (recovery_head - TDB_DATA_START(tdb->header.hash_size));
-               if (tdb_free_region(tdb, TDB_DATA_START(tdb->header.hash_size), data_len) != 0) {
+               data_len = (recovery_head - TDB_DATA_START(tdb->hash_size));
+               if (tdb_free_region(tdb, TDB_DATA_START(tdb->hash_size), data_len) != 0) {
                        goto failed;
                }
                /* and the 2nd free list entry after the recovery area - if any */
@@ -886,6 +881,8 @@ int tdb_wipe_all(struct tdb_context *tdb)
                }
        }
 
+       tdb_increment_seqnum_nonblock(tdb);
+
        if (tdb_unlockall(tdb) != 0) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n"));
                goto failed;
@@ -919,7 +916,7 @@ static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
 /*
   repack a tdb
  */
-int tdb_repack(struct tdb_context *tdb)
+_PUBLIC_ int tdb_repack(struct tdb_context *tdb)
 {
        struct tdb_context *tmp_db;
        struct traverse_state state;
@@ -945,7 +942,7 @@ int tdb_repack(struct tdb_context *tdb)
                TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to traverse copying out\n"));
                tdb_transaction_cancel(tdb);
                tdb_close(tmp_db);
-               return -1;              
+               return -1;
        }
 
        if (state.error) {
@@ -969,7 +966,7 @@ int tdb_repack(struct tdb_context *tdb)
                TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to traverse copying back\n"));
                tdb_transaction_cancel(tdb);
                tdb_close(tmp_db);
-               return -1;              
+               return -1;
        }
 
        if (state.error) {
@@ -993,7 +990,7 @@ int tdb_repack(struct tdb_context *tdb)
 bool tdb_write_all(int fd, const void *buf, size_t count)
 {
        while (count) {
-               size_t ret;
+               ssize_t ret;
                ret = write(fd, buf, count);
                if (ret < 0)
                        return false;
@@ -1003,10 +1000,21 @@ bool tdb_write_all(int fd, const void *buf, size_t count)
        return true;
 }
 
+bool tdb_add_off_t(tdb_off_t a, tdb_off_t b, tdb_off_t *pret)
+{
+       tdb_off_t ret = a + b;
+
+       if ((ret < a) || (ret < b)) {
+               return false;
+       }
+       *pret = ret;
+       return true;
+}
+
 #ifdef TDB_TRACE
 static void tdb_trace_write(struct tdb_context *tdb, const char *str)
 {
-       if (!tdb_write_alltdb->tracefd, str, strlen(str)) {
+       if (!tdb_write_all(tdb->tracefd, str, strlen(str))) {
                close(tdb->tracefd);
                tdb->tracefd = -1;
        }
index 0c621636fa2bc4b60df90fa04a24183e7ce29bb8..ce92188688b021eb63923e2a89263a9b3c29af3f 100644 (file)
@@ -1,4 +1,6 @@
- /* 
+#ifndef TDB_PRIVATE_H
+#define TDB_PRIVATE_H
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library - private includes
@@ -59,7 +61,7 @@ typedef uint32_t tdb_off_t;
 #define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
 #define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
 #define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off_t))
-#define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t))
+#define TDB_HASHTABLE_SIZE(tdb) ((tdb->hash_size+1)*sizeof(tdb_off_t))
 #define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + sizeof(tdb_off_t))
 #define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start)
 #define TDB_SEQNUM_OFS    offsetof(struct tdb_header, sequence_number)
@@ -112,7 +114,7 @@ void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op,
 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
 #endif
 
-#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+#define BUCKET(hash) ((hash) % tdb->hash_size)
 
 #define DOCONV() (tdb->flags & TDB_CONVERT)
 #define CONVERT(x) (DOCONV() ? tdb_convert(&x, sizeof(x)) : &x)
@@ -180,7 +182,7 @@ struct tdb_methods {
        int (*tdb_read)(struct tdb_context *, tdb_off_t , void *, tdb_len_t , int );
        int (*tdb_write)(struct tdb_context *, tdb_off_t, const void *, tdb_len_t);
        void (*next_hash_chain)(struct tdb_context *, uint32_t *);
-       int (*tdb_oob)(struct tdb_context *, tdb_off_t , int );
+       int (*tdb_oob)(struct tdb_context *, tdb_off_t , tdb_len_t, int );
        int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t );
 };
 
@@ -196,7 +198,7 @@ struct tdb_context {
        int num_lockrecs;
        struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */
        enum TDB_ERROR ecode; /* error code for last tdb error */
-       struct tdb_header header; /* a cached copy of the header */
+       uint32_t hash_size;
        uint32_t flags; /* the flags passed to tdb_open */
        struct tdb_traverse_lock travlocks; /* current traversal locks */
        struct tdb_context *next; /* all tdbs to avoid multiple opens */
@@ -220,7 +222,7 @@ struct tdb_context {
   internal prototypes
 */
 int tdb_munmap(struct tdb_context *tdb);
-void tdb_mmap(struct tdb_context *tdb);
+int tdb_mmap(struct tdb_context *tdb);
 int tdb_lock(struct tdb_context *tdb, int list, int ltype);
 int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype);
 int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype,
@@ -238,6 +240,10 @@ void tdb_release_transaction_locks(struct tdb_context *tdb);
 int tdb_transaction_lock(struct tdb_context *tdb, int ltype,
                         enum tdb_lock_flags lockflags);
 int tdb_transaction_unlock(struct tdb_context *tdb, int ltype);
+int tdb_recovery_area(struct tdb_context *tdb,
+                     const struct tdb_methods *methods,
+                     tdb_off_t *recovery_offset,
+                     struct tdb_record *rec);
 int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
                       enum tdb_lock_flags flags, bool upgradable);
 int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock);
@@ -267,6 +273,7 @@ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t has
                           struct tdb_record *rec);
 void tdb_io_init(struct tdb_context *tdb);
 int tdb_expand(struct tdb_context *tdb, tdb_off_t size);
+tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size, int page_size);
 int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off,
                      struct tdb_record *rec);
 bool tdb_write_all(int fd, const void *buf, size_t count);
@@ -274,3 +281,9 @@ int tdb_transaction_recover(struct tdb_context *tdb);
 void tdb_header_hash(struct tdb_context *tdb,
                     uint32_t *magic1_hash, uint32_t *magic2_hash);
 unsigned int tdb_old_hash(TDB_DATA *key);
+size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off);
+bool tdb_add_off_t(tdb_off_t a, tdb_off_t b, tdb_off_t *pret);
+
+/* tdb_off_t and tdb_len_t right now are both uint32_t */
+#define tdb_add_len_t tdb_add_off_t
+#endif /* TDB_PRIVATE_H */
index 304a03fa3836940197c1319eeae5ddbcb3fce823..a2c3bbdff375b48f74107844cc320d7a9fa79e7c 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
@@ -82,8 +82,9 @@
     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.
+    still available, but no fsync/msync calls are made.  This means we
+    are still proof against a process dying during transaction commit,
+    but not against machine reboot.
 
   - if TDB_ALLOW_NESTING is passed to flags in tdb open, or added using
     tdb_add_flags() transaction nesting is enabled.
@@ -138,8 +139,8 @@ struct tdb_transaction {
        /* old file size before transaction */
        tdb_len_t old_map_size;
 
-       /* we should re-pack on commit */
-       bool need_repack;
+       /* did we expand in this transaction */
+       bool expanded;
 };
 
 
@@ -147,7 +148,7 @@ struct tdb_transaction {
   read while in a transaction. We need to check first if the data is in our list
   of transaction elements, then if not do a real read
 */
-static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, 
+static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
                            tdb_len_t len, int cv)
 {
        uint32_t blk;
@@ -194,7 +195,7 @@ static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
        return 0;
 
 fail:
-       TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len));
+       TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%u len=%u\n", off, len));
        tdb->ecode = TDB_ERR_IO;
        tdb->transaction->transaction_error = 1;
        return -1;
@@ -204,7 +205,7 @@ fail:
 /*
   write while in a transaction
 */
-static int transaction_write(struct tdb_context *tdb, tdb_off_t off, 
+static int transaction_write(struct tdb_context *tdb, tdb_off_t off,
                             const void *buf, tdb_len_t len)
 {
        uint32_t blk;
@@ -248,19 +249,13 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off,
        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 *));
-               }
+               new_blocks = (uint8_t **)realloc(tdb->transaction->blocks,
+                                                (blk+1)*sizeof(uint8_t *));
                if (new_blocks == NULL) {
                        tdb->ecode = TDB_ERR_OOM;
                        goto fail;
                }
-               memset(&new_blocks[tdb->transaction->num_blocks], 0, 
+               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;
@@ -273,23 +268,23 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off,
                if (tdb->transaction->blocks[blk] == NULL) {
                        tdb->ecode = TDB_ERR_OOM;
                        tdb->transaction->transaction_error = 1;
-                       return -1;                      
+                       return -1;
                }
                if (tdb->transaction->old_map_size > blk * tdb->transaction->block_size) {
                        tdb_len_t len2 = tdb->transaction->block_size;
                        if (len2 + (blk * tdb->transaction->block_size) > tdb->transaction->old_map_size) {
                                len2 = tdb->transaction->old_map_size - (blk * tdb->transaction->block_size);
                        }
-                       if (tdb->transaction->io_methods->tdb_read(tdb, blk * tdb->transaction->block_size, 
-                                                                  tdb->transaction->blocks[blk], 
+                       if (tdb->transaction->io_methods->tdb_read(tdb, blk * tdb->transaction->block_size,
+                                                                  tdb->transaction->blocks[blk],
                                                                   len2, 0) != 0) {
-                               SAFE_FREE(tdb->transaction->blocks[blk]);                               
+                               SAFE_FREE(tdb->transaction->blocks[blk]);
                                tdb->ecode = TDB_ERR_IO;
                                goto fail;
                        }
                        if (blk == tdb->transaction->num_blocks-1) {
                                tdb->transaction->last_block_size = len2;
-                       }                       
+                       }
                }
        }
 
@@ -308,7 +303,7 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off,
        return 0;
 
 fail:
-       TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", 
+       TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%u len=%u\n",
                 (blk*tdb->transaction->block_size) + off, len));
        tdb->transaction->transaction_error = 1;
        return -1;
@@ -316,10 +311,10 @@ fail:
 
 
 /*
-  write while in a transaction - this varient never expands the transaction blocks, it only
+  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 int transaction_write_existing(struct tdb_context *tdb, tdb_off_t off, 
+static int transaction_write_existing(struct tdb_context *tdb, tdb_off_t off,
                                      const void *buf, tdb_len_t len)
 {
        uint32_t blk;
@@ -370,7 +365,7 @@ static int transaction_write_existing(struct tdb_context *tdb, tdb_off_t off,
 static void transaction_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
 {
        uint32_t h = *chain;
-       for (;h < tdb->header.hash_size;h++) {
+       for (;h < tdb->hash_size;h++) {
                /* the +1 takes account of the freelist */
                if (0 != tdb->transaction->hash_heads[h+1]) {
                        break;
@@ -382,9 +377,10 @@ static void transaction_next_hash_chain(struct tdb_context *tdb, uint32_t *chain
 /*
   out of bounds check during a transaction
 */
-static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
+static int transaction_oob(struct tdb_context *tdb, tdb_off_t off,
+                          tdb_len_t len, int probe)
 {
-       if (len <= tdb->map_size) {
+       if (off + len >= off && off + len <= tdb->map_size) {
                return 0;
        }
        tdb->ecode = TDB_ERR_IO;
@@ -394,7 +390,7 @@ static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
 /*
   transaction version of tdb_expand().
 */
-static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size, 
+static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
                                   tdb_off_t addition)
 {
        /* add a write to the transaction elements, so subsequent
@@ -403,7 +399,7 @@ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
                return -1;
        }
 
-       tdb->transaction->need_repack = true;
+       tdb->transaction->expanded = true;
 
        return 0;
 }
@@ -438,7 +434,7 @@ static int _tdb_transaction_start(struct tdb_context *tdb,
                        return -1;
                }
                tdb->transaction->nesting++;
-               TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", 
+               TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n",
                         tdb->transaction->nesting));
                return 0;
        }
@@ -493,7 +489,7 @@ static int _tdb_transaction_start(struct tdb_context *tdb,
        /* setup a copy of the hash table heads so the hash scan in
           traverse can be fast */
        tdb->transaction->hash_heads = (uint32_t *)
-               calloc(tdb->header.hash_size+1, sizeof(uint32_t));
+               calloc(tdb->hash_size+1, sizeof(uint32_t));
        if (tdb->transaction->hash_heads == NULL) {
                tdb->ecode = TDB_ERR_OOM;
                goto fail;
@@ -507,7 +503,7 @@ static int _tdb_transaction_start(struct tdb_context *tdb,
 
        /* make sure we know about any file expansions already done by
           anyone else */
-       tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+       tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
        tdb->transaction->old_map_size = tdb->map_size;
 
        /* finally hook the io methods, replacing them with
@@ -529,12 +525,12 @@ fail_allrecord_lock:
        return -1;
 }
 
-int tdb_transaction_start(struct tdb_context *tdb)
+_PUBLIC_ int tdb_transaction_start(struct tdb_context *tdb)
 {
        return _tdb_transaction_start(tdb, TDB_LOCK_WAIT);
 }
 
-int tdb_transaction_start_nonblock(struct tdb_context *tdb)
+_PUBLIC_ int tdb_transaction_start_nonblock(struct tdb_context *tdb)
 {
        return _tdb_transaction_start(tdb, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE);
 }
@@ -543,12 +539,16 @@ int tdb_transaction_start_nonblock(struct tdb_context *tdb)
   sync to disk
 */
 static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length)
-{      
+{
        if (tdb->flags & TDB_NOSYNC) {
                return 0;
        }
 
+#ifdef HAVE_FDATASYNC
        if (fdatasync(tdb->fd) != 0) {
+#else
+       if (fsync(tdb->fd) != 0) {
+#endif
                tdb->ecode = TDB_ERR_IO;
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
                return -1;
@@ -556,7 +556,7 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t
 #ifdef HAVE_MMAP
        if (tdb->map_ptr) {
                tdb_off_t moffset = offset & ~(tdb->page_size-1);
-               if (msync(moffset + (char *)tdb->map_ptr, 
+               if (msync(moffset + (char *)tdb->map_ptr,
                          length + (offset - moffset), MS_SYNC) != 0) {
                        tdb->ecode = TDB_ERR_IO;
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n",
@@ -570,7 +570,7 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t
 
 
 static int _tdb_transaction_cancel(struct tdb_context *tdb)
-{      
+{
        int i, ret = 0;
 
        if (tdb->transaction == NULL) {
@@ -582,7 +582,7 @@ static int _tdb_transaction_cancel(struct tdb_context *tdb)
                tdb->transaction->transaction_error = 1;
                tdb->transaction->nesting--;
                return 0;
-       }               
+       }
 
        tdb->map_size = tdb->transaction->old_map_size;
 
@@ -621,7 +621,7 @@ static int _tdb_transaction_cancel(struct tdb_context *tdb)
 /*
   cancel the current transaction
 */
-int tdb_transaction_cancel(struct tdb_context *tdb)
+_PUBLIC_ int tdb_transaction_cancel(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_transaction_cancel");
        return _tdb_transaction_cancel(tdb);
@@ -630,64 +630,92 @@ int tdb_transaction_cancel(struct tdb_context *tdb)
 /*
   work out how much space the linearised recovery data will consume
 */
-static tdb_len_t tdb_recovery_size(struct tdb_context *tdb)
+static bool tdb_recovery_size(struct tdb_context *tdb, tdb_len_t *result)
 {
        tdb_len_t recovery_size = 0;
        int i;
 
        recovery_size = sizeof(uint32_t);
        for (i=0;i<tdb->transaction->num_blocks;i++) {
+               tdb_len_t block_size;
                if (i * tdb->transaction->block_size >= tdb->transaction->old_map_size) {
                        break;
                }
                if (tdb->transaction->blocks[i] == NULL) {
                        continue;
                }
-               recovery_size += 2*sizeof(tdb_off_t);
+               if (!tdb_add_len_t(recovery_size, 2*sizeof(tdb_off_t),
+                                  &recovery_size)) {
+                       return false;
+               }
                if (i == tdb->transaction->num_blocks-1) {
-                       recovery_size += tdb->transaction->last_block_size;
+                       block_size = tdb->transaction->last_block_size;
                } else {
-                       recovery_size += tdb->transaction->block_size;
+                       block_size =  tdb->transaction->block_size;
                }
-       }       
+               if (!tdb_add_len_t(recovery_size, block_size,
+                                  &recovery_size)) {
+                       return false;
+               }
+       }
 
-       return recovery_size;
+       *result = recovery_size;
+       return true;
+}
+
+int tdb_recovery_area(struct tdb_context *tdb,
+                     const struct tdb_methods *methods,
+                     tdb_off_t *recovery_offset,
+                     struct tdb_record *rec)
+{
+       if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, recovery_offset) == -1) {
+               return -1;
+       }
+
+       if (*recovery_offset == 0) {
+               rec->rec_len = 0;
+               return 0;
+       }
+
+       if (methods->tdb_read(tdb, *recovery_offset, rec, sizeof(*rec),
+                             DOCONV()) == -1) {
+               return -1;
+       }
+
+       /* ignore invalid recovery regions: can happen in crash */
+       if (rec->magic != TDB_RECOVERY_MAGIC &&
+           rec->magic != TDB_RECOVERY_INVALID_MAGIC) {
+               *recovery_offset = 0;
+               rec->rec_len = 0;
+       }
+       return 0;
 }
 
 /*
   allocate the recovery area, or use an existing recovery area if it is
   large enough
 */
-static int tdb_recovery_allocate(struct tdb_context *tdb, 
+static int tdb_recovery_allocate(struct tdb_context *tdb,
                                 tdb_len_t *recovery_size,
                                 tdb_off_t *recovery_offset,
                                 tdb_len_t *recovery_max_size)
 {
        struct tdb_record rec;
        const struct tdb_methods *methods = tdb->transaction->io_methods;
-       tdb_off_t recovery_head;
+       tdb_off_t recovery_head, new_end;
 
-       if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
+       if (tdb_recovery_area(tdb, methods, &recovery_head, &rec) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n"));
                return -1;
        }
 
-       rec.rec_len = 0;
-
-       if (recovery_head != 0) {
-               if (methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));
-                       return -1;
-               }
-               /* ignore invalid recovery regions: can happen in crash */
-               if (rec.magic != TDB_RECOVERY_MAGIC &&
-                   rec.magic != TDB_RECOVERY_INVALID_MAGIC) {
-                       recovery_head = 0;
-               }
+       if (!tdb_recovery_size(tdb, recovery_size)) {
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: "
+                        "overflow recovery size\n"));
+               return -1;
        }
 
-       *recovery_size = tdb_recovery_size(tdb);
-
+       /* Existing recovery area? */
        if (recovery_head != 0 && *recovery_size <= rec.rec_len) {
                /* it fits in the existing area */
                *recovery_max_size = rec.rec_len;
@@ -695,35 +723,61 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
                return 0;
        }
 
-       /* we need to free up the old recovery area, then allocate a
-          new one at the end of the file. Note that we cannot use
-          tdb_allocate() to allocate the new one as that might return
-          us an area that is being currently used (as of the start of
-          the transaction) */
-       if (recovery_head != 0) {
-               if (tdb_free(tdb, recovery_head, &rec) == -1) {
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n"));
-                       return -1;
+       /* If recovery area in middle of file, we need a new one. */
+       if (recovery_head == 0
+           || recovery_head + sizeof(rec) + rec.rec_len != tdb->map_size) {
+               /* we need to free up the old recovery area, then allocate a
+                  new one at the end of the file. Note that we cannot use
+                  tdb_allocate() to allocate the new one as that might return
+                  us an area that is being currently used (as of the start of
+                  the transaction) */
+               if (recovery_head) {
+                       if (tdb_free(tdb, recovery_head, &rec) == -1) {
+                               TDB_LOG((tdb, TDB_DEBUG_FATAL,
+                                        "tdb_recovery_allocate: failed to"
+                                        " free previous recovery area\n"));
+                               return -1;
+                       }
+
+                       /* the tdb_free() call might have increased
+                        * the recovery size */
+                       if (!tdb_recovery_size(tdb, recovery_size)) {
+                               TDB_LOG((tdb, TDB_DEBUG_FATAL,
+                                        "tdb_recovery_allocate: "
+                                        "overflow recovery size\n"));
+                               return -1;
+                       }
                }
+
+               /* New head will be at end of file. */
+               recovery_head = tdb->map_size;
        }
 
-       /* the tdb_free() call might have increased the recovery size */
-       *recovery_size = tdb_recovery_size(tdb);
+       /* Now we know where it will be. */
+       *recovery_offset = recovery_head;
 
-       /* round up to a multiple of page size */
-       *recovery_max_size = TDB_ALIGN(sizeof(rec) + *recovery_size, tdb->page_size) - sizeof(rec);
-       *recovery_offset = tdb->map_size;
-       recovery_head = *recovery_offset;
+       /* Expand by more than we need, so we don't do it often. */
+       *recovery_max_size = tdb_expand_adjust(tdb->map_size,
+                                              *recovery_size,
+                                              tdb->page_size)
+               - sizeof(rec);
 
-       if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, 
-                                    (tdb->map_size - tdb->transaction->old_map_size) +
-                                    sizeof(rec) + *recovery_max_size) == -1) {
+       if (!tdb_add_off_t(recovery_head, sizeof(rec), &new_end) ||
+           !tdb_add_off_t(new_end, *recovery_max_size, &new_end)) {
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: "
+                        "overflow recovery area\n"));
+               return -1;
+       }
+
+       if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size,
+                                    new_end - tdb->transaction->old_map_size)
+           == -1) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n"));
                return -1;
        }
 
        /* remap the file (if using mmap) */
-       methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+       methods->tdb_oob(tdb, tdb->map_size, 1, 1);
 
        /* we have to reset the old map size so that we don't try to expand the file
           again in the transaction commit, which would destroy the recovery area */
@@ -732,7 +786,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
        /* write the recovery header offset and sync - we can sync without a race here
           as the magic ptr in the recovery record has not been set */
        CONVERT(recovery_head);
-       if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD, 
+       if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD,
                               &recovery_head, sizeof(tdb_off_t)) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n"));
                return -1;
@@ -749,7 +803,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
 /*
   setup the recovery data that will be used on a crash during commit
 */
-static int transaction_setup_recovery(struct tdb_context *tdb, 
+static int transaction_setup_recovery(struct tdb_context *tdb,
                                      tdb_off_t *magic_offset)
 {
        tdb_len_t recovery_size;
@@ -764,7 +818,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
        /*
          check that the recovery area has enough space
        */
-       if (tdb_recovery_allocate(tdb, &recovery_size, 
+       if (tdb_recovery_allocate(tdb, &recovery_size,
                                  &recovery_offset, &recovery_max_size) == -1) {
                return -1;
        }
@@ -782,7 +836,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
        rec->data_len = recovery_size;
        rec->rec_len  = recovery_max_size;
        rec->key_len  = old_map_size;
-       CONVERT(rec);
+       CONVERT(*rec);
 
        /* build the recovery data into a single blob to allow us to do a single
           large write, which should be more efficient */
@@ -829,7 +883,9 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
        /* and the tailer */
        tailer = sizeof(*rec) + recovery_max_size;
        memcpy(p, &tailer, 4);
-       CONVERT(p);
+       if (DOCONV()) {
+               tdb_convert(p, 4);
+       }
 
        /* write the recovery data to the recovery area */
        if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) {
@@ -880,7 +936,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
 }
 
 static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
-{      
+{
        const struct tdb_methods *methods;
 
        if (tdb->transaction == NULL) {
@@ -905,7 +961,7 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
 
        if (tdb->transaction->nesting != 0) {
                return 0;
-       }               
+       }
 
        /* check for a null transaction */
        if (tdb->transaction->blocks == NULL) {
@@ -938,21 +994,19 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
                return -1;
        }
 
-       if (!(tdb->flags & TDB_NOSYNC)) {
-               /* write the recovery data to the end of the file */
-               if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) {
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n"));
-                       _tdb_transaction_cancel(tdb);
-                       return -1;
-               }
+       /* write the recovery data to the end of the file */
+       if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) {
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n"));
+               _tdb_transaction_cancel(tdb);
+               return -1;
        }
 
        tdb->transaction->prepared = true;
 
        /* expand the file to the new size if needed */
        if (tdb->map_size != tdb->transaction->old_map_size) {
-               if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, 
-                                            tdb->map_size - 
+               if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size,
+                                            tdb->map_size -
                                             tdb->transaction->old_map_size) == -1) {
                        tdb->ecode = TDB_ERR_IO;
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: expansion failed\n"));
@@ -960,7 +1014,7 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
                        return -1;
                }
                tdb->map_size = tdb->transaction->old_map_size;
-               methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+               methods->tdb_oob(tdb, tdb->map_size, 1, 1);
        }
 
        /* Keep the open lock until the actual commit */
@@ -971,20 +1025,42 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
 /*
    prepare to commit the current transaction
 */
-int tdb_transaction_prepare_commit(struct tdb_context *tdb)
-{      
+_PUBLIC_ int tdb_transaction_prepare_commit(struct tdb_context *tdb)
+{
        tdb_trace(tdb, "tdb_transaction_prepare_commit");
        return _tdb_transaction_prepare_commit(tdb);
 }
 
+/* A repack is worthwhile if the largest is less than half total free. */
+static bool repack_worthwhile(struct tdb_context *tdb)
+{
+       tdb_off_t ptr;
+       struct tdb_record rec;
+       tdb_len_t total = 0, largest = 0;
+
+       if (tdb_ofs_read(tdb, FREELIST_TOP, &ptr) == -1) {
+               return false;
+       }
+
+       while (ptr != 0 && tdb_rec_free_read(tdb, ptr, &rec) == 0) {
+               total += rec.rec_len;
+               if (rec.rec_len > largest) {
+                       largest = rec.rec_len;
+               }
+               ptr = rec.next;
+       }
+
+       return total > largest * 2;
+}
+
 /*
   commit the current transaction
 */
-int tdb_transaction_commit(struct tdb_context *tdb)
-{      
+_PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb)
+{
        const struct tdb_methods *methods;
        int i;
-       bool need_repack;
+       bool need_repack = false;
 
        if (tdb->transaction == NULL) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
@@ -1042,7 +1118,7 @@ int tdb_transaction_commit(struct tdb_context *tdb)
                           possibly expanded the file, so we need to
                           run the crash recovery code */
                        tdb->methods = methods;
-                       tdb_transaction_recover(tdb); 
+                       tdb_transaction_recover(tdb);
 
                        _tdb_transaction_cancel(tdb);
 
@@ -1050,7 +1126,12 @@ int tdb_transaction_commit(struct tdb_context *tdb)
                        return -1;
                }
                SAFE_FREE(tdb->transaction->blocks[i]);
-       } 
+       }
+
+       /* Do this before we drop lock or blocks. */
+       if (tdb->transaction->expanded) {
+               need_repack = repack_worthwhile(tdb);
+       }
 
        SAFE_FREE(tdb->transaction->blocks);
        tdb->transaction->num_blocks = 0;
@@ -1075,8 +1156,6 @@ int tdb_transaction_commit(struct tdb_context *tdb)
        utime(tdb->name, NULL);
 #endif
 
-       need_repack = tdb->transaction->need_repack;
-
        /* use a transaction cancel to free memory and remove the
           transaction locks */
        _tdb_transaction_cancel(tdb);
@@ -1114,9 +1193,9 @@ int tdb_transaction_recover(struct tdb_context *tdb)
        }
 
        /* read the recovery record */
-       if (tdb->methods->tdb_read(tdb, recovery_head, &rec, 
+       if (tdb->methods->tdb_read(tdb, recovery_head, &rec,
                                   sizeof(rec), DOCONV()) == -1) {
-               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n"));           
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n"));
                tdb->ecode = TDB_ERR_IO;
                return -1;
        }
@@ -1136,7 +1215,7 @@ int tdb_transaction_recover(struct tdb_context *tdb)
 
        data = (unsigned char *)malloc(rec.data_len);
        if (data == NULL) {
-               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n"));         
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n"));
                tdb->ecode = TDB_ERR_OOM;
                return -1;
        }
@@ -1144,7 +1223,7 @@ int tdb_transaction_recover(struct tdb_context *tdb)
        /* read the full recovery data */
        if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data,
                                   rec.data_len, 0) == -1) {
-               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n"));             
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n"));
                tdb->ecode = TDB_ERR_IO;
                return -1;
        }
@@ -1161,7 +1240,7 @@ int tdb_transaction_recover(struct tdb_context *tdb)
 
                if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) {
                        free(data);
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs));
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %u bytes at offset %u\n", len, ofs));
                        tdb->ecode = TDB_ERR_IO;
                        return -1;
                }
@@ -1181,7 +1260,7 @@ int tdb_transaction_recover(struct tdb_context *tdb)
                if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) {
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n"));
                        tdb->ecode = TDB_ERR_IO;
-                       return -1;                      
+                       return -1;
                }
        }
 
@@ -1190,7 +1269,7 @@ int tdb_transaction_recover(struct tdb_context *tdb)
                          &zero) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n"));
                tdb->ecode = TDB_ERR_IO;
-               return -1;                      
+               return -1;
        }
 
        if (transaction_sync(tdb, 0, recovery_eof) == -1) {
@@ -1199,7 +1278,7 @@ int tdb_transaction_recover(struct tdb_context *tdb)
                return -1;
        }
 
-       TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n", 
+       TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %u byte database\n",
                 recovery_eof));
 
        /* all done */
index d77086a79aa6c270dd3a7f799fa978ad74ca8da2..a843359082bd1b65dc590084e61b6e2fba0cb16b 100644 (file)
@@ -1,4 +1,4 @@
- /* 
+ /*
    Unix SMB/CIFS implementation.
 
    trivial database library
@@ -37,7 +37,7 @@ static tdb_off_t tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock
        int want_next = (tlock->off != 0);
 
        /* Lock each chain from the start one. */
-       for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
+       for (; tlock->hash < tdb->hash_size; tlock->hash++) {
                if (!tlock->off && tlock->hash != 0) {
                        /* this is an optimisation for the common case where
                           the hash chain is empty, which is particularly
@@ -68,7 +68,7 @@ static tdb_off_t tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock
                           system (testing using ldbtest).
                        */
                        tdb->methods->next_hash_chain(tdb, &tlock->hash);
-                       if (tlock->hash == tdb->header.hash_size) {
+                       if (tlock->hash == tdb->hash_size) {
                                continue;
                        }
                }
@@ -117,7 +117,7 @@ static tdb_off_t tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock
                        /* Try to clean dead ones from old traverses */
                        current = tlock->off;
                        tlock->off = rec->next;
-                       if (!(tdb->read_only || tdb->traverse_read) && 
+                       if (!(tdb->read_only || tdb->traverse_read) &&
                            tdb_do_delete(tdb, current, rec) != 0)
                                goto fail;
                }
@@ -140,7 +140,7 @@ static tdb_off_t tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock
    if fn is NULL then it is not called
    a non-zero return value from fn() indicates that the traversal should stop
   */
-static int tdb_traverse_internal(struct tdb_context *tdb, 
+static int tdb_traverse_internal(struct tdb_context *tdb,
                                 tdb_traverse_func fn, void *private_data,
                                 struct tdb_traverse_lock *tl)
 {
@@ -149,7 +149,7 @@ static int tdb_traverse_internal(struct tdb_context *tdb,
        int ret = 0, count = 0;
        tdb_off_t off;
 
-       /* This was in the initializaton, above, but the IRIX compiler
+       /* This was in the initialization, above, but the IRIX compiler
         * did not like it.  crh
         */
        tl->next = tdb->travlocks.next;
@@ -165,7 +165,7 @@ static int tdb_traverse_internal(struct tdb_context *tdb,
                }
                count++;
                /* now read the full record */
-               key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec), 
+               key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec),
                                          rec.key_len + rec.data_len);
                if (!key.dptr) {
                        ret = -1;
@@ -210,9 +210,9 @@ out:
 
 
 /*
-  a write style traverse - temporarily marks the db read only
+  a read style traverse - temporarily marks the db read only
 */
-int tdb_traverse_read(struct tdb_context *tdb, 
+_PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb,
                      tdb_traverse_func fn, void *private_data)
 {
        struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK };
@@ -241,7 +241,7 @@ int tdb_traverse_read(struct tdb_context *tdb,
   WARNING: The data buffer given to the callback fn does NOT meet the
   alignment restrictions malloc gives you.
 */
-int tdb_traverse(struct tdb_context *tdb, 
+_PUBLIC_ int tdb_traverse(struct tdb_context *tdb,
                 tdb_traverse_func fn, void *private_data)
 {
        struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK };
@@ -267,7 +267,7 @@ int tdb_traverse(struct tdb_context *tdb,
 
 
 /* find the first entry in the database and return its key */
-TDB_DATA tdb_firstkey(struct tdb_context *tdb)
+_PUBLIC_ TDB_DATA tdb_firstkey(struct tdb_context *tdb)
 {
        TDB_DATA key;
        struct tdb_record rec;
@@ -298,7 +298,7 @@ TDB_DATA tdb_firstkey(struct tdb_context *tdb)
 }
 
 /* find the next entry in the database, returning its key */
-TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)
+_PUBLIC_ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)
 {
        uint32_t oldhash;
        TDB_DATA key = tdb_null;
index fc42e00c15375e7ab2eec7c033d3587e174e8600..513aa71a0b022be41036eec4c2ee72c65bc1be23 100644 (file)
@@ -2,7 +2,7 @@ AC_PREREQ(2.50)
 AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""])
 AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""])
 AC_DEFUN([SMB_ENABLE], [echo -n ""])
-AC_INIT(tdb, 1.2.6)
+AC_INIT(tdb, 1.2.12)
 AC_CONFIG_SRCDIR([common/tdb.c])
 AC_CONFIG_HEADER(include/config.h)
 AC_LIBREPLACE_ALL_CHECKS
diff --git a/lib/tdb/docs/mainpage.dox b/lib/tdb/docs/mainpage.dox
new file mode 100644 (file)
index 0000000..d130769
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+
+@mainpage
+
+This is a simple database API. It was inspired by the realisation that in Samba
+we have several ad-hoc bits of code that essentially implement small databases
+for sharing structures between parts of Samba.
+
+The interface is based on gdbm. gdbm couldn't be use as we needed to be able to
+have multiple writers to the databases at one time.
+
+@section tdb_download Download
+
+You can download the latest releases of tdb from the
+<a href="http://samba.org/ftp/tdb">tdb directory</a> on the samba public source
+archive.
+
+You can download the latest code either via git or rsync.
+
+To fetch via git see the following guide:
+
+<a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a>
+Once you have cloned the tree switch to the master branch and cd into the source/lib/tdb directory.
+
+To fetch via rsync use these commands:
+
+<pre>
+  rsync -Pavz samba.org::ftp/unpacked/standalone_projects/lib/tdb .
+  rsync -Pavz samba.org::ftp/unpacked/standalone_projects/lib/replace .
+</pre>
+
+and build in tdb. It will find the replace library in the directory above
+automatically.
+
+@section tdb_bugs Discussion and bug reports
+
+tdb does not currently have its own mailing list or bug tracking system. For now,
+please use the
+<a href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
+mailing list, and the <a href="http://bugzilla.samba.org/">Samba bugzilla</a> bug
+tracking system.
+
+
+@section tdb_compilation Compilation
+
+add HAVE_MMAP=1 to use mmap instead of read/write
+add NOLOCK=1 to disable locking code
+
+@section tdb_testing Testing
+
+Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
+identical operations via tdb and gdbm then make sure the result is the
+same
+
+Also included is tdbtool, which allows simple database manipulation
+on the commandline.
+
+tdbtest and tdbtool are not built as part of Samba, but are included
+for completeness.
+
+*/
diff --git a/lib/tdb/doxy.config b/lib/tdb/doxy.config
new file mode 100644 (file)
index 0000000..f55e9c3
--- /dev/null
@@ -0,0 +1,1697 @@
+# Doxyfile 1.7.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = tdb
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = 1.2.9
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description for a project that appears at the top of each page and should give viewer a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even if there is only one candidate or it is obvious which candidate to choose by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = include \
+                         docs
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          = *.cpp \
+                         *.cc \
+                         *.c \
+                         *.h \
+                         *.hh \
+                         *.hpp \
+                         *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = */.git/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [0,1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+# Note that a value of 0 will completely suppress the enum values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NONE
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             = DOXYGEN \
+                         PRINTF_ATTRIBUTE(x,y)=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, svg, gif or svg.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
index 536a0b373722347c6322504ca787cb196e91ca45..e371e33876f2101ea400b54d0dd42eeb291a7dec 100644 (file)
 extern "C" {
 #endif
 
-#include "signal.h"
+#include <signal.h>
 
-/* Samba sets hidden attribute when building libraries: we don't. */
-#ifndef _PUBLIC_
-#define _PUBLIC_
-#endif
+/**
+ * @defgroup tdb The tdb API
+ *
+ * tdb is a Trivial database. In concept, it is very much like GDBM, and BSD's
+ * DB except that it allows multiple simultaneous writers and uses locking
+ * internally to keep writers from trampling on each other. tdb is also
+ * extremely small.
+ *
+ * @section tdb_interface Interface
+ *
+ * The interface is very similar to gdbm except for the following:
+ *
+ * <ul>
+ * <li>different open interface. The tdb_open call is more similar to a
+ * traditional open()</li>
+ * <li>no tdbm_reorganise() function</li>
+ * <li>no tdbm_sync() function. No operations are cached in the library
+ *     anyway</li>
+ * <li>added a tdb_traverse() function for traversing the whole database</li>
+ * <li>added transactions support</li>
+ * </ul>
+ *
+ * A general rule for using tdb is that the caller frees any returned TDB_DATA
+ * structures. Just call free(p.dptr) to free a TDB_DATA return value called p.
+ * This is the same as gdbm.
+ *
+ * @{
+ */
+
+/** Flags to tdb_store() */
+#define TDB_REPLACE 1          /** Unused */
+#define TDB_INSERT 2           /** Don't overwrite an existing entry */
+#define TDB_MODIFY 3           /** Don't create an existing entry    */
 
-/* flags to tdb_store() */
-#define TDB_REPLACE 1          /* Unused */
-#define TDB_INSERT 2           /* Don't overwrite an existing entry */
-#define TDB_MODIFY 3           /* Don't create an existing entry    */
-
-/* flags for tdb_open() */
-#define TDB_DEFAULT 0 /* just a readability place holder */
-#define TDB_CLEAR_IF_FIRST 1
-#define TDB_INTERNAL 2 /* don't store on disk */
-#define TDB_NOLOCK   4 /* don't do any locking */
-#define TDB_NOMMAP   8 /* don't use mmap */
-#define TDB_CONVERT 16 /* convert endian (internal use) */
-#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
-#define TDB_NOSYNC   64 /* don't use synchronous transactions */
-#define TDB_SEQNUM   128 /* maintain a sequence number */
-#define TDB_VOLATILE   256 /* Activate the per-hashchain freelist, default 5 */
-#define TDB_ALLOW_NESTING 512 /* Allow transactions to nest */
-#define TDB_DISALLOW_NESTING 1024 /* Disallow transactions to nest */
-#define TDB_INCOMPATIBLE_HASH 2048 /* Better hashing: can't be opened by tdb < 1.2.6. */
-
-/* error codes */
+/** Flags for tdb_open() */
+#define TDB_DEFAULT 0 /** just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1 /** If this is the first open, wipe the db */
+#define TDB_INTERNAL 2 /** Don't store on disk */
+#define TDB_NOLOCK   4 /** Don't do any locking */
+#define TDB_NOMMAP   8 /** Don't use mmap */
+#define TDB_CONVERT 16 /** Convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /** Header is big-endian (internal use) */
+#define TDB_NOSYNC   64 /** Don't use synchronous transactions */
+#define TDB_SEQNUM   128 /** Maintain a sequence number */
+#define TDB_VOLATILE   256 /** Activate the per-hashchain freelist, default 5 */
+#define TDB_ALLOW_NESTING 512 /** Allow transactions to nest */
+#define TDB_DISALLOW_NESTING 1024 /** Disallow transactions to nest */
+#define TDB_INCOMPATIBLE_HASH 2048 /** Better hashing: can't be opened by tdb < 1.2.6. */
+
+/** The tdb error codes */
 enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
                TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
                TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY,
                TDB_ERR_NESTING};
 
-/* debugging uses one of the following levels */
+/** Debugging uses one of the following levels */
 enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, 
                      TDB_DEBUG_WARNING, TDB_DEBUG_TRACE};
 
+/** The tdb data structure */
 typedef struct TDB_DATA {
        unsigned char *dptr;
        size_t dsize;
@@ -84,7 +109,7 @@ typedef struct TDB_DATA {
 #endif
 #endif
 
-/* this is the context structure that is returned from a db open */
+/** This is the context structure that is returned from a db open. */
 typedef struct tdb_context TDB_CONTEXT;
 
 typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *);
@@ -96,89 +121,752 @@ struct tdb_logging_context {
         void *log_private;
 };
 
-_PUBLIC_ struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
+/**
+ * @brief Open the database and creating it if necessary.
+ *
+ * @param[in]  name     The name of the db to open.
+ *
+ * @param[in]  hash_size The hash size is advisory, use zero for a default
+ *                       value.
+ *
+ * @param[in]  tdb_flags The flags to use to open the db:\n\n
+ *                         TDB_CLEAR_IF_FIRST - Clear database if we are the
+ *                                              only one with it open\n
+ *                         TDB_INTERNAL - Don't use a file, instaed store the
+ *                                        data in memory. The filename is
+ *                                        ignored in this case.\n
+ *                         TDB_NOLOCK - Don't do any locking\n
+ *                         TDB_NOMMAP - Don't use mmap\n
+ *                         TDB_NOSYNC - Don't synchronise transactions to disk\n
+ *                         TDB_SEQNUM - Maintain a sequence number\n
+ *                         TDB_VOLATILE - activate the per-hashchain freelist,
+ *                                        default 5.\n
+ *                         TDB_ALLOW_NESTING - Allow transactions to nest.\n
+ *                         TDB_DISALLOW_NESTING - Disallow transactions to nest.\n
+ *
+ * @param[in]  open_flags Flags for the open(2) function.
+ *
+ * @param[in]  mode     The mode for the open(2) function.
+ *
+ * @return              A tdb context structure, NULL on error.
+ */
+struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
                      int open_flags, mode_t mode);
-_PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+
+/**
+ * @brief Open the database and creating it if necessary.
+ *
+ * This is like tdb_open(), but allows you to pass an initial logging and
+ * hash function. Be careful when passing a hash function - all users of the
+ * database must use the same hash function or you will get data corruption.
+ *
+ * @param[in]  name     The name of the db to open.
+ *
+ * @param[in]  hash_size The hash size is advisory, use zero for a default
+ *                       value.
+ *
+ * @param[in]  tdb_flags The flags to use to open the db:\n\n
+ *                         TDB_CLEAR_IF_FIRST - Clear database if we are the
+ *                                              only one with it open\n
+ *                         TDB_INTERNAL - Don't use a file, instaed store the
+ *                                        data in memory. The filename is
+ *                                        ignored in this case.\n
+ *                         TDB_NOLOCK - Don't do any locking\n
+ *                         TDB_NOMMAP - Don't use mmap\n
+ *                         TDB_NOSYNC - Don't synchronise transactions to disk\n
+ *                         TDB_SEQNUM - Maintain a sequence number\n
+ *                         TDB_VOLATILE - activate the per-hashchain freelist,
+ *                                        default 5.\n
+ *                         TDB_ALLOW_NESTING - Allow transactions to nest.\n
+ *                         TDB_DISALLOW_NESTING - Disallow transactions to nest.\n
+ *
+ * @param[in]  open_flags Flags for the open(2) function.
+ *
+ * @param[in]  mode     The mode for the open(2) function.
+ *
+ * @param[in]  log_ctx  The logging function to use.
+ *
+ * @param[in]  hash_fn  The hash function you want to use.
+ *
+ * @return              A tdb context structure, NULL on error.
+ *
+ * @see tdb_open()
+ */
+struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                         int open_flags, mode_t mode,
                         const struct tdb_logging_context *log_ctx,
                         tdb_hash_func hash_fn);
-_PUBLIC_ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
-
-_PUBLIC_ int tdb_reopen(struct tdb_context *tdb);
-_PUBLIC_ int tdb_reopen_all(int parent_longlived);
-_PUBLIC_ void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx);
-_PUBLIC_ enum TDB_ERROR tdb_error(struct tdb_context *tdb);
-_PUBLIC_ const char *tdb_errorstr(struct tdb_context *tdb);
-_PUBLIC_ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
+
+/**
+ * @brief Set the maximum number of dead records per hash chain.
+ *
+ * @param[in]  tdb      The database handle to set the maximum.
+ *
+ * @param[in]  max_dead The maximum number of dead records per hash chain.
+ */
+void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
+
+/**
+ * @brief Reopen a tdb.
+ *
+ * This can be used after a fork to ensure that we have an independent seek
+ * pointer from our parent and to re-establish locks.
+ *
+ * @param[in]  tdb      The database to reopen. It will be free'd on error!
+ *
+ * @return              0 on success, -1 on error.
+ *
+ * @note Don't call tdb_error() after this function cause the tdb context will
+ *       be freed on error.
+ */
+int tdb_reopen(struct tdb_context *tdb);
+
+/**
+ * @brief Reopen all tdb's
+ *
+ * If the parent is longlived (ie. a parent daemon architecture), we know it
+ * will keep it's active lock on a tdb opened with CLEAR_IF_FIRST. Thus for
+ * child processes we don't have to add an active lock. This is essential to
+ * improve performance on systems that keep POSIX locks as a non-scalable data
+ * structure in the kernel.
+ *
+ * @param[in]  parent_longlived Wether the parent is longlived or not.
+ *
+ * @return              0 on success, -1 on error.
+ */
+int tdb_reopen_all(int parent_longlived);
+
+/**
+ * @brief Set a different tdb logging function.
+ *
+ * @param[in]  tdb      The tdb to set the logging function.
+ *
+ * @param[in]  log_ctx  The logging function to set.
+ */
+void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx);
+
+/**
+ * @brief Get the tdb last error code.
+ *
+ * @param[in]  tdb      The tdb to get the error code from.
+ *
+ * @return              A TDB_ERROR code.
+ *
+ * @see TDB_ERROR
+ */
+enum TDB_ERROR tdb_error(struct tdb_context *tdb);
+
+/**
+ * @brief Get a error string for the last tdb error
+ *
+ * @param[in]  tdb      The tdb to get the error code from.
+ *
+ * @return              An error string.
+ */
+const char *tdb_errorstr(struct tdb_context *tdb);
+
+/**
+ * @brief Fetch an entry in the database given a key.
+ *
+ * The caller must free the resulting data.
+ *
+ * @param[in]  tdb      The tdb to fetch the key.
+ *
+ * @param[in]  key      The key to fetch.
+ *
+ * @return              The key entry found in the database, NULL on error with
+ *                      TDB_ERROR set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Hand a record to a parser function without allocating it.
+ *
+ * This function is meant as a fast tdb_fetch alternative for large records
+ * that are frequently read. The "key" and "data" arguments point directly
+ * into the tdb shared memory, they are not aligned at any boundary.
+ *
+ * @warning The parser is called while tdb holds a lock on the record. DO NOT
+ * call other tdb routines from within the parser. Also, for good performance
+ * you should make the parser fast to allow parallel operations.
+ *
+ * @param[in]  tdb      The tdb to parse the record.
+ *
+ * @param[in]  key      The key to parse.
+ *
+ * @param[in]  parser   The parser to use to parse the data.
+ *
+ * @param[in]  private_data A private data pointer which is passed to the parser
+ *                          function.
+ *
+ * @return              -1 if the record was not found. If the record was found,
+ *                      the return value of "parser" is passed up to the caller.
+ */
+int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
                              int (*parser)(TDB_DATA key, TDB_DATA data,
                                            void *private_data),
                              void *private_data);
-_PUBLIC_ int tdb_delete(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
-_PUBLIC_ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf);
-_PUBLIC_ int tdb_close(struct tdb_context *tdb);
-_PUBLIC_ TDB_DATA tdb_firstkey(struct tdb_context *tdb);
-_PUBLIC_ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *);
-_PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *);
-_PUBLIC_ int tdb_exists(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_lockall(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_nonblock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_unlockall(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_read(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_read_nonblock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_unlockall_read(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_mark(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_unmark(struct tdb_context *tdb);
-_PUBLIC_ const char *tdb_name(struct tdb_context *tdb);
-_PUBLIC_ int tdb_fd(struct tdb_context *tdb);
-_PUBLIC_ tdb_log_func tdb_log_fn(struct tdb_context *tdb);
-_PUBLIC_ void *tdb_get_logging_private(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_start(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_start_nonblock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_prepare_commit(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_cancel(struct tdb_context *tdb);
-_PUBLIC_ int tdb_get_seqnum(struct tdb_context *tdb);
-_PUBLIC_ int tdb_hash_size(struct tdb_context *tdb);
-_PUBLIC_ size_t tdb_map_size(struct tdb_context *tdb);
-_PUBLIC_ int tdb_get_flags(struct tdb_context *tdb);
-_PUBLIC_ void tdb_add_flags(struct tdb_context *tdb, unsigned flag);
-_PUBLIC_ void tdb_remove_flags(struct tdb_context *tdb, unsigned flag);
-_PUBLIC_ void tdb_enable_seqnum(struct tdb_context *tdb);
-_PUBLIC_ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb);
-_PUBLIC_ unsigned int tdb_jenkins_hash(TDB_DATA *key);
-_PUBLIC_ int tdb_check(struct tdb_context *tdb,
-             int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
+
+/**
+ * @brief Delete an entry in the database given a key.
+ *
+ * @param[in]  tdb      The tdb to delete the key.
+ *
+ * @param[in]  key      The key to delete.
+ *
+ * @return              0 on success, -1 if the key doesn't exist.
+ */
+int tdb_delete(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Store an element in the database.
+ *
+ * This replaces any existing element with the same key.
+ *
+ * @param[in]  tdb      The tdb to store the entry.
+ *
+ * @param[in]  key      The key to use to store the entry.
+ *
+ * @param[in]  dbuf     The data to store under the key.
+ *
+ * @param[in]  flag     The flags to store the key:\n\n
+ *                      TDB_INSERT: Don't overwrite an existing entry.\n
+ *                      TDB_MODIFY: Don't create a new entry\n
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+
+/**
+ * @brief Append data to an entry.
+ *
+ * If the entry doesn't exist, it will create a new one.
+ *
+ * @param[in]  tdb      The database to use.
+ *
+ * @param[in]  key      The key to append the data.
+ *
+ * @param[in]  new_dbuf The data to append to the key.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf);
+
+/**
+ * @brief Close a database.
+ *
+ * @param[in]  tdb      The database to close. The context will be free'd.
+ *
+ * @return              0 for success, -1 on error.
+ *
+ * @note Don't call tdb_error() after this function cause the tdb context will
+ *       be freed on error.
+ */
+int tdb_close(struct tdb_context *tdb);
+
+/**
+ * @brief Find the first entry in the database and return its key.
+ *
+ * The caller must free the returned data.
+ *
+ * @param[in]  tdb      The database to use.
+ *
+ * @return              The first entry of the database, an empty TDB_DATA entry
+ *                      if the database is empty.
+ */
+TDB_DATA tdb_firstkey(struct tdb_context *tdb);
+
+/**
+ * @brief Find the next entry in the database, returning its key.
+ *
+ * The caller must free the returned data.
+ *
+ * @param[in]  tdb      The database to use.
+ *
+ * @param[in]  key      The key from which you want the next key.
+ *
+ * @return              The next entry of the current key, an empty TDB_DATA
+ *                      entry if there is no entry.
+ */
+TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Traverse the entire database.
+ *
+ * While travering the function fn(tdb, key, data, state) is called on each
+ * element. If fn is NULL then it is not called. A non-zero return value from
+ * fn() indicates that the traversal should stop. Traversal callbacks may not
+ * start transactions.
+ *
+ * @warning The data buffer given to the callback fn does NOT meet the alignment
+ * restrictions malloc gives you.
+ *
+ * @param[in]  tdb      The database to traverse.
+ *
+ * @param[in]  fn       The function to call on each entry.
+ *
+ * @param[in]  private_data The private data which should be passed to the
+ *                          traversing function.
+ *
+ * @return              The record count traversed, -1 on error.
+ */
+int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data);
+
+/**
+ * @brief Traverse the entire database.
+ *
+ * While traversing the database the function fn(tdb, key, data, state) is
+ * called on each element, but marking the database read only during the
+ * traversal, so any write operations will fail. This allows tdb to use read
+ * locks, which increases the parallelism possible during the traversal.
+ *
+ * @param[in]  tdb      The database to traverse.
+ *
+ * @param[in]  fn       The function to call on each entry.
+ *
+ * @param[in]  private_data The private data which should be passed to the
+ *                          traversing function.
+ *
+ * @return              The record count traversed, -1 on error.
+ */
+int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data);
+
+/**
+ * @brief Check if an entry in the database exists.
+ *
+ * @note 1 is returned if the key is found and 0 is returned if not found this
+ * doesn't match the conventions in the rest of this module, but is compatible
+ * with gdbm.
+ *
+ * @param[in]  tdb      The database to check if the entry exists.
+ *
+ * @param[in]  key      The key to check if the entry exists.
+ *
+ * @return              1 if the key is found, 0 if not.
+ */
+int tdb_exists(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Lock entire database with a write lock.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with a write lock.
+ *
+ * This is the non-blocking call.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Unlock entire database with write lock.
+ *
+ * @param[in]  tdb      The database to unlock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_unlockall(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with a read lock.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_read(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with a read lock.
+ *
+ * This is the non-blocking call.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall_read()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_read_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Unlock entire database with read lock.
+ *
+ * @param[in]  tdb      The database to unlock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall_read()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_unlockall_read(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with write lock - mark only.
+ *
+ * @todo Add more details.
+ *
+ * @param[in]  tdb      The database to mark.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_mark(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with write lock - unmark only.
+ *
+ * @todo Add more details.
+ *
+ * @param[in]  tdb      The database to mark.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_unmark(struct tdb_context *tdb);
+
+/**
+ * @brief Get the name of the current tdb file.
+ *
+ * This is useful for external logging functions.
+ *
+ * @param[in]  tdb      The database to get the name from.
+ *
+ * @return              The name of the database.
+ */
+const char *tdb_name(struct tdb_context *tdb);
+
+/**
+ * @brief Get the underlying file descriptor being used by tdb.
+ *
+ * This is useful for external routines that want to check the device/inode
+ * of the fd.
+ *
+ * @param[in]  tdb      The database to get the fd from.
+ *
+ * @return              The file descriptor or -1.
+ */
+int tdb_fd(struct tdb_context *tdb);
+
+/**
+ * @brief Get the current logging function.
+ *
+ * This is useful for external tdb routines that wish to log tdb errors.
+ *
+ * @param[in]  tdb      The database to get the logging function from.
+ *
+ * @return              The logging function of the database.
+ *
+ * @see tdb_get_logging_private()
+ */
+tdb_log_func tdb_log_fn(struct tdb_context *tdb);
+
+/**
+ * @brief Get the private data of the logging function.
+ *
+ * @param[in]  tdb      The database to get the data from.
+ *
+ * @return              The private data pointer of the logging function.
+ *
+ * @see tdb_log_fn()
+ */
+void *tdb_get_logging_private(struct tdb_context *tdb);
+
+/**
+ * @brief Start a transaction.
+ *
+ * All operations after the transaction start can either be committed with
+ * tdb_transaction_commit() or cancelled with tdb_transaction_cancel().
+ *
+ * If you call tdb_transaction_start() again on the same tdb context while a
+ * transaction is in progress, then the same transaction buffer is re-used. The
+ * number of tdb_transaction_{commit,cancel} operations must match the number
+ * of successful tdb_transaction_start() calls.
+ *
+ * Note that transactions are by default disk synchronous, and use a recover
+ * area in the database to automatically recover the database on the next open
+ * if the system crashes during a transaction. You can disable the synchronous
+ * transaction recovery setup using the TDB_NOSYNC flag, which will greatly
+ * speed up operations at the risk of corrupting your database if the system
+ * crashes.
+ *
+ * Operations made within a transaction are not visible to other users of the
+ * database until a successful commit.
+ *
+ * @param[in]  tdb      The database to start the transaction.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_start(struct tdb_context *tdb);
+
+/**
+ * @brief Start a transaction, non-blocking.
+ *
+ * @param[in]  tdb      The database to start the transaction.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ * @see tdb_transaction_start()
+ */
+int tdb_transaction_start_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Prepare to commit a current transaction, for two-phase commits.
+ *
+ * Once prepared for commit, the only allowed calls are tdb_transaction_commit()
+ * or tdb_transaction_cancel(). Preparing allocates disk space for the pending
+ * updates, so a subsequent commit should succeed (barring any hardware
+ * failures).
+ *
+ * @param[in]  tdb      The database to prepare the commit.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_prepare_commit(struct tdb_context *tdb);
+
+/**
+ * @brief Commit a current transaction.
+ *
+ * This updates the database and releases the current transaction locks.
+ *
+ * @param[in]  tdb      The database to commit the transaction.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_commit(struct tdb_context *tdb);
+
+/**
+ * @brief Cancel a current transaction.
+ *
+ * This discards all write and lock operations that have been made since the
+ * transaction started.
+ *
+ * @param[in]  tdb      The tdb to cancel the transaction on.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_cancel(struct tdb_context *tdb);
+
+/**
+ * @brief Get the tdb sequence number.
+ *
+ * Only makes sense if the writers opened with TDB_SEQNUM set. Note that this
+ * sequence number will wrap quite quickly, so it should only be used for a
+ * 'has something changed' test, not for code that relies on the count of the
+ * number of changes made. If you want a counter then use a tdb record.
+ *
+ * The aim of this sequence number is to allow for a very lightweight test of a
+ * possible tdb change.
+ *
+ * @param[in]  tdb      The database to get the sequence number from.
+ *
+ * @return              The sequence number or 0.
+ *
+ * @see tdb_open()
+ * @see tdb_enable_seqnum()
+ */
+int tdb_get_seqnum(struct tdb_context *tdb);
+
+/**
+ * @brief Get the hash size.
+ *
+ * @param[in]  tdb      The database to get the hash size from.
+ *
+ * @return              The hash size.
+ */
+int tdb_hash_size(struct tdb_context *tdb);
+
+/**
+ * @brief Get the map size.
+ *
+ * @param[in]  tdb     The database to get the map size from.
+ *
+ * @return             The map size.
+ */
+size_t tdb_map_size(struct tdb_context *tdb);
+
+/**
+ * @brief Get the tdb flags set during open.
+ *
+ * @param[in]  tdb      The database to get the flags form.
+ *
+ * @return              The flags set to on the database.
+ */
+int tdb_get_flags(struct tdb_context *tdb);
+
+/**
+ * @brief Add flags to the database.
+ *
+ * @param[in]  tdb      The database to add the flags.
+ *
+ * @param[in]  flag     The tdb flags to add.
+ */
+void tdb_add_flags(struct tdb_context *tdb, unsigned flag);
+
+/**
+ * @brief Remove flags from the database.
+ *
+ * @param[in]  tdb      The database to remove the flags.
+ *
+ * @param[in]  flag     The tdb flags to remove.
+ */
+void tdb_remove_flags(struct tdb_context *tdb, unsigned flag);
+
+/**
+ * @brief Enable sequence number handling on an open tdb.
+ *
+ * @param[in]  tdb      The database to enable sequence number handling.
+ *
+ * @see tdb_get_seqnum()
+ */
+void tdb_enable_seqnum(struct tdb_context *tdb);
+
+/**
+ * @brief Increment the tdb sequence number.
+ *
+ * This only works if the tdb has been opened using the TDB_SEQNUM flag or
+ * enabled useing tdb_enable_seqnum().
+ *
+ * @param[in]  tdb      The database to increment the sequence number.
+ *
+ * @see tdb_enable_seqnum()
+ * @see tdb_get_seqnum()
+ */
+void tdb_increment_seqnum_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Create a hash of the key.
+ *
+ * @param[in]  key      The key to hash
+ *
+ * @return              The hash.
+ */
+unsigned int tdb_jenkins_hash(TDB_DATA *key);
+
+/**
+ * @brief Check the consistency of the database.
+ *
+ * This check the consistency of the database calling back the check function
+ * (if non-NULL) on each record.  If some consistency check fails, or the
+ * supplied check function returns -1, tdb_check returns -1, otherwise 0.
+ *
+ * @note The logging function (if set) will be called with additional
+ * information on the corruption found.
+ *
+ * @param[in]  tdb      The database to check.
+ *
+ * @param[in]  check    The check function to use.
+ *
+ * @param[in]  private_data the private data to pass to the check function.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_check(struct tdb_context *tdb,
+             int (*check) (TDB_DATA key, TDB_DATA data, void *private_data),
              void *private_data);
 
+/**
+ * @brief Dump all possible records in a corrupt database.
+ *
+ * This is the only way to get data out of a database where tdb_check() fails.
+ * It will call walk() with anything which looks like a database record; this
+ * may well include invalid, incomplete or duplicate records.
+ *
+ * @param[in]  tdb      The database to check.
+ *
+ * @param[in]  walk     The walk function to use.
+ *
+ * @param[in]  private_data the private data to pass to the walk function.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_rescue(struct tdb_context *tdb,
+              void (*walk) (TDB_DATA key, TDB_DATA data, void *private_data),
+              void *private_data);
+
+/* @} ******************************************************************/
+
 /* Low level locking functions: use with care */
-_PUBLIC_ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_transaction_write_lock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_write_unlock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_write_lock_mark(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_write_lock_unmark(struct tdb_context *tdb);
-
-_PUBLIC_ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
+int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
+
+void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
 
 /* wipe and repack */
-_PUBLIC_ int tdb_wipe_all(struct tdb_context *tdb);
-_PUBLIC_ int tdb_repack(struct tdb_context *tdb);
+int tdb_wipe_all(struct tdb_context *tdb);
+int tdb_repack(struct tdb_context *tdb);
 
 /* Debug functions. Not used in production. */
-_PUBLIC_ void tdb_dump_all(struct tdb_context *tdb);
-_PUBLIC_ int tdb_printfreelist(struct tdb_context *tdb);
-_PUBLIC_ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries);
-_PUBLIC_ int tdb_freelist_size(struct tdb_context *tdb);
+void tdb_dump_all(struct tdb_context *tdb);
+int tdb_printfreelist(struct tdb_context *tdb);
+int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries);
+int tdb_freelist_size(struct tdb_context *tdb);
+char *tdb_summary(struct tdb_context *tdb);
 
-_PUBLIC_ extern TDB_DATA tdb_null;
+extern TDB_DATA tdb_null;
 
 #ifdef  __cplusplus
 }
diff --git a/lib/tdb/man/tdbbackup.8.xml b/lib/tdb/man/tdbbackup.8.xml
new file mode 100644 (file)
index 0000000..c15cc14
--- /dev/null
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="tdbbackup.8">
+
+<refmeta>
+       <refentrytitle>tdbbackup</refentrytitle>
+       <manvolnum>8</manvolnum>
+       <refmiscinfo class="source">Samba</refmiscinfo>
+       <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+       <refmiscinfo class="version">3.6</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+       <refname>tdbbackup</refname>
+       <refpurpose>tool for backing up and for validating the integrity of samba .tdb files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>tdbbackup</command>
+               <arg choice="opt">-s suffix</arg>
+               <arg choice="opt">-v</arg>
+               <arg choice="opt">-h</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle>
+       <manvolnum>1</manvolnum></citerefentry> suite.</para>
+
+       <para><command>tdbbackup</command> is a tool that may be used to backup samba .tdb
+       files. This tool may also be used to verify the integrity of the .tdb files prior
+       to samba startup or during normal operation. If it finds file damage and it finds
+       a prior backup the backup file will be restored.
+       </para>
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+
+               <varlistentry>
+               <term>-h</term>
+               <listitem><para>
+               Get help information.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>-s suffix</term>
+               <listitem><para>
+               The <command>-s</command> option allows the adminisistrator to specify a file
+               backup extension. This way it is possible to keep a history of tdb backup
+               files by using a new suffix for each backup.
+               </para> </listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>-v</term>
+               <listitem><para>
+               The <command>-v</command> will check the database for damages (currupt data)
+               which if detected causes the backup to be restored.
+               </para></listitem>
+               </varlistentry>
+
+       </variablelist>
+</refsect1>
+
+
+<refsect1>
+       <title>COMMANDS</title>
+
+       <para><emphasis>GENERAL INFORMATION</emphasis></para>
+
+       <para>
+       The <command>tdbbackup</command> utility can safely be run at any time. It was designed so
+       that it can be used at any time to validate the integrity of tdb files, even during Samba
+       operation. Typical usage for the command will be:
+       </para>
+
+       <para>tdbbackup [-s suffix] *.tdb</para>
+
+       <para>
+       Before restarting samba the following command may be run to validate .tdb files:
+       </para>
+
+       <para>tdbbackup -v [-s suffix] *.tdb</para>
+
+       <para>
+       Note that Samba 4 can use .ntdb files instead, so you should
+       use <command>ntdbbackup</command> on those files.
+       </para>
+
+       <para>
+       Samba .tdb files are stored in various locations, be sure to run backup all
+       .tdb file on the system. Important files includes:
+       </para>
+
+       <itemizedlist>
+               <listitem><para>
+               <command>secrets.tdb</command> - usual location is in the /usr/local/samba/private
+               directory, or on some systems in /etc/samba.
+               </para></listitem>
+
+               <listitem><para>
+               <command>passdb.tdb</command> - usual location is in the /usr/local/samba/private
+               directory, or on some systems in /etc/samba.
+               </para></listitem>
+
+               <listitem><para>
+               <command>*.tdb</command> located in the /usr/local/samba/var directory or on some
+               systems in the /var/cache or /var/lib/samba directories.
+               </para></listitem>
+       </itemizedlist>
+
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 3 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para>
+       The original Samba software and related utilities were created by Andrew Tridgell.
+       Samba is now developed by the Samba Team as an Open Source project similar to the way
+       the Linux kernel is developed.
+       </para>
+
+       <para>The tdbbackup man page was written by John H Terpstra.</para>
+</refsect1>
+
+</refentry>
diff --git a/lib/tdb/man/tdbdump.8.xml b/lib/tdb/man/tdbdump.8.xml
new file mode 100644 (file)
index 0000000..25fd432
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="tdbdump.8">
+
+<refmeta>
+       <refentrytitle>tdbdump</refentrytitle>
+       <manvolnum>8</manvolnum>
+       <refmiscinfo class="source">Samba</refmiscinfo>
+       <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+       <refmiscinfo class="version">3.6</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+       <refname>tdbdump</refname>
+       <refpurpose>tool for printing the contents of a TDB file</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>tdbdump</command>
+               <arg choice="opt">-k <replaceable>keyname</replaceable></arg>
+               <arg choice="opt">-e</arg>
+               <arg choice="opt">-h</arg>
+               <arg choice="req">filename</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle>
+       <manvolnum>1</manvolnum></citerefentry> suite.</para>
+
+       <para><command>tdbdump</command> is a very simple utility that 'dumps' the
+               contents of a TDB (Trivial DataBase) file to standard output in a
+               human-readable format.
+       </para>
+
+       <para>This tool can be used when debugging problems with TDB files. It is
+               intended for those who are somewhat familiar with Samba internals.
+       </para>
+</refsect1>
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+
+               <varlistentry>
+               <term>-h</term>
+               <listitem><para>
+               Get help information.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>-k <replaceable>keyname</replaceable></term>
+               <listitem><para>
+               The <command>-k</command> option restricts dumping to a single key, if found.
+               </para> </listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>-e</term>
+               <listitem><para>
+               The <command>-e</command> tries to dump out from a corrupt database.  Naturally, such a dump is unreliable, at best.
+               </para></listitem>
+               </varlistentry>
+
+       </variablelist>
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 3 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para>
+       The original Samba software and related utilities were created by Andrew Tridgell.
+       Samba is now developed by the Samba Team as an Open Source project similar to the way
+       the Linux kernel is developed.
+       </para>
+
+       <para>The tdbdump man page was written by Jelmer Vernooij.</para>
+</refsect1>
+
+</refentry>
diff --git a/lib/tdb/man/tdbrestore.8.xml b/lib/tdb/man/tdbrestore.8.xml
new file mode 100644 (file)
index 0000000..64c0ba2
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="tdbrestore.8">
+
+<refmeta>
+       <refentrytitle>tdbrestore</refentrytitle>
+       <manvolnum>8</manvolnum>
+       <refmiscinfo class="source">Samba</refmiscinfo>
+       <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+       <refmiscinfo class="version">3.6</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+       <refname>tdbrestore</refname>
+       <refpurpose>tool for creating a TDB file out of a tdbdump output</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>tdbrestore</command>
+               <arg choice="req">tdbfilename</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle>
+       <manvolnum>1</manvolnum></citerefentry> suite.</para>
+
+       <para><command>tdbrestore</command> is a very simple utility that 'restores' the
+               contents of dump file into TDB (Trivial DataBase) file. The dump file is obtained from the tdbdump
+               command.
+       </para>
+
+       <para>This tool wait on the standard input for the content of the dump and will write the tdb in the tdbfilename
+  parameter.
+       </para>
+       <para>This tool can be used for unpacking the content of tdb as backup mean.
+       </para>
+</refsect1>
+
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 3 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para>
+       The original Samba software and related utilities were created by Andrew Tridgell.
+       Samba is now developed by the Samba Team as an Open Source project similar to the way
+       the Linux kernel is developed.
+
+        This tool was initially written by Volker Lendecke based on an
+        idea by Simon McVittie.
+       </para>
+
+       <para>The tdbrestore man page was written by Matthieu Patou.</para>
+</refsect1>
+
+</refentry>
diff --git a/lib/tdb/man/tdbtool.8.xml b/lib/tdb/man/tdbtool.8.xml
new file mode 100644 (file)
index 0000000..bc5c001
--- /dev/null
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="tdbtool.8">
+
+<refmeta>
+       <refentrytitle>tdbtool</refentrytitle>
+       <manvolnum>8</manvolnum>
+       <refmiscinfo class="source">Samba</refmiscinfo>
+       <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+       <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+       <refname>tdbtool</refname>
+       <refpurpose>manipulate the contents TDB files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+
+       <cmdsynopsis>
+               <command>tdbtool</command>
+       </cmdsynopsis>
+
+       <cmdsynopsis>
+               <command>tdbtool</command>
+               <arg choice="plain">
+               <replaceable>TDBFILE</replaceable>
+               </arg>
+               <arg rep="repeat" choice="opt">
+               <replaceable>COMMANDS</replaceable>
+               </arg>
+       </cmdsynopsis>
+
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>This tool is part of the
+       <citerefentry><refentrytitle>samba</refentrytitle>
+       <manvolnum>1</manvolnum></citerefentry> suite.</para>
+
+       <para><command>tdbtool</command> a tool for displaying and
+       altering the contents of Samba TDB (Trivial DataBase) files. Each
+       of the commands listed below can be entered interactively or
+       provided on the command line.</para>
+
+</refsect1>
+
+
+<refsect1>
+       <title>COMMANDS</title>
+
+       <variablelist>
+
+               <varlistentry>
+               <term><option>create</option>
+               <replaceable>TDBFILE</replaceable></term>
+               <listitem><para>Create a new database named
+               <replaceable>TDBFILE</replaceable>.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>open</option>
+               <replaceable>TDBFILE</replaceable></term>
+               <listitem><para>Open an existing database named
+               <replaceable>TDBFILE</replaceable>.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>erase</option></term>
+               <listitem><para>Erase the current database.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>dump</option></term>
+               <listitem><para>Dump the current database as strings.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>cdump</option></term>
+               <listitem><para>Dump the current database as connection records.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>keys</option></term>
+               <listitem><para>Dump the current database keys as strings.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>hexkeys</option></term>
+               <listitem><para>Dump the current database keys as hex values.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>info</option></term>
+               <listitem><para>Print summary information about the
+               current database.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>insert</option>
+               <replaceable>KEY</replaceable>
+               <replaceable>DATA</replaceable>
+               </term>
+               <listitem><para>Insert a record into the
+               current database.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>move</option>
+               <replaceable>KEY</replaceable>
+               <replaceable>TDBFILE</replaceable>
+               </term>
+               <listitem><para>Move a record from the
+               current database into <replaceable>TDBFILE</replaceable>.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>store</option>
+               <replaceable>KEY</replaceable>
+               <replaceable>DATA</replaceable>
+               </term>
+               <listitem><para>Store (replace) a record in the
+               current database.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>show</option>
+               <replaceable>KEY</replaceable>
+               </term>
+               <listitem><para>Show a record by key.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>delete</option>
+               <replaceable>KEY</replaceable>
+               </term>
+               <listitem><para>Delete a record by key.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>list</option>
+               </term>
+               <listitem><para>Print the current database hash table and free list.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>free</option>
+               </term>
+               <listitem><para>Print the current database and free list.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term><option>!</option>
+               <replaceable>COMMAND</replaceable>
+               </term>
+               <listitem><para>Execute the given system command.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>
+               <option>first</option>
+               </term>
+               <listitem><para>Print the first record in the current database.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>
+               <option>next</option>
+               </term>
+               <listitem><para>Print the next record in the current database.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>
+               <option>check</option>
+               </term>
+               <listitem><para>Check the integrity of the current database.
+               </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>
+               <option>quit</option>
+               </term>
+               <listitem><para>Exit <command>tdbtool</command>.
+               </para></listitem>
+               </varlistentry>
+
+       </variablelist>
+</refsect1>
+
+<refsect1>
+       <title>CAVEATS</title>
+       <para>The contents of the Samba TDB files are private
+       to the implementation and should not be altered with
+       <command>tdbtool</command>.
+       </para>
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+       <para>This man page is correct for version 3.0.25 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para> The original Samba software and related utilities were
+       created by Andrew Tridgell.  Samba is now developed by the
+       Samba Team as an Open Source project similar to the way the
+       Linux kernel is developed.</para>
+</refsect1>
+
+</refentry>
index b857438e16ec0ef6185be78f0a28275be512f6a1..cf77a2527466ae8ad7440c5b216240bf36314143 100644 (file)
@@ -9,7 +9,7 @@
      ** 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
@@ -41,7 +41,7 @@ typedef struct {
        bool closed;
 } PyTdbObject;
 
-PyAPI_DATA(PyTypeObject) PyTdb;
+staticforward PyTypeObject PyTdb;
 
 static void PyErr_SetTDBError(TDB_CONTEXT *tdb)
 {
@@ -75,6 +75,20 @@ static PyObject *PyString_FromTDB_DATA(TDB_DATA data)
                return NULL; \
        }
 
+#define PyErr_TDB_RAISE_IF_CLOSED(self) \
+       if (self->closed) {                                             \
+               PyErr_SetObject(PyExc_RuntimeError,                             \
+                               Py_BuildValue("(i,s)", TDB_ERR_IO, "Database is already closed")); \
+               return NULL;                                            \
+       }
+
+#define PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self) \
+       if (self->closed) {                                             \
+               PyErr_SetObject(PyExc_RuntimeError,                             \
+                               Py_BuildValue("(i,s)", TDB_ERR_IO, "Database is already closed")); \
+               return -1;                                              \
+       }
+
 static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
 {
        char *name = NULL;
@@ -109,56 +123,81 @@ static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwarg
 
 static PyObject *obj_transaction_cancel(PyTdbObject *self)
 {
-       int ret = tdb_transaction_cancel(self->ctx);
+       int ret;
+
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
+       ret = tdb_transaction_cancel(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_transaction_commit(PyTdbObject *self)
 {
-       int ret = tdb_transaction_commit(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_transaction_commit(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_transaction_prepare_commit(PyTdbObject *self)
 {
-       int ret = tdb_transaction_prepare_commit(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_transaction_prepare_commit(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_transaction_start(PyTdbObject *self)
 {
-       int ret = tdb_transaction_start(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_transaction_start(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_reopen(PyTdbObject *self)
 {
-       int ret = tdb_reopen(self->ctx);
-       PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_reopen(self->ctx);
+       if (ret != 0) {
+               self->closed = true;
+               PyErr_SetObject(PyExc_RuntimeError,
+                               Py_BuildValue("(i,s)",
+                                             TDB_ERR_IO,
+                                             "Failed to reopen database"));
+               return NULL;
+       }
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_lockall(PyTdbObject *self)
 {
-       int ret = tdb_lockall(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_lockall(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_unlockall(PyTdbObject *self)
 {
-       int ret = tdb_unlockall(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_unlockall(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_lockall_read(PyTdbObject *self)
 {
-       int ret = tdb_lockall_read(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_lockall_read(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
@@ -177,7 +216,13 @@ static PyObject *obj_close(PyTdbObject *self)
                Py_RETURN_NONE;
        ret = tdb_close(self->ctx);
        self->closed = true;
-       PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
+       if (ret != 0) {
+               PyErr_SetObject(PyExc_RuntimeError,
+                               Py_BuildValue("(i,s)",
+                                             TDB_ERR_IO,
+                                             "Failed to close database"));
+               return NULL;
+       }
        Py_RETURN_NONE;
 }
 
@@ -185,10 +230,15 @@ static PyObject *obj_get(PyTdbObject *self, PyObject *args)
 {
        TDB_DATA key;
        PyObject *py_key;
+
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "O", &py_key))
                return NULL;
 
        key = PyString_AsTDB_DATA(py_key);
+       if (!key.dptr)
+               return NULL;
 
        return PyString_FromTDB_DATA(tdb_fetch(self->ctx, key));
 }
@@ -198,11 +248,18 @@ static PyObject *obj_append(PyTdbObject *self, PyObject *args)
        TDB_DATA key, data;
        PyObject *py_key, *py_data;
        int ret;
+
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data))
                return NULL;
 
        key = PyString_AsTDB_DATA(py_key);
+       if (!key.dptr)
+               return NULL;
        data = PyString_AsTDB_DATA(py_data);
+       if (!data.dptr)
+               return NULL;
 
        ret = tdb_append(self->ctx, key, data);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
@@ -211,6 +268,8 @@ static PyObject *obj_append(PyTdbObject *self, PyObject *args)
 
 static PyObject *obj_firstkey(PyTdbObject *self)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        return PyString_FromTDB_DATA(tdb_firstkey(self->ctx));
 }
 
@@ -218,10 +277,14 @@ static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args)
 {
        TDB_DATA key;
        PyObject *py_key;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "O", &py_key))
                return NULL;
 
        key = PyString_AsTDB_DATA(py_key);
+       if (!key.dptr)
+               return NULL;
        
        return PyString_FromTDB_DATA(tdb_nextkey(self->ctx, key));
 }
@@ -231,10 +294,14 @@ static PyObject *obj_delete(PyTdbObject *self, PyObject *args)
        TDB_DATA key;
        PyObject *py_key;
        int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "O", &py_key))
                return NULL;
 
        key = PyString_AsTDB_DATA(py_key);
+       if (!key.dptr)
+               return NULL;
        ret = tdb_delete(self->ctx, key);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
@@ -245,10 +312,14 @@ static PyObject *obj_has_key(PyTdbObject *self, PyObject *args)
        TDB_DATA key;
        int ret;
        PyObject *py_key;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "O", &py_key))
                return NULL;
 
        key = PyString_AsTDB_DATA(py_key);
+       if (!key.dptr)
+               return NULL;
        ret = tdb_exists(self->ctx, key);
        if (ret != TDB_ERR_NOEXIST) {
                PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
@@ -264,11 +335,17 @@ static PyObject *obj_store(PyTdbObject *self, PyObject *args)
        int flag = TDB_REPLACE;
        PyObject *py_key, *py_value;
 
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag))
                return NULL;
 
        key = PyString_AsTDB_DATA(py_key);
+       if (!key.dptr)
+               return NULL;
        value = PyString_AsTDB_DATA(py_value);
+       if (!value.dptr)
+               return NULL;
 
        ret = tdb_store(self->ctx, key, value, flag);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
@@ -279,6 +356,8 @@ static PyObject *obj_add_flags(PyTdbObject *self, PyObject *args)
 {
        unsigned flags;
 
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "I", &flags))
                return NULL;
 
@@ -290,6 +369,8 @@ static PyObject *obj_remove_flags(PyTdbObject *self, PyObject *args)
 {
        unsigned flags;
 
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        if (!PyArg_ParseTuple(args, "I", &flags))
                return NULL;
 
@@ -334,6 +415,8 @@ static PyObject *tdb_object_iter(PyTdbObject *self)
 {
        PyTdbIteratorObject *ret;       
 
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+
        ret = PyObject_New(PyTdbIteratorObject, &PyTdbIterator);
        if (!ret)
                return NULL;
@@ -345,26 +428,32 @@ static PyObject *tdb_object_iter(PyTdbObject *self)
 
 static PyObject *obj_clear(PyTdbObject *self)
 {
-       int ret = tdb_wipe_all(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_wipe_all(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_repack(PyTdbObject *self)
 {
-       int ret = tdb_repack(self->ctx);
+       int ret;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
+       ret = tdb_repack(self->ctx);
        PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_enable_seqnum(PyTdbObject *self)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        tdb_enable_seqnum(self->ctx);
        Py_RETURN_NONE;
 }
 
 static PyObject *obj_increment_seqnum_nonblock(PyTdbObject *self)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        tdb_increment_seqnum_nonblock(self->ctx);
        Py_RETURN_NONE;
 }
@@ -418,11 +507,13 @@ static PyMethodDef tdb_object_methods[] = {
 
 static PyObject *obj_get_hash_size(PyTdbObject *self, void *closure)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        return PyInt_FromLong(tdb_hash_size(self->ctx));
 }
 
 static int obj_set_max_dead(PyTdbObject *self, PyObject *max_dead, void *closure)
 {
+       PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self);
        if (!PyInt_Check(max_dead))
                return -1;
        tdb_set_max_dead(self->ctx, PyInt_AsLong(max_dead));
@@ -431,26 +522,31 @@ static int obj_set_max_dead(PyTdbObject *self, PyObject *max_dead, void *closure
 
 static PyObject *obj_get_map_size(PyTdbObject *self, void *closure)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        return PyInt_FromLong(tdb_map_size(self->ctx));
 }
 
 static PyObject *obj_get_freelist_size(PyTdbObject *self, void *closure)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        return PyInt_FromLong(tdb_freelist_size(self->ctx));
 }
 
 static PyObject *obj_get_flags(PyTdbObject *self, void *closure)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        return PyInt_FromLong(tdb_get_flags(self->ctx));
 }
 
 static PyObject *obj_get_filename(PyTdbObject *self, void *closure)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        return PyString_FromString(tdb_name(self->ctx));
 }
 
 static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        return PyInt_FromLong(tdb_get_seqnum(self->ctx));
 }
 
@@ -468,6 +564,7 @@ static PyGetSetDef tdb_object_getsetters[] = {
 
 static PyObject *tdb_object_repr(PyTdbObject *self)
 {
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        if (tdb_get_flags(self->ctx) & TDB_INTERNAL) {
                return PyString_FromString("Tdb(<internal>)");
        } else {
@@ -479,12 +576,13 @@ static void tdb_object_dealloc(PyTdbObject *self)
 {
        if (!self->closed)
                tdb_close(self->ctx);
-       PyObject_Del(self);
+       self->ob_type->tp_free(self);
 }
 
 static PyObject *obj_getitem(PyTdbObject *self, PyObject *key)
 {
        TDB_DATA tkey, val;
+       PyErr_TDB_RAISE_IF_CLOSED(self);
        if (!PyString_Check(key)) {
                PyErr_SetString(PyExc_TypeError, "Expected string as key");
                return NULL;
@@ -506,6 +604,7 @@ static int obj_setitem(PyTdbObject *self, PyObject *key, PyObject *value)
 {
        TDB_DATA tkey, tval;
        int ret;
+       PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self);
        if (!PyString_Check(key)) {
                PyErr_SetString(PyExc_TypeError, "Expected string as key");
                return -1;
@@ -538,8 +637,8 @@ static PyMappingMethods tdb_object_mapping = {
        .mp_subscript = (binaryfunc)obj_getitem,
        .mp_ass_subscript = (objobjargproc)obj_setitem,
 };
-PyTypeObject PyTdb = {
-       .tp_name = "Tdb",
+static PyTypeObject PyTdb = {
+       .tp_name = "tdb.Tdb",
        .tp_basicsize = sizeof(PyTdbObject),
        .tp_methods = tdb_object_methods,
        .tp_getset = tdb_object_getsetters,
@@ -558,6 +657,7 @@ static PyMethodDef tdb_methods[] = {
        { NULL }
 };
 
+void inittdb(void);
 void inittdb(void)
 {
        PyObject *m;
@@ -568,7 +668,8 @@ void inittdb(void)
        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.");
+       m = Py_InitModule3("tdb", tdb_methods,
+               "simple key-value database that supports multiple writers.");
        if (m == NULL)
                return;
 
index 615de494b51c83ae145bf991913f11e5c7b181da..7e295a8c95295e0f6d5122e69ce776c62a8db3b1 100644 (file)
@@ -12,19 +12,32 @@ import os, tempfile
 
 
 class OpenTdbTests(TestCase):
-    def test_nonexistant_read(self):
-        self.assertRaises(IOError, tdb.Tdb, "/some/nonexistant/file", 0, tdb.DEFAULT, os.O_RDWR)
+
+    def test_nonexistent_read(self):
+        self.assertRaises(IOError, tdb.Tdb, "/some/nonexistent/file", 0,
+                tdb.DEFAULT, os.O_RDWR)
 
 class CloseTdbTests(TestCase):
 
     def test_double_close(self):
-        self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR)
+        # No hash size in tdb2.
+        if tdb.__version__.startswith("2"):
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
+        else:
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
         self.assertNotEqual(None, self.tdb)
 
         # ensure that double close does not crash python
         self.tdb.close()
         self.tdb.close()
 
+        # Check that further operations do not crash python
+        self.assertRaises(RuntimeError, lambda: self.tdb.transaction_start())
+
+        self.assertRaises(RuntimeError, lambda: self.tdb["bar"])
+
 
 class InternalTdbTests(TestCase):
 
@@ -36,9 +49,15 @@ class InternalTdbTests(TestCase):
 
 
 class SimpleTdbTests(TestCase):
+
     def setUp(self):
         super(SimpleTdbTests, self).setUp()
-        self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR)
+        if tdb.__version__.startswith("2"):
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
+        else:
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
         self.assertNotEqual(None, self.tdb)
 
     def tearDown(self):
@@ -51,7 +70,8 @@ class SimpleTdbTests(TestCase):
         self.tdb.lock_all()
 
     def test_max_dead(self):
-        self.tdb.max_dead = 20
+        if not tdb.__version__.startswith("2"):
+            self.tdb.max_dead = 20
 
     def test_unlockall(self):
         self.tdb.lock_all()
@@ -62,7 +82,8 @@ class SimpleTdbTests(TestCase):
         self.tdb.read_unlock_all()
 
     def test_reopen(self):
-        self.tdb.reopen()
+        if not tdb.__version__.startswith("2"):
+            self.tdb.reopen()
 
     def test_store(self):
         self.tdb.store("bar", "bla")
@@ -70,7 +91,8 @@ class SimpleTdbTests(TestCase):
 
     def test_getitem(self):
         self.tdb["bar"] = "foo"
-        self.tdb.reopen()
+        if not tdb.__version__.startswith("2"):
+            self.tdb.reopen()
         self.assertEquals("foo", self.tdb["bar"])
 
     def test_delete(self):
@@ -86,13 +108,16 @@ class SimpleTdbTests(TestCase):
         self.assertRaises(KeyError, lambda: self.tdb["bla"])
 
     def test_hash_size(self):
-        self.tdb.hash_size
+        if not tdb.__version__.startswith("2"):
+            self.tdb.hash_size
 
     def test_map_size(self):
-        self.tdb.map_size
+        if not tdb.__version__.startswith("2"):
+            self.tdb.map_size
 
     def test_freelist_size(self):
-        self.tdb.freelist_size
+        if not tdb.__version__.startswith("2"):
+            self.tdb.freelist_size
 
     def test_name(self):
         self.tdb.filename
@@ -100,7 +125,9 @@ class SimpleTdbTests(TestCase):
     def test_iterator(self):
         self.tdb["bla"] = "1"
         self.tdb["brainslug"] = "2"
-        self.assertEquals(["bla", "brainslug"], list(self.tdb))
+        l = list(self.tdb)
+        l.sort()
+        self.assertEquals(["bla", "brainslug"], l)
 
     def test_transaction_cancel(self):
         self.tdb["bloe"] = "2"
@@ -138,17 +165,19 @@ class SimpleTdbTests(TestCase):
         self.assertEquals(0, len(list(self.tdb)))
 
     def test_repack(self):
-        self.tdb["foo"] = "abc"
-        self.tdb["bar"] = "def"
-        del self.tdb["foo"]
-        self.tdb.repack()
+        if not tdb.__version__.startswith("2"):
+            self.tdb["foo"] = "abc"
+            self.tdb["bar"] = "def"
+            del self.tdb["foo"]
+            self.tdb.repack()
 
     def test_seqnum(self):
-        self.tdb.enable_seqnum()
-        seq1 = self.tdb.seqnum
-        self.tdb.increment_seqnum_nonblock()
-        seq2 = self.tdb.seqnum
-        self.assertEquals(seq2-seq1, 1)
+        if not tdb.__version__.startswith("2"):
+            self.tdb.enable_seqnum()
+            seq1 = self.tdb.seqnum
+            self.tdb.increment_seqnum_nonblock()
+            seq2 = self.tdb.seqnum
+            self.assertEquals(seq2-seq1, 1)
 
     def test_len(self):
         self.assertEquals(0, len(list(self.tdb)))
@@ -156,8 +185,12 @@ class SimpleTdbTests(TestCase):
         self.assertEquals(1, len(list(self.tdb)))
 
     def test_add_flags(self):
-        self.tdb.add_flags(tdb.NOMMAP)
-        self.tdb.remove_flags(tdb.NOMMAP)
+        if tdb.__version__.startswith("2"):
+            self.tdb.add_flag(tdb.NOMMAP)
+            self.tdb.remove_flag(tdb.NOMMAP)
+        else:
+            self.tdb.add_flags(tdb.NOMMAP)
+            self.tdb.remove_flags(tdb.NOMMAP)
 
 
 class VersionTests(TestCase):
index 6f8f553736c31df3a4f6976c01df5b3e72b825b6..b78419ea7845b28d01920e41ec5379ab15f4a0ef 100644 (file)
@@ -6,6 +6,6 @@ includedir=@includedir@
 Name: tdb
 Description: A trivial database
 Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -ltdb
+Libs: @LIB_RPATH@ -L${libdir} -ltdb
 Cflags: -I${includedir} 
 URL: http://tdb.samba.org/
diff --git a/lib/tdb/test/external-agent.c b/lib/tdb/test/external-agent.c
new file mode 100644 (file)
index 0000000..8140e70
--- /dev/null
@@ -0,0 +1,198 @@
+#include "external-agent.h"
+#include "lock-tracking.h"
+#include "logging.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include "../common/tdb_private.h"
+#include "tap-interface.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+static struct tdb_context *tdb;
+
+static enum agent_return do_operation(enum operation op, const char *name)
+{
+       TDB_DATA k;
+       enum agent_return ret;
+       TDB_DATA data;
+
+       if (op != OPEN && op != OPEN_WITH_CLEAR_IF_FIRST && !tdb) {
+               diag("external: No tdb open!");
+               return OTHER_FAILURE;
+       }
+
+       k.dptr = (void *)name;
+       k.dsize = strlen(name);
+
+       locking_would_block = 0;
+       switch (op) {
+       case OPEN:
+               if (tdb) {
+                       diag("Already have tdb %s open", tdb_name(tdb));
+                       return OTHER_FAILURE;
+               }
+               tdb = tdb_open_ex(name, 0, TDB_DEFAULT, O_RDWR, 0,
+                                 &taplogctx, NULL);
+               if (!tdb) {
+                       if (!locking_would_block)
+                               diag("Opening tdb gave %s", strerror(errno));
+                       ret = OTHER_FAILURE;
+               } else
+                       ret = SUCCESS;
+               break;
+       case OPEN_WITH_CLEAR_IF_FIRST:
+               if (tdb)
+                       return OTHER_FAILURE;
+               tdb = tdb_open_ex(name, 0, TDB_CLEAR_IF_FIRST, O_RDWR, 0,
+                                 &taplogctx, NULL);
+               ret = tdb ? SUCCESS : OTHER_FAILURE;
+               break;
+       case TRANSACTION_START:
+               ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
+               break;
+       case FETCH:
+               data = tdb_fetch(tdb, k);
+               if (data.dptr == NULL) {
+                       if (tdb_error(tdb) == TDB_ERR_NOEXIST)
+                               ret = FAILED;
+                       else
+                               ret = OTHER_FAILURE;
+               } else if (data.dsize != k.dsize
+                          || memcmp(data.dptr, k.dptr, k.dsize) != 0) {
+                       ret = OTHER_FAILURE;
+               } else {
+                       ret = SUCCESS;
+               }
+               free(data.dptr);
+               break;
+       case STORE:
+               ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
+               break;
+       case TRANSACTION_COMMIT:
+               ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE;
+               break;
+       case CHECK:
+               ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
+               break;
+       case NEEDS_RECOVERY:
+               ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
+               break;
+       case CLOSE:
+               ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
+               tdb = NULL;
+               break;
+       default:
+               ret = OTHER_FAILURE;
+       }
+
+       if (locking_would_block)
+               ret = WOULD_HAVE_BLOCKED;
+
+       return ret;
+}
+
+struct agent {
+       int cmdfd, responsefd;
+};
+
+/* Do this before doing any tdb stuff.  Return handle, or NULL. */
+struct agent *prepare_external_agent(void)
+{
+       int pid, ret;
+       int command[2], response[2];
+       char name[1+PATH_MAX];
+
+       if (pipe(command) != 0 || pipe(response) != 0) {
+               fprintf(stderr, "pipe failed: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               fprintf(stderr, "fork failed: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if (pid != 0) {
+               struct agent *agent = malloc(sizeof(*agent));
+
+               close(command[0]);
+               close(response[1]);
+               agent->cmdfd = command[1];
+               agent->responsefd = response[0];
+               return agent;
+       }
+
+       close(command[1]);
+       close(response[0]);
+
+       /* We want to fail, not block. */
+       nonblocking_locks = true;
+       log_prefix = "external: ";
+       while ((ret = read(command[0], name, sizeof(name))) > 0) {
+               enum agent_return result;
+
+               result = do_operation(name[0], name+1);
+               if (write(response[1], &result, sizeof(result))
+                   != sizeof(result))
+                       abort();
+       }
+       exit(0);
+}
+
+/* Ask the external agent to try to do an operation. */
+enum agent_return external_agent_operation(struct agent *agent,
+                                          enum operation op,
+                                          const char *name)
+{
+       enum agent_return res;
+       unsigned int len;
+       char *string;
+
+       if (!name)
+               name = "";
+       len = 1 + strlen(name) + 1;
+       string = malloc(len);
+
+       string[0] = op;
+       strcpy(string+1, name);
+
+       if (write(agent->cmdfd, string, len) != len
+           || read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
+               res = AGENT_DIED;
+
+       free(string);
+       return res;
+}
+
+const char *agent_return_name(enum agent_return ret)
+{
+       return ret == SUCCESS ? "SUCCESS"
+               : ret == WOULD_HAVE_BLOCKED ? "WOULD_HAVE_BLOCKED"
+               : ret == AGENT_DIED ? "AGENT_DIED"
+               : ret == FAILED ? "FAILED"
+               : ret == OTHER_FAILURE ? "OTHER_FAILURE"
+               : "**INVALID**";
+}
+
+const char *operation_name(enum operation op)
+{
+       switch (op) {
+       case OPEN: return "OPEN";
+       case OPEN_WITH_CLEAR_IF_FIRST: return "OPEN_WITH_CLEAR_IF_FIRST";
+       case TRANSACTION_START: return "TRANSACTION_START";
+       case FETCH: return "FETCH";
+       case STORE: return "STORE";
+       case TRANSACTION_COMMIT: return "TRANSACTION_COMMIT";
+       case CHECK: return "CHECK";
+       case NEEDS_RECOVERY: return "NEEDS_RECOVERY";
+       case CLOSE: return "CLOSE";
+       }
+       return "**INVALID**";
+}
diff --git a/lib/tdb/test/external-agent.h b/lib/tdb/test/external-agent.h
new file mode 100644 (file)
index 0000000..dffdca9
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef TDB_TEST_EXTERNAL_AGENT_H
+#define TDB_TEST_EXTERNAL_AGENT_H
+
+/* For locking tests, we need a different process to try things at
+ * various times. */
+enum operation {
+       OPEN,
+       OPEN_WITH_CLEAR_IF_FIRST,
+       TRANSACTION_START,
+       FETCH,
+       STORE,
+       TRANSACTION_COMMIT,
+       CHECK,
+       NEEDS_RECOVERY,
+       CLOSE,
+};
+
+/* Do this before doing any tdb stuff.  Return handle, or -1. */
+struct agent *prepare_external_agent(void);
+
+enum agent_return {
+       SUCCESS,
+       WOULD_HAVE_BLOCKED,
+       AGENT_DIED,
+       FAILED, /* For fetch, or NEEDS_RECOVERY */
+       OTHER_FAILURE,
+};
+
+/* Ask the external agent to try to do an operation.
+ * name == tdb name for OPEN/OPEN_WITH_CLEAR_IF_FIRST,
+ * record name for FETCH/STORE (store stores name as data too)
+ */
+enum agent_return external_agent_operation(struct agent *handle,
+                                          enum operation op,
+                                          const char *name);
+
+/* Mapping enum -> string. */
+const char *agent_return_name(enum agent_return ret);
+const char *operation_name(enum operation op);
+
+#endif /* TDB_TEST_EXTERNAL_AGENT_H */
diff --git a/lib/tdb/test/jenkins-be-hash.tdb b/lib/tdb/test/jenkins-be-hash.tdb
new file mode 100644 (file)
index 0000000..b652840
Binary files /dev/null and b/lib/tdb/test/jenkins-be-hash.tdb differ
diff --git a/lib/tdb/test/jenkins-le-hash.tdb b/lib/tdb/test/jenkins-le-hash.tdb
new file mode 100644 (file)
index 0000000..007e0a3
Binary files /dev/null and b/lib/tdb/test/jenkins-le-hash.tdb differ
diff --git a/lib/tdb/test/lock-tracking.c b/lib/tdb/test/lock-tracking.c
new file mode 100644 (file)
index 0000000..b2f092c
--- /dev/null
@@ -0,0 +1,157 @@
+/* We save the locks so we can reaquire them. */
+#include "../common/tdb_private.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "tap-interface.h"
+#include "lock-tracking.h"
+
+struct testlock {
+       struct testlock *next;
+       unsigned int off;
+       unsigned int len;
+       int type;
+};
+static struct testlock *testlocks;
+int locking_errors = 0;
+bool suppress_lockcheck = false;
+bool nonblocking_locks;
+int locking_would_block = 0;
+void (*unlock_callback)(int fd);
+
+int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
+{
+       va_list ap;
+       int ret, arg3;
+       struct flock *fl;
+       bool may_block = false;
+
+       if (cmd != F_SETLK && cmd != F_SETLKW) {
+               /* This may be totally bogus, but we don't know in general. */
+               va_start(ap, cmd);
+               arg3 = va_arg(ap, int);
+               va_end(ap);
+
+               return fcntl(fd, cmd, arg3);
+       }
+
+       va_start(ap, cmd);
+       fl = va_arg(ap, struct flock *);
+       va_end(ap);
+
+       if (cmd == F_SETLKW && nonblocking_locks) {
+               cmd = F_SETLK;
+               may_block = true;
+       }
+       ret = fcntl(fd, cmd, fl);
+
+       /* Detect when we failed, but might have been OK if we waited. */
+       if (may_block && ret == -1 && (errno == EAGAIN || errno == EACCES)) {
+               locking_would_block++;
+       }
+
+       if (fl->l_type == F_UNLCK) {
+               struct testlock **l;
+               struct testlock *old = NULL;
+
+               for (l = &testlocks; *l; l = &(*l)->next) {
+                       if ((*l)->off == fl->l_start
+                           && (*l)->len == fl->l_len) {
+                               if (ret == 0) {
+                                       old = *l;
+                                       *l = (*l)->next;
+                                       free(old);
+                               }
+                               break;
+                       }
+                       if (((*l)->off == fl->l_start)
+                           && ((*l)->len == 0)
+                           && (ret == 0)) {
+                               /*
+                                * Remove a piece from the start of the
+                                * allrecord_lock
+                                */
+                               old = *l;
+                               (*l)->off += fl->l_len;
+                               break;
+                       }
+               }
+               if (!old && !suppress_lockcheck) {
+                       diag("Unknown unlock %u@%u - %i",
+                            (int)fl->l_len, (int)fl->l_start, ret);
+                       locking_errors++;
+               }
+       } else {
+               struct testlock *new, *i;
+               unsigned int fl_end = fl->l_start + fl->l_len;
+               if (fl->l_len == 0)
+                       fl_end = (unsigned int)-1;
+
+               /* Check for overlaps: we shouldn't do this. */
+               for (i = testlocks; i; i = i->next) {
+                       unsigned int i_end = i->off + i->len;
+                       if (i->len == 0)
+                               i_end = (unsigned int)-1;
+
+                       if (fl->l_start >= i->off && fl->l_start < i_end)
+                               break;
+                       if (fl_end >= i->off && fl_end < i_end)
+                               break;
+
+                       /* tdb_allrecord_lock does this, handle adjacent: */
+                       if (fl->l_start == i_end && fl->l_type == i->type) {
+                               if (ret == 0) {
+                                       i->len = fl->l_len
+                                               ? i->len + fl->l_len
+                                               : 0;
+                               }
+                               goto done;
+                       }
+               }
+               if (i) {
+                       /* Special case: upgrade of allrecord lock. */
+                       if (i->type == F_RDLCK && fl->l_type == F_WRLCK
+                           && i->off == FREELIST_TOP
+                           && fl->l_start == FREELIST_TOP
+                           && i->len == 0
+                           && fl->l_len == 0) {
+                               if (ret == 0)
+                                       i->type = F_WRLCK;
+                               goto done;
+                       }
+                       if (!suppress_lockcheck) {
+                               diag("%s testlock %u@%u overlaps %u@%u",
+                                    fl->l_type == F_WRLCK ? "write" : "read",
+                                    (int)fl->l_len, (int)fl->l_start,
+                                    i->len, (int)i->off);
+                               locking_errors++;
+                       }
+               }
+
+               if (ret == 0) {
+                       new = malloc(sizeof *new);
+                       new->off = fl->l_start;
+                       new->len = fl->l_len;
+                       new->type = fl->l_type;
+                       new->next = testlocks;
+                       testlocks = new;
+               }
+       }
+done:
+       if (ret == 0 && fl->l_type == F_UNLCK && unlock_callback)
+               unlock_callback(fd);
+       return ret;
+}
+
+unsigned int forget_locking(void)
+{
+       unsigned int num = 0;
+       while (testlocks) {
+               struct testlock *next = testlocks->next;
+               free(testlocks);
+               testlocks = next;
+               num++;
+       }
+       return num;
+}
diff --git a/lib/tdb/test/lock-tracking.h b/lib/tdb/test/lock-tracking.h
new file mode 100644 (file)
index 0000000..f2c9c44
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef LOCK_TRACKING_H
+#define LOCK_TRACKING_H
+#include <stdbool.h>
+
+/* Set this if you want a callback after fnctl unlock. */
+extern void (*unlock_callback)(int fd);
+
+/* Replacement fcntl. */
+int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ );
+
+/* Discard locking info: returns number of locks outstanding. */
+unsigned int forget_locking(void);
+
+/* Number of errors in locking. */
+extern int locking_errors;
+
+/* Suppress lock checking. */
+extern bool suppress_lockcheck;
+
+/* Make all locks non-blocking. */
+extern bool nonblocking_locks;
+
+/* Number of times we failed a lock because we made it non-blocking. */
+extern int locking_would_block;
+#endif /* LOCK_TRACKING_H */
diff --git a/lib/tdb/test/logging.c b/lib/tdb/test/logging.c
new file mode 100644 (file)
index 0000000..dfab486
--- /dev/null
@@ -0,0 +1,33 @@
+#include "logging.h"
+#include "tap-interface.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+bool suppress_logging = false;
+const char *log_prefix = "";
+
+/* Turn log messages into tap diag messages. */
+static void taplog(struct tdb_context *tdb,
+                  enum tdb_debug_level level,
+                  const char *fmt, ...)
+{
+       va_list ap;
+       char line[200];
+
+       if (suppress_logging)
+               return;
+
+       va_start(ap, fmt);
+       vsprintf(line, fmt, ap);
+       va_end(ap);
+
+       /* Strip trailing \n: diag adds it. */
+       if (line[0] && line[strlen(line)-1] == '\n')
+               diag("%s%.*s", log_prefix, (unsigned)strlen(line)-1, line);
+       else
+               diag("%s%s", log_prefix, line);
+}
+
+struct tdb_logging_context taplogctx = { taplog, NULL };
diff --git a/lib/tdb/test/logging.h b/lib/tdb/test/logging.h
new file mode 100644 (file)
index 0000000..89e77b2
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef TDB_TEST_LOGGING_H
+#define TDB_TEST_LOGGING_H
+#include "replace.h"
+#include "../include/tdb.h"
+#include <stdbool.h>
+
+extern bool suppress_logging;
+extern const char *log_prefix;
+extern struct tdb_logging_context taplogctx;
+
+#endif /* TDB_TEST_LOGGING_H */
diff --git a/lib/tdb/test/old-nohash-be.tdb b/lib/tdb/test/old-nohash-be.tdb
new file mode 100644 (file)
index 0000000..1c49116
Binary files /dev/null and b/lib/tdb/test/old-nohash-be.tdb differ
diff --git a/lib/tdb/test/old-nohash-le.tdb b/lib/tdb/test/old-nohash-le.tdb
new file mode 100644 (file)
index 0000000..0655072
Binary files /dev/null and b/lib/tdb/test/old-nohash-le.tdb differ
diff --git a/lib/tdb/test/run-3G-file.c b/lib/tdb/test/run-3G-file.c
new file mode 100644 (file)
index 0000000..3ee9de1
--- /dev/null
@@ -0,0 +1,144 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+static int tdb_expand_file_sparse(struct tdb_context *tdb,
+                                 tdb_off_t size,
+                                 tdb_off_t addition)
+{
+       if (tdb->read_only || tdb->traverse_read) {
+               tdb->ecode = TDB_ERR_RDONLY;
+               return -1;
+       }
+
+       if (ftruncate(tdb->fd, size+addition) == -1) {
+               char b = 0;
+               ssize_t written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
+               if (written == 0) {
+                       /* try once more, potentially revealing errno */
+                       written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
+               }
+               if (written == 0) {
+                       /* again - give up, guessing errno */
+                       errno = ENOSPC;
+               }
+               if (written != 1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n",
+                                size+addition, strerror(errno)));
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static const struct tdb_methods large_io_methods = {
+       tdb_read,
+       tdb_write,
+       tdb_next_hash_chain,
+       tdb_oob,
+       tdb_expand_file_sparse
+};
+
+static int test_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
+                        void *_data)
+{
+       TDB_DATA *expect = _data;
+       ok1(key.dsize == strlen("hi"));
+       ok1(memcmp(key.dptr, "hi", strlen("hi")) == 0);
+       ok1(data.dsize == expect->dsize);
+       ok1(memcmp(data.dptr, expect->dptr, data.dsize) == 0);
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, orig_data, data;
+       uint32_t hash;
+       tdb_off_t rec_ptr;
+       struct tdb_record rec;
+       int ret;
+
+       plan_tests(24);
+       tdb = tdb_open_ex("run-36-file.tdb", 1024, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       ok1(tdb);
+       tdb->methods = &large_io_methods;
+
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       orig_data.dsize = strlen("world");
+       orig_data.dptr = (void *)"world";
+
+       /* Enlarge the file (internally multiplies by 2). */
+       ret = tdb_expand(tdb, 1500000000);
+#ifdef HAVE_INCOHERENT_MMAP
+       /* This can fail due to mmap failure on 32 bit systems. */
+       if (ret == -1) {
+               /* These should now fail. */
+               ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == -1);
+               data = tdb_fetch(tdb, key);
+               ok1(data.dptr == NULL);
+               ok1(tdb_traverse(tdb, test_traverse, &orig_data) == -1);
+               ok1(tdb_delete(tdb, key) == -1);
+               ok1(tdb_traverse(tdb, test_traverse, NULL) == -1);
+               /* Skip the rest... */
+               for (ret = 0; ret < 24 - 6; ret++)
+                       ok1(1);
+               tdb_close(tdb);
+               return exit_status();
+       }
+#endif
+       ok1(ret == 0);
+
+       /* Put an entry in, and check it. */
+       ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == 0);
+
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+
+       /* That currently fills at the end, make sure that's true. */
+       hash = tdb->hash_fn(&key);
+       rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec);
+       ok1(rec_ptr);
+       ok1(rec_ptr > 2U*1024*1024*1024);
+       tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+
+       /* Traverse must work. */
+       ok1(tdb_traverse(tdb, test_traverse, &orig_data) == 1);
+
+       /* Delete should work. */
+       ok1(tdb_delete(tdb, key) == 0);
+
+       ok1(tdb_traverse(tdb, test_traverse, NULL) == 0);
+
+       /* Transactions should work. */
+       ok1(tdb_transaction_start(tdb) == 0);
+       ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == 0);
+
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+       ok1(tdb_transaction_commit(tdb) == 0);
+
+       ok1(tdb_traverse(tdb, test_traverse, &orig_data) == 1);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-bad-tdb-header.c b/lib/tdb/test/run-bad-tdb-header.c
new file mode 100644 (file)
index 0000000..b00fb89
--- /dev/null
@@ -0,0 +1,58 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       struct tdb_header hdr;
+       int fd;
+
+       plan_tests(11);
+       /* Can open fine if complete crap, as long as O_CREAT. */
+       fd = open("run-bad-tdb-header.tdb", O_RDWR|O_CREAT|O_TRUNC, 0600);
+       ok1(fd >= 0);
+       ok1(write(fd, "hello world", 11) == 11);
+       close(fd);
+       tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(!tdb);
+       tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_CREAT|O_RDWR,
+                         0600, &taplogctx, NULL);
+       ok1(tdb);
+       tdb_close(tdb);
+
+       /* Now, with wrong version it should *not* overwrite. */
+       fd = open("run-bad-tdb-header.tdb", O_RDWR);
+       ok1(fd >= 0);
+       ok1(read(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
+       ok1(hdr.version == TDB_VERSION);
+       hdr.version++;
+       lseek(fd, 0, SEEK_SET);
+       ok1(write(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
+       close(fd);
+
+       tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_RDWR|O_CREAT,
+                         0600, &taplogctx, NULL);
+       ok1(errno == EIO);
+       ok1(!tdb);
+
+       /* With truncate, will be fine. */
+       tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0,
+                         O_RDWR|O_CREAT|O_TRUNC, 0600, &taplogctx, NULL);
+       ok1(tdb);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-check.c b/lib/tdb/test/run-check.c
new file mode 100644 (file)
index 0000000..05f7aec
--- /dev/null
@@ -0,0 +1,64 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(13);
+       tdb = tdb_open_ex("run-check.tdb", 1, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data.dsize = strlen("world");
+       data.dptr = (void *)"world";
+
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       tdb = tdb_open_ex("run-check.tdb", 1024, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       tdb = tdb_open_ex("test/tdb.corrupt", 1024, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == -1);
+       ok1(tdb_error(tdb) == TDB_ERR_CORRUPT);
+       tdb_close(tdb);
+
+       /* Big and little endian should work! */
+       tdb = tdb_open_ex("test/old-nohash-le.tdb", 1024, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       tdb = tdb_open_ex("test/old-nohash-be.tdb", 1024, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-corrupt.c b/lib/tdb/test/run-corrupt.c
new file mode 100644 (file)
index 0000000..1a3c769
--- /dev/null
@@ -0,0 +1,131 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+static int check(TDB_DATA key, TDB_DATA data, void *private)
+{
+       unsigned int *sizes = private;
+
+       if (key.dsize > strlen("hello"))
+               return -1;
+       if (memcmp(key.dptr, "hello", key.dsize) != 0)
+               return -1;
+
+       if (data.dsize != strlen("world"))
+               return -1;
+       if (memcmp(data.dptr, "world", data.dsize) != 0)
+               return -1;
+
+       sizes[0] += key.dsize;
+       sizes[1] += data.dsize;
+       return 0;
+}
+
+static void tdb_flip_bit(struct tdb_context *tdb, unsigned int bit)
+{
+       unsigned int off = bit / CHAR_BIT;
+       unsigned char mask = (1 << (bit % CHAR_BIT));
+
+       if (tdb->map_ptr)
+               ((unsigned char *)tdb->map_ptr)[off] ^= mask;
+       else {
+               unsigned char c;
+               if (pread(tdb->fd, &c, 1, off) != 1) {
+                       fprintf(stderr, "pread: %s\n", strerror(errno));
+                       exit(1);
+               }
+               c ^= mask;
+               if (pwrite(tdb->fd, &c, 1, off) != 1) {
+                       fprintf(stderr, "pwrite: %s\n", strerror(errno));
+                       exit(1);
+               }
+       }
+}
+
+static void check_test(struct tdb_context *tdb)
+{
+       TDB_DATA key, data;
+       unsigned int i, verifiable, corrupt, sizes[2], dsize, ksize;
+
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+
+       key.dptr = (void *)"hello";
+       data.dsize = strlen("world");
+       data.dptr = (void *)"world";
+
+       /* Key and data size respectively. */
+       dsize = ksize = 0;
+
+       /* 5 keys in hash size 2 means we'll have multichains. */
+       for (key.dsize = 1; key.dsize <= 5; key.dsize++) {
+               ksize += key.dsize;
+               dsize += data.dsize;
+               if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
+                       abort();
+       }
+
+       /* This is how many bytes we expect to be verifiable. */
+       /* From the file header. */
+       verifiable = strlen(TDB_MAGIC_FOOD) + 1
+               + 2 * sizeof(uint32_t) + 2 * sizeof(tdb_off_t)
+               + 2 * sizeof(uint32_t);
+       /* From the free list chain and hash chains. */
+       verifiable += 3 * sizeof(tdb_off_t);
+       /* From the record headers & tailer */
+       verifiable += 5 * (sizeof(struct tdb_record) + sizeof(uint32_t));
+       /* The free block: we ignore datalen, keylen, full_hash. */
+       verifiable += sizeof(struct tdb_record) - 3*sizeof(uint32_t) +
+               sizeof(uint32_t);
+       /* Our check function verifies the key and data. */
+       verifiable += ksize + dsize;
+
+       /* Flip one bit at a time, make sure it detects verifiable bytes. */
+       for (i = 0, corrupt = 0; i < tdb->map_size * CHAR_BIT; i++) {
+               tdb_flip_bit(tdb, i);
+               memset(sizes, 0, sizeof(sizes));
+               if (tdb_check(tdb, check, sizes) != 0)
+                       corrupt++;
+               else if (sizes[0] != ksize || sizes[1] != dsize)
+                       corrupt++;
+               tdb_flip_bit(tdb, i);
+       }
+       ok(corrupt == verifiable * CHAR_BIT, "corrupt %u should be %u",
+          corrupt, verifiable * CHAR_BIT);
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+
+       plan_tests(4);
+       /* This should use mmap. */
+       tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       if (!tdb)
+               abort();
+       check_test(tdb);
+       tdb_close(tdb);
+
+       /* This should not. */
+       tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST|TDB_NOMMAP,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       if (!tdb)
+               abort();
+       check_test(tdb);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-die-during-transaction.c b/lib/tdb/test/run-die-during-transaction.c
new file mode 100644 (file)
index 0000000..6e3a70d
--- /dev/null
@@ -0,0 +1,231 @@
+#include "../common/tdb_private.h"
+#include "lock-tracking.h"
+static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
+static ssize_t write_check(int fd, const void *buf, size_t count);
+static int ftruncate_check(int fd, off_t length);
+
+#define pwrite pwrite_check
+#define write write_check
+#define fcntl fcntl_with_lockcheck
+#define ftruncate ftruncate_check
+
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include "external-agent.h"
+#include "logging.h"
+
+#undef write
+#undef pwrite
+#undef fcntl
+#undef ftruncate
+
+static bool in_transaction;
+static int target, current;
+static jmp_buf jmpbuf;
+#define TEST_DBNAME "run-die-during-transaction.tdb"
+#define KEY_STRING "helloworld"
+
+static void maybe_die(int fd)
+{
+       if (in_transaction && current++ == target) {
+               longjmp(jmpbuf, 1);
+       }
+}
+
+static ssize_t pwrite_check(int fd,
+                           const void *buf, size_t count, off_t offset)
+{
+       ssize_t ret;
+
+       maybe_die(fd);
+
+       ret = pwrite(fd, buf, count, offset);
+       if (ret != count)
+               return ret;
+
+       maybe_die(fd);
+       return ret;
+}
+
+static ssize_t write_check(int fd, const void *buf, size_t count)
+{
+       ssize_t ret;
+
+       maybe_die(fd);
+
+       ret = write(fd, buf, count);
+       if (ret != count)
+               return ret;
+
+       maybe_die(fd);
+       return ret;
+}
+
+static int ftruncate_check(int fd, off_t length)
+{
+       int ret;
+
+       maybe_die(fd);
+
+       ret = ftruncate(fd, length);
+
+       maybe_die(fd);
+       return ret;
+}
+
+static bool test_death(enum operation op, struct agent *agent)
+{
+       struct tdb_context *tdb = NULL;
+       TDB_DATA key;
+       enum agent_return ret;
+       int needed_recovery = 0;
+
+       current = target = 0;
+reset:
+       unlink(TEST_DBNAME);
+       tdb = tdb_open_ex(TEST_DBNAME, 1024, TDB_NOMMAP,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       if (setjmp(jmpbuf) != 0) {
+               /* We're partway through.  Simulate our death. */
+               close(tdb->fd);
+               forget_locking();
+               in_transaction = false;
+
+               ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
+               if (ret == SUCCESS)
+                       needed_recovery++;
+               else if (ret != FAILED) {
+                       diag("Step %u agent NEEDS_RECOVERY = %s", current,
+                            agent_return_name(ret));
+                       return false;
+               }
+
+               ret = external_agent_operation(agent, op, KEY_STRING);
+               if (ret != SUCCESS) {
+                       diag("Step %u op %s failed = %s", current,
+                            operation_name(op),
+                            agent_return_name(ret));
+                       return false;
+               }
+
+               ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
+               if (ret != FAILED) {
+                       diag("Still needs recovery after step %u = %s",
+                            current, agent_return_name(ret));
+                       return false;
+               }
+
+               ret = external_agent_operation(agent, CHECK, "");
+               if (ret != SUCCESS) {
+                       diag("Step %u check failed = %s", current,
+                            agent_return_name(ret));
+                       return false;
+               }
+
+               ret = external_agent_operation(agent, CLOSE, "");
+               if (ret != SUCCESS) {
+                       diag("Step %u close failed = %s", current,
+                            agent_return_name(ret));
+                       return false;
+               }
+
+               /* Suppress logging as this tries to use closed fd. */
+               suppress_logging = true;
+               suppress_lockcheck = true;
+               tdb_close(tdb);
+               suppress_logging = false;
+               suppress_lockcheck = false;
+               target++;
+               current = 0;
+               goto reset;
+       }
+
+       /* Put key for agent to fetch. */
+       key.dsize = strlen(KEY_STRING);
+       key.dptr = (void *)KEY_STRING;
+       if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
+               return false;
+
+       /* This is the key we insert in transaction. */
+       key.dsize--;
+
+       ret = external_agent_operation(agent, OPEN, TEST_DBNAME);
+       if (ret != SUCCESS) {
+               fprintf(stderr, "Agent failed to open: %s\n",
+                       agent_return_name(ret));
+               exit(1);
+       }
+
+       ret = external_agent_operation(agent, FETCH, KEY_STRING);
+       if (ret != SUCCESS) {
+               fprintf(stderr, "Agent failed find key: %s\n",
+                       agent_return_name(ret));
+               exit(1);
+       }
+
+       in_transaction = true;
+       if (tdb_transaction_start(tdb) != 0)
+               return false;
+
+       if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
+               return false;
+
+       if (tdb_transaction_commit(tdb) != 0)
+               return false;
+
+       in_transaction = false;
+
+       /* We made it! */
+       diag("Completed %u runs", current);
+       tdb_close(tdb);
+       ret = external_agent_operation(agent, CLOSE, "");
+       if (ret != SUCCESS) {
+               diag("Step %u close failed = %s", current,
+                    agent_return_name(ret));
+               return false;
+       }
+
+#ifdef HAVE_INCOHERENT_MMAP
+       /* This means we always mmap, which makes this test a noop. */
+       ok1(1);
+#else
+       ok1(needed_recovery);
+#endif
+       ok1(locking_errors == 0);
+       ok1(forget_locking() == 0);
+       locking_errors = 0;
+       return true;
+}
+
+int main(int argc, char *argv[])
+{
+       enum operation ops[] = { FETCH, STORE, TRANSACTION_START };
+       struct agent *agent;
+       int i;
+
+       plan_tests(12);
+       unlock_callback = maybe_die;
+
+       agent = prepare_external_agent();
+
+       for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
+               diag("Testing %s after death", operation_name(ops[i]));
+               ok1(test_death(ops[i], agent));
+       }
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-endian.c b/lib/tdb/test/run-endian.c
new file mode 100644 (file)
index 0000000..b19ffd3
--- /dev/null
@@ -0,0 +1,63 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(13);
+       tdb = tdb_open_ex("run-endian.tdb", 1024,
+                         TDB_CLEAR_IF_FIRST|TDB_CONVERT,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       ok1(tdb);
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data.dsize = strlen("world");
+       data.dptr = (void *)"world";
+
+       ok1(tdb_store(tdb, key, data, TDB_MODIFY) < 0);
+       ok1(tdb_error(tdb) == TDB_ERR_NOEXIST);
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) < 0);
+       ok1(tdb_error(tdb) == TDB_ERR_EXISTS);
+       ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0);
+
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+
+       key.dsize++;
+       data = tdb_fetch(tdb, key);
+       ok1(data.dptr == NULL);
+       tdb_close(tdb);
+
+       /* Reopen: should read it */
+       tdb = tdb_open_ex("run-endian.tdb", 1024, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(tdb);
+
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-incompatible.c b/lib/tdb/test/run-incompatible.c
new file mode 100644 (file)
index 0000000..628927c
--- /dev/null
@@ -0,0 +1,185 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+
+static unsigned int tdb_dumb_hash(TDB_DATA *key)
+{
+       return key->dsize;
+}
+
+static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
+{
+       unsigned int *count = tdb_get_logging_private(tdb);
+       if (strstr(fmt, "hash"))
+               (*count)++;
+}
+
+static unsigned int hdr_rwlocks(const char *fname)
+{
+       struct tdb_header hdr;
+
+       int fd = open(fname, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+       if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+               return -1;
+
+       close(fd);
+       return hdr.rwlocks;
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       unsigned int log_count, flags;
+       TDB_DATA d, r;
+       struct tdb_logging_context log_ctx = { log_fn, &log_count };
+
+       plan_tests(38 * 2);
+
+       for (flags = 0; flags <= TDB_CONVERT; flags += TDB_CONVERT) {
+               unsigned int rwmagic = TDB_HASH_RWLOCK_MAGIC;
+
+               if (flags & TDB_CONVERT)
+                       tdb_convert(&rwmagic, sizeof(rwmagic));
+
+               /* Create an old-style hash. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0, flags,
+                                 O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
+                                 NULL);
+               ok1(tdb);
+               ok1(log_count == 0);
+               d.dptr = (void *)"Hello";
+               d.dsize = 5;
+               ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
+               tdb_close(tdb);
+
+               /* Should not have marked rwlocks field. */
+               ok1(hdr_rwlocks("run-incompatible.tdb") == 0);
+
+               /* We can still open any old-style with incompat flag. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0,
+                                 TDB_INCOMPATIBLE_HASH,
+                                 O_RDWR, 0600, &log_ctx, NULL);
+               ok1(tdb);
+               ok1(log_count == 0);
+               r = tdb_fetch(tdb, d);
+               ok1(r.dsize == 5);
+               free(r.dptr);
+               ok1(tdb_check(tdb, NULL, NULL) == 0);
+               tdb_close(tdb);
+
+               log_count = 0;
+               tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDONLY,
+                                 0, &log_ctx, tdb_jenkins_hash);
+               ok1(tdb);
+               ok1(log_count == 0);
+               ok1(tdb_check(tdb, NULL, NULL) == 0);
+               tdb_close(tdb);
+
+               log_count = 0;
+               tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDONLY,
+                                 0, &log_ctx, tdb_jenkins_hash);
+               ok1(tdb);
+               ok1(log_count == 0);
+               ok1(tdb_check(tdb, NULL, NULL) == 0);
+               tdb_close(tdb);
+
+               /* OK, now create with incompatible flag, default hash. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0,
+                                 flags|TDB_INCOMPATIBLE_HASH,
+                                 O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
+                                 NULL);
+               ok1(tdb);
+               ok1(log_count == 0);
+               d.dptr = (void *)"Hello";
+               d.dsize = 5;
+               ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
+               tdb_close(tdb);
+
+               /* Should have marked rwlocks field. */
+               ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic);
+
+               /* Cannot open with old hash. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0, 0,
+                                 O_RDWR, 0600, &log_ctx, tdb_old_hash);
+               ok1(!tdb);
+               ok1(log_count == 1);
+
+               /* Can open with jenkins hash. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0, 0,
+                                 O_RDWR, 0600, &log_ctx, tdb_jenkins_hash);
+               ok1(tdb);
+               ok1(log_count == 0);
+               r = tdb_fetch(tdb, d);
+               ok1(r.dsize == 5);
+               free(r.dptr);
+               ok1(tdb_check(tdb, NULL, NULL) == 0);
+               tdb_close(tdb);
+
+               /* Can open by letting it figure it out itself. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0, 0,
+                                 O_RDWR, 0600, &log_ctx, NULL);
+               ok1(tdb);
+               ok1(log_count == 0);
+               r = tdb_fetch(tdb, d);
+               ok1(r.dsize == 5);
+               free(r.dptr);
+               ok1(tdb_check(tdb, NULL, NULL) == 0);
+               tdb_close(tdb);
+
+               /* We can also use incompatible hash with other hashes. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0,
+                                 flags|TDB_INCOMPATIBLE_HASH,
+                                 O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx,
+                                 tdb_dumb_hash);
+               ok1(tdb);
+               ok1(log_count == 0);
+               d.dptr = (void *)"Hello";
+               d.dsize = 5;
+               ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
+               tdb_close(tdb);
+
+               /* Should have marked rwlocks field. */
+               ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic);
+
+               /* It should not open if we don't specify. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, O_RDWR, 0,
+                                 &log_ctx, NULL);
+               ok1(!tdb);
+               ok1(log_count == 1);
+
+               /* Should reopen with correct hash. */
+               log_count = 0;
+               tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, O_RDWR, 0,
+                                 &log_ctx, tdb_dumb_hash);
+               ok1(tdb);
+               ok1(log_count == 0);
+               r = tdb_fetch(tdb, d);
+               ok1(r.dsize == 5);
+               free(r.dptr);
+               ok1(tdb_check(tdb, NULL, NULL) == 0);
+               tdb_close(tdb);
+       }
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-nested-transactions.c b/lib/tdb/test/run-nested-transactions.c
new file mode 100644 (file)
index 0000000..8c84bca
--- /dev/null
@@ -0,0 +1,78 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include <stdbool.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(27);
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+
+       tdb = tdb_open_ex("run-nested-transactions.tdb",
+                         1024, TDB_CLEAR_IF_FIRST|TDB_DISALLOW_NESTING,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+       ok1(tdb);
+
+       /* Nesting disallowed. */
+       ok1(tdb_transaction_start(tdb) == 0);
+       data.dptr = (void *)"world";
+       data.dsize = strlen("world");
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+       ok1(tdb_transaction_start(tdb) != 0);
+       ok1(tdb_error(tdb) == TDB_ERR_NESTING);
+
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+       ok1(tdb_transaction_commit(tdb) == 0);
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+       tdb_close(tdb);
+
+       /* Nesting allowed by default */
+       tdb = tdb_open_ex("run-nested-transactions.tdb",
+                         1024, TDB_DEFAULT, O_RDWR, 0, &taplogctx, NULL);
+       ok1(tdb);
+
+       ok1(tdb_transaction_start(tdb) == 0);
+       ok1(tdb_transaction_start(tdb) == 0);
+       ok1(tdb_delete(tdb, key) == 0);
+       ok1(tdb_transaction_commit(tdb) == 0);
+       ok1(!tdb_exists(tdb, key));
+       ok1(tdb_transaction_cancel(tdb) == 0);
+       /* Surprise! Kills inner "committed" transaction. */
+       ok1(tdb_exists(tdb, key));
+
+       ok1(tdb_transaction_start(tdb) == 0);
+       ok1(tdb_transaction_start(tdb) == 0);
+       ok1(tdb_delete(tdb, key) == 0);
+       ok1(tdb_transaction_commit(tdb) == 0);
+       ok1(!tdb_exists(tdb, key));
+       ok1(tdb_transaction_commit(tdb) == 0);
+       ok1(!tdb_exists(tdb, key));
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-nested-traverse.c b/lib/tdb/test/run-nested-traverse.c
new file mode 100644 (file)
index 0000000..37d57c0
--- /dev/null
@@ -0,0 +1,87 @@
+#include "../common/tdb_private.h"
+#include "lock-tracking.h"
+#define fcntl fcntl_with_lockcheck
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#undef fcntl
+#include <stdlib.h>
+#include <stdbool.h>
+#include "external-agent.h"
+#include "logging.h"
+
+static struct agent *agent;
+
+static bool correct_key(TDB_DATA key)
+{
+       return key.dsize == strlen("hi")
+               && memcmp(key.dptr, "hi", key.dsize) == 0;
+}
+
+static bool correct_data(TDB_DATA data)
+{
+       return data.dsize == strlen("world")
+               && memcmp(data.dptr, "world", data.dsize) == 0;
+}
+
+static int traverse2(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
+                    void *p)
+{
+       ok1(correct_key(key));
+       ok1(correct_data(data));
+       return 0;
+}
+
+static int traverse1(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
+                    void *p)
+{
+       ok1(correct_key(key));
+       ok1(correct_data(data));
+       ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
+           == WOULD_HAVE_BLOCKED);
+       tdb_traverse(tdb, traverse2, NULL);
+
+       /* That should *not* release the transaction lock! */
+       ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
+           == WOULD_HAVE_BLOCKED);
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(17);
+       agent = prepare_external_agent();
+
+       tdb = tdb_open_ex("run-nested-traverse.tdb", 1024, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+       ok1(tdb);
+
+       ok1(external_agent_operation(agent, OPEN, tdb_name(tdb)) == SUCCESS);
+       ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
+           == SUCCESS);
+       ok1(external_agent_operation(agent, TRANSACTION_COMMIT, tdb_name(tdb))
+           == SUCCESS);
+
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data.dptr = (void *)"world";
+       data.dsize = strlen("world");
+
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+       tdb_traverse(tdb, traverse1, NULL);
+       tdb_traverse_read(tdb, traverse1, NULL);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-no-lock-during-traverse.c b/lib/tdb/test/run-no-lock-during-traverse.c
new file mode 100644 (file)
index 0000000..0a72282
--- /dev/null
@@ -0,0 +1,113 @@
+#include "../common/tdb_private.h"
+#include "lock-tracking.h"
+
+#define fcntl fcntl_with_lockcheck
+
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+#undef fcntl
+
+#define NUM_ENTRIES 10
+
+static bool prepare_entries(struct tdb_context *tdb)
+{
+       unsigned int i;
+       TDB_DATA key, data;
+
+       for (i = 0; i < NUM_ENTRIES; i++) {
+               key.dsize = sizeof(i);
+               key.dptr = (void *)&i;
+               data.dsize = strlen("world");
+               data.dptr = (void *)"world";
+
+               if (tdb_store(tdb, key, data, 0) != 0)
+                       return false;
+       }
+       return true;
+}
+
+static void delete_entries(struct tdb_context *tdb)
+{
+       unsigned int i;
+       TDB_DATA key;
+
+       for (i = 0; i < NUM_ENTRIES; i++) {
+               key.dsize = sizeof(i);
+               key.dptr = (void *)&i;
+
+               ok1(tdb_delete(tdb, key) == 0);
+       }
+}
+
+/* We don't know how many times this will run. */
+static int delete_other(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
+                       void *private_data)
+{
+       unsigned int i;
+       memcpy(&i, key.dptr, 4);
+       i = (i + 1) % NUM_ENTRIES;
+       key.dptr = (void *)&i;
+       if (tdb_delete(tdb, key) != 0)
+               (*(int *)private_data)++;
+       return 0;
+}
+
+static int delete_self(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
+                       void *private_data)
+{
+       ok1(tdb_delete(tdb, key) == 0);
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       int errors = 0;
+
+       plan_tests(41);
+       tdb = tdb_open_ex("run-no-lock-during-traverse.tdb",
+                         1024, TDB_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR,
+                         0600, &taplogctx, NULL);
+
+       ok1(tdb);
+       ok1(prepare_entries(tdb));
+       ok1(locking_errors == 0);
+       ok1(tdb_lockall(tdb) == 0);
+       ok1(locking_errors == 0);
+       tdb_traverse(tdb, delete_other, &errors);
+       ok1(errors == 0);
+       ok1(locking_errors == 0);
+       ok1(tdb_unlockall(tdb) == 0);
+
+       ok1(prepare_entries(tdb));
+       ok1(locking_errors == 0);
+       ok1(tdb_lockall(tdb) == 0);
+       ok1(locking_errors == 0);
+       tdb_traverse(tdb, delete_self, NULL);
+       ok1(locking_errors == 0);
+       ok1(tdb_unlockall(tdb) == 0);
+
+       ok1(prepare_entries(tdb));
+       ok1(locking_errors == 0);
+       ok1(tdb_lockall(tdb) == 0);
+       ok1(locking_errors == 0);
+       delete_entries(tdb);
+       ok1(locking_errors == 0);
+       ok1(tdb_unlockall(tdb) == 0);
+
+       ok1(tdb_close(tdb) == 0);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-oldhash.c b/lib/tdb/test/run-oldhash.c
new file mode 100644 (file)
index 0000000..535336c
--- /dev/null
@@ -0,0 +1,49 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+
+       plan_tests(8);
+
+       /* Old format (with zeroes in the hash magic fields) should
+        * open with any hash (since we don't know what hash they used). */
+       tdb = tdb_open_ex("test/old-nohash-le.tdb", 0, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       tdb = tdb_open_ex("test/old-nohash-be.tdb", 0, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       tdb = tdb_open_ex("test/old-nohash-le.tdb", 0, 0, O_RDWR, 0,
+                         &taplogctx, tdb_jenkins_hash);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       tdb = tdb_open_ex("test/old-nohash-be.tdb", 0, 0, O_RDWR, 0,
+                         &taplogctx, tdb_jenkins_hash);
+       ok1(tdb);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-open-during-transaction.c b/lib/tdb/test/run-open-during-transaction.c
new file mode 100644 (file)
index 0000000..a825e62
--- /dev/null
@@ -0,0 +1,181 @@
+#include "../common/tdb_private.h"
+#include "lock-tracking.h"
+
+static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
+static ssize_t write_check(int fd, const void *buf, size_t count);
+static int ftruncate_check(int fd, off_t length);
+
+#define pwrite pwrite_check
+#define write write_check
+#define fcntl fcntl_with_lockcheck
+#define ftruncate ftruncate_check
+
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include "external-agent.h"
+#include "logging.h"
+
+static struct agent *agent;
+static bool opened;
+static int errors = 0;
+static bool clear_if_first;
+#define TEST_DBNAME "run-open-during-transaction.tdb"
+
+#undef write
+#undef pwrite
+#undef fcntl
+#undef ftruncate
+
+static bool is_same(const char *snapshot, const char *latest, off_t len)
+{
+       unsigned i;
+
+       for (i = 0; i < len; i++) {
+               if (snapshot[i] != latest[i])
+                       return false;
+       }
+       return true;
+}
+
+static bool compare_file(int fd, const char *snapshot, off_t snapshot_len)
+{
+       char *contents;
+       bool same;
+
+       /* over-length read serves as length check. */
+       contents = malloc(snapshot_len+1);
+       same = pread(fd, contents, snapshot_len+1, 0) == snapshot_len
+               && is_same(snapshot, contents, snapshot_len);
+       free(contents);
+       return same;
+}
+
+static void check_file_intact(int fd)
+{
+       enum agent_return ret;
+       struct stat st;
+       char *contents;
+
+       fstat(fd, &st);
+       contents = malloc(st.st_size);
+       if (pread(fd, contents, st.st_size, 0) != st.st_size) {
+               diag("Read fail");
+               errors++;
+               return;
+       }
+
+       /* Ask agent to open file. */
+       ret = external_agent_operation(agent, clear_if_first ?
+                                      OPEN_WITH_CLEAR_IF_FIRST :
+                                      OPEN,
+                                      TEST_DBNAME);
+
+       /* It's OK to open it, but it must not have changed! */
+       if (!compare_file(fd, contents, st.st_size)) {
+               diag("Agent changed file after opening %s",
+                    agent_return_name(ret));
+               errors++;
+       }
+
+       if (ret == SUCCESS) {
+               ret = external_agent_operation(agent, CLOSE, NULL);
+               if (ret != SUCCESS) {
+                       diag("Agent failed to close tdb: %s",
+                            agent_return_name(ret));
+                       errors++;
+               }
+       } else if (ret != WOULD_HAVE_BLOCKED) {
+               diag("Agent opening file gave %s",
+                    agent_return_name(ret));
+               errors++;
+       }
+
+       free(contents);
+}
+
+static void after_unlock(int fd)
+{
+       if (opened)
+               check_file_intact(fd);
+}
+
+static ssize_t pwrite_check(int fd,
+                           const void *buf, size_t count, off_t offset)
+{
+       if (opened)
+               check_file_intact(fd);
+
+       return pwrite(fd, buf, count, offset);
+}
+
+static ssize_t write_check(int fd, const void *buf, size_t count)
+{
+       if (opened)
+               check_file_intact(fd);
+
+       return write(fd, buf, count);
+}
+
+static int ftruncate_check(int fd, off_t length)
+{
+       if (opened)
+               check_file_intact(fd);
+
+       return ftruncate(fd, length);
+
+}
+
+int main(int argc, char *argv[])
+{
+       const int flags[] = { TDB_DEFAULT,
+                             TDB_CLEAR_IF_FIRST,
+                             TDB_NOMMAP,
+                             TDB_CLEAR_IF_FIRST | TDB_NOMMAP };
+       int i;
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(20);
+       agent = prepare_external_agent();
+
+       unlock_callback = after_unlock;
+       for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
+               clear_if_first = (flags[i] & TDB_CLEAR_IF_FIRST);
+               diag("Test with %s and %s\n",
+                    clear_if_first ? "CLEAR" : "DEFAULT",
+                    (flags[i] & TDB_NOMMAP) ? "no mmap" : "mmap");
+               unlink(TEST_DBNAME);
+               tdb = tdb_open_ex(TEST_DBNAME, 1024, flags[i],
+                                 O_CREAT|O_TRUNC|O_RDWR, 0600,
+                                 &taplogctx, NULL);
+               ok1(tdb);
+
+               opened = true;
+               ok1(tdb_transaction_start(tdb) == 0);
+               key.dsize = strlen("hi");
+               key.dptr = (void *)"hi";
+               data.dptr = (void *)"world";
+               data.dsize = strlen("world");
+
+               ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+               ok1(tdb_transaction_commit(tdb) == 0);
+               ok(!errors, "We had %u open errors", errors);
+
+               opened = false;
+               tdb_close(tdb);
+       }
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-readonly-check.c b/lib/tdb/test/run-readonly-check.c
new file mode 100644 (file)
index 0000000..fdd9507
--- /dev/null
@@ -0,0 +1,52 @@
+/* We should be able to tdb_check a O_RDONLY tdb, and we were previously allowed
+ * to tdb_check() inside a transaction (though that's paranoia!). */
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(11);
+       tdb = tdb_open_ex("run-readonly-check.tdb", 1024,
+                         TDB_DEFAULT,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       ok1(tdb);
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data.dsize = strlen("world");
+       data.dptr = (void *)"world";
+
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+
+       /* We are also allowed to do a check inside a transaction. */
+       ok1(tdb_transaction_start(tdb) == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       ok1(tdb_close(tdb) == 0);
+
+       tdb = tdb_open_ex("run-readonly-check.tdb", 1024,
+                         TDB_DEFAULT, O_RDONLY, 0, &taplogctx, NULL);
+
+       ok1(tdb);
+       ok1(tdb_store(tdb, key, data, TDB_MODIFY) == -1);
+       ok1(tdb_error(tdb) == TDB_ERR_RDONLY);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       ok1(tdb_close(tdb) == 0);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-rescue-find_entry.c b/lib/tdb/test/run-rescue-find_entry.c
new file mode 100644 (file)
index 0000000..25f4f1c
--- /dev/null
@@ -0,0 +1,50 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "../common/rescue.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+#define NUM 20
+
+/* Binary searches are deceptively simple: easy to screw up! */
+int main(int argc, char *argv[])
+{
+       unsigned int i, j, n;
+       struct found f[NUM+1];
+       struct found_table table;
+
+       /* Set up array for searching. */
+       for (i = 0; i < NUM+1; i++) {
+               f[i].head = i * 3;
+       }
+       table.arr = f;
+
+       for (i = 0; i < NUM; i++) {
+               table.num = i;
+               for (j = 0; j < (i + 2) * 3; j++) {
+                       n = find_entry(&table, j);
+                       ok1(n <= i);
+
+                       /* If we were searching for something too large... */
+                       if (j > i*3)
+                               ok1(n == i);
+                       else {
+                               /* It must give us something after j */
+                               ok1(f[n].head >= j);
+                               ok1(n == 0 || f[n-1].head < j);
+                       }
+               }
+       }
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-rescue.c b/lib/tdb/test/run-rescue.c
new file mode 100644 (file)
index 0000000..a26c493
--- /dev/null
@@ -0,0 +1,126 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "../common/rescue.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+struct walk_data {
+       TDB_DATA key;
+       TDB_DATA data;
+       bool fail;
+       unsigned count;
+};
+
+static inline bool tdb_deq(TDB_DATA a, TDB_DATA b)
+{
+       return a.dsize == b.dsize && memcmp(a.dptr, b.dptr, a.dsize) == 0;
+}
+
+static inline TDB_DATA tdb_mkdata(const void *p, size_t len)
+{
+       TDB_DATA d;
+       d.dptr = (void *)p;
+       d.dsize = len;
+       return d;
+}
+
+static void walk(TDB_DATA key, TDB_DATA data, void *_wd)
+{
+       struct walk_data *wd = _wd;
+
+       if (!tdb_deq(key, wd->key)) {
+               wd->fail = true;
+       }
+
+       if (!tdb_deq(data, wd->data)) {
+               wd->fail = true;
+       }
+       wd->count++;
+}
+
+static void count_records(TDB_DATA key, TDB_DATA data, void *_wd)
+{
+       struct walk_data *wd = _wd;
+
+       if (!tdb_deq(key, wd->key) || !tdb_deq(data, wd->data))
+               diag("%.*s::%.*s\n",
+                    (int)key.dsize, key.dptr, (int)data.dsize, data.dptr);
+       wd->count++;
+}
+
+static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
+{
+       unsigned int *count = tdb_get_logging_private(tdb);
+       (*count)++;
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       struct walk_data wd;
+       unsigned int i, size, log_count = 0;
+       struct tdb_logging_context log_ctx = { log_fn, &log_count };
+
+       plan_tests(8);
+       tdb = tdb_open_ex("run-rescue.tdb", 1, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &log_ctx, NULL);
+
+       wd.key.dsize = strlen("hi");
+       wd.key.dptr = (void *)"hi";
+       wd.data.dsize = strlen("world");
+       wd.data.dptr = (void *)"world";
+       wd.count = 0;
+       wd.fail = false;
+
+       ok1(tdb_store(tdb, wd.key, wd.data, TDB_INSERT) == 0);
+
+       ok1(tdb_rescue(tdb, walk, &wd) == 0);
+       ok1(!wd.fail);
+       ok1(wd.count == 1);
+
+       /* Corrupt the database, walk should either get it or not. */
+       size = tdb->map_size;
+       for (i = sizeof(struct tdb_header); i < size; i++) {
+               char c;
+               if (tdb->methods->tdb_read(tdb, i, &c, 1, false) != 0)
+                       fail("Reading offset %i", i);
+               if (tdb->methods->tdb_write(tdb, i, "X", 1) != 0)
+                       fail("Writing X at offset %i", i);
+
+               wd.count = 0;
+               if (tdb_rescue(tdb, count_records, &wd) != 0) {
+                       wd.fail = true;
+                       break;
+               }
+               /* Could be 0 or 1. */
+               if (wd.count > 1) {
+                       wd.fail = true;
+                       break;
+               }
+               if (tdb->methods->tdb_write(tdb, i, &c, 1) != 0)
+                       fail("Restoring offset %i", i);
+       }
+       ok1(log_count == 0);
+       ok1(!wd.fail);
+       tdb_close(tdb);
+
+       /* Now try our known-corrupt db. */
+       tdb = tdb_open_ex("test/tdb.corrupt", 1024, 0, O_RDWR, 0,
+                         &taplogctx, NULL);
+       wd.count = 0;
+       ok1(tdb_rescue(tdb, count_records, &wd) == 0);
+       ok1(wd.count == 1627);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-rwlock-check.c b/lib/tdb/test/run-rwlock-check.c
new file mode 100644 (file)
index 0000000..8b8072d
--- /dev/null
@@ -0,0 +1,45 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+
+static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
+{
+       unsigned int *count = tdb_get_logging_private(tdb);
+       if (strstr(fmt, "spinlocks"))
+               (*count)++;
+}
+
+/* The code should barf on TDBs created with rwlocks. */
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       unsigned int log_count;
+       struct tdb_logging_context log_ctx = { log_fn, &log_count };
+
+       plan_tests(4);
+
+       /* We should fail to open rwlock-using tdbs of either endian. */
+       log_count = 0;
+       tdb = tdb_open_ex("test/rwlock-le.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, NULL);
+       ok1(!tdb);
+       ok1(log_count == 1);
+
+       log_count = 0;
+       tdb = tdb_open_ex("test/rwlock-be.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, NULL);
+       ok1(!tdb);
+       ok1(log_count == 1);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-summary.c b/lib/tdb/test/run-summary.c
new file mode 100644 (file)
index 0000000..2231284
--- /dev/null
@@ -0,0 +1,64 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "../common/summary.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+       unsigned int i, j;
+       struct tdb_context *tdb;
+       int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
+                       TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
+                       TDB_NOMMAP|TDB_CONVERT };
+       TDB_DATA key = { (unsigned char *)&j, sizeof(j) };
+       TDB_DATA data = { (unsigned char *)&j, sizeof(j) };
+       char *summary;
+
+       plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
+       for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+               tdb = tdb_open("run-summary.tdb", 131, flags[i],
+                              O_RDWR|O_CREAT|O_TRUNC, 0600);
+               ok1(tdb);
+               if (!tdb)
+                       continue;
+
+               /* Put some stuff in there. */
+               for (j = 0; j < 500; j++) {
+                       /* Make sure padding varies to we get some graphs! */
+                       data.dsize = j % (sizeof(j) + 1);
+                       if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
+                               fail("Storing in tdb");
+               }
+
+               summary = tdb_summary(tdb);
+               diag("%s", summary);
+               ok1(strstr(summary, "Size of file/data: "));
+               ok1(strstr(summary, "Number of records: 500\n"));
+               ok1(strstr(summary, "Smallest/average/largest keys: 4/4/4\n"));
+               ok1(strstr(summary, "Smallest/average/largest data: 0/2/4\n"));
+               ok1(strstr(summary, "Smallest/average/largest padding: "));
+               ok1(strstr(summary, "Number of dead records: 0\n"));
+               ok1(strstr(summary, "Number of free records: 1\n"));
+               ok1(strstr(summary, "Smallest/average/largest free records: "));
+               ok1(strstr(summary, "Number of hash chains: 131\n"));
+               ok1(strstr(summary, "Smallest/average/largest hash chains: "));
+               ok1(strstr(summary, "Number of uncoalesced records: 0\n"));
+               ok1(strstr(summary, "Smallest/average/largest uncoalesced runs: 0/0/0\n"));
+               ok1(strstr(summary, "Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: "));
+
+               free(summary);
+               tdb_close(tdb);
+       }
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-transaction-expand.c b/lib/tdb/test/run-transaction-expand.c
new file mode 100644 (file)
index 0000000..1271d92
--- /dev/null
@@ -0,0 +1,119 @@
+#include "../common/tdb_private.h"
+
+/* Speed up the tests, but do the actual sync tests. */
+static unsigned int sync_counts = 0;
+static inline int fake_fsync(int fd)
+{
+       sync_counts++;
+       return 0;
+}
+#define fsync fake_fsync
+
+#ifdef MS_SYNC
+static inline int fake_msync(void *addr, size_t length, int flags)
+{
+       sync_counts++;
+       return 0;
+}
+#define msync fake_msync
+#endif
+
+#ifdef HAVE_FDATASYNC
+static inline int fake_fdatasync(int fd)
+{
+       sync_counts++;
+       return 0;
+}
+#define fdatasync fake_fdatasync
+#endif
+
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+static void write_record(struct tdb_context *tdb, size_t extra_len,
+                        TDB_DATA *data)
+{
+       TDB_DATA key;
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+
+       data->dsize += extra_len;
+       tdb_transaction_start(tdb);
+       tdb_store(tdb, key, *data, TDB_REPLACE);
+       tdb_transaction_commit(tdb);
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       size_t i;
+       TDB_DATA data;
+       struct tdb_record rec;
+       tdb_off_t off;
+
+       /* Do *not* suppress sync for this test; we do it ourselves. */
+       unsetenv("TDB_NO_FSYNC");
+
+       plan_tests(5);
+       tdb = tdb_open_ex("run-transaction-expand.tdb",
+                         1024, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+       ok1(tdb);
+
+       data.dsize = 0;
+       data.dptr = calloc(1000, getpagesize());
+
+       /* Simulate a slowly growing record. */
+       for (i = 0; i < 1000; i++)
+               write_record(tdb, getpagesize(), &data);
+
+       tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off);
+       tdb_read(tdb, off, &rec, sizeof(rec), DOCONV());
+       diag("TDB size = %zu, recovery = %llu-%llu",
+            (size_t)tdb->map_size, (unsigned long long)off, (unsigned long long)(off + sizeof(rec) + rec.rec_len));
+
+       /* We should only be about 5 times larger than largest record. */
+       ok1(tdb->map_size < 6 * i * getpagesize());
+       tdb_close(tdb);
+
+       tdb = tdb_open_ex("run-transaction-expand.tdb",
+                         1024, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+       ok1(tdb);
+
+       data.dsize = 0;
+
+       /* Simulate a slowly growing record, repacking to keep
+        * recovery area at end. */
+       for (i = 0; i < 1000; i++) {
+               write_record(tdb, getpagesize(), &data);
+               if (i % 10 == 0)
+                       tdb_repack(tdb);
+       }
+
+       tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off);
+       tdb_read(tdb, off, &rec, sizeof(rec), DOCONV());
+       diag("TDB size = %zu, recovery = %llu-%llu",
+            (size_t)tdb->map_size, (unsigned long long)off, (unsigned long long)(off + sizeof(rec) + rec.rec_len));
+
+       /* We should only be about 4 times larger than largest record. */
+       ok1(tdb->map_size < 5 * i * getpagesize());
+
+       /* We should have synchronized multiple times. */
+       ok1(sync_counts);
+       tdb_close(tdb);
+       free(data.dptr);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-traverse-in-transaction.c b/lib/tdb/test/run-traverse-in-transaction.c
new file mode 100644 (file)
index 0000000..bcdc354
--- /dev/null
@@ -0,0 +1,86 @@
+#include "lock-tracking.h"
+#include "../common/tdb_private.h"
+#define fcntl fcntl_with_lockcheck
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#undef fcntl_with_lockcheck
+#include <stdlib.h>
+#include <stdbool.h>
+#include "external-agent.h"
+#include "logging.h"
+
+static struct agent *agent;
+
+static bool correct_key(TDB_DATA key)
+{
+       return key.dsize == strlen("hi")
+               && memcmp(key.dptr, "hi", key.dsize) == 0;
+}
+
+static bool correct_data(TDB_DATA data)
+{
+       return data.dsize == strlen("world")
+               && memcmp(data.dptr, "world", data.dsize) == 0;
+}
+
+static int traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
+                    void *p)
+{
+       ok1(correct_key(key));
+       ok1(correct_data(data));
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(13);
+       agent = prepare_external_agent();
+
+       tdb = tdb_open_ex("run-traverse-in-transaction.tdb",
+                         1024, TDB_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR,
+                         0600, &taplogctx, NULL);
+       ok1(tdb);
+
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data.dptr = (void *)"world";
+       data.dsize = strlen("world");
+
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+
+       ok1(external_agent_operation(agent, OPEN, tdb_name(tdb)) == SUCCESS);
+
+       ok1(tdb_transaction_start(tdb) == 0);
+       ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
+           == WOULD_HAVE_BLOCKED);
+       tdb_traverse(tdb, traverse, NULL);
+
+       /* That should *not* release the transaction lock! */
+       ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
+           == WOULD_HAVE_BLOCKED);
+       tdb_traverse_read(tdb, traverse, NULL);
+
+       /* That should *not* release the transaction lock! */
+       ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
+           == WOULD_HAVE_BLOCKED);
+       ok1(tdb_transaction_commit(tdb) == 0);
+       /* Now we should be fine. */
+       ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb))
+           == SUCCESS);
+
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-wronghash-fail.c b/lib/tdb/test/run-wronghash-fail.c
new file mode 100644 (file)
index 0000000..74bbc30
--- /dev/null
@@ -0,0 +1,120 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+
+static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
+{
+       unsigned int *count = tdb_get_logging_private(tdb);
+       if (strstr(fmt, "hash"))
+               (*count)++;
+}
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       unsigned int log_count;
+       TDB_DATA d;
+       struct tdb_logging_context log_ctx = { log_fn, &log_count };
+
+       plan_tests(28);
+
+       /* Create with default hash. */
+       log_count = 0;
+       tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0,
+                         O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, NULL);
+       ok1(tdb);
+       ok1(log_count == 0);
+       d.dptr = (void *)"Hello";
+       d.dsize = 5;
+       ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
+       tdb_close(tdb);
+
+       /* Fail to open with different hash. */
+       tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, tdb_jenkins_hash);
+       ok1(!tdb);
+       ok1(log_count == 1);
+
+       /* Create with different hash. */
+       log_count = 0;
+       tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0,
+                         O_CREAT|O_RDWR|O_TRUNC,
+                         0600, &log_ctx, tdb_jenkins_hash);
+       ok1(tdb);
+       ok1(log_count == 0);
+       tdb_close(tdb);
+
+       /* Endian should be no problem. */
+       log_count = 0;
+       tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, tdb_old_hash);
+       ok1(!tdb);
+       ok1(log_count == 1);
+
+       log_count = 0;
+       tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, tdb_old_hash);
+       ok1(!tdb);
+       ok1(log_count == 1);
+
+       log_count = 0;
+       /* Fail to open with old default hash. */
+       tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, tdb_old_hash);
+       ok1(!tdb);
+       ok1(log_count == 1);
+
+       log_count = 0;
+       tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDONLY,
+                         0, &log_ctx, tdb_jenkins_hash);
+       ok1(tdb);
+       ok1(log_count == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       log_count = 0;
+       tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDONLY,
+                         0, &log_ctx, tdb_jenkins_hash);
+       ok1(tdb);
+       ok1(log_count == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       /* It should open with jenkins hash if we don't specify. */
+       log_count = 0;
+       tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, NULL);
+       ok1(tdb);
+       ok1(log_count == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       log_count = 0;
+       tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDWR, 0,
+                         &log_ctx, NULL);
+       ok1(tdb);
+       ok1(log_count == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+       log_count = 0;
+       tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDONLY,
+                         0, &log_ctx, NULL);
+       ok1(tdb);
+       ok1(log_count == 0);
+       ok1(tdb_check(tdb, NULL, NULL) == 0);
+       tdb_close(tdb);
+
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run-zero-append.c b/lib/tdb/test/run-zero-append.c
new file mode 100644 (file)
index 0000000..36bf699
--- /dev/null
@@ -0,0 +1,40 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(4);
+       tdb = tdb_open_ex(NULL, 1024, TDB_INTERNAL, O_CREAT|O_TRUNC|O_RDWR,
+                         0600, &taplogctx, NULL);
+       ok1(tdb);
+
+       /* Tickle bug on appending zero length buffer to zero length buffer. */
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data.dptr = (void *)"world";
+       data.dsize = 0;
+
+       ok1(tdb_append(tdb, key, data) == 0);
+       ok1(tdb_append(tdb, key, data) == 0);
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == 0);
+       tdb_close(tdb);
+       free(data.dptr);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/run.c b/lib/tdb/test/run.c
new file mode 100644 (file)
index 0000000..f49e850
--- /dev/null
@@ -0,0 +1,49 @@
+#include "../common/tdb_private.h"
+#include "../common/io.c"
+#include "../common/tdb.c"
+#include "../common/lock.c"
+#include "../common/freelist.c"
+#include "../common/traverse.c"
+#include "../common/transaction.c"
+#include "../common/error.c"
+#include "../common/open.c"
+#include "../common/check.c"
+#include "../common/hash.c"
+#include "tap-interface.h"
+#include <stdlib.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+       struct tdb_context *tdb;
+       TDB_DATA key, data;
+
+       plan_tests(10);
+       tdb = tdb_open_ex("run.tdb", 1024, TDB_CLEAR_IF_FIRST,
+                         O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
+
+       ok1(tdb);
+       key.dsize = strlen("hi");
+       key.dptr = (void *)"hi";
+       data.dsize = strlen("world");
+       data.dptr = (void *)"world";
+
+       ok1(tdb_store(tdb, key, data, TDB_MODIFY) < 0);
+       ok1(tdb_error(tdb) == TDB_ERR_NOEXIST);
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+       ok1(tdb_store(tdb, key, data, TDB_INSERT) < 0);
+       ok1(tdb_error(tdb) == TDB_ERR_EXISTS);
+       ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0);
+
+       data = tdb_fetch(tdb, key);
+       ok1(data.dsize == strlen("world"));
+       ok1(memcmp(data.dptr, "world", strlen("world")) == 0);
+       free(data.dptr);
+
+       key.dsize++;
+       data = tdb_fetch(tdb, key);
+       ok1(data.dptr == NULL);
+       tdb_close(tdb);
+
+       return exit_status();
+}
diff --git a/lib/tdb/test/rwlock-be.tdb b/lib/tdb/test/rwlock-be.tdb
new file mode 100644 (file)
index 0000000..45b5f09
Binary files /dev/null and b/lib/tdb/test/rwlock-be.tdb differ
diff --git a/lib/tdb/test/rwlock-le.tdb b/lib/tdb/test/rwlock-le.tdb
new file mode 100644 (file)
index 0000000..45b5f09
Binary files /dev/null and b/lib/tdb/test/rwlock-le.tdb differ
diff --git a/lib/tdb/test/tap-interface.h b/lib/tdb/test/tap-interface.h
new file mode 100644 (file)
index 0000000..d9ed6e8
--- /dev/null
@@ -0,0 +1,39 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Simplistic implementation of tap interface.
+
+   Copyright (C) Rusty Russell 2012
+   
+     ** NOTE! The following LGPL license applies to the talloc
+     ** 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 <stdio.h>
+
+#ifndef __location__
+#define __TAP_STRING_LINE1__(s)    #s
+#define __TAP_STRING_LINE2__(s)   __TAP_STRING_LINE1__(s)
+#define __TAP_STRING_LINE3__  __TAP_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TAP_STRING_LINE3__
+#endif
+
+#define plan_tests(num)
+#define ok(e, ...) do { if (e) { (void)printf("."); } else { fprintf(stderr, __VA_ARGS__); exit(1); } } while(0)
+#define ok1(e) ok((e), "%s:%s", __location__, #e)
+#define pass(...) printf(".")
+#define fail(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while(0)
+#define diag printf
+#define exit_status() 0
diff --git a/lib/tdb/test/tap-to-subunit.h b/lib/tdb/test/tap-to-subunit.h
new file mode 100644 (file)
index 0000000..a5cf74f
--- /dev/null
@@ -0,0 +1,155 @@
+#ifndef TAP_TO_SUBUNIT_H
+#define TAP_TO_SUBUNIT_H
+/*
+ * tap-style wrapper for subunit.
+ *
+ * Copyright (c) 2011 Rusty Russell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "replace.h"
+
+/**
+ * plan_tests - announce the number of tests you plan to run
+ * @tests: the number of tests
+ *
+ * This should be the first call in your test program: it allows tracing
+ * of failures which mean that not all tests are run.
+ *
+ * If you don't know how many tests will actually be run, assume all of them
+ * and use skip() if you don't actually run some tests.
+ *
+ * Example:
+ *     plan_tests(13);
+ */
+void plan_tests(unsigned int tests);
+
+/**
+ * ok1 - Simple conditional test
+ * @e: the expression which we expect to be true.
+ *
+ * This is the simplest kind of test: if the expression is true, the
+ * test passes.  The name of the test which is printed will simply be
+ * file name, line number, and the expression itself.
+ *
+ * Example:
+ *     ok1(somefunc() == 1);
+ */
+# define ok1(e) ((e) ?                                                 \
+                _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+                _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+/**
+ * ok - Conditional test with a name
+ * @e: the expression which we expect to be true.
+ * @...: the printf-style name of the test.
+ *
+ * If the expression is true, the test passes.  The name of the test will be
+ * the filename, line number, and the printf-style string.  This can be clearer
+ * than simply the expression itself.
+ *
+ * Example:
+ *     ok1(somefunc() == 1);
+ *     ok(somefunc() == 0, "Second somefunc() should fail");
+ */
+# define ok(e, ...) ((e) ?                                             \
+                    _gen_result(1, __func__, __FILE__, __LINE__,       \
+                                __VA_ARGS__) :                         \
+                    _gen_result(0, __func__, __FILE__, __LINE__,       \
+                                __VA_ARGS__))
+
+/**
+ * pass - Note that a test passed
+ * @...: the printf-style name of the test.
+ *
+ * For complicated code paths, it can be easiest to simply call pass() in one
+ * branch and fail() in another.
+ *
+ * Example:
+ *     int x = somefunc();
+ *     if (x > 0)
+ *             pass("somefunc() returned a valid value");
+ *     else
+ *             fail("somefunc() returned an invalid value");
+ */
+# define pass(...) ok(1, __VA_ARGS__)
+
+/**
+ * fail - Note that a test failed
+ * @...: the printf-style name of the test.
+ *
+ * For complicated code paths, it can be easiest to simply call pass() in one
+ * branch and fail() in another.
+ */
+# define fail(...) ok(0, __VA_ARGS__)
+
+unsigned int _gen_result(int, const char *, const char *, unsigned int,
+   const char *, ...) PRINTF_ATTRIBUTE(5, 6);
+
+/**
+ * diag - print a diagnostic message (use instead of printf/fprintf)
+ * @fmt: the format of the printf-style message
+ *
+ * diag ensures that the output will not be considered to be a test
+ * result by the TAP test harness.  It will append '\n' for you.
+ *
+ * Example:
+ *     diag("Now running complex tests");
+ */
+void diag(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+
+/**
+ * skip - print a diagnostic message (use instead of printf/fprintf)
+ * @n: number of tests you're skipping.
+ * @fmt: the format of the reason you're skipping the tests.
+ *
+ * Sometimes tests cannot be run because the test system lacks some feature:
+ * you should explicitly document that you're skipping tests using skip().
+ *
+ * From the Test::More documentation:
+ *   If it's something the user might not be able to do, use SKIP.  This
+ *   includes optional modules that aren't installed, running under an OS that
+ *   doesn't have some feature (like fork() or symlinks), or maybe you need an
+ *   Internet connection and one isn't available.
+ *
+ * Example:
+ *     #ifdef HAVE_SOME_FEATURE
+ *     ok1(somefunc());
+ *     #else
+ *     skip(1, "Don't have SOME_FEATURE");
+ *     #endif
+ */
+void skip(unsigned int n, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
+
+/**
+ * exit_status - the value that main should return.
+ *
+ * For maximum compatibility your test program should return a particular exit
+ * code (ie. 0 if all tests were run, and every test which was expected to
+ * succeed succeeded).
+ *
+ * Example:
+ *     exit(exit_status());
+ */
+int exit_status(void);
+#endif /* CCAN_TAP_H */
diff --git a/lib/tdb/test/tdb.corrupt b/lib/tdb/test/tdb.corrupt
new file mode 100644 (file)
index 0000000..83d6677
Binary files /dev/null and b/lib/tdb/test/tdb.corrupt differ
index 6aca8dd99c50bfdcef1c26cd2bc4bd2e154fcaaa..276a281e46f2f6f5e1d914ef58c481fa0ef39179 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    low level tdb backup and restore utility
    Copyright (C) Andrew Tridgell              2002
@@ -7,12 +7,12 @@
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
@@ -61,7 +61,7 @@ static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const c
 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
 {
        va_list ap;
-    
+
        va_start(ap, format);
        vfprintf(stdout, format, ap);
        va_end(ap);
@@ -122,7 +122,7 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
        }
 
        /* open the old tdb */
-       tdb = tdb_open_ex(old_name, 0, 0, 
+       tdb = tdb_open_ex(old_name, 0, 0,
                          O_RDWR, 0, &log_ctx, NULL);
        if (!tdb) {
                printf("Failed to open %s\n", old_name);
@@ -132,10 +132,10 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
 
        /* create the new tdb */
        unlink(tmp_name);
-       tdb_new = tdb_open_ex(tmp_name, 
-                             hash_size ? hash_size : tdb_hash_size(tdb), 
-                             TDB_DEFAULT, 
-                             O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777, 
+       tdb_new = tdb_open_ex(tmp_name,
+                             hash_size ? hash_size : tdb_hash_size(tdb),
+                             TDB_DEFAULT,
+                             O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777,
                              &log_ctx, NULL);
        if (!tdb_new) {
                perror(tmp_name);
@@ -152,8 +152,9 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
                return 1;
        }
 
-       if (tdb_transaction_start(tdb_new) != 0) {
-               printf("Failed to start transaction on new tdb\n");
+       /* lock the backup tdb so that nobody else can change it */
+       if (tdb_lockall(tdb_new) != 0) {
+               printf("Failed to lock backup tdb\n");
                tdb_close(tdb);
                tdb_close(tdb_new);
                unlink(tmp_name);
@@ -177,19 +178,23 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
        /* close the old tdb */
        tdb_close(tdb);
 
-       if (tdb_transaction_commit(tdb_new) != 0) {
-               fprintf(stderr, "Failed to commit new tdb\n");
-               tdb_close(tdb_new);
-               unlink(tmp_name);
-               free(tmp_name);         
-               return 1;
+       /* copy done, unlock the backup tdb */
+       tdb_unlockall(tdb_new);
+
+#ifdef HAVE_FDATASYNC
+       if (fdatasync(tdb_fd(tdb_new)) != 0) {
+#else
+       if (fsync(tdb_fd(tdb_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);
-       tdb_new = tdb_open_ex(tmp_name, 
+       tdb_new = tdb_open_ex(tmp_name,
                              0,
-                             TDB_DEFAULT, 
+                             TDB_DEFAULT,
                              O_RDONLY, 0,
                              &log_ctx, NULL);
        if (!tdb_new) {
@@ -199,7 +204,7 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
                free(tmp_name);
                return 1;
        }
-       
+
        /* traverse the new tdb to confirm */
        count2 = tdb_traverse(tdb_new, test_fn, NULL);
        if (count2 != count1) {
@@ -232,7 +237,7 @@ static int verify_tdb(const char *fname, const char *bak_name)
        int count = -1;
 
        /* open the tdb */
-       tdb = tdb_open_ex(fname, 0, 0, 
+       tdb = tdb_open_ex(fname, 0, 0,
                          O_RDONLY, 0, &log_ctx, NULL);
 
        /* traverse the tdb, then close it */
@@ -275,7 +280,6 @@ static void usage(void)
        printf("   -v            verify mode (restore if corrupt)\n");
        printf("   -n hashsize   set the new hash size for the backup\n");
 }
-               
 
  int main(int argc, char *argv[])
 {
index 027fda3d424209c6aff2af6ce9df540546a46850..e66ea5685b8541579ebc6e2d59259aebc356b780 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    simple tdb dump util
    Copyright (C) Andrew Tridgell              2001
@@ -7,12 +7,12 @@
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
@@ -51,19 +51,66 @@ static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *stat
        return 0;
 }
 
-static int dump_tdb(const char *fname, const char *keyname)
+static void log_stderr(struct tdb_context *tdb, enum tdb_debug_level level,
+                      const char *fmt, ...)
+{
+       va_list ap;
+       const char *name = tdb_name(tdb);
+       const char *prefix = "";
+
+       if (!name)
+               name = "unnamed";
+
+       switch (level) {
+       case TDB_DEBUG_ERROR:
+               prefix = "ERROR: ";
+               break;
+       case TDB_DEBUG_WARNING:
+               prefix = "WARNING: ";
+               break;
+       case TDB_DEBUG_TRACE:
+               return;
+
+       default:
+       case TDB_DEBUG_FATAL:
+               prefix = "FATAL: ";
+               break;
+       }
+
+       va_start(ap, fmt);
+       fprintf(stderr, "tdb(%s): %s", name, prefix);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+}
+
+static void emergency_walk(TDB_DATA key, TDB_DATA dbuf, void *keyname)
+{
+       if (keyname) {
+               if (key.dsize != strlen(keyname))
+                       return;
+               if (memcmp(key.dptr, keyname, key.dsize) != 0)
+                       return;
+       }
+       traverse_fn(NULL, key, dbuf, NULL);
+}
+
+static int dump_tdb(const char *fname, const char *keyname, bool emergency)
 {
        TDB_CONTEXT *tdb;
        TDB_DATA key, value;
-       
-       tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+       struct tdb_logging_context logfn = { log_stderr };
+
+       tdb = tdb_open_ex(fname, 0, 0, O_RDONLY, 0, &logfn, NULL);
        if (!tdb) {
                printf("Failed to open %s\n", fname);
                return 1;
        }
 
+       if (emergency) {
+               return tdb_rescue(tdb, emergency_walk, keyname) == 0;
+       }
        if (!keyname) {
-               tdb_traverse(tdb, traverse_fn, NULL);
+               return tdb_traverse(tdb, traverse_fn, NULL) == -1 ? 1 : 0;
        } else {
                key.dptr = discard_const_p(uint8_t, keyname);
                key.dsize = strlen(keyname);
@@ -84,11 +131,13 @@ static void usage( void)
        printf( "Usage: tdbdump [options] <filename>\n\n");
        printf( "   -h          this help message\n");
        printf( "   -k keyname  dumps value of keyname\n");
+       printf( "   -e          emergency dump, for corrupt databases\n");
 }
 
  int main(int argc, char *argv[])
 {
        char *fname, *keyname=NULL;
+       bool emergency = false;
        int c;
 
        if (argc < 2) {
@@ -96,7 +145,7 @@ static void usage( void)
                exit(1);
        }
 
-       while ((c = getopt( argc, argv, "hk:")) != -1) {
+       while ((c = getopt( argc, argv, "hk:e")) != -1) {
                switch (c) {
                case 'h':
                        usage();
@@ -104,6 +153,9 @@ static void usage( void)
                case 'k':
                        keyname = optarg;
                        break;
+               case 'e':
+                       emergency = true;
+                       break;
                default:
                        usage();
                        exit( 1);
@@ -112,5 +164,5 @@ static void usage( void)
 
        fname = argv[optind];
 
-       return dump_tdb(fname, keyname);
+       return dump_tdb(fname, keyname, emergency);
 }
index 485c440df1f9c871078ce889adab94cc98ccbb47..f65b36fa12e448c8c8820f4bef23300ad3a2df00 100644 (file)
@@ -5,7 +5,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include <assert.h>
@@ -26,8 +25,6 @@
 #include "system/wait.h"
 #include "tdb.h"
 
-#define debug_fprintf(file, fmt, ...) do {/*nothing*/} while (0)
-
 static int read_linehead(FILE *f)
 {
        int i, c;
@@ -39,7 +36,7 @@ static int read_linehead(FILE *f)
                if (c == EOF) {
                        return -1;
                }
-               if (c == '\(') {
+               if (c == '(') {
                        break;
                }
        }
@@ -171,7 +168,7 @@ static int read_rec(FILE *f, TDB_CONTEXT *tdb, int *eof)
            || (swallow(f, "}\n", NULL) == -1)) {
                goto fail;
        }
-       if (tdb_store(tdb, key, data, TDB_INSERT) == -1) {
+       if (tdb_store(tdb, key, data, TDB_INSERT) != 0) {
                fprintf(stderr, "TDB error: %s\n", tdb_errorstr(tdb));
                goto fail;
        }
@@ -207,7 +204,6 @@ static int restore_tdb(const char *fname)
                fprintf(stderr, "Error closing tdb\n");
                return 1;
        }
-       fprintf(stderr, "EOF\n");
        return 0;
 }
 
index 416bc50a5b073c07fad11c2d10a7a08fdb9253ae..0be35dc30383b8c70da46f8af08eeeb5c05ee34a 100644 (file)
@@ -24,7 +24,7 @@ static void _start_timer(void)
 static double _end_timer(void)
 {
        gettimeofday(&tp2,NULL);
-       return((tp2.tv_sec - tp1.tv_sec) + 
+       return((tp2.tv_sec - tp1.tv_sec) +
               (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
 }
 
@@ -40,7 +40,7 @@ static void tdb_log(struct tdb_context *tdb, int level, const char *format, ...)
 static void tdb_log(struct tdb_context *tdb, int level, const char *format, ...)
 {
        va_list ap;
-    
+
        va_start(ap, format);
        vfprintf(stdout, format, ap);
        va_end(ap);
@@ -189,15 +189,15 @@ static void merge_test(void)
        char keys[5][2];
        char tdata[] = "test";
        TDB_DATA key, data;
-       
+
        for (i = 0; i < 5; i++) {
                snprintf(keys[i],2, "%d", i);
                key.dptr = keys[i];
                key.dsize = 2;
-               
+
                data.dptr = tdata;
                data.dsize = 4;
-               
+
                if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
                        fatal("tdb_store failed");
                }
@@ -215,18 +215,40 @@ static void merge_test(void)
        tdb_delete(db, key);
 }
 
+static char *test_path(const char *filename)
+{
+       const char *prefix = getenv("TEST_DATA_PREFIX");
+
+       if (prefix) {
+               char *path = NULL;
+               int ret;
+
+               ret = asprintf(&path, "%s/%s", prefix, filename);
+               if (ret == -1) {
+                       return NULL;
+               }
+               return path;
+       }
+
+       return strdup(filename);
+}
+
  int main(int argc, const char *argv[])
 {
        int i, seed=0;
        int loops = 10000;
        int num_entries;
-       char test_gdbm[] = "test.gdbm";
+       char test_gdbm[1] = "test.gdbm";
+       char *test_tdb;
+
+       test_gdbm[0] = test_path("test.gdbm");
+       test_tdb = test_path("test.tdb");
 
-       unlink("test.gdbm");
+       unlink(test_gdbm[0]);
 
-       db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST, 
+       db = tdb_open(test_tdb, 0, TDB_CLEAR_IF_FIRST,
                      O_RDWR | O_CREAT | O_TRUNC, 0600);
-       gdbm = gdbm_open(test_gdbm, 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, 
+       gdbm = gdbm_open(test_gdbm, 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST,
                         0600, NULL);
 
        if (!db || !gdbm) {
@@ -261,5 +283,8 @@ static void merge_test(void)
        tdb_close(db);
        gdbm_close(gdbm);
 
+       free(test_gdbm[0]);
+       free(test_tdb);
+
        return 0;
 }
index b380883e0ac247e18b4b324fd50cdd6e8e4830d0..d00779611c5136f8170d64cf5079cc05746034e3 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    Samba database functions
    Copyright (C) Andrew Tridgell              1999-2000
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
@@ -61,6 +61,7 @@ enum commands {
        CMD_NEXT,
        CMD_SYSTEM,
        CMD_CHECK,
+       CMD_REPACK,
        CMD_QUIT,
        CMD_HELP
 };
@@ -98,6 +99,7 @@ COMMAND_TABLE cmd_table[] = {
        {"quit",        CMD_QUIT},
        {"q",           CMD_QUIT},
        {"!",           CMD_SYSTEM},
+       {"repack",      CMD_REPACK},
        {NULL,          CMD_HELP}
 };
 
@@ -111,7 +113,7 @@ static void _start_timer(void)
 static double _end_timer(void)
 {
        gettimeofday(&tp2,NULL);
-       return((tp2.tv_sec - tp1.tv_sec) + 
+       return((tp2.tv_sec - tp1.tv_sec) +
               (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
 }
 
@@ -158,7 +160,7 @@ static void print_data(const char *buf,int len)
                printf("%02X ",(int)((unsigned char)buf[i]));
                i++;
                if (i%8 == 0) printf(" ");
-               if (i%16 == 0) {      
+               if (i%16 == 0) {
                        print_asc(&buf[i-16],8); printf(" ");
                        print_asc(&buf[i-8],8); printf("\n");
                        if (i<len) printf("[%03X] ",i);
@@ -166,18 +168,18 @@ static void print_data(const char *buf,int len)
        }
        if (i%16) {
                int n;
-               
+
                n = 16 - (i%16);
                printf(" ");
                if (n>8) printf(" ");
                while (n--) printf("   ");
-               
+
                n = i%16;
                if (n > 8) n = 8;
                print_asc(&buf[i-(i%16)],n); printf(" ");
                n = (i%16) - n;
-               if (n>0) print_asc(&buf[i-n],n); 
-               printf("\n");    
+               if (n>0) print_asc(&buf[i-n],n);
+               printf("\n");
        }
 }
 
@@ -203,6 +205,7 @@ static void help(void)
 "  list                 : print the database hash table and freelist\n"
 "  free                 : print the database freelist\n"
 "  check                : check the integrity of an opened database\n"
+"  repack               : repack the database\n"
 "  speed                : perform speed tests on the database\n"
 "  ! command            : execute system command\n"
 "  1 | first            : print the first record\n"
@@ -257,7 +260,7 @@ static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
        dbuf.dptr = (unsigned char *)data;
        dbuf.dsize = datalen;
 
-       if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+       if (tdb_store(tdb, key, dbuf, TDB_INSERT) != 0) {
                terror("insert failed");
        }
 }
@@ -284,7 +287,7 @@ static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
        printf("Storing key:\n");
        print_rec(tdb, key, dbuf, NULL);
 
-       if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+       if (tdb_store(tdb, key, dbuf, TDB_REPLACE) != 0) {
                terror("store failed");
        }
 }
@@ -306,11 +309,11 @@ static void show_tdb(char *keyname, size_t keylen)
            terror("fetch failed");
            return;
        }
-       
+
        print_rec(tdb, key, dbuf, NULL);
-       
+
        free( dbuf.dptr );
-       
+
        return;
 }
 
@@ -354,23 +357,23 @@ static void move_rec(char *keyname, size_t keylen, char* tdbname)
                terror("fetch failed");
                return;
        }
-       
+
        print_rec(tdb, key, dbuf, NULL);
-       
+
        dst_tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
        if ( !dst_tdb ) {
                terror("unable to open destination tdb");
                return;
        }
-       
-       if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
+
+       if (tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) != 0) {
                terror("failed to move record");
        }
        else
                printf("record moved\n");
-       
+
        tdb_close( dst_tdb );
-       
+
        return;
 }
 
@@ -409,12 +412,14 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *
 
 static void info_tdb(void)
 {
-       int count;
-       total_bytes = 0;
-       if ((count = tdb_traverse(tdb, traverse_fn, NULL)) == -1)
+       char *summary = tdb_summary(tdb);
+
+       if (!summary) {
                printf("Error = %s\n", tdb_errorstr(tdb));
-       else
-               printf("%d records totalling %d bytes\n", count, total_bytes);
+       } else {
+               printf("%s", summary);
+               free(summary);
+       }
 }
 
 static void speed_tdb(const char *tlimit)
@@ -445,12 +450,9 @@ static void speed_tdb(const char *tlimit)
        printf("Testing fetch speed for %u seconds\n", timelimit);
        _start_timer();
        do {
-               long int r = random();
-               TDB_DATA key, dbuf;
+               TDB_DATA key;
                key.dptr = discard_const_p(uint8_t, str);
                key.dsize = strlen((char *)key.dptr);
-               dbuf.dptr = (uint8_t *) &r;
-               dbuf.dsize = sizeof(r);
                tdb_fetch(tdb, key);
                t = _end_timer();
                ops++;
@@ -518,7 +520,7 @@ static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
 {
        TDB_DATA dbuf;
        *pkey = tdb_firstkey(the_tdb);
-       
+
        dbuf = tdb_fetch(the_tdb, *pkey);
        if (!dbuf.dptr) terror("fetch failed");
        else {
@@ -530,17 +532,17 @@ static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
 {
        TDB_DATA dbuf;
        *pkey = tdb_nextkey(the_tdb, *pkey);
-       
+
        dbuf = tdb_fetch(the_tdb, *pkey);
-       if (!dbuf.dptr) 
+       if (!dbuf.dptr)
                terror("fetch failed");
        else
                print_rec(the_tdb, *pkey, dbuf, NULL);
 }
 
-static int count(TDB_DATA key, TDB_DATA data, void *private)
+static int count(TDB_DATA key, TDB_DATA data, void *private_data)
 {
-       (*(unsigned int *)private)++;
+       (*(unsigned int *)private_data)++;
        return 0;
 }
 
@@ -609,6 +611,10 @@ static int do_command(void)
                        bIterate = 0;
                        tdb_transaction_commit(tdb);
                        return 0;
+               case CMD_REPACK:
+                       bIterate = 0;
+                       tdb_repack(tdb);
+                       return 0;
                case CMD_TRANSACTION_CANCEL:
                        bIterate = 0;
                        tdb_transaction_cancel(tdb);
@@ -691,7 +697,7 @@ static int do_command(void)
        return 0;
 }
 
-static char *convert_string(char *instring, size_t *sizep)
+static char *tdb_convert_string(char *instring, size_t *sizep)
 {
        size_t length = 0;
        char *outp, *inp;
@@ -757,15 +763,15 @@ int main(int argc, char *argv[])
                                        }
                                }
                        }
-                       if (arg1) arg1 = convert_string(arg1,&arg1len);
-                       if (arg2) arg2 = convert_string(arg2,&arg2len);
+                       if (arg1) arg1 = tdb_convert_string(arg1,&arg1len);
+                       if (arg2) arg2 = tdb_convert_string(arg2,&arg2len);
                        if (do_command()) break;
                }
                break;
        case 5:
-               arg2 = convert_string(argv[4],&arg2len);
+               arg2 = tdb_convert_string(argv[4],&arg2len);
        case 4:
-               arg1 = convert_string(argv[3],&arg1len);
+               arg1 = tdb_convert_string(argv[3],&arg1len);
        case 3:
                cmdname = argv[2];
        default:
index 79fe3cd5e0e9ba6dd9c95b882770b5f2fde37f43..a23d1543e57321147aab6f9ac003b8b3e3b582f7 100644 (file)
@@ -1,5 +1,5 @@
 /* this tests tdb by doing lots of ops from several simultaneous
-   writers - that stresses the locking code. 
+   writers - that stresses the locking code.
 */
 
 #include "replace.h"
@@ -59,7 +59,7 @@ static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const c
                system(ptr);
                free(ptr);
        }
-#endif 
+#endif
 }
 
 static void fatal(const char *why)
@@ -228,9 +228,9 @@ static void send_count_and_suicide(int sig)
        kill(getpid(), SIGUSR2);
 }
 
-static int run_child(int i, int seed, unsigned num_loops, unsigned start)
+static int run_child(const char *filename, int i, int seed, unsigned num_loops, unsigned start)
 {
-       db = tdb_open_ex("torture.tdb", hash_size, TDB_DEFAULT,
+       db = tdb_open_ex(filename, hash_size, TDB_DEFAULT,
                         O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
        if (!db) {
                fatal("db open failed");
@@ -270,6 +270,24 @@ static int run_child(int i, int seed, unsigned num_loops, unsigned start)
        return (error_count < 100 ? error_count : 100);
 }
 
+static char *test_path(const char *filename)
+{
+       const char *prefix = getenv("TEST_DATA_PREFIX");
+
+       if (prefix) {
+               char *path = NULL;
+               int ret;
+
+               ret = asprintf(&path, "%s/%s", prefix, filename);
+               if (ret == -1) {
+                       return NULL;
+               }
+               return path;
+       }
+
+       return strdup(filename);
+}
+
 int main(int argc, char * const *argv)
 {
        int i, seed = -1;
@@ -280,6 +298,7 @@ int main(int argc, char * const *argv)
        pid_t *pids;
        int kill_random = 0;
        int *done;
+       char *test_tdb;
 
        log_ctx.log_fn = tdb_log;
 
@@ -308,7 +327,9 @@ int main(int argc, char * const *argv)
                }
        }
 
-       unlink("torture.tdb");
+       test_tdb = test_path("torture.tdb");
+
+       unlink(test_tdb);
 
        if (seed == -1) {
                seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
@@ -316,7 +337,7 @@ 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(0, seed, num_loops, 0);
+               error_count = run_child(test_tdb, 0, seed, num_loops, 0);
                goto done;
        }
 
@@ -336,7 +357,7 @@ int main(int argc, char * const *argv)
                                printf("Testing with %d processes, %d loops, %d hash_size, seed=%d%s\n",
                                       num_procs, num_loops, hash_size, seed, always_transaction ? " (all within transactions)" : "");
                        }
-                       exit(run_child(i, seed, num_loops, 0));
+                       exit(run_child(test_tdb, i, seed, num_loops, 0));
                }
        }
 
@@ -389,8 +410,8 @@ int main(int argc, char * const *argv)
                                }
                                pids[j] = fork();
                                if (pids[j] == 0)
-                                       exit(run_child(j, seed, num_loops,
-                                                      done[j]));
+                                       exit(run_child(test_tdb, j, seed,
+                                                      num_loops, done[j]));
                                printf("Restarting child %i for %u-%u\n",
                                       j, done[j], num_loops);
                                continue;
@@ -414,18 +435,20 @@ int main(int argc, char * const *argv)
 
 done:
        if (error_count == 0) {
-               db = tdb_open_ex("torture.tdb", hash_size, TDB_DEFAULT,
+               db = tdb_open_ex(test_tdb, hash_size, TDB_DEFAULT,
                                 O_RDWR, 0, &log_ctx, NULL);
                if (!db) {
-                       fatal("db open failed");
+                       fatal("db open failed\n");
+                       exit(1);
                }
                if (tdb_check(db, NULL, NULL) == -1) {
-                       printf("db check failed");
+                       printf("db check failed\n");
                        exit(1);
                }
                tdb_close(db);
                printf("OK\n");
        }
 
+       free(test_tdb);
        return error_count;
 }
diff --git a/lib/tdb/wscript b/lib/tdb/wscript
new file mode 100644 (file)
index 0000000..00a1c34
--- /dev/null
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+
+APPNAME = 'tdb'
+VERSION = '1.2.12'
+
+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
+
+samba_dist.DIST_DIRS('lib/tdb:. lib/replace:lib/replace buildtools:buildtools')
+
+def set_options(opt):
+    opt.BUILTIN_DEFAULT('replace')
+    opt.PRIVATE_EXTENSION_DEFAULT('tdb', noextension='tdb')
+    opt.RECURSE('lib/replace')
+    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):
+    conf.RECURSE('lib/replace')
+
+    conf.env.standalone_tdb = conf.IN_LAUNCH_DIR()
+    conf.env.building_tdb = True
+
+    if not conf.env.standalone_tdb:
+        if conf.CHECK_BUNDLED_SYSTEM_PKG('tdb', minversion=VERSION,
+                                     implied_deps='replace'):
+            conf.define('USING_SYSTEM_TDB', 1)
+            conf.env.building_tdb = False
+            if conf.CHECK_BUNDLED_SYSTEM_PYTHON('pytdb', 'tdb', minversion=VERSION):
+                conf.define('USING_SYSTEM_PYTDB', 1)
+
+    conf.env.disable_python = getattr(Options.options, 'disable_python', False)
+
+    conf.CHECK_XSLTPROC_MANPAGES()
+
+    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
+
+    conf.SAMBA_CONFIG_H()
+
+    conf.SAMBA_CHECK_UNDEFINED_SYMBOL_FLAGS()
+
+def build(bld):
+    bld.RECURSE('lib/replace')
+
+    COMMON_SRC = bld.SUBDIR('common',
+                            '''check.c error.c tdb.c traverse.c
+                            freelistcheck.c lock.c dump.c freelist.c
+                            io.c open.c transaction.c hash.c summary.c rescue.c''')
+
+    if bld.env.standalone_tdb:
+        bld.env.PKGCONFIGDIR = '${LIBDIR}/pkgconfig'
+        private_library = False
+    else:
+        private_library = True
+
+    if not bld.CONFIG_SET('USING_SYSTEM_TDB'):
+        bld.SAMBA_LIBRARY('tdb',
+                          COMMON_SRC,
+                          deps='replace',
+                          includes='include',
+                          abi_directory='ABI',
+                          abi_match='tdb_*',
+                          hide_symbols=True,
+                          vnum=VERSION,
+                          public_headers='include/tdb.h',
+                          public_headers_install=not private_library,
+                          pc_files='tdb.pc',
+                          private_library=private_library)
+
+        bld.SAMBA_BINARY('tdbtorture',
+                         'tools/tdbtorture.c',
+                         'tdb',
+                         install=False)
+
+        bld.SAMBA_BINARY('tdbrestore',
+                         'tools/tdbrestore.c',
+                         'tdb', manpages='man/tdbrestore.8')
+
+        bld.SAMBA_BINARY('tdbdump',
+                         'tools/tdbdump.c',
+                         'tdb', manpages='man/tdbdump.8')
+
+        bld.SAMBA_BINARY('tdbbackup',
+                         'tools/tdbbackup.c',
+                         'tdb',
+                         manpages='man/tdbbackup.8')
+
+        bld.SAMBA_BINARY('tdbtool',
+                         'tools/tdbtool.c',
+                         'tdb', manpages='man/tdbtool.8')
+
+        # FIXME: This hardcoded list is stupid, stupid, stupid.
+        bld.SAMBA_SUBSYSTEM('tdb-test-helpers',
+                            'test/external-agent.c test/lock-tracking.c test/logging.c',
+                            'replace',
+                            includes='include')
+
+        bld.SAMBA_BINARY('tdb1-run-3G-file', 'test/run-3G-file.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-bad-tdb-header', 'test/run-bad-tdb-header.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run', 'test/run.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-check', 'test/run-check.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-corrupt', 'test/run-corrupt.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-die-during-transaction', 'test/run-die-during-transaction.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-endian', 'test/run-endian.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-incompatible', 'test/run-incompatible.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-nested-transactions', 'test/run-nested-transactions.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-nested-traverse', 'test/run-nested-traverse.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-no-lock-during-traverse', 'test/run-no-lock-during-traverse.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-oldhash', 'test/run-oldhash.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-open-during-transaction', 'test/run-open-during-transaction.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-readonly-check', 'test/run-readonly-check.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-rescue', 'test/run-rescue.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-rescue-find_entry', 'test/run-rescue-find_entry.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-rwlock-check', 'test/run-rwlock-check.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-summary', 'test/run-summary.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-transaction-expand', 'test/run-transaction-expand.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-traverse-in-transaction', 'test/run-traverse-in-transaction.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-wronghash-fail', 'test/run-wronghash-fail.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+        bld.SAMBA_BINARY('tdb1-run-zero-append', 'test/run-zero-append.c',
+                         'replace tdb-test-helpers', includes='include', install=False)
+
+    if not bld.CONFIG_SET('USING_SYSTEM_PYTDB'):
+        bld.SAMBA_PYTHON('pytdb',
+                         'pytdb.c',
+                         deps='tdb',
+                         enabled=not bld.env.disable_python,
+                         realname='tdb.so',
+                         cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION)
+
+def testonly(ctx):
+    '''run tdb testsuite'''
+    import Utils, samba_utils, shutil
+    ecode = 0
+
+    test_prefix = "%s/st" % (Utils.g_module.blddir)
+    shutil.rmtree(test_prefix, ignore_errors=True)
+    os.makedirs(test_prefix)
+    os.environ['TEST_DATA_PREFIX'] = test_prefix
+
+    env = samba_utils.LOAD_ENVIRONMENT()
+    # FIXME: This is horrible :(
+    if env.building_tdb:
+        # Create scratch directory for tests.
+        testdir = os.path.join(test_prefix, 'tdb-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)
+
+        for f in 'tdb1-run-3G-file', 'tdb1-run-bad-tdb-header', 'tdb1-run', 'tdb1-run-check', 'tdb1-run-corrupt', 'tdb1-run-die-during-transaction', 'tdb1-run-endian', 'tdb1-run-incompatible', 'tdb1-run-nested-transactions', 'tdb1-run-nested-traverse', 'tdb1-run-no-lock-during-traverse', 'tdb1-run-oldhash', 'tdb1-run-open-during-transaction', 'tdb1-run-readonly-check', 'tdb1-run-rescue', 'tdb1-run-rescue-find_entry', 'tdb1-run-rwlock-check', 'tdb1-run-summary', 'tdb1-run-transaction-expand', 'tdb1-run-traverse-in-transaction', 'tdb1-run-wronghash-fail', 'tdb1-run-zero-append':
+            cmd = "cd " + testdir + " && " + os.path.abspath(os.path.join(Utils.g_module.blddir, f)) + " > test-output 2>&1"
+            print("..." + f)
+            ret = samba_utils.RUN_COMMAND(cmd)
+            if ret != 0:
+                print("%s failed:" % f)
+                samba_utils.RUN_COMMAND("cat " + os.path.join(testdir, 'test-output'))
+                ecode = ret
+                break
+
+    if ecode == 0:
+        cmd = os.path.join(Utils.g_module.blddir, 'tdbtorture')
+        ret = samba_utils.RUN_COMMAND(cmd)
+        print("testsuite returned %d" % ret)
+        if ret != 0:
+            ecode = ret
+    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)