mirror of
https://github.com/samba-team/samba.git
synced 2025-03-27 22:50:26 +03:00
TDB2: Goodbye TDB2, Hello NTDB.
This renames everything from tdb2 to ntdb: importantly, we no longer use the tdb_ namespace, so you can link against both ntdb and tdb if you want to. This also enables building of standalone ntdb by the autobuild script. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
76758b9767
commit
16cc345d4f
39
lib/ntdb/ABI/ntdb-0.9.sigs
Normal file
39
lib/ntdb/ABI/ntdb-0.9.sigs
Normal file
@ -0,0 +1,39 @@
|
||||
ntdb_add_flag: void (struct ntdb_context *, unsigned int)
|
||||
ntdb_append: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, NTDB_DATA)
|
||||
ntdb_chainlock: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA)
|
||||
ntdb_chainlock_read: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA)
|
||||
ntdb_chainunlock: void (struct ntdb_context *, NTDB_DATA)
|
||||
ntdb_chainunlock_read: void (struct ntdb_context *, NTDB_DATA)
|
||||
ntdb_check_: enum NTDB_ERROR (struct ntdb_context *, enum NTDB_ERROR (*)(NTDB_DATA, NTDB_DATA, void *), void *)
|
||||
ntdb_close: int (struct ntdb_context *)
|
||||
ntdb_delete: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA)
|
||||
ntdb_error: enum NTDB_ERROR (struct ntdb_context *)
|
||||
ntdb_errorstr: const char *(enum NTDB_ERROR)
|
||||
ntdb_exists: bool (struct ntdb_context *, NTDB_DATA)
|
||||
ntdb_fd: int (const struct ntdb_context *)
|
||||
ntdb_fetch: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, NTDB_DATA *)
|
||||
ntdb_firstkey: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA *)
|
||||
ntdb_foreach_: void (int (*)(struct ntdb_context *, void *), void *)
|
||||
ntdb_get_attribute: enum NTDB_ERROR (struct ntdb_context *, union ntdb_attribute *)
|
||||
ntdb_get_flags: unsigned int (struct ntdb_context *)
|
||||
ntdb_get_seqnum: int64_t (struct ntdb_context *)
|
||||
ntdb_lockall: enum NTDB_ERROR (struct ntdb_context *)
|
||||
ntdb_lockall_read: enum NTDB_ERROR (struct ntdb_context *)
|
||||
ntdb_name: const char *(const struct ntdb_context *)
|
||||
ntdb_nextkey: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA *)
|
||||
ntdb_open: struct ntdb_context *(const char *, int, int, mode_t, union ntdb_attribute *)
|
||||
ntdb_parse_record_: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, enum NTDB_ERROR (*)(NTDB_DATA, NTDB_DATA, void *), void *)
|
||||
ntdb_remove_flag: void (struct ntdb_context *, unsigned int)
|
||||
ntdb_repack: enum NTDB_ERROR (struct ntdb_context *)
|
||||
ntdb_set_attribute: enum NTDB_ERROR (struct ntdb_context *, const union ntdb_attribute *)
|
||||
ntdb_store: enum NTDB_ERROR (struct ntdb_context *, NTDB_DATA, NTDB_DATA, int)
|
||||
ntdb_summary: enum NTDB_ERROR (struct ntdb_context *, enum ntdb_summary_flags, char **)
|
||||
ntdb_transaction_cancel: void (struct ntdb_context *)
|
||||
ntdb_transaction_commit: enum NTDB_ERROR (struct ntdb_context *)
|
||||
ntdb_transaction_prepare_commit: enum NTDB_ERROR (struct ntdb_context *)
|
||||
ntdb_transaction_start: enum NTDB_ERROR (struct ntdb_context *)
|
||||
ntdb_traverse_: int64_t (struct ntdb_context *, int (*)(struct ntdb_context *, NTDB_DATA, NTDB_DATA, void *), void *)
|
||||
ntdb_unlockall: void (struct ntdb_context *)
|
||||
ntdb_unlockall_read: void (struct ntdb_context *)
|
||||
ntdb_unset_attribute: void (struct ntdb_context *, enum ntdb_attribute_type)
|
||||
ntdb_wipe_all: enum NTDB_ERROR (struct ntdb_context *)
|
864
lib/ntdb/check.c
Normal file
864
lib/ntdb/check.c
Normal file
@ -0,0 +1,864 @@
|
||||
/*
|
||||
Trivial Database 2: free list/block handling
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "private.h"
|
||||
#include <ccan/likely/likely.h>
|
||||
#include <ccan/asearch/asearch.h>
|
||||
|
||||
/* We keep an ordered array of offsets. */
|
||||
static bool append(ntdb_off_t **arr, size_t *num, ntdb_off_t off)
|
||||
{
|
||||
ntdb_off_t *new = realloc(*arr, (*num + 1) * sizeof(ntdb_off_t));
|
||||
if (!new)
|
||||
return false;
|
||||
new[(*num)++] = off;
|
||||
*arr = new;
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_header(struct ntdb_context *ntdb, ntdb_off_t *recovery,
|
||||
uint64_t *features, size_t *num_capabilities)
|
||||
{
|
||||
uint64_t hash_test;
|
||||
struct ntdb_header hdr;
|
||||
enum NTDB_ERROR ecode;
|
||||
ntdb_off_t off, next;
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, 0, &hdr, sizeof(hdr));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
/* magic food should not be converted, so convert back. */
|
||||
ntdb_convert(ntdb, hdr.magic_food, sizeof(hdr.magic_food));
|
||||
|
||||
hash_test = NTDB_HASH_MAGIC;
|
||||
hash_test = ntdb_hash(ntdb, &hash_test, sizeof(hash_test));
|
||||
if (hdr.hash_test != hash_test) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"check: hash test %llu should be %llu",
|
||||
(long long)hdr.hash_test,
|
||||
(long long)hash_test);
|
||||
}
|
||||
|
||||
if (strcmp(hdr.magic_food, NTDB_MAGIC_FOOD) != 0) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"check: bad magic '%.*s'",
|
||||
(unsigned)sizeof(hdr.magic_food),
|
||||
hdr.magic_food);
|
||||
}
|
||||
|
||||
/* Features which are used must be a subset of features offered. */
|
||||
if (hdr.features_used & ~hdr.features_offered) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"check: features used (0x%llx) which"
|
||||
" are not offered (0x%llx)",
|
||||
(long long)hdr.features_used,
|
||||
(long long)hdr.features_offered);
|
||||
}
|
||||
|
||||
*features = hdr.features_offered;
|
||||
*recovery = hdr.recovery;
|
||||
if (*recovery) {
|
||||
if (*recovery < sizeof(hdr)
|
||||
|| *recovery > ntdb->file->map_size) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check:"
|
||||
" invalid recovery offset %zu",
|
||||
(size_t)*recovery);
|
||||
}
|
||||
}
|
||||
|
||||
for (off = hdr.capabilities; off && ecode == NTDB_SUCCESS; off = next) {
|
||||
const struct ntdb_capability *cap;
|
||||
enum NTDB_ERROR e;
|
||||
|
||||
cap = ntdb_access_read(ntdb, off, sizeof(*cap), true);
|
||||
if (NTDB_PTR_IS_ERR(cap)) {
|
||||
return NTDB_PTR_ERR(cap);
|
||||
}
|
||||
|
||||
/* All capabilities are unknown. */
|
||||
e = unknown_capability(ntdb, "ntdb_check", cap->type);
|
||||
next = cap->next;
|
||||
ntdb_access_release(ntdb, cap);
|
||||
if (e)
|
||||
return e;
|
||||
(*num_capabilities)++;
|
||||
}
|
||||
|
||||
/* Don't check reserved: they *can* be used later. */
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_hash_tree(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, unsigned int group_bits,
|
||||
uint64_t hprefix,
|
||||
unsigned hprefix_bits,
|
||||
ntdb_off_t used[],
|
||||
size_t num_used,
|
||||
size_t *num_found,
|
||||
enum NTDB_ERROR (*check)(NTDB_DATA,
|
||||
NTDB_DATA, void *),
|
||||
void *data);
|
||||
|
||||
static enum NTDB_ERROR check_hash_chain(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off,
|
||||
uint64_t hash,
|
||||
ntdb_off_t used[],
|
||||
size_t num_used,
|
||||
size_t *num_found,
|
||||
enum NTDB_ERROR (*check)(NTDB_DATA,
|
||||
NTDB_DATA,
|
||||
void *),
|
||||
void *data)
|
||||
{
|
||||
struct ntdb_used_record rec;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (rec_magic(&rec) != NTDB_CHAIN_MAGIC) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad hash chain magic %llu",
|
||||
(long long)rec_magic(&rec));
|
||||
}
|
||||
|
||||
if (rec_data_length(&rec) != sizeof(struct ntdb_chain)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check:"
|
||||
" Bad hash chain length %llu vs %zu",
|
||||
(long long)rec_data_length(&rec),
|
||||
sizeof(struct ntdb_chain));
|
||||
}
|
||||
if (rec_key_length(&rec) != 0) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad hash chain key length %llu",
|
||||
(long long)rec_key_length(&rec));
|
||||
}
|
||||
if (rec_hash(&rec) != 0) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad hash chain hash value %llu",
|
||||
(long long)rec_hash(&rec));
|
||||
}
|
||||
|
||||
off += sizeof(rec);
|
||||
ecode = check_hash_tree(ntdb, off, 0, hash, 64,
|
||||
used, num_used, num_found, check, data);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
off = ntdb_read_off(ntdb, off + offsetof(struct ntdb_chain, next));
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
if (off == 0)
|
||||
return NTDB_SUCCESS;
|
||||
(*num_found)++;
|
||||
return check_hash_chain(ntdb, off, hash, used, num_used, num_found,
|
||||
check, data);
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_hash_record(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off,
|
||||
uint64_t hprefix,
|
||||
unsigned hprefix_bits,
|
||||
ntdb_off_t used[],
|
||||
size_t num_used,
|
||||
size_t *num_found,
|
||||
enum NTDB_ERROR (*check)(NTDB_DATA,
|
||||
NTDB_DATA,
|
||||
void *),
|
||||
void *data)
|
||||
{
|
||||
struct ntdb_used_record rec;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (hprefix_bits >= 64)
|
||||
return check_hash_chain(ntdb, off, hprefix, used, num_used,
|
||||
num_found, check, data);
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (rec_magic(&rec) != NTDB_HTABLE_MAGIC) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad hash table magic %llu",
|
||||
(long long)rec_magic(&rec));
|
||||
}
|
||||
if (rec_data_length(&rec)
|
||||
!= sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check:"
|
||||
" Bad hash table length %llu vs %llu",
|
||||
(long long)rec_data_length(&rec),
|
||||
(long long)sizeof(ntdb_off_t)
|
||||
<< NTDB_SUBLEVEL_HASH_BITS);
|
||||
}
|
||||
if (rec_key_length(&rec) != 0) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad hash table key length %llu",
|
||||
(long long)rec_key_length(&rec));
|
||||
}
|
||||
if (rec_hash(&rec) != 0) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad hash table hash value %llu",
|
||||
(long long)rec_hash(&rec));
|
||||
}
|
||||
|
||||
off += sizeof(rec);
|
||||
return check_hash_tree(ntdb, off,
|
||||
NTDB_SUBLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS,
|
||||
hprefix, hprefix_bits,
|
||||
used, num_used, num_found, check, data);
|
||||
}
|
||||
|
||||
static int off_cmp(const ntdb_off_t *a, const ntdb_off_t *b)
|
||||
{
|
||||
/* Can overflow an int. */
|
||||
return *a > *b ? 1
|
||||
: *a < *b ? -1
|
||||
: 0;
|
||||
}
|
||||
|
||||
static uint64_t get_bits(uint64_t h, unsigned num, unsigned *used)
|
||||
{
|
||||
*used += num;
|
||||
|
||||
return (h >> (64 - *used)) & ((1U << num) - 1);
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_hash_tree(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, unsigned int group_bits,
|
||||
uint64_t hprefix,
|
||||
unsigned hprefix_bits,
|
||||
ntdb_off_t used[],
|
||||
size_t num_used,
|
||||
size_t *num_found,
|
||||
enum NTDB_ERROR (*check)(NTDB_DATA,
|
||||
NTDB_DATA, void *),
|
||||
void *data)
|
||||
{
|
||||
unsigned int g, b;
|
||||
const ntdb_off_t *hash;
|
||||
struct ntdb_used_record rec;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
hash = ntdb_access_read(ntdb, off,
|
||||
sizeof(ntdb_off_t)
|
||||
<< (group_bits + NTDB_HASH_GROUP_BITS),
|
||||
true);
|
||||
if (NTDB_PTR_IS_ERR(hash)) {
|
||||
return NTDB_PTR_ERR(hash);
|
||||
}
|
||||
|
||||
for (g = 0; g < (1 << group_bits); g++) {
|
||||
const ntdb_off_t *group = hash + (g << NTDB_HASH_GROUP_BITS);
|
||||
for (b = 0; b < (1 << NTDB_HASH_GROUP_BITS); b++) {
|
||||
unsigned int bucket, i, used_bits;
|
||||
uint64_t h;
|
||||
ntdb_off_t *p;
|
||||
if (group[b] == 0)
|
||||
continue;
|
||||
|
||||
off = group[b] & NTDB_OFF_MASK;
|
||||
p = asearch(&off, used, num_used, off_cmp);
|
||||
if (!p) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: Invalid offset"
|
||||
" %llu in hash",
|
||||
(long long)off);
|
||||
goto fail;
|
||||
}
|
||||
/* Mark it invalid. */
|
||||
*p ^= 1;
|
||||
(*num_found)++;
|
||||
|
||||
if (hprefix_bits == 64) {
|
||||
/* Chained entries are unordered. */
|
||||
if (is_subhash(group[b])) {
|
||||
ecode = NTDB_ERR_CORRUPT;
|
||||
ntdb_logerr(ntdb, ecode,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: Invalid chain"
|
||||
" entry subhash");
|
||||
goto fail;
|
||||
}
|
||||
h = hash_record(ntdb, off);
|
||||
if (h != hprefix) {
|
||||
ecode = NTDB_ERR_CORRUPT;
|
||||
ntdb_logerr(ntdb, ecode,
|
||||
NTDB_LOG_ERROR,
|
||||
"check: bad hash chain"
|
||||
" placement"
|
||||
" 0x%llx vs 0x%llx",
|
||||
(long long)h,
|
||||
(long long)hprefix);
|
||||
goto fail;
|
||||
}
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec,
|
||||
sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
goto check;
|
||||
}
|
||||
|
||||
if (is_subhash(group[b])) {
|
||||
uint64_t subprefix;
|
||||
subprefix = (hprefix
|
||||
<< (group_bits + NTDB_HASH_GROUP_BITS))
|
||||
+ g * (1 << NTDB_HASH_GROUP_BITS) + b;
|
||||
|
||||
ecode = check_hash_record(ntdb,
|
||||
group[b] & NTDB_OFF_MASK,
|
||||
subprefix,
|
||||
hprefix_bits
|
||||
+ group_bits
|
||||
+ NTDB_HASH_GROUP_BITS,
|
||||
used, num_used, num_found,
|
||||
check, data);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* A normal entry */
|
||||
|
||||
/* Does it belong here at all? */
|
||||
h = hash_record(ntdb, off);
|
||||
used_bits = 0;
|
||||
if (get_bits(h, hprefix_bits, &used_bits) != hprefix
|
||||
&& hprefix_bits) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"check: bad hash placement"
|
||||
" 0x%llx vs 0x%llx",
|
||||
(long long)h,
|
||||
(long long)hprefix);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Does it belong in this group? */
|
||||
if (get_bits(h, group_bits, &used_bits) != g) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"check: bad group %llu"
|
||||
" vs %u",
|
||||
(long long)h, g);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Are bucket bits correct? */
|
||||
bucket = group[b] & NTDB_OFF_HASH_GROUP_MASK;
|
||||
if (get_bits(h, NTDB_HASH_GROUP_BITS, &used_bits)
|
||||
!= bucket) {
|
||||
used_bits -= NTDB_HASH_GROUP_BITS;
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"check: bad bucket %u vs %u",
|
||||
(unsigned)get_bits(h,
|
||||
NTDB_HASH_GROUP_BITS,
|
||||
&used_bits),
|
||||
bucket);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* There must not be any zero entries between
|
||||
* the bucket it belongs in and this one! */
|
||||
for (i = bucket;
|
||||
i != b;
|
||||
i = (i + 1) % (1 << NTDB_HASH_GROUP_BITS)) {
|
||||
if (group[i] == 0) {
|
||||
ecode = NTDB_ERR_CORRUPT;
|
||||
ntdb_logerr(ntdb, ecode,
|
||||
NTDB_LOG_ERROR,
|
||||
"check: bad group placement"
|
||||
" %u vs %u",
|
||||
b, bucket);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Bottom bits must match header. */
|
||||
if ((h & ((1 << 11)-1)) != rec_hash(&rec)) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad hash magic"
|
||||
" at offset %llu"
|
||||
" (0x%llx vs 0x%llx)",
|
||||
(long long)off,
|
||||
(long long)h,
|
||||
(long long)rec_hash(&rec));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
check:
|
||||
if (check) {
|
||||
NTDB_DATA k, d;
|
||||
const unsigned char *kptr;
|
||||
|
||||
kptr = ntdb_access_read(ntdb,
|
||||
off + sizeof(rec),
|
||||
rec_key_length(&rec)
|
||||
+ rec_data_length(&rec),
|
||||
false);
|
||||
if (NTDB_PTR_IS_ERR(kptr)) {
|
||||
ecode = NTDB_PTR_ERR(kptr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
k = ntdb_mkdata(kptr, rec_key_length(&rec));
|
||||
d = ntdb_mkdata(kptr + k.dsize,
|
||||
rec_data_length(&rec));
|
||||
ecode = check(k, d, data);
|
||||
ntdb_access_release(ntdb, kptr);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ntdb_access_release(ntdb, hash);
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
fail:
|
||||
ntdb_access_release(ntdb, hash);
|
||||
return ecode;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_hash(struct ntdb_context *ntdb,
|
||||
ntdb_off_t used[],
|
||||
size_t num_used, size_t num_other_used,
|
||||
enum NTDB_ERROR (*check)(NTDB_DATA, NTDB_DATA, void *),
|
||||
void *data)
|
||||
{
|
||||
/* Free tables and capabilities also show up as used. */
|
||||
size_t num_found = num_other_used;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ecode = check_hash_tree(ntdb, offsetof(struct ntdb_header, hashtable),
|
||||
NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS,
|
||||
0, 0, used, num_used, &num_found,
|
||||
check, data);
|
||||
if (ecode == NTDB_SUCCESS) {
|
||||
if (num_found != num_used) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Not all entries"
|
||||
" are in hash");
|
||||
}
|
||||
}
|
||||
return ecode;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_free(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off,
|
||||
const struct ntdb_free_record *frec,
|
||||
ntdb_off_t prev, unsigned int ftable,
|
||||
unsigned int bucket)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (frec_magic(frec) != NTDB_FREE_MAGIC) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: offset %llu bad magic 0x%llx",
|
||||
(long long)off,
|
||||
(long long)frec->magic_and_prev);
|
||||
}
|
||||
if (frec_ftable(frec) != ftable) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: offset %llu bad freetable %u",
|
||||
(long long)off, frec_ftable(frec));
|
||||
|
||||
}
|
||||
|
||||
ecode = ntdb->io->oob(ntdb, off,
|
||||
frec_len(frec)
|
||||
+ sizeof(struct ntdb_used_record),
|
||||
false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
if (size_to_bucket(frec_len(frec)) != bucket) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: offset %llu in wrong bucket"
|
||||
" (%u vs %u)",
|
||||
(long long)off,
|
||||
bucket, size_to_bucket(frec_len(frec)));
|
||||
}
|
||||
if (prev && prev != frec_prev(frec)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: offset %llu bad prev"
|
||||
" (%llu vs %llu)",
|
||||
(long long)off,
|
||||
(long long)prev, (long long)frec_len(frec));
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_free_table(struct ntdb_context *ntdb,
|
||||
ntdb_off_t ftable_off,
|
||||
unsigned ftable_num,
|
||||
ntdb_off_t fr[],
|
||||
size_t num_free,
|
||||
size_t *num_found)
|
||||
{
|
||||
struct ntdb_freetable ft;
|
||||
ntdb_off_t h;
|
||||
unsigned int i;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, ftable_off, &ft, sizeof(ft));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (rec_magic(&ft.hdr) != NTDB_FTABLE_MAGIC
|
||||
|| rec_key_length(&ft.hdr) != 0
|
||||
|| rec_data_length(&ft.hdr) != sizeof(ft) - sizeof(ft.hdr)
|
||||
|| rec_hash(&ft.hdr) != 0) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Invalid header on free table");
|
||||
}
|
||||
|
||||
for (i = 0; i < NTDB_FREE_BUCKETS; i++) {
|
||||
ntdb_off_t off, prev = 0, *p, first = 0;
|
||||
struct ntdb_free_record f;
|
||||
|
||||
h = bucket_off(ftable_off, i);
|
||||
for (off = ntdb_read_off(ntdb, h); off; off = f.next) {
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
if (!first) {
|
||||
off &= NTDB_OFF_MASK;
|
||||
first = off;
|
||||
}
|
||||
ecode = ntdb_read_convert(ntdb, off, &f, sizeof(f));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
ecode = check_free(ntdb, off, &f, prev, ftable_num, i);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* FIXME: Check hash bits */
|
||||
p = asearch(&off, fr, num_free, off_cmp);
|
||||
if (!p) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: Invalid offset"
|
||||
" %llu in free table",
|
||||
(long long)off);
|
||||
}
|
||||
/* Mark it invalid. */
|
||||
*p ^= 1;
|
||||
(*num_found)++;
|
||||
prev = off;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
/* Now we can check first back pointer. */
|
||||
ecode = ntdb_read_convert(ntdb, first, &f, sizeof(f));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
ecode = check_free(ntdb, first, &f, prev, ftable_num, i);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* Slow, but should be very rare. */
|
||||
ntdb_off_t dead_space(struct ntdb_context *ntdb, ntdb_off_t off)
|
||||
{
|
||||
size_t len;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
for (len = 0; off + len < ntdb->file->map_size; len++) {
|
||||
char c;
|
||||
ecode = ntdb->io->tread(ntdb, off, &c, 1);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
if (c != 0 && c != 0x43)
|
||||
break;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR check_linear(struct ntdb_context *ntdb,
|
||||
ntdb_off_t **used, size_t *num_used,
|
||||
ntdb_off_t **fr, size_t *num_free,
|
||||
uint64_t features, ntdb_off_t recovery)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
ntdb_len_t len;
|
||||
enum NTDB_ERROR ecode;
|
||||
bool found_recovery = false;
|
||||
|
||||
for (off = sizeof(struct ntdb_header);
|
||||
off < ntdb->file->map_size;
|
||||
off += len) {
|
||||
union {
|
||||
struct ntdb_used_record u;
|
||||
struct ntdb_free_record f;
|
||||
struct ntdb_recovery_record r;
|
||||
} rec;
|
||||
/* r is larger: only get that if we need to. */
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec.f));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* If we crash after ftruncate, we can get zeroes or fill. */
|
||||
if (rec.r.magic == NTDB_RECOVERY_INVALID_MAGIC
|
||||
|| rec.r.magic == 0x4343434343434343ULL) {
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec.r));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
if (recovery == off) {
|
||||
found_recovery = true;
|
||||
len = sizeof(rec.r) + rec.r.max_len;
|
||||
} else {
|
||||
len = dead_space(ntdb, off);
|
||||
if (NTDB_OFF_IS_ERR(len)) {
|
||||
return NTDB_OFF_TO_ERR(len);
|
||||
}
|
||||
if (len < sizeof(rec.r)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: invalid"
|
||||
" dead space at %zu",
|
||||
(size_t)off);
|
||||
}
|
||||
|
||||
ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
|
||||
"Dead space at %zu-%zu (of %zu)",
|
||||
(size_t)off, (size_t)(off + len),
|
||||
(size_t)ntdb->file->map_size);
|
||||
}
|
||||
} else if (rec.r.magic == NTDB_RECOVERY_MAGIC) {
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec.r));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
if (recovery != off) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: unexpected"
|
||||
" recovery record at offset"
|
||||
" %zu",
|
||||
(size_t)off);
|
||||
}
|
||||
if (rec.r.len > rec.r.max_len) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: invalid recovery"
|
||||
" length %zu",
|
||||
(size_t)rec.r.len);
|
||||
}
|
||||
if (rec.r.eof > ntdb->file->map_size) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: invalid old EOF"
|
||||
" %zu", (size_t)rec.r.eof);
|
||||
}
|
||||
found_recovery = true;
|
||||
len = sizeof(rec.r) + rec.r.max_len;
|
||||
} else if (frec_magic(&rec.f) == NTDB_FREE_MAGIC) {
|
||||
len = sizeof(rec.u) + frec_len(&rec.f);
|
||||
if (off + len > ntdb->file->map_size) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: free overlength"
|
||||
" %llu at offset %llu",
|
||||
(long long)len,
|
||||
(long long)off);
|
||||
}
|
||||
/* This record should be in free lists. */
|
||||
if (frec_ftable(&rec.f) != NTDB_FTABLE_NONE
|
||||
&& !append(fr, num_free, off)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_OOM,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: tracking %zu'th"
|
||||
" free record.", *num_free);
|
||||
}
|
||||
} else if (rec_magic(&rec.u) == NTDB_USED_MAGIC
|
||||
|| rec_magic(&rec.u) == NTDB_CHAIN_MAGIC
|
||||
|| rec_magic(&rec.u) == NTDB_HTABLE_MAGIC
|
||||
|| rec_magic(&rec.u) == NTDB_FTABLE_MAGIC
|
||||
|| rec_magic(&rec.u) == NTDB_CAP_MAGIC) {
|
||||
uint64_t klen, dlen, extra;
|
||||
|
||||
/* This record is used! */
|
||||
if (!append(used, num_used, off)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_OOM,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: tracking %zu'th"
|
||||
" used record.", *num_used);
|
||||
}
|
||||
|
||||
klen = rec_key_length(&rec.u);
|
||||
dlen = rec_data_length(&rec.u);
|
||||
extra = rec_extra_padding(&rec.u);
|
||||
|
||||
len = sizeof(rec.u) + klen + dlen + extra;
|
||||
if (off + len > ntdb->file->map_size) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: used overlength"
|
||||
" %llu at offset %llu",
|
||||
(long long)len,
|
||||
(long long)off);
|
||||
}
|
||||
|
||||
if (len < sizeof(rec.f)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: too short record"
|
||||
" %llu at %llu",
|
||||
(long long)len,
|
||||
(long long)off);
|
||||
}
|
||||
|
||||
/* Check that records have correct 0 at end (but may
|
||||
* not in future). */
|
||||
if (extra && !features
|
||||
&& rec_magic(&rec.u) != NTDB_CAP_MAGIC) {
|
||||
const char *p;
|
||||
char c;
|
||||
p = ntdb_access_read(ntdb, off + sizeof(rec.u)
|
||||
+ klen + dlen, 1, false);
|
||||
if (NTDB_PTR_IS_ERR(p))
|
||||
return NTDB_PTR_ERR(p);
|
||||
c = *p;
|
||||
ntdb_access_release(ntdb, p);
|
||||
|
||||
if (c != '\0') {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check:"
|
||||
" non-zero extra"
|
||||
" at %llu",
|
||||
(long long)off);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"ntdb_check: Bad magic 0x%llx"
|
||||
" at offset %zu",
|
||||
(long long)rec_magic(&rec.u),
|
||||
(size_t)off);
|
||||
}
|
||||
}
|
||||
|
||||
/* We must have found recovery area if there was one. */
|
||||
if (recovery != 0 && !found_recovery) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: expected a recovery area at %zu",
|
||||
(size_t)recovery);
|
||||
}
|
||||
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_check_(struct ntdb_context *ntdb,
|
||||
enum NTDB_ERROR (*check)(NTDB_DATA, NTDB_DATA, void *),
|
||||
void *data)
|
||||
{
|
||||
ntdb_off_t *fr = NULL, *used = NULL, ft, recovery;
|
||||
size_t num_free = 0, num_used = 0, num_found = 0, num_ftables = 0,
|
||||
num_capabilities = 0;
|
||||
uint64_t features;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (ntdb->flags & NTDB_CANT_CHECK) {
|
||||
return ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
|
||||
"ntdb_check: database has unknown capability,"
|
||||
" cannot check.");
|
||||
}
|
||||
|
||||
ecode = ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
ecode = ntdb_lock_expand(ntdb, F_RDLCK);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_allrecord_unlock(ntdb, F_RDLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
ecode = check_header(ntdb, &recovery, &features, &num_capabilities);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* First we do a linear scan, checking all records. */
|
||||
ecode = check_linear(ntdb, &used, &num_used, &fr, &num_free, features,
|
||||
recovery);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto out;
|
||||
|
||||
for (ft = first_ftable(ntdb); ft; ft = next_ftable(ntdb, ft)) {
|
||||
if (NTDB_OFF_IS_ERR(ft)) {
|
||||
ecode = NTDB_OFF_TO_ERR(ft);
|
||||
goto out;
|
||||
}
|
||||
ecode = check_free_table(ntdb, ft, num_ftables, fr, num_free,
|
||||
&num_found);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto out;
|
||||
num_ftables++;
|
||||
}
|
||||
|
||||
/* FIXME: Check key uniqueness? */
|
||||
ecode = check_hash(ntdb, used, num_used, num_ftables + num_capabilities,
|
||||
check, data);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto out;
|
||||
|
||||
if (num_found != num_free) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"ntdb_check: Not all entries are in"
|
||||
" free table");
|
||||
}
|
||||
|
||||
out:
|
||||
ntdb_allrecord_unlock(ntdb, F_RDLCK);
|
||||
ntdb_unlock_expand(ntdb, F_RDLCK);
|
||||
free(fr);
|
||||
free(used);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
0
lib/tdb2/configure → lib/ntdb/configure
vendored
0
lib/tdb2/configure → lib/ntdb/configure
vendored
65
lib/ntdb/doc/TDB_porting.txt
Normal file
65
lib/ntdb/doc/TDB_porting.txt
Normal file
@ -0,0 +1,65 @@
|
||||
Interface differences between TDB and NTDB.
|
||||
|
||||
- ntdb shares 'struct TDB_DATA' with tdb, but TDB defines the TDB_DATA
|
||||
typedef, whereas ntdb defines NTDB_DATA (ie. both are compatible).
|
||||
If you include both ntdb.h and tdb.h, #include tdb.h first,
|
||||
otherwise you'll get a compile error when tdb.h re-defined struct
|
||||
TDB_DATA.
|
||||
|
||||
- ntdb functions return NTDB_SUCCESS (ie 0) on success, and a negative
|
||||
error on failure, whereas tdb functions returned 0 on success, and
|
||||
-1 on failure. tdb then used tdb_error() to determine the error;
|
||||
this is also supported in ntdb to ease backwards compatibility,
|
||||
though the other form is preferred.
|
||||
|
||||
- ntdb's ntdb_fetch() returns an error, tdb's returned the data directly
|
||||
(or tdb_null, and you were supposed to check tdb_error() to find out why).
|
||||
|
||||
- ntdb's ntdb_nextkey() frees the old key's dptr, in tdb you needed to do
|
||||
this manually.
|
||||
|
||||
- tdb's tdb_open/tdb_open_ex took an explicit hash size. ntdb's hash table
|
||||
resizes as required.
|
||||
|
||||
- ntdb uses a linked list of attribute structures to implement logging and
|
||||
alternate hashes. tdb used tdb_open_ex, which was not extensible.
|
||||
|
||||
- ntdb does locking on read-only databases (ie. O_RDONLY passed to ntdb_open).
|
||||
tdb did not: use the NTDB_NOLOCK flag if you want to suppress locking.
|
||||
|
||||
- ntdb's log function is simpler than tdb's log function. The string is
|
||||
already formatted, and it takes an enum ntdb_log_level not a tdb_debug_level,
|
||||
and which has only three values: NTDB_LOG_ERROR, NTDB_LOG_USE_ERROR and
|
||||
NTDB_LOG_WARNING.
|
||||
|
||||
- ntdb provides ntdb_deq() for comparing two NTDB_DATA, and ntdb_mkdata() for
|
||||
creating an NTDB_DATA.
|
||||
|
||||
- ntdb's ntdb_name() returns a copy of the name even for NTDB_INTERNAL dbs.
|
||||
|
||||
- ntdb does not need tdb_reopen() or tdb_reopen_all(). If you call
|
||||
fork() after during certain operations the child should close the
|
||||
tdb, or complete the operations before continuing to use the tdb:
|
||||
|
||||
ntdb_transaction_start(): child must ntdb_transaction_cancel()
|
||||
ntdb_lockall(): child must call ntdb_unlockall()
|
||||
ntdb_lockall_read(): child must call ntdb_unlockall_read()
|
||||
ntdb_chainlock(): child must call ntdb_chainunlock()
|
||||
ntdb_parse() callback: child must return from ntdb_parse()
|
||||
|
||||
- ntdb will not open a non-tdb file, even if O_CREAT is specified.
|
||||
|
||||
- There is no ntdb_traverse_read. For operating on TDB files, you can
|
||||
simulate it by ntdb_add_flag(tdb, NTDB_RDONLY); ntdb_traverse();
|
||||
ntdb_remove_flag(tdb, NTDB_RDONLY). This may be desirable because
|
||||
traverse on TDB files use a write lock on the entire database
|
||||
unless it's read-only.
|
||||
|
||||
- Failure inside a transaction (such as a lock function failing) does
|
||||
not implicitly cancel the transaction; you still need to call
|
||||
ntdb_transaction_cancel().
|
||||
|
||||
- There is no NTDB_CLEAR_IF_FIRST flag; it has severe scalability and
|
||||
API problems. If necessary, you can emulate this by using the open
|
||||
hook and placing a 1-byte lock at offset 4. If your program forks,
|
||||
you will need to place this lock again in the child.
|
976
lib/ntdb/free.c
Normal file
976
lib/ntdb/free.c
Normal file
@ -0,0 +1,976 @@
|
||||
/*
|
||||
Trivial Database 2: free list/block handling
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "private.h"
|
||||
#include <ccan/likely/likely.h>
|
||||
#include <ccan/ilog/ilog.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
static unsigned fls64(uint64_t val)
|
||||
{
|
||||
return ilog64(val);
|
||||
}
|
||||
|
||||
/* In which bucket would we find a particular record size? (ignoring header) */
|
||||
unsigned int size_to_bucket(ntdb_len_t data_len)
|
||||
{
|
||||
unsigned int bucket;
|
||||
|
||||
/* We can't have records smaller than this. */
|
||||
assert(data_len >= NTDB_MIN_DATA_LEN);
|
||||
|
||||
/* Ignoring the header... */
|
||||
if (data_len - NTDB_MIN_DATA_LEN <= 64) {
|
||||
/* 0 in bucket 0, 8 in bucket 1... 64 in bucket 8. */
|
||||
bucket = (data_len - NTDB_MIN_DATA_LEN) / 8;
|
||||
} else {
|
||||
/* After that we go power of 2. */
|
||||
bucket = fls64(data_len - NTDB_MIN_DATA_LEN) + 2;
|
||||
}
|
||||
|
||||
if (unlikely(bucket >= NTDB_FREE_BUCKETS))
|
||||
bucket = NTDB_FREE_BUCKETS - 1;
|
||||
return bucket;
|
||||
}
|
||||
|
||||
ntdb_off_t first_ftable(struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb_read_off(ntdb, offsetof(struct ntdb_header, free_table));
|
||||
}
|
||||
|
||||
ntdb_off_t next_ftable(struct ntdb_context *ntdb, ntdb_off_t ftable)
|
||||
{
|
||||
return ntdb_read_off(ntdb, ftable + offsetof(struct ntdb_freetable,next));
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_ftable_init(struct ntdb_context *ntdb)
|
||||
{
|
||||
/* Use reservoir sampling algorithm to select a free list at random. */
|
||||
unsigned int rnd, max = 0, count = 0;
|
||||
ntdb_off_t off;
|
||||
|
||||
ntdb->ftable_off = off = first_ftable(ntdb);
|
||||
ntdb->ftable = 0;
|
||||
|
||||
while (off) {
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
|
||||
rnd = random();
|
||||
if (rnd >= max) {
|
||||
ntdb->ftable_off = off;
|
||||
ntdb->ftable = count;
|
||||
max = rnd;
|
||||
}
|
||||
|
||||
off = next_ftable(ntdb, off);
|
||||
count++;
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* Offset of a given bucket. */
|
||||
ntdb_off_t bucket_off(ntdb_off_t ftable_off, unsigned bucket)
|
||||
{
|
||||
return ftable_off + offsetof(struct ntdb_freetable, buckets)
|
||||
+ bucket * sizeof(ntdb_off_t);
|
||||
}
|
||||
|
||||
/* Returns free_buckets + 1, or list number to search, or -ve error. */
|
||||
static ntdb_off_t find_free_head(struct ntdb_context *ntdb,
|
||||
ntdb_off_t ftable_off,
|
||||
ntdb_off_t bucket)
|
||||
{
|
||||
/* Speculatively search for a non-zero bucket. */
|
||||
return ntdb_find_nonzero_off(ntdb, bucket_off(ftable_off, 0),
|
||||
bucket, NTDB_FREE_BUCKETS);
|
||||
}
|
||||
|
||||
static void check_list(struct ntdb_context *ntdb, ntdb_off_t b_off)
|
||||
{
|
||||
#ifdef CCAN_NTDB_DEBUG
|
||||
ntdb_off_t off, prev = 0, first;
|
||||
struct ntdb_free_record r;
|
||||
|
||||
first = off = (ntdb_read_off(ntdb, b_off) & NTDB_OFF_MASK);
|
||||
while (off != 0) {
|
||||
ntdb_read_convert(ntdb, off, &r, sizeof(r));
|
||||
if (frec_magic(&r) != NTDB_FREE_MAGIC)
|
||||
abort();
|
||||
if (prev && frec_prev(&r) != prev)
|
||||
abort();
|
||||
prev = off;
|
||||
off = r.next;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
ntdb_read_convert(ntdb, first, &r, sizeof(r));
|
||||
if (frec_prev(&r) != prev)
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Remove from free bucket. */
|
||||
static enum NTDB_ERROR remove_from_list(struct ntdb_context *ntdb,
|
||||
ntdb_off_t b_off, ntdb_off_t r_off,
|
||||
const struct ntdb_free_record *r)
|
||||
{
|
||||
ntdb_off_t off, prev_next, head;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* Is this only element in list? Zero out bucket, and we're done. */
|
||||
if (frec_prev(r) == r_off)
|
||||
return ntdb_write_off(ntdb, b_off, 0);
|
||||
|
||||
/* off = &r->prev->next */
|
||||
off = frec_prev(r) + offsetof(struct ntdb_free_record, next);
|
||||
|
||||
/* Get prev->next */
|
||||
prev_next = ntdb_read_off(ntdb, off);
|
||||
if (NTDB_OFF_IS_ERR(prev_next))
|
||||
return NTDB_OFF_TO_ERR(prev_next);
|
||||
|
||||
/* If prev->next == 0, we were head: update bucket to point to next. */
|
||||
if (prev_next == 0) {
|
||||
/* We must preserve upper bits. */
|
||||
head = ntdb_read_off(ntdb, b_off);
|
||||
if (NTDB_OFF_IS_ERR(head))
|
||||
return NTDB_OFF_TO_ERR(head);
|
||||
|
||||
if ((head & NTDB_OFF_MASK) != r_off) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"remove_from_list:"
|
||||
" %llu head %llu on list %llu",
|
||||
(long long)r_off,
|
||||
(long long)head,
|
||||
(long long)b_off);
|
||||
}
|
||||
head = ((head & ~NTDB_OFF_MASK) | r->next);
|
||||
ecode = ntdb_write_off(ntdb, b_off, head);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
} else {
|
||||
/* r->prev->next = r->next */
|
||||
ecode = ntdb_write_off(ntdb, off, r->next);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* If we were the tail, off = &head->prev. */
|
||||
if (r->next == 0) {
|
||||
head = ntdb_read_off(ntdb, b_off);
|
||||
if (NTDB_OFF_IS_ERR(head))
|
||||
return NTDB_OFF_TO_ERR(head);
|
||||
head &= NTDB_OFF_MASK;
|
||||
off = head + offsetof(struct ntdb_free_record, magic_and_prev);
|
||||
} else {
|
||||
/* off = &r->next->prev */
|
||||
off = r->next + offsetof(struct ntdb_free_record,
|
||||
magic_and_prev);
|
||||
}
|
||||
|
||||
#ifdef CCAN_NTDB_DEBUG
|
||||
/* *off == r */
|
||||
if ((ntdb_read_off(ntdb, off) & NTDB_OFF_MASK) != r_off) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"remove_from_list:"
|
||||
" %llu bad prev in list %llu",
|
||||
(long long)r_off, (long long)b_off);
|
||||
}
|
||||
#endif
|
||||
/* r->next->prev = r->prev */
|
||||
return ntdb_write_off(ntdb, off, r->magic_and_prev);
|
||||
}
|
||||
|
||||
/* Enqueue in this free bucket: sets coalesce if we've added 128
|
||||
* entries to it. */
|
||||
static enum NTDB_ERROR enqueue_in_free(struct ntdb_context *ntdb,
|
||||
ntdb_off_t b_off,
|
||||
ntdb_off_t off,
|
||||
ntdb_len_t len,
|
||||
bool *coalesce)
|
||||
{
|
||||
struct ntdb_free_record new;
|
||||
enum NTDB_ERROR ecode;
|
||||
ntdb_off_t prev, head;
|
||||
uint64_t magic = (NTDB_FREE_MAGIC << (64 - NTDB_OFF_UPPER_STEAL));
|
||||
|
||||
head = ntdb_read_off(ntdb, b_off);
|
||||
if (NTDB_OFF_IS_ERR(head))
|
||||
return NTDB_OFF_TO_ERR(head);
|
||||
|
||||
/* We only need to set ftable_and_len; rest is set in enqueue_in_free */
|
||||
new.ftable_and_len = ((uint64_t)ntdb->ftable
|
||||
<< (64 - NTDB_OFF_UPPER_STEAL))
|
||||
| len;
|
||||
|
||||
/* new->next = head. */
|
||||
new.next = (head & NTDB_OFF_MASK);
|
||||
|
||||
/* First element? Prev points to ourselves. */
|
||||
if (!new.next) {
|
||||
new.magic_and_prev = (magic | off);
|
||||
} else {
|
||||
/* new->prev = next->prev */
|
||||
prev = ntdb_read_off(ntdb,
|
||||
new.next + offsetof(struct ntdb_free_record,
|
||||
magic_and_prev));
|
||||
new.magic_and_prev = prev;
|
||||
if (frec_magic(&new) != NTDB_FREE_MAGIC) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"enqueue_in_free: %llu bad head"
|
||||
" prev %llu",
|
||||
(long long)new.next,
|
||||
(long long)prev);
|
||||
}
|
||||
/* next->prev = new. */
|
||||
ecode = ntdb_write_off(ntdb, new.next
|
||||
+ offsetof(struct ntdb_free_record,
|
||||
magic_and_prev),
|
||||
off | magic);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
#ifdef CCAN_NTDB_DEBUG
|
||||
prev = ntdb_read_off(ntdb, frec_prev(&new)
|
||||
+ offsetof(struct ntdb_free_record, next));
|
||||
if (prev != 0) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"enqueue_in_free:"
|
||||
" %llu bad tail next ptr %llu",
|
||||
(long long)frec_prev(&new)
|
||||
+ offsetof(struct ntdb_free_record,
|
||||
next),
|
||||
(long long)prev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Update enqueue count, but don't set high bit: see NTDB_OFF_IS_ERR */
|
||||
if (*coalesce)
|
||||
head += (1ULL << (64 - NTDB_OFF_UPPER_STEAL));
|
||||
head &= ~(NTDB_OFF_MASK | (1ULL << 63));
|
||||
head |= off;
|
||||
|
||||
ecode = ntdb_write_off(ntdb, b_off, head);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* It's time to coalesce if counter wrapped. */
|
||||
if (*coalesce)
|
||||
*coalesce = ((head & ~NTDB_OFF_MASK) == 0);
|
||||
|
||||
return ntdb_write_convert(ntdb, off, &new, sizeof(new));
|
||||
}
|
||||
|
||||
static ntdb_off_t ftable_offset(struct ntdb_context *ntdb, unsigned int ftable)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
unsigned int i;
|
||||
|
||||
if (likely(ntdb->ftable == ftable))
|
||||
return ntdb->ftable_off;
|
||||
|
||||
off = first_ftable(ntdb);
|
||||
for (i = 0; i < ftable; i++) {
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
break;
|
||||
}
|
||||
off = next_ftable(ntdb, off);
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
/* Note: we unlock the current bucket if fail (-ve), or coalesce (+ve) and
|
||||
* need to blatt the *protect record (which is set to an error). */
|
||||
static ntdb_len_t coalesce(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_off_t b_off,
|
||||
ntdb_len_t data_len,
|
||||
ntdb_off_t *protect)
|
||||
{
|
||||
ntdb_off_t end;
|
||||
struct ntdb_free_record rec;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ntdb->stats.alloc_coalesce_tried++;
|
||||
end = off + sizeof(struct ntdb_used_record) + data_len;
|
||||
|
||||
while (end < ntdb->file->map_size) {
|
||||
const struct ntdb_free_record *r;
|
||||
ntdb_off_t nb_off;
|
||||
unsigned ftable, bucket;
|
||||
|
||||
r = ntdb_access_read(ntdb, end, sizeof(*r), true);
|
||||
if (NTDB_PTR_IS_ERR(r)) {
|
||||
ecode = NTDB_PTR_ERR(r);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (frec_magic(r) != NTDB_FREE_MAGIC
|
||||
|| frec_ftable(r) == NTDB_FTABLE_NONE) {
|
||||
ntdb_access_release(ntdb, r);
|
||||
break;
|
||||
}
|
||||
|
||||
ftable = frec_ftable(r);
|
||||
bucket = size_to_bucket(frec_len(r));
|
||||
nb_off = ftable_offset(ntdb, ftable);
|
||||
if (NTDB_OFF_IS_ERR(nb_off)) {
|
||||
ntdb_access_release(ntdb, r);
|
||||
ecode = NTDB_OFF_TO_ERR(nb_off);
|
||||
goto err;
|
||||
}
|
||||
nb_off = bucket_off(nb_off, bucket);
|
||||
ntdb_access_release(ntdb, r);
|
||||
|
||||
/* We may be violating lock order here, so best effort. */
|
||||
if (ntdb_lock_free_bucket(ntdb, nb_off, NTDB_LOCK_NOWAIT)
|
||||
!= NTDB_SUCCESS) {
|
||||
ntdb->stats.alloc_coalesce_lockfail++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now we have lock, re-check. */
|
||||
ecode = ntdb_read_convert(ntdb, end, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_unlock_free_bucket(ntdb, nb_off);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (unlikely(frec_magic(&rec) != NTDB_FREE_MAGIC)) {
|
||||
ntdb->stats.alloc_coalesce_race++;
|
||||
ntdb_unlock_free_bucket(ntdb, nb_off);
|
||||
break;
|
||||
}
|
||||
|
||||
if (unlikely(frec_ftable(&rec) != ftable)
|
||||
|| unlikely(size_to_bucket(frec_len(&rec)) != bucket)) {
|
||||
ntdb->stats.alloc_coalesce_race++;
|
||||
ntdb_unlock_free_bucket(ntdb, nb_off);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Did we just mess up a record you were hoping to use? */
|
||||
if (end == *protect) {
|
||||
ntdb->stats.alloc_coalesce_iterate_clash++;
|
||||
*protect = NTDB_ERR_TO_OFF(NTDB_ERR_NOEXIST);
|
||||
}
|
||||
|
||||
ecode = remove_from_list(ntdb, nb_off, end, &rec);
|
||||
check_list(ntdb, nb_off);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_unlock_free_bucket(ntdb, nb_off);
|
||||
goto err;
|
||||
}
|
||||
|
||||
end += sizeof(struct ntdb_used_record) + frec_len(&rec);
|
||||
ntdb_unlock_free_bucket(ntdb, nb_off);
|
||||
ntdb->stats.alloc_coalesce_num_merged++;
|
||||
}
|
||||
|
||||
/* Didn't find any adjacent free? */
|
||||
if (end == off + sizeof(struct ntdb_used_record) + data_len)
|
||||
return 0;
|
||||
|
||||
/* Before we expand, check this isn't one you wanted protected? */
|
||||
if (off == *protect) {
|
||||
*protect = NTDB_ERR_TO_OFF(NTDB_ERR_EXISTS);
|
||||
ntdb->stats.alloc_coalesce_iterate_clash++;
|
||||
}
|
||||
|
||||
/* OK, expand initial record */
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (frec_len(&rec) != data_len) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"coalesce: expected data len %zu not %zu",
|
||||
(size_t)data_len, (size_t)frec_len(&rec));
|
||||
goto err;
|
||||
}
|
||||
|
||||
ecode = remove_from_list(ntdb, b_off, off, &rec);
|
||||
check_list(ntdb, b_off);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Try locking violation first. We don't allow coalesce recursion! */
|
||||
ecode = add_free_record(ntdb, off, end - off, NTDB_LOCK_NOWAIT, false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
/* Need to drop lock. Can't rely on anything stable. */
|
||||
ntdb->stats.alloc_coalesce_lockfail++;
|
||||
*protect = NTDB_ERR_TO_OFF(NTDB_ERR_CORRUPT);
|
||||
|
||||
/* We have to drop this to avoid deadlocks, so make sure record
|
||||
* doesn't get coalesced by someone else! */
|
||||
rec.ftable_and_len = (NTDB_FTABLE_NONE
|
||||
<< (64 - NTDB_OFF_UPPER_STEAL))
|
||||
| (end - off - sizeof(struct ntdb_used_record));
|
||||
ecode = ntdb_write_off(ntdb,
|
||||
off + offsetof(struct ntdb_free_record,
|
||||
ftable_and_len),
|
||||
rec.ftable_and_len);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
|
||||
ecode = add_free_record(ntdb, off, end - off, NTDB_LOCK_WAIT,
|
||||
false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
} else if (NTDB_OFF_IS_ERR(*protect)) {
|
||||
/* For simplicity, we always drop lock if they can't continue */
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
}
|
||||
ntdb->stats.alloc_coalesce_succeeded++;
|
||||
|
||||
/* Return usable length. */
|
||||
return end - off - sizeof(struct ntdb_used_record);
|
||||
|
||||
err:
|
||||
/* To unify error paths, we *always* unlock bucket on error. */
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
|
||||
/* List is locked: we unlock it. */
|
||||
static enum NTDB_ERROR coalesce_list(struct ntdb_context *ntdb,
|
||||
ntdb_off_t ftable_off,
|
||||
ntdb_off_t b_off,
|
||||
unsigned int limit)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
ntdb_off_t off;
|
||||
|
||||
off = ntdb_read_off(ntdb, b_off);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
ecode = NTDB_OFF_TO_ERR(off);
|
||||
goto unlock_err;
|
||||
}
|
||||
/* A little bit of paranoia: counter should be 0. */
|
||||
off &= NTDB_OFF_MASK;
|
||||
|
||||
while (off && limit--) {
|
||||
struct ntdb_free_record rec;
|
||||
ntdb_len_t coal;
|
||||
ntdb_off_t next;
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
next = rec.next;
|
||||
coal = coalesce(ntdb, off, b_off, frec_len(&rec), &next);
|
||||
if (NTDB_OFF_IS_ERR(coal)) {
|
||||
/* This has already unlocked on error. */
|
||||
return NTDB_OFF_TO_ERR(coal);
|
||||
}
|
||||
if (NTDB_OFF_IS_ERR(next)) {
|
||||
/* Coalescing had to unlock, so stop. */
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
/* Keep going if we're doing well... */
|
||||
limit += size_to_bucket(coal / 16 + NTDB_MIN_DATA_LEN);
|
||||
off = next;
|
||||
}
|
||||
|
||||
/* Now, move those elements to the tail of the list so we get something
|
||||
* else next time. */
|
||||
if (off) {
|
||||
struct ntdb_free_record oldhrec, newhrec, oldtrec, newtrec;
|
||||
ntdb_off_t oldhoff, oldtoff, newtoff;
|
||||
|
||||
/* The record we were up to is the new head. */
|
||||
ecode = ntdb_read_convert(ntdb, off, &newhrec, sizeof(newhrec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
/* Get the new tail. */
|
||||
newtoff = frec_prev(&newhrec);
|
||||
ecode = ntdb_read_convert(ntdb, newtoff, &newtrec,
|
||||
sizeof(newtrec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
/* Get the old head. */
|
||||
oldhoff = ntdb_read_off(ntdb, b_off);
|
||||
if (NTDB_OFF_IS_ERR(oldhoff)) {
|
||||
ecode = NTDB_OFF_TO_ERR(oldhoff);
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
/* This could happen if they all coalesced away. */
|
||||
if (oldhoff == off)
|
||||
goto out;
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, oldhoff, &oldhrec,
|
||||
sizeof(oldhrec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
/* Get the old tail. */
|
||||
oldtoff = frec_prev(&oldhrec);
|
||||
ecode = ntdb_read_convert(ntdb, oldtoff, &oldtrec,
|
||||
sizeof(oldtrec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
/* Old tail's next points to old head. */
|
||||
oldtrec.next = oldhoff;
|
||||
|
||||
/* Old head's prev points to old tail. */
|
||||
oldhrec.magic_and_prev
|
||||
= (NTDB_FREE_MAGIC << (64 - NTDB_OFF_UPPER_STEAL))
|
||||
| oldtoff;
|
||||
|
||||
/* New tail's next is 0. */
|
||||
newtrec.next = 0;
|
||||
|
||||
/* Write out the modified versions. */
|
||||
ecode = ntdb_write_convert(ntdb, oldtoff, &oldtrec,
|
||||
sizeof(oldtrec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
ecode = ntdb_write_convert(ntdb, oldhoff, &oldhrec,
|
||||
sizeof(oldhrec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
ecode = ntdb_write_convert(ntdb, newtoff, &newtrec,
|
||||
sizeof(newtrec));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
|
||||
/* And finally link in new head. */
|
||||
ecode = ntdb_write_off(ntdb, b_off, off);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto unlock_err;
|
||||
}
|
||||
out:
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
unlock_err:
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* List must not be locked if coalesce_ok is set. */
|
||||
enum NTDB_ERROR add_free_record(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_len_t len_with_header,
|
||||
enum ntdb_lock_flags waitflag,
|
||||
bool coalesce_ok)
|
||||
{
|
||||
ntdb_off_t b_off;
|
||||
ntdb_len_t len;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
assert(len_with_header >= sizeof(struct ntdb_free_record));
|
||||
|
||||
len = len_with_header - sizeof(struct ntdb_used_record);
|
||||
|
||||
b_off = bucket_off(ntdb->ftable_off, size_to_bucket(len));
|
||||
ecode = ntdb_lock_free_bucket(ntdb, b_off, waitflag);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
ecode = enqueue_in_free(ntdb, b_off, off, len, &coalesce_ok);
|
||||
check_list(ntdb, b_off);
|
||||
|
||||
/* Coalescing unlocks free list. */
|
||||
if (!ecode && coalesce_ok)
|
||||
ecode = coalesce_list(ntdb, ntdb->ftable_off, b_off, 2);
|
||||
else
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
return ecode;
|
||||
}
|
||||
|
||||
static size_t adjust_size(size_t keylen, size_t datalen)
|
||||
{
|
||||
size_t size = keylen + datalen;
|
||||
|
||||
if (size < NTDB_MIN_DATA_LEN)
|
||||
size = NTDB_MIN_DATA_LEN;
|
||||
|
||||
/* Round to next uint64_t boundary. */
|
||||
return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL);
|
||||
}
|
||||
|
||||
/* If we have enough left over to be useful, split that off. */
|
||||
static size_t record_leftover(size_t keylen, size_t datalen,
|
||||
bool want_extra, size_t total_len)
|
||||
{
|
||||
ssize_t leftover;
|
||||
|
||||
if (want_extra)
|
||||
datalen += datalen / 2;
|
||||
leftover = total_len - adjust_size(keylen, datalen);
|
||||
|
||||
if (leftover < (ssize_t)sizeof(struct ntdb_free_record))
|
||||
return 0;
|
||||
|
||||
return leftover;
|
||||
}
|
||||
|
||||
/* We need size bytes to put our key and data in. */
|
||||
static ntdb_off_t lock_and_alloc(struct ntdb_context *ntdb,
|
||||
ntdb_off_t ftable_off,
|
||||
ntdb_off_t bucket,
|
||||
size_t keylen, size_t datalen,
|
||||
bool want_extra,
|
||||
unsigned magic,
|
||||
unsigned hashlow)
|
||||
{
|
||||
ntdb_off_t off, b_off,best_off;
|
||||
struct ntdb_free_record best = { 0 };
|
||||
double multiplier;
|
||||
size_t size = adjust_size(keylen, datalen);
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ntdb->stats.allocs++;
|
||||
b_off = bucket_off(ftable_off, bucket);
|
||||
|
||||
/* FIXME: Try non-blocking wait first, to measure contention. */
|
||||
/* Lock this bucket. */
|
||||
ecode = ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
|
||||
best.ftable_and_len = -1ULL;
|
||||
best_off = 0;
|
||||
|
||||
/* Get slack if we're after extra. */
|
||||
if (want_extra)
|
||||
multiplier = 1.5;
|
||||
else
|
||||
multiplier = 1.0;
|
||||
|
||||
/* Walk the list to see if any are large enough, getting less fussy
|
||||
* as we go. */
|
||||
off = ntdb_read_off(ntdb, b_off);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
ecode = NTDB_OFF_TO_ERR(off);
|
||||
goto unlock_err;
|
||||
}
|
||||
off &= NTDB_OFF_MASK;
|
||||
|
||||
while (off) {
|
||||
const struct ntdb_free_record *r;
|
||||
ntdb_len_t len;
|
||||
ntdb_off_t next;
|
||||
|
||||
r = ntdb_access_read(ntdb, off, sizeof(*r), true);
|
||||
if (NTDB_PTR_IS_ERR(r)) {
|
||||
ecode = NTDB_PTR_ERR(r);
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
if (frec_magic(r) != NTDB_FREE_MAGIC) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
|
||||
"lock_and_alloc:"
|
||||
" %llu non-free 0x%llx",
|
||||
(long long)off,
|
||||
(long long)r->magic_and_prev);
|
||||
ntdb_access_release(ntdb, r);
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
if (frec_len(r) >= size && frec_len(r) < frec_len(&best)) {
|
||||
best_off = off;
|
||||
best = *r;
|
||||
}
|
||||
|
||||
if (frec_len(&best) <= size * multiplier && best_off) {
|
||||
ntdb_access_release(ntdb, r);
|
||||
break;
|
||||
}
|
||||
|
||||
multiplier *= 1.01;
|
||||
|
||||
next = r->next;
|
||||
len = frec_len(r);
|
||||
ntdb_access_release(ntdb, r);
|
||||
off = next;
|
||||
}
|
||||
|
||||
/* If we found anything at all, use it. */
|
||||
if (best_off) {
|
||||
struct ntdb_used_record rec;
|
||||
size_t leftover;
|
||||
|
||||
/* We're happy with this size: take it. */
|
||||
ecode = remove_from_list(ntdb, b_off, best_off, &best);
|
||||
check_list(ntdb, b_off);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
leftover = record_leftover(keylen, datalen, want_extra,
|
||||
frec_len(&best));
|
||||
|
||||
assert(keylen + datalen + leftover <= frec_len(&best));
|
||||
/* We need to mark non-free before we drop lock, otherwise
|
||||
* coalesce() could try to merge it! */
|
||||
ecode = set_header(ntdb, &rec, magic, keylen, datalen,
|
||||
frec_len(&best) - leftover, hashlow);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
ecode = ntdb_write_convert(ntdb, best_off, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
/* For futureproofing, we put a 0 in any unused space. */
|
||||
if (rec_extra_padding(&rec)) {
|
||||
ecode = ntdb->io->twrite(ntdb, best_off + sizeof(rec)
|
||||
+ keylen + datalen, "", 1);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto unlock_err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bucket of leftover will be <= current bucket, so nested
|
||||
* locking is allowed. */
|
||||
if (leftover) {
|
||||
ntdb->stats.alloc_leftover++;
|
||||
ecode = add_free_record(ntdb,
|
||||
best_off + sizeof(rec)
|
||||
+ frec_len(&best) - leftover,
|
||||
leftover, NTDB_LOCK_WAIT, false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
best_off = NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
}
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
|
||||
return best_off;
|
||||
}
|
||||
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
return 0;
|
||||
|
||||
unlock_err:
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
|
||||
/* Get a free block from current free list, or 0 if none, -ve on error. */
|
||||
static ntdb_off_t get_free(struct ntdb_context *ntdb,
|
||||
size_t keylen, size_t datalen, bool want_extra,
|
||||
unsigned magic, unsigned hashlow)
|
||||
{
|
||||
ntdb_off_t off, ftable_off;
|
||||
ntdb_off_t start_b, b, ftable;
|
||||
bool wrapped = false;
|
||||
|
||||
/* If they are growing, add 50% to get to higher bucket. */
|
||||
if (want_extra)
|
||||
start_b = size_to_bucket(adjust_size(keylen,
|
||||
datalen + datalen / 2));
|
||||
else
|
||||
start_b = size_to_bucket(adjust_size(keylen, datalen));
|
||||
|
||||
ftable_off = ntdb->ftable_off;
|
||||
ftable = ntdb->ftable;
|
||||
while (!wrapped || ftable_off != ntdb->ftable_off) {
|
||||
/* Start at exact size bucket, and search up... */
|
||||
for (b = find_free_head(ntdb, ftable_off, start_b);
|
||||
b < NTDB_FREE_BUCKETS;
|
||||
b = find_free_head(ntdb, ftable_off, b + 1)) {
|
||||
/* Try getting one from list. */
|
||||
off = lock_and_alloc(ntdb, ftable_off,
|
||||
b, keylen, datalen, want_extra,
|
||||
magic, hashlow);
|
||||
if (NTDB_OFF_IS_ERR(off))
|
||||
return off;
|
||||
if (off != 0) {
|
||||
if (b == start_b)
|
||||
ntdb->stats.alloc_bucket_exact++;
|
||||
if (b == NTDB_FREE_BUCKETS - 1)
|
||||
ntdb->stats.alloc_bucket_max++;
|
||||
/* Worked? Stay using this list. */
|
||||
ntdb->ftable_off = ftable_off;
|
||||
ntdb->ftable = ftable;
|
||||
return off;
|
||||
}
|
||||
/* Didn't work. Try next bucket. */
|
||||
}
|
||||
|
||||
if (NTDB_OFF_IS_ERR(b)) {
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Hmm, try next table. */
|
||||
ftable_off = next_ftable(ntdb, ftable_off);
|
||||
if (NTDB_OFF_IS_ERR(ftable_off)) {
|
||||
return ftable_off;
|
||||
}
|
||||
ftable++;
|
||||
|
||||
if (ftable_off == 0) {
|
||||
wrapped = true;
|
||||
ftable_off = first_ftable(ntdb);
|
||||
if (NTDB_OFF_IS_ERR(ftable_off)) {
|
||||
return ftable_off;
|
||||
}
|
||||
ftable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR set_header(struct ntdb_context *ntdb,
|
||||
struct ntdb_used_record *rec,
|
||||
unsigned magic, uint64_t keylen, uint64_t datalen,
|
||||
uint64_t actuallen, unsigned hashlow)
|
||||
{
|
||||
uint64_t keybits = (fls64(keylen) + 1) / 2;
|
||||
|
||||
/* Use bottom bits of hash, so it's independent of hash table size. */
|
||||
rec->magic_and_meta = (hashlow & ((1 << 11)-1))
|
||||
| ((actuallen - (keylen + datalen)) << 11)
|
||||
| (keybits << 43)
|
||||
| ((uint64_t)magic << 48);
|
||||
rec->key_and_data_len = (keylen | (datalen << (keybits*2)));
|
||||
|
||||
/* Encoding can fail on big values. */
|
||||
if (rec_key_length(rec) != keylen
|
||||
|| rec_data_length(rec) != datalen
|
||||
|| rec_extra_padding(rec) != actuallen - (keylen + datalen)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"Could not encode k=%llu,d=%llu,a=%llu",
|
||||
(long long)keylen, (long long)datalen,
|
||||
(long long)actuallen);
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* You need 'size', this tells you how much you should expand by. */
|
||||
ntdb_off_t ntdb_expand_adjust(ntdb_off_t map_size, ntdb_off_t size)
|
||||
{
|
||||
ntdb_off_t new_size, top_size;
|
||||
|
||||
/* limit size in order to avoid using up huge amounts of memory for
|
||||
* in memory tdbs if an oddball huge record creeps in */
|
||||
if (size > 100 * 1024) {
|
||||
top_size = map_size + size * 2;
|
||||
} else {
|
||||
top_size = map_size + size * 100;
|
||||
}
|
||||
|
||||
/* always make room for at least top_size more records, and at
|
||||
least 25% more space. if the DB is smaller than 100MiB,
|
||||
otherwise grow it by 10% only. */
|
||||
if (map_size > 100 * 1024 * 1024) {
|
||||
new_size = map_size * 1.10;
|
||||
} else {
|
||||
new_size = map_size * 1.25;
|
||||
}
|
||||
|
||||
/* Round the database up to a multiple of the page size */
|
||||
if (new_size < top_size)
|
||||
new_size = top_size;
|
||||
return new_size - map_size;
|
||||
}
|
||||
|
||||
/* Expand the database. */
|
||||
static enum NTDB_ERROR ntdb_expand(struct ntdb_context *ntdb, ntdb_len_t size)
|
||||
{
|
||||
uint64_t old_size;
|
||||
ntdb_len_t wanted;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* Need to hold a hash lock to expand DB: transactions rely on it. */
|
||||
if (!(ntdb->flags & NTDB_NOLOCK)
|
||||
&& !ntdb->file->allrecord_lock.count && !ntdb_has_hash_locks(ntdb)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_expand: must hold lock during expand");
|
||||
}
|
||||
|
||||
/* Only one person can expand file at a time. */
|
||||
ecode = ntdb_lock_expand(ntdb, F_WRLCK);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* Someone else may have expanded the file, so retry. */
|
||||
old_size = ntdb->file->map_size;
|
||||
ntdb->io->oob(ntdb, ntdb->file->map_size, 1, true);
|
||||
if (ntdb->file->map_size != old_size) {
|
||||
ntdb_unlock_expand(ntdb, F_WRLCK);
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* Overallocate. */
|
||||
wanted = ntdb_expand_adjust(old_size, size);
|
||||
/* We need room for the record header too. */
|
||||
wanted = adjust_size(0, sizeof(struct ntdb_used_record) + wanted);
|
||||
|
||||
ecode = ntdb->io->expand_file(ntdb, wanted);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_unlock_expand(ntdb, F_WRLCK);
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* We need to drop this lock before adding free record. */
|
||||
ntdb_unlock_expand(ntdb, F_WRLCK);
|
||||
|
||||
ntdb->stats.expands++;
|
||||
return add_free_record(ntdb, old_size, wanted, NTDB_LOCK_WAIT, true);
|
||||
}
|
||||
|
||||
/* This won't fail: it will expand the database if it has to. */
|
||||
ntdb_off_t alloc(struct ntdb_context *ntdb, size_t keylen, size_t datalen,
|
||||
uint64_t hash, unsigned magic, bool growing)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
|
||||
/* We can't hold pointers during this: we could unmap! */
|
||||
assert(!ntdb->direct_access);
|
||||
|
||||
for (;;) {
|
||||
enum NTDB_ERROR ecode;
|
||||
off = get_free(ntdb, keylen, datalen, growing, magic, hash);
|
||||
if (likely(off != 0))
|
||||
break;
|
||||
|
||||
ecode = ntdb_expand(ntdb, adjust_size(keylen, datalen));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
894
lib/ntdb/hash.c
Normal file
894
lib/ntdb/hash.c
Normal file
@ -0,0 +1,894 @@
|
||||
/*
|
||||
Trivial Database 2: hash handling
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "private.h"
|
||||
#include <ccan/hash/hash.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Default hash function. */
|
||||
uint64_t ntdb_jenkins_hash(const void *key, size_t length, uint64_t seed,
|
||||
void *unused)
|
||||
{
|
||||
uint64_t ret;
|
||||
/* hash64_stable assumes lower bits are more important; they are a
|
||||
* slightly better hash. We use the upper bits first, so swap them. */
|
||||
ret = hash64_stable((const unsigned char *)key, length, seed);
|
||||
return (ret >> 32) | (ret << 32);
|
||||
}
|
||||
|
||||
uint64_t ntdb_hash(struct ntdb_context *ntdb, const void *ptr, size_t len)
|
||||
{
|
||||
return ntdb->hash_fn(ptr, len, ntdb->hash_seed, ntdb->hash_data);
|
||||
}
|
||||
|
||||
uint64_t hash_record(struct ntdb_context *ntdb, ntdb_off_t off)
|
||||
{
|
||||
const struct ntdb_used_record *r;
|
||||
const void *key;
|
||||
uint64_t klen, hash;
|
||||
|
||||
r = ntdb_access_read(ntdb, off, sizeof(*r), true);
|
||||
if (NTDB_PTR_IS_ERR(r)) {
|
||||
/* FIXME */
|
||||
return 0;
|
||||
}
|
||||
|
||||
klen = rec_key_length(r);
|
||||
ntdb_access_release(ntdb, r);
|
||||
|
||||
key = ntdb_access_read(ntdb, off + sizeof(*r), klen, false);
|
||||
if (NTDB_PTR_IS_ERR(key)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hash = ntdb_hash(ntdb, key, klen);
|
||||
ntdb_access_release(ntdb, key);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Get bits from a value. */
|
||||
static uint32_t bits_from(uint64_t val, unsigned start, unsigned num)
|
||||
{
|
||||
assert(num <= 32);
|
||||
return (val >> start) & ((1U << num) - 1);
|
||||
}
|
||||
|
||||
/* We take bits from the top: that way we can lock whole sections of the hash
|
||||
* by using lock ranges. */
|
||||
static uint32_t use_bits(struct hash_info *h, unsigned num)
|
||||
{
|
||||
h->hash_used += num;
|
||||
return bits_from(h->h, 64 - h->hash_used, num);
|
||||
}
|
||||
|
||||
static ntdb_bool_err key_matches(struct ntdb_context *ntdb,
|
||||
const struct ntdb_used_record *rec,
|
||||
ntdb_off_t off,
|
||||
const NTDB_DATA *key)
|
||||
{
|
||||
ntdb_bool_err ret = false;
|
||||
const char *rkey;
|
||||
|
||||
if (rec_key_length(rec) != key->dsize) {
|
||||
ntdb->stats.compare_wrong_keylen++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
rkey = ntdb_access_read(ntdb, off + sizeof(*rec), key->dsize, false);
|
||||
if (NTDB_PTR_IS_ERR(rkey)) {
|
||||
return (ntdb_bool_err)NTDB_PTR_ERR(rkey);
|
||||
}
|
||||
if (memcmp(rkey, key->dptr, key->dsize) == 0)
|
||||
ret = true;
|
||||
else
|
||||
ntdb->stats.compare_wrong_keycmp++;
|
||||
ntdb_access_release(ntdb, rkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Does entry match? */
|
||||
static ntdb_bool_err match(struct ntdb_context *ntdb,
|
||||
struct hash_info *h,
|
||||
const NTDB_DATA *key,
|
||||
ntdb_off_t val,
|
||||
struct ntdb_used_record *rec)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ntdb->stats.compares++;
|
||||
/* Desired bucket must match. */
|
||||
if (h->home_bucket != (val & NTDB_OFF_HASH_GROUP_MASK)) {
|
||||
ntdb->stats.compare_wrong_bucket++;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Top bits of offset == next bits of hash. */
|
||||
if (bits_from(val, NTDB_OFF_HASH_EXTRA_BIT, NTDB_OFF_UPPER_STEAL_EXTRA)
|
||||
!= bits_from(h->h, 64 - h->hash_used - NTDB_OFF_UPPER_STEAL_EXTRA,
|
||||
NTDB_OFF_UPPER_STEAL_EXTRA)) {
|
||||
ntdb->stats.compare_wrong_offsetbits++;
|
||||
return false;
|
||||
}
|
||||
|
||||
off = val & NTDB_OFF_MASK;
|
||||
ecode = ntdb_read_convert(ntdb, off, rec, sizeof(*rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return (ntdb_bool_err)ecode;
|
||||
}
|
||||
|
||||
if ((h->h & ((1 << 11)-1)) != rec_hash(rec)) {
|
||||
ntdb->stats.compare_wrong_rechash++;
|
||||
return false;
|
||||
}
|
||||
|
||||
return key_matches(ntdb, rec, off, key);
|
||||
}
|
||||
|
||||
static ntdb_off_t hbucket_off(ntdb_off_t group_start, unsigned bucket)
|
||||
{
|
||||
return group_start
|
||||
+ (bucket % (1 << NTDB_HASH_GROUP_BITS)) * sizeof(ntdb_off_t);
|
||||
}
|
||||
|
||||
bool is_subhash(ntdb_off_t val)
|
||||
{
|
||||
return (val >> NTDB_OFF_UPPER_STEAL_SUBHASH_BIT) & 1;
|
||||
}
|
||||
|
||||
/* FIXME: Guess the depth, don't over-lock! */
|
||||
static ntdb_off_t hlock_range(ntdb_off_t group, ntdb_off_t *size)
|
||||
{
|
||||
*size = 1ULL << (64 - (NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS));
|
||||
return group << (64 - (NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS));
|
||||
}
|
||||
|
||||
static ntdb_off_t COLD find_in_chain(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key,
|
||||
ntdb_off_t chain,
|
||||
struct hash_info *h,
|
||||
struct ntdb_used_record *rec,
|
||||
struct traverse_info *tinfo)
|
||||
{
|
||||
ntdb_off_t off, next;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* In case nothing is free, we set these to zero. */
|
||||
h->home_bucket = h->found_bucket = 0;
|
||||
|
||||
for (off = chain; off; off = next) {
|
||||
unsigned int i;
|
||||
|
||||
h->group_start = off;
|
||||
ecode = ntdb_read_convert(ntdb, off, h->group, sizeof(h->group));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
|
||||
for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
|
||||
ntdb_off_t recoff;
|
||||
if (!h->group[i]) {
|
||||
/* Remember this empty bucket. */
|
||||
h->home_bucket = h->found_bucket = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We can insert extra bits via add_to_hash
|
||||
* empty bucket logic. */
|
||||
recoff = h->group[i] & NTDB_OFF_MASK;
|
||||
ecode = ntdb_read_convert(ntdb, recoff, rec,
|
||||
sizeof(*rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
|
||||
ecode = NTDB_OFF_TO_ERR(key_matches(ntdb, rec, recoff,
|
||||
&key));
|
||||
if (ecode < 0) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
if (ecode == (enum NTDB_ERROR)1) {
|
||||
h->home_bucket = h->found_bucket = i;
|
||||
|
||||
if (tinfo) {
|
||||
tinfo->levels[tinfo->num_levels]
|
||||
.hashtable = off;
|
||||
tinfo->levels[tinfo->num_levels]
|
||||
.total_buckets
|
||||
= 1 << NTDB_HASH_GROUP_BITS;
|
||||
tinfo->levels[tinfo->num_levels].entry
|
||||
= i;
|
||||
tinfo->num_levels++;
|
||||
}
|
||||
return recoff;
|
||||
}
|
||||
}
|
||||
next = ntdb_read_off(ntdb, off
|
||||
+ offsetof(struct ntdb_chain, next));
|
||||
if (NTDB_OFF_IS_ERR(next)) {
|
||||
return next;
|
||||
}
|
||||
if (next)
|
||||
next += sizeof(struct ntdb_used_record);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the core routine which searches the hashtable for an entry.
|
||||
* On error, no locks are held and -ve is returned.
|
||||
* Otherwise, hinfo is filled in (and the optional tinfo).
|
||||
* If not found, the return value is 0.
|
||||
* If found, the return value is the offset, and *rec is the record. */
|
||||
ntdb_off_t find_and_lock(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key,
|
||||
int ltype,
|
||||
struct hash_info *h,
|
||||
struct ntdb_used_record *rec,
|
||||
struct traverse_info *tinfo)
|
||||
{
|
||||
uint32_t i, group;
|
||||
ntdb_off_t hashtable;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
h->h = ntdb_hash(ntdb, key.dptr, key.dsize);
|
||||
h->hash_used = 0;
|
||||
group = use_bits(h, NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS);
|
||||
h->home_bucket = use_bits(h, NTDB_HASH_GROUP_BITS);
|
||||
|
||||
h->hlock_start = hlock_range(group, &h->hlock_range);
|
||||
ecode = ntdb_lock_hashes(ntdb, h->hlock_start, h->hlock_range, ltype,
|
||||
NTDB_LOCK_WAIT);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
|
||||
hashtable = offsetof(struct ntdb_header, hashtable);
|
||||
if (tinfo) {
|
||||
tinfo->toplevel_group = group;
|
||||
tinfo->num_levels = 1;
|
||||
tinfo->levels[0].entry = 0;
|
||||
tinfo->levels[0].hashtable = hashtable
|
||||
+ (group << NTDB_HASH_GROUP_BITS) * sizeof(ntdb_off_t);
|
||||
tinfo->levels[0].total_buckets = 1 << NTDB_HASH_GROUP_BITS;
|
||||
}
|
||||
|
||||
while (h->hash_used <= 64) {
|
||||
/* Read in the hash group. */
|
||||
h->group_start = hashtable
|
||||
+ group * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, h->group_start, &h->group,
|
||||
sizeof(h->group));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Pointer to another hash table? Go down... */
|
||||
if (is_subhash(h->group[h->home_bucket])) {
|
||||
hashtable = (h->group[h->home_bucket] & NTDB_OFF_MASK)
|
||||
+ sizeof(struct ntdb_used_record);
|
||||
if (tinfo) {
|
||||
/* When we come back, use *next* bucket */
|
||||
tinfo->levels[tinfo->num_levels-1].entry
|
||||
+= h->home_bucket + 1;
|
||||
}
|
||||
group = use_bits(h, NTDB_SUBLEVEL_HASH_BITS
|
||||
- NTDB_HASH_GROUP_BITS);
|
||||
h->home_bucket = use_bits(h, NTDB_HASH_GROUP_BITS);
|
||||
if (tinfo) {
|
||||
tinfo->levels[tinfo->num_levels].hashtable
|
||||
= hashtable;
|
||||
tinfo->levels[tinfo->num_levels].total_buckets
|
||||
= 1 << NTDB_SUBLEVEL_HASH_BITS;
|
||||
tinfo->levels[tinfo->num_levels].entry
|
||||
= group << NTDB_HASH_GROUP_BITS;
|
||||
tinfo->num_levels++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* It's in this group: search (until 0 or all searched) */
|
||||
for (i = 0, h->found_bucket = h->home_bucket;
|
||||
i < (1 << NTDB_HASH_GROUP_BITS);
|
||||
i++, h->found_bucket = ((h->found_bucket+1)
|
||||
% (1 << NTDB_HASH_GROUP_BITS))) {
|
||||
ntdb_bool_err berr;
|
||||
if (is_subhash(h->group[h->found_bucket]))
|
||||
continue;
|
||||
|
||||
if (!h->group[h->found_bucket])
|
||||
break;
|
||||
|
||||
berr = match(ntdb, h, &key, h->group[h->found_bucket],
|
||||
rec);
|
||||
if (berr < 0) {
|
||||
ecode = NTDB_OFF_TO_ERR(berr);
|
||||
goto fail;
|
||||
}
|
||||
if (berr) {
|
||||
if (tinfo) {
|
||||
tinfo->levels[tinfo->num_levels-1].entry
|
||||
+= h->found_bucket;
|
||||
}
|
||||
return h->group[h->found_bucket] & NTDB_OFF_MASK;
|
||||
}
|
||||
}
|
||||
/* Didn't find it: h indicates where it would go. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return find_in_chain(ntdb, key, hashtable, h, rec, tinfo);
|
||||
|
||||
fail:
|
||||
ntdb_unlock_hashes(ntdb, h->hlock_start, h->hlock_range, ltype);
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
|
||||
/* I wrote a simple test, expanding a hash to 2GB, for the following
|
||||
* cases:
|
||||
* 1) Expanding all the buckets at once,
|
||||
* 2) Expanding the bucket we wanted to place the new entry into.
|
||||
* 3) Expanding the most-populated bucket,
|
||||
*
|
||||
* I measured the worst/average/best density during this process.
|
||||
* 1) 3%/16%/30%
|
||||
* 2) 4%/20%/38%
|
||||
* 3) 6%/22%/41%
|
||||
*
|
||||
* So we figure out the busiest bucket for the moment.
|
||||
*/
|
||||
static unsigned fullest_bucket(struct ntdb_context *ntdb,
|
||||
const ntdb_off_t *group,
|
||||
unsigned new_bucket)
|
||||
{
|
||||
unsigned counts[1 << NTDB_HASH_GROUP_BITS] = { 0 };
|
||||
unsigned int i, best_bucket;
|
||||
|
||||
/* Count the new entry. */
|
||||
counts[new_bucket]++;
|
||||
best_bucket = new_bucket;
|
||||
|
||||
for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
|
||||
unsigned this_bucket;
|
||||
|
||||
if (is_subhash(group[i]))
|
||||
continue;
|
||||
this_bucket = group[i] & NTDB_OFF_HASH_GROUP_MASK;
|
||||
if (++counts[this_bucket] > counts[best_bucket])
|
||||
best_bucket = this_bucket;
|
||||
}
|
||||
|
||||
return best_bucket;
|
||||
}
|
||||
|
||||
static bool put_into_group(ntdb_off_t *group,
|
||||
unsigned bucket, ntdb_off_t encoded)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
|
||||
unsigned b = (bucket + i) % (1 << NTDB_HASH_GROUP_BITS);
|
||||
|
||||
if (group[b] == 0) {
|
||||
group[b] = encoded;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void force_into_group(ntdb_off_t *group,
|
||||
unsigned bucket, ntdb_off_t encoded)
|
||||
{
|
||||
if (!put_into_group(group, bucket, encoded))
|
||||
abort();
|
||||
}
|
||||
|
||||
static ntdb_off_t encode_offset(ntdb_off_t new_off, struct hash_info *h)
|
||||
{
|
||||
return h->home_bucket
|
||||
| new_off
|
||||
| ((uint64_t)bits_from(h->h,
|
||||
64 - h->hash_used - NTDB_OFF_UPPER_STEAL_EXTRA,
|
||||
NTDB_OFF_UPPER_STEAL_EXTRA)
|
||||
<< NTDB_OFF_HASH_EXTRA_BIT);
|
||||
}
|
||||
|
||||
/* Simply overwrite the hash entry we found before. */
|
||||
enum NTDB_ERROR replace_in_hash(struct ntdb_context *ntdb,
|
||||
struct hash_info *h,
|
||||
ntdb_off_t new_off)
|
||||
{
|
||||
return ntdb_write_off(ntdb, hbucket_off(h->group_start, h->found_bucket),
|
||||
encode_offset(new_off, h));
|
||||
}
|
||||
|
||||
/* We slot in anywhere that's empty in the chain. */
|
||||
static enum NTDB_ERROR COLD add_to_chain(struct ntdb_context *ntdb,
|
||||
ntdb_off_t subhash,
|
||||
ntdb_off_t new_off)
|
||||
{
|
||||
ntdb_off_t entry;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
entry = ntdb_find_zero_off(ntdb, subhash, 1<<NTDB_HASH_GROUP_BITS);
|
||||
if (NTDB_OFF_IS_ERR(entry)) {
|
||||
return NTDB_OFF_TO_ERR(entry);
|
||||
}
|
||||
|
||||
if (entry == 1 << NTDB_HASH_GROUP_BITS) {
|
||||
ntdb_off_t next;
|
||||
|
||||
next = ntdb_read_off(ntdb, subhash
|
||||
+ offsetof(struct ntdb_chain, next));
|
||||
if (NTDB_OFF_IS_ERR(next)) {
|
||||
return NTDB_OFF_TO_ERR(next);
|
||||
}
|
||||
|
||||
if (!next) {
|
||||
next = alloc(ntdb, 0, sizeof(struct ntdb_chain), 0,
|
||||
NTDB_CHAIN_MAGIC, false);
|
||||
if (NTDB_OFF_IS_ERR(next))
|
||||
return NTDB_OFF_TO_ERR(next);
|
||||
ecode = zero_out(ntdb,
|
||||
next+sizeof(struct ntdb_used_record),
|
||||
sizeof(struct ntdb_chain));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
ecode = ntdb_write_off(ntdb, subhash
|
||||
+ offsetof(struct ntdb_chain,
|
||||
next),
|
||||
next);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
}
|
||||
return add_to_chain(ntdb, next, new_off);
|
||||
}
|
||||
|
||||
return ntdb_write_off(ntdb, subhash + entry * sizeof(ntdb_off_t),
|
||||
new_off);
|
||||
}
|
||||
|
||||
/* Add into a newly created subhash. */
|
||||
static enum NTDB_ERROR add_to_subhash(struct ntdb_context *ntdb, ntdb_off_t subhash,
|
||||
unsigned hash_used, ntdb_off_t val)
|
||||
{
|
||||
ntdb_off_t off = (val & NTDB_OFF_MASK), *group;
|
||||
struct hash_info h;
|
||||
unsigned int gnum;
|
||||
|
||||
h.hash_used = hash_used;
|
||||
|
||||
if (hash_used + NTDB_SUBLEVEL_HASH_BITS > 64)
|
||||
return add_to_chain(ntdb, subhash, off);
|
||||
|
||||
h.h = hash_record(ntdb, off);
|
||||
gnum = use_bits(&h, NTDB_SUBLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS);
|
||||
h.group_start = subhash
|
||||
+ gnum * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
|
||||
h.home_bucket = use_bits(&h, NTDB_HASH_GROUP_BITS);
|
||||
|
||||
group = ntdb_access_write(ntdb, h.group_start,
|
||||
sizeof(*group) << NTDB_HASH_GROUP_BITS, true);
|
||||
if (NTDB_PTR_IS_ERR(group)) {
|
||||
return NTDB_PTR_ERR(group);
|
||||
}
|
||||
force_into_group(group, h.home_bucket, encode_offset(off, &h));
|
||||
return ntdb_access_commit(ntdb, group);
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR expand_group(struct ntdb_context *ntdb, struct hash_info *h)
|
||||
{
|
||||
unsigned bucket, num_vals, i, magic;
|
||||
size_t subsize;
|
||||
ntdb_off_t subhash;
|
||||
ntdb_off_t vals[1 << NTDB_HASH_GROUP_BITS];
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* Attach new empty subhash under fullest bucket. */
|
||||
bucket = fullest_bucket(ntdb, h->group, h->home_bucket);
|
||||
|
||||
if (h->hash_used == 64) {
|
||||
ntdb->stats.alloc_chain++;
|
||||
subsize = sizeof(struct ntdb_chain);
|
||||
magic = NTDB_CHAIN_MAGIC;
|
||||
} else {
|
||||
ntdb->stats.alloc_subhash++;
|
||||
subsize = (sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS);
|
||||
magic = NTDB_HTABLE_MAGIC;
|
||||
}
|
||||
|
||||
subhash = alloc(ntdb, 0, subsize, 0, magic, false);
|
||||
if (NTDB_OFF_IS_ERR(subhash)) {
|
||||
return NTDB_OFF_TO_ERR(subhash);
|
||||
}
|
||||
|
||||
ecode = zero_out(ntdb, subhash + sizeof(struct ntdb_used_record),
|
||||
subsize);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* Remove any which are destined for bucket or are in wrong place. */
|
||||
num_vals = 0;
|
||||
for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
|
||||
unsigned home_bucket = h->group[i] & NTDB_OFF_HASH_GROUP_MASK;
|
||||
if (!h->group[i] || is_subhash(h->group[i]))
|
||||
continue;
|
||||
if (home_bucket == bucket || home_bucket != i) {
|
||||
vals[num_vals++] = h->group[i];
|
||||
h->group[i] = 0;
|
||||
}
|
||||
}
|
||||
/* FIXME: This assert is valid, but we do this during unit test :( */
|
||||
/* assert(num_vals); */
|
||||
|
||||
/* Overwrite expanded bucket with subhash pointer. */
|
||||
h->group[bucket] = subhash | (1ULL << NTDB_OFF_UPPER_STEAL_SUBHASH_BIT);
|
||||
|
||||
/* Point to actual contents of record. */
|
||||
subhash += sizeof(struct ntdb_used_record);
|
||||
|
||||
/* Put values back. */
|
||||
for (i = 0; i < num_vals; i++) {
|
||||
unsigned this_bucket = vals[i] & NTDB_OFF_HASH_GROUP_MASK;
|
||||
|
||||
if (this_bucket == bucket) {
|
||||
ecode = add_to_subhash(ntdb, subhash, h->hash_used,
|
||||
vals[i]);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
} else {
|
||||
/* There should be room to put this back. */
|
||||
force_into_group(h->group, this_bucket, vals[i]);
|
||||
}
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR delete_from_hash(struct ntdb_context *ntdb, struct hash_info *h)
|
||||
{
|
||||
unsigned int i, num_movers = 0;
|
||||
ntdb_off_t movers[1 << NTDB_HASH_GROUP_BITS];
|
||||
|
||||
h->group[h->found_bucket] = 0;
|
||||
for (i = 1; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
|
||||
unsigned this_bucket;
|
||||
|
||||
this_bucket = (h->found_bucket+i) % (1 << NTDB_HASH_GROUP_BITS);
|
||||
/* Empty bucket? We're done. */
|
||||
if (!h->group[this_bucket])
|
||||
break;
|
||||
|
||||
/* Ignore subhashes. */
|
||||
if (is_subhash(h->group[this_bucket]))
|
||||
continue;
|
||||
|
||||
/* If this one is not happy where it is, we'll move it. */
|
||||
if ((h->group[this_bucket] & NTDB_OFF_HASH_GROUP_MASK)
|
||||
!= this_bucket) {
|
||||
movers[num_movers++] = h->group[this_bucket];
|
||||
h->group[this_bucket] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Put back the ones we erased. */
|
||||
for (i = 0; i < num_movers; i++) {
|
||||
force_into_group(h->group, movers[i] & NTDB_OFF_HASH_GROUP_MASK,
|
||||
movers[i]);
|
||||
}
|
||||
|
||||
/* Now we write back the hash group */
|
||||
return ntdb_write_convert(ntdb, h->group_start,
|
||||
h->group, sizeof(h->group));
|
||||
}
|
||||
|
||||
enum NTDB_ERROR add_to_hash(struct ntdb_context *ntdb, struct hash_info *h,
|
||||
ntdb_off_t new_off)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* We hit an empty bucket during search? That's where it goes. */
|
||||
if (!h->group[h->found_bucket]) {
|
||||
h->group[h->found_bucket] = encode_offset(new_off, h);
|
||||
/* Write back the modified group. */
|
||||
return ntdb_write_convert(ntdb, h->group_start,
|
||||
h->group, sizeof(h->group));
|
||||
}
|
||||
|
||||
if (h->hash_used > 64)
|
||||
return add_to_chain(ntdb, h->group_start, new_off);
|
||||
|
||||
/* We're full. Expand. */
|
||||
ecode = expand_group(ntdb, h);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (is_subhash(h->group[h->home_bucket])) {
|
||||
/* We were expanded! */
|
||||
ntdb_off_t hashtable;
|
||||
unsigned int gnum;
|
||||
|
||||
/* Write back the modified group. */
|
||||
ecode = ntdb_write_convert(ntdb, h->group_start, h->group,
|
||||
sizeof(h->group));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* Move hashinfo down a level. */
|
||||
hashtable = (h->group[h->home_bucket] & NTDB_OFF_MASK)
|
||||
+ sizeof(struct ntdb_used_record);
|
||||
gnum = use_bits(h,NTDB_SUBLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS);
|
||||
h->home_bucket = use_bits(h, NTDB_HASH_GROUP_BITS);
|
||||
h->group_start = hashtable
|
||||
+ gnum * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
|
||||
ecode = ntdb_read_convert(ntdb, h->group_start, &h->group,
|
||||
sizeof(h->group));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
}
|
||||
|
||||
/* Expanding the group must have made room if it didn't choose this
|
||||
* bucket. */
|
||||
if (put_into_group(h->group, h->home_bucket, encode_offset(new_off,h))){
|
||||
return ntdb_write_convert(ntdb, h->group_start,
|
||||
h->group, sizeof(h->group));
|
||||
}
|
||||
|
||||
/* This can happen if all hashes in group (and us) dropped into same
|
||||
* group in subhash. */
|
||||
return add_to_hash(ntdb, h, new_off);
|
||||
}
|
||||
|
||||
/* Traverse support: returns offset of record, or 0 or -ve error. */
|
||||
static ntdb_off_t iterate_hash(struct ntdb_context *ntdb,
|
||||
struct traverse_info *tinfo)
|
||||
{
|
||||
ntdb_off_t off, val, i;
|
||||
struct traverse_level *tlevel;
|
||||
|
||||
tlevel = &tinfo->levels[tinfo->num_levels-1];
|
||||
|
||||
again:
|
||||
for (i = ntdb_find_nonzero_off(ntdb, tlevel->hashtable,
|
||||
tlevel->entry, tlevel->total_buckets);
|
||||
i != tlevel->total_buckets;
|
||||
i = ntdb_find_nonzero_off(ntdb, tlevel->hashtable,
|
||||
i+1, tlevel->total_buckets)) {
|
||||
if (NTDB_OFF_IS_ERR(i)) {
|
||||
return i;
|
||||
}
|
||||
|
||||
val = ntdb_read_off(ntdb, tlevel->hashtable+sizeof(ntdb_off_t)*i);
|
||||
if (NTDB_OFF_IS_ERR(val)) {
|
||||
return val;
|
||||
}
|
||||
|
||||
off = val & NTDB_OFF_MASK;
|
||||
|
||||
/* This makes the delete-all-in-traverse case work
|
||||
* (and simplifies our logic a little). */
|
||||
if (off == tinfo->prev)
|
||||
continue;
|
||||
|
||||
tlevel->entry = i;
|
||||
|
||||
if (!is_subhash(val)) {
|
||||
/* Found one. */
|
||||
tinfo->prev = off;
|
||||
return off;
|
||||
}
|
||||
|
||||
/* When we come back, we want the next one */
|
||||
tlevel->entry++;
|
||||
tinfo->num_levels++;
|
||||
tlevel++;
|
||||
tlevel->hashtable = off + sizeof(struct ntdb_used_record);
|
||||
tlevel->entry = 0;
|
||||
/* Next level is a chain? */
|
||||
if (unlikely(tinfo->num_levels == NTDB_MAX_LEVELS + 1))
|
||||
tlevel->total_buckets = (1 << NTDB_HASH_GROUP_BITS);
|
||||
else
|
||||
tlevel->total_buckets = (1 << NTDB_SUBLEVEL_HASH_BITS);
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* Nothing there? */
|
||||
if (tinfo->num_levels == 1)
|
||||
return 0;
|
||||
|
||||
/* Handle chained entries. */
|
||||
if (unlikely(tinfo->num_levels == NTDB_MAX_LEVELS + 1)) {
|
||||
tlevel->hashtable = ntdb_read_off(ntdb, tlevel->hashtable
|
||||
+ offsetof(struct ntdb_chain,
|
||||
next));
|
||||
if (NTDB_OFF_IS_ERR(tlevel->hashtable)) {
|
||||
return tlevel->hashtable;
|
||||
}
|
||||
if (tlevel->hashtable) {
|
||||
tlevel->hashtable += sizeof(struct ntdb_used_record);
|
||||
tlevel->entry = 0;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
/* Go back up and keep searching. */
|
||||
tinfo->num_levels--;
|
||||
tlevel--;
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* Return success if we find something, NTDB_ERR_NOEXIST if none. */
|
||||
enum NTDB_ERROR next_in_hash(struct ntdb_context *ntdb,
|
||||
struct traverse_info *tinfo,
|
||||
NTDB_DATA *kbuf, size_t *dlen)
|
||||
{
|
||||
const unsigned group_bits = NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS;
|
||||
ntdb_off_t hl_start, hl_range, off;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
while (tinfo->toplevel_group < (1 << group_bits)) {
|
||||
hl_start = (ntdb_off_t)tinfo->toplevel_group
|
||||
<< (64 - group_bits);
|
||||
hl_range = 1ULL << group_bits;
|
||||
ecode = ntdb_lock_hashes(ntdb, hl_start, hl_range, F_RDLCK,
|
||||
NTDB_LOCK_WAIT);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
off = iterate_hash(ntdb, tinfo);
|
||||
if (off) {
|
||||
struct ntdb_used_record rec;
|
||||
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
ecode = NTDB_OFF_TO_ERR(off);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, off, &rec, sizeof(rec));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
if (rec_magic(&rec) != NTDB_USED_MAGIC) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
|
||||
NTDB_LOG_ERROR,
|
||||
"next_in_hash:"
|
||||
" corrupt record at %llu",
|
||||
(long long)off);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kbuf->dsize = rec_key_length(&rec);
|
||||
|
||||
/* They want data as well? */
|
||||
if (dlen) {
|
||||
*dlen = rec_data_length(&rec);
|
||||
kbuf->dptr = ntdb_alloc_read(ntdb,
|
||||
off + sizeof(rec),
|
||||
kbuf->dsize
|
||||
+ *dlen);
|
||||
} else {
|
||||
kbuf->dptr = ntdb_alloc_read(ntdb,
|
||||
off + sizeof(rec),
|
||||
kbuf->dsize);
|
||||
}
|
||||
ntdb_unlock_hashes(ntdb, hl_start, hl_range, F_RDLCK);
|
||||
if (NTDB_PTR_IS_ERR(kbuf->dptr)) {
|
||||
return NTDB_PTR_ERR(kbuf->dptr);
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
ntdb_unlock_hashes(ntdb, hl_start, hl_range, F_RDLCK);
|
||||
|
||||
tinfo->toplevel_group++;
|
||||
tinfo->levels[0].hashtable
|
||||
+= (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
|
||||
tinfo->levels[0].entry = 0;
|
||||
}
|
||||
return NTDB_ERR_NOEXIST;
|
||||
|
||||
fail:
|
||||
ntdb_unlock_hashes(ntdb, hl_start, hl_range, F_RDLCK);
|
||||
return ecode;
|
||||
|
||||
}
|
||||
|
||||
enum NTDB_ERROR first_in_hash(struct ntdb_context *ntdb,
|
||||
struct traverse_info *tinfo,
|
||||
NTDB_DATA *kbuf, size_t *dlen)
|
||||
{
|
||||
tinfo->prev = 0;
|
||||
tinfo->toplevel_group = 0;
|
||||
tinfo->num_levels = 1;
|
||||
tinfo->levels[0].hashtable = offsetof(struct ntdb_header, hashtable);
|
||||
tinfo->levels[0].entry = 0;
|
||||
tinfo->levels[0].total_buckets = (1 << NTDB_HASH_GROUP_BITS);
|
||||
|
||||
return next_in_hash(ntdb, tinfo, kbuf, dlen);
|
||||
}
|
||||
|
||||
/* Even if the entry isn't in this hash bucket, you'd have to lock this
|
||||
* bucket to find it. */
|
||||
static enum NTDB_ERROR chainlock(struct ntdb_context *ntdb, const NTDB_DATA *key,
|
||||
int ltype, enum ntdb_lock_flags waitflag,
|
||||
const char *func)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
uint64_t h = ntdb_hash(ntdb, key->dptr, key->dsize);
|
||||
ntdb_off_t lockstart, locksize;
|
||||
unsigned int group, gbits;
|
||||
|
||||
gbits = NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS;
|
||||
group = bits_from(h, 64 - gbits, gbits);
|
||||
|
||||
lockstart = hlock_range(group, &locksize);
|
||||
|
||||
ecode = ntdb_lock_hashes(ntdb, lockstart, locksize, ltype, waitflag);
|
||||
ntdb_trace_1rec(ntdb, func, *key);
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* lock/unlock one hash chain. This is meant to be used to reduce
|
||||
contention - it cannot guarantee how many records will be locked */
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_chainlock(struct ntdb_context *ntdb, NTDB_DATA key)
|
||||
{
|
||||
return ntdb->last_error = chainlock(ntdb, &key, F_WRLCK, NTDB_LOCK_WAIT,
|
||||
"ntdb_chainlock");
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_chainunlock(struct ntdb_context *ntdb, NTDB_DATA key)
|
||||
{
|
||||
uint64_t h = ntdb_hash(ntdb, key.dptr, key.dsize);
|
||||
ntdb_off_t lockstart, locksize;
|
||||
unsigned int group, gbits;
|
||||
|
||||
gbits = NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS;
|
||||
group = bits_from(h, 64 - gbits, gbits);
|
||||
|
||||
lockstart = hlock_range(group, &locksize);
|
||||
|
||||
ntdb_trace_1rec(ntdb, "ntdb_chainunlock", key);
|
||||
ntdb_unlock_hashes(ntdb, lockstart, locksize, F_WRLCK);
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_chainlock_read(struct ntdb_context *ntdb, NTDB_DATA key)
|
||||
{
|
||||
return ntdb->last_error = chainlock(ntdb, &key, F_RDLCK, NTDB_LOCK_WAIT,
|
||||
"ntdb_chainlock_read");
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_chainunlock_read(struct ntdb_context *ntdb, NTDB_DATA key)
|
||||
{
|
||||
uint64_t h = ntdb_hash(ntdb, key.dptr, key.dsize);
|
||||
ntdb_off_t lockstart, locksize;
|
||||
unsigned int group, gbits;
|
||||
|
||||
gbits = NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS;
|
||||
group = bits_from(h, 64 - gbits, gbits);
|
||||
|
||||
lockstart = hlock_range(group, &locksize);
|
||||
|
||||
ntdb_trace_1rec(ntdb, "ntdb_chainunlock_read", key);
|
||||
ntdb_unlock_hashes(ntdb, lockstart, locksize, F_RDLCK);
|
||||
}
|
650
lib/ntdb/io.c
Normal file
650
lib/ntdb/io.c
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
trivial database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 1999-2005
|
||||
Copyright (C) Paul `Rusty' Russell 2000
|
||||
Copyright (C) Jeremy Allison 2000-2003
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
** NOTE! The following LGPL license applies to the ntdb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "private.h"
|
||||
#include <assert.h>
|
||||
#include <ccan/likely/likely.h>
|
||||
|
||||
void ntdb_munmap(struct ntdb_file *file)
|
||||
{
|
||||
if (file->fd == -1)
|
||||
return;
|
||||
|
||||
if (file->map_ptr) {
|
||||
munmap(file->map_ptr, file->map_size);
|
||||
file->map_ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_mmap(struct ntdb_context *ntdb)
|
||||
{
|
||||
int mmap_flags;
|
||||
|
||||
if (ntdb->flags & NTDB_INTERNAL)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
#ifndef HAVE_INCOHERENT_MMAP
|
||||
if (ntdb->flags & NTDB_NOMMAP)
|
||||
return NTDB_SUCCESS;
|
||||
#endif
|
||||
|
||||
if ((ntdb->open_flags & O_ACCMODE) == O_RDONLY)
|
||||
mmap_flags = PROT_READ;
|
||||
else
|
||||
mmap_flags = PROT_READ | PROT_WRITE;
|
||||
|
||||
/* size_t can be smaller than off_t. */
|
||||
if ((size_t)ntdb->file->map_size == ntdb->file->map_size) {
|
||||
ntdb->file->map_ptr = mmap(NULL, ntdb->file->map_size,
|
||||
mmap_flags,
|
||||
MAP_SHARED, ntdb->file->fd, 0);
|
||||
} else
|
||||
ntdb->file->map_ptr = MAP_FAILED;
|
||||
|
||||
/*
|
||||
* NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
|
||||
*/
|
||||
if (ntdb->file->map_ptr == MAP_FAILED) {
|
||||
ntdb->file->map_ptr = NULL;
|
||||
#ifdef HAVE_INCOHERENT_MMAP
|
||||
/* Incoherent mmap means everyone must mmap! */
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_mmap failed for size %lld (%s)",
|
||||
(long long)ntdb->file->map_size,
|
||||
strerror(errno));
|
||||
#else
|
||||
ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
|
||||
"ntdb_mmap failed for size %lld (%s)",
|
||||
(long long)ntdb->file->map_size, strerror(errno));
|
||||
#endif
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* check for an out of bounds access - if it is out of bounds then
|
||||
see if the database has been expanded by someone else and expand
|
||||
if necessary
|
||||
note that "len" is the minimum length needed for the db.
|
||||
|
||||
If probe is true, len being too large isn't a failure.
|
||||
*/
|
||||
static enum NTDB_ERROR ntdb_oob(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_len_t len, bool probe)
|
||||
{
|
||||
struct stat st;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* We can't hold pointers during this: we could unmap! */
|
||||
assert(!ntdb->direct_access
|
||||
|| (ntdb->flags & NTDB_NOLOCK)
|
||||
|| ntdb_has_expansion_lock(ntdb));
|
||||
|
||||
if (len + off < len) {
|
||||
if (probe)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_oob off %llu len %llu wrap\n",
|
||||
(long long)off, (long long)len);
|
||||
}
|
||||
|
||||
if (len + off <= ntdb->file->map_size)
|
||||
return NTDB_SUCCESS;
|
||||
if (ntdb->flags & NTDB_INTERNAL) {
|
||||
if (probe)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_oob len %lld beyond internal"
|
||||
" malloc size %lld",
|
||||
(long long)(off + len),
|
||||
(long long)ntdb->file->map_size);
|
||||
return NTDB_ERR_IO;
|
||||
}
|
||||
|
||||
ecode = ntdb_lock_expand(ntdb, F_RDLCK);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (fstat(ntdb->file->fd, &st) != 0) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"Failed to fstat file: %s", strerror(errno));
|
||||
ntdb_unlock_expand(ntdb, F_RDLCK);
|
||||
return NTDB_ERR_IO;
|
||||
}
|
||||
|
||||
ntdb_unlock_expand(ntdb, F_RDLCK);
|
||||
|
||||
if (st.st_size < off + len) {
|
||||
if (probe)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_oob len %llu beyond eof at %llu",
|
||||
(long long)(off + len), (long long)st.st_size);
|
||||
return NTDB_ERR_IO;
|
||||
}
|
||||
|
||||
/* Unmap, update size, remap */
|
||||
ntdb_munmap(ntdb->file);
|
||||
|
||||
ntdb->file->map_size = st.st_size;
|
||||
return ntdb_mmap(ntdb);
|
||||
}
|
||||
|
||||
/* Endian conversion: we only ever deal with 8 byte quantities */
|
||||
void *ntdb_convert(const struct ntdb_context *ntdb, void *buf, ntdb_len_t size)
|
||||
{
|
||||
assert(size % 8 == 0);
|
||||
if (unlikely((ntdb->flags & NTDB_CONVERT)) && buf) {
|
||||
uint64_t i, *p = (uint64_t *)buf;
|
||||
for (i = 0; i < size / 8; i++)
|
||||
p[i] = bswap_64(p[i]);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Return first non-zero offset in offset array, or end, or -ve error. */
|
||||
/* FIXME: Return the off? */
|
||||
uint64_t ntdb_find_nonzero_off(struct ntdb_context *ntdb,
|
||||
ntdb_off_t base, uint64_t start, uint64_t end)
|
||||
{
|
||||
uint64_t i;
|
||||
const uint64_t *val;
|
||||
|
||||
/* Zero vs non-zero is the same unconverted: minor optimization. */
|
||||
val = ntdb_access_read(ntdb, base + start * sizeof(ntdb_off_t),
|
||||
(end - start) * sizeof(ntdb_off_t), false);
|
||||
if (NTDB_PTR_IS_ERR(val)) {
|
||||
return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
|
||||
}
|
||||
|
||||
for (i = 0; i < (end - start); i++) {
|
||||
if (val[i])
|
||||
break;
|
||||
}
|
||||
ntdb_access_release(ntdb, val);
|
||||
return start + i;
|
||||
}
|
||||
|
||||
/* Return first zero offset in num offset array, or num, or -ve error. */
|
||||
uint64_t ntdb_find_zero_off(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
uint64_t num)
|
||||
{
|
||||
uint64_t i;
|
||||
const uint64_t *val;
|
||||
|
||||
/* Zero vs non-zero is the same unconverted: minor optimization. */
|
||||
val = ntdb_access_read(ntdb, off, num * sizeof(ntdb_off_t), false);
|
||||
if (NTDB_PTR_IS_ERR(val)) {
|
||||
return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (!val[i])
|
||||
break;
|
||||
}
|
||||
ntdb_access_release(ntdb, val);
|
||||
return i;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR zero_out(struct ntdb_context *ntdb, ntdb_off_t off, ntdb_len_t len)
|
||||
{
|
||||
char buf[8192] = { 0 };
|
||||
void *p = ntdb->io->direct(ntdb, off, len, true);
|
||||
enum NTDB_ERROR ecode = NTDB_SUCCESS;
|
||||
|
||||
assert(!(ntdb->flags & NTDB_RDONLY));
|
||||
if (NTDB_PTR_IS_ERR(p)) {
|
||||
return NTDB_PTR_ERR(p);
|
||||
}
|
||||
if (p) {
|
||||
memset(p, 0, len);
|
||||
return ecode;
|
||||
}
|
||||
while (len) {
|
||||
unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
|
||||
ecode = ntdb->io->twrite(ntdb, off, buf, todo);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
len -= todo;
|
||||
off += todo;
|
||||
}
|
||||
return ecode;
|
||||
}
|
||||
|
||||
ntdb_off_t ntdb_read_off(struct ntdb_context *ntdb, ntdb_off_t off)
|
||||
{
|
||||
ntdb_off_t ret;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (likely(!(ntdb->flags & NTDB_CONVERT))) {
|
||||
ntdb_off_t *p = ntdb->io->direct(ntdb, off, sizeof(*p), false);
|
||||
if (NTDB_PTR_IS_ERR(p)) {
|
||||
return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(p));
|
||||
}
|
||||
if (p)
|
||||
return *p;
|
||||
}
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, off, &ret, sizeof(ret));
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return NTDB_ERR_TO_OFF(ecode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write a lump of data at a specified offset */
|
||||
static enum NTDB_ERROR ntdb_write(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
const void *buf, ntdb_len_t len)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (ntdb->flags & NTDB_RDONLY) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
|
||||
"Write to read-only database");
|
||||
}
|
||||
|
||||
ecode = ntdb->io->oob(ntdb, off, len, false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (ntdb->file->map_ptr) {
|
||||
memcpy(off + (char *)ntdb->file->map_ptr, buf, len);
|
||||
} else {
|
||||
#ifdef HAVE_INCOHERENT_MMAP
|
||||
return NTDB_ERR_IO;
|
||||
#else
|
||||
ssize_t ret;
|
||||
ret = pwrite(ntdb->file->fd, buf, len, off);
|
||||
if (ret != len) {
|
||||
/* This shouldn't happen: we avoid sparse files. */
|
||||
if (ret >= 0)
|
||||
errno = ENOSPC;
|
||||
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_write: %zi at %zu len=%zu (%s)",
|
||||
ret, (size_t)off, (size_t)len,
|
||||
strerror(errno));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* read a lump of data at a specified offset */
|
||||
static enum NTDB_ERROR ntdb_read(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
void *buf, ntdb_len_t len)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ecode = ntdb->io->oob(ntdb, off, len, false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (ntdb->file->map_ptr) {
|
||||
memcpy(buf, off + (char *)ntdb->file->map_ptr, len);
|
||||
} else {
|
||||
#ifdef HAVE_INCOHERENT_MMAP
|
||||
return NTDB_ERR_IO;
|
||||
#else
|
||||
ssize_t r = pread(ntdb->file->fd, buf, len, off);
|
||||
if (r != len) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_read failed with %zi at %zu "
|
||||
"len=%zu (%s) map_size=%zu",
|
||||
r, (size_t)off, (size_t)len,
|
||||
strerror(errno),
|
||||
(size_t)ntdb->file->map_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_write_convert(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
const void *rec, size_t len)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (unlikely((ntdb->flags & NTDB_CONVERT))) {
|
||||
void *conv = malloc(len);
|
||||
if (!conv) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"ntdb_write: no memory converting"
|
||||
" %zu bytes", len);
|
||||
}
|
||||
memcpy(conv, rec, len);
|
||||
ecode = ntdb->io->twrite(ntdb, off,
|
||||
ntdb_convert(ntdb, conv, len), len);
|
||||
free(conv);
|
||||
} else {
|
||||
ecode = ntdb->io->twrite(ntdb, off, rec, len);
|
||||
}
|
||||
return ecode;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_read_convert(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
void *rec, size_t len)
|
||||
{
|
||||
enum NTDB_ERROR ecode = ntdb->io->tread(ntdb, off, rec, len);
|
||||
ntdb_convert(ntdb, rec, len);
|
||||
return ecode;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_write_off(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_off_t val)
|
||||
{
|
||||
if (ntdb->flags & NTDB_RDONLY) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
|
||||
"Write to read-only database");
|
||||
}
|
||||
|
||||
if (likely(!(ntdb->flags & NTDB_CONVERT))) {
|
||||
ntdb_off_t *p = ntdb->io->direct(ntdb, off, sizeof(*p), true);
|
||||
if (NTDB_PTR_IS_ERR(p)) {
|
||||
return NTDB_PTR_ERR(p);
|
||||
}
|
||||
if (p) {
|
||||
*p = val;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
}
|
||||
return ntdb_write_convert(ntdb, off, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static void *_ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset,
|
||||
ntdb_len_t len, unsigned int prefix)
|
||||
{
|
||||
unsigned char *buf;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* some systems don't like zero length malloc */
|
||||
buf = malloc(prefix + len ? prefix + len : 1);
|
||||
if (!buf) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_alloc_read malloc failed len=%zu",
|
||||
(size_t)(prefix + len));
|
||||
return NTDB_ERR_PTR(NTDB_ERR_OOM);
|
||||
} else {
|
||||
ecode = ntdb->io->tread(ntdb, offset, buf+prefix, len);
|
||||
if (unlikely(ecode != NTDB_SUCCESS)) {
|
||||
free(buf);
|
||||
return NTDB_ERR_PTR(ecode);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* read a lump of data, allocating the space for it */
|
||||
void *ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset, ntdb_len_t len)
|
||||
{
|
||||
return _ntdb_alloc_read(ntdb, offset, len, 0);
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR fill(struct ntdb_context *ntdb,
|
||||
const void *buf, size_t size,
|
||||
ntdb_off_t off, ntdb_len_t len)
|
||||
{
|
||||
while (len) {
|
||||
size_t n = len > size ? size : len;
|
||||
ssize_t ret = pwrite(ntdb->file->fd, buf, n, off);
|
||||
if (ret != n) {
|
||||
if (ret >= 0)
|
||||
errno = ENOSPC;
|
||||
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"fill failed:"
|
||||
" %zi at %zu len=%zu (%s)",
|
||||
ret, (size_t)off, (size_t)len,
|
||||
strerror(errno));
|
||||
}
|
||||
len -= n;
|
||||
off += n;
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* expand a file. we prefer to use ftruncate, as that is what posix
|
||||
says to use for mmap expansion */
|
||||
static enum NTDB_ERROR ntdb_expand_file(struct ntdb_context *ntdb,
|
||||
ntdb_len_t addition)
|
||||
{
|
||||
char buf[8192];
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (ntdb->flags & NTDB_RDONLY) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
|
||||
"Expand on read-only database");
|
||||
}
|
||||
|
||||
if (ntdb->flags & NTDB_INTERNAL) {
|
||||
char *new = realloc(ntdb->file->map_ptr,
|
||||
ntdb->file->map_size + addition);
|
||||
if (!new) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"No memory to expand database");
|
||||
}
|
||||
ntdb->file->map_ptr = new;
|
||||
ntdb->file->map_size += addition;
|
||||
return NTDB_SUCCESS;
|
||||
} else {
|
||||
/* Unmap before trying to write; old NTDB claimed OpenBSD had
|
||||
* problem with this otherwise. */
|
||||
ntdb_munmap(ntdb->file);
|
||||
|
||||
/* If this fails, we try to fill anyway. */
|
||||
if (ftruncate(ntdb->file->fd, ntdb->file->map_size + addition))
|
||||
;
|
||||
|
||||
/* now fill the file with something. This ensures that the
|
||||
file isn't sparse, which would be very bad if we ran out of
|
||||
disk. This must be done with write, not via mmap */
|
||||
memset(buf, 0x43, sizeof(buf));
|
||||
ecode = fill(ntdb, buf, sizeof(buf), ntdb->file->map_size,
|
||||
addition);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
ntdb->file->map_size += addition;
|
||||
return ntdb_mmap(ntdb);
|
||||
}
|
||||
}
|
||||
|
||||
const void *ntdb_access_read(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_len_t len, bool convert)
|
||||
{
|
||||
void *ret = NULL;
|
||||
|
||||
if (likely(!(ntdb->flags & NTDB_CONVERT))) {
|
||||
ret = ntdb->io->direct(ntdb, off, len, false);
|
||||
|
||||
if (NTDB_PTR_IS_ERR(ret)) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
struct ntdb_access_hdr *hdr;
|
||||
hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
|
||||
if (NTDB_PTR_IS_ERR(hdr)) {
|
||||
return hdr;
|
||||
}
|
||||
hdr->next = ntdb->access;
|
||||
ntdb->access = hdr;
|
||||
ret = hdr + 1;
|
||||
if (convert) {
|
||||
ntdb_convert(ntdb, (void *)ret, len);
|
||||
}
|
||||
} else
|
||||
ntdb->direct_access++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *ntdb_access_write(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_len_t len, bool convert)
|
||||
{
|
||||
void *ret = NULL;
|
||||
|
||||
if (ntdb->flags & NTDB_RDONLY) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
|
||||
"Write to read-only database");
|
||||
return NTDB_ERR_PTR(NTDB_ERR_RDONLY);
|
||||
}
|
||||
|
||||
if (likely(!(ntdb->flags & NTDB_CONVERT))) {
|
||||
ret = ntdb->io->direct(ntdb, off, len, true);
|
||||
|
||||
if (NTDB_PTR_IS_ERR(ret)) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
struct ntdb_access_hdr *hdr;
|
||||
hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
|
||||
if (NTDB_PTR_IS_ERR(hdr)) {
|
||||
return hdr;
|
||||
}
|
||||
hdr->next = ntdb->access;
|
||||
ntdb->access = hdr;
|
||||
hdr->off = off;
|
||||
hdr->len = len;
|
||||
hdr->convert = convert;
|
||||
ret = hdr + 1;
|
||||
if (convert)
|
||||
ntdb_convert(ntdb, (void *)ret, len);
|
||||
} else
|
||||
ntdb->direct_access++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ntdb_access_hdr **find_hdr(struct ntdb_context *ntdb, const void *p)
|
||||
{
|
||||
struct ntdb_access_hdr **hp;
|
||||
|
||||
for (hp = &ntdb->access; *hp; hp = &(*hp)->next) {
|
||||
if (*hp + 1 == p)
|
||||
return hp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ntdb_access_release(struct ntdb_context *ntdb, const void *p)
|
||||
{
|
||||
struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
|
||||
|
||||
if (hp) {
|
||||
hdr = *hp;
|
||||
*hp = hdr->next;
|
||||
free(hdr);
|
||||
} else
|
||||
ntdb->direct_access--;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_access_commit(struct ntdb_context *ntdb, void *p)
|
||||
{
|
||||
struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (hp) {
|
||||
hdr = *hp;
|
||||
if (hdr->convert)
|
||||
ecode = ntdb_write_convert(ntdb, hdr->off, p, hdr->len);
|
||||
else
|
||||
ecode = ntdb_write(ntdb, hdr->off, p, hdr->len);
|
||||
*hp = hdr->next;
|
||||
free(hdr);
|
||||
} else {
|
||||
ntdb->direct_access--;
|
||||
ecode = NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
return ecode;
|
||||
}
|
||||
|
||||
static void *ntdb_direct(struct ntdb_context *ntdb, ntdb_off_t off, size_t len,
|
||||
bool write_mode)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (unlikely(!ntdb->file->map_ptr))
|
||||
return NULL;
|
||||
|
||||
ecode = ntdb_oob(ntdb, off, len, false);
|
||||
if (unlikely(ecode != NTDB_SUCCESS))
|
||||
return NTDB_ERR_PTR(ecode);
|
||||
return (char *)ntdb->file->map_ptr + off;
|
||||
}
|
||||
|
||||
void ntdb_inc_seqnum(struct ntdb_context *ntdb)
|
||||
{
|
||||
ntdb_off_t seq;
|
||||
|
||||
if (likely(!(ntdb->flags & NTDB_CONVERT))) {
|
||||
int64_t *direct;
|
||||
|
||||
direct = ntdb->io->direct(ntdb,
|
||||
offsetof(struct ntdb_header, seqnum),
|
||||
sizeof(*direct), true);
|
||||
if (likely(direct)) {
|
||||
/* Don't let it go negative, even briefly */
|
||||
if (unlikely((*direct) + 1) < 0)
|
||||
*direct = 0;
|
||||
(*direct)++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
seq = ntdb_read_off(ntdb, offsetof(struct ntdb_header, seqnum));
|
||||
if (!NTDB_OFF_IS_ERR(seq)) {
|
||||
seq++;
|
||||
if (unlikely((int64_t)seq < 0))
|
||||
seq = 0;
|
||||
ntdb_write_off(ntdb, offsetof(struct ntdb_header, seqnum), seq);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ntdb_methods io_methods = {
|
||||
ntdb_read,
|
||||
ntdb_write,
|
||||
ntdb_oob,
|
||||
ntdb_expand_file,
|
||||
ntdb_direct,
|
||||
};
|
||||
|
||||
/*
|
||||
initialise the default methods table
|
||||
*/
|
||||
void ntdb_io_init(struct ntdb_context *ntdb)
|
||||
{
|
||||
ntdb->io = &io_methods;
|
||||
}
|
883
lib/ntdb/lock.c
Normal file
883
lib/ntdb/lock.c
Normal file
@ -0,0 +1,883 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
trivial database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 1999-2005
|
||||
Copyright (C) Paul `Rusty' Russell 2000
|
||||
Copyright (C) Jeremy Allison 2000-2003
|
||||
|
||||
** NOTE! The following LGPL license applies to the ntdb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "private.h"
|
||||
#include <assert.h>
|
||||
#include <ccan/build_assert/build_assert.h>
|
||||
|
||||
/* If we were threaded, we could wait for unlock, but we're not, so fail. */
|
||||
enum NTDB_ERROR owner_conflict(struct ntdb_context *ntdb, const char *call)
|
||||
{
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"%s: lock owned by another ntdb in this process.",
|
||||
call);
|
||||
}
|
||||
|
||||
/* If we fork, we no longer really own locks. */
|
||||
bool check_lock_pid(struct ntdb_context *ntdb, const char *call, bool log)
|
||||
{
|
||||
/* No locks? No problem! */
|
||||
if (ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* No fork? No problem! */
|
||||
if (ntdb->file->locker == getpid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (log) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"%s: fork() detected after lock acquisition!"
|
||||
" (%u vs %u)", call, ntdb->file->locker, getpid());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ntdb_fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag,
|
||||
void *unused)
|
||||
{
|
||||
struct flock fl;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
fl.l_type = rw;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = off;
|
||||
fl.l_len = len;
|
||||
|
||||
if (waitflag)
|
||||
ret = fcntl(fd, F_SETLKW, &fl);
|
||||
else
|
||||
ret = fcntl(fd, F_SETLK, &fl);
|
||||
} while (ret != 0 && errno == EINTR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ntdb_fcntl_unlock(int fd, int rw, off_t off, off_t len, void *unused)
|
||||
{
|
||||
struct flock fl;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
fl.l_type = F_UNLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = off;
|
||||
fl.l_len = len;
|
||||
|
||||
ret = fcntl(fd, F_SETLKW, &fl);
|
||||
} while (ret != 0 && errno == EINTR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lock(struct ntdb_context *ntdb,
|
||||
int rw, off_t off, off_t len, bool waitflag)
|
||||
{
|
||||
int ret;
|
||||
if (ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0) {
|
||||
ntdb->file->locker = getpid();
|
||||
}
|
||||
|
||||
ntdb->stats.lock_lowlevel++;
|
||||
ret = ntdb->lock_fn(ntdb->file->fd, rw, off, len, waitflag,
|
||||
ntdb->lock_data);
|
||||
if (!waitflag) {
|
||||
ntdb->stats.lock_nonblock++;
|
||||
if (ret != 0)
|
||||
ntdb->stats.lock_nonblock_fail++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int unlock(struct ntdb_context *ntdb, int rw, off_t off, off_t len)
|
||||
{
|
||||
#if 0 /* Check they matched up locks and unlocks correctly. */
|
||||
char line[80];
|
||||
FILE *locks;
|
||||
bool found = false;
|
||||
|
||||
locks = fopen("/proc/locks", "r");
|
||||
|
||||
while (fgets(line, 80, locks)) {
|
||||
char *p;
|
||||
int type, start, l;
|
||||
|
||||
/* eg. 1: FLOCK ADVISORY WRITE 2440 08:01:2180826 0 EOF */
|
||||
p = strchr(line, ':') + 1;
|
||||
if (strncmp(p, " POSIX ADVISORY ", strlen(" POSIX ADVISORY ")))
|
||||
continue;
|
||||
p += strlen(" FLOCK ADVISORY ");
|
||||
if (strncmp(p, "READ ", strlen("READ ")) == 0)
|
||||
type = F_RDLCK;
|
||||
else if (strncmp(p, "WRITE ", strlen("WRITE ")) == 0)
|
||||
type = F_WRLCK;
|
||||
else
|
||||
abort();
|
||||
p += 6;
|
||||
if (atoi(p) != getpid())
|
||||
continue;
|
||||
p = strchr(strchr(p, ' ') + 1, ' ') + 1;
|
||||
start = atoi(p);
|
||||
p = strchr(p, ' ') + 1;
|
||||
if (strncmp(p, "EOF", 3) == 0)
|
||||
l = 0;
|
||||
else
|
||||
l = atoi(p) - start + 1;
|
||||
|
||||
if (off == start) {
|
||||
if (len != l) {
|
||||
fprintf(stderr, "Len %u should be %u: %s",
|
||||
(int)len, l, line);
|
||||
abort();
|
||||
}
|
||||
if (type != rw) {
|
||||
fprintf(stderr, "Type %s wrong: %s",
|
||||
rw == F_RDLCK ? "READ" : "WRITE", line);
|
||||
abort();
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "Unlock on %u@%u not found!",
|
||||
(int)off, (int)len);
|
||||
abort();
|
||||
}
|
||||
|
||||
fclose(locks);
|
||||
#endif
|
||||
|
||||
return ntdb->unlock_fn(ntdb->file->fd, rw, off, len, ntdb->lock_data);
|
||||
}
|
||||
|
||||
/* a byte range locking function - return 0 on success
|
||||
this functions locks len bytes at the specified offset.
|
||||
|
||||
note that a len of zero means lock to end of file
|
||||
*/
|
||||
static enum NTDB_ERROR ntdb_brlock(struct ntdb_context *ntdb,
|
||||
int rw_type, ntdb_off_t offset, ntdb_off_t len,
|
||||
enum ntdb_lock_flags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ntdb->flags & NTDB_NOLOCK) {
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
if (rw_type == F_WRLCK && (ntdb->flags & NTDB_RDONLY)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
|
||||
"Write lock attempted on read-only database");
|
||||
}
|
||||
|
||||
/* A 32 bit system cannot open a 64-bit file, but it could have
|
||||
* expanded since then: check here. */
|
||||
if ((size_t)(offset + len) != offset + len) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_brlock: lock on giant offset %llu",
|
||||
(long long)(offset + len));
|
||||
}
|
||||
|
||||
ret = lock(ntdb, rw_type, offset, len, flags & NTDB_LOCK_WAIT);
|
||||
if (ret != 0) {
|
||||
/* Generic lock error. errno set by fcntl.
|
||||
* EAGAIN is an expected return from non-blocking
|
||||
* locks. */
|
||||
if (!(flags & NTDB_LOCK_PROBE)
|
||||
&& (errno != EAGAIN && errno != EINTR)) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_brlock failed (fd=%d) at"
|
||||
" offset %zu rw_type=%d flags=%d len=%zu:"
|
||||
" %s",
|
||||
ntdb->file->fd, (size_t)offset, rw_type,
|
||||
flags, (size_t)len, strerror(errno));
|
||||
}
|
||||
return NTDB_ERR_LOCK;
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR ntdb_brunlock(struct ntdb_context *ntdb,
|
||||
int rw_type, ntdb_off_t offset, size_t len)
|
||||
{
|
||||
if (ntdb->flags & NTDB_NOLOCK) {
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
if (!check_lock_pid(ntdb, "ntdb_brunlock", true))
|
||||
return NTDB_ERR_LOCK;
|
||||
|
||||
if (unlock(ntdb, rw_type, offset, len) == -1) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_brunlock failed (fd=%d) at offset %zu"
|
||||
" rw_type=%d len=%zu: %s",
|
||||
ntdb->file->fd, (size_t)offset, rw_type,
|
||||
(size_t)len, strerror(errno));
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
upgrade a read lock to a write lock. This needs to be handled in a
|
||||
special way as some OSes (such as solaris) have too conservative
|
||||
deadlock detection and claim a deadlock when progress can be
|
||||
made. For those OSes we may loop for a while.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_allrecord_upgrade(struct ntdb_context *ntdb, off_t start)
|
||||
{
|
||||
int count = 1000;
|
||||
|
||||
if (!check_lock_pid(ntdb, "ntdb_transaction_prepare_commit", true))
|
||||
return NTDB_ERR_LOCK;
|
||||
|
||||
if (ntdb->file->allrecord_lock.count != 1) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_allrecord_upgrade failed:"
|
||||
" count %u too high",
|
||||
ntdb->file->allrecord_lock.count);
|
||||
}
|
||||
|
||||
if (ntdb->file->allrecord_lock.off != 1) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_allrecord_upgrade failed:"
|
||||
" already upgraded?");
|
||||
}
|
||||
|
||||
if (ntdb->file->allrecord_lock.owner != ntdb) {
|
||||
return owner_conflict(ntdb, "ntdb_allrecord_upgrade");
|
||||
}
|
||||
|
||||
while (count--) {
|
||||
struct timeval tv;
|
||||
if (ntdb_brlock(ntdb, F_WRLCK, start, 0,
|
||||
NTDB_LOCK_WAIT|NTDB_LOCK_PROBE) == NTDB_SUCCESS) {
|
||||
ntdb->file->allrecord_lock.ltype = F_WRLCK;
|
||||
ntdb->file->allrecord_lock.off = 0;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
if (errno != EDEADLK) {
|
||||
break;
|
||||
}
|
||||
/* sleep for as short a time as we can - more portable than usleep() */
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 1;
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
if (errno != EAGAIN && errno != EINTR)
|
||||
ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_allrecord_upgrade failed");
|
||||
return NTDB_ERR_LOCK;
|
||||
}
|
||||
|
||||
static struct ntdb_lock *find_nestlock(struct ntdb_context *ntdb, ntdb_off_t offset,
|
||||
const struct ntdb_context *owner)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i=0; i<ntdb->file->num_lockrecs; i++) {
|
||||
if (ntdb->file->lockrecs[i].off == offset) {
|
||||
if (owner && ntdb->file->lockrecs[i].owner != owner)
|
||||
return NULL;
|
||||
return &ntdb->file->lockrecs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_lock_and_recover(struct ntdb_context *ntdb)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (!check_lock_pid(ntdb, "ntdb_transaction_prepare_commit", true))
|
||||
return NTDB_ERR_LOCK;
|
||||
|
||||
ecode = ntdb_allrecord_lock(ntdb, F_WRLCK, NTDB_LOCK_WAIT|NTDB_LOCK_NOCHECK,
|
||||
false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
ecode = ntdb_lock_open(ntdb, F_WRLCK, NTDB_LOCK_WAIT|NTDB_LOCK_NOCHECK);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_allrecord_unlock(ntdb, F_WRLCK);
|
||||
return ecode;
|
||||
}
|
||||
ecode = ntdb_transaction_recover(ntdb);
|
||||
ntdb_unlock_open(ntdb, F_WRLCK);
|
||||
ntdb_allrecord_unlock(ntdb, F_WRLCK);
|
||||
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* lock an offset in the database. */
|
||||
static enum NTDB_ERROR ntdb_nest_lock(struct ntdb_context *ntdb,
|
||||
ntdb_off_t offset, int ltype,
|
||||
enum ntdb_lock_flags flags)
|
||||
{
|
||||
struct ntdb_lock *new_lck;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (offset > (NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE
|
||||
+ ntdb->file->map_size / 8)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_nest_lock: invalid offset %zu ltype=%d",
|
||||
(size_t)offset, ltype);
|
||||
}
|
||||
|
||||
if (ntdb->flags & NTDB_NOLOCK)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
if (!check_lock_pid(ntdb, "ntdb_nest_lock", true)) {
|
||||
return NTDB_ERR_LOCK;
|
||||
}
|
||||
|
||||
ntdb->stats.locks++;
|
||||
|
||||
new_lck = find_nestlock(ntdb, offset, NULL);
|
||||
if (new_lck) {
|
||||
if (new_lck->owner != ntdb) {
|
||||
return owner_conflict(ntdb, "ntdb_nest_lock");
|
||||
}
|
||||
|
||||
if (new_lck->ltype == F_RDLCK && ltype == F_WRLCK) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_nest_lock:"
|
||||
" offset %zu has read lock",
|
||||
(size_t)offset);
|
||||
}
|
||||
/* Just increment the struct, posix locks don't stack. */
|
||||
new_lck->count++;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (ntdb->file->num_lockrecs
|
||||
&& offset >= NTDB_HASH_LOCK_START
|
||||
&& offset < NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_nest_lock: already have a hash lock?");
|
||||
}
|
||||
#endif
|
||||
|
||||
new_lck = (struct ntdb_lock *)realloc(
|
||||
ntdb->file->lockrecs,
|
||||
sizeof(*ntdb->file->lockrecs) * (ntdb->file->num_lockrecs+1));
|
||||
if (new_lck == NULL) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"ntdb_nest_lock:"
|
||||
" unable to allocate %zu lock struct",
|
||||
ntdb->file->num_lockrecs + 1);
|
||||
}
|
||||
ntdb->file->lockrecs = new_lck;
|
||||
|
||||
/* Since fcntl locks don't nest, we do a lock for the first one,
|
||||
and simply bump the count for future ones */
|
||||
ecode = ntdb_brlock(ntdb, ltype, offset, 1, flags);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* First time we grab a lock, perhaps someone died in commit? */
|
||||
if (!(flags & NTDB_LOCK_NOCHECK)
|
||||
&& ntdb->file->num_lockrecs == 0) {
|
||||
ntdb_bool_err berr = ntdb_needs_recovery(ntdb);
|
||||
if (berr != false) {
|
||||
ntdb_brunlock(ntdb, ltype, offset, 1);
|
||||
|
||||
if (berr < 0)
|
||||
return NTDB_OFF_TO_ERR(berr);
|
||||
ecode = ntdb_lock_and_recover(ntdb);
|
||||
if (ecode == NTDB_SUCCESS) {
|
||||
ecode = ntdb_brlock(ntdb, ltype, offset, 1,
|
||||
flags);
|
||||
}
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ntdb->file->lockrecs[ntdb->file->num_lockrecs].owner = ntdb;
|
||||
ntdb->file->lockrecs[ntdb->file->num_lockrecs].off = offset;
|
||||
ntdb->file->lockrecs[ntdb->file->num_lockrecs].count = 1;
|
||||
ntdb->file->lockrecs[ntdb->file->num_lockrecs].ltype = ltype;
|
||||
ntdb->file->num_lockrecs++;
|
||||
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR ntdb_nest_unlock(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, int ltype)
|
||||
{
|
||||
struct ntdb_lock *lck;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (ntdb->flags & NTDB_NOLOCK)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
lck = find_nestlock(ntdb, off, ntdb);
|
||||
if ((lck == NULL) || (lck->count == 0)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_nest_unlock: no lock for %zu",
|
||||
(size_t)off);
|
||||
}
|
||||
|
||||
if (lck->count > 1) {
|
||||
lck->count--;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* This lock has count==1 left, so we need to unlock it in the
|
||||
* kernel. We don't bother with decrementing the in-memory array
|
||||
* element, we're about to overwrite it with the last array element
|
||||
* anyway.
|
||||
*/
|
||||
ecode = ntdb_brunlock(ntdb, ltype, off, 1);
|
||||
|
||||
/*
|
||||
* Shrink the array by overwriting the element just unlocked with the
|
||||
* last array element.
|
||||
*/
|
||||
*lck = ntdb->file->lockrecs[--ntdb->file->num_lockrecs];
|
||||
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/*
|
||||
get the transaction lock
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_transaction_lock(struct ntdb_context *ntdb, int ltype)
|
||||
{
|
||||
return ntdb_nest_lock(ntdb, NTDB_TRANSACTION_LOCK, ltype, NTDB_LOCK_WAIT);
|
||||
}
|
||||
|
||||
/*
|
||||
release the transaction lock
|
||||
*/
|
||||
void ntdb_transaction_unlock(struct ntdb_context *ntdb, int ltype)
|
||||
{
|
||||
ntdb_nest_unlock(ntdb, NTDB_TRANSACTION_LOCK, ltype);
|
||||
}
|
||||
|
||||
/* We only need to lock individual bytes, but Linux merges consecutive locks
|
||||
* so we lock in contiguous ranges. */
|
||||
static enum NTDB_ERROR ntdb_lock_gradual(struct ntdb_context *ntdb,
|
||||
int ltype, enum ntdb_lock_flags flags,
|
||||
ntdb_off_t off, ntdb_off_t len)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
enum ntdb_lock_flags nb_flags = (flags & ~NTDB_LOCK_WAIT);
|
||||
|
||||
if (len <= 1) {
|
||||
/* 0 would mean to end-of-file... */
|
||||
assert(len != 0);
|
||||
/* Single hash. Just do blocking lock. */
|
||||
return ntdb_brlock(ntdb, ltype, off, len, flags);
|
||||
}
|
||||
|
||||
/* First we try non-blocking. */
|
||||
ecode = ntdb_brlock(ntdb, ltype, off, len, nb_flags);
|
||||
if (ecode != NTDB_ERR_LOCK) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* Try locking first half, then second. */
|
||||
ecode = ntdb_lock_gradual(ntdb, ltype, flags, off, len / 2);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
|
||||
ecode = ntdb_lock_gradual(ntdb, ltype, flags,
|
||||
off + len / 2, len - len / 2);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_brunlock(ntdb, ltype, off, len / 2);
|
||||
}
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* lock/unlock entire database. It can only be upgradable if you have some
|
||||
* other way of guaranteeing exclusivity (ie. transaction write lock). */
|
||||
enum NTDB_ERROR ntdb_allrecord_lock(struct ntdb_context *ntdb, int ltype,
|
||||
enum ntdb_lock_flags flags, bool upgradable)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
ntdb_bool_err berr;
|
||||
|
||||
if (ntdb->flags & NTDB_NOLOCK)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
if (!check_lock_pid(ntdb, "ntdb_allrecord_lock", true)) {
|
||||
return NTDB_ERR_LOCK;
|
||||
}
|
||||
|
||||
if (ntdb->file->allrecord_lock.count) {
|
||||
if (ntdb->file->allrecord_lock.owner != ntdb) {
|
||||
return owner_conflict(ntdb, "ntdb_allrecord_lock");
|
||||
}
|
||||
|
||||
if (ltype == F_RDLCK
|
||||
|| ntdb->file->allrecord_lock.ltype == F_WRLCK) {
|
||||
ntdb->file->allrecord_lock.count++;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* a global lock of a different type exists */
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_allrecord_lock: already have %s lock",
|
||||
ntdb->file->allrecord_lock.ltype == F_RDLCK
|
||||
? "read" : "write");
|
||||
}
|
||||
|
||||
if (ntdb_has_hash_locks(ntdb)) {
|
||||
/* can't combine global and chain locks */
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_allrecord_lock:"
|
||||
" already have chain lock");
|
||||
}
|
||||
|
||||
if (upgradable && ltype != F_RDLCK) {
|
||||
/* ntdb error: you can't upgrade a write lock! */
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_allrecord_lock:"
|
||||
" can't upgrade a write lock");
|
||||
}
|
||||
|
||||
ntdb->stats.locks++;
|
||||
again:
|
||||
/* Lock hashes, gradually. */
|
||||
ecode = ntdb_lock_gradual(ntdb, ltype, flags, NTDB_HASH_LOCK_START,
|
||||
NTDB_HASH_LOCK_RANGE);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
|
||||
/* Lock free tables: there to end of file. */
|
||||
ecode = ntdb_brlock(ntdb, ltype,
|
||||
NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE,
|
||||
0, flags);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_brunlock(ntdb, ltype, NTDB_HASH_LOCK_START,
|
||||
NTDB_HASH_LOCK_RANGE);
|
||||
return ecode;
|
||||
}
|
||||
|
||||
ntdb->file->allrecord_lock.owner = ntdb;
|
||||
ntdb->file->allrecord_lock.count = 1;
|
||||
/* If it's upgradable, it's actually exclusive so we can treat
|
||||
* it as a write lock. */
|
||||
ntdb->file->allrecord_lock.ltype = upgradable ? F_WRLCK : ltype;
|
||||
ntdb->file->allrecord_lock.off = upgradable;
|
||||
|
||||
/* Now check for needing recovery. */
|
||||
if (flags & NTDB_LOCK_NOCHECK)
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
berr = ntdb_needs_recovery(ntdb);
|
||||
if (likely(berr == false))
|
||||
return NTDB_SUCCESS;
|
||||
|
||||
ntdb_allrecord_unlock(ntdb, ltype);
|
||||
if (berr < 0)
|
||||
return NTDB_OFF_TO_ERR(berr);
|
||||
ecode = ntdb_lock_and_recover(ntdb);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_lock_open(struct ntdb_context *ntdb,
|
||||
int ltype, enum ntdb_lock_flags flags)
|
||||
{
|
||||
return ntdb_nest_lock(ntdb, NTDB_OPEN_LOCK, ltype, flags);
|
||||
}
|
||||
|
||||
void ntdb_unlock_open(struct ntdb_context *ntdb, int ltype)
|
||||
{
|
||||
ntdb_nest_unlock(ntdb, NTDB_OPEN_LOCK, ltype);
|
||||
}
|
||||
|
||||
bool ntdb_has_open_lock(struct ntdb_context *ntdb)
|
||||
{
|
||||
return !(ntdb->flags & NTDB_NOLOCK)
|
||||
&& find_nestlock(ntdb, NTDB_OPEN_LOCK, ntdb) != NULL;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_lock_expand(struct ntdb_context *ntdb, int ltype)
|
||||
{
|
||||
/* Lock doesn't protect data, so don't check (we recurse if we do!) */
|
||||
return ntdb_nest_lock(ntdb, NTDB_EXPANSION_LOCK, ltype,
|
||||
NTDB_LOCK_WAIT | NTDB_LOCK_NOCHECK);
|
||||
}
|
||||
|
||||
void ntdb_unlock_expand(struct ntdb_context *ntdb, int ltype)
|
||||
{
|
||||
ntdb_nest_unlock(ntdb, NTDB_EXPANSION_LOCK, ltype);
|
||||
}
|
||||
|
||||
/* unlock entire db */
|
||||
void ntdb_allrecord_unlock(struct ntdb_context *ntdb, int ltype)
|
||||
{
|
||||
if (ntdb->flags & NTDB_NOLOCK)
|
||||
return;
|
||||
|
||||
if (ntdb->file->allrecord_lock.count == 0) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_allrecord_unlock: not locked!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ntdb->file->allrecord_lock.owner != ntdb) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_allrecord_unlock: not locked by us!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Upgradable locks are marked as write locks. */
|
||||
if (ntdb->file->allrecord_lock.ltype != ltype
|
||||
&& (!ntdb->file->allrecord_lock.off || ltype != F_RDLCK)) {
|
||||
ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_allrecord_unlock: have %s lock",
|
||||
ntdb->file->allrecord_lock.ltype == F_RDLCK
|
||||
? "read" : "write");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ntdb->file->allrecord_lock.count > 1) {
|
||||
ntdb->file->allrecord_lock.count--;
|
||||
return;
|
||||
}
|
||||
|
||||
ntdb->file->allrecord_lock.count = 0;
|
||||
ntdb->file->allrecord_lock.ltype = 0;
|
||||
|
||||
ntdb_brunlock(ntdb, ltype, NTDB_HASH_LOCK_START, 0);
|
||||
}
|
||||
|
||||
bool ntdb_has_expansion_lock(struct ntdb_context *ntdb)
|
||||
{
|
||||
return find_nestlock(ntdb, NTDB_EXPANSION_LOCK, ntdb) != NULL;
|
||||
}
|
||||
|
||||
bool ntdb_has_hash_locks(struct ntdb_context *ntdb)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i=0; i<ntdb->file->num_lockrecs; i++) {
|
||||
if (ntdb->file->lockrecs[i].off >= NTDB_HASH_LOCK_START
|
||||
&& ntdb->file->lockrecs[i].off < (NTDB_HASH_LOCK_START
|
||||
+ NTDB_HASH_LOCK_RANGE))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ntdb_has_free_lock(struct ntdb_context *ntdb)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (ntdb->flags & NTDB_NOLOCK)
|
||||
return false;
|
||||
|
||||
for (i=0; i<ntdb->file->num_lockrecs; i++) {
|
||||
if (ntdb->file->lockrecs[i].off
|
||||
> NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_lock_hashes(struct ntdb_context *ntdb,
|
||||
ntdb_off_t hash_lock,
|
||||
ntdb_len_t hash_range,
|
||||
int ltype, enum ntdb_lock_flags waitflag)
|
||||
{
|
||||
/* FIXME: Do this properly, using hlock_range */
|
||||
unsigned l = NTDB_HASH_LOCK_START
|
||||
+ (hash_lock >> (64 - NTDB_HASH_LOCK_RANGE_BITS));
|
||||
|
||||
/* a allrecord lock allows us to avoid per chain locks */
|
||||
if (ntdb->file->allrecord_lock.count) {
|
||||
if (!check_lock_pid(ntdb, "ntdb_lock_hashes", true))
|
||||
return NTDB_ERR_LOCK;
|
||||
|
||||
if (ntdb->file->allrecord_lock.owner != ntdb)
|
||||
return owner_conflict(ntdb, "ntdb_lock_hashes");
|
||||
if (ltype == ntdb->file->allrecord_lock.ltype
|
||||
|| ltype == F_RDLCK) {
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_lock_hashes:"
|
||||
" already have %s allrecordlock",
|
||||
ntdb->file->allrecord_lock.ltype == F_RDLCK
|
||||
? "read" : "write");
|
||||
}
|
||||
|
||||
if (ntdb_has_free_lock(ntdb)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_lock_hashes: already have free lock");
|
||||
}
|
||||
|
||||
if (ntdb_has_expansion_lock(ntdb)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_lock_hashes:"
|
||||
" already have expansion lock");
|
||||
}
|
||||
|
||||
return ntdb_nest_lock(ntdb, l, ltype, waitflag);
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_unlock_hashes(struct ntdb_context *ntdb,
|
||||
ntdb_off_t hash_lock,
|
||||
ntdb_len_t hash_range, int ltype)
|
||||
{
|
||||
unsigned l = NTDB_HASH_LOCK_START
|
||||
+ (hash_lock >> (64 - NTDB_HASH_LOCK_RANGE_BITS));
|
||||
|
||||
if (ntdb->flags & NTDB_NOLOCK)
|
||||
return 0;
|
||||
|
||||
/* a allrecord lock allows us to avoid per chain locks */
|
||||
if (ntdb->file->allrecord_lock.count) {
|
||||
if (ntdb->file->allrecord_lock.ltype == F_RDLCK
|
||||
&& ltype == F_WRLCK) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_unlock_hashes RO allrecord!");
|
||||
}
|
||||
if (ntdb->file->allrecord_lock.owner != ntdb) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_unlock_hashes:"
|
||||
" not locked by us!");
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
return ntdb_nest_unlock(ntdb, l, ltype);
|
||||
}
|
||||
|
||||
/* Hash locks use NTDB_HASH_LOCK_START + the next 30 bits.
|
||||
* Then we begin; bucket offsets are sizeof(ntdb_len_t) apart, so we divide.
|
||||
* The result is that on 32 bit systems we don't use lock values > 2^31 on
|
||||
* files that are less than 4GB.
|
||||
*/
|
||||
static ntdb_off_t free_lock_off(ntdb_off_t b_off)
|
||||
{
|
||||
return NTDB_HASH_LOCK_START + NTDB_HASH_LOCK_RANGE
|
||||
+ b_off / sizeof(ntdb_off_t);
|
||||
}
|
||||
|
||||
enum NTDB_ERROR ntdb_lock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off,
|
||||
enum ntdb_lock_flags waitflag)
|
||||
{
|
||||
assert(b_off >= sizeof(struct ntdb_header));
|
||||
|
||||
if (ntdb->flags & NTDB_NOLOCK)
|
||||
return 0;
|
||||
|
||||
/* a allrecord lock allows us to avoid per chain locks */
|
||||
if (ntdb->file->allrecord_lock.count) {
|
||||
if (!check_lock_pid(ntdb, "ntdb_lock_free_bucket", true))
|
||||
return NTDB_ERR_LOCK;
|
||||
|
||||
if (ntdb->file->allrecord_lock.owner != ntdb) {
|
||||
return owner_conflict(ntdb, "ntdb_lock_free_bucket");
|
||||
}
|
||||
|
||||
if (ntdb->file->allrecord_lock.ltype == F_WRLCK)
|
||||
return 0;
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_lock_free_bucket with"
|
||||
" read-only allrecordlock!");
|
||||
}
|
||||
|
||||
#if 0 /* FIXME */
|
||||
if (ntdb_has_expansion_lock(ntdb)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_LOCK, NTDB_LOG_ERROR,
|
||||
"ntdb_lock_free_bucket:"
|
||||
" already have expansion lock");
|
||||
}
|
||||
#endif
|
||||
|
||||
return ntdb_nest_lock(ntdb, free_lock_off(b_off), F_WRLCK, waitflag);
|
||||
}
|
||||
|
||||
void ntdb_unlock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off)
|
||||
{
|
||||
if (ntdb->file->allrecord_lock.count)
|
||||
return;
|
||||
|
||||
ntdb_nest_unlock(ntdb, free_lock_off(b_off), F_WRLCK);
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_lockall(struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb_allrecord_lock(ntdb, F_WRLCK, NTDB_LOCK_WAIT, false);
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_unlockall(struct ntdb_context *ntdb)
|
||||
{
|
||||
ntdb_allrecord_unlock(ntdb, F_WRLCK);
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_lockall_read(struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, false);
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_unlockall_read(struct ntdb_context *ntdb)
|
||||
{
|
||||
ntdb_allrecord_unlock(ntdb, F_RDLCK);
|
||||
}
|
||||
|
||||
void ntdb_lock_cleanup(struct ntdb_context *ntdb)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* We don't want to warn: they're allowed to close ntdb after fork. */
|
||||
if (!check_lock_pid(ntdb, "ntdb_close", false))
|
||||
return;
|
||||
|
||||
while (ntdb->file->allrecord_lock.count
|
||||
&& ntdb->file->allrecord_lock.owner == ntdb) {
|
||||
ntdb_allrecord_unlock(ntdb, ntdb->file->allrecord_lock.ltype);
|
||||
}
|
||||
|
||||
for (i=0; i<ntdb->file->num_lockrecs; i++) {
|
||||
if (ntdb->file->lockrecs[i].owner == ntdb) {
|
||||
ntdb_nest_unlock(ntdb,
|
||||
ntdb->file->lockrecs[i].off,
|
||||
ntdb->file->lockrecs[i].ltype);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
605
lib/ntdb/ntdb.c
Normal file
605
lib/ntdb/ntdb.c
Normal file
@ -0,0 +1,605 @@
|
||||
/*
|
||||
Trivial Database 2: fetch, store and misc routines.
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "private.h"
|
||||
#ifndef HAVE_LIBREPLACE
|
||||
#include <ccan/asprintf/asprintf.h>
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
static enum NTDB_ERROR update_rec_hdr(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off,
|
||||
ntdb_len_t keylen,
|
||||
ntdb_len_t datalen,
|
||||
struct ntdb_used_record *rec,
|
||||
uint64_t h)
|
||||
{
|
||||
uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ecode = set_header(ntdb, rec, NTDB_USED_MAGIC, keylen, datalen,
|
||||
keylen + dataroom, h);
|
||||
if (ecode == NTDB_SUCCESS) {
|
||||
ecode = ntdb_write_convert(ntdb, off, rec, sizeof(*rec));
|
||||
}
|
||||
return ecode;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR replace_data(struct ntdb_context *ntdb,
|
||||
struct hash_info *h,
|
||||
NTDB_DATA key, NTDB_DATA dbuf,
|
||||
ntdb_off_t old_off, ntdb_len_t old_room,
|
||||
bool growing)
|
||||
{
|
||||
ntdb_off_t new_off;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* Allocate a new record. */
|
||||
new_off = alloc(ntdb, key.dsize, dbuf.dsize, h->h, NTDB_USED_MAGIC,
|
||||
growing);
|
||||
if (NTDB_OFF_IS_ERR(new_off)) {
|
||||
return NTDB_OFF_TO_ERR(new_off);
|
||||
}
|
||||
|
||||
/* We didn't like the existing one: remove it. */
|
||||
if (old_off) {
|
||||
ntdb->stats.frees++;
|
||||
ecode = add_free_record(ntdb, old_off,
|
||||
sizeof(struct ntdb_used_record)
|
||||
+ key.dsize + old_room,
|
||||
NTDB_LOCK_WAIT, true);
|
||||
if (ecode == NTDB_SUCCESS)
|
||||
ecode = replace_in_hash(ntdb, h, new_off);
|
||||
} else {
|
||||
ecode = add_to_hash(ntdb, h, new_off);
|
||||
}
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
new_off += sizeof(struct ntdb_used_record);
|
||||
ecode = ntdb->io->twrite(ntdb, new_off, key.dptr, key.dsize);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
new_off += key.dsize;
|
||||
ecode = ntdb->io->twrite(ntdb, new_off, dbuf.dptr, dbuf.dsize);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
if (ntdb->flags & NTDB_SEQNUM)
|
||||
ntdb_inc_seqnum(ntdb);
|
||||
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR update_data(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off,
|
||||
NTDB_DATA dbuf,
|
||||
ntdb_len_t extra)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ecode = ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize);
|
||||
if (ecode == NTDB_SUCCESS && extra) {
|
||||
/* Put a zero in; future versions may append other data. */
|
||||
ecode = ntdb->io->twrite(ntdb, off + dbuf.dsize, "", 1);
|
||||
}
|
||||
if (ntdb->flags & NTDB_SEQNUM)
|
||||
ntdb_inc_seqnum(ntdb);
|
||||
|
||||
return ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_store(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key, NTDB_DATA dbuf, int flag)
|
||||
{
|
||||
struct hash_info h;
|
||||
ntdb_off_t off;
|
||||
ntdb_len_t old_room = 0;
|
||||
struct ntdb_used_record rec;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
off = find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return ntdb->last_error = NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
|
||||
/* Now we have lock on this hash bucket. */
|
||||
if (flag == NTDB_INSERT) {
|
||||
if (off) {
|
||||
ecode = NTDB_ERR_EXISTS;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (off) {
|
||||
old_room = rec_data_length(&rec)
|
||||
+ rec_extra_padding(&rec);
|
||||
if (old_room >= dbuf.dsize) {
|
||||
/* Can modify in-place. Easy! */
|
||||
ecode = update_rec_hdr(ntdb, off,
|
||||
key.dsize, dbuf.dsize,
|
||||
&rec, h.h);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
ecode = update_data(ntdb,
|
||||
off + sizeof(rec)
|
||||
+ key.dsize, dbuf,
|
||||
old_room - dbuf.dsize);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start,
|
||||
h.hlock_range, F_WRLCK);
|
||||
return ntdb->last_error = NTDB_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
if (flag == NTDB_MODIFY) {
|
||||
/* if the record doesn't exist and we
|
||||
are in NTDB_MODIFY mode then we should fail
|
||||
the store */
|
||||
ecode = NTDB_ERR_NOEXIST;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we didn't use the old record, this implies we're growing. */
|
||||
ecode = replace_data(ntdb, &h, key, dbuf, off, old_room, off);
|
||||
out:
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_WRLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_append(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key, NTDB_DATA dbuf)
|
||||
{
|
||||
struct hash_info h;
|
||||
ntdb_off_t off;
|
||||
struct ntdb_used_record rec;
|
||||
ntdb_len_t old_room = 0, old_dlen;
|
||||
unsigned char *newdata;
|
||||
NTDB_DATA new_dbuf;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
off = find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return ntdb->last_error = NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
|
||||
if (off) {
|
||||
old_dlen = rec_data_length(&rec);
|
||||
old_room = old_dlen + rec_extra_padding(&rec);
|
||||
|
||||
/* Fast path: can append in place. */
|
||||
if (rec_extra_padding(&rec) >= dbuf.dsize) {
|
||||
ecode = update_rec_hdr(ntdb, off, key.dsize,
|
||||
old_dlen + dbuf.dsize, &rec,
|
||||
h.h);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
off += sizeof(rec) + key.dsize + old_dlen;
|
||||
ecode = update_data(ntdb, off, dbuf,
|
||||
rec_extra_padding(&rec));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Slow path. */
|
||||
newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
|
||||
if (!newdata) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"ntdb_append:"
|
||||
" failed to allocate %zu bytes",
|
||||
(size_t)(key.dsize + old_dlen
|
||||
+ dbuf.dsize));
|
||||
goto out;
|
||||
}
|
||||
ecode = ntdb->io->tread(ntdb, off + sizeof(rec) + key.dsize,
|
||||
newdata, old_dlen);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto out_free_newdata;
|
||||
}
|
||||
memcpy(newdata + old_dlen, dbuf.dptr, dbuf.dsize);
|
||||
new_dbuf.dptr = newdata;
|
||||
new_dbuf.dsize = old_dlen + dbuf.dsize;
|
||||
} else {
|
||||
newdata = NULL;
|
||||
new_dbuf = dbuf;
|
||||
}
|
||||
|
||||
/* If they're using ntdb_append(), it implies they're growing record. */
|
||||
ecode = replace_data(ntdb, &h, key, new_dbuf, off, old_room, true);
|
||||
|
||||
out_free_newdata:
|
||||
free(newdata);
|
||||
out:
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_WRLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_fetch(struct ntdb_context *ntdb, NTDB_DATA key,
|
||||
NTDB_DATA *data)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
struct ntdb_used_record rec;
|
||||
struct hash_info h;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
off = find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return ntdb->last_error = NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
|
||||
if (!off) {
|
||||
ecode = NTDB_ERR_NOEXIST;
|
||||
} else {
|
||||
data->dsize = rec_data_length(&rec);
|
||||
data->dptr = ntdb_alloc_read(ntdb, off + sizeof(rec) + key.dsize,
|
||||
data->dsize);
|
||||
if (NTDB_PTR_IS_ERR(data->dptr)) {
|
||||
ecode = NTDB_PTR_ERR(data->dptr);
|
||||
} else
|
||||
ecode = NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ bool ntdb_exists(struct ntdb_context *ntdb, NTDB_DATA key)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
struct ntdb_used_record rec;
|
||||
struct hash_info h;
|
||||
|
||||
off = find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
ntdb->last_error = NTDB_OFF_TO_ERR(off);
|
||||
return false;
|
||||
}
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
|
||||
|
||||
ntdb->last_error = NTDB_SUCCESS;
|
||||
return off ? true : false;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_delete(struct ntdb_context *ntdb, NTDB_DATA key)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
struct ntdb_used_record rec;
|
||||
struct hash_info h;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
off = find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return ntdb->last_error = NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
|
||||
if (!off) {
|
||||
ecode = NTDB_ERR_NOEXIST;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ecode = delete_from_hash(ntdb, &h);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Free the deleted entry. */
|
||||
ntdb->stats.frees++;
|
||||
ecode = add_free_record(ntdb, off,
|
||||
sizeof(struct ntdb_used_record)
|
||||
+ rec_key_length(&rec)
|
||||
+ rec_data_length(&rec)
|
||||
+ rec_extra_padding(&rec),
|
||||
NTDB_LOCK_WAIT, true);
|
||||
|
||||
if (ntdb->flags & NTDB_SEQNUM)
|
||||
ntdb_inc_seqnum(ntdb);
|
||||
|
||||
unlock:
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_WRLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ unsigned int ntdb_get_flags(struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb->flags;
|
||||
}
|
||||
|
||||
static bool inside_transaction(const struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb->transaction != NULL;
|
||||
}
|
||||
|
||||
static bool readonly_changable(struct ntdb_context *ntdb, const char *caller)
|
||||
{
|
||||
if (inside_transaction(ntdb)) {
|
||||
ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"%s: can't change"
|
||||
" NTDB_RDONLY inside transaction",
|
||||
caller);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_add_flag(struct ntdb_context *ntdb, unsigned flag)
|
||||
{
|
||||
if (ntdb->flags & NTDB_INTERNAL) {
|
||||
ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_add_flag: internal db");
|
||||
return;
|
||||
}
|
||||
switch (flag) {
|
||||
case NTDB_NOLOCK:
|
||||
ntdb->flags |= NTDB_NOLOCK;
|
||||
break;
|
||||
case NTDB_NOMMAP:
|
||||
ntdb->flags |= NTDB_NOMMAP;
|
||||
#ifndef HAVE_INCOHERENT_MMAP
|
||||
ntdb_munmap(ntdb->file);
|
||||
#endif
|
||||
break;
|
||||
case NTDB_NOSYNC:
|
||||
ntdb->flags |= NTDB_NOSYNC;
|
||||
break;
|
||||
case NTDB_SEQNUM:
|
||||
ntdb->flags |= NTDB_SEQNUM;
|
||||
break;
|
||||
case NTDB_ALLOW_NESTING:
|
||||
ntdb->flags |= NTDB_ALLOW_NESTING;
|
||||
break;
|
||||
case NTDB_RDONLY:
|
||||
if (readonly_changable(ntdb, "ntdb_add_flag"))
|
||||
ntdb->flags |= NTDB_RDONLY;
|
||||
break;
|
||||
default:
|
||||
ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_add_flag: Unknown flag %u",
|
||||
flag);
|
||||
}
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_remove_flag(struct ntdb_context *ntdb, unsigned flag)
|
||||
{
|
||||
if (ntdb->flags & NTDB_INTERNAL) {
|
||||
ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_remove_flag: internal db");
|
||||
return;
|
||||
}
|
||||
switch (flag) {
|
||||
case NTDB_NOLOCK:
|
||||
ntdb->flags &= ~NTDB_NOLOCK;
|
||||
break;
|
||||
case NTDB_NOMMAP:
|
||||
ntdb->flags &= ~NTDB_NOMMAP;
|
||||
#ifndef HAVE_INCOHERENT_MMAP
|
||||
/* If mmap incoherent, we were mmaping anyway. */
|
||||
ntdb_mmap(ntdb);
|
||||
#endif
|
||||
break;
|
||||
case NTDB_NOSYNC:
|
||||
ntdb->flags &= ~NTDB_NOSYNC;
|
||||
break;
|
||||
case NTDB_SEQNUM:
|
||||
ntdb->flags &= ~NTDB_SEQNUM;
|
||||
break;
|
||||
case NTDB_ALLOW_NESTING:
|
||||
ntdb->flags &= ~NTDB_ALLOW_NESTING;
|
||||
break;
|
||||
case NTDB_RDONLY:
|
||||
if ((ntdb->open_flags & O_ACCMODE) == O_RDONLY) {
|
||||
ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_remove_flag: can't"
|
||||
" remove NTDB_RDONLY on ntdb"
|
||||
" opened with O_RDONLY");
|
||||
break;
|
||||
}
|
||||
if (readonly_changable(ntdb, "ntdb_remove_flag"))
|
||||
ntdb->flags &= ~NTDB_RDONLY;
|
||||
break;
|
||||
default:
|
||||
ntdb->last_error = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_remove_flag: Unknown flag %u",
|
||||
flag);
|
||||
}
|
||||
}
|
||||
|
||||
_PUBLIC_ const char *ntdb_errorstr(enum NTDB_ERROR ecode)
|
||||
{
|
||||
/* Gcc warns if you miss a case in the switch, so use that. */
|
||||
switch (NTDB_ERR_TO_OFF(ecode)) {
|
||||
case NTDB_ERR_TO_OFF(NTDB_SUCCESS): return "Success";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_CORRUPT): return "Corrupt database";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_IO): return "IO Error";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_LOCK): return "Locking error";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_OOM): return "Out of memory";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_EXISTS): return "Record exists";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_EINVAL): return "Invalid parameter";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_NOEXIST): return "Record does not exist";
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_RDONLY): return "write not permitted";
|
||||
}
|
||||
return "Invalid error code";
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_error(struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb->last_error;
|
||||
}
|
||||
|
||||
enum NTDB_ERROR COLD ntdb_logerr(struct ntdb_context *ntdb,
|
||||
enum NTDB_ERROR ecode,
|
||||
enum ntdb_log_level level,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char *message;
|
||||
va_list ap;
|
||||
size_t len;
|
||||
/* ntdb_open paths care about errno, so save it. */
|
||||
int saved_errno = errno;
|
||||
|
||||
if (!ntdb->log_fn)
|
||||
return ecode;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vasprintf(&message, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len < 0) {
|
||||
ntdb->log_fn(ntdb, NTDB_LOG_ERROR, NTDB_ERR_OOM,
|
||||
"out of memory formatting message:", ntdb->log_data);
|
||||
ntdb->log_fn(ntdb, level, ecode, fmt, ntdb->log_data);
|
||||
} else {
|
||||
ntdb->log_fn(ntdb, level, ecode, message, ntdb->log_data);
|
||||
free(message);
|
||||
}
|
||||
errno = saved_errno;
|
||||
return ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_parse_record_(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key,
|
||||
enum NTDB_ERROR (*parse)(NTDB_DATA k,
|
||||
NTDB_DATA d,
|
||||
void *data),
|
||||
void *data)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
struct ntdb_used_record rec;
|
||||
struct hash_info h;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
off = find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL);
|
||||
if (NTDB_OFF_IS_ERR(off)) {
|
||||
return ntdb->last_error = NTDB_OFF_TO_ERR(off);
|
||||
}
|
||||
|
||||
if (!off) {
|
||||
ecode = NTDB_ERR_NOEXIST;
|
||||
} else {
|
||||
const void *dptr;
|
||||
dptr = ntdb_access_read(ntdb, off + sizeof(rec) + key.dsize,
|
||||
rec_data_length(&rec), false);
|
||||
if (NTDB_PTR_IS_ERR(dptr)) {
|
||||
ecode = NTDB_PTR_ERR(dptr);
|
||||
} else {
|
||||
NTDB_DATA d = ntdb_mkdata(dptr, rec_data_length(&rec));
|
||||
|
||||
ecode = parse(key, d, data);
|
||||
ntdb_access_release(ntdb, dptr);
|
||||
}
|
||||
}
|
||||
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ const char *ntdb_name(const struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb->name;
|
||||
}
|
||||
|
||||
_PUBLIC_ int64_t ntdb_get_seqnum(struct ntdb_context *ntdb)
|
||||
{
|
||||
ntdb_off_t off;
|
||||
|
||||
off = ntdb_read_off(ntdb, offsetof(struct ntdb_header, seqnum));
|
||||
if (NTDB_OFF_IS_ERR(off))
|
||||
ntdb->last_error = NTDB_OFF_TO_ERR(off);
|
||||
else
|
||||
ntdb->last_error = NTDB_SUCCESS;
|
||||
return off;
|
||||
}
|
||||
|
||||
|
||||
_PUBLIC_ int ntdb_fd(const struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb->file->fd;
|
||||
}
|
||||
|
||||
struct traverse_state {
|
||||
enum NTDB_ERROR error;
|
||||
struct ntdb_context *dest_db;
|
||||
};
|
||||
|
||||
/*
|
||||
traverse function for repacking
|
||||
*/
|
||||
static int repack_traverse(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA data,
|
||||
struct traverse_state *state)
|
||||
{
|
||||
state->error = ntdb_store(state->dest_db, key, data, NTDB_INSERT);
|
||||
if (state->error != NTDB_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_repack(struct ntdb_context *ntdb)
|
||||
{
|
||||
struct ntdb_context *tmp_db;
|
||||
struct traverse_state state;
|
||||
|
||||
state.error = ntdb_transaction_start(ntdb);
|
||||
if (state.error != NTDB_SUCCESS) {
|
||||
return state.error;
|
||||
}
|
||||
|
||||
tmp_db = ntdb_open("tmpdb", NTDB_INTERNAL, O_RDWR|O_CREAT, 0, NULL);
|
||||
if (tmp_db == NULL) {
|
||||
state.error = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
__location__
|
||||
" Failed to create tmp_db");
|
||||
ntdb_transaction_cancel(ntdb);
|
||||
return ntdb->last_error = state.error;
|
||||
}
|
||||
|
||||
state.dest_db = tmp_db;
|
||||
if (ntdb_traverse(ntdb, repack_traverse, &state) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
state.error = ntdb_wipe_all(ntdb);
|
||||
if (state.error != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
state.dest_db = ntdb;
|
||||
if (ntdb_traverse(tmp_db, repack_traverse, &state) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ntdb_close(tmp_db);
|
||||
return ntdb_transaction_commit(ntdb);
|
||||
|
||||
fail:
|
||||
ntdb_transaction_cancel(ntdb);
|
||||
ntdb_close(tmp_db);
|
||||
return state.error;
|
||||
}
|
901
lib/ntdb/ntdb.h
Normal file
901
lib/ntdb/ntdb.h
Normal file
@ -0,0 +1,901 @@
|
||||
#ifndef CCAN_NTDB_H
|
||||
#define CCAN_NTDB_H
|
||||
|
||||
/*
|
||||
NTDB: trivial database library version 2
|
||||
|
||||
Copyright (C) Andrew Tridgell 1999-2004
|
||||
Copyright (C) Rusty Russell 2010-2012
|
||||
|
||||
** NOTE! The following LGPL license applies to the ntdb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBREPLACE
|
||||
#include <replace.h>
|
||||
#else
|
||||
#if HAVE_FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
/* For mode_t */
|
||||
#include <sys/types.h>
|
||||
/* For O_* flags. */
|
||||
#include <sys/stat.h>
|
||||
/* For sig_atomic_t. */
|
||||
#include <signal.h>
|
||||
/* For uint64_t */
|
||||
#include <stdint.h>
|
||||
/* For bool */
|
||||
#include <stdbool.h>
|
||||
/* For memcmp */
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_CCAN
|
||||
#include <ccan/compiler/compiler.h>
|
||||
#include <ccan/typesafe_cb/typesafe_cb.h>
|
||||
#include <ccan/cast/cast.h>
|
||||
#else
|
||||
#ifndef typesafe_cb_preargs
|
||||
/* Failing to have CCAN just mean less typesafe protection, etc. */
|
||||
#define typesafe_cb_preargs(rtype, atype, fn, arg, ...) \
|
||||
((rtype (*)(__VA_ARGS__, atype))(fn))
|
||||
#endif
|
||||
#ifndef cast_const
|
||||
#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
|
||||
#define cast_const(type, expr) ((type)((intptr_t)(expr)))
|
||||
#else
|
||||
#define cast_const(type, expr) ((type *)(expr))
|
||||
#endif
|
||||
#endif
|
||||
#endif /* !HAVE_CCAN */
|
||||
|
||||
union ntdb_attribute;
|
||||
struct ntdb_context;
|
||||
|
||||
/**
|
||||
* struct TDB_DATA - (n)tdb data blob
|
||||
*
|
||||
* To ease compatibility, we use 'struct TDB_DATA' from tdb.h, so if
|
||||
* you want to include both tdb.h and ntdb.h, you need to #include
|
||||
* tdb.h first.
|
||||
*/
|
||||
#ifndef __TDB_H__
|
||||
struct TDB_DATA {
|
||||
unsigned char *dptr;
|
||||
size_t dsize;
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct TDB_DATA NTDB_DATA;
|
||||
|
||||
/**
|
||||
* ntdb_open - open a database file
|
||||
* @name: the file name (can be NULL if flags contains NTDB_INTERNAL)
|
||||
* @ntdb_flags: options for this database
|
||||
* @open_flags: flags argument for ntdb's open() call.
|
||||
* @mode: mode argument for ntdb's open() call.
|
||||
* @attributes: linked list of extra attributes for this ntdb.
|
||||
*
|
||||
* This call opens (and potentially creates) a database file.
|
||||
* Multiple processes can have the NTDB file open at once.
|
||||
*
|
||||
* On failure it will return NULL, and set errno: it may also call
|
||||
* any log attribute found in @attributes.
|
||||
*
|
||||
* See also:
|
||||
* union ntdb_attribute
|
||||
*/
|
||||
struct ntdb_context *ntdb_open(const char *name, int ntdb_flags,
|
||||
int open_flags, mode_t mode,
|
||||
union ntdb_attribute *attributes);
|
||||
|
||||
|
||||
/* flags for ntdb_open() */
|
||||
#define NTDB_DEFAULT 0 /* just a readability place holder */
|
||||
#define NTDB_INTERNAL 2 /* don't store on disk */
|
||||
#define NTDB_NOLOCK 4 /* don't do any locking */
|
||||
#define NTDB_NOMMAP 8 /* don't use mmap */
|
||||
#define NTDB_CONVERT 16 /* convert endian */
|
||||
#define NTDB_NOSYNC 64 /* don't use synchronous transactions */
|
||||
#define NTDB_SEQNUM 128 /* maintain a sequence number */
|
||||
#define NTDB_ALLOW_NESTING 256 /* fake nested transactions */
|
||||
#define NTDB_RDONLY 512 /* implied by O_RDONLY */
|
||||
#define NTDB_CANT_CHECK 2048 /* has a feature which we don't understand */
|
||||
|
||||
/**
|
||||
* ntdb_close - close and free a ntdb.
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This always succeeds, in that @ntdb is unusable after this call. But if
|
||||
* some unexpected error occurred while closing, it will return non-zero
|
||||
* (the only clue as to cause will be via the log attribute).
|
||||
*/
|
||||
int ntdb_close(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* enum NTDB_ERROR - error returns for NTDB
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_errorstr()
|
||||
*/
|
||||
enum NTDB_ERROR {
|
||||
NTDB_SUCCESS = 0, /* No error. */
|
||||
NTDB_ERR_CORRUPT = -1, /* We read the db, and it was bogus. */
|
||||
NTDB_ERR_IO = -2, /* We couldn't read/write the db. */
|
||||
NTDB_ERR_LOCK = -3, /* Locking failed. */
|
||||
NTDB_ERR_OOM = -4, /* Out of Memory. */
|
||||
NTDB_ERR_EXISTS = -5, /* The key already exists. */
|
||||
NTDB_ERR_NOEXIST = -6, /* The key does not exist. */
|
||||
NTDB_ERR_EINVAL = -7, /* You're using it wrong. */
|
||||
NTDB_ERR_RDONLY = -8, /* The database is read-only. */
|
||||
NTDB_ERR_LAST = NTDB_ERR_RDONLY
|
||||
};
|
||||
|
||||
/**
|
||||
* ntdb_store - store a key/value pair in a ntdb.
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key
|
||||
* @dbuf: the data to associate with the key.
|
||||
* @flag: NTDB_REPLACE, NTDB_INSERT or NTDB_MODIFY.
|
||||
*
|
||||
* This inserts (or overwrites) a key/value pair in the NTDB. If flag
|
||||
* is NTDB_REPLACE, it doesn't matter whether the key exists or not;
|
||||
* NTDB_INSERT means it must not exist (returns NTDB_ERR_EXISTS otherwise),
|
||||
* and NTDB_MODIFY means it must exist (returns NTDB_ERR_NOEXIST otherwise).
|
||||
*
|
||||
* On success, this returns NTDB_SUCCESS.
|
||||
*
|
||||
* See also:
|
||||
* ntdb_fetch, ntdb_transaction_start, ntdb_append, ntdb_delete.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_store(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key,
|
||||
NTDB_DATA dbuf,
|
||||
int flag);
|
||||
|
||||
/* flags to ntdb_store() */
|
||||
#define NTDB_REPLACE 1 /* A readability place holder */
|
||||
#define NTDB_INSERT 2 /* Don't overwrite an existing entry */
|
||||
#define NTDB_MODIFY 3 /* Don't create an existing entry */
|
||||
|
||||
/**
|
||||
* ntdb_fetch - fetch a value from a ntdb.
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key
|
||||
* @data: pointer to data.
|
||||
*
|
||||
* This looks up a key in the database and sets it in @data.
|
||||
*
|
||||
* If it returns NTDB_SUCCESS, the key was found: it is your
|
||||
* responsibility to call free() on @data->dptr.
|
||||
*
|
||||
* Otherwise, it returns an error (usually, NTDB_ERR_NOEXIST) and @data is
|
||||
* undefined.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_fetch(struct ntdb_context *ntdb, NTDB_DATA key,
|
||||
NTDB_DATA *data);
|
||||
|
||||
/**
|
||||
* ntdb_errorstr - map the ntdb error onto a constant readable string
|
||||
* @ecode: the enum NTDB_ERROR to map.
|
||||
*
|
||||
* This is useful for displaying errors to users.
|
||||
*/
|
||||
const char *ntdb_errorstr(enum NTDB_ERROR ecode);
|
||||
|
||||
/**
|
||||
* ntdb_append - append a value to a key/value pair in a ntdb.
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key
|
||||
* @dbuf: the data to append.
|
||||
*
|
||||
* This is equivalent to fetching a record, reallocating .dptr to add the
|
||||
* data, and writing it back, only it's much more efficient. If the key
|
||||
* doesn't exist, it's equivalent to ntdb_store (with an additional hint that
|
||||
* you expect to expand the record in future).
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_fetch(), ntdb_store()
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_append(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key, NTDB_DATA dbuf);
|
||||
|
||||
/**
|
||||
* ntdb_delete - delete a key from a ntdb.
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key to delete.
|
||||
*
|
||||
* Returns NTDB_SUCCESS on success, or an error (usually NTDB_ERR_NOEXIST).
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_fetch(), ntdb_store()
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_delete(struct ntdb_context *ntdb, NTDB_DATA key);
|
||||
|
||||
/**
|
||||
* ntdb_exists - does a key exist in the database?
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key to search for.
|
||||
*
|
||||
* Returns true if it exists, or false if it doesn't or any other error.
|
||||
*/
|
||||
bool ntdb_exists(struct ntdb_context *ntdb, NTDB_DATA key);
|
||||
|
||||
/**
|
||||
* ntdb_deq - are NTDB_DATA equal?
|
||||
* @a: one NTDB_DATA
|
||||
* @b: another NTDB_DATA
|
||||
*/
|
||||
static inline bool ntdb_deq(NTDB_DATA a, NTDB_DATA b)
|
||||
{
|
||||
return a.dsize == b.dsize && memcmp(a.dptr, b.dptr, a.dsize) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntdb_mkdata - make a NTDB_DATA from const data
|
||||
* @p: the constant pointer
|
||||
* @len: the length
|
||||
*
|
||||
* As the dptr member of NTDB_DATA is not constant, you need to
|
||||
* cast it. This function keeps thost casts in one place, as well as
|
||||
* suppressing the warning some compilers give when casting away a
|
||||
* qualifier (eg. gcc with -Wcast-qual)
|
||||
*/
|
||||
static inline NTDB_DATA ntdb_mkdata(const void *p, size_t len)
|
||||
{
|
||||
NTDB_DATA d;
|
||||
d.dptr = cast_const(void *, p);
|
||||
d.dsize = len;
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntdb_transaction_start - start a transaction
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This begins a series of atomic operations. Other processes will be able
|
||||
* to read the ntdb, but not alter it (they will block), nor will they see
|
||||
* any changes until ntdb_transaction_commit() is called.
|
||||
*
|
||||
* Note that if the NTDB_ALLOW_NESTING flag is set, a ntdb_transaction_start()
|
||||
* within a transaction will succeed, but it's not a real transaction:
|
||||
* (1) An inner transaction which is committed is not actually committed until
|
||||
* the outer transaction is; if the outer transaction is cancelled, the
|
||||
* inner ones are discarded.
|
||||
* (2) ntdb_transaction_cancel() marks the outer transaction as having an error,
|
||||
* so the final ntdb_transaction_commit() will fail.
|
||||
* (3) the outer transaction will see the results of the inner transaction.
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_transaction_cancel, ntdb_transaction_commit.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_transaction_start(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_transaction_cancel - abandon a transaction
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This aborts a transaction, discarding any changes which were made.
|
||||
* ntdb_close() does this implicitly.
|
||||
*/
|
||||
void ntdb_transaction_cancel(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_transaction_commit - commit a transaction
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This completes a transaction, writing any changes which were made.
|
||||
*
|
||||
* fsync() is used to commit the transaction (unless NTDB_NOSYNC is set),
|
||||
* making it robust against machine crashes, but very slow compared to
|
||||
* other NTDB operations.
|
||||
*
|
||||
* A failure can only be caused by unexpected errors (eg. I/O or
|
||||
* memory); this is no point looping on transaction failure.
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_transaction_prepare_commit()
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_transaction_commit(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_transaction_prepare_commit - prepare to commit a transaction
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This ensures we have the resources to commit a transaction (using
|
||||
* ntdb_transaction_commit): if this succeeds then a transaction will only
|
||||
* fail if the write() or fsync() calls fail.
|
||||
*
|
||||
* If this fails you must still call ntdb_transaction_cancel() to cancel
|
||||
* the transaction.
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_transaction_commit()
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_transaction_prepare_commit(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_traverse - traverse a NTDB
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @fn: the function to call for every key/value pair (or NULL)
|
||||
* @p: the pointer to hand to @f
|
||||
*
|
||||
* This walks the NTDB until all they keys have been traversed, or @fn
|
||||
* returns non-zero. If the traverse function or other processes are
|
||||
* changing data or adding or deleting keys, the traverse may be
|
||||
* unreliable: keys may be skipped or (rarely) visited twice.
|
||||
*
|
||||
* There is one specific exception: the special case of deleting the
|
||||
* current key does not undermine the reliability of the traversal.
|
||||
*
|
||||
* On success, returns the number of keys iterated. On error returns
|
||||
* a negative enum NTDB_ERROR value.
|
||||
*/
|
||||
#define ntdb_traverse(ntdb, fn, p) \
|
||||
ntdb_traverse_(ntdb, typesafe_cb_preargs(int, void *, (fn), (p), \
|
||||
struct ntdb_context *, \
|
||||
NTDB_DATA, NTDB_DATA), (p))
|
||||
|
||||
int64_t ntdb_traverse_(struct ntdb_context *ntdb,
|
||||
int (*fn)(struct ntdb_context *,
|
||||
NTDB_DATA, NTDB_DATA, void *), void *p);
|
||||
|
||||
/**
|
||||
* ntdb_parse_record - operate directly on data in the database.
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key whose record we should hand to @parse
|
||||
* @parse: the function to call for the data
|
||||
* @data: the private pointer to hand to @parse (types must match).
|
||||
*
|
||||
* This avoids a copy for many cases, by handing you a pointer into
|
||||
* the memory-mapped database. It also locks the record to prevent
|
||||
* other accesses at the same time.
|
||||
*
|
||||
* Do not alter the data handed to parse()!
|
||||
*/
|
||||
#define ntdb_parse_record(ntdb, key, parse, data) \
|
||||
ntdb_parse_record_((ntdb), (key), \
|
||||
typesafe_cb_preargs(enum NTDB_ERROR, void *, \
|
||||
(parse), (data), \
|
||||
NTDB_DATA, NTDB_DATA), (data))
|
||||
|
||||
enum NTDB_ERROR ntdb_parse_record_(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key,
|
||||
enum NTDB_ERROR (*parse)(NTDB_DATA k,
|
||||
NTDB_DATA d,
|
||||
void *data),
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* ntdb_get_seqnum - get a database sequence number
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This returns a sequence number: any change to the database from a
|
||||
* ntdb context opened with the NTDB_SEQNUM flag will cause that number
|
||||
* to increment. Note that the incrementing is unreliable (it is done
|
||||
* without locking), so this is only useful as an optimization.
|
||||
*
|
||||
* For example, you may have a regular database backup routine which
|
||||
* does not operate if the sequence number is unchanged. In the
|
||||
* unlikely event of a failed increment, it will be backed up next
|
||||
* time any way.
|
||||
*
|
||||
* Returns an enum NTDB_ERROR (ie. negative) on error.
|
||||
*/
|
||||
int64_t ntdb_get_seqnum(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_firstkey - get the "first" key in a NTDB
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: pointer to key.
|
||||
*
|
||||
* This returns an arbitrary key in the database; with ntdb_nextkey() it allows
|
||||
* open-coded traversal of the database, though it is slightly less efficient
|
||||
* than ntdb_traverse.
|
||||
*
|
||||
* It is your responsibility to free @key->dptr on success.
|
||||
*
|
||||
* Returns NTDB_ERR_NOEXIST if the database is empty.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_firstkey(struct ntdb_context *ntdb, NTDB_DATA *key);
|
||||
|
||||
/**
|
||||
* ntdb_nextkey - get the "next" key in a NTDB
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: a key returned by ntdb_firstkey() or ntdb_nextkey().
|
||||
*
|
||||
* This returns another key in the database; it will free @key.dptr for
|
||||
* your convenience.
|
||||
*
|
||||
* Returns NTDB_ERR_NOEXIST if there are no more keys.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_nextkey(struct ntdb_context *ntdb, NTDB_DATA *key);
|
||||
|
||||
/**
|
||||
* ntdb_chainlock - lock a record in the NTDB
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key to lock.
|
||||
*
|
||||
* This prevents any access occurring to a group of keys including @key,
|
||||
* even if @key does not exist. This allows primitive atomic updates of
|
||||
* records without using transactions.
|
||||
*
|
||||
* You cannot begin a transaction while holding a ntdb_chainlock(), nor can
|
||||
* you do any operations on any other keys in the database. This also means
|
||||
* that you cannot hold more than one ntdb_chainlock() at a time.
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_chainunlock()
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_chainlock(struct ntdb_context *ntdb, NTDB_DATA key);
|
||||
|
||||
/**
|
||||
* ntdb_chainunlock - unlock a record in the NTDB
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key to unlock.
|
||||
*
|
||||
* The key must have previously been locked by ntdb_chainlock().
|
||||
*/
|
||||
void ntdb_chainunlock(struct ntdb_context *ntdb, NTDB_DATA key);
|
||||
|
||||
/**
|
||||
* ntdb_chainlock_read - lock a record in the NTDB, for reading
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key to lock.
|
||||
*
|
||||
* This prevents any changes from occurring to a group of keys including @key,
|
||||
* even if @key does not exist. This allows primitive atomic updates of
|
||||
* records without using transactions.
|
||||
*
|
||||
* You cannot begin a transaction while holding a ntdb_chainlock_read(), nor can
|
||||
* you do any operations on any other keys in the database. This also means
|
||||
* that you cannot hold more than one ntdb_chainlock()/read() at a time.
|
||||
*
|
||||
* See Also:
|
||||
* ntdb_chainlock()
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_chainlock_read(struct ntdb_context *ntdb, NTDB_DATA key);
|
||||
|
||||
/**
|
||||
* ntdb_chainunlock_read - unlock a record in the NTDB for reading
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @key: the key to unlock.
|
||||
*
|
||||
* The key must have previously been locked by ntdb_chainlock_read().
|
||||
*/
|
||||
void ntdb_chainunlock_read(struct ntdb_context *ntdb, NTDB_DATA key);
|
||||
|
||||
/**
|
||||
* ntdb_lockall - lock the entire NTDB
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* You cannot hold a ntdb_chainlock while calling this. It nests, so you
|
||||
* must call ntdb_unlockall as many times as you call ntdb_lockall.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_lockall(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_unlockall - unlock the entire NTDB
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*/
|
||||
void ntdb_unlockall(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_lockall_read - lock the entire NTDB for reading
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This prevents others writing to the database, eg. ntdb_delete, ntdb_store,
|
||||
* ntdb_append, but not ntdb_fetch.
|
||||
*
|
||||
* You cannot hold a ntdb_chainlock while calling this. It nests, so you
|
||||
* must call ntdb_unlockall_read as many times as you call ntdb_lockall_read.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_lockall_read(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_unlockall_read - unlock the entire NTDB for reading
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*/
|
||||
void ntdb_unlockall_read(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_wipe_all - wipe the database clean
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* Completely erase the database. This is faster than iterating through
|
||||
* each key and doing ntdb_delete.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_wipe_all(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_repack - repack the database
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This repacks the database; if it is suffering from a great deal of
|
||||
* fragmentation this might help. However, it can take twice the
|
||||
* memory of the existing NTDB.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_repack(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_check - check a NTDB for consistency
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @check: function to check each key/data pair (or NULL)
|
||||
* @data: argument for @check, must match type.
|
||||
*
|
||||
* This performs a consistency check of the open database, optionally calling
|
||||
* a check() function on each record so you can do your own data consistency
|
||||
* checks as well. If check() returns an error, that is returned from
|
||||
* ntdb_check().
|
||||
*
|
||||
* Note that the NTDB uses a feature which we don't understand which
|
||||
* indicates we can't run ntdb_check(), this will log a warning to that
|
||||
* effect and return NTDB_SUCCESS. You can detect this condition by
|
||||
* looking for NTDB_CANT_CHECK in ntdb_get_flags().
|
||||
*
|
||||
* Returns NTDB_SUCCESS or an error.
|
||||
*/
|
||||
#define ntdb_check(ntdb, check, data) \
|
||||
ntdb_check_((ntdb), typesafe_cb_preargs(enum NTDB_ERROR, void *, \
|
||||
(check), (data), \
|
||||
NTDB_DATA, \
|
||||
NTDB_DATA), \
|
||||
(data))
|
||||
|
||||
enum NTDB_ERROR ntdb_check_(struct ntdb_context *ntdb,
|
||||
enum NTDB_ERROR (*check)(NTDB_DATA k,
|
||||
NTDB_DATA d,
|
||||
void *data),
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* ntdb_error - get the last error (not threadsafe)
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* Returns the last error returned by a NTDB function.
|
||||
*
|
||||
* This makes porting from TDB easier, but note that the last error is not
|
||||
* reliable in threaded programs.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_error(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* enum ntdb_summary_flags - flags for ntdb_summary.
|
||||
*/
|
||||
enum ntdb_summary_flags {
|
||||
NTDB_SUMMARY_HISTOGRAMS = 1 /* Draw graphs in the summary. */
|
||||
};
|
||||
|
||||
/**
|
||||
* ntdb_summary - return a string describing the NTDB state
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @flags: flags to control the summary output.
|
||||
* @summary: pointer to string to allocate.
|
||||
*
|
||||
* This returns a developer-readable string describing the overall
|
||||
* state of the ntdb, such as the percentage used and sizes of records.
|
||||
* It is designed to provide information about the ntdb at a glance
|
||||
* without displaying any keys or data in the database.
|
||||
*
|
||||
* On success, sets @summary to point to a malloc()'ed nul-terminated
|
||||
* multi-line string. It is your responsibility to free() it.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_summary(struct ntdb_context *ntdb,
|
||||
enum ntdb_summary_flags flags,
|
||||
char **summary);
|
||||
|
||||
|
||||
/**
|
||||
* ntdb_get_flags - return the flags for a ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This returns the flags on the current ntdb. Some of these are caused by
|
||||
* the flags argument to ntdb_open(), others (such as NTDB_CONVERT) are
|
||||
* intuited.
|
||||
*/
|
||||
unsigned int ntdb_get_flags(struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_add_flag - set a flag for a ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @flag: one of NTDB_NOLOCK, NTDB_NOMMAP, NTDB_NOSYNC or NTDB_ALLOW_NESTING.
|
||||
*
|
||||
* You can use this to set a flag on the NTDB. You cannot set these flags
|
||||
* on a NTDB_INTERNAL ntdb.
|
||||
*/
|
||||
void ntdb_add_flag(struct ntdb_context *ntdb, unsigned flag);
|
||||
|
||||
/**
|
||||
* ntdb_remove_flag - unset a flag for a ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @flag: one of NTDB_NOLOCK, NTDB_NOMMAP, NTDB_NOSYNC or NTDB_ALLOW_NESTING.
|
||||
*
|
||||
* You can use this to clear a flag on the NTDB. You cannot clear flags
|
||||
* on a NTDB_INTERNAL ntdb.
|
||||
*/
|
||||
void ntdb_remove_flag(struct ntdb_context *ntdb, unsigned flag);
|
||||
|
||||
/**
|
||||
* enum ntdb_attribute_type - descriminator for union ntdb_attribute.
|
||||
*/
|
||||
enum ntdb_attribute_type {
|
||||
NTDB_ATTRIBUTE_LOG = 0,
|
||||
NTDB_ATTRIBUTE_HASH = 1,
|
||||
NTDB_ATTRIBUTE_SEED = 2,
|
||||
NTDB_ATTRIBUTE_STATS = 3,
|
||||
NTDB_ATTRIBUTE_OPENHOOK = 4,
|
||||
NTDB_ATTRIBUTE_FLOCK = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* ntdb_get_attribute - get an attribute for an existing ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @attr: the union ntdb_attribute to set.
|
||||
*
|
||||
* This gets an attribute from a NTDB which has previously been set (or
|
||||
* may return the default values). Set @attr.base.attr to the
|
||||
* attribute type you want get.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_get_attribute(struct ntdb_context *ntdb,
|
||||
union ntdb_attribute *attr);
|
||||
|
||||
/**
|
||||
* ntdb_set_attribute - set an attribute for an existing ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @attr: the union ntdb_attribute to set.
|
||||
*
|
||||
* This sets an attribute on a NTDB, overriding any previous attribute
|
||||
* of the same type. It returns NTDB_ERR_EINVAL if the attribute is
|
||||
* unknown or invalid.
|
||||
*
|
||||
* Note that NTDB_ATTRIBUTE_HASH, NTDB_ATTRIBUTE_SEED, and
|
||||
* NTDB_ATTRIBUTE_OPENHOOK cannot currently be set after ntdb_open.
|
||||
*/
|
||||
enum NTDB_ERROR ntdb_set_attribute(struct ntdb_context *ntdb,
|
||||
const union ntdb_attribute *attr);
|
||||
|
||||
/**
|
||||
* ntdb_unset_attribute - reset an attribute for an existing ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
* @type: the attribute type to unset.
|
||||
*
|
||||
* This unsets an attribute on a NTDB, returning it to the defaults
|
||||
* (where applicable).
|
||||
*
|
||||
* Note that it only makes sense for NTDB_ATTRIBUTE_LOG and NTDB_ATTRIBUTE_FLOCK
|
||||
* to be unset.
|
||||
*/
|
||||
void ntdb_unset_attribute(struct ntdb_context *ntdb,
|
||||
enum ntdb_attribute_type type);
|
||||
|
||||
/**
|
||||
* ntdb_name - get the name of a ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This returns a copy of the name string, made at ntdb_open() time. If that
|
||||
* argument was NULL (possible for a NTDB_INTERNAL db) this will return NULL.
|
||||
*
|
||||
* This is mostly useful for logging.
|
||||
*/
|
||||
const char *ntdb_name(const struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_fd - get the file descriptor of a ntdb
|
||||
* @ntdb: the ntdb context returned from ntdb_open()
|
||||
*
|
||||
* This returns the file descriptor for the underlying database file, or -1
|
||||
* for NTDB_INTERNAL.
|
||||
*/
|
||||
int ntdb_fd(const struct ntdb_context *ntdb);
|
||||
|
||||
/**
|
||||
* ntdb_foreach - iterate through every open NTDB.
|
||||
* @fn: the function to call for every NTDB
|
||||
* @p: the pointer to hand to @fn
|
||||
*
|
||||
* NTDB internally keeps track of all open TDBs; this function allows you to
|
||||
* iterate through them. If @fn returns non-zero, traversal stops.
|
||||
*/
|
||||
#define ntdb_foreach(fn, p) \
|
||||
ntdb_foreach_(typesafe_cb_preargs(int, void *, (fn), (p), \
|
||||
struct ntdb_context *), (p))
|
||||
|
||||
void ntdb_foreach_(int (*fn)(struct ntdb_context *, void *), void *p);
|
||||
|
||||
/**
|
||||
* struct ntdb_attribute_base - common fields for all ntdb attributes.
|
||||
*/
|
||||
struct ntdb_attribute_base {
|
||||
enum ntdb_attribute_type attr;
|
||||
union ntdb_attribute *next;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ntdb_log_level - log levels for ntdb_attribute_log
|
||||
* @NTDB_LOG_ERROR: used to log unrecoverable errors such as I/O errors
|
||||
* or internal consistency failures.
|
||||
* @NTDB_LOG_USE_ERROR: used to log usage errors such as invalid parameters
|
||||
* or writing to a read-only database.
|
||||
* @NTDB_LOG_WARNING: used for informational messages on issues which
|
||||
* are unusual but handled by NTDB internally, such
|
||||
* as a failure to mmap or failure to open /dev/urandom.
|
||||
*/
|
||||
enum ntdb_log_level {
|
||||
NTDB_LOG_ERROR,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
NTDB_LOG_WARNING
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ntdb_attribute_log - log function attribute
|
||||
*
|
||||
* This attribute provides a hook for you to log errors.
|
||||
*/
|
||||
struct ntdb_attribute_log {
|
||||
struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_LOG */
|
||||
void (*fn)(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ntdb_attribute_hash - hash function attribute
|
||||
*
|
||||
* This attribute allows you to provide an alternative hash function.
|
||||
* This hash function will be handed keys from the database; it will also
|
||||
* be handed the 8-byte NTDB_HASH_MAGIC value for checking the header (the
|
||||
* ntdb_open() will fail if the hash value doesn't match the header).
|
||||
*
|
||||
* Note that if your hash function gives different results on
|
||||
* different machine endians, your ntdb will no longer work across
|
||||
* different architectures!
|
||||
*/
|
||||
struct ntdb_attribute_hash {
|
||||
struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_HASH */
|
||||
uint64_t (*fn)(const void *key, size_t len, uint64_t seed,
|
||||
void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ntdb_attribute_seed - hash function seed attribute
|
||||
*
|
||||
* The hash function seed is normally taken from /dev/urandom (or equivalent)
|
||||
* but can be set manually here. This is mainly for testing purposes.
|
||||
*/
|
||||
struct ntdb_attribute_seed {
|
||||
struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_SEED */
|
||||
uint64_t seed;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ntdb_attribute_stats - ntdb operational statistics
|
||||
*
|
||||
* This attribute records statistics of various low-level NTDB operations.
|
||||
* This can be used to assist performance evaluation. This is only
|
||||
* useful for ntdb_get_attribute().
|
||||
*
|
||||
* New fields will be added at the end, hence the "size" argument which
|
||||
* indicates how large your structure is: it must be filled in before
|
||||
* calling ntdb_get_attribute(), which will overwrite it with the size
|
||||
* ntdb knows about.
|
||||
*/
|
||||
struct ntdb_attribute_stats {
|
||||
struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_STATS */
|
||||
size_t size; /* = sizeof(struct ntdb_attribute_stats) */
|
||||
uint64_t allocs;
|
||||
uint64_t alloc_subhash;
|
||||
uint64_t alloc_chain;
|
||||
uint64_t alloc_bucket_exact;
|
||||
uint64_t alloc_bucket_max;
|
||||
uint64_t alloc_leftover;
|
||||
uint64_t alloc_coalesce_tried;
|
||||
uint64_t alloc_coalesce_iterate_clash;
|
||||
uint64_t alloc_coalesce_lockfail;
|
||||
uint64_t alloc_coalesce_race;
|
||||
uint64_t alloc_coalesce_succeeded;
|
||||
uint64_t alloc_coalesce_num_merged;
|
||||
uint64_t compares;
|
||||
uint64_t compare_wrong_bucket;
|
||||
uint64_t compare_wrong_offsetbits;
|
||||
uint64_t compare_wrong_keylen;
|
||||
uint64_t compare_wrong_rechash;
|
||||
uint64_t compare_wrong_keycmp;
|
||||
uint64_t transactions;
|
||||
uint64_t transaction_cancel;
|
||||
uint64_t transaction_nest;
|
||||
uint64_t transaction_expand_file;
|
||||
uint64_t transaction_read_direct;
|
||||
uint64_t transaction_read_direct_fail;
|
||||
uint64_t transaction_write_direct;
|
||||
uint64_t transaction_write_direct_fail;
|
||||
uint64_t expands;
|
||||
uint64_t frees;
|
||||
uint64_t locks;
|
||||
uint64_t lock_lowlevel;
|
||||
uint64_t lock_nonblock;
|
||||
uint64_t lock_nonblock_fail;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ntdb_attribute_openhook - ntdb special effects hook for open
|
||||
*
|
||||
* This attribute contains a function to call once we have the OPEN_LOCK
|
||||
* for the ntdb, but before we've examined its contents. If this succeeds,
|
||||
* the ntdb will be populated if it's then zero-length.
|
||||
*
|
||||
* This is a hack to allow support for TDB-style TDB_CLEAR_IF_FIRST
|
||||
* behaviour.
|
||||
*/
|
||||
struct ntdb_attribute_openhook {
|
||||
struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_OPENHOOK */
|
||||
enum NTDB_ERROR (*fn)(int fd, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ntdb_attribute_flock - ntdb special effects hook for file locking
|
||||
*
|
||||
* This attribute contains function to call to place locks on a file; it can
|
||||
* be used to support non-blocking operations or lock proxying.
|
||||
*
|
||||
* They should return 0 on success, -1 on failure and set errno.
|
||||
*
|
||||
* An error will be logged on error if errno is neither EAGAIN nor EINTR
|
||||
* (normally it would only return EAGAIN if waitflag is false, and
|
||||
* loop internally on EINTR).
|
||||
*/
|
||||
struct ntdb_attribute_flock {
|
||||
struct ntdb_attribute_base base; /* .attr = NTDB_ATTRIBUTE_FLOCK */
|
||||
int (*lock)(int fd,int rw, off_t off, off_t len, bool waitflag, void *);
|
||||
int (*unlock)(int fd, int rw, off_t off, off_t len, void *);
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* union ntdb_attribute - ntdb attributes.
|
||||
*
|
||||
* This represents all the known attributes.
|
||||
*
|
||||
* See also:
|
||||
* struct ntdb_attribute_log, struct ntdb_attribute_hash,
|
||||
* struct ntdb_attribute_seed, struct ntdb_attribute_stats,
|
||||
* struct ntdb_attribute_openhook, struct ntdb_attribute_flock.
|
||||
*/
|
||||
union ntdb_attribute {
|
||||
struct ntdb_attribute_base base;
|
||||
struct ntdb_attribute_log log;
|
||||
struct ntdb_attribute_hash hash;
|
||||
struct ntdb_attribute_seed seed;
|
||||
struct ntdb_attribute_stats stats;
|
||||
struct ntdb_attribute_openhook openhook;
|
||||
struct ntdb_attribute_flock flock;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ntdb.h */
|
@ -3,9 +3,9 @@ exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: tdb
|
||||
Description: A trivial database
|
||||
Name: ntdb
|
||||
Description: A (not-so) trivial database
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: @LIB_RPATH@ -L${libdir} -ltdb
|
||||
Libs: @LIB_RPATH@ -L${libdir} -lntdb
|
||||
Cflags: -I${includedir}
|
||||
URL: http://tdb.samba.org/
|
768
lib/ntdb/open.c
Normal file
768
lib/ntdb/open.c
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
Trivial Database 2: opening and closing TDBs
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "private.h"
|
||||
#include <ccan/build_assert/build_assert.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* all tdbs, to detect double-opens (fcntl file don't nest!) */
|
||||
static struct ntdb_context *tdbs = NULL;
|
||||
|
||||
static struct ntdb_file *find_file(dev_t device, ino_t ino)
|
||||
{
|
||||
struct ntdb_context *i;
|
||||
|
||||
for (i = tdbs; i; i = i->next) {
|
||||
if (i->file->device == device && i->file->inode == ino) {
|
||||
i->file->refcnt++;
|
||||
return i->file;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool read_all(int fd, void *buf, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
ssize_t ret;
|
||||
ret = read(fd, buf, len);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
if (ret == 0) {
|
||||
/* ETOOSHORT? */
|
||||
errno = EWOULDBLOCK;
|
||||
return false;
|
||||
}
|
||||
buf = (char *)buf + ret;
|
||||
len -= ret;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t random_number(struct ntdb_context *ntdb)
|
||||
{
|
||||
int fd;
|
||||
uint64_t ret = 0;
|
||||
struct timeval now;
|
||||
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
if (read_all(fd, &ret, sizeof(ret))) {
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
/* FIXME: Untested! Based on Wikipedia protocol description! */
|
||||
fd = open("/dev/egd-pool", O_RDWR);
|
||||
if (fd >= 0) {
|
||||
/* Command is 1, next byte is size we want to read. */
|
||||
char cmd[2] = { 1, sizeof(uint64_t) };
|
||||
if (write(fd, cmd, sizeof(cmd)) == sizeof(cmd)) {
|
||||
char reply[1 + sizeof(uint64_t)];
|
||||
int r = read(fd, reply, sizeof(reply));
|
||||
if (r > 1) {
|
||||
/* Copy at least some bytes. */
|
||||
memcpy(&ret, reply+1, r - 1);
|
||||
if (reply[0] == sizeof(uint64_t)
|
||||
&& r == sizeof(reply)) {
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Fallback: pid and time. */
|
||||
gettimeofday(&now, NULL);
|
||||
ret = getpid() * 100132289ULL + now.tv_sec * 1000000ULL + now.tv_usec;
|
||||
ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
|
||||
"ntdb_open: random from getpid and time");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ntdb_context_init(struct ntdb_context *ntdb)
|
||||
{
|
||||
/* Initialize the NTDB fields here */
|
||||
ntdb_io_init(ntdb);
|
||||
ntdb->direct_access = 0;
|
||||
ntdb->transaction = NULL;
|
||||
ntdb->access = NULL;
|
||||
}
|
||||
|
||||
struct new_database {
|
||||
struct ntdb_header hdr;
|
||||
struct ntdb_freetable ftable;
|
||||
};
|
||||
|
||||
/* initialise a new database */
|
||||
static enum NTDB_ERROR ntdb_new_database(struct ntdb_context *ntdb,
|
||||
struct ntdb_attribute_seed *seed,
|
||||
struct ntdb_header *hdr)
|
||||
{
|
||||
/* We make it up in memory, then write it out if not internal */
|
||||
struct new_database newdb;
|
||||
unsigned int magic_len;
|
||||
ssize_t rlen;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* Fill in the header */
|
||||
newdb.hdr.version = NTDB_VERSION;
|
||||
if (seed)
|
||||
newdb.hdr.hash_seed = seed->seed;
|
||||
else
|
||||
newdb.hdr.hash_seed = random_number(ntdb);
|
||||
newdb.hdr.hash_test = NTDB_HASH_MAGIC;
|
||||
newdb.hdr.hash_test = ntdb->hash_fn(&newdb.hdr.hash_test,
|
||||
sizeof(newdb.hdr.hash_test),
|
||||
newdb.hdr.hash_seed,
|
||||
ntdb->hash_data);
|
||||
newdb.hdr.recovery = 0;
|
||||
newdb.hdr.features_used = newdb.hdr.features_offered = NTDB_FEATURE_MASK;
|
||||
newdb.hdr.seqnum = 0;
|
||||
newdb.hdr.capabilities = 0;
|
||||
memset(newdb.hdr.reserved, 0, sizeof(newdb.hdr.reserved));
|
||||
/* Initial hashes are empty. */
|
||||
memset(newdb.hdr.hashtable, 0, sizeof(newdb.hdr.hashtable));
|
||||
|
||||
/* Free is empty. */
|
||||
newdb.hdr.free_table = offsetof(struct new_database, ftable);
|
||||
memset(&newdb.ftable, 0, sizeof(newdb.ftable));
|
||||
ecode = set_header(NULL, &newdb.ftable.hdr, NTDB_FTABLE_MAGIC, 0,
|
||||
sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
|
||||
sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
|
||||
0);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
/* Magic food */
|
||||
memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
|
||||
strcpy(newdb.hdr.magic_food, NTDB_MAGIC_FOOD);
|
||||
|
||||
/* This creates an endian-converted database, as if read from disk */
|
||||
magic_len = sizeof(newdb.hdr.magic_food);
|
||||
ntdb_convert(ntdb,
|
||||
(char *)&newdb.hdr + magic_len, sizeof(newdb) - magic_len);
|
||||
|
||||
*hdr = newdb.hdr;
|
||||
|
||||
if (ntdb->flags & NTDB_INTERNAL) {
|
||||
ntdb->file->map_size = sizeof(newdb);
|
||||
ntdb->file->map_ptr = malloc(ntdb->file->map_size);
|
||||
if (!ntdb->file->map_ptr) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"ntdb_new_database:"
|
||||
" failed to allocate");
|
||||
}
|
||||
memcpy(ntdb->file->map_ptr, &newdb, ntdb->file->map_size);
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
if (lseek(ntdb->file->fd, 0, SEEK_SET) == -1) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_new_database:"
|
||||
" failed to seek: %s", strerror(errno));
|
||||
}
|
||||
|
||||
if (ftruncate(ntdb->file->fd, 0) == -1) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_new_database:"
|
||||
" failed to truncate: %s", strerror(errno));
|
||||
}
|
||||
|
||||
rlen = write(ntdb->file->fd, &newdb, sizeof(newdb));
|
||||
if (rlen != sizeof(newdb)) {
|
||||
if (rlen >= 0)
|
||||
errno = ENOSPC;
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_new_database: %zi writing header: %s",
|
||||
rlen, strerror(errno));
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR ntdb_new_file(struct ntdb_context *ntdb)
|
||||
{
|
||||
ntdb->file = malloc(sizeof(*ntdb->file));
|
||||
if (!ntdb->file)
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"ntdb_open: cannot alloc ntdb_file structure");
|
||||
ntdb->file->num_lockrecs = 0;
|
||||
ntdb->file->lockrecs = NULL;
|
||||
ntdb->file->allrecord_lock.count = 0;
|
||||
ntdb->file->refcnt = 1;
|
||||
ntdb->file->map_ptr = NULL;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_set_attribute(struct ntdb_context *ntdb,
|
||||
const union ntdb_attribute *attr)
|
||||
{
|
||||
switch (attr->base.attr) {
|
||||
case NTDB_ATTRIBUTE_LOG:
|
||||
ntdb->log_fn = attr->log.fn;
|
||||
ntdb->log_data = attr->log.data;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_HASH:
|
||||
case NTDB_ATTRIBUTE_SEED:
|
||||
case NTDB_ATTRIBUTE_OPENHOOK:
|
||||
return ntdb->last_error
|
||||
= ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_set_attribute:"
|
||||
" cannot set %s after opening",
|
||||
attr->base.attr == NTDB_ATTRIBUTE_HASH
|
||||
? "NTDB_ATTRIBUTE_HASH"
|
||||
: attr->base.attr == NTDB_ATTRIBUTE_SEED
|
||||
? "NTDB_ATTRIBUTE_SEED"
|
||||
: "NTDB_ATTRIBUTE_OPENHOOK");
|
||||
case NTDB_ATTRIBUTE_STATS:
|
||||
return ntdb->last_error
|
||||
= ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_set_attribute:"
|
||||
" cannot set NTDB_ATTRIBUTE_STATS");
|
||||
case NTDB_ATTRIBUTE_FLOCK:
|
||||
ntdb->lock_fn = attr->flock.lock;
|
||||
ntdb->unlock_fn = attr->flock.unlock;
|
||||
ntdb->lock_data = attr->flock.data;
|
||||
break;
|
||||
default:
|
||||
return ntdb->last_error
|
||||
= ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_set_attribute:"
|
||||
" unknown attribute type %u",
|
||||
attr->base.attr);
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_get_attribute(struct ntdb_context *ntdb,
|
||||
union ntdb_attribute *attr)
|
||||
{
|
||||
switch (attr->base.attr) {
|
||||
case NTDB_ATTRIBUTE_LOG:
|
||||
if (!ntdb->log_fn)
|
||||
return ntdb->last_error = NTDB_ERR_NOEXIST;
|
||||
attr->log.fn = ntdb->log_fn;
|
||||
attr->log.data = ntdb->log_data;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_HASH:
|
||||
attr->hash.fn = ntdb->hash_fn;
|
||||
attr->hash.data = ntdb->hash_data;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_SEED:
|
||||
attr->seed.seed = ntdb->hash_seed;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_OPENHOOK:
|
||||
if (!ntdb->openhook)
|
||||
return ntdb->last_error = NTDB_ERR_NOEXIST;
|
||||
attr->openhook.fn = ntdb->openhook;
|
||||
attr->openhook.data = ntdb->openhook_data;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_STATS: {
|
||||
size_t size = attr->stats.size;
|
||||
if (size > ntdb->stats.size)
|
||||
size = ntdb->stats.size;
|
||||
memcpy(&attr->stats, &ntdb->stats, size);
|
||||
break;
|
||||
}
|
||||
case NTDB_ATTRIBUTE_FLOCK:
|
||||
attr->flock.lock = ntdb->lock_fn;
|
||||
attr->flock.unlock = ntdb->unlock_fn;
|
||||
attr->flock.data = ntdb->lock_data;
|
||||
break;
|
||||
default:
|
||||
return ntdb->last_error
|
||||
= ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_get_attribute:"
|
||||
" unknown attribute type %u",
|
||||
attr->base.attr);
|
||||
}
|
||||
attr->base.next = NULL;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_unset_attribute(struct ntdb_context *ntdb,
|
||||
enum ntdb_attribute_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case NTDB_ATTRIBUTE_LOG:
|
||||
ntdb->log_fn = NULL;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_OPENHOOK:
|
||||
ntdb->openhook = NULL;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_HASH:
|
||||
case NTDB_ATTRIBUTE_SEED:
|
||||
ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_unset_attribute: cannot unset %s after opening",
|
||||
type == NTDB_ATTRIBUTE_HASH
|
||||
? "NTDB_ATTRIBUTE_HASH"
|
||||
: "NTDB_ATTRIBUTE_SEED");
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_STATS:
|
||||
ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_unset_attribute:"
|
||||
"cannot unset NTDB_ATTRIBUTE_STATS");
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_FLOCK:
|
||||
ntdb->lock_fn = ntdb_fcntl_lock;
|
||||
ntdb->unlock_fn = ntdb_fcntl_unlock;
|
||||
break;
|
||||
default:
|
||||
ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_unset_attribute: unknown attribute type %u",
|
||||
type);
|
||||
}
|
||||
}
|
||||
|
||||
/* The top three bits of the capability tell us whether it matters. */
|
||||
enum NTDB_ERROR unknown_capability(struct ntdb_context *ntdb, const char *caller,
|
||||
ntdb_off_t type)
|
||||
{
|
||||
if (type & NTDB_CAP_NOOPEN) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"%s: file has unknown capability %llu",
|
||||
caller, type & NTDB_CAP_NOOPEN);
|
||||
}
|
||||
|
||||
if ((type & NTDB_CAP_NOWRITE) && !(ntdb->flags & NTDB_RDONLY)) {
|
||||
return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_ERROR,
|
||||
"%s: file has unknown capability %llu"
|
||||
" (cannot write to it)",
|
||||
caller, type & NTDB_CAP_NOOPEN);
|
||||
}
|
||||
|
||||
if (type & NTDB_CAP_NOCHECK) {
|
||||
ntdb->flags |= NTDB_CANT_CHECK;
|
||||
}
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR capabilities_ok(struct ntdb_context *ntdb,
|
||||
ntdb_off_t capabilities)
|
||||
{
|
||||
ntdb_off_t off, next;
|
||||
enum NTDB_ERROR ecode = NTDB_SUCCESS;
|
||||
const struct ntdb_capability *cap;
|
||||
|
||||
/* Check capability list. */
|
||||
for (off = capabilities; off && ecode == NTDB_SUCCESS; off = next) {
|
||||
cap = ntdb_access_read(ntdb, off, sizeof(*cap), true);
|
||||
if (NTDB_PTR_IS_ERR(cap)) {
|
||||
return NTDB_PTR_ERR(cap);
|
||||
}
|
||||
|
||||
switch (cap->type & NTDB_CAP_TYPE_MASK) {
|
||||
/* We don't understand any capabilities (yet). */
|
||||
default:
|
||||
ecode = unknown_capability(ntdb, "ntdb_open", cap->type);
|
||||
}
|
||||
next = cap->next;
|
||||
ntdb_access_release(ntdb, cap);
|
||||
}
|
||||
return ecode;
|
||||
}
|
||||
|
||||
_PUBLIC_ struct ntdb_context *ntdb_open(const char *name, int ntdb_flags,
|
||||
int open_flags, mode_t mode,
|
||||
union ntdb_attribute *attr)
|
||||
{
|
||||
struct ntdb_context *ntdb;
|
||||
struct stat st;
|
||||
int saved_errno = 0;
|
||||
uint64_t hash_test;
|
||||
unsigned v;
|
||||
ssize_t rlen;
|
||||
struct ntdb_header hdr;
|
||||
struct ntdb_attribute_seed *seed = NULL;
|
||||
ntdb_bool_err berr;
|
||||
enum NTDB_ERROR ecode;
|
||||
int openlock;
|
||||
|
||||
ntdb = malloc(sizeof(*ntdb) + (name ? strlen(name) + 1 : 0));
|
||||
if (!ntdb) {
|
||||
/* Can't log this */
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
/* Set name immediately for logging functions. */
|
||||
if (name) {
|
||||
ntdb->name = strcpy((char *)(ntdb + 1), name);
|
||||
} else {
|
||||
ntdb->name = NULL;
|
||||
}
|
||||
ntdb->flags = ntdb_flags;
|
||||
ntdb->log_fn = NULL;
|
||||
ntdb->open_flags = open_flags;
|
||||
ntdb->last_error = NTDB_SUCCESS;
|
||||
ntdb->file = NULL;
|
||||
ntdb->openhook = NULL;
|
||||
ntdb->lock_fn = ntdb_fcntl_lock;
|
||||
ntdb->unlock_fn = ntdb_fcntl_unlock;
|
||||
ntdb->hash_fn = ntdb_jenkins_hash;
|
||||
memset(&ntdb->stats, 0, sizeof(ntdb->stats));
|
||||
ntdb->stats.base.attr = NTDB_ATTRIBUTE_STATS;
|
||||
ntdb->stats.size = sizeof(ntdb->stats);
|
||||
|
||||
while (attr) {
|
||||
switch (attr->base.attr) {
|
||||
case NTDB_ATTRIBUTE_HASH:
|
||||
ntdb->hash_fn = attr->hash.fn;
|
||||
ntdb->hash_data = attr->hash.data;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_SEED:
|
||||
seed = &attr->seed;
|
||||
break;
|
||||
case NTDB_ATTRIBUTE_OPENHOOK:
|
||||
ntdb->openhook = attr->openhook.fn;
|
||||
ntdb->openhook_data = attr->openhook.data;
|
||||
break;
|
||||
default:
|
||||
/* These are set as normal. */
|
||||
ecode = ntdb_set_attribute(ntdb, attr);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto fail;
|
||||
}
|
||||
attr = attr->base.next;
|
||||
}
|
||||
|
||||
if (ntdb_flags & ~(NTDB_INTERNAL | NTDB_NOLOCK | NTDB_NOMMAP | NTDB_CONVERT
|
||||
| NTDB_NOSYNC | NTDB_SEQNUM | NTDB_ALLOW_NESTING
|
||||
| NTDB_RDONLY)) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_open: unknown flags %u", ntdb_flags);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (seed) {
|
||||
if (!(ntdb_flags & NTDB_INTERNAL) && !(open_flags & O_CREAT)) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_open:"
|
||||
" cannot set NTDB_ATTRIBUTE_SEED"
|
||||
" without O_CREAT.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if ((open_flags & O_ACCMODE) == O_WRONLY) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL, NTDB_LOG_USE_ERROR,
|
||||
"ntdb_open: can't open ntdb %s write-only",
|
||||
name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((open_flags & O_ACCMODE) == O_RDONLY) {
|
||||
openlock = F_RDLCK;
|
||||
ntdb->flags |= NTDB_RDONLY;
|
||||
} else {
|
||||
if (ntdb_flags & NTDB_RDONLY) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_EINVAL,
|
||||
NTDB_LOG_USE_ERROR,
|
||||
"ntdb_open: can't use NTDB_RDONLY"
|
||||
" without O_RDONLY");
|
||||
goto fail;
|
||||
}
|
||||
openlock = F_WRLCK;
|
||||
}
|
||||
|
||||
/* internal databases don't need any of the rest. */
|
||||
if (ntdb->flags & NTDB_INTERNAL) {
|
||||
ntdb->flags |= (NTDB_NOLOCK | NTDB_NOMMAP);
|
||||
ecode = ntdb_new_file(ntdb);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
ntdb->file->fd = -1;
|
||||
ecode = ntdb_new_database(ntdb, seed, &hdr);
|
||||
if (ecode == NTDB_SUCCESS) {
|
||||
ntdb_convert(ntdb, &hdr.hash_seed,
|
||||
sizeof(hdr.hash_seed));
|
||||
ntdb->hash_seed = hdr.hash_seed;
|
||||
ntdb_context_init(ntdb);
|
||||
ntdb_ftable_init(ntdb);
|
||||
}
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
return ntdb;
|
||||
}
|
||||
|
||||
if (stat(name, &st) != -1)
|
||||
ntdb->file = find_file(st.st_dev, st.st_ino);
|
||||
|
||||
if (!ntdb->file) {
|
||||
int fd;
|
||||
|
||||
if ((fd = open(name, open_flags, mode)) == -1) {
|
||||
/* errno set by open(2) */
|
||||
saved_errno = errno;
|
||||
ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open: could not open file %s: %s",
|
||||
name, strerror(errno));
|
||||
goto fail_errno;
|
||||
}
|
||||
|
||||
/* on exec, don't inherit the fd */
|
||||
v = fcntl(fd, F_GETFD, 0);
|
||||
fcntl(fd, F_SETFD, v | FD_CLOEXEC);
|
||||
|
||||
if (fstat(fd, &st) == -1) {
|
||||
saved_errno = errno;
|
||||
ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open: could not stat open %s: %s",
|
||||
name, strerror(errno));
|
||||
close(fd);
|
||||
goto fail_errno;
|
||||
}
|
||||
|
||||
ecode = ntdb_new_file(ntdb);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
close(fd);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ntdb->file->fd = fd;
|
||||
ntdb->file->device = st.st_dev;
|
||||
ntdb->file->inode = st.st_ino;
|
||||
ntdb->file->map_ptr = NULL;
|
||||
ntdb->file->map_size = 0;
|
||||
}
|
||||
|
||||
/* ensure there is only one process initialising at once */
|
||||
ecode = ntdb_lock_open(ntdb, openlock, NTDB_LOCK_WAIT|NTDB_LOCK_NOCHECK);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
saved_errno = errno;
|
||||
goto fail_errno;
|
||||
}
|
||||
|
||||
/* call their open hook if they gave us one. */
|
||||
if (ntdb->openhook) {
|
||||
ecode = ntdb->openhook(ntdb->file->fd, ntdb->openhook_data);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_logerr(ntdb, ecode, NTDB_LOG_ERROR,
|
||||
"ntdb_open: open hook failed");
|
||||
goto fail;
|
||||
}
|
||||
open_flags |= O_CREAT;
|
||||
}
|
||||
|
||||
/* If they used O_TRUNC, read will return 0. */
|
||||
rlen = pread(ntdb->file->fd, &hdr, sizeof(hdr), 0);
|
||||
if (rlen == 0 && (open_flags & O_CREAT)) {
|
||||
ecode = ntdb_new_database(ntdb, seed, &hdr);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
} else if (rlen < 0) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open: error %s reading %s",
|
||||
strerror(errno), name);
|
||||
goto fail;
|
||||
} else if (rlen < sizeof(hdr)
|
||||
|| strcmp(hdr.magic_food, NTDB_MAGIC_FOOD) != 0) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open: %s is not a ntdb file", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (hdr.version != NTDB_VERSION) {
|
||||
if (hdr.version == bswap_64(NTDB_VERSION))
|
||||
ntdb->flags |= NTDB_CONVERT;
|
||||
else {
|
||||
/* wrong version */
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open:"
|
||||
" %s is unknown version 0x%llx",
|
||||
name, (long long)hdr.version);
|
||||
goto fail;
|
||||
}
|
||||
} else if (ntdb->flags & NTDB_CONVERT) {
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open:"
|
||||
" %s does not need NTDB_CONVERT",
|
||||
name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ntdb_context_init(ntdb);
|
||||
|
||||
ntdb_convert(ntdb, &hdr, sizeof(hdr));
|
||||
ntdb->hash_seed = hdr.hash_seed;
|
||||
hash_test = NTDB_HASH_MAGIC;
|
||||
hash_test = ntdb_hash(ntdb, &hash_test, sizeof(hash_test));
|
||||
if (hdr.hash_test != hash_test) {
|
||||
/* wrong hash variant */
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open:"
|
||||
" %s uses a different hash function",
|
||||
name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ecode = capabilities_ok(ntdb, hdr.capabilities);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Clear any features we don't understand. */
|
||||
if ((open_flags & O_ACCMODE) != O_RDONLY) {
|
||||
hdr.features_used &= NTDB_FEATURE_MASK;
|
||||
ecode = ntdb_write_convert(ntdb, offsetof(struct ntdb_header,
|
||||
features_used),
|
||||
&hdr.features_used,
|
||||
sizeof(hdr.features_used));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ntdb_unlock_open(ntdb, openlock);
|
||||
|
||||
/* This makes sure we have current map_size and mmap. */
|
||||
ecode = ntdb->io->oob(ntdb, ntdb->file->map_size, 1, true);
|
||||
if (unlikely(ecode != NTDB_SUCCESS))
|
||||
goto fail;
|
||||
|
||||
/* Now it's fully formed, recover if necessary. */
|
||||
berr = ntdb_needs_recovery(ntdb);
|
||||
if (unlikely(berr != false)) {
|
||||
if (berr < 0) {
|
||||
ecode = NTDB_OFF_TO_ERR(berr);
|
||||
goto fail;
|
||||
}
|
||||
ecode = ntdb_lock_and_recover(ntdb);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ecode = ntdb_ftable_init(ntdb);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ntdb->next = tdbs;
|
||||
tdbs = ntdb;
|
||||
return ntdb;
|
||||
|
||||
fail:
|
||||
/* Map ecode to some logical errno. */
|
||||
switch (NTDB_ERR_TO_OFF(ecode)) {
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_CORRUPT):
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_IO):
|
||||
saved_errno = EIO;
|
||||
break;
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_LOCK):
|
||||
saved_errno = EWOULDBLOCK;
|
||||
break;
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_OOM):
|
||||
saved_errno = ENOMEM;
|
||||
break;
|
||||
case NTDB_ERR_TO_OFF(NTDB_ERR_EINVAL):
|
||||
saved_errno = EINVAL;
|
||||
break;
|
||||
default:
|
||||
saved_errno = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
fail_errno:
|
||||
#ifdef NTDB_TRACE
|
||||
close(ntdb->tracefd);
|
||||
#endif
|
||||
if (ntdb->file) {
|
||||
ntdb_lock_cleanup(ntdb);
|
||||
if (--ntdb->file->refcnt == 0) {
|
||||
assert(ntdb->file->num_lockrecs == 0);
|
||||
if (ntdb->file->map_ptr) {
|
||||
if (ntdb->flags & NTDB_INTERNAL) {
|
||||
free(ntdb->file->map_ptr);
|
||||
} else
|
||||
ntdb_munmap(ntdb->file);
|
||||
}
|
||||
if (close(ntdb->file->fd) != 0)
|
||||
ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
|
||||
"ntdb_open: failed to close ntdb fd"
|
||||
" on error: %s", strerror(errno));
|
||||
free(ntdb->file->lockrecs);
|
||||
free(ntdb->file);
|
||||
}
|
||||
}
|
||||
|
||||
free(ntdb);
|
||||
errno = saved_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_PUBLIC_ int ntdb_close(struct ntdb_context *ntdb)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ntdb_context **i;
|
||||
|
||||
ntdb_trace(ntdb, "ntdb_close");
|
||||
|
||||
if (ntdb->transaction) {
|
||||
ntdb_transaction_cancel(ntdb);
|
||||
}
|
||||
|
||||
if (ntdb->file->map_ptr) {
|
||||
if (ntdb->flags & NTDB_INTERNAL)
|
||||
free(ntdb->file->map_ptr);
|
||||
else
|
||||
ntdb_munmap(ntdb->file);
|
||||
}
|
||||
if (ntdb->file) {
|
||||
ntdb_lock_cleanup(ntdb);
|
||||
if (--ntdb->file->refcnt == 0) {
|
||||
ret = close(ntdb->file->fd);
|
||||
free(ntdb->file->lockrecs);
|
||||
free(ntdb->file);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove from tdbs list */
|
||||
for (i = &tdbs; *i; i = &(*i)->next) {
|
||||
if (*i == ntdb) {
|
||||
*i = ntdb->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NTDB_TRACE
|
||||
close(ntdb->tracefd);
|
||||
#endif
|
||||
free(ntdb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntdb_foreach_(int (*fn)(struct ntdb_context *, void *), void *p)
|
||||
{
|
||||
struct ntdb_context *i;
|
||||
|
||||
for (i = tdbs; i; i = i->next) {
|
||||
if (fn(i, p) != 0)
|
||||
break;
|
||||
}
|
||||
}
|
657
lib/ntdb/private.h
Normal file
657
lib/ntdb/private.h
Normal file
@ -0,0 +1,657 @@
|
||||
#ifndef NTDB_PRIVATE_H
|
||||
#define NTDB_PRIVATE_H
|
||||
/*
|
||||
Trivial Database 2: private types and prototypes
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifndef HAVE_CCAN
|
||||
#error You need ccan to build ntdb!
|
||||
#endif
|
||||
#include "ntdb.h"
|
||||
#include <ccan/compiler/compiler.h>
|
||||
#include <ccan/likely/likely.h>
|
||||
#include <ccan/endian/endian.h>
|
||||
|
||||
#ifdef HAVE_LIBREPLACE
|
||||
#include "replace.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/time.h"
|
||||
#include "system/shmem.h"
|
||||
#include "system/select.h"
|
||||
#include "system/wait.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <utime.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef TEST_IT
|
||||
#define TEST_IT(cond)
|
||||
#endif
|
||||
|
||||
/* #define NTDB_TRACE 1 */
|
||||
|
||||
#ifndef __STRING
|
||||
#define __STRING(x) #x
|
||||
#endif
|
||||
|
||||
#ifndef __STRINGSTRING
|
||||
#define __STRINGSTRING(x) __STRING(x)
|
||||
#endif
|
||||
|
||||
#ifndef __location__
|
||||
#define __location__ __FILE__ ":" __STRINGSTRING(__LINE__)
|
||||
#endif
|
||||
|
||||
typedef uint64_t ntdb_len_t;
|
||||
typedef uint64_t ntdb_off_t;
|
||||
|
||||
#define NTDB_MAGIC_FOOD "NTDB file\n"
|
||||
#define NTDB_VERSION ((uint64_t)(0x26011967 + 7))
|
||||
#define NTDB_USED_MAGIC ((uint64_t)0x1999)
|
||||
#define NTDB_HTABLE_MAGIC ((uint64_t)0x1888)
|
||||
#define NTDB_CHAIN_MAGIC ((uint64_t)0x1777)
|
||||
#define NTDB_FTABLE_MAGIC ((uint64_t)0x1666)
|
||||
#define NTDB_CAP_MAGIC ((uint64_t)0x1555)
|
||||
#define NTDB_FREE_MAGIC ((uint64_t)0xFE)
|
||||
#define NTDB_HASH_MAGIC (0xA1ABE11A01092008ULL)
|
||||
#define NTDB_RECOVERY_MAGIC (0xf53bc0e7ad124589ULL)
|
||||
#define NTDB_RECOVERY_INVALID_MAGIC (0x0ULL)
|
||||
|
||||
/* Capability bits. */
|
||||
#define NTDB_CAP_TYPE_MASK 0x1FFFFFFFFFFFFFFFULL
|
||||
#define NTDB_CAP_NOCHECK 0x8000000000000000ULL
|
||||
#define NTDB_CAP_NOWRITE 0x4000000000000000ULL
|
||||
#define NTDB_CAP_NOOPEN 0x2000000000000000ULL
|
||||
|
||||
#define NTDB_OFF_IS_ERR(off) unlikely(off >= (ntdb_off_t)(long)NTDB_ERR_LAST)
|
||||
#define NTDB_OFF_TO_ERR(off) ((enum NTDB_ERROR)(long)(off))
|
||||
#define NTDB_ERR_TO_OFF(ecode) ((ntdb_off_t)(long)(ecode))
|
||||
|
||||
/* Packing errors into pointers and v.v. */
|
||||
#define NTDB_PTR_IS_ERR(ptr) \
|
||||
unlikely((unsigned long)(ptr) >= (unsigned long)NTDB_ERR_LAST)
|
||||
#define NTDB_PTR_ERR(p) ((enum NTDB_ERROR)(long)(p))
|
||||
#define NTDB_ERR_PTR(err) ((void *)(long)(err))
|
||||
|
||||
/* Common case of returning true, false or -ve error. */
|
||||
typedef int ntdb_bool_err;
|
||||
|
||||
/* Prevent others from opening the file. */
|
||||
#define NTDB_OPEN_LOCK 0
|
||||
/* Expanding file. */
|
||||
#define NTDB_EXPANSION_LOCK 2
|
||||
/* Doing a transaction. */
|
||||
#define NTDB_TRANSACTION_LOCK 8
|
||||
/* Hash chain locks. */
|
||||
#define NTDB_HASH_LOCK_START 64
|
||||
|
||||
/* Range for hash locks. */
|
||||
#define NTDB_HASH_LOCK_RANGE_BITS 30
|
||||
#define NTDB_HASH_LOCK_RANGE (1 << NTDB_HASH_LOCK_RANGE_BITS)
|
||||
|
||||
/* We have 1024 entries in the top level. */
|
||||
#define NTDB_TOPLEVEL_HASH_BITS 10
|
||||
/* And 64 entries in each sub-level: thus 64 bits exactly after 9 levels. */
|
||||
#define NTDB_SUBLEVEL_HASH_BITS 6
|
||||
/* And 8 entries in each group, ie 8 groups per sublevel. */
|
||||
#define NTDB_HASH_GROUP_BITS 3
|
||||
/* This is currently 10: beyond this we chain. */
|
||||
#define NTDB_MAX_LEVELS (1+(64-NTDB_TOPLEVEL_HASH_BITS) / NTDB_SUBLEVEL_HASH_BITS)
|
||||
|
||||
/* Extend file by least 100 times larger than needed. */
|
||||
#define NTDB_EXTENSION_FACTOR 100
|
||||
|
||||
/* We steal bits from the offsets to store hash info. */
|
||||
#define NTDB_OFF_HASH_GROUP_MASK ((1ULL << NTDB_HASH_GROUP_BITS) - 1)
|
||||
/* We steal this many upper bits, giving a maximum offset of 64 exabytes. */
|
||||
#define NTDB_OFF_UPPER_STEAL 8
|
||||
#define NTDB_OFF_UPPER_STEAL_EXTRA 7
|
||||
/* The bit number where we store extra hash bits. */
|
||||
#define NTDB_OFF_HASH_EXTRA_BIT 57
|
||||
#define NTDB_OFF_UPPER_STEAL_SUBHASH_BIT 56
|
||||
|
||||
/* Additional features we understand. Currently: none. */
|
||||
#define NTDB_FEATURE_MASK ((uint64_t)0)
|
||||
|
||||
/* The bit number where we store the extra hash bits. */
|
||||
/* Convenience mask to get actual offset. */
|
||||
#define NTDB_OFF_MASK \
|
||||
(((1ULL << (64 - NTDB_OFF_UPPER_STEAL)) - 1) - NTDB_OFF_HASH_GROUP_MASK)
|
||||
|
||||
/* How many buckets in a free list: see size_to_bucket(). */
|
||||
#define NTDB_FREE_BUCKETS (64 - NTDB_OFF_UPPER_STEAL)
|
||||
|
||||
/* We have to be able to fit a free record here. */
|
||||
#define NTDB_MIN_DATA_LEN \
|
||||
(sizeof(struct ntdb_free_record) - sizeof(struct ntdb_used_record))
|
||||
|
||||
/* Indicates this entry is not on an flist (can happen during coalescing) */
|
||||
#define NTDB_FTABLE_NONE ((1ULL << NTDB_OFF_UPPER_STEAL) - 1)
|
||||
|
||||
struct ntdb_used_record {
|
||||
/* For on-disk compatibility, we avoid bitfields:
|
||||
magic: 16, (highest)
|
||||
key_len_bits: 5,
|
||||
extra_padding: 32
|
||||
hash_bits: 11
|
||||
*/
|
||||
uint64_t magic_and_meta;
|
||||
/* The bottom key_len_bits*2 are key length, rest is data length. */
|
||||
uint64_t key_and_data_len;
|
||||
};
|
||||
|
||||
static inline unsigned rec_key_bits(const struct ntdb_used_record *r)
|
||||
{
|
||||
return ((r->magic_and_meta >> 43) & ((1 << 5)-1)) * 2;
|
||||
}
|
||||
|
||||
static inline uint64_t rec_key_length(const struct ntdb_used_record *r)
|
||||
{
|
||||
return r->key_and_data_len & ((1ULL << rec_key_bits(r)) - 1);
|
||||
}
|
||||
|
||||
static inline uint64_t rec_data_length(const struct ntdb_used_record *r)
|
||||
{
|
||||
return r->key_and_data_len >> rec_key_bits(r);
|
||||
}
|
||||
|
||||
static inline uint64_t rec_extra_padding(const struct ntdb_used_record *r)
|
||||
{
|
||||
return (r->magic_and_meta >> 11) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static inline uint32_t rec_hash(const struct ntdb_used_record *r)
|
||||
{
|
||||
return r->magic_and_meta & ((1 << 11) - 1);
|
||||
}
|
||||
|
||||
static inline uint16_t rec_magic(const struct ntdb_used_record *r)
|
||||
{
|
||||
return (r->magic_and_meta >> 48);
|
||||
}
|
||||
|
||||
struct ntdb_free_record {
|
||||
uint64_t magic_and_prev; /* NTDB_OFF_UPPER_STEAL bits magic, then prev */
|
||||
uint64_t ftable_and_len; /* Len not counting these two fields. */
|
||||
/* This is why the minimum record size is 8 bytes. */
|
||||
uint64_t next;
|
||||
};
|
||||
|
||||
static inline uint64_t frec_prev(const struct ntdb_free_record *f)
|
||||
{
|
||||
return f->magic_and_prev & ((1ULL << (64 - NTDB_OFF_UPPER_STEAL)) - 1);
|
||||
}
|
||||
|
||||
static inline uint64_t frec_magic(const struct ntdb_free_record *f)
|
||||
{
|
||||
return f->magic_and_prev >> (64 - NTDB_OFF_UPPER_STEAL);
|
||||
}
|
||||
|
||||
static inline uint64_t frec_len(const struct ntdb_free_record *f)
|
||||
{
|
||||
return f->ftable_and_len & ((1ULL << (64 - NTDB_OFF_UPPER_STEAL))-1);
|
||||
}
|
||||
|
||||
static inline unsigned frec_ftable(const struct ntdb_free_record *f)
|
||||
{
|
||||
return f->ftable_and_len >> (64 - NTDB_OFF_UPPER_STEAL);
|
||||
}
|
||||
|
||||
struct ntdb_recovery_record {
|
||||
uint64_t magic;
|
||||
/* Length of record (add this header to get total length). */
|
||||
uint64_t max_len;
|
||||
/* Length used. */
|
||||
uint64_t len;
|
||||
/* Old length of file before transaction. */
|
||||
uint64_t eof;
|
||||
};
|
||||
|
||||
/* If we bottom out of the subhashes, we chain. */
|
||||
struct ntdb_chain {
|
||||
ntdb_off_t rec[1 << NTDB_HASH_GROUP_BITS];
|
||||
ntdb_off_t next;
|
||||
};
|
||||
|
||||
/* this is stored at the front of every database */
|
||||
struct ntdb_header {
|
||||
char magic_food[64]; /* for /etc/magic */
|
||||
/* FIXME: Make me 32 bit? */
|
||||
uint64_t version; /* version of the code */
|
||||
uint64_t hash_test; /* result of hashing HASH_MAGIC. */
|
||||
uint64_t hash_seed; /* "random" seed written at creation time. */
|
||||
ntdb_off_t free_table; /* (First) free table. */
|
||||
ntdb_off_t recovery; /* Transaction recovery area. */
|
||||
|
||||
uint64_t features_used; /* Features all writers understand */
|
||||
uint64_t features_offered; /* Features offered */
|
||||
|
||||
uint64_t seqnum; /* Sequence number for NTDB_SEQNUM */
|
||||
|
||||
ntdb_off_t capabilities; /* Optional linked list of capabilities. */
|
||||
ntdb_off_t reserved[22];
|
||||
|
||||
/* Top level hash table. */
|
||||
ntdb_off_t hashtable[1ULL << NTDB_TOPLEVEL_HASH_BITS];
|
||||
};
|
||||
|
||||
struct ntdb_freetable {
|
||||
struct ntdb_used_record hdr;
|
||||
ntdb_off_t next;
|
||||
ntdb_off_t buckets[NTDB_FREE_BUCKETS];
|
||||
};
|
||||
|
||||
struct ntdb_capability {
|
||||
struct ntdb_used_record hdr;
|
||||
ntdb_off_t type;
|
||||
ntdb_off_t next;
|
||||
/* ... */
|
||||
};
|
||||
|
||||
/* Information about a particular (locked) hash entry. */
|
||||
struct hash_info {
|
||||
/* Full hash value of entry. */
|
||||
uint64_t h;
|
||||
/* Start and length of lock acquired. */
|
||||
ntdb_off_t hlock_start;
|
||||
ntdb_len_t hlock_range;
|
||||
/* Start of hash group. */
|
||||
ntdb_off_t group_start;
|
||||
/* Bucket we belong in. */
|
||||
unsigned int home_bucket;
|
||||
/* Bucket we (or an empty space) were found in. */
|
||||
unsigned int found_bucket;
|
||||
/* How many bits of the hash are already used. */
|
||||
unsigned int hash_used;
|
||||
/* Current working group. */
|
||||
ntdb_off_t group[1 << NTDB_HASH_GROUP_BITS];
|
||||
};
|
||||
|
||||
struct traverse_info {
|
||||
struct traverse_level {
|
||||
ntdb_off_t hashtable;
|
||||
/* We ignore groups here, and treat it as a big array. */
|
||||
unsigned entry;
|
||||
unsigned int total_buckets;
|
||||
} levels[NTDB_MAX_LEVELS + 1];
|
||||
unsigned int num_levels;
|
||||
unsigned int toplevel_group;
|
||||
/* This makes delete-everything-inside-traverse work as expected. */
|
||||
ntdb_off_t prev;
|
||||
};
|
||||
|
||||
enum ntdb_lock_flags {
|
||||
/* WAIT == F_SETLKW, NOWAIT == F_SETLK */
|
||||
NTDB_LOCK_NOWAIT = 0,
|
||||
NTDB_LOCK_WAIT = 1,
|
||||
/* If set, don't log an error on failure. */
|
||||
NTDB_LOCK_PROBE = 2,
|
||||
/* If set, don't check for recovery (used by recovery code). */
|
||||
NTDB_LOCK_NOCHECK = 4,
|
||||
};
|
||||
|
||||
struct ntdb_lock {
|
||||
struct ntdb_context *owner;
|
||||
off_t off;
|
||||
uint32_t count;
|
||||
uint32_t ltype;
|
||||
};
|
||||
|
||||
/* This is only needed for ntdb_access_commit, but used everywhere to
|
||||
* simplify. */
|
||||
struct ntdb_access_hdr {
|
||||
struct ntdb_access_hdr *next;
|
||||
ntdb_off_t off;
|
||||
ntdb_len_t len;
|
||||
bool convert;
|
||||
};
|
||||
|
||||
struct ntdb_file {
|
||||
/* How many are sharing us? */
|
||||
unsigned int refcnt;
|
||||
|
||||
/* Mmap (if any), or malloc (for NTDB_INTERNAL). */
|
||||
void *map_ptr;
|
||||
|
||||
/* How much space has been mapped (<= current file size) */
|
||||
ntdb_len_t map_size;
|
||||
|
||||
/* The file descriptor (-1 for NTDB_INTERNAL). */
|
||||
int fd;
|
||||
|
||||
/* Lock information */
|
||||
pid_t locker;
|
||||
struct ntdb_lock allrecord_lock;
|
||||
size_t num_lockrecs;
|
||||
struct ntdb_lock *lockrecs;
|
||||
|
||||
/* Identity of this file. */
|
||||
dev_t device;
|
||||
ino_t inode;
|
||||
};
|
||||
|
||||
struct ntdb_methods {
|
||||
enum NTDB_ERROR (*tread)(struct ntdb_context *, ntdb_off_t, void *,
|
||||
ntdb_len_t);
|
||||
enum NTDB_ERROR (*twrite)(struct ntdb_context *, ntdb_off_t, const void *,
|
||||
ntdb_len_t);
|
||||
enum NTDB_ERROR (*oob)(struct ntdb_context *, ntdb_off_t, ntdb_len_t, bool);
|
||||
enum NTDB_ERROR (*expand_file)(struct ntdb_context *, ntdb_len_t);
|
||||
void *(*direct)(struct ntdb_context *, ntdb_off_t, size_t, bool);
|
||||
};
|
||||
|
||||
/*
|
||||
internal prototypes
|
||||
*/
|
||||
/* hash.c: */
|
||||
uint64_t ntdb_jenkins_hash(const void *key, size_t length, uint64_t seed,
|
||||
void *unused);
|
||||
|
||||
enum NTDB_ERROR first_in_hash(struct ntdb_context *ntdb,
|
||||
struct traverse_info *tinfo,
|
||||
NTDB_DATA *kbuf, size_t *dlen);
|
||||
|
||||
enum NTDB_ERROR next_in_hash(struct ntdb_context *ntdb,
|
||||
struct traverse_info *tinfo,
|
||||
NTDB_DATA *kbuf, size_t *dlen);
|
||||
|
||||
/* Hash random memory. */
|
||||
uint64_t ntdb_hash(struct ntdb_context *ntdb, const void *ptr, size_t len);
|
||||
|
||||
/* Hash on disk. */
|
||||
uint64_t hash_record(struct ntdb_context *ntdb, ntdb_off_t off);
|
||||
|
||||
/* Find and lock a hash entry (or where it would be). */
|
||||
ntdb_off_t find_and_lock(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key,
|
||||
int ltype,
|
||||
struct hash_info *h,
|
||||
struct ntdb_used_record *rec,
|
||||
struct traverse_info *tinfo);
|
||||
|
||||
enum NTDB_ERROR replace_in_hash(struct ntdb_context *ntdb,
|
||||
struct hash_info *h,
|
||||
ntdb_off_t new_off);
|
||||
|
||||
enum NTDB_ERROR add_to_hash(struct ntdb_context *ntdb, struct hash_info *h,
|
||||
ntdb_off_t new_off);
|
||||
|
||||
enum NTDB_ERROR delete_from_hash(struct ntdb_context *ntdb, struct hash_info *h);
|
||||
|
||||
/* For ntdb_check */
|
||||
bool is_subhash(ntdb_off_t val);
|
||||
enum NTDB_ERROR unknown_capability(struct ntdb_context *ntdb, const char *caller,
|
||||
ntdb_off_t type);
|
||||
|
||||
/* free.c: */
|
||||
enum NTDB_ERROR ntdb_ftable_init(struct ntdb_context *ntdb);
|
||||
|
||||
/* check.c needs these to iterate through free lists. */
|
||||
ntdb_off_t first_ftable(struct ntdb_context *ntdb);
|
||||
ntdb_off_t next_ftable(struct ntdb_context *ntdb, ntdb_off_t ftable);
|
||||
|
||||
/* This returns space or -ve error number. */
|
||||
ntdb_off_t alloc(struct ntdb_context *ntdb, size_t keylen, size_t datalen,
|
||||
uint64_t hash, unsigned magic, bool growing);
|
||||
|
||||
/* Put this record in a free list. */
|
||||
enum NTDB_ERROR add_free_record(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_len_t len_with_header,
|
||||
enum ntdb_lock_flags waitflag,
|
||||
bool coalesce_ok);
|
||||
|
||||
/* Set up header for a used/ftable/htable/chain/capability record. */
|
||||
enum NTDB_ERROR set_header(struct ntdb_context *ntdb,
|
||||
struct ntdb_used_record *rec,
|
||||
unsigned magic, uint64_t keylen, uint64_t datalen,
|
||||
uint64_t actuallen, unsigned hashlow);
|
||||
|
||||
/* Used by ntdb_check to verify. */
|
||||
unsigned int size_to_bucket(ntdb_len_t data_len);
|
||||
ntdb_off_t bucket_off(ntdb_off_t ftable_off, unsigned bucket);
|
||||
|
||||
/* Used by ntdb_summary */
|
||||
ntdb_off_t dead_space(struct ntdb_context *ntdb, ntdb_off_t off);
|
||||
|
||||
/* Adjust expansion, used by create_recovery_area */
|
||||
ntdb_off_t ntdb_expand_adjust(ntdb_off_t map_size, ntdb_off_t size);
|
||||
|
||||
/* io.c: */
|
||||
/* Initialize ntdb->methods. */
|
||||
void ntdb_io_init(struct ntdb_context *ntdb);
|
||||
|
||||
/* Convert endian of the buffer if required. */
|
||||
void *ntdb_convert(const struct ntdb_context *ntdb, void *buf, ntdb_len_t size);
|
||||
|
||||
/* Unmap and try to map the ntdb. */
|
||||
void ntdb_munmap(struct ntdb_file *file);
|
||||
enum NTDB_ERROR ntdb_mmap(struct ntdb_context *ntdb);
|
||||
|
||||
/* Either alloc a copy, or give direct access. Release frees or noop. */
|
||||
const void *ntdb_access_read(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_len_t len, bool convert);
|
||||
void *ntdb_access_write(struct ntdb_context *ntdb,
|
||||
ntdb_off_t off, ntdb_len_t len, bool convert);
|
||||
|
||||
/* Release result of ntdb_access_read/write. */
|
||||
void ntdb_access_release(struct ntdb_context *ntdb, const void *p);
|
||||
/* Commit result of ntdb_acces_write. */
|
||||
enum NTDB_ERROR ntdb_access_commit(struct ntdb_context *ntdb, void *p);
|
||||
|
||||
/* Convenience routine to get an offset. */
|
||||
ntdb_off_t ntdb_read_off(struct ntdb_context *ntdb, ntdb_off_t off);
|
||||
|
||||
/* Write an offset at an offset. */
|
||||
enum NTDB_ERROR ntdb_write_off(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
ntdb_off_t val);
|
||||
|
||||
/* Clear an ondisk area. */
|
||||
enum NTDB_ERROR zero_out(struct ntdb_context *ntdb, ntdb_off_t off, ntdb_len_t len);
|
||||
|
||||
/* Return a non-zero offset between >= start < end in this array (or end). */
|
||||
ntdb_off_t ntdb_find_nonzero_off(struct ntdb_context *ntdb,
|
||||
ntdb_off_t base,
|
||||
uint64_t start,
|
||||
uint64_t end);
|
||||
|
||||
/* Return a zero offset in this array, or num. */
|
||||
ntdb_off_t ntdb_find_zero_off(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
uint64_t num);
|
||||
|
||||
/* Allocate and make a copy of some offset. */
|
||||
void *ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset, ntdb_len_t len);
|
||||
|
||||
/* Writes a converted copy of a record. */
|
||||
enum NTDB_ERROR ntdb_write_convert(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
const void *rec, size_t len);
|
||||
|
||||
/* Reads record and converts it */
|
||||
enum NTDB_ERROR ntdb_read_convert(struct ntdb_context *ntdb, ntdb_off_t off,
|
||||
void *rec, size_t len);
|
||||
|
||||
/* Bump the seqnum (caller checks for ntdb->flags & NTDB_SEQNUM) */
|
||||
void ntdb_inc_seqnum(struct ntdb_context *ntdb);
|
||||
|
||||
/* lock.c: */
|
||||
/* Print message because another ntdb owns a lock we want. */
|
||||
enum NTDB_ERROR owner_conflict(struct ntdb_context *ntdb, const char *call);
|
||||
|
||||
/* If we fork, we no longer really own locks. */
|
||||
bool check_lock_pid(struct ntdb_context *ntdb, const char *call, bool log);
|
||||
|
||||
/* Lock/unlock a range of hashes. */
|
||||
enum NTDB_ERROR ntdb_lock_hashes(struct ntdb_context *ntdb,
|
||||
ntdb_off_t hash_lock, ntdb_len_t hash_range,
|
||||
int ltype, enum ntdb_lock_flags waitflag);
|
||||
enum NTDB_ERROR ntdb_unlock_hashes(struct ntdb_context *ntdb,
|
||||
ntdb_off_t hash_lock,
|
||||
ntdb_len_t hash_range, int ltype);
|
||||
|
||||
/* For closing the file. */
|
||||
void ntdb_lock_cleanup(struct ntdb_context *ntdb);
|
||||
|
||||
/* Lock/unlock a particular free bucket. */
|
||||
enum NTDB_ERROR ntdb_lock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off,
|
||||
enum ntdb_lock_flags waitflag);
|
||||
void ntdb_unlock_free_bucket(struct ntdb_context *ntdb, ntdb_off_t b_off);
|
||||
|
||||
/* Serialize transaction start. */
|
||||
enum NTDB_ERROR ntdb_transaction_lock(struct ntdb_context *ntdb, int ltype);
|
||||
void ntdb_transaction_unlock(struct ntdb_context *ntdb, int ltype);
|
||||
|
||||
/* Do we have any hash locks (ie. via ntdb_chainlock) ? */
|
||||
bool ntdb_has_hash_locks(struct ntdb_context *ntdb);
|
||||
|
||||
/* Lock entire database. */
|
||||
enum NTDB_ERROR ntdb_allrecord_lock(struct ntdb_context *ntdb, int ltype,
|
||||
enum ntdb_lock_flags flags, bool upgradable);
|
||||
void ntdb_allrecord_unlock(struct ntdb_context *ntdb, int ltype);
|
||||
enum NTDB_ERROR ntdb_allrecord_upgrade(struct ntdb_context *ntdb, off_t start);
|
||||
|
||||
/* Serialize db open. */
|
||||
enum NTDB_ERROR ntdb_lock_open(struct ntdb_context *ntdb,
|
||||
int ltype, enum ntdb_lock_flags flags);
|
||||
void ntdb_unlock_open(struct ntdb_context *ntdb, int ltype);
|
||||
bool ntdb_has_open_lock(struct ntdb_context *ntdb);
|
||||
|
||||
/* Serialize db expand. */
|
||||
enum NTDB_ERROR ntdb_lock_expand(struct ntdb_context *ntdb, int ltype);
|
||||
void ntdb_unlock_expand(struct ntdb_context *ntdb, int ltype);
|
||||
bool ntdb_has_expansion_lock(struct ntdb_context *ntdb);
|
||||
|
||||
/* If it needs recovery, grab all the locks and do it. */
|
||||
enum NTDB_ERROR ntdb_lock_and_recover(struct ntdb_context *ntdb);
|
||||
|
||||
/* Default lock and unlock functions. */
|
||||
int ntdb_fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag, void *);
|
||||
int ntdb_fcntl_unlock(int fd, int rw, off_t off, off_t len, void *);
|
||||
|
||||
/* transaction.c: */
|
||||
enum NTDB_ERROR ntdb_transaction_recover(struct ntdb_context *ntdb);
|
||||
ntdb_bool_err ntdb_needs_recovery(struct ntdb_context *ntdb);
|
||||
|
||||
struct ntdb_context {
|
||||
/* Single list of all TDBs, to detect multiple opens. */
|
||||
struct ntdb_context *next;
|
||||
|
||||
/* Filename of the database. */
|
||||
const char *name;
|
||||
|
||||
/* Logging function */
|
||||
void (*log_fn)(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data);
|
||||
void *log_data;
|
||||
|
||||
/* Open flags passed to ntdb_open. */
|
||||
int open_flags;
|
||||
|
||||
/* low level (fnctl) lock functions. */
|
||||
int (*lock_fn)(int fd, int rw, off_t off, off_t len, bool w, void *);
|
||||
int (*unlock_fn)(int fd, int rw, off_t off, off_t len, void *);
|
||||
void *lock_data;
|
||||
|
||||
/* the ntdb flags passed to ntdb_open. */
|
||||
uint32_t flags;
|
||||
|
||||
/* Our statistics. */
|
||||
struct ntdb_attribute_stats stats;
|
||||
|
||||
/* The actual file information */
|
||||
struct ntdb_file *file;
|
||||
|
||||
/* Hash function. */
|
||||
uint64_t (*hash_fn)(const void *key, size_t len, uint64_t seed, void *);
|
||||
void *hash_data;
|
||||
uint64_t hash_seed;
|
||||
|
||||
/* Our open hook, if any. */
|
||||
enum NTDB_ERROR (*openhook)(int fd, void *data);
|
||||
void *openhook_data;
|
||||
|
||||
/* Last error we returned. */
|
||||
enum NTDB_ERROR last_error;
|
||||
|
||||
/* Are we accessing directly? (debugging check). */
|
||||
int direct_access;
|
||||
|
||||
/* Set if we are in a transaction. */
|
||||
struct ntdb_transaction *transaction;
|
||||
|
||||
/* What free table are we using? */
|
||||
ntdb_off_t ftable_off;
|
||||
unsigned int ftable;
|
||||
|
||||
/* IO methods: changes for transactions. */
|
||||
const struct ntdb_methods *io;
|
||||
|
||||
/* Direct access information */
|
||||
struct ntdb_access_hdr *access;
|
||||
};
|
||||
|
||||
/* ntdb.c: */
|
||||
enum NTDB_ERROR COLD PRINTF_FMT(4, 5)
|
||||
ntdb_logerr(struct ntdb_context *ntdb,
|
||||
enum NTDB_ERROR ecode,
|
||||
enum ntdb_log_level level,
|
||||
const char *fmt, ...);
|
||||
|
||||
#ifdef NTDB_TRACE
|
||||
void ntdb_trace(struct ntdb_context *ntdb, const char *op);
|
||||
void ntdb_trace_seqnum(struct ntdb_context *ntdb, uint32_t seqnum, const char *op);
|
||||
void ntdb_trace_open(struct ntdb_context *ntdb, const char *op,
|
||||
unsigned hash_size, unsigned ntdb_flags, unsigned open_flags);
|
||||
void ntdb_trace_ret(struct ntdb_context *ntdb, const char *op, int ret);
|
||||
void ntdb_trace_retrec(struct ntdb_context *ntdb, const char *op, NTDB_DATA ret);
|
||||
void ntdb_trace_1rec(struct ntdb_context *ntdb, const char *op,
|
||||
NTDB_DATA rec);
|
||||
void ntdb_trace_1rec_ret(struct ntdb_context *ntdb, const char *op,
|
||||
NTDB_DATA rec, int ret);
|
||||
void ntdb_trace_1rec_retrec(struct ntdb_context *ntdb, const char *op,
|
||||
NTDB_DATA rec, NTDB_DATA ret);
|
||||
void ntdb_trace_2rec_flag_ret(struct ntdb_context *ntdb, const char *op,
|
||||
NTDB_DATA rec1, NTDB_DATA rec2, unsigned flag,
|
||||
int ret);
|
||||
void ntdb_trace_2rec_retrec(struct ntdb_context *ntdb, const char *op,
|
||||
NTDB_DATA rec1, NTDB_DATA rec2, NTDB_DATA ret);
|
||||
#else
|
||||
#define ntdb_trace(ntdb, op)
|
||||
#define ntdb_trace_seqnum(ntdb, seqnum, op)
|
||||
#define ntdb_trace_open(ntdb, op, hash_size, ntdb_flags, open_flags)
|
||||
#define ntdb_trace_ret(ntdb, op, ret)
|
||||
#define ntdb_trace_retrec(ntdb, op, ret)
|
||||
#define ntdb_trace_1rec(ntdb, op, rec)
|
||||
#define ntdb_trace_1rec_ret(ntdb, op, rec, ret)
|
||||
#define ntdb_trace_1rec_retrec(ntdb, op, rec, ret)
|
||||
#define ntdb_trace_2rec_flag_ret(ntdb, op, rec1, rec2, flag, ret)
|
||||
#define ntdb_trace_2rec_retrec(ntdb, op, rec1, rec2, ret)
|
||||
#endif /* !NTDB_TRACE */
|
||||
|
||||
#endif
|
591
lib/ntdb/pyntdb.c
Normal file
591
lib/ntdb/pyntdb.c
Normal file
@ -0,0 +1,591 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Python interface to ntdb. Simply modified from tdb version.
|
||||
|
||||
Copyright (C) 2004-2006 Tim Potter <tpot@samba.org>
|
||||
Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
|
||||
Copyright (C) 2011 Rusty Russell <rusty@rustcorp.com.au>
|
||||
|
||||
** NOTE! The following LGPL license applies to the ntdb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include "replace.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
#ifndef Py_RETURN_NONE
|
||||
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
|
||||
#endif
|
||||
|
||||
/* Include ntdb headers */
|
||||
#include <ntdb.h>
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
struct ntdb_context *ctx;
|
||||
bool closed;
|
||||
} PyNtdbObject;
|
||||
|
||||
staticforward PyTypeObject PyNtdb;
|
||||
|
||||
static void PyErr_SetTDBError(enum NTDB_ERROR e)
|
||||
{
|
||||
PyErr_SetObject(PyExc_RuntimeError,
|
||||
Py_BuildValue("(i,s)", e, ntdb_errorstr(e)));
|
||||
}
|
||||
|
||||
static NTDB_DATA PyString_AsNtdb_Data(PyObject *data)
|
||||
{
|
||||
NTDB_DATA ret;
|
||||
ret.dptr = (unsigned char *)PyString_AsString(data);
|
||||
ret.dsize = PyString_Size(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *PyString_FromNtdb_Data(NTDB_DATA data)
|
||||
{
|
||||
PyObject *ret = PyString_FromStringAndSize((const char *)data.dptr,
|
||||
data.dsize);
|
||||
free(data.dptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define PyErr_NTDB_ERROR_IS_ERR_RAISE(ret) \
|
||||
if (ret != NTDB_SUCCESS) { \
|
||||
PyErr_SetTDBError(ret); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
static void stderr_log(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data)
|
||||
{
|
||||
fprintf(stderr, "%s:%s:%s\n",
|
||||
ntdb_name(ntdb), ntdb_errorstr(ecode), message);
|
||||
}
|
||||
|
||||
static PyObject *py_ntdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
char *name = NULL;
|
||||
int ntdb_flags = NTDB_DEFAULT, flags = O_RDWR, mode = 0600;
|
||||
struct ntdb_context *ctx;
|
||||
PyNtdbObject *ret;
|
||||
union ntdb_attribute logattr;
|
||||
const char *kwnames[] = { "name", "ntdb_flags", "flags", "mode", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siii", cast_const2(char **, kwnames), &name, &ntdb_flags, &flags, &mode))
|
||||
return NULL;
|
||||
|
||||
if (name == NULL) {
|
||||
ntdb_flags |= NTDB_INTERNAL;
|
||||
}
|
||||
|
||||
logattr.log.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
logattr.log.base.next = NULL;
|
||||
logattr.log.fn = stderr_log;
|
||||
ctx = ntdb_open(name, ntdb_flags, flags, mode, &logattr);
|
||||
if (ctx == NULL) {
|
||||
PyErr_SetFromErrno(PyExc_IOError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = PyObject_New(PyNtdbObject, &PyNtdb);
|
||||
if (!ret) {
|
||||
ntdb_close(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->ctx = ctx;
|
||||
ret->closed = false;
|
||||
return (PyObject *)ret;
|
||||
}
|
||||
|
||||
static PyObject *obj_transaction_cancel(PyNtdbObject *self)
|
||||
{
|
||||
ntdb_transaction_cancel(self->ctx);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_transaction_commit(PyNtdbObject *self)
|
||||
{
|
||||
enum NTDB_ERROR ret = ntdb_transaction_commit(self->ctx);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_transaction_prepare_commit(PyNtdbObject *self)
|
||||
{
|
||||
enum NTDB_ERROR ret = ntdb_transaction_prepare_commit(self->ctx);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_transaction_start(PyNtdbObject *self)
|
||||
{
|
||||
enum NTDB_ERROR ret = ntdb_transaction_start(self->ctx);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_lockall(PyNtdbObject *self)
|
||||
{
|
||||
enum NTDB_ERROR ret = ntdb_lockall(self->ctx);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_unlockall(PyNtdbObject *self)
|
||||
{
|
||||
ntdb_unlockall(self->ctx);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_lockall_read(PyNtdbObject *self)
|
||||
{
|
||||
enum NTDB_ERROR ret = ntdb_lockall_read(self->ctx);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_unlockall_read(PyNtdbObject *self)
|
||||
{
|
||||
ntdb_unlockall_read(self->ctx);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_close(PyNtdbObject *self)
|
||||
{
|
||||
int ret;
|
||||
if (self->closed)
|
||||
Py_RETURN_NONE;
|
||||
ret = ntdb_close(self->ctx);
|
||||
self->closed = true;
|
||||
if (ret != 0) {
|
||||
PyErr_SetTDBError(NTDB_ERR_IO);
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_get(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
NTDB_DATA key, data;
|
||||
PyObject *py_key;
|
||||
enum NTDB_ERROR ret;
|
||||
if (!PyArg_ParseTuple(args, "O", &py_key))
|
||||
return NULL;
|
||||
|
||||
key = PyString_AsNtdb_Data(py_key);
|
||||
ret = ntdb_fetch(self->ctx, key, &data);
|
||||
if (ret == NTDB_ERR_NOEXIST)
|
||||
Py_RETURN_NONE;
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
return PyString_FromNtdb_Data(data);
|
||||
}
|
||||
|
||||
static PyObject *obj_append(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
NTDB_DATA key, data;
|
||||
PyObject *py_key, *py_data;
|
||||
enum NTDB_ERROR ret;
|
||||
if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data))
|
||||
return NULL;
|
||||
|
||||
key = PyString_AsNtdb_Data(py_key);
|
||||
data = PyString_AsNtdb_Data(py_data);
|
||||
|
||||
ret = ntdb_append(self->ctx, key, data);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_firstkey(PyNtdbObject *self)
|
||||
{
|
||||
enum NTDB_ERROR ret;
|
||||
NTDB_DATA key;
|
||||
|
||||
ret = ntdb_firstkey(self->ctx, &key);
|
||||
if (ret == NTDB_ERR_NOEXIST)
|
||||
Py_RETURN_NONE;
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
|
||||
return PyString_FromNtdb_Data(key);
|
||||
}
|
||||
|
||||
static PyObject *obj_nextkey(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
NTDB_DATA key;
|
||||
PyObject *py_key;
|
||||
enum NTDB_ERROR ret;
|
||||
if (!PyArg_ParseTuple(args, "O", &py_key))
|
||||
return NULL;
|
||||
|
||||
/* Malloc here, since ntdb_nextkey frees. */
|
||||
key.dsize = PyString_Size(py_key);
|
||||
key.dptr = malloc(key.dsize);
|
||||
memcpy(key.dptr, PyString_AsString(py_key), key.dsize);
|
||||
|
||||
ret = ntdb_nextkey(self->ctx, &key);
|
||||
if (ret == NTDB_ERR_NOEXIST)
|
||||
Py_RETURN_NONE;
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
|
||||
return PyString_FromNtdb_Data(key);
|
||||
}
|
||||
|
||||
static PyObject *obj_delete(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
NTDB_DATA key;
|
||||
PyObject *py_key;
|
||||
enum NTDB_ERROR ret;
|
||||
if (!PyArg_ParseTuple(args, "O", &py_key))
|
||||
return NULL;
|
||||
|
||||
key = PyString_AsNtdb_Data(py_key);
|
||||
ret = ntdb_delete(self->ctx, key);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_has_key(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
NTDB_DATA key;
|
||||
PyObject *py_key;
|
||||
if (!PyArg_ParseTuple(args, "O", &py_key))
|
||||
return NULL;
|
||||
|
||||
key = PyString_AsNtdb_Data(py_key);
|
||||
if (ntdb_exists(self->ctx, key))
|
||||
return Py_True;
|
||||
if (ntdb_error(self->ctx) != NTDB_ERR_NOEXIST)
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ntdb_error(self->ctx));
|
||||
return Py_False;
|
||||
}
|
||||
|
||||
static PyObject *obj_store(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
NTDB_DATA key, value;
|
||||
enum NTDB_ERROR ret;
|
||||
int flag = NTDB_REPLACE;
|
||||
PyObject *py_key, *py_value;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag))
|
||||
return NULL;
|
||||
|
||||
key = PyString_AsNtdb_Data(py_key);
|
||||
value = PyString_AsNtdb_Data(py_value);
|
||||
|
||||
ret = ntdb_store(self->ctx, key, value, flag);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_add_flag(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
unsigned flag;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "I", &flag))
|
||||
return NULL;
|
||||
|
||||
ntdb_add_flag(self->ctx, flag);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_remove_flag(PyNtdbObject *self, PyObject *args)
|
||||
{
|
||||
unsigned flag;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "I", &flag))
|
||||
return NULL;
|
||||
|
||||
ntdb_remove_flag(self->ctx, flag);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
NTDB_DATA current;
|
||||
bool end;
|
||||
PyNtdbObject *iteratee;
|
||||
} PyNtdbIteratorObject;
|
||||
|
||||
static PyObject *ntdb_iter_next(PyNtdbIteratorObject *self)
|
||||
{
|
||||
enum NTDB_ERROR e;
|
||||
PyObject *ret;
|
||||
if (self->end)
|
||||
return NULL;
|
||||
ret = PyString_FromStringAndSize((const char *)self->current.dptr,
|
||||
self->current.dsize);
|
||||
e = ntdb_nextkey(self->iteratee->ctx, &self->current);
|
||||
if (e == NTDB_ERR_NOEXIST)
|
||||
self->end = true;
|
||||
else
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(e);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ntdb_iter_dealloc(PyNtdbIteratorObject *self)
|
||||
{
|
||||
Py_DECREF(self->iteratee);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyTypeObject PyNtdbIterator = {
|
||||
.tp_name = "Iterator",
|
||||
.tp_basicsize = sizeof(PyNtdbIteratorObject),
|
||||
.tp_iternext = (iternextfunc)ntdb_iter_next,
|
||||
.tp_dealloc = (destructor)ntdb_iter_dealloc,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_iter = PyObject_SelfIter,
|
||||
};
|
||||
|
||||
static PyObject *ntdb_object_iter(PyNtdbObject *self)
|
||||
{
|
||||
PyNtdbIteratorObject *ret;
|
||||
enum NTDB_ERROR e;
|
||||
|
||||
ret = PyObject_New(PyNtdbIteratorObject, &PyNtdbIterator);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
e = ntdb_firstkey(self->ctx, &ret->current);
|
||||
if (e == NTDB_ERR_NOEXIST) {
|
||||
ret->end = true;
|
||||
} else {
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(e);
|
||||
ret->end = false;
|
||||
}
|
||||
ret->iteratee = self;
|
||||
Py_INCREF(self);
|
||||
return (PyObject *)ret;
|
||||
}
|
||||
|
||||
static PyObject *obj_clear(PyNtdbObject *self)
|
||||
{
|
||||
enum NTDB_ERROR ret = ntdb_wipe_all(self->ctx);
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *obj_enable_seqnum(PyNtdbObject *self)
|
||||
{
|
||||
ntdb_add_flag(self->ctx, NTDB_SEQNUM);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef ntdb_object_methods[] = {
|
||||
{ "transaction_cancel", (PyCFunction)obj_transaction_cancel, METH_NOARGS,
|
||||
"S.transaction_cancel() -> None\n"
|
||||
"Cancel the currently active transaction." },
|
||||
{ "transaction_commit", (PyCFunction)obj_transaction_commit, METH_NOARGS,
|
||||
"S.transaction_commit() -> None\n"
|
||||
"Commit the currently active transaction." },
|
||||
{ "transaction_prepare_commit", (PyCFunction)obj_transaction_prepare_commit, METH_NOARGS,
|
||||
"S.transaction_prepare_commit() -> None\n"
|
||||
"Prepare to commit the currently active transaction" },
|
||||
{ "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS,
|
||||
"S.transaction_start() -> None\n"
|
||||
"Start a new transaction." },
|
||||
{ "lock_all", (PyCFunction)obj_lockall, METH_NOARGS, NULL },
|
||||
{ "unlock_all", (PyCFunction)obj_unlockall, METH_NOARGS, NULL },
|
||||
{ "read_lock_all", (PyCFunction)obj_lockall_read, METH_NOARGS, NULL },
|
||||
{ "read_unlock_all", (PyCFunction)obj_unlockall_read, METH_NOARGS, NULL },
|
||||
{ "close", (PyCFunction)obj_close, METH_NOARGS, NULL },
|
||||
{ "get", (PyCFunction)obj_get, METH_VARARGS, "S.get(key) -> value\n"
|
||||
"Fetch a value." },
|
||||
{ "append", (PyCFunction)obj_append, METH_VARARGS, "S.append(key, value) -> None\n"
|
||||
"Append data to an existing key." },
|
||||
{ "firstkey", (PyCFunction)obj_firstkey, METH_NOARGS, "S.firstkey() -> data\n"
|
||||
"Return the first key in this database." },
|
||||
{ "nextkey", (PyCFunction)obj_nextkey, METH_NOARGS, "S.nextkey(key) -> data\n"
|
||||
"Return the next key in this database." },
|
||||
{ "delete", (PyCFunction)obj_delete, METH_VARARGS, "S.delete(key) -> None\n"
|
||||
"Delete an entry." },
|
||||
{ "has_key", (PyCFunction)obj_has_key, METH_VARARGS, "S.has_key(key) -> None\n"
|
||||
"Check whether key exists in this database." },
|
||||
{ "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None"
|
||||
"Store data." },
|
||||
{ "add_flag", (PyCFunction)obj_add_flag, METH_VARARGS, "S.add_flag(flag) -> None" },
|
||||
{ "remove_flag", (PyCFunction)obj_remove_flag, METH_VARARGS, "S.remove_flag(flag) -> None" },
|
||||
{ "iterkeys", (PyCFunction)ntdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" },
|
||||
{ "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n"
|
||||
"Wipe the entire database." },
|
||||
{ "enable_seqnum", (PyCFunction)obj_enable_seqnum, METH_NOARGS,
|
||||
"S.enable_seqnum() -> None" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static PyObject *obj_get_flags(PyNtdbObject *self, void *closure)
|
||||
{
|
||||
return PyInt_FromLong(ntdb_get_flags(self->ctx));
|
||||
}
|
||||
|
||||
static PyObject *obj_get_filename(PyNtdbObject *self, void *closure)
|
||||
{
|
||||
return PyString_FromString(ntdb_name(self->ctx));
|
||||
}
|
||||
|
||||
static PyObject *obj_get_seqnum(PyNtdbObject *self, void *closure)
|
||||
{
|
||||
return PyInt_FromLong(ntdb_get_seqnum(self->ctx));
|
||||
}
|
||||
|
||||
|
||||
static PyGetSetDef ntdb_object_getsetters[] = {
|
||||
{ cast_const(char *, "flags"), (getter)obj_get_flags, NULL, NULL },
|
||||
{ cast_const(char *, "filename"), (getter)obj_get_filename, NULL,
|
||||
cast_const(char *, "The filename of this NTDB file.")},
|
||||
{ cast_const(char *, "seqnum"), (getter)obj_get_seqnum, NULL, NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static PyObject *ntdb_object_repr(PyNtdbObject *self)
|
||||
{
|
||||
if (ntdb_get_flags(self->ctx) & NTDB_INTERNAL) {
|
||||
return PyString_FromString("Ntdb(<internal>)");
|
||||
} else {
|
||||
return PyString_FromFormat("Ntdb('%s')", ntdb_name(self->ctx));
|
||||
}
|
||||
}
|
||||
|
||||
static void ntdb_object_dealloc(PyNtdbObject *self)
|
||||
{
|
||||
if (!self->closed)
|
||||
ntdb_close(self->ctx);
|
||||
self->ob_type->tp_free(self);
|
||||
}
|
||||
|
||||
static PyObject *obj_getitem(PyNtdbObject *self, PyObject *key)
|
||||
{
|
||||
NTDB_DATA tkey, val;
|
||||
enum NTDB_ERROR ret;
|
||||
|
||||
if (!PyString_Check(key)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected string as key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tkey.dptr = (unsigned char *)PyString_AsString(key);
|
||||
tkey.dsize = PyString_Size(key);
|
||||
|
||||
ret = ntdb_fetch(self->ctx, tkey, &val);
|
||||
if (ret == NTDB_ERR_NOEXIST) {
|
||||
PyErr_SetString(PyExc_KeyError, "No such NTDB entry");
|
||||
return NULL;
|
||||
} else {
|
||||
PyErr_NTDB_ERROR_IS_ERR_RAISE(ret);
|
||||
return PyString_FromNtdb_Data(val);
|
||||
}
|
||||
}
|
||||
|
||||
static int obj_setitem(PyNtdbObject *self, PyObject *key, PyObject *value)
|
||||
{
|
||||
NTDB_DATA tkey, tval;
|
||||
enum NTDB_ERROR ret;
|
||||
if (!PyString_Check(key)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected string as key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tkey = PyString_AsNtdb_Data(key);
|
||||
|
||||
if (value == NULL) {
|
||||
ret = ntdb_delete(self->ctx, tkey);
|
||||
} else {
|
||||
if (!PyString_Check(value)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected string as value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tval = PyString_AsNtdb_Data(value);
|
||||
|
||||
ret = ntdb_store(self->ctx, tkey, tval, NTDB_REPLACE);
|
||||
}
|
||||
|
||||
if (ret != NTDB_SUCCESS) {
|
||||
PyErr_SetTDBError(ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyMappingMethods ntdb_object_mapping = {
|
||||
.mp_subscript = (binaryfunc)obj_getitem,
|
||||
.mp_ass_subscript = (objobjargproc)obj_setitem,
|
||||
};
|
||||
static PyTypeObject PyNtdb = {
|
||||
.tp_name = "ntdb.Ntdb",
|
||||
.tp_basicsize = sizeof(PyNtdbObject),
|
||||
.tp_methods = ntdb_object_methods,
|
||||
.tp_getset = ntdb_object_getsetters,
|
||||
.tp_new = py_ntdb_open,
|
||||
.tp_doc = "A NTDB file",
|
||||
.tp_repr = (reprfunc)ntdb_object_repr,
|
||||
.tp_dealloc = (destructor)ntdb_object_dealloc,
|
||||
.tp_as_mapping = &ntdb_object_mapping,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER,
|
||||
.tp_iter = (getiterfunc)ntdb_object_iter,
|
||||
};
|
||||
|
||||
static PyMethodDef ntdb_methods[] = {
|
||||
{ "open", (PyCFunction)py_ntdb_open, METH_VARARGS|METH_KEYWORDS, "open(name, hash_size=0, ntdb_flags=NTDB_DEFAULT, flags=O_RDWR, mode=0600)\n"
|
||||
"Open a NTDB file." },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
void inittdb(void);
|
||||
void inittdb(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
if (PyType_Ready(&PyNtdb) < 0)
|
||||
return;
|
||||
|
||||
if (PyType_Ready(&PyNtdbIterator) < 0)
|
||||
return;
|
||||
|
||||
m = Py_InitModule3("ntdb", ntdb_methods, "NTDB is a simple key-value database similar to GDBM that supports multiple writers.");
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
PyModule_AddObject(m, "REPLACE", PyInt_FromLong(NTDB_REPLACE));
|
||||
PyModule_AddObject(m, "INSERT", PyInt_FromLong(NTDB_INSERT));
|
||||
PyModule_AddObject(m, "MODIFY", PyInt_FromLong(NTDB_MODIFY));
|
||||
|
||||
PyModule_AddObject(m, "DEFAULT", PyInt_FromLong(NTDB_DEFAULT));
|
||||
PyModule_AddObject(m, "INTERNAL", PyInt_FromLong(NTDB_INTERNAL));
|
||||
PyModule_AddObject(m, "NOLOCK", PyInt_FromLong(NTDB_NOLOCK));
|
||||
PyModule_AddObject(m, "NOMMAP", PyInt_FromLong(NTDB_NOMMAP));
|
||||
PyModule_AddObject(m, "CONVERT", PyInt_FromLong(NTDB_CONVERT));
|
||||
PyModule_AddObject(m, "NOSYNC", PyInt_FromLong(NTDB_NOSYNC));
|
||||
PyModule_AddObject(m, "SEQNUM", PyInt_FromLong(NTDB_SEQNUM));
|
||||
PyModule_AddObject(m, "ALLOW_NESTING", PyInt_FromLong(NTDB_ALLOW_NESTING));
|
||||
|
||||
PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
|
||||
|
||||
PyModule_AddObject(m, "__version__", PyString_FromString(PACKAGE_VERSION));
|
||||
|
||||
Py_INCREF(&PyNtdb);
|
||||
PyModule_AddObject(m, "Ntdb", (PyObject *)&PyNtdb);
|
||||
|
||||
Py_INCREF(&PyNtdbIterator);
|
||||
}
|
@ -47,25 +47,25 @@
|
||||
#define HISTO_WIDTH 70
|
||||
#define HISTO_HEIGHT 20
|
||||
|
||||
static tdb_off_t count_hash(struct tdb_context *tdb,
|
||||
tdb_off_t hash_off, unsigned bits)
|
||||
static ntdb_off_t count_hash(struct ntdb_context *ntdb,
|
||||
ntdb_off_t hash_off, unsigned bits)
|
||||
{
|
||||
const tdb_off_t *h;
|
||||
tdb_off_t count = 0;
|
||||
const ntdb_off_t *h;
|
||||
ntdb_off_t count = 0;
|
||||
unsigned int i;
|
||||
|
||||
h = tdb_access_read(tdb, hash_off, sizeof(*h) << bits, true);
|
||||
if (TDB_PTR_IS_ERR(h)) {
|
||||
return TDB_ERR_TO_OFF(TDB_PTR_ERR(h));
|
||||
h = ntdb_access_read(ntdb, hash_off, sizeof(*h) << bits, true);
|
||||
if (NTDB_PTR_IS_ERR(h)) {
|
||||
return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(h));
|
||||
}
|
||||
for (i = 0; i < (1 << bits); i++)
|
||||
count += (h[i] != 0);
|
||||
|
||||
tdb_access_release(tdb, h);
|
||||
ntdb_access_release(ntdb, h);
|
||||
return count;
|
||||
}
|
||||
|
||||
static enum TDB_ERROR summarize(struct tdb_context *tdb,
|
||||
static enum NTDB_ERROR summarize(struct ntdb_context *ntdb,
|
||||
struct tally *hashes,
|
||||
struct tally *ftables,
|
||||
struct tally *fr,
|
||||
@ -76,39 +76,39 @@ static enum TDB_ERROR summarize(struct tdb_context *tdb,
|
||||
struct tally *chains,
|
||||
size_t *num_caps)
|
||||
{
|
||||
tdb_off_t off;
|
||||
tdb_len_t len;
|
||||
tdb_len_t unc = 0;
|
||||
ntdb_off_t off;
|
||||
ntdb_len_t len;
|
||||
ntdb_len_t unc = 0;
|
||||
|
||||
for (off = sizeof(struct tdb_header);
|
||||
off < tdb->file->map_size;
|
||||
for (off = sizeof(struct ntdb_header);
|
||||
off < ntdb->file->map_size;
|
||||
off += len) {
|
||||
const union {
|
||||
struct tdb_used_record u;
|
||||
struct tdb_free_record f;
|
||||
struct tdb_recovery_record r;
|
||||
struct ntdb_used_record u;
|
||||
struct ntdb_free_record f;
|
||||
struct ntdb_recovery_record r;
|
||||
} *p;
|
||||
/* We might not be able to get the whole thing. */
|
||||
p = tdb_access_read(tdb, off, sizeof(p->f), true);
|
||||
if (TDB_PTR_IS_ERR(p)) {
|
||||
return TDB_PTR_ERR(p);
|
||||
p = ntdb_access_read(ntdb, off, sizeof(p->f), true);
|
||||
if (NTDB_PTR_IS_ERR(p)) {
|
||||
return NTDB_PTR_ERR(p);
|
||||
}
|
||||
if (frec_magic(&p->f) != TDB_FREE_MAGIC) {
|
||||
if (frec_magic(&p->f) != NTDB_FREE_MAGIC) {
|
||||
if (unc > 1) {
|
||||
tally_add(uncoal, unc);
|
||||
unc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->r.magic == TDB_RECOVERY_INVALID_MAGIC
|
||||
|| p->r.magic == TDB_RECOVERY_MAGIC) {
|
||||
if (p->r.magic == NTDB_RECOVERY_INVALID_MAGIC
|
||||
|| p->r.magic == NTDB_RECOVERY_MAGIC) {
|
||||
len = sizeof(p->r) + p->r.max_len;
|
||||
} else if (frec_magic(&p->f) == TDB_FREE_MAGIC) {
|
||||
} else if (frec_magic(&p->f) == NTDB_FREE_MAGIC) {
|
||||
len = frec_len(&p->f);
|
||||
tally_add(fr, len);
|
||||
len += sizeof(p->u);
|
||||
unc++;
|
||||
} else if (rec_magic(&p->u) == TDB_USED_MAGIC) {
|
||||
} else if (rec_magic(&p->u) == NTDB_USED_MAGIC) {
|
||||
len = sizeof(p->u)
|
||||
+ rec_key_length(&p->u)
|
||||
+ rec_data_length(&p->u)
|
||||
@ -117,105 +117,105 @@ static enum TDB_ERROR summarize(struct tdb_context *tdb,
|
||||
tally_add(keys, rec_key_length(&p->u));
|
||||
tally_add(data, rec_data_length(&p->u));
|
||||
tally_add(extra, rec_extra_padding(&p->u));
|
||||
} else if (rec_magic(&p->u) == TDB_HTABLE_MAGIC) {
|
||||
tdb_off_t count = count_hash(tdb,
|
||||
} else if (rec_magic(&p->u) == NTDB_HTABLE_MAGIC) {
|
||||
ntdb_off_t count = count_hash(ntdb,
|
||||
off + sizeof(p->u),
|
||||
TDB_SUBLEVEL_HASH_BITS);
|
||||
if (TDB_OFF_IS_ERR(count)) {
|
||||
return TDB_OFF_TO_ERR(count);
|
||||
NTDB_SUBLEVEL_HASH_BITS);
|
||||
if (NTDB_OFF_IS_ERR(count)) {
|
||||
return NTDB_OFF_TO_ERR(count);
|
||||
}
|
||||
tally_add(hashes, count);
|
||||
tally_add(extra, rec_extra_padding(&p->u));
|
||||
len = sizeof(p->u)
|
||||
+ rec_data_length(&p->u)
|
||||
+ rec_extra_padding(&p->u);
|
||||
} else if (rec_magic(&p->u) == TDB_FTABLE_MAGIC) {
|
||||
} else if (rec_magic(&p->u) == NTDB_FTABLE_MAGIC) {
|
||||
len = sizeof(p->u)
|
||||
+ rec_data_length(&p->u)
|
||||
+ rec_extra_padding(&p->u);
|
||||
tally_add(ftables, rec_data_length(&p->u));
|
||||
tally_add(extra, rec_extra_padding(&p->u));
|
||||
} else if (rec_magic(&p->u) == TDB_CHAIN_MAGIC) {
|
||||
} else if (rec_magic(&p->u) == NTDB_CHAIN_MAGIC) {
|
||||
len = sizeof(p->u)
|
||||
+ rec_data_length(&p->u)
|
||||
+ rec_extra_padding(&p->u);
|
||||
tally_add(chains, 1);
|
||||
tally_add(extra, rec_extra_padding(&p->u));
|
||||
} else if (rec_magic(&p->u) == TDB_CAP_MAGIC) {
|
||||
} else if (rec_magic(&p->u) == NTDB_CAP_MAGIC) {
|
||||
len = sizeof(p->u)
|
||||
+ rec_data_length(&p->u)
|
||||
+ rec_extra_padding(&p->u);
|
||||
(*num_caps)++;
|
||||
} else {
|
||||
len = dead_space(tdb, off);
|
||||
if (TDB_OFF_IS_ERR(len)) {
|
||||
return TDB_OFF_TO_ERR(len);
|
||||
len = dead_space(ntdb, off);
|
||||
if (NTDB_OFF_IS_ERR(len)) {
|
||||
return NTDB_OFF_TO_ERR(len);
|
||||
}
|
||||
}
|
||||
tdb_access_release(tdb, p);
|
||||
ntdb_access_release(ntdb, p);
|
||||
}
|
||||
if (unc)
|
||||
tally_add(uncoal, unc);
|
||||
return TDB_SUCCESS;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static void add_capabilities(struct tdb_context *tdb, char *summary)
|
||||
static void add_capabilities(struct ntdb_context *ntdb, char *summary)
|
||||
{
|
||||
tdb_off_t off, next;
|
||||
const struct tdb_capability *cap;
|
||||
ntdb_off_t off, next;
|
||||
const struct ntdb_capability *cap;
|
||||
size_t count = 0;
|
||||
|
||||
/* Append to summary. */
|
||||
summary += strlen(summary);
|
||||
|
||||
off = tdb_read_off(tdb, offsetof(struct tdb_header, capabilities));
|
||||
if (TDB_OFF_IS_ERR(off))
|
||||
off = ntdb_read_off(ntdb, offsetof(struct ntdb_header, capabilities));
|
||||
if (NTDB_OFF_IS_ERR(off))
|
||||
return;
|
||||
|
||||
/* Walk capability list. */
|
||||
for (; off; off = next) {
|
||||
cap = tdb_access_read(tdb, off, sizeof(*cap), true);
|
||||
if (TDB_PTR_IS_ERR(cap)) {
|
||||
cap = ntdb_access_read(ntdb, off, sizeof(*cap), true);
|
||||
if (NTDB_PTR_IS_ERR(cap)) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
sprintf(summary, CAPABILITY_FORMAT,
|
||||
cap->type & TDB_CAP_TYPE_MASK,
|
||||
cap->type & NTDB_CAP_TYPE_MASK,
|
||||
/* Noopen? How did we get here? */
|
||||
(cap->type & TDB_CAP_NOOPEN) ? " (unopenable)"
|
||||
: ((cap->type & TDB_CAP_NOWRITE)
|
||||
&& (cap->type & TDB_CAP_NOCHECK)) ? " (uncheckable,read-only)"
|
||||
: (cap->type & TDB_CAP_NOWRITE) ? " (read-only)"
|
||||
: (cap->type & TDB_CAP_NOCHECK) ? " (uncheckable)"
|
||||
(cap->type & NTDB_CAP_NOOPEN) ? " (unopenable)"
|
||||
: ((cap->type & NTDB_CAP_NOWRITE)
|
||||
&& (cap->type & NTDB_CAP_NOCHECK)) ? " (uncheckable,read-only)"
|
||||
: (cap->type & NTDB_CAP_NOWRITE) ? " (read-only)"
|
||||
: (cap->type & NTDB_CAP_NOCHECK) ? " (uncheckable)"
|
||||
: "");
|
||||
summary += strlen(summary);
|
||||
next = cap->next;
|
||||
tdb_access_release(tdb, cap);
|
||||
ntdb_access_release(ntdb, cap);
|
||||
}
|
||||
}
|
||||
|
||||
_PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
|
||||
enum tdb_summary_flags flags,
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_summary(struct ntdb_context *ntdb,
|
||||
enum ntdb_summary_flags flags,
|
||||
char **summary)
|
||||
{
|
||||
tdb_len_t len;
|
||||
ntdb_len_t len;
|
||||
size_t num_caps = 0;
|
||||
struct tally *ftables, *hashes, *freet, *keys, *data, *extra, *uncoal,
|
||||
*chains;
|
||||
char *hashesg, *freeg, *keysg, *datag, *extrag, *uncoalg;
|
||||
enum TDB_ERROR ecode;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
hashesg = freeg = keysg = datag = extrag = uncoalg = NULL;
|
||||
|
||||
ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
|
||||
if (ecode != TDB_SUCCESS) {
|
||||
return tdb->last_error = ecode;
|
||||
ecode = ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, false);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
ecode = tdb_lock_expand(tdb, F_RDLCK);
|
||||
if (ecode != TDB_SUCCESS) {
|
||||
tdb_allrecord_unlock(tdb, F_RDLCK);
|
||||
return tdb->last_error = ecode;
|
||||
ecode = ntdb_lock_expand(ntdb, F_RDLCK);
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
ntdb_allrecord_unlock(ntdb, F_RDLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
||||
|
||||
/* Start stats off empty. */
|
||||
@ -229,19 +229,19 @@ _PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
|
||||
chains = tally_new(HISTO_HEIGHT);
|
||||
if (!ftables || !hashes || !freet || !keys || !data || !extra
|
||||
|| !uncoal || !chains) {
|
||||
ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
|
||||
"tdb_summary: failed to allocate"
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"ntdb_summary: failed to allocate"
|
||||
" tally structures");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ecode = summarize(tdb, hashes, ftables, freet, keys, data, extra,
|
||||
ecode = summarize(ntdb, hashes, ftables, freet, keys, data, extra,
|
||||
uncoal, chains, &num_caps);
|
||||
if (ecode != TDB_SUCCESS) {
|
||||
if (ecode != NTDB_SUCCESS) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (flags & TDB_SUMMARY_HISTOGRAMS) {
|
||||
if (flags & NTDB_SUMMARY_HISTOGRAMS) {
|
||||
hashesg = tally_histogram(hashes, HISTO_WIDTH, HISTO_HEIGHT);
|
||||
freeg = tally_histogram(freet, HISTO_WIDTH, HISTO_HEIGHT);
|
||||
keysg = tally_histogram(keys, HISTO_WIDTH, HISTO_HEIGHT);
|
||||
@ -263,13 +263,13 @@ _PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
|
||||
|
||||
*summary = malloc(len);
|
||||
if (!*summary) {
|
||||
ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
|
||||
"tdb_summary: failed to allocate string");
|
||||
ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
|
||||
"ntdb_summary: failed to allocate string");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
sprintf(*summary, SUMMARY_FORMAT,
|
||||
(size_t)tdb->file->map_size,
|
||||
(size_t)ntdb->file->map_size,
|
||||
tally_total(keys, NULL) + tally_total(data, NULL),
|
||||
tally_num(keys),
|
||||
tally_min(keys), tally_mean(keys), tally_max(keys),
|
||||
@ -284,29 +284,29 @@ _PUBLIC_ enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
|
||||
tally_total(uncoal, NULL),
|
||||
tally_min(uncoal), tally_mean(uncoal), tally_max(uncoal),
|
||||
uncoalg ? uncoalg : "",
|
||||
(unsigned)count_hash(tdb, offsetof(struct tdb_header,
|
||||
(unsigned)count_hash(ntdb, offsetof(struct ntdb_header,
|
||||
hashtable),
|
||||
TDB_TOPLEVEL_HASH_BITS),
|
||||
1 << TDB_TOPLEVEL_HASH_BITS,
|
||||
NTDB_TOPLEVEL_HASH_BITS),
|
||||
1 << NTDB_TOPLEVEL_HASH_BITS,
|
||||
tally_num(chains),
|
||||
tally_num(hashes),
|
||||
tally_min(hashes), tally_mean(hashes), tally_max(hashes),
|
||||
hashesg ? hashesg : "",
|
||||
tally_total(keys, NULL) * 100.0 / tdb->file->map_size,
|
||||
tally_total(data, NULL) * 100.0 / tdb->file->map_size,
|
||||
tally_total(extra, NULL) * 100.0 / tdb->file->map_size,
|
||||
tally_total(freet, NULL) * 100.0 / tdb->file->map_size,
|
||||
tally_total(keys, NULL) * 100.0 / ntdb->file->map_size,
|
||||
tally_total(data, NULL) * 100.0 / ntdb->file->map_size,
|
||||
tally_total(extra, NULL) * 100.0 / ntdb->file->map_size,
|
||||
tally_total(freet, NULL) * 100.0 / ntdb->file->map_size,
|
||||
(tally_num(keys) + tally_num(freet) + tally_num(hashes))
|
||||
* sizeof(struct tdb_used_record) * 100.0 / tdb->file->map_size,
|
||||
tally_num(ftables) * sizeof(struct tdb_freetable)
|
||||
* 100.0 / tdb->file->map_size,
|
||||
* sizeof(struct ntdb_used_record) * 100.0 / ntdb->file->map_size,
|
||||
tally_num(ftables) * sizeof(struct ntdb_freetable)
|
||||
* 100.0 / ntdb->file->map_size,
|
||||
(tally_num(hashes)
|
||||
* (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS)
|
||||
+ (sizeof(tdb_off_t) << TDB_TOPLEVEL_HASH_BITS)
|
||||
+ sizeof(struct tdb_chain) * tally_num(chains))
|
||||
* 100.0 / tdb->file->map_size);
|
||||
* (sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS)
|
||||
+ (sizeof(ntdb_off_t) << NTDB_TOPLEVEL_HASH_BITS)
|
||||
+ sizeof(struct ntdb_chain) * tally_num(chains))
|
||||
* 100.0 / ntdb->file->map_size);
|
||||
|
||||
add_capabilities(tdb, *summary);
|
||||
add_capabilities(ntdb, *summary);
|
||||
|
||||
unlock:
|
||||
free(hashesg);
|
||||
@ -324,7 +324,7 @@ unlock:
|
||||
free(ftables);
|
||||
free(chains);
|
||||
|
||||
tdb_allrecord_unlock(tdb, F_RDLCK);
|
||||
tdb_unlock_expand(tdb, F_RDLCK);
|
||||
return tdb->last_error = ecode;
|
||||
ntdb_allrecord_unlock(ntdb, F_RDLCK);
|
||||
ntdb_unlock_expand(ntdb, F_RDLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/hash/hash.h>
|
||||
#include <sys/types.h>
|
||||
@ -18,38 +18,38 @@ static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
uint64_t seed = 16014841315512641303ULL;
|
||||
union tdb_attribute fixed_hattr
|
||||
= { .hash = { .base = { TDB_ATTRIBUTE_HASH },
|
||||
union ntdb_attribute fixed_hattr
|
||||
= { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = fixedhash,
|
||||
.data = &seed } };
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = { (unsigned char *)&j, sizeof(j) };
|
||||
struct tdb_data data = { (unsigned char *)&j, sizeof(j) };
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
|
||||
NTDB_DATA data = { (unsigned char *)&j, sizeof(j) };
|
||||
|
||||
fixed_hattr.base.next = &tap_log_attr;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * (1 + 500 * 3) + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-12-store.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-12-store.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &fixed_hattr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* We seemed to lose some keys.
|
||||
* Insert and check they're in there! */
|
||||
for (j = 0; j < 500; j++) {
|
||||
struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
|
||||
ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS);
|
||||
ok1(tdb_deq(d, data));
|
||||
NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(ntdb_deq(d, data));
|
||||
free(d.dptr);
|
||||
}
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
205
lib/ntdb/test/api-13-delete.c
Normal file
205
lib/ntdb/test/api-13-delete.c
Normal file
@ -0,0 +1,205 @@
|
||||
#include "private.h" // For NTDB_TOPLEVEL_HASH_BITS
|
||||
#include <ccan/hash/hash.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* We rig the hash so adjacent-numbered records always clash. */
|
||||
static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
|
||||
{
|
||||
return ((uint64_t)*(const unsigned int *)key)
|
||||
<< (64 - NTDB_TOPLEVEL_HASH_BITS - 1);
|
||||
}
|
||||
|
||||
/* We use the same seed which we saw a failure on. */
|
||||
static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
|
||||
{
|
||||
return hash64_stable((const unsigned char *)key, len,
|
||||
*(uint64_t *)p);
|
||||
}
|
||||
|
||||
static bool store_records(struct ntdb_context *ntdb)
|
||||
{
|
||||
int i;
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA d, data = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
return false;
|
||||
ntdb_fetch(ntdb, key, &d);
|
||||
if (!ntdb_deq(d, data))
|
||||
return false;
|
||||
free(d.dptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void test_val(struct ntdb_context *ntdb, uint64_t val)
|
||||
{
|
||||
uint64_t v;
|
||||
NTDB_DATA key = { (unsigned char *)&v, sizeof(v) };
|
||||
NTDB_DATA d, data = { (unsigned char *)&v, sizeof(v) };
|
||||
|
||||
/* Insert an entry, then delete it. */
|
||||
v = val;
|
||||
/* Delete should fail. */
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_ERR_NOEXIST);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Insert should succeed. */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Delete should succeed. */
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Re-add it, then add collision. */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
v = val + 1;
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Can find both? */
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
v = val;
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
|
||||
/* Delete second one. */
|
||||
v = val + 1;
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Re-add */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Now, try deleting first one. */
|
||||
v = val;
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Can still find second? */
|
||||
v = val + 1;
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
|
||||
/* Now, this will be ideally placed. */
|
||||
v = val + 2;
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* This will collide with both. */
|
||||
v = val;
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
|
||||
/* We can still find them all, right? */
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
v = val + 1;
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
v = val + 2;
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
|
||||
/* And if we delete val + 1, that val + 2 should not move! */
|
||||
v = val + 1;
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
v = val;
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
v = val + 2;
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == data.dsize);
|
||||
free(d.dptr);
|
||||
|
||||
/* Delete those two, so we are empty. */
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
v = val;
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct ntdb_context *ntdb;
|
||||
uint64_t seed = 16014841315512641303ULL;
|
||||
union ntdb_attribute clash_hattr
|
||||
= { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = clash } };
|
||||
union ntdb_attribute fixed_hattr
|
||||
= { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = fixedhash,
|
||||
.data = &seed } };
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
/* These two values gave trouble before. */
|
||||
int vals[] = { 755, 837 };
|
||||
|
||||
clash_hattr.base.next = &tap_log_attr;
|
||||
fixed_hattr.base.next = &tap_log_attr;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0])
|
||||
* (39 * 3 + 5 + sizeof(vals)/sizeof(vals[0])*2) + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("run-13-delete.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &clash_hattr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* Check start of hash table. */
|
||||
test_val(ntdb, 0);
|
||||
|
||||
/* Check end of hash table. */
|
||||
test_val(ntdb, -1ULL);
|
||||
|
||||
/* Check mixed bitpattern. */
|
||||
test_val(ntdb, 0x123456789ABCDEF0ULL);
|
||||
|
||||
ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0));
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* Deleting these entries in the db gave problems. */
|
||||
ntdb = ntdb_open("run-13-delete.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &fixed_hattr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ok1(store_records(ntdb));
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
for (j = 0; j < sizeof(vals)/sizeof(vals[0]); j++) {
|
||||
NTDB_DATA key;
|
||||
|
||||
key.dptr = (unsigned char *)&vals[j];
|
||||
key.dsize = sizeof(vals[j]);
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
}
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
54
lib/ntdb/test/api-14-exists.c
Normal file
54
lib/ntdb/test/api-14-exists.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "config.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "logging.h"
|
||||
|
||||
static bool test_records(struct ntdb_context *ntdb)
|
||||
{
|
||||
int i;
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (ntdb_exists(ntdb, key))
|
||||
return false;
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
return false;
|
||||
if (!ntdb_exists(ntdb, key))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (!ntdb_exists(ntdb, key))
|
||||
return false;
|
||||
if (ntdb_delete(ntdb, key) != 0)
|
||||
return false;
|
||||
if (ntdb_exists(ntdb, key))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 2 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("run-14-exists.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (ok1(ntdb))
|
||||
ok1(test_records(ntdb));
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
46
lib/ntdb/test/api-16-wipe_all.c
Normal file
46
lib/ntdb/test/api-16-wipe_all.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include "config.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "logging.h"
|
||||
|
||||
static bool add_records(struct ntdb_context *ntdb)
|
||||
{
|
||||
int i;
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("run-16-wipe_all.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (ok1(ntdb)) {
|
||||
NTDB_DATA key;
|
||||
ok1(add_records(ntdb));
|
||||
ok1(ntdb_wipe_all(ntdb) == NTDB_SUCCESS);
|
||||
ok1(ntdb_firstkey(ntdb, &key) == NTDB_ERR_NOEXIST);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
67
lib/ntdb/test/api-21-parse_record.c
Normal file
67
lib/ntdb/test/api-21-parse_record.c
Normal file
@ -0,0 +1,67 @@
|
||||
#include "config.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "logging.h"
|
||||
|
||||
static enum NTDB_ERROR parse(NTDB_DATA key, NTDB_DATA data, NTDB_DATA *expected)
|
||||
{
|
||||
if (!ntdb_deq(data, *expected))
|
||||
return NTDB_ERR_EINVAL;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum NTDB_ERROR parse_err(NTDB_DATA key, NTDB_DATA data, void *unused)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
static bool test_records(struct ntdb_context *ntdb)
|
||||
{
|
||||
int i;
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (ntdb_parse_record(ntdb, key, parse, &data) != NTDB_SUCCESS)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ntdb_parse_record(ntdb, key, parse, &data) != NTDB_ERR_NOEXIST)
|
||||
return false;
|
||||
|
||||
/* Test error return from parse function. */
|
||||
i = 0;
|
||||
if (ntdb_parse_record(ntdb, key, parse_err, NULL) != 100)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 2 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("api-21-parse_record.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (ok1(ntdb))
|
||||
ok1(test_records(ntdb));
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
73
lib/ntdb/test/api-55-transaction.c
Normal file
73
lib/ntdb/test/api-55-transaction.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include "private.h" // struct ntdb_context
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
unsigned char *buffer;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data;
|
||||
|
||||
buffer = malloc(1000);
|
||||
for (i = 0; i < 1000; i++)
|
||||
buffer[i] = i;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 20 + 1);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("run-55-transaction.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ok1(ntdb_transaction_start(ntdb) == 0);
|
||||
data.dptr = buffer;
|
||||
data.dsize = 1000;
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
|
||||
ok1(data.dsize == 1000);
|
||||
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
|
||||
free(data.dptr);
|
||||
|
||||
/* Cancelling a transaction means no store */
|
||||
ntdb_transaction_cancel(ntdb);
|
||||
ok1(ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &data) == NTDB_ERR_NOEXIST);
|
||||
|
||||
/* Commit the transaction. */
|
||||
ok1(ntdb_transaction_start(ntdb) == 0);
|
||||
data.dptr = buffer;
|
||||
data.dsize = 1000;
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
|
||||
ok1(data.dsize == 1000);
|
||||
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
|
||||
free(data.dptr);
|
||||
ok1(ntdb_transaction_commit(ntdb) == 0);
|
||||
ok1(ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
|
||||
ok1(data.dsize == 1000);
|
||||
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
|
||||
free(data.dptr);
|
||||
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
free(buffer);
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -9,23 +9,23 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 3);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("api-80-tdb_fd.tdb", flags[i],
|
||||
ntdb = ntdb_open("api-80-ntdb_fd.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
continue;
|
||||
|
||||
if (flags[i] & TDB_INTERNAL)
|
||||
ok1(tdb_fd(tdb) == -1);
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
ok1(ntdb_fd(ntdb) == -1);
|
||||
else
|
||||
ok1(tdb_fd(tdb) > 2);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_fd(ntdb) > 2);
|
||||
ntdb_close(ntdb);
|
||||
ok1(tap_log_messages == 0);
|
||||
}
|
||||
return exit_status();
|
69
lib/ntdb/test/api-81-seqnum.c
Normal file
69
lib/ntdb/test/api-81-seqnum.c
Normal file
@ -0,0 +1,69 @@
|
||||
#include "config.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, seq;
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 15 + 4 * 13);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("api-81-seqnum.ntdb", flags[i]|NTDB_SEQNUM,
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(ntdb))
|
||||
continue;
|
||||
|
||||
seq = 0;
|
||||
ok1(ntdb_get_seqnum(ntdb) == seq);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
/* Fetch doesn't change seqnum */
|
||||
if (ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS))
|
||||
free(d.dptr);
|
||||
ok1(ntdb_get_seqnum(ntdb) == seq);
|
||||
ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
/* Empty append works */
|
||||
ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
|
||||
ok1(ntdb_wipe_all(ntdb) == NTDB_SUCCESS);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
|
||||
if (!(flags[i] & NTDB_INTERNAL)) {
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
|
||||
ok1(ntdb_get_seqnum(ntdb) == ++seq);
|
||||
ok1(ntdb_transaction_commit(ntdb) == NTDB_SUCCESS);
|
||||
ok1(ntdb_get_seqnum(ntdb) == seq);
|
||||
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_get_seqnum(ntdb) == seq + 1);
|
||||
ntdb_transaction_cancel(ntdb);
|
||||
ok1(ntdb_get_seqnum(ntdb) == seq);
|
||||
}
|
||||
ntdb_close(ntdb);
|
||||
ok1(tap_log_messages == 0);
|
||||
}
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "private.h" // for tdb_fcntl_unlock
|
||||
#include "tdb2.h"
|
||||
#include "private.h" // for ntdb_fcntl_unlock
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -35,7 +35,7 @@ static int mylock(int fd, int rw, off_t off, off_t len, bool waitflag,
|
||||
}
|
||||
|
||||
static int trav_err;
|
||||
static int trav(struct tdb_context *tdb, TDB_DATA k, TDB_DATA d, int *terr)
|
||||
static int trav(struct ntdb_context *ntdb, NTDB_DATA k, NTDB_DATA d, int *terr)
|
||||
{
|
||||
*terr = trav_err;
|
||||
return 0;
|
||||
@ -44,193 +44,193 @@ static int trav(struct tdb_context *tdb, TDB_DATA k, TDB_DATA d, int *terr)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
union tdb_attribute lock_attr;
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data = tdb_mkdata("data", 4);
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
union ntdb_attribute lock_attr;
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
int lock_err;
|
||||
|
||||
lock_attr.base.attr = TDB_ATTRIBUTE_FLOCK;
|
||||
lock_attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
|
||||
lock_attr.base.next = &tap_log_attr;
|
||||
lock_attr.flock.lock = mylock;
|
||||
lock_attr.flock.unlock = tdb_fcntl_unlock;
|
||||
lock_attr.flock.unlock = ntdb_fcntl_unlock;
|
||||
lock_attr.flock.data = &lock_err;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 80);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
struct tdb_data d;
|
||||
NTDB_DATA d;
|
||||
|
||||
/* Nonblocking open; expect no error message. */
|
||||
lock_err = EAGAIN;
|
||||
tdb = tdb_open("run-82-lockattr.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
|
||||
ok(errno == lock_err, "Errno is %u", errno);
|
||||
ok1(!tdb);
|
||||
ok1(!ntdb);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
lock_err = EINTR;
|
||||
tdb = tdb_open("run-82-lockattr.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
|
||||
ok(errno == lock_err, "Errno is %u", errno);
|
||||
ok1(!tdb);
|
||||
ok1(!ntdb);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
/* Forced fail open. */
|
||||
lock_err = ENOMEM;
|
||||
tdb = tdb_open("run-82-lockattr.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
|
||||
ok1(errno == lock_err);
|
||||
ok1(!tdb);
|
||||
ok1(!ntdb);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
lock_err = 0;
|
||||
tdb = tdb_open("run-82-lockattr.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-82-lockattr.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &lock_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
continue;
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
/* Nonblocking store. */
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
/* Nonblocking fetch. */
|
||||
lock_err = EAGAIN;
|
||||
ok1(!tdb_exists(tdb, key));
|
||||
ok1(!ntdb_exists(ntdb, key));
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(!tdb_exists(tdb, key));
|
||||
ok1(!ntdb_exists(ntdb, key));
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(!tdb_exists(tdb, key));
|
||||
ok1(!ntdb_exists(ntdb, key));
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
/* Nonblocking delete. */
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_delete(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_delete(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_delete(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
/* Nonblocking locks. */
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_chainlock(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_chainlock(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_chainlock(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_chainlock_read(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_chainlock_read(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_chainlock_read(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_chainlock_read(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_chainlock_read(tdb, key) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_chainlock_read(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_lockall(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_lockall(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_lockall(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_ERR_LOCK);
|
||||
/* This actually does divide and conquer. */
|
||||
ok1(tap_log_messages > 0);
|
||||
tap_log_messages = 0;
|
||||
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_lockall_read(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_lockall_read(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_lockall_read(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_lockall_read(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_lockall_read(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_lockall_read(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages > 0);
|
||||
tap_log_messages = 0;
|
||||
|
||||
/* Nonblocking traverse; go nonblock partway through. */
|
||||
lock_err = 0;
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
|
||||
trav_err = EAGAIN;
|
||||
ok1(tdb_traverse(tdb, trav, &lock_err) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_traverse(ntdb, trav, &lock_err) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
trav_err = EINTR;
|
||||
lock_err = 0;
|
||||
ok1(tdb_traverse(tdb, trav, &lock_err) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_traverse(ntdb, trav, &lock_err) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
trav_err = ENOMEM;
|
||||
lock_err = 0;
|
||||
ok1(tdb_traverse(tdb, trav, &lock_err) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_traverse(ntdb, trav, &lock_err) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
/* Nonblocking transactions. */
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_transaction_start(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = EINTR;
|
||||
ok1(tdb_transaction_start(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
lock_err = ENOMEM;
|
||||
ok1(tdb_transaction_start(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
tap_log_messages = 0;
|
||||
|
||||
/* Nonblocking transaction prepare. */
|
||||
lock_err = 0;
|
||||
ok1(tdb_transaction_start(tdb) == 0);
|
||||
ok1(tdb_delete(tdb, key) == 0);
|
||||
ok1(ntdb_transaction_start(ntdb) == 0);
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
|
||||
lock_err = EAGAIN;
|
||||
ok1(tdb_transaction_prepare_commit(tdb) == TDB_ERR_LOCK);
|
||||
ok1(ntdb_transaction_prepare_commit(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
lock_err = 0;
|
||||
ok1(tdb_transaction_prepare_commit(tdb) == 0);
|
||||
ok1(tdb_transaction_commit(tdb) == 0);
|
||||
ok1(ntdb_transaction_prepare_commit(ntdb) == 0);
|
||||
ok1(ntdb_transaction_commit(ntdb) == 0);
|
||||
|
||||
/* And the transaction was committed, right? */
|
||||
ok1(!tdb_exists(tdb, key));
|
||||
tdb_close(tdb);
|
||||
ok1(!ntdb_exists(ntdb, key));
|
||||
ntdb_close(ntdb);
|
||||
ok1(tap_log_messages == 0);
|
||||
}
|
||||
return exit_status();
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -11,14 +11,14 @@
|
||||
#include "external-agent.h"
|
||||
#include "logging.h"
|
||||
|
||||
static enum TDB_ERROR clear_if_first(int fd, void *arg)
|
||||
static enum NTDB_ERROR clear_if_first(int fd, void *arg)
|
||||
{
|
||||
/* We hold a lock offset 4 always, so we can tell if anyone is holding it.
|
||||
* (This is compatible with tdb1's TDB_CLEAR_IF_FIRST flag). */
|
||||
* (This is compatible with tdb's TDB_CLEAR_IF_FIRST flag). */
|
||||
struct flock fl;
|
||||
|
||||
if (arg != clear_if_first)
|
||||
return TDB_ERR_CORRUPT;
|
||||
return NTDB_ERR_CORRUPT;
|
||||
|
||||
fl.l_type = F_WRLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
@ -29,27 +29,27 @@ static enum TDB_ERROR clear_if_first(int fd, void *arg)
|
||||
/* We must be first ones to open it! */
|
||||
diag("truncating file!");
|
||||
if (ftruncate(fd, 0) != 0) {
|
||||
return TDB_ERR_IO;
|
||||
return NTDB_ERR_IO;
|
||||
}
|
||||
}
|
||||
fl.l_type = F_RDLCK;
|
||||
if (fcntl(fd, F_SETLKW, &fl) != 0) {
|
||||
return TDB_ERR_IO;
|
||||
return NTDB_ERR_IO;
|
||||
}
|
||||
return TDB_SUCCESS;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
struct agent *agent;
|
||||
union tdb_attribute cif;
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
union ntdb_attribute cif;
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
cif.openhook.base.attr = TDB_ATTRIBUTE_OPENHOOK;
|
||||
cif.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
|
||||
cif.openhook.base.next = &tap_log_attr;
|
||||
cif.openhook.fn = clear_if_first;
|
||||
cif.openhook.data = clear_if_first;
|
||||
@ -58,33 +58,33 @@ int main(int argc, char *argv[])
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 13);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
/* Create it */
|
||||
tdb = tdb_open("run-83-openhook.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-83-openhook.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, NULL);
|
||||
ok1(tdb);
|
||||
ok1(tdb_store(tdb, key, key, TDB_REPLACE) == 0);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb);
|
||||
ok1(ntdb_store(ntdb, key, key, NTDB_REPLACE) == 0);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* Now, open with CIF, should clear it. */
|
||||
tdb = tdb_open("run-83-openhook.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-83-openhook.ntdb", flags[i],
|
||||
O_RDWR, 0, &cif);
|
||||
ok1(tdb);
|
||||
ok1(!tdb_exists(tdb, key));
|
||||
ok1(tdb_store(tdb, key, key, TDB_REPLACE) == 0);
|
||||
ok1(ntdb);
|
||||
ok1(!ntdb_exists(ntdb, key));
|
||||
ok1(ntdb_store(ntdb, key, key, NTDB_REPLACE) == 0);
|
||||
|
||||
/* Agent should not clear it, since it's still open. */
|
||||
ok1(external_agent_operation(agent, OPEN_WITH_HOOK,
|
||||
"run-83-openhook.tdb") == SUCCESS);
|
||||
"run-83-openhook.ntdb") == SUCCESS);
|
||||
ok1(external_agent_operation(agent, FETCH, "key") == SUCCESS);
|
||||
ok1(external_agent_operation(agent, CLOSE, "") == SUCCESS);
|
||||
|
||||
/* Still exists for us too. */
|
||||
ok1(tdb_exists(tdb, key));
|
||||
ok1(ntdb_exists(ntdb, key));
|
||||
|
||||
/* Close it, now agent should clear it. */
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
ok1(external_agent_operation(agent, OPEN_WITH_HOOK,
|
||||
"run-83-openhook.tdb") == SUCCESS);
|
||||
"run-83-openhook.ntdb") == SUCCESS);
|
||||
ok1(external_agent_operation(agent, FETCH, "key") == FAILED);
|
||||
ok1(external_agent_operation(agent, CLOSE, "") == SUCCESS);
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -11,27 +11,27 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 11);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
union tdb_attribute *attr;
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
union ntdb_attribute *attr;
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
|
||||
tdb = tdb_open("run-91-get-stats.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-91-get-stats.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
ok1(tdb_store(tdb, key, key, TDB_REPLACE) == 0);
|
||||
ok1(ntdb);
|
||||
ok1(ntdb_store(ntdb, key, key, NTDB_REPLACE) == 0);
|
||||
|
||||
/* Use malloc so valgrind will catch overruns. */
|
||||
attr = malloc(sizeof *attr);
|
||||
attr->stats.base.attr = TDB_ATTRIBUTE_STATS;
|
||||
attr->stats.base.attr = NTDB_ATTRIBUTE_STATS;
|
||||
attr->stats.size = sizeof(*attr);
|
||||
|
||||
ok1(tdb_get_attribute(tdb, attr) == 0);
|
||||
ok1(ntdb_get_attribute(ntdb, attr) == 0);
|
||||
ok1(attr->stats.size == sizeof(*attr));
|
||||
ok1(attr->stats.allocs > 0);
|
||||
ok1(attr->stats.expands > 0);
|
||||
@ -39,18 +39,18 @@ int main(int argc, char *argv[])
|
||||
free(attr);
|
||||
|
||||
/* Try short one. */
|
||||
attr = malloc(offsetof(struct tdb_attribute_stats, allocs)
|
||||
attr = malloc(offsetof(struct ntdb_attribute_stats, allocs)
|
||||
+ sizeof(attr->stats.allocs));
|
||||
attr->stats.base.attr = TDB_ATTRIBUTE_STATS;
|
||||
attr->stats.size = offsetof(struct tdb_attribute_stats, allocs)
|
||||
attr->stats.base.attr = NTDB_ATTRIBUTE_STATS;
|
||||
attr->stats.size = offsetof(struct ntdb_attribute_stats, allocs)
|
||||
+ sizeof(attr->stats.allocs);
|
||||
ok1(tdb_get_attribute(tdb, attr) == 0);
|
||||
ok1(ntdb_get_attribute(ntdb, attr) == 0);
|
||||
ok1(attr->stats.size == sizeof(*attr));
|
||||
ok1(attr->stats.allocs > 0);
|
||||
free(attr);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
}
|
||||
return exit_status();
|
105
lib/ntdb/test/api-92-get-set-readonly.c
Normal file
105
lib/ntdb/test/api-92-get-set-readonly.c
Normal file
@ -0,0 +1,105 @@
|
||||
#include "config.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 48);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
/* RW -> R0 */
|
||||
ntdb = ntdb_open("run-92-get-set-readonly.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
ok1(!(ntdb_get_flags(ntdb) & NTDB_RDONLY));
|
||||
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == NTDB_SUCCESS);
|
||||
|
||||
ntdb_add_flag(ntdb, NTDB_RDONLY);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_RDONLY);
|
||||
|
||||
/* Can't store, append, delete. */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 1);
|
||||
ok1(ntdb_append(ntdb, key, data) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 2);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 3);
|
||||
|
||||
/* Can't start a transaction, or any write lock. */
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 4);
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 5);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 6);
|
||||
ok1(ntdb_wipe_all(ntdb) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 7);
|
||||
|
||||
/* Back to RW. */
|
||||
ntdb_remove_flag(ntdb, NTDB_RDONLY);
|
||||
ok1(!(ntdb_get_flags(ntdb) & NTDB_RDONLY));
|
||||
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY) == NTDB_SUCCESS);
|
||||
ok1(ntdb_append(ntdb, key, data) == NTDB_SUCCESS);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
|
||||
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == NTDB_SUCCESS);
|
||||
ok1(ntdb_transaction_commit(ntdb) == NTDB_SUCCESS);
|
||||
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_SUCCESS);
|
||||
ntdb_chainunlock(ntdb, key);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
|
||||
ntdb_unlockall(ntdb);
|
||||
ok1(ntdb_wipe_all(ntdb) == NTDB_SUCCESS);
|
||||
ok1(tap_log_messages == 7);
|
||||
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* R0 -> RW */
|
||||
ntdb = ntdb_open("run-92-get-set-readonly.ntdb", flags[i],
|
||||
O_RDONLY, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_RDONLY);
|
||||
|
||||
/* Can't store, append, delete. */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 8);
|
||||
ok1(ntdb_append(ntdb, key, data) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 9);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 10);
|
||||
|
||||
/* Can't start a transaction, or any write lock. */
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 11);
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 12);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 13);
|
||||
ok1(ntdb_wipe_all(ntdb) == NTDB_ERR_RDONLY);
|
||||
ok1(tap_log_messages == 14);
|
||||
|
||||
/* Can't remove NTDB_RDONLY since we opened with O_RDONLY */
|
||||
ntdb_remove_flag(ntdb, NTDB_RDONLY);
|
||||
ok1(tap_log_messages == 15);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_RDONLY);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
ok1(tap_log_messages == 15);
|
||||
tap_log_messages = 0;
|
||||
}
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -8,21 +8,21 @@
|
||||
|
||||
#define NUM_TESTS 1000
|
||||
|
||||
static bool store_all(struct tdb_context *tdb)
|
||||
static bool store_all(struct ntdb_context *ntdb)
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
|
||||
struct tdb_data dbuf = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA dbuf = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < NUM_TESTS; i++) {
|
||||
if (tdb_store(tdb, key, dbuf, TDB_INSERT) != TDB_SUCCESS)
|
||||
if (ntdb_store(ntdb, key, dbuf, NTDB_INSERT) != NTDB_SUCCESS)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mark_entry(struct tdb_context *tdb,
|
||||
TDB_DATA key, TDB_DATA data, bool found[])
|
||||
static int mark_entry(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key, NTDB_DATA data, bool found[])
|
||||
{
|
||||
unsigned int num;
|
||||
|
||||
@ -51,28 +51,28 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
bool found[NUM_TESTS];
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT
|
||||
};
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 6 + 1);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-93-repack.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-93-repack.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
break;
|
||||
|
||||
ok1(store_all(tdb));
|
||||
ok1(store_all(ntdb));
|
||||
|
||||
ok1(tdb_repack(tdb) == TDB_SUCCESS);
|
||||
ok1(ntdb_repack(ntdb) == NTDB_SUCCESS);
|
||||
memset(found, 0, sizeof(found));
|
||||
ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
|
||||
ok1(tdb_traverse(tdb, mark_entry, found) == NUM_TESTS);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
ok1(ntdb_traverse(ntdb, mark_entry, found) == NUM_TESTS);
|
||||
ok1(is_all_set(found, NUM_TESTS));
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
89
lib/ntdb/test/api-add-remove-flags.c
Normal file
89
lib/ntdb/test/api-add-remove-flags.c
Normal file
@ -0,0 +1,89 @@
|
||||
#include "private.h" // for ntdb_context
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(87);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("run-add-remove-flags.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ok1(ntdb_get_flags(ntdb) == ntdb->flags);
|
||||
tap_log_messages = 0;
|
||||
ntdb_add_flag(ntdb, NTDB_NOLOCK);
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
ok1(tap_log_messages == 1);
|
||||
else {
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_NOLOCK);
|
||||
}
|
||||
|
||||
tap_log_messages = 0;
|
||||
ntdb_add_flag(ntdb, NTDB_NOMMAP);
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
ok1(tap_log_messages == 1);
|
||||
else {
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_NOMMAP);
|
||||
ok1(ntdb->file->map_ptr == NULL);
|
||||
}
|
||||
|
||||
tap_log_messages = 0;
|
||||
ntdb_add_flag(ntdb, NTDB_NOSYNC);
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
ok1(tap_log_messages == 1);
|
||||
else {
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_NOSYNC);
|
||||
}
|
||||
|
||||
ok1(ntdb_get_flags(ntdb) == ntdb->flags);
|
||||
|
||||
tap_log_messages = 0;
|
||||
ntdb_remove_flag(ntdb, NTDB_NOLOCK);
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
ok1(tap_log_messages == 1);
|
||||
else {
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(!(ntdb_get_flags(ntdb) & NTDB_NOLOCK));
|
||||
}
|
||||
|
||||
tap_log_messages = 0;
|
||||
ntdb_remove_flag(ntdb, NTDB_NOMMAP);
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
ok1(tap_log_messages == 1);
|
||||
else {
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(!(ntdb_get_flags(ntdb) & NTDB_NOMMAP));
|
||||
ok1(ntdb->file->map_ptr != NULL);
|
||||
}
|
||||
|
||||
tap_log_messages = 0;
|
||||
ntdb_remove_flag(ntdb, NTDB_NOSYNC);
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
ok1(tap_log_messages == 1);
|
||||
else {
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(!(ntdb_get_flags(ntdb) & NTDB_NOSYNC));
|
||||
}
|
||||
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -8,77 +8,77 @@
|
||||
|
||||
#define NUM_RECORDS 1000
|
||||
|
||||
static bool store_records(struct tdb_context *tdb)
|
||||
static bool store_records(struct ntdb_context *ntdb)
|
||||
{
|
||||
int i;
|
||||
struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
|
||||
struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < NUM_RECORDS; i++)
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum TDB_ERROR check(struct tdb_data key,
|
||||
struct tdb_data data,
|
||||
static enum NTDB_ERROR check(NTDB_DATA key,
|
||||
NTDB_DATA data,
|
||||
bool *array)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (key.dsize != sizeof(val)) {
|
||||
diag("Wrong key size: %u\n", key.dsize);
|
||||
return TDB_ERR_CORRUPT;
|
||||
return NTDB_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
if (key.dsize != data.dsize
|
||||
|| memcmp(key.dptr, data.dptr, sizeof(val)) != 0) {
|
||||
diag("Key and data differ\n");
|
||||
return TDB_ERR_CORRUPT;
|
||||
return NTDB_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
memcpy(&val, key.dptr, sizeof(val));
|
||||
if (val >= NUM_RECORDS || val < 0) {
|
||||
diag("check value %i\n", val);
|
||||
return TDB_ERR_CORRUPT;
|
||||
return NTDB_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
if (array[val]) {
|
||||
diag("Value %i already seen\n", val);
|
||||
return TDB_ERR_CORRUPT;
|
||||
return NTDB_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
array[val] = true;
|
||||
return TDB_SUCCESS;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
bool array[NUM_RECORDS];
|
||||
|
||||
tdb = tdb_open("run-check-callback.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-check-callback.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ok1(store_records(tdb));
|
||||
ok1(store_records(ntdb));
|
||||
for (j = 0; j < NUM_RECORDS; j++)
|
||||
array[j] = false;
|
||||
ok1(tdb_check(tdb, check, array) == TDB_SUCCESS);
|
||||
ok1(ntdb_check(ntdb, check, array) == NTDB_SUCCESS);
|
||||
for (j = 0; j < NUM_RECORDS; j++)
|
||||
if (!array[j])
|
||||
break;
|
||||
ok1(j == NUM_RECORDS);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -9,14 +9,14 @@
|
||||
|
||||
#define NUM_RECORDS 1000
|
||||
|
||||
static bool store_records(struct tdb_context *tdb)
|
||||
static bool store_records(struct ntdb_context *ntdb)
|
||||
{
|
||||
int i;
|
||||
struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
|
||||
struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < NUM_RECORDS; i++)
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -26,7 +26,7 @@ struct trav_data {
|
||||
unsigned int calls;
|
||||
};
|
||||
|
||||
static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *p)
|
||||
static int trav(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *p)
|
||||
{
|
||||
struct trav_data *td = p;
|
||||
int val;
|
||||
@ -36,8 +36,8 @@ static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Since tdb_nextkey frees dptr, we need to clone it. */
|
||||
static TDB_DATA dup_key(TDB_DATA key)
|
||||
/* Since ntdb_nextkey frees dptr, we need to clone it. */
|
||||
static NTDB_DATA dup_key(NTDB_DATA key)
|
||||
{
|
||||
void *p = malloc(key.dsize);
|
||||
memcpy(p, key.dptr, key.dsize);
|
||||
@ -50,81 +50,81 @@ int main(int argc, char *argv[])
|
||||
unsigned int i, j;
|
||||
int num;
|
||||
struct trav_data td;
|
||||
TDB_DATA k;
|
||||
struct tdb_context *tdb;
|
||||
union tdb_attribute seed_attr;
|
||||
enum TDB_ERROR ecode;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
NTDB_DATA k;
|
||||
struct ntdb_context *ntdb;
|
||||
union ntdb_attribute seed_attr;
|
||||
enum NTDB_ERROR ecode;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
|
||||
seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
seed_attr.base.next = &tap_log_attr;
|
||||
seed_attr.seed.seed = 6334326220117065685ULL;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0])
|
||||
* (NUM_RECORDS*6 + (NUM_RECORDS-1)*3 + 22) + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("api-firstkey-nextkey.tdb", flags[i],
|
||||
ntdb = ntdb_open("api-firstkey-nextkey.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600,
|
||||
&seed_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ok1(tdb_firstkey(tdb, &k) == TDB_ERR_NOEXIST);
|
||||
ok1(ntdb_firstkey(ntdb, &k) == NTDB_ERR_NOEXIST);
|
||||
|
||||
/* One entry... */
|
||||
k.dptr = (unsigned char *)#
|
||||
k.dsize = sizeof(num);
|
||||
num = 0;
|
||||
ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
|
||||
ok1(tdb_firstkey(tdb, &k) == TDB_SUCCESS);
|
||||
ok1(ntdb_store(ntdb, k, k, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_firstkey(ntdb, &k) == NTDB_SUCCESS);
|
||||
ok1(k.dsize == sizeof(num));
|
||||
ok1(memcmp(k.dptr, &num, sizeof(num)) == 0);
|
||||
ok1(tdb_nextkey(tdb, &k) == TDB_ERR_NOEXIST);
|
||||
ok1(ntdb_nextkey(ntdb, &k) == NTDB_ERR_NOEXIST);
|
||||
|
||||
/* Two entries. */
|
||||
k.dptr = (unsigned char *)#
|
||||
k.dsize = sizeof(num);
|
||||
num = 1;
|
||||
ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
|
||||
ok1(tdb_firstkey(tdb, &k) == TDB_SUCCESS);
|
||||
ok1(ntdb_store(ntdb, k, k, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_firstkey(ntdb, &k) == NTDB_SUCCESS);
|
||||
ok1(k.dsize == sizeof(num));
|
||||
memcpy(&num, k.dptr, sizeof(num));
|
||||
ok1(num == 0 || num == 1);
|
||||
ok1(tdb_nextkey(tdb, &k) == TDB_SUCCESS);
|
||||
ok1(ntdb_nextkey(ntdb, &k) == NTDB_SUCCESS);
|
||||
ok1(k.dsize == sizeof(j));
|
||||
memcpy(&j, k.dptr, sizeof(j));
|
||||
ok1(j == 0 || j == 1);
|
||||
ok1(j != num);
|
||||
ok1(tdb_nextkey(tdb, &k) == TDB_ERR_NOEXIST);
|
||||
ok1(ntdb_nextkey(ntdb, &k) == NTDB_ERR_NOEXIST);
|
||||
|
||||
/* Clean up. */
|
||||
k.dptr = (unsigned char *)#
|
||||
k.dsize = sizeof(num);
|
||||
num = 0;
|
||||
ok1(tdb_delete(tdb, k) == 0);
|
||||
ok1(ntdb_delete(ntdb, k) == 0);
|
||||
num = 1;
|
||||
ok1(tdb_delete(tdb, k) == 0);
|
||||
ok1(ntdb_delete(ntdb, k) == 0);
|
||||
|
||||
/* Now lots of records. */
|
||||
ok1(store_records(tdb));
|
||||
ok1(store_records(ntdb));
|
||||
td.calls = 0;
|
||||
|
||||
num = tdb_traverse(tdb, trav, &td);
|
||||
num = ntdb_traverse(ntdb, trav, &td);
|
||||
ok1(num == NUM_RECORDS);
|
||||
ok1(td.calls == NUM_RECORDS);
|
||||
|
||||
/* Simple loop should match tdb_traverse */
|
||||
for (j = 0, ecode = tdb_firstkey(tdb, &k); j < td.calls; j++) {
|
||||
/* Simple loop should match ntdb_traverse */
|
||||
for (j = 0, ecode = ntdb_firstkey(ntdb, &k); j < td.calls; j++) {
|
||||
int val;
|
||||
|
||||
ok1(ecode == TDB_SUCCESS);
|
||||
ok1(ecode == NTDB_SUCCESS);
|
||||
ok1(k.dsize == sizeof(val));
|
||||
memcpy(&val, k.dptr, k.dsize);
|
||||
ok1(td.records[j] == val);
|
||||
ecode = tdb_nextkey(tdb, &k);
|
||||
ecode = ntdb_nextkey(ntdb, &k);
|
||||
}
|
||||
|
||||
/* But arbitrary orderings should work too. */
|
||||
@ -132,26 +132,26 @@ int main(int argc, char *argv[])
|
||||
k.dptr = (unsigned char *)&td.records[j-1];
|
||||
k.dsize = sizeof(td.records[j-1]);
|
||||
k = dup_key(k);
|
||||
ok1(tdb_nextkey(tdb, &k) == TDB_SUCCESS);
|
||||
ok1(ntdb_nextkey(ntdb, &k) == NTDB_SUCCESS);
|
||||
ok1(k.dsize == sizeof(td.records[j]));
|
||||
ok1(memcmp(k.dptr, &td.records[j], k.dsize) == 0);
|
||||
free(k.dptr);
|
||||
}
|
||||
|
||||
/* Even delete should work. */
|
||||
for (j = 0, ecode = tdb_firstkey(tdb, &k);
|
||||
ecode != TDB_ERR_NOEXIST;
|
||||
for (j = 0, ecode = ntdb_firstkey(ntdb, &k);
|
||||
ecode != NTDB_ERR_NOEXIST;
|
||||
j++) {
|
||||
ok1(ecode == TDB_SUCCESS);
|
||||
ok1(ecode == NTDB_SUCCESS);
|
||||
ok1(k.dsize == 4);
|
||||
ok1(tdb_delete(tdb, k) == 0);
|
||||
ecode = tdb_nextkey(tdb, &k);
|
||||
ok1(ntdb_delete(ntdb, k) == 0);
|
||||
ecode = ntdb_nextkey(ntdb, &k);
|
||||
}
|
||||
|
||||
diag("delete using first/nextkey gave %u of %u records",
|
||||
j, NUM_RECORDS);
|
||||
ok1(j == NUM_RECORDS);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
@ -1,17 +1,17 @@
|
||||
/* Test forking while holding lock.
|
||||
*
|
||||
* There are only five ways to do this currently:
|
||||
* (1) grab a tdb_chainlock, then fork.
|
||||
* (2) grab a tdb_lockall, then fork.
|
||||
* (3) grab a tdb_lockall_read, then fork.
|
||||
* (1) grab a ntdb_chainlock, then fork.
|
||||
* (2) grab a ntdb_lockall, then fork.
|
||||
* (3) grab a ntdb_lockall_read, then fork.
|
||||
* (4) start a transaction, then fork.
|
||||
* (5) fork from inside a tdb_parse() callback.
|
||||
* (5) fork from inside a ntdb_parse() callback.
|
||||
*
|
||||
* Note that we don't hold a lock across tdb_traverse callbacks, so
|
||||
* Note that we don't hold a lock across ntdb_traverse callbacks, so
|
||||
* that doesn't matter.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -22,40 +22,40 @@
|
||||
#include <stdlib.h>
|
||||
#include "logging.h"
|
||||
|
||||
static enum TDB_ERROR fork_in_parse(TDB_DATA key, TDB_DATA data,
|
||||
struct tdb_context *tdb)
|
||||
static enum NTDB_ERROR fork_in_parse(NTDB_DATA key, NTDB_DATA data,
|
||||
struct ntdb_context *ntdb)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (fork() == 0) {
|
||||
/* We expect this to fail. */
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
|
||||
exit(1);
|
||||
|
||||
if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
|
||||
if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
|
||||
exit(1);
|
||||
|
||||
if (tap_log_messages != 2)
|
||||
exit(2);
|
||||
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
if (tap_log_messages != 2)
|
||||
exit(3);
|
||||
exit(0);
|
||||
}
|
||||
wait(&status);
|
||||
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
return TDB_SUCCESS;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data = tdb_mkdata("data", 4);
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
@ -63,116 +63,116 @@ int main(int argc, char *argv[])
|
||||
|
||||
tap_log_messages = 0;
|
||||
|
||||
tdb = tdb_open("run-fork-test.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-fork-test.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
continue;
|
||||
|
||||
/* Put a record in here. */
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_SUCCESS);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_SUCCESS);
|
||||
|
||||
ok1(tdb_chainlock(tdb, key) == TDB_SUCCESS);
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_SUCCESS);
|
||||
if (fork() == 0) {
|
||||
/* We expect this to fail. */
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
|
||||
if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tap_log_messages != 2)
|
||||
return 2;
|
||||
|
||||
tdb_chainunlock(tdb, key);
|
||||
ntdb_chainunlock(ntdb, key);
|
||||
if (tap_log_messages != 3)
|
||||
return 3;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
if (tap_log_messages != 3)
|
||||
return 4;
|
||||
return 0;
|
||||
}
|
||||
wait(&status);
|
||||
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
tdb_chainunlock(tdb, key);
|
||||
ntdb_chainunlock(ntdb, key);
|
||||
|
||||
ok1(tdb_lockall(tdb) == TDB_SUCCESS);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
|
||||
if (fork() == 0) {
|
||||
/* We expect this to fail. */
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
|
||||
if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tap_log_messages != 2)
|
||||
return 2;
|
||||
|
||||
tdb_unlockall(tdb);
|
||||
ntdb_unlockall(ntdb);
|
||||
if (tap_log_messages != 2)
|
||||
return 3;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
if (tap_log_messages != 2)
|
||||
return 4;
|
||||
return 0;
|
||||
}
|
||||
wait(&status);
|
||||
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
tdb_unlockall(tdb);
|
||||
ntdb_unlockall(ntdb);
|
||||
|
||||
ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
|
||||
ok1(ntdb_lockall_read(ntdb) == NTDB_SUCCESS);
|
||||
if (fork() == 0) {
|
||||
/* We expect this to fail. */
|
||||
/* This would always fail anyway... */
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
|
||||
if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tap_log_messages != 2)
|
||||
return 2;
|
||||
|
||||
tdb_unlockall_read(tdb);
|
||||
ntdb_unlockall_read(ntdb);
|
||||
if (tap_log_messages != 2)
|
||||
return 3;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
if (tap_log_messages != 2)
|
||||
return 4;
|
||||
return 0;
|
||||
}
|
||||
wait(&status);
|
||||
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
tdb_unlockall_read(tdb);
|
||||
ntdb_unlockall_read(ntdb);
|
||||
|
||||
ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
|
||||
/* If transactions is empty, noop "commit" succeeds. */
|
||||
ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
|
||||
if (fork() == 0) {
|
||||
/* We expect this to fail. */
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
|
||||
if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
|
||||
return 1;
|
||||
|
||||
if (tap_log_messages != 2)
|
||||
return 2;
|
||||
|
||||
if (tdb_transaction_commit(tdb) != TDB_ERR_LOCK)
|
||||
if (ntdb_transaction_commit(ntdb) != NTDB_ERR_LOCK)
|
||||
return 3;
|
||||
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
if (tap_log_messages < 3)
|
||||
return 4;
|
||||
return 0;
|
||||
}
|
||||
wait(&status);
|
||||
ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
tdb_transaction_cancel(tdb);
|
||||
ntdb_transaction_cancel(ntdb);
|
||||
|
||||
ok1(tdb_parse_record(tdb, key, fork_in_parse, tdb)
|
||||
== TDB_SUCCESS);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_parse_record(ntdb, key, fork_in_parse, ntdb)
|
||||
== NTDB_SUCCESS);
|
||||
ntdb_close(ntdb);
|
||||
ok1(tap_log_messages == 0);
|
||||
}
|
||||
return exit_status();
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include "system/wait.h"
|
||||
#include <sys/types.h>
|
||||
@ -94,35 +94,35 @@ static int timeout_lock(int fd, int rw, off_t off, off_t len, bool waitflag,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tdb_chainlock_with_timeout_internal(struct tdb_context *tdb,
|
||||
TDB_DATA key,
|
||||
static int ntdb_chainlock_with_timeout_internal(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key,
|
||||
unsigned int timeout,
|
||||
int rw_type)
|
||||
{
|
||||
union tdb_attribute locking;
|
||||
enum TDB_ERROR ecode;
|
||||
union ntdb_attribute locking;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if (timeout) {
|
||||
locking.base.attr = TDB_ATTRIBUTE_FLOCK;
|
||||
ecode = tdb_get_attribute(tdb, &locking);
|
||||
if (ecode != TDB_SUCCESS)
|
||||
locking.base.attr = NTDB_ATTRIBUTE_FLOCK;
|
||||
ecode = ntdb_get_attribute(ntdb, &locking);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
|
||||
/* Replace locking function with our own. */
|
||||
locking.flock.data = &timeout;
|
||||
locking.flock.lock = timeout_lock;
|
||||
|
||||
ecode = tdb_set_attribute(tdb, &locking);
|
||||
if (ecode != TDB_SUCCESS)
|
||||
ecode = ntdb_set_attribute(ntdb, &locking);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
}
|
||||
if (rw_type == F_RDLCK)
|
||||
ecode = tdb_chainlock_read(tdb, key);
|
||||
ecode = ntdb_chainlock_read(ntdb, key);
|
||||
else
|
||||
ecode = tdb_chainlock(tdb, key);
|
||||
ecode = ntdb_chainlock(ntdb, key);
|
||||
|
||||
if (timeout) {
|
||||
tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
|
||||
}
|
||||
return ecode;
|
||||
}
|
||||
@ -130,10 +130,10 @@ static int tdb_chainlock_with_timeout_internal(struct tdb_context *tdb,
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
TDB_DATA key = tdb_mkdata("hello", 5);
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA key = ntdb_mkdata("hello", 5);
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
struct agent *agent;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 15);
|
||||
@ -141,52 +141,52 @@ int main(int argc, char *argv[])
|
||||
agent = prepare_external_agent();
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
enum TDB_ERROR ecode;
|
||||
tdb = tdb_open("run-locktimeout.tdb", flags[i],
|
||||
enum NTDB_ERROR ecode;
|
||||
ntdb = ntdb_open("run-locktimeout.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
break;
|
||||
|
||||
/* Simple cases: should succeed. */
|
||||
ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
|
||||
ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
|
||||
F_RDLCK);
|
||||
ok1(ecode == TDB_SUCCESS);
|
||||
ok1(ecode == NTDB_SUCCESS);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
tdb_chainunlock_read(tdb, key);
|
||||
ntdb_chainunlock_read(ntdb, key);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
|
||||
ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
|
||||
F_WRLCK);
|
||||
ok1(ecode == TDB_SUCCESS);
|
||||
ok1(ecode == NTDB_SUCCESS);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
tdb_chainunlock(tdb, key);
|
||||
ntdb_chainunlock(ntdb, key);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
/* OK, get agent to start transaction, then we should time out. */
|
||||
ok1(external_agent_operation(agent, OPEN, "run-locktimeout.tdb")
|
||||
ok1(external_agent_operation(agent, OPEN, "run-locktimeout.ntdb")
|
||||
== SUCCESS);
|
||||
ok1(external_agent_operation(agent, TRANSACTION_START, "")
|
||||
== SUCCESS);
|
||||
ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
|
||||
ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
|
||||
F_WRLCK);
|
||||
ok1(ecode == TDB_ERR_LOCK);
|
||||
ok1(ecode == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
/* Even if we get a different signal, should be fine. */
|
||||
CatchSignal(SIGUSR1, do_nothing);
|
||||
external_agent_operation(agent, SEND_SIGNAL, "");
|
||||
ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
|
||||
ecode = ntdb_chainlock_with_timeout_internal(ntdb, key, 20,
|
||||
F_WRLCK);
|
||||
ok1(ecode == TDB_ERR_LOCK);
|
||||
ok1(ecode == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
ok1(external_agent_operation(agent, TRANSACTION_COMMIT, "")
|
||||
== SUCCESS);
|
||||
ok1(external_agent_operation(agent, CLOSE, "")
|
||||
== SUCCESS);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
free_external_agent(agent);
|
||||
return exit_status();
|
@ -1,6 +1,6 @@
|
||||
/* Another test revealed that we lost an entry. This reproduces it. */
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include <ccan/hash/hash.h>
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
@ -20,23 +20,23 @@ static uint64_t failhash(const void *key, size_t len, uint64_t seed, void *p)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
struct tdb_context *tdb;
|
||||
struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
|
||||
struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
|
||||
union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = failhash } };
|
||||
|
||||
hattr.base.next = &tap_log_attr;
|
||||
plan_tests(1 + NUM_RECORDS + 2);
|
||||
|
||||
tdb = tdb_open("run-missing-entries.tdb", TDB_INTERNAL,
|
||||
ntdb = ntdb_open("run-missing-entries.ntdb", NTDB_INTERNAL,
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
|
||||
if (ok1(tdb)) {
|
||||
if (ok1(ntdb)) {
|
||||
for (i = 0; i < NUM_RECORDS; i++) {
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
|
||||
}
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
83
lib/ntdb/test/api-open-multiple-times.c
Normal file
83
lib/ntdb/test/api-open-multiple-times.c
Normal file
@ -0,0 +1,83 @@
|
||||
#include "config.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb, *ntdb2;
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 28);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("run-open-multiple-times.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ntdb2 = ntdb_open("run-open-multiple-times.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT, 0600, &tap_log_attr);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb2, NULL, NULL) == 0);
|
||||
|
||||
/* Store in one, fetch in the other. */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
|
||||
ok1(ntdb_fetch(ntdb2, key, &d) == NTDB_SUCCESS);
|
||||
ok1(ntdb_deq(d, data));
|
||||
free(d.dptr);
|
||||
|
||||
/* Vice versa, with delete. */
|
||||
ok1(ntdb_delete(ntdb2, key) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_NOEXIST);
|
||||
|
||||
/* OK, now close first one, check second still good. */
|
||||
ok1(ntdb_close(ntdb) == 0);
|
||||
|
||||
ok1(ntdb_store(ntdb2, key, data, NTDB_REPLACE) == 0);
|
||||
ok1(ntdb_fetch(ntdb2, key, &d) == NTDB_SUCCESS);
|
||||
ok1(ntdb_deq(d, data));
|
||||
free(d.dptr);
|
||||
|
||||
/* Reopen */
|
||||
ntdb = ntdb_open("run-open-multiple-times.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
|
||||
ok1(ntdb_transaction_start(ntdb2) == 0);
|
||||
|
||||
/* Anything in the other one should fail. */
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 1);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 2);
|
||||
ok1(ntdb_transaction_start(ntdb) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 3);
|
||||
ok1(ntdb_chainlock(ntdb, key) == NTDB_ERR_LOCK);
|
||||
ok1(tap_log_messages == 4);
|
||||
|
||||
/* Transaciton should work as normal. */
|
||||
ok1(ntdb_store(ntdb2, key, data, NTDB_REPLACE) == NTDB_SUCCESS);
|
||||
|
||||
/* Now... try closing with locks held. */
|
||||
ok1(ntdb_close(ntdb2) == 0);
|
||||
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(ntdb_deq(d, data));
|
||||
free(d.dptr);
|
||||
ok1(ntdb_close(ntdb) == 0);
|
||||
ok1(tap_log_messages == 4);
|
||||
tap_log_messages = 0;
|
||||
}
|
||||
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -13,12 +13,12 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data;
|
||||
|
||||
data.dptr = malloc(MAX_SIZE);
|
||||
memset(data.dptr, 0x24, MAX_SIZE);
|
||||
@ -26,23 +26,23 @@ int main(int argc, char *argv[])
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0])
|
||||
* (3 + (1 + (MAX_SIZE/SIZE_STEP)) * 2) + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-record-expand.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-record-expand.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
data.dsize = 0;
|
||||
ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
for (data.dsize = 0;
|
||||
data.dsize < MAX_SIZE;
|
||||
data.dsize += SIZE_STEP) {
|
||||
memset(data.dptr, data.dsize, data.dsize);
|
||||
ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
}
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
ok1(tap_log_messages == 0);
|
||||
free(data.dptr);
|
39
lib/ntdb/test/api-simple-delete.c
Normal file
39
lib/ntdb/test/api-simple-delete.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include "config.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
ntdb = ntdb_open("run-simple-delete.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
if (ntdb) {
|
||||
/* Delete should fail. */
|
||||
ok1(ntdb_delete(ntdb, key) == NTDB_ERR_NOEXIST);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
/* Insert should succeed. */
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
/* Delete should now work. */
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
}
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "tap-interface.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -10,38 +10,38 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = { (unsigned char *)&j, sizeof(j) };
|
||||
struct tdb_data data = { (unsigned char *)&j, sizeof(j) };
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
|
||||
NTDB_DATA data = { (unsigned char *)&j, sizeof(j) };
|
||||
char *summary;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * (1 + 2 * 5) + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-summary.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-summary.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* Put some stuff in there. */
|
||||
for (j = 0; j < 500; j++) {
|
||||
/* Make sure padding varies to we get some graphs! */
|
||||
data.dsize = j % (sizeof(j) + 1);
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
|
||||
fail("Storing in tdb");
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
fail("Storing in ntdb");
|
||||
}
|
||||
|
||||
for (j = 0;
|
||||
j <= TDB_SUMMARY_HISTOGRAMS;
|
||||
j += TDB_SUMMARY_HISTOGRAMS) {
|
||||
ok1(tdb_summary(tdb, j, &summary) == TDB_SUCCESS);
|
||||
j <= NTDB_SUMMARY_HISTOGRAMS;
|
||||
j += NTDB_SUMMARY_HISTOGRAMS) {
|
||||
ok1(ntdb_summary(ntdb, j, &summary) == NTDB_SUCCESS);
|
||||
ok1(strstr(summary, "Number of records: 500\n"));
|
||||
ok1(strstr(summary, "Smallest/average/largest keys: 4/4/4\n"));
|
||||
ok1(strstr(summary, "Smallest/average/largest data: 0/2/4\n"));
|
||||
if (j == TDB_SUMMARY_HISTOGRAMS) {
|
||||
if (j == NTDB_SUMMARY_HISTOGRAMS) {
|
||||
ok1(strstr(summary, "|")
|
||||
&& strstr(summary, "*"));
|
||||
} else {
|
||||
@ -50,7 +50,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
free(summary);
|
||||
}
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
@ -14,14 +14,14 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
static struct tdb_context *tdb;
|
||||
static struct ntdb_context *ntdb;
|
||||
|
||||
void (*external_agent_free)(void *) = free;
|
||||
|
||||
static enum TDB_ERROR clear_if_first(int fd, void *arg)
|
||||
static enum NTDB_ERROR clear_if_first(int fd, void *arg)
|
||||
{
|
||||
/* We hold a lock offset 4 always, so we can tell if anyone is holding it.
|
||||
* (This is compatible with tdb1's TDB_CLEAR_IF_FIRST flag). */
|
||||
* (This is compatible with tdb's TDB_CLEAR_IF_FIRST flag). */
|
||||
struct flock fl;
|
||||
|
||||
fl.l_type = F_WRLCK;
|
||||
@ -33,73 +33,73 @@ static enum TDB_ERROR clear_if_first(int fd, void *arg)
|
||||
/* We must be first ones to open it! */
|
||||
diag("agent truncating file!");
|
||||
if (ftruncate(fd, 0) != 0) {
|
||||
return TDB_ERR_IO;
|
||||
return NTDB_ERR_IO;
|
||||
}
|
||||
}
|
||||
fl.l_type = F_RDLCK;
|
||||
if (fcntl(fd, F_SETLKW, &fl) != 0) {
|
||||
return TDB_ERR_IO;
|
||||
return NTDB_ERR_IO;
|
||||
}
|
||||
return TDB_SUCCESS;
|
||||
return NTDB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum agent_return do_operation(enum operation op, const char *name)
|
||||
{
|
||||
TDB_DATA k;
|
||||
NTDB_DATA k;
|
||||
enum agent_return ret;
|
||||
TDB_DATA data;
|
||||
enum TDB_ERROR ecode;
|
||||
union tdb_attribute cif;
|
||||
NTDB_DATA data;
|
||||
enum NTDB_ERROR ecode;
|
||||
union ntdb_attribute cif;
|
||||
|
||||
if (op != OPEN && op != OPEN_WITH_HOOK && !tdb) {
|
||||
diag("external: No tdb open!");
|
||||
if (op != OPEN && op != OPEN_WITH_HOOK && !ntdb) {
|
||||
diag("external: No ntdb open!");
|
||||
return OTHER_FAILURE;
|
||||
}
|
||||
|
||||
diag("external: %s", operation_name(op));
|
||||
|
||||
k = tdb_mkdata(name, strlen(name));
|
||||
k = ntdb_mkdata(name, strlen(name));
|
||||
|
||||
locking_would_block = 0;
|
||||
switch (op) {
|
||||
case OPEN:
|
||||
if (tdb) {
|
||||
diag("Already have tdb %s open", tdb_name(tdb));
|
||||
if (ntdb) {
|
||||
diag("Already have ntdb %s open", ntdb_name(ntdb));
|
||||
return OTHER_FAILURE;
|
||||
}
|
||||
tdb = tdb_open(name, TDB_DEFAULT, O_RDWR, 0, &tap_log_attr);
|
||||
if (!tdb) {
|
||||
ntdb = ntdb_open(name, NTDB_DEFAULT, O_RDWR, 0, &tap_log_attr);
|
||||
if (!ntdb) {
|
||||
if (!locking_would_block)
|
||||
diag("Opening tdb gave %s", strerror(errno));
|
||||
diag("Opening ntdb gave %s", strerror(errno));
|
||||
forget_locking();
|
||||
ret = OTHER_FAILURE;
|
||||
} else
|
||||
ret = SUCCESS;
|
||||
break;
|
||||
case OPEN_WITH_HOOK:
|
||||
if (tdb) {
|
||||
diag("Already have tdb %s open", tdb_name(tdb));
|
||||
if (ntdb) {
|
||||
diag("Already have ntdb %s open", ntdb_name(ntdb));
|
||||
return OTHER_FAILURE;
|
||||
}
|
||||
cif.openhook.base.attr = TDB_ATTRIBUTE_OPENHOOK;
|
||||
cif.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
|
||||
cif.openhook.base.next = &tap_log_attr;
|
||||
cif.openhook.fn = clear_if_first;
|
||||
tdb = tdb_open(name, TDB_DEFAULT, O_RDWR, 0, &cif);
|
||||
if (!tdb) {
|
||||
ntdb = ntdb_open(name, NTDB_DEFAULT, O_RDWR, 0, &cif);
|
||||
if (!ntdb) {
|
||||
if (!locking_would_block)
|
||||
diag("Opening tdb gave %s", strerror(errno));
|
||||
diag("Opening ntdb gave %s", strerror(errno));
|
||||
forget_locking();
|
||||
ret = OTHER_FAILURE;
|
||||
} else
|
||||
ret = SUCCESS;
|
||||
break;
|
||||
case FETCH:
|
||||
ecode = tdb_fetch(tdb, k, &data);
|
||||
if (ecode == TDB_ERR_NOEXIST) {
|
||||
ecode = ntdb_fetch(ntdb, k, &data);
|
||||
if (ecode == NTDB_ERR_NOEXIST) {
|
||||
ret = FAILED;
|
||||
} else if (ecode < 0) {
|
||||
ret = OTHER_FAILURE;
|
||||
} else if (!tdb_deq(data, k)) {
|
||||
} else if (!ntdb_deq(data, k)) {
|
||||
ret = OTHER_FAILURE;
|
||||
external_agent_free(data.dptr);
|
||||
} else {
|
||||
@ -108,23 +108,23 @@ static enum agent_return do_operation(enum operation op, const char *name)
|
||||
}
|
||||
break;
|
||||
case STORE:
|
||||
ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
ret = ntdb_store(ntdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
break;
|
||||
case TRANSACTION_START:
|
||||
ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
ret = ntdb_transaction_start(ntdb) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
break;
|
||||
case TRANSACTION_COMMIT:
|
||||
ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE;
|
||||
ret = ntdb_transaction_commit(ntdb)==0 ? SUCCESS : OTHER_FAILURE;
|
||||
break;
|
||||
case NEEDS_RECOVERY:
|
||||
ret = external_agent_needs_rec(tdb);
|
||||
ret = external_agent_needs_rec(ntdb);
|
||||
break;
|
||||
case CHECK:
|
||||
ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
ret = ntdb_check(ntdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
break;
|
||||
case CLOSE:
|
||||
ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
tdb = NULL;
|
||||
ret = ntdb_close(ntdb) == 0 ? SUCCESS : OTHER_FAILURE;
|
||||
ntdb = NULL;
|
||||
break;
|
||||
case SEND_SIGNAL:
|
||||
/* We do this async */
|
||||
@ -144,7 +144,7 @@ struct agent {
|
||||
int cmdfd, responsefd;
|
||||
};
|
||||
|
||||
/* Do this before doing any tdb stuff. Return handle, or NULL. */
|
||||
/* Do this before doing any ntdb stuff. Return handle, or NULL. */
|
||||
struct agent *prepare_external_agent(void)
|
||||
{
|
||||
int pid, ret;
|
@ -1,5 +1,5 @@
|
||||
#ifndef TDB2_TEST_EXTERNAL_AGENT_H
|
||||
#define TDB2_TEST_EXTERNAL_AGENT_H
|
||||
#ifndef NTDB_TEST_EXTERNAL_AGENT_H
|
||||
#define NTDB_TEST_EXTERNAL_AGENT_H
|
||||
|
||||
/* For locking tests, we need a different process to try things at
|
||||
* various times. */
|
||||
@ -16,7 +16,7 @@ enum operation {
|
||||
CLOSE,
|
||||
};
|
||||
|
||||
/* Do this before doing any tdb stuff. Return handle, or -1. */
|
||||
/* Do this before doing any ntdb stuff. Return handle, or -1. */
|
||||
struct agent *prepare_external_agent(void);
|
||||
|
||||
enum agent_return {
|
||||
@ -28,14 +28,14 @@ enum agent_return {
|
||||
};
|
||||
|
||||
/* Ask the external agent to try to do an operation.
|
||||
* name == tdb name for OPEN/OPEN_WITH_CLEAR_IF_FIRST,
|
||||
* name == ntdb name for OPEN/OPEN_WITH_CLEAR_IF_FIRST,
|
||||
* record name for FETCH/STORE (store stores name as data too)
|
||||
*/
|
||||
enum agent_return external_agent_operation(struct agent *handle,
|
||||
enum operation op,
|
||||
const char *name);
|
||||
|
||||
/* Hook into free() on tdb_data in external agent. */
|
||||
/* Hook into free() on ntdb_data in external agent. */
|
||||
extern void (*external_agent_free)(void *);
|
||||
|
||||
/* Mapping enum -> string. */
|
||||
@ -45,7 +45,7 @@ const char *operation_name(enum operation op);
|
||||
void free_external_agent(struct agent *agent);
|
||||
|
||||
/* Internal use: */
|
||||
struct tdb_context;
|
||||
enum agent_return external_agent_needs_rec(struct tdb_context *tdb);
|
||||
struct ntdb_context;
|
||||
enum agent_return external_agent_needs_rec(struct ntdb_context *ntdb);
|
||||
|
||||
#endif /* TDB2_TEST_EXTERNAL_AGENT_H */
|
||||
#endif /* NTDB_TEST_EXTERNAL_AGENT_H */
|
@ -51,8 +51,8 @@ bool exit_check_log(struct tlist_calls *history)
|
||||
if (failmatch(i, URANDOM_READ))
|
||||
continue;
|
||||
|
||||
/* Initial allocation of tdb doesn't log. */
|
||||
if (failmatch(i, INITIAL_TDB_MALLOC))
|
||||
/* Initial allocation of ntdb doesn't log. */
|
||||
if (failmatch(i, INITIAL_NTDB_MALLOC))
|
||||
continue;
|
||||
|
||||
/* We don't block "failures" on non-blocking locks. */
|
||||
@ -77,7 +77,7 @@ block_repeat_failures(struct tlist_calls *history)
|
||||
if (failtest_suppress)
|
||||
return FAIL_DONT_FAIL;
|
||||
|
||||
if (failmatch(last, INITIAL_TDB_MALLOC)
|
||||
if (failmatch(last, INITIAL_NTDB_MALLOC)
|
||||
|| failmatch(last, URANDOM_OPEN)
|
||||
|| failmatch(last, URANDOM_READ)) {
|
||||
return FAIL_PROBE;
|
@ -1,10 +1,10 @@
|
||||
#ifndef TDB2_TEST_FAILTEST_HELPER_H
|
||||
#define TDB2_TEST_FAILTEST_HELPER_H
|
||||
#ifndef NTDB_TEST_FAILTEST_HELPER_H
|
||||
#define NTDB_TEST_FAILTEST_HELPER_H
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* FIXME: Check these! */
|
||||
#define INITIAL_TDB_MALLOC "open.c", 403, FAILTEST_MALLOC
|
||||
#define INITIAL_NTDB_MALLOC "open.c", 403, FAILTEST_MALLOC
|
||||
#define URANDOM_OPEN "open.c", 62, FAILTEST_OPEN
|
||||
#define URANDOM_READ "open.c", 42, FAILTEST_READ
|
||||
|
||||
@ -16,4 +16,4 @@ enum failtest_result block_repeat_failures(struct tlist_calls *history);
|
||||
/* Set this to suppress failure. */
|
||||
extern bool failtest_suppress;
|
||||
|
||||
#endif /* TDB2_TEST_LOGGING_H */
|
||||
#endif /* NTDB_TEST_LOGGING_H */
|
7
lib/ntdb/test/helpapi-external-agent.c
Normal file
7
lib/ntdb/test/helpapi-external-agent.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include "external-agent.h"
|
||||
|
||||
/* This isn't possible with via the ntdb API, but this makes it link. */
|
||||
enum agent_return external_agent_needs_rec(struct ntdb_context *ntdb)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
7
lib/ntdb/test/helprun-external-agent.c
Normal file
7
lib/ntdb/test/helprun-external-agent.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include "external-agent.h"
|
||||
#include "private.h"
|
||||
|
||||
enum agent_return external_agent_needs_rec(struct ntdb_context *ntdb)
|
||||
{
|
||||
return ntdb_needs_recovery(ntdb) ? SUCCESS : FAILED;
|
||||
}
|
402
lib/ntdb/test/helprun-layout.c
Normal file
402
lib/ntdb/test/helprun-layout.c
Normal file
@ -0,0 +1,402 @@
|
||||
/* NTDB tools to create various canned database layouts. */
|
||||
#include "layout.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include "logging.h"
|
||||
|
||||
struct ntdb_layout *new_ntdb_layout(void)
|
||||
{
|
||||
struct ntdb_layout *layout = malloc(sizeof(*layout));
|
||||
layout->num_elems = 0;
|
||||
layout->elem = NULL;
|
||||
return layout;
|
||||
}
|
||||
|
||||
static void add(struct ntdb_layout *layout, union ntdb_layout_elem elem)
|
||||
{
|
||||
layout->elem = realloc(layout->elem,
|
||||
sizeof(layout->elem[0])
|
||||
* (layout->num_elems+1));
|
||||
layout->elem[layout->num_elems++] = elem;
|
||||
}
|
||||
|
||||
void ntdb_layout_add_freetable(struct ntdb_layout *layout)
|
||||
{
|
||||
union ntdb_layout_elem elem;
|
||||
elem.base.type = FREETABLE;
|
||||
add(layout, elem);
|
||||
}
|
||||
|
||||
void ntdb_layout_add_free(struct ntdb_layout *layout, ntdb_len_t len,
|
||||
unsigned ftable)
|
||||
{
|
||||
union ntdb_layout_elem elem;
|
||||
elem.base.type = FREE;
|
||||
elem.free.len = len;
|
||||
elem.free.ftable_num = ftable;
|
||||
add(layout, elem);
|
||||
}
|
||||
|
||||
void ntdb_layout_add_capability(struct ntdb_layout *layout,
|
||||
uint64_t type,
|
||||
bool write_breaks,
|
||||
bool check_breaks,
|
||||
bool open_breaks,
|
||||
ntdb_len_t extra)
|
||||
{
|
||||
union ntdb_layout_elem elem;
|
||||
elem.base.type = CAPABILITY;
|
||||
elem.capability.type = type;
|
||||
if (write_breaks)
|
||||
elem.capability.type |= NTDB_CAP_NOWRITE;
|
||||
if (open_breaks)
|
||||
elem.capability.type |= NTDB_CAP_NOOPEN;
|
||||
if (check_breaks)
|
||||
elem.capability.type |= NTDB_CAP_NOCHECK;
|
||||
elem.capability.extra = extra;
|
||||
add(layout, elem);
|
||||
}
|
||||
|
||||
static NTDB_DATA dup_key(NTDB_DATA key)
|
||||
{
|
||||
NTDB_DATA ret;
|
||||
ret.dsize = key.dsize;
|
||||
ret.dptr = malloc(ret.dsize);
|
||||
memcpy(ret.dptr, key.dptr, ret.dsize);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ntdb_layout_add_used(struct ntdb_layout *layout,
|
||||
NTDB_DATA key, NTDB_DATA data,
|
||||
ntdb_len_t extra)
|
||||
{
|
||||
union ntdb_layout_elem elem;
|
||||
elem.base.type = DATA;
|
||||
elem.used.key = dup_key(key);
|
||||
elem.used.data = dup_key(data);
|
||||
elem.used.extra = extra;
|
||||
add(layout, elem);
|
||||
}
|
||||
|
||||
static ntdb_len_t free_record_len(ntdb_len_t len)
|
||||
{
|
||||
return sizeof(struct ntdb_used_record) + len;
|
||||
}
|
||||
|
||||
static ntdb_len_t data_record_len(struct tle_used *used)
|
||||
{
|
||||
ntdb_len_t len;
|
||||
len = sizeof(struct ntdb_used_record)
|
||||
+ used->key.dsize + used->data.dsize + used->extra;
|
||||
assert(len >= sizeof(struct ntdb_free_record));
|
||||
return len;
|
||||
}
|
||||
|
||||
static ntdb_len_t hashtable_len(struct tle_hashtable *htable)
|
||||
{
|
||||
return sizeof(struct ntdb_used_record)
|
||||
+ (sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS)
|
||||
+ htable->extra;
|
||||
}
|
||||
|
||||
static ntdb_len_t capability_len(struct tle_capability *cap)
|
||||
{
|
||||
return sizeof(struct ntdb_capability) + cap->extra;
|
||||
}
|
||||
|
||||
static ntdb_len_t freetable_len(struct tle_freetable *ftable)
|
||||
{
|
||||
return sizeof(struct ntdb_freetable);
|
||||
}
|
||||
|
||||
static void set_free_record(void *mem, ntdb_len_t len)
|
||||
{
|
||||
/* We do all the work in add_to_freetable */
|
||||
}
|
||||
|
||||
static void add_zero_pad(struct ntdb_used_record *u, size_t len, size_t extra)
|
||||
{
|
||||
if (extra)
|
||||
((char *)(u + 1))[len] = '\0';
|
||||
}
|
||||
|
||||
static void set_data_record(void *mem, struct ntdb_context *ntdb,
|
||||
struct tle_used *used)
|
||||
{
|
||||
struct ntdb_used_record *u = mem;
|
||||
|
||||
set_header(ntdb, u, NTDB_USED_MAGIC, used->key.dsize, used->data.dsize,
|
||||
used->key.dsize + used->data.dsize + used->extra,
|
||||
ntdb_hash(ntdb, used->key.dptr, used->key.dsize));
|
||||
memcpy(u + 1, used->key.dptr, used->key.dsize);
|
||||
memcpy((char *)(u + 1) + used->key.dsize,
|
||||
used->data.dptr, used->data.dsize);
|
||||
add_zero_pad(u, used->key.dsize + used->data.dsize, used->extra);
|
||||
}
|
||||
|
||||
static void set_hashtable(void *mem, struct ntdb_context *ntdb,
|
||||
struct tle_hashtable *htable)
|
||||
{
|
||||
struct ntdb_used_record *u = mem;
|
||||
ntdb_len_t len = sizeof(ntdb_off_t) << NTDB_SUBLEVEL_HASH_BITS;
|
||||
|
||||
set_header(ntdb, u, NTDB_HTABLE_MAGIC, 0, len, len + htable->extra, 0);
|
||||
memset(u + 1, 0, len);
|
||||
add_zero_pad(u, len, htable->extra);
|
||||
}
|
||||
|
||||
static void set_capability(void *mem, struct ntdb_context *ntdb,
|
||||
struct tle_capability *cap, struct ntdb_header *hdr,
|
||||
ntdb_off_t last_cap)
|
||||
{
|
||||
struct ntdb_capability *c = mem;
|
||||
ntdb_len_t len = sizeof(*c) - sizeof(struct ntdb_used_record) + cap->extra;
|
||||
|
||||
c->type = cap->type;
|
||||
c->next = 0;
|
||||
set_header(ntdb, &c->hdr, NTDB_CAP_MAGIC, 0, len, len, 0);
|
||||
|
||||
/* Append to capability list. */
|
||||
if (!last_cap) {
|
||||
hdr->capabilities = cap->base.off;
|
||||
} else {
|
||||
c = (struct ntdb_capability *)((char *)hdr + last_cap);
|
||||
c->next = cap->base.off;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_freetable(void *mem, struct ntdb_context *ntdb,
|
||||
struct tle_freetable *freetable, struct ntdb_header *hdr,
|
||||
ntdb_off_t last_ftable)
|
||||
{
|
||||
struct ntdb_freetable *ftable = mem;
|
||||
memset(ftable, 0, sizeof(*ftable));
|
||||
set_header(ntdb, &ftable->hdr, NTDB_FTABLE_MAGIC, 0,
|
||||
sizeof(*ftable) - sizeof(ftable->hdr),
|
||||
sizeof(*ftable) - sizeof(ftable->hdr), 0);
|
||||
|
||||
if (last_ftable) {
|
||||
ftable = (struct ntdb_freetable *)((char *)hdr + last_ftable);
|
||||
ftable->next = freetable->base.off;
|
||||
} else {
|
||||
hdr->free_table = freetable->base.off;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_to_freetable(struct ntdb_context *ntdb,
|
||||
ntdb_off_t eoff,
|
||||
ntdb_off_t elen,
|
||||
unsigned ftable,
|
||||
struct tle_freetable *freetable)
|
||||
{
|
||||
ntdb->ftable_off = freetable->base.off;
|
||||
ntdb->ftable = ftable;
|
||||
add_free_record(ntdb, eoff, sizeof(struct ntdb_used_record) + elen,
|
||||
NTDB_LOCK_WAIT, false);
|
||||
}
|
||||
|
||||
static ntdb_off_t hbucket_off(ntdb_off_t group_start, unsigned ingroup)
|
||||
{
|
||||
return group_start
|
||||
+ (ingroup % (1 << NTDB_HASH_GROUP_BITS)) * sizeof(ntdb_off_t);
|
||||
}
|
||||
|
||||
/* Get bits from a value. */
|
||||
static uint32_t bits(uint64_t val, unsigned start, unsigned num)
|
||||
{
|
||||
assert(num <= 32);
|
||||
return (val >> start) & ((1U << num) - 1);
|
||||
}
|
||||
|
||||
/* We take bits from the top: that way we can lock whole sections of the hash
|
||||
* by using lock ranges. */
|
||||
static uint32_t use_bits(uint64_t h, unsigned num, unsigned *used)
|
||||
{
|
||||
*used += num;
|
||||
return bits(h, 64 - *used, num);
|
||||
}
|
||||
|
||||
static ntdb_off_t encode_offset(ntdb_off_t new_off, unsigned bucket,
|
||||
uint64_t h)
|
||||
{
|
||||
return bucket
|
||||
| new_off
|
||||
| ((uint64_t)bits(h, 64 - NTDB_OFF_UPPER_STEAL_EXTRA,
|
||||
NTDB_OFF_UPPER_STEAL_EXTRA)
|
||||
<< NTDB_OFF_HASH_EXTRA_BIT);
|
||||
}
|
||||
|
||||
/* FIXME: Our hash table handling here is primitive: we don't expand! */
|
||||
static void add_to_hashtable(struct ntdb_context *ntdb,
|
||||
ntdb_off_t eoff,
|
||||
NTDB_DATA key)
|
||||
{
|
||||
uint64_t h = ntdb_hash(ntdb, key.dptr, key.dsize);
|
||||
ntdb_off_t b_off, group_start;
|
||||
unsigned i, group, in_group;
|
||||
unsigned used = 0;
|
||||
|
||||
group = use_bits(h, NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS, &used);
|
||||
in_group = use_bits(h, NTDB_HASH_GROUP_BITS, &used);
|
||||
|
||||
group_start = offsetof(struct ntdb_header, hashtable)
|
||||
+ group * (sizeof(ntdb_off_t) << NTDB_HASH_GROUP_BITS);
|
||||
|
||||
for (i = 0; i < (1 << NTDB_HASH_GROUP_BITS); i++) {
|
||||
unsigned bucket = (in_group + i) % (1 << NTDB_HASH_GROUP_BITS);
|
||||
|
||||
b_off = hbucket_off(group_start, bucket);
|
||||
if (ntdb_read_off(ntdb, b_off) == 0) {
|
||||
ntdb_write_off(ntdb, b_off,
|
||||
encode_offset(eoff, in_group, h));
|
||||
return;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static struct tle_freetable *find_ftable(struct ntdb_layout *layout, unsigned num)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < layout->num_elems; i++) {
|
||||
if (layout->elem[i].base.type != FREETABLE)
|
||||
continue;
|
||||
if (num == 0)
|
||||
return &layout->elem[i].ftable;
|
||||
num--;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
/* FIXME: Support NTDB_CONVERT */
|
||||
struct ntdb_context *ntdb_layout_get(struct ntdb_layout *layout,
|
||||
void (*freefn)(void *),
|
||||
union ntdb_attribute *attr)
|
||||
{
|
||||
unsigned int i;
|
||||
ntdb_off_t off, len, last_ftable, last_cap;
|
||||
char *mem;
|
||||
struct ntdb_context *ntdb;
|
||||
|
||||
off = sizeof(struct ntdb_header);
|
||||
|
||||
/* First pass of layout: calc lengths */
|
||||
for (i = 0; i < layout->num_elems; i++) {
|
||||
union ntdb_layout_elem *e = &layout->elem[i];
|
||||
e->base.off = off;
|
||||
switch (e->base.type) {
|
||||
case FREETABLE:
|
||||
len = freetable_len(&e->ftable);
|
||||
break;
|
||||
case FREE:
|
||||
len = free_record_len(e->free.len);
|
||||
break;
|
||||
case DATA:
|
||||
len = data_record_len(&e->used);
|
||||
break;
|
||||
case HASHTABLE:
|
||||
len = hashtable_len(&e->hashtable);
|
||||
break;
|
||||
case CAPABILITY:
|
||||
len = capability_len(&e->capability);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
off += len;
|
||||
}
|
||||
|
||||
mem = malloc(off);
|
||||
/* Fill with some weird pattern. */
|
||||
memset(mem, 0x99, off);
|
||||
/* Now populate our header, cribbing from a real NTDB header. */
|
||||
ntdb = ntdb_open(NULL, NTDB_INTERNAL, O_RDWR, 0, attr);
|
||||
memcpy(mem, ntdb->file->map_ptr, sizeof(struct ntdb_header));
|
||||
|
||||
/* Mug the ntdb we have to make it use this. */
|
||||
freefn(ntdb->file->map_ptr);
|
||||
ntdb->file->map_ptr = mem;
|
||||
ntdb->file->map_size = off;
|
||||
|
||||
last_ftable = 0;
|
||||
last_cap = 0;
|
||||
for (i = 0; i < layout->num_elems; i++) {
|
||||
union ntdb_layout_elem *e = &layout->elem[i];
|
||||
switch (e->base.type) {
|
||||
case FREETABLE:
|
||||
set_freetable(mem + e->base.off, ntdb, &e->ftable,
|
||||
(struct ntdb_header *)mem, last_ftable);
|
||||
last_ftable = e->base.off;
|
||||
break;
|
||||
case FREE:
|
||||
set_free_record(mem + e->base.off, e->free.len);
|
||||
break;
|
||||
case DATA:
|
||||
set_data_record(mem + e->base.off, ntdb, &e->used);
|
||||
break;
|
||||
case HASHTABLE:
|
||||
set_hashtable(mem + e->base.off, ntdb, &e->hashtable);
|
||||
break;
|
||||
case CAPABILITY:
|
||||
set_capability(mem + e->base.off, ntdb, &e->capability,
|
||||
(struct ntdb_header *)mem, last_cap);
|
||||
last_cap = e->base.off;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Must have a free table! */
|
||||
assert(last_ftable);
|
||||
|
||||
/* Now fill the free and hash tables. */
|
||||
for (i = 0; i < layout->num_elems; i++) {
|
||||
union ntdb_layout_elem *e = &layout->elem[i];
|
||||
switch (e->base.type) {
|
||||
case FREE:
|
||||
add_to_freetable(ntdb, e->base.off, e->free.len,
|
||||
e->free.ftable_num,
|
||||
find_ftable(layout, e->free.ftable_num));
|
||||
break;
|
||||
case DATA:
|
||||
add_to_hashtable(ntdb, e->base.off, e->used.key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ntdb->ftable_off = find_ftable(layout, 0)->base.off;
|
||||
return ntdb;
|
||||
}
|
||||
|
||||
void ntdb_layout_write(struct ntdb_layout *layout, void (*freefn)(void *),
|
||||
union ntdb_attribute *attr, const char *filename)
|
||||
{
|
||||
struct ntdb_context *ntdb = ntdb_layout_get(layout, freefn, attr);
|
||||
int fd;
|
||||
|
||||
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600);
|
||||
if (fd < 0)
|
||||
err(1, "opening %s for writing", filename);
|
||||
if (write(fd, ntdb->file->map_ptr, ntdb->file->map_size)
|
||||
!= ntdb->file->map_size)
|
||||
err(1, "writing %s", filename);
|
||||
close(fd);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
void ntdb_layout_free(struct ntdb_layout *layout)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < layout->num_elems; i++) {
|
||||
if (layout->elem[i].base.type == DATA) {
|
||||
free(layout->elem[i].used.key.dptr);
|
||||
free(layout->elem[i].used.data.dptr);
|
||||
}
|
||||
}
|
||||
free(layout->elem);
|
||||
free(layout);
|
||||
}
|
@ -1,35 +1,35 @@
|
||||
#ifndef TDB2_TEST_LAYOUT_H
|
||||
#define TDB2_TEST_LAYOUT_H
|
||||
#ifndef NTDB_TEST_LAYOUT_H
|
||||
#define NTDB_TEST_LAYOUT_H
|
||||
#include "private.h"
|
||||
|
||||
struct tdb_layout *new_tdb_layout(void);
|
||||
void tdb_layout_add_freetable(struct tdb_layout *layout);
|
||||
void tdb_layout_add_free(struct tdb_layout *layout, tdb_len_t len,
|
||||
struct ntdb_layout *new_ntdb_layout(void);
|
||||
void ntdb_layout_add_freetable(struct ntdb_layout *layout);
|
||||
void ntdb_layout_add_free(struct ntdb_layout *layout, ntdb_len_t len,
|
||||
unsigned ftable);
|
||||
void tdb_layout_add_used(struct tdb_layout *layout,
|
||||
TDB_DATA key, TDB_DATA data,
|
||||
tdb_len_t extra);
|
||||
void tdb_layout_add_capability(struct tdb_layout *layout,
|
||||
void ntdb_layout_add_used(struct ntdb_layout *layout,
|
||||
NTDB_DATA key, NTDB_DATA data,
|
||||
ntdb_len_t extra);
|
||||
void ntdb_layout_add_capability(struct ntdb_layout *layout,
|
||||
uint64_t type,
|
||||
bool write_breaks,
|
||||
bool check_breaks,
|
||||
bool open_breaks,
|
||||
tdb_len_t extra);
|
||||
ntdb_len_t extra);
|
||||
|
||||
#if 0 /* FIXME: Allow allocation of subtables */
|
||||
void tdb_layout_add_hashtable(struct tdb_layout *layout,
|
||||
void ntdb_layout_add_hashtable(struct ntdb_layout *layout,
|
||||
int htable_parent, /* -1 == toplevel */
|
||||
unsigned int bucket,
|
||||
tdb_len_t extra);
|
||||
ntdb_len_t extra);
|
||||
#endif
|
||||
/* freefn is needed if we're using failtest_free. */
|
||||
struct tdb_context *tdb_layout_get(struct tdb_layout *layout,
|
||||
struct ntdb_context *ntdb_layout_get(struct ntdb_layout *layout,
|
||||
void (*freefn)(void *),
|
||||
union tdb_attribute *attr);
|
||||
void tdb_layout_write(struct tdb_layout *layout, void (*freefn)(void *),
|
||||
union tdb_attribute *attr, const char *filename);
|
||||
union ntdb_attribute *attr);
|
||||
void ntdb_layout_write(struct ntdb_layout *layout, void (*freefn)(void *),
|
||||
union ntdb_attribute *attr, const char *filename);
|
||||
|
||||
void tdb_layout_free(struct tdb_layout *layout);
|
||||
void ntdb_layout_free(struct ntdb_layout *layout);
|
||||
|
||||
enum layout_type {
|
||||
FREETABLE, FREE, DATA, HASHTABLE, CAPABILITY
|
||||
@ -38,7 +38,7 @@ enum layout_type {
|
||||
/* Shared by all union members. */
|
||||
struct tle_base {
|
||||
enum layout_type type;
|
||||
tdb_off_t off;
|
||||
ntdb_off_t off;
|
||||
};
|
||||
|
||||
struct tle_freetable {
|
||||
@ -47,31 +47,31 @@ struct tle_freetable {
|
||||
|
||||
struct tle_free {
|
||||
struct tle_base base;
|
||||
tdb_len_t len;
|
||||
ntdb_len_t len;
|
||||
unsigned ftable_num;
|
||||
};
|
||||
|
||||
struct tle_used {
|
||||
struct tle_base base;
|
||||
TDB_DATA key;
|
||||
TDB_DATA data;
|
||||
tdb_len_t extra;
|
||||
NTDB_DATA key;
|
||||
NTDB_DATA data;
|
||||
ntdb_len_t extra;
|
||||
};
|
||||
|
||||
struct tle_hashtable {
|
||||
struct tle_base base;
|
||||
int parent;
|
||||
unsigned int bucket;
|
||||
tdb_len_t extra;
|
||||
ntdb_len_t extra;
|
||||
};
|
||||
|
||||
struct tle_capability {
|
||||
struct tle_base base;
|
||||
uint64_t type;
|
||||
tdb_len_t extra;
|
||||
ntdb_len_t extra;
|
||||
};
|
||||
|
||||
union tdb_layout_elem {
|
||||
union ntdb_layout_elem {
|
||||
struct tle_base base;
|
||||
struct tle_freetable ftable;
|
||||
struct tle_free free;
|
||||
@ -80,8 +80,8 @@ union tdb_layout_elem {
|
||||
struct tle_capability capability;
|
||||
};
|
||||
|
||||
struct tdb_layout {
|
||||
struct ntdb_layout {
|
||||
unsigned int num_elems;
|
||||
union tdb_layout_elem *elem;
|
||||
union ntdb_layout_elem *elem;
|
||||
};
|
||||
#endif /* TDB2_TEST_LAYOUT_H */
|
||||
#endif /* NTDB_TEST_LAYOUT_H */
|
@ -1,5 +1,5 @@
|
||||
/* We save the locks so we can reaquire them. */
|
||||
#include "private.h" /* For TDB_HASH_LOCK_START, etc. */
|
||||
#include "private.h" /* For NTDB_HASH_LOCK_START, etc. */
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
@ -88,8 +88,8 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
|
||||
if (fl_end > i->off && fl_end < i_end)
|
||||
break;
|
||||
|
||||
/* tdb_allrecord_lock does this, handle adjacent: */
|
||||
if (fl->l_start > TDB_HASH_LOCK_START
|
||||
/* ntdb_allrecord_lock does this, handle adjacent: */
|
||||
if (fl->l_start > NTDB_HASH_LOCK_START
|
||||
&& fl->l_start == i_end && fl->l_type == i->type) {
|
||||
if (ret == 0) {
|
||||
i->len = fl->l_len
|
||||
@ -102,8 +102,8 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
|
||||
if (i) {
|
||||
/* Special case: upgrade of allrecord lock. */
|
||||
if (i->type == F_RDLCK && fl->l_type == F_WRLCK
|
||||
&& i->off == TDB_HASH_LOCK_START
|
||||
&& fl->l_start == TDB_HASH_LOCK_START
|
||||
&& i->off == NTDB_HASH_LOCK_START
|
||||
&& fl->l_start == NTDB_HASH_LOCK_START
|
||||
&& i->len == 0
|
||||
&& fl->l_len == 0) {
|
||||
if (ret == 0)
|
@ -8,21 +8,21 @@ const char *log_prefix = "";
|
||||
char *log_last = NULL;
|
||||
bool suppress_logging;
|
||||
|
||||
union tdb_attribute tap_log_attr = {
|
||||
.log = { .base = { .attr = TDB_ATTRIBUTE_LOG },
|
||||
union ntdb_attribute tap_log_attr = {
|
||||
.log = { .base = { .attr = NTDB_ATTRIBUTE_LOG },
|
||||
.fn = tap_log_fn }
|
||||
};
|
||||
|
||||
void tap_log_fn(struct tdb_context *tdb,
|
||||
enum tdb_log_level level,
|
||||
enum TDB_ERROR ecode,
|
||||
void tap_log_fn(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message, void *priv)
|
||||
{
|
||||
if (suppress_logging)
|
||||
return;
|
||||
|
||||
diag("tdb log level %u: %s: %s%s",
|
||||
level, tdb_errorstr(ecode), log_prefix, message);
|
||||
diag("ntdb log level %u: %s: %s%s",
|
||||
level, ntdb_errorstr(ecode), log_prefix, message);
|
||||
if (log_last)
|
||||
free(log_last);
|
||||
log_last = strdup(message);
|
17
lib/ntdb/test/logging.h
Normal file
17
lib/ntdb/test/logging.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef NTDB_TEST_LOGGING_H
|
||||
#define NTDB_TEST_LOGGING_H
|
||||
#include "ntdb.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
extern bool suppress_logging;
|
||||
extern const char *log_prefix;
|
||||
extern unsigned tap_log_messages;
|
||||
extern union ntdb_attribute tap_log_attr;
|
||||
extern char *log_last;
|
||||
|
||||
void tap_log_fn(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message, void *priv);
|
||||
#endif /* NTDB_TEST_LOGGING_H */
|
@ -6,6 +6,6 @@
|
||||
#include "lock.c"
|
||||
#include "open.c"
|
||||
#include "summary.c"
|
||||
#include "tdb.c"
|
||||
#include "ntdb.c"
|
||||
#include "transaction.c"
|
||||
#include "traverse.c"
|
@ -1,24 +1,24 @@
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_used_record rec;
|
||||
struct tdb_context tdb = { .log_fn = tap_log_fn };
|
||||
struct ntdb_used_record rec;
|
||||
struct ntdb_context ntdb = { .log_fn = tap_log_fn };
|
||||
|
||||
plan_tests(64 + 32 + 48*6 + 1);
|
||||
|
||||
/* We should be able to encode any data value. */
|
||||
for (i = 0; i < 64; i++)
|
||||
ok1(set_header(&tdb, &rec, TDB_USED_MAGIC, 0, 1ULL << i,
|
||||
ok1(set_header(&ntdb, &rec, NTDB_USED_MAGIC, 0, 1ULL << i,
|
||||
1ULL << i, 0) == 0);
|
||||
|
||||
/* And any key and data with < 64 bits between them. */
|
||||
for (i = 0; i < 32; i++) {
|
||||
tdb_len_t dlen = 1ULL >> (63 - i), klen = 1ULL << i;
|
||||
ok1(set_header(&tdb, &rec, TDB_USED_MAGIC, klen, dlen,
|
||||
ntdb_len_t dlen = 1ULL >> (63 - i), klen = 1ULL << i;
|
||||
ok1(set_header(&ntdb, &rec, NTDB_USED_MAGIC, klen, dlen,
|
||||
klen + dlen, 0) == 0);
|
||||
}
|
||||
|
||||
@ -28,13 +28,13 @@ int main(int argc, char *argv[])
|
||||
uint64_t klen = 1ULL << (i < 16 ? i : 15);
|
||||
uint64_t dlen = 1ULL << i;
|
||||
uint64_t xlen = 1ULL << (i < 32 ? i : 31);
|
||||
ok1(set_header(&tdb, &rec, TDB_USED_MAGIC, klen, dlen,
|
||||
ok1(set_header(&ntdb, &rec, NTDB_USED_MAGIC, klen, dlen,
|
||||
klen+dlen+xlen, h) == 0);
|
||||
ok1(rec_key_length(&rec) == klen);
|
||||
ok1(rec_data_length(&rec) == dlen);
|
||||
ok1(rec_extra_padding(&rec) == xlen);
|
||||
ok1((uint64_t)rec_hash(&rec) == h);
|
||||
ok1(rec_magic(&rec) == TDB_USED_MAGIC);
|
||||
ok1(rec_magic(&rec) == NTDB_USED_MAGIC);
|
||||
}
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
@ -1,4 +1,4 @@
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
|
||||
static unsigned int dumb_fls(uint64_t num)
|
@ -1,5 +1,5 @@
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include "logging.h"
|
||||
@ -8,25 +8,25 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
failtest_init(argc, argv);
|
||||
failtest_hook = block_repeat_failures;
|
||||
failtest_exit_check = exit_check_log;
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 3);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-new_database.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-new_database.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
failtest_exit(exit_status());
|
||||
|
||||
failtest_suppress = true;
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
failtest_suppress = false;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
if (!ok1(tap_log_messages == 0))
|
||||
break;
|
||||
}
|
62
lib/ntdb/test/run-02-expand.c
Normal file
62
lib/ntdb/test/run-02-expand.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include "logging.h"
|
||||
#include "failtest_helper.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
uint64_t val;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 11 + 1);
|
||||
|
||||
failtest_init(argc, argv);
|
||||
failtest_hook = block_repeat_failures;
|
||||
failtest_exit_check = exit_check_log;
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
failtest_suppress = true;
|
||||
ntdb = ntdb_open("run-expand.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(ntdb))
|
||||
break;
|
||||
|
||||
val = ntdb->file->map_size;
|
||||
/* Need some hash lock for expand. */
|
||||
ok1(ntdb_lock_hashes(ntdb, 0, 1, F_WRLCK, NTDB_LOCK_WAIT) == 0);
|
||||
failtest_suppress = false;
|
||||
if (!ok1(ntdb_expand(ntdb, 1) == 0)) {
|
||||
failtest_suppress = true;
|
||||
ntdb_close(ntdb);
|
||||
break;
|
||||
}
|
||||
failtest_suppress = true;
|
||||
|
||||
ok1(ntdb->file->map_size >= val + 1 * NTDB_EXTENSION_FACTOR);
|
||||
ok1(ntdb_unlock_hashes(ntdb, 0, 1, F_WRLCK) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
val = ntdb->file->map_size;
|
||||
ok1(ntdb_lock_hashes(ntdb, 0, 1, F_WRLCK, NTDB_LOCK_WAIT) == 0);
|
||||
failtest_suppress = false;
|
||||
if (!ok1(ntdb_expand(ntdb, 1024) == 0)) {
|
||||
failtest_suppress = true;
|
||||
ntdb_close(ntdb);
|
||||
break;
|
||||
}
|
||||
failtest_suppress = true;
|
||||
ok1(ntdb_unlock_hashes(ntdb, 0, 1, F_WRLCK) == 0);
|
||||
ok1(ntdb->file->map_size >= val + 1024 * NTDB_EXTENSION_FACTOR);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
failtest_exit(exit_status());
|
||||
}
|
178
lib/ntdb/test/run-03-coalesce.c
Normal file
178
lib/ntdb/test/run-03-coalesce.c
Normal file
@ -0,0 +1,178 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
#include "layout.h"
|
||||
|
||||
static ntdb_len_t free_record_length(struct ntdb_context *ntdb, ntdb_off_t off)
|
||||
{
|
||||
struct ntdb_free_record f;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
ecode = ntdb_read_convert(ntdb, off, &f, sizeof(f));
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ecode;
|
||||
if (frec_magic(&f) != NTDB_FREE_MAGIC)
|
||||
return NTDB_ERR_CORRUPT;
|
||||
return frec_len(&f);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
ntdb_off_t b_off, test;
|
||||
struct ntdb_context *ntdb;
|
||||
struct ntdb_layout *layout;
|
||||
NTDB_DATA data, key;
|
||||
ntdb_len_t len;
|
||||
|
||||
/* FIXME: Test NTDB_CONVERT */
|
||||
/* FIXME: Test lock order fail. */
|
||||
|
||||
plan_tests(42);
|
||||
data = ntdb_mkdata("world", 5);
|
||||
key = ntdb_mkdata("hello", 5);
|
||||
|
||||
/* No coalescing can be done due to EOF */
|
||||
layout = new_ntdb_layout();
|
||||
ntdb_layout_add_freetable(layout);
|
||||
len = 1024;
|
||||
ntdb_layout_add_free(layout, len, 0);
|
||||
ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
|
||||
/* NOMMAP is for lockcheck. */
|
||||
ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off) == len);
|
||||
|
||||
/* Figure out which bucket free entry is. */
|
||||
b_off = bucket_off(ntdb->ftable_off, size_to_bucket(len));
|
||||
/* Lock and fail to coalesce. */
|
||||
ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
|
||||
test = layout->elem[1].base.off;
|
||||
ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, len, &test)
|
||||
== 0);
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off) == len);
|
||||
ok1(test == layout->elem[1].base.off);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
ntdb_layout_free(layout);
|
||||
|
||||
/* No coalescing can be done due to used record */
|
||||
layout = new_ntdb_layout();
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_free(layout, 1024, 0);
|
||||
ntdb_layout_add_used(layout, key, data, 6);
|
||||
ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
|
||||
/* NOMMAP is for lockcheck. */
|
||||
ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Figure out which bucket free entry is. */
|
||||
b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
|
||||
/* Lock and fail to coalesce. */
|
||||
ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
|
||||
test = layout->elem[1].base.off;
|
||||
ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
|
||||
== 0);
|
||||
ntdb_unlock_free_bucket(ntdb, b_off);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
|
||||
ok1(test == layout->elem[1].base.off);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
ntdb_layout_free(layout);
|
||||
|
||||
/* Coalescing can be done due to two free records, then EOF */
|
||||
layout = new_ntdb_layout();
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_free(layout, 1024, 0);
|
||||
ntdb_layout_add_free(layout, 2048, 0);
|
||||
ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
|
||||
/* NOMMAP is for lockcheck. */
|
||||
ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
|
||||
ok1(free_record_length(ntdb, layout->elem[2].base.off) == 2048);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Figure out which bucket (first) free entry is. */
|
||||
b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
|
||||
/* Lock and coalesce. */
|
||||
ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
|
||||
test = layout->elem[2].base.off;
|
||||
ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
|
||||
== 1024 + sizeof(struct ntdb_used_record) + 2048);
|
||||
/* Should tell us it's erased this one... */
|
||||
ok1(test == NTDB_ERR_NOEXIST);
|
||||
ok1(ntdb->file->allrecord_lock.count == 0 && ntdb->file->num_lockrecs == 0);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off)
|
||||
== 1024 + sizeof(struct ntdb_used_record) + 2048);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
ntdb_layout_free(layout);
|
||||
|
||||
/* Coalescing can be done due to two free records, then data */
|
||||
layout = new_ntdb_layout();
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_free(layout, 1024, 0);
|
||||
ntdb_layout_add_free(layout, 512, 0);
|
||||
ntdb_layout_add_used(layout, key, data, 6);
|
||||
ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
|
||||
/* NOMMAP is for lockcheck. */
|
||||
ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
|
||||
ok1(free_record_length(ntdb, layout->elem[2].base.off) == 512);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Figure out which bucket free entry is. */
|
||||
b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
|
||||
/* Lock and coalesce. */
|
||||
ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
|
||||
test = layout->elem[2].base.off;
|
||||
ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
|
||||
== 1024 + sizeof(struct ntdb_used_record) + 512);
|
||||
ok1(ntdb->file->allrecord_lock.count == 0 && ntdb->file->num_lockrecs == 0);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off)
|
||||
== 1024 + sizeof(struct ntdb_used_record) + 512);
|
||||
ok1(test == NTDB_ERR_NOEXIST);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
ntdb_layout_free(layout);
|
||||
|
||||
/* Coalescing can be done due to three free records, then EOF */
|
||||
layout = new_ntdb_layout();
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_free(layout, 1024, 0);
|
||||
ntdb_layout_add_free(layout, 512, 0);
|
||||
ntdb_layout_add_free(layout, 256, 0);
|
||||
ntdb_layout_write(layout, free, &tap_log_attr, "run-03-coalesce.ntdb");
|
||||
/* NOMMAP is for lockcheck. */
|
||||
ntdb = ntdb_open("run-03-coalesce.ntdb", NTDB_NOMMAP, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off) == 1024);
|
||||
ok1(free_record_length(ntdb, layout->elem[2].base.off) == 512);
|
||||
ok1(free_record_length(ntdb, layout->elem[3].base.off) == 256);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Figure out which bucket free entry is. */
|
||||
b_off = bucket_off(ntdb->ftable_off, size_to_bucket(1024));
|
||||
/* Lock and coalesce. */
|
||||
ok1(ntdb_lock_free_bucket(ntdb, b_off, NTDB_LOCK_WAIT) == 0);
|
||||
test = layout->elem[2].base.off;
|
||||
ok1(coalesce(ntdb, layout->elem[1].base.off, b_off, 1024, &test)
|
||||
== 1024 + sizeof(struct ntdb_used_record) + 512
|
||||
+ sizeof(struct ntdb_used_record) + 256);
|
||||
ok1(ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0);
|
||||
ok1(free_record_length(ntdb, layout->elem[1].base.off)
|
||||
== 1024 + sizeof(struct ntdb_used_record) + 512
|
||||
+ sizeof(struct ntdb_used_record) + 256);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
ntdb_layout_free(layout);
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
260
lib/ntdb/test/run-04-basichash.c
Normal file
260
lib/ntdb/test/run-04-basichash.c
Normal file
@ -0,0 +1,260 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* We rig the hash so adjacent-numbered records always clash. */
|
||||
static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
|
||||
{
|
||||
return ((uint64_t)*(const unsigned int *)key)
|
||||
<< (64 - NTDB_TOPLEVEL_HASH_BITS - 1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct ntdb_context *ntdb;
|
||||
unsigned int v;
|
||||
struct ntdb_used_record rec;
|
||||
NTDB_DATA key = { (unsigned char *)&v, sizeof(v) };
|
||||
NTDB_DATA dbuf = { (unsigned char *)&v, sizeof(v) };
|
||||
union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = clash } };
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT,
|
||||
};
|
||||
|
||||
hattr.base.next = &tap_log_attr;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0])
|
||||
* (91 + (2 * ((1 << NTDB_HASH_GROUP_BITS) - 1))) + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
struct hash_info h;
|
||||
ntdb_off_t new_off, off, subhash;
|
||||
|
||||
ntdb = ntdb_open("run-04-basichash.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
v = 0;
|
||||
/* Should not find it. */
|
||||
ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in group 0, bucket 0. */
|
||||
ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 0);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
|
||||
|
||||
/* Should have lock on bucket 0 */
|
||||
ok1(h.hlock_start == 0);
|
||||
ok1(h.hlock_range ==
|
||||
1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
|
||||
ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
|
||||
ok1((ntdb->flags & NTDB_NOLOCK)
|
||||
|| ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
|
||||
/* FIXME: Check lock length */
|
||||
|
||||
/* Allocate a new record. */
|
||||
new_off = alloc(ntdb, key.dsize, dbuf.dsize, h.h,
|
||||
NTDB_USED_MAGIC, false);
|
||||
ok1(!NTDB_OFF_IS_ERR(new_off));
|
||||
|
||||
/* We should be able to add it now. */
|
||||
ok1(add_to_hash(ntdb, &h, new_off) == 0);
|
||||
|
||||
/* Make sure we fill it in for later finding. */
|
||||
off = new_off + sizeof(struct ntdb_used_record);
|
||||
ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
|
||||
off += key.dsize;
|
||||
ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
|
||||
|
||||
/* We should be able to unlock that OK. */
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_WRLCK) == 0);
|
||||
|
||||
/* Database should be consistent. */
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Now, this should give a successful lookup. */
|
||||
ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL)
|
||||
== new_off);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in group 0, bucket 0. */
|
||||
ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 0);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
|
||||
|
||||
/* Should have lock on bucket 0 */
|
||||
ok1(h.hlock_start == 0);
|
||||
ok1(h.hlock_range ==
|
||||
1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
|
||||
ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
|
||||
ok1((ntdb->flags & NTDB_NOLOCK)
|
||||
|| ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
|
||||
/* FIXME: Check lock length */
|
||||
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_WRLCK) == 0);
|
||||
|
||||
/* Database should be consistent. */
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Test expansion. */
|
||||
v = 1;
|
||||
ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in group 0, bucket 1. */
|
||||
ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 1);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
|
||||
|
||||
/* Should have lock on bucket 0 */
|
||||
ok1(h.hlock_start == 0);
|
||||
ok1(h.hlock_range ==
|
||||
1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
|
||||
ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
|
||||
ok1((ntdb->flags & NTDB_NOLOCK)
|
||||
|| ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
|
||||
/* FIXME: Check lock length */
|
||||
|
||||
/* Make it expand 0'th bucket. */
|
||||
ok1(expand_group(ntdb, &h) == 0);
|
||||
/* First one should be subhash, next should be empty. */
|
||||
ok1(is_subhash(h.group[0]));
|
||||
subhash = (h.group[0] & NTDB_OFF_MASK);
|
||||
for (j = 1; j < (1 << NTDB_HASH_GROUP_BITS); j++)
|
||||
ok1(h.group[j] == 0);
|
||||
|
||||
ok1(ntdb_write_convert(ntdb, h.group_start,
|
||||
h.group, sizeof(h.group)) == 0);
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_WRLCK) == 0);
|
||||
|
||||
/* Should be happy with expansion. */
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Should be able to find it. */
|
||||
v = 0;
|
||||
ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL)
|
||||
== new_off);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in expanded group 0, bucket 0. */
|
||||
ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 0);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
|
||||
+ NTDB_SUBLEVEL_HASH_BITS);
|
||||
|
||||
/* Should have lock on bucket 0 */
|
||||
ok1(h.hlock_start == 0);
|
||||
ok1(h.hlock_range ==
|
||||
1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
|
||||
ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
|
||||
ok1((ntdb->flags & NTDB_NOLOCK)
|
||||
|| ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
|
||||
/* FIXME: Check lock length */
|
||||
|
||||
/* Simple delete should work. */
|
||||
ok1(delete_from_hash(ntdb, &h) == 0);
|
||||
ok1(add_free_record(ntdb, new_off,
|
||||
sizeof(struct ntdb_used_record)
|
||||
+ rec_key_length(&rec)
|
||||
+ rec_data_length(&rec)
|
||||
+ rec_extra_padding(&rec),
|
||||
NTDB_LOCK_NOWAIT, false) == 0);
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_WRLCK) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Test second-level expansion: should expand 0th bucket. */
|
||||
v = 0;
|
||||
ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in group 0, bucket 0. */
|
||||
ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 0);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS+NTDB_SUBLEVEL_HASH_BITS);
|
||||
|
||||
/* Should have lock on bucket 0 */
|
||||
ok1(h.hlock_start == 0);
|
||||
ok1(h.hlock_range ==
|
||||
1ULL << (64-(NTDB_TOPLEVEL_HASH_BITS-NTDB_HASH_GROUP_BITS)));
|
||||
ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
|
||||
ok1((ntdb->flags & NTDB_NOLOCK)
|
||||
|| ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
|
||||
/* FIXME: Check lock length */
|
||||
|
||||
ok1(expand_group(ntdb, &h) == 0);
|
||||
/* First one should be subhash, next should be empty. */
|
||||
ok1(is_subhash(h.group[0]));
|
||||
subhash = (h.group[0] & NTDB_OFF_MASK);
|
||||
for (j = 1; j < (1 << NTDB_HASH_GROUP_BITS); j++)
|
||||
ok1(h.group[j] == 0);
|
||||
ok1(ntdb_write_convert(ntdb, h.group_start,
|
||||
h.group, sizeof(h.group)) == 0);
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_WRLCK) == 0);
|
||||
|
||||
/* Should be happy with expansion. */
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in group 0, bucket 0. */
|
||||
ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 0);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
|
||||
+ NTDB_SUBLEVEL_HASH_BITS * 2);
|
||||
|
||||
/* We should be able to add it now. */
|
||||
/* Allocate a new record. */
|
||||
new_off = alloc(ntdb, key.dsize, dbuf.dsize, h.h,
|
||||
NTDB_USED_MAGIC, false);
|
||||
ok1(!NTDB_OFF_IS_ERR(new_off));
|
||||
ok1(add_to_hash(ntdb, &h, new_off) == 0);
|
||||
|
||||
/* Make sure we fill it in for later finding. */
|
||||
off = new_off + sizeof(struct ntdb_used_record);
|
||||
ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
|
||||
off += key.dsize;
|
||||
ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
|
||||
|
||||
/* We should be able to unlock that OK. */
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_WRLCK) == 0);
|
||||
|
||||
/* Database should be consistent. */
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Should be able to find it. */
|
||||
v = 0;
|
||||
ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL)
|
||||
== new_off);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in expanded group 0, bucket 0. */
|
||||
ok1(h.group_start == subhash + sizeof(struct ntdb_used_record));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 0);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
|
||||
+ NTDB_SUBLEVEL_HASH_BITS * 2);
|
||||
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include "logging.h"
|
||||
@ -8,55 +8,55 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data = tdb_mkdata("data", 4), d;
|
||||
union tdb_attribute seed_attr;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4), d;
|
||||
union ntdb_attribute seed_attr;
|
||||
unsigned int msgs = 0;
|
||||
|
||||
failtest_init(argc, argv);
|
||||
failtest_hook = block_repeat_failures;
|
||||
failtest_exit_check = exit_check_log;
|
||||
|
||||
seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
|
||||
seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
seed_attr.base.next = &tap_log_attr;
|
||||
seed_attr.seed.seed = 0;
|
||||
|
||||
failtest_suppress = true;
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 11);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-05-readonly-open.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-05-readonly-open.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600,
|
||||
&seed_attr);
|
||||
ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-05-readonly-open.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-05-readonly-open.ntdb", flags[i],
|
||||
O_RDONLY, 0600, &tap_log_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
break;
|
||||
ok1(tap_log_messages == msgs);
|
||||
/* Fetch should succeed, stores should fail. */
|
||||
if (!ok1(tdb_fetch(tdb, key, &d) == 0))
|
||||
if (!ok1(ntdb_fetch(ntdb, key, &d) == 0))
|
||||
goto fail;
|
||||
ok1(tdb_deq(d, data));
|
||||
ok1(ntdb_deq(d, data));
|
||||
free(d.dptr);
|
||||
if (!ok1(tdb_store(tdb, key, data, TDB_MODIFY)
|
||||
== TDB_ERR_RDONLY))
|
||||
if (!ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY)
|
||||
== NTDB_ERR_RDONLY))
|
||||
goto fail;
|
||||
ok1(tap_log_messages == ++msgs);
|
||||
if (!ok1(tdb_store(tdb, key, data, TDB_INSERT)
|
||||
== TDB_ERR_RDONLY))
|
||||
if (!ok1(ntdb_store(ntdb, key, data, NTDB_INSERT)
|
||||
== NTDB_ERR_RDONLY))
|
||||
goto fail;
|
||||
ok1(tap_log_messages == ++msgs);
|
||||
failtest_suppress = true;
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
ok1(tap_log_messages == msgs);
|
||||
/* SIGH: failtest bug, it doesn't save the tdb file because
|
||||
/* SIGH: failtest bug, it doesn't save the ntdb file because
|
||||
* we have it read-only. If we go around again, it gets
|
||||
* changed underneath us and things get screwy. */
|
||||
if (failtest_has_failed())
|
||||
@ -66,6 +66,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
fail:
|
||||
failtest_suppress = true;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
failtest_exit(exit_status());
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include "logging.h"
|
||||
@ -8,12 +8,12 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data = tdb_mkdata("data", 4);
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
|
||||
failtest_init(argc, argv);
|
||||
failtest_hook = block_repeat_failures;
|
||||
@ -22,37 +22,37 @@ int main(int argc, char *argv[])
|
||||
failtest_suppress = true;
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-10-simple-store.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-10-simple-store.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
break;
|
||||
/* Modify should fail. */
|
||||
failtest_suppress = false;
|
||||
if (!ok1(tdb_store(tdb, key, data, TDB_MODIFY)
|
||||
== TDB_ERR_NOEXIST))
|
||||
if (!ok1(ntdb_store(ntdb, key, data, NTDB_MODIFY)
|
||||
== NTDB_ERR_NOEXIST))
|
||||
goto fail;
|
||||
failtest_suppress = true;
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
/* Insert should succeed. */
|
||||
failtest_suppress = false;
|
||||
if (!ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0))
|
||||
if (!ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0))
|
||||
goto fail;
|
||||
failtest_suppress = true;
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
/* Second insert should fail. */
|
||||
failtest_suppress = false;
|
||||
if (!ok1(tdb_store(tdb, key, data, TDB_INSERT)
|
||||
== TDB_ERR_EXISTS))
|
||||
if (!ok1(ntdb_store(ntdb, key, data, NTDB_INSERT)
|
||||
== NTDB_ERR_EXISTS))
|
||||
goto fail;
|
||||
failtest_suppress = true;
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
ok1(tap_log_messages == 0);
|
||||
failtest_exit(exit_status());
|
||||
|
||||
fail:
|
||||
failtest_suppress = true;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
failtest_exit(exit_status());
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include "logging.h"
|
||||
@ -8,12 +8,12 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data = tdb_mkdata("data", 4);
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
|
||||
failtest_init(argc, argv);
|
||||
failtest_hook = block_repeat_failures;
|
||||
@ -22,30 +22,30 @@ int main(int argc, char *argv[])
|
||||
failtest_suppress = true;
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 8 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-11-simple-fetch.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-11-simple-fetch.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (tdb) {
|
||||
struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */
|
||||
ok1(ntdb);
|
||||
if (ntdb) {
|
||||
NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
|
||||
|
||||
/* fetch should fail. */
|
||||
failtest_suppress = false;
|
||||
if (!ok1(tdb_fetch(tdb, key, &d) == TDB_ERR_NOEXIST))
|
||||
if (!ok1(ntdb_fetch(ntdb, key, &d) == NTDB_ERR_NOEXIST))
|
||||
goto fail;
|
||||
failtest_suppress = true;
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
/* Insert should succeed. */
|
||||
ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
/* Fetch should now work. */
|
||||
failtest_suppress = false;
|
||||
if (!ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS))
|
||||
if (!ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS))
|
||||
goto fail;
|
||||
failtest_suppress = true;
|
||||
ok1(tdb_deq(d, data));
|
||||
ok1(ntdb_deq(d, data));
|
||||
free(d.dptr);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
}
|
||||
ok1(tap_log_messages == 0);
|
||||
@ -53,6 +53,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
fail:
|
||||
failtest_suppress = true;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
failtest_exit(exit_status());
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#include "private.h"
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include "logging.h"
|
||||
@ -9,12 +9,12 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_INTERNAL,
|
||||
TDB_INTERNAL|TDB_CONVERT,
|
||||
TDB_CONVERT };
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data = tdb_mkdata("data", 4);
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL,
|
||||
NTDB_INTERNAL|NTDB_CONVERT,
|
||||
NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
|
||||
failtest_init(argc, argv);
|
||||
failtest_hook = block_repeat_failures;
|
||||
@ -23,24 +23,24 @@ int main(int argc, char *argv[])
|
||||
failtest_suppress = true;
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 3 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-12-check.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-12-check.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
|
||||
ok1(ntdb);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
|
||||
/* This is what we really want to test: tdb_check(). */
|
||||
/* This is what we really want to test: ntdb_check(). */
|
||||
failtest_suppress = false;
|
||||
if (!ok1(tdb_check(tdb, NULL, NULL) == 0))
|
||||
if (!ok1(ntdb_check(ntdb, NULL, NULL) == 0))
|
||||
goto fail;
|
||||
failtest_suppress = true;
|
||||
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
ok1(tap_log_messages == 0);
|
||||
failtest_exit(exit_status());
|
||||
|
||||
fail:
|
||||
failtest_suppress = true;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
failtest_exit(exit_status());
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/ilog/ilog.h>
|
||||
#include "logging.h"
|
||||
@ -6,30 +6,30 @@
|
||||
#define MAX_SIZE 13100
|
||||
#define SIZE_STEP 131
|
||||
|
||||
static tdb_off_t tdb_offset(struct tdb_context *tdb, struct tdb_data key)
|
||||
static ntdb_off_t ntdb_offset(struct ntdb_context *ntdb, NTDB_DATA key)
|
||||
{
|
||||
tdb_off_t off;
|
||||
struct tdb_used_record urec;
|
||||
ntdb_off_t off;
|
||||
struct ntdb_used_record urec;
|
||||
struct hash_info h;
|
||||
|
||||
off = find_and_lock(tdb, key, F_RDLCK, &h, &urec, NULL);
|
||||
if (TDB_OFF_IS_ERR(off))
|
||||
off = find_and_lock(ntdb, key, F_RDLCK, &h, &urec, NULL);
|
||||
if (NTDB_OFF_IS_ERR(off))
|
||||
return 0;
|
||||
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
|
||||
return off;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j, moves;
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
unsigned char *buffer;
|
||||
tdb_off_t oldoff = 0, newoff;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = tdb_mkdata("key", 3);
|
||||
struct tdb_data data;
|
||||
ntdb_off_t oldoff = 0, newoff;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data;
|
||||
|
||||
buffer = malloc(MAX_SIZE);
|
||||
for (i = 0; i < MAX_SIZE; i++)
|
||||
@ -39,89 +39,89 @@ int main(int argc, char *argv[])
|
||||
* ((3 + MAX_SIZE/SIZE_STEP * 5) * 2 + 7)
|
||||
+ 1);
|
||||
|
||||
/* Using tdb_store. */
|
||||
/* Using ntdb_store. */
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-append.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-append.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
moves = 0;
|
||||
for (j = 0; j < MAX_SIZE; j += SIZE_STEP) {
|
||||
data.dptr = buffer;
|
||||
data.dsize = j;
|
||||
ok1(tdb_store(tdb, key, data, TDB_REPLACE) == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
|
||||
ok1(data.dsize == j);
|
||||
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
|
||||
free(data.dptr);
|
||||
newoff = tdb_offset(tdb, key);
|
||||
newoff = ntdb_offset(ntdb, key);
|
||||
if (newoff != oldoff)
|
||||
moves++;
|
||||
oldoff = newoff;
|
||||
}
|
||||
ok1(!tdb->file || (tdb->file->allrecord_lock.count == 0
|
||||
&& tdb->file->num_lockrecs == 0));
|
||||
ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0));
|
||||
/* We should increase by 50% each time... */
|
||||
ok(moves <= ilog64(j / SIZE_STEP)*2,
|
||||
"Moved %u times", moves);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
/* Using tdb_append. */
|
||||
/* Using ntdb_append. */
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
size_t prev_len = 0;
|
||||
tdb = tdb_open("run-append.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-append.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
moves = 0;
|
||||
for (j = 0; j < MAX_SIZE; j += SIZE_STEP) {
|
||||
data.dptr = buffer + prev_len;
|
||||
data.dsize = j - prev_len;
|
||||
ok1(tdb_append(tdb, key, data) == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
|
||||
ok1(ntdb_append(ntdb, key, data) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
|
||||
ok1(data.dsize == j);
|
||||
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
|
||||
free(data.dptr);
|
||||
prev_len = data.dsize;
|
||||
newoff = tdb_offset(tdb, key);
|
||||
newoff = ntdb_offset(ntdb, key);
|
||||
if (newoff != oldoff)
|
||||
moves++;
|
||||
oldoff = newoff;
|
||||
}
|
||||
ok1(!tdb->file || (tdb->file->allrecord_lock.count == 0
|
||||
&& tdb->file->num_lockrecs == 0));
|
||||
ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0));
|
||||
/* We should increase by 50% each time... */
|
||||
ok(moves <= ilog64(j / SIZE_STEP)*2,
|
||||
"Moved %u times", moves);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-append.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-append.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* Huge initial store. */
|
||||
data.dptr = buffer;
|
||||
data.dsize = MAX_SIZE;
|
||||
ok1(tdb_append(tdb, key, data) == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(tdb_fetch(tdb, key, &data) == TDB_SUCCESS);
|
||||
ok1(ntdb_append(ntdb, key, data) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS);
|
||||
ok1(data.dsize == MAX_SIZE);
|
||||
ok1(memcmp(data.dptr, buffer, data.dsize) == 0);
|
||||
free(data.dptr);
|
||||
ok1(!tdb->file || (tdb->file->allrecord_lock.count == 0
|
||||
&& tdb->file->num_lockrecs == 0));
|
||||
tdb_close(tdb);
|
||||
ok1(!ntdb->file || (ntdb->file->allrecord_lock.count == 0
|
||||
&& ntdb->file->num_lockrecs == 0));
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
137
lib/ntdb/test/run-20-growhash.c
Normal file
137
lib/ntdb/test/run-20-growhash.c
Normal file
@ -0,0 +1,137 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
static uint64_t myhash(const void *key, size_t len, uint64_t seed, void *priv)
|
||||
{
|
||||
return *(const uint64_t *)key;
|
||||
}
|
||||
|
||||
static void add_bits(uint64_t *val, unsigned new, unsigned new_bits,
|
||||
unsigned *done)
|
||||
{
|
||||
*done += new_bits;
|
||||
*val |= ((uint64_t)new << (64 - *done));
|
||||
}
|
||||
|
||||
static uint64_t make_key(unsigned topgroup, unsigned topbucket,
|
||||
unsigned subgroup1, unsigned subbucket1,
|
||||
unsigned subgroup2, unsigned subbucket2)
|
||||
{
|
||||
uint64_t key = 0;
|
||||
unsigned done = 0;
|
||||
|
||||
add_bits(&key, topgroup, NTDB_TOPLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS,
|
||||
&done);
|
||||
add_bits(&key, topbucket, NTDB_HASH_GROUP_BITS, &done);
|
||||
add_bits(&key, subgroup1, NTDB_SUBLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS,
|
||||
&done);
|
||||
add_bits(&key, subbucket1, NTDB_HASH_GROUP_BITS, &done);
|
||||
add_bits(&key, subgroup2, NTDB_SUBLEVEL_HASH_BITS - NTDB_HASH_GROUP_BITS,
|
||||
&done);
|
||||
add_bits(&key, subbucket2, NTDB_HASH_GROUP_BITS, &done);
|
||||
return key;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct ntdb_context *ntdb;
|
||||
uint64_t kdata;
|
||||
struct ntdb_used_record rec;
|
||||
NTDB_DATA key = { (unsigned char *)&kdata, sizeof(kdata) };
|
||||
NTDB_DATA dbuf = { (unsigned char *)&kdata, sizeof(kdata) };
|
||||
union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = myhash } };
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT,
|
||||
};
|
||||
|
||||
hattr.base.next = &tap_log_attr;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0])
|
||||
* (9 + (20 + 2 * ((1 << NTDB_HASH_GROUP_BITS) - 2))
|
||||
* (1 << NTDB_HASH_GROUP_BITS)) + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
struct hash_info h;
|
||||
|
||||
ntdb = ntdb_open("run-20-growhash.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* Fill a group. */
|
||||
for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
|
||||
kdata = make_key(0, j, 0, 0, 0, 0);
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
}
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Check first still exists. */
|
||||
kdata = make_key(0, 0, 0, 0, 0, 0);
|
||||
ok1(find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL) != 0);
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have located space in group 0, bucket 0. */
|
||||
ok1(h.group_start == offsetof(struct ntdb_header, hashtable));
|
||||
ok1(h.home_bucket == 0);
|
||||
ok1(h.found_bucket == 0);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS);
|
||||
/* Entire group should be full! */
|
||||
for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++)
|
||||
ok1(h.group[j] != 0);
|
||||
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_RDLCK) == 0);
|
||||
|
||||
/* Now, add one more to each should expand (that) bucket. */
|
||||
for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
|
||||
unsigned int k;
|
||||
kdata = make_key(0, j, 0, 1, 0, 0);
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
ok1(find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL));
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have moved to subhash */
|
||||
ok1(h.group_start >= sizeof(struct ntdb_header));
|
||||
ok1(h.home_bucket == 1);
|
||||
ok1(h.found_bucket == 1);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
|
||||
+ NTDB_SUBLEVEL_HASH_BITS);
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_RDLCK) == 0);
|
||||
|
||||
/* Keep adding, make it expand again. */
|
||||
for (k = 2; k < (1 << NTDB_HASH_GROUP_BITS); k++) {
|
||||
kdata = make_key(0, j, 0, k, 0, 0);
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
}
|
||||
|
||||
/* This should tip it over to sub-sub-hash. */
|
||||
kdata = make_key(0, j, 0, 0, 0, 1);
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
ok1(find_and_lock(ntdb, key, F_RDLCK, &h, &rec, NULL));
|
||||
/* Should have created correct hash. */
|
||||
ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
|
||||
/* Should have moved to subhash */
|
||||
ok1(h.group_start >= sizeof(struct ntdb_header));
|
||||
ok1(h.home_bucket == 1);
|
||||
ok1(h.found_bucket == 1);
|
||||
ok1(h.hash_used == NTDB_TOPLEVEL_HASH_BITS
|
||||
+ NTDB_SUBLEVEL_HASH_BITS + NTDB_SUBLEVEL_HASH_BITS);
|
||||
ok1(ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range,
|
||||
F_RDLCK) == 0);
|
||||
}
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
113
lib/ntdb/test/run-25-hashoverload.c
Normal file
113
lib/ntdb/test/run-25-hashoverload.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
static uint64_t badhash(const void *key, size_t len, uint64_t seed, void *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trav(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *p)
|
||||
{
|
||||
if (p)
|
||||
return ntdb_delete(ntdb, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
|
||||
NTDB_DATA dbuf = { (unsigned char *)&j, sizeof(j) };
|
||||
union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = badhash } };
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT,
|
||||
};
|
||||
|
||||
hattr.base.next = &tap_log_attr;
|
||||
|
||||
plan_tests(6883);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
NTDB_DATA d = { NULL, 0 }; /* Bogus GCC warning */
|
||||
|
||||
ntdb = ntdb_open("run-25-hashoverload.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* Fill a group. */
|
||||
for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
}
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Now store one last value: should form chain. */
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Check we can find them all. */
|
||||
for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS) + 1; j++) {
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == sizeof(j));
|
||||
ok1(d.dptr != NULL);
|
||||
ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
|
||||
free(d.dptr);
|
||||
}
|
||||
|
||||
/* Now add a *lot* more. */
|
||||
for (j = (1 << NTDB_HASH_GROUP_BITS) + 1;
|
||||
j < (16 << NTDB_HASH_GROUP_BITS);
|
||||
j++) {
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == sizeof(j));
|
||||
ok1(d.dptr != NULL);
|
||||
ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
|
||||
free(d.dptr);
|
||||
}
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Traverse through them. */
|
||||
ok1(ntdb_traverse(ntdb, trav, NULL) == j);
|
||||
|
||||
/* Empty the first chain-worth. */
|
||||
for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++)
|
||||
ok1(ntdb_delete(ntdb, key) == 0);
|
||||
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
for (j = (1 << NTDB_HASH_GROUP_BITS);
|
||||
j < (16 << NTDB_HASH_GROUP_BITS);
|
||||
j++) {
|
||||
ok1(ntdb_fetch(ntdb, key, &d) == NTDB_SUCCESS);
|
||||
ok1(d.dsize == sizeof(j));
|
||||
ok1(d.dptr != NULL);
|
||||
ok1(d.dptr && memcmp(d.dptr, &j, d.dsize) == 0);
|
||||
free(d.dptr);
|
||||
}
|
||||
|
||||
/* Traverse through them. */
|
||||
ok1(ntdb_traverse(ntdb, trav, NULL)
|
||||
== (15 << NTDB_HASH_GROUP_BITS));
|
||||
|
||||
/* Re-add */
|
||||
for (j = 0; j < (1 << NTDB_HASH_GROUP_BITS); j++) {
|
||||
ok1(ntdb_store(ntdb, key, dbuf, NTDB_INSERT) == 0);
|
||||
}
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Now try deleting as we go. */
|
||||
ok1(ntdb_traverse(ntdb, trav, trav)
|
||||
== (16 << NTDB_HASH_GROUP_BITS));
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_traverse(ntdb, trav, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
71
lib/ntdb/test/run-30-exhaust-before-expand.c
Normal file
71
lib/ntdb/test/run-30-exhaust-before-expand.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
static bool empty_freetable(struct ntdb_context *ntdb)
|
||||
{
|
||||
struct ntdb_freetable ftab;
|
||||
unsigned int i;
|
||||
|
||||
/* Now, free table should be completely exhausted in zone 0 */
|
||||
if (ntdb_read_convert(ntdb, ntdb->ftable_off, &ftab, sizeof(ftab)) != 0)
|
||||
abort();
|
||||
|
||||
for (i = 0; i < sizeof(ftab.buckets)/sizeof(ftab.buckets[0]); i++) {
|
||||
if (ftab.buckets[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 9 + 1);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
NTDB_DATA k;
|
||||
uint64_t size;
|
||||
bool was_empty = false;
|
||||
|
||||
k.dptr = (void *)&j;
|
||||
k.dsize = sizeof(j);
|
||||
|
||||
ntdb = ntdb_open("run-30-exhaust-before-expand.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ok1(empty_freetable(ntdb));
|
||||
/* Need some hash lock for expand. */
|
||||
ok1(ntdb_lock_hashes(ntdb, 0, 1, F_WRLCK, NTDB_LOCK_WAIT) == 0);
|
||||
/* Create some free space. */
|
||||
ok1(ntdb_expand(ntdb, 1) == 0);
|
||||
ok1(ntdb_unlock_hashes(ntdb, 0, 1, F_WRLCK) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(!empty_freetable(ntdb));
|
||||
|
||||
size = ntdb->file->map_size;
|
||||
/* Insert minimal-length records until we expand. */
|
||||
for (j = 0; ntdb->file->map_size == size; j++) {
|
||||
was_empty = empty_freetable(ntdb);
|
||||
if (ntdb_store(ntdb, k, k, NTDB_INSERT) != 0)
|
||||
err(1, "Failed to store record %i", j);
|
||||
}
|
||||
|
||||
/* Would have been empty before expansion, but no longer. */
|
||||
ok1(was_empty);
|
||||
ok1(!empty_freetable(ntdb));
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#include "private.h"
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <ccan/failtest/failtest.h>
|
||||
#include "logging.h"
|
||||
@ -9,46 +9,46 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, messages = 0;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
failtest_init(argc, argv);
|
||||
failtest_hook = block_repeat_failures;
|
||||
failtest_exit_check = exit_check_log;
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 4);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-35-convert.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-35-convert.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
failtest_exit(exit_status());
|
||||
|
||||
tdb_close(tdb);
|
||||
/* If we say TDB_CONVERT, it must be converted */
|
||||
tdb = tdb_open("run-35-convert.tdb",
|
||||
flags[i]|TDB_CONVERT,
|
||||
ntdb_close(ntdb);
|
||||
/* If we say NTDB_CONVERT, it must be converted */
|
||||
ntdb = ntdb_open("run-35-convert.ntdb",
|
||||
flags[i]|NTDB_CONVERT,
|
||||
O_RDWR, 0600, &tap_log_attr);
|
||||
if (flags[i] & TDB_CONVERT) {
|
||||
if (!tdb)
|
||||
if (flags[i] & NTDB_CONVERT) {
|
||||
if (!ntdb)
|
||||
failtest_exit(exit_status());
|
||||
ok1(tdb_get_flags(tdb) & TDB_CONVERT);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_CONVERT);
|
||||
ntdb_close(ntdb);
|
||||
} else {
|
||||
if (!ok1(!tdb && errno == EIO))
|
||||
if (!ok1(!ntdb && errno == EIO))
|
||||
failtest_exit(exit_status());
|
||||
ok1(tap_log_messages == ++messages);
|
||||
if (!ok1(log_last && strstr(log_last, "TDB_CONVERT")))
|
||||
if (!ok1(log_last && strstr(log_last, "NTDB_CONVERT")))
|
||||
failtest_exit(exit_status());
|
||||
}
|
||||
|
||||
/* If don't say TDB_CONVERT, it *may* be converted */
|
||||
tdb = tdb_open("run-35-convert.tdb",
|
||||
flags[i] & ~TDB_CONVERT,
|
||||
/* If don't say NTDB_CONVERT, it *may* be converted */
|
||||
ntdb = ntdb_open("run-35-convert.ntdb",
|
||||
flags[i] & ~NTDB_CONVERT,
|
||||
O_RDWR, 0600, &tap_log_attr);
|
||||
if (!tdb)
|
||||
if (!ntdb)
|
||||
failtest_exit(exit_status());
|
||||
ok1(tdb_get_flags(tdb) == flags[i]);
|
||||
tdb_close(tdb);
|
||||
ok1(ntdb_get_flags(ntdb) == flags[i]);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
failtest_exit(exit_status());
|
||||
}
|
70
lib/ntdb/test/run-50-multiple-freelists.c
Normal file
70
lib/ntdb/test/run-50-multiple-freelists.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
#include "layout.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
ntdb_off_t off;
|
||||
struct ntdb_context *ntdb;
|
||||
struct ntdb_layout *layout;
|
||||
NTDB_DATA key, data;
|
||||
union ntdb_attribute seed;
|
||||
|
||||
/* This seed value previously tickled a layout.c bug. */
|
||||
seed.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
seed.seed.seed = 0xb1142bc054d035b4ULL;
|
||||
seed.base.next = &tap_log_attr;
|
||||
|
||||
plan_tests(11);
|
||||
key = ntdb_mkdata("Hello", 5);
|
||||
data = ntdb_mkdata("world", 5);
|
||||
|
||||
/* Create a NTDB with three free tables. */
|
||||
layout = new_ntdb_layout();
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_free(layout, 80, 0);
|
||||
/* Used record prevent coalescing. */
|
||||
ntdb_layout_add_used(layout, key, data, 6);
|
||||
ntdb_layout_add_free(layout, 160, 1);
|
||||
key.dsize--;
|
||||
ntdb_layout_add_used(layout, key, data, 7);
|
||||
ntdb_layout_add_free(layout, 320, 2);
|
||||
key.dsize--;
|
||||
ntdb_layout_add_used(layout, key, data, 8);
|
||||
ntdb_layout_add_free(layout, 40, 0);
|
||||
ntdb = ntdb_layout_get(layout, free, &seed);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
off = get_free(ntdb, 0, 80 - sizeof(struct ntdb_used_record), 0,
|
||||
NTDB_USED_MAGIC, 0);
|
||||
ok1(off == layout->elem[3].base.off);
|
||||
ok1(ntdb->ftable_off == layout->elem[0].base.off);
|
||||
|
||||
off = get_free(ntdb, 0, 160 - sizeof(struct ntdb_used_record), 0,
|
||||
NTDB_USED_MAGIC, 0);
|
||||
ok1(off == layout->elem[5].base.off);
|
||||
ok1(ntdb->ftable_off == layout->elem[1].base.off);
|
||||
|
||||
off = get_free(ntdb, 0, 320 - sizeof(struct ntdb_used_record), 0,
|
||||
NTDB_USED_MAGIC, 0);
|
||||
ok1(off == layout->elem[7].base.off);
|
||||
ok1(ntdb->ftable_off == layout->elem[2].base.off);
|
||||
|
||||
off = get_free(ntdb, 0, 40 - sizeof(struct ntdb_used_record), 0,
|
||||
NTDB_USED_MAGIC, 0);
|
||||
ok1(off == layout->elem[9].base.off);
|
||||
ok1(ntdb->ftable_off == layout->elem[0].base.off);
|
||||
|
||||
/* Now we fail. */
|
||||
off = get_free(ntdb, 0, 0, 1, NTDB_USED_MAGIC, 0);
|
||||
ok1(off == 0);
|
||||
|
||||
ntdb_close(ntdb);
|
||||
ntdb_layout_free(layout);
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
@ -11,7 +11,7 @@ static int ftruncate_check(int fd, off_t length);
|
||||
#define fcntl fcntl_with_lockcheck
|
||||
#define ftruncate ftruncate_check
|
||||
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
@ -22,7 +22,7 @@ static int ftruncate_check(int fd, off_t length);
|
||||
static struct agent *agent;
|
||||
static bool opened;
|
||||
static int errors = 0;
|
||||
#define TEST_DBNAME "run-56-open-during-transaction.tdb"
|
||||
#define TEST_DBNAME "run-56-open-during-transaction.ntdb"
|
||||
|
||||
#undef write
|
||||
#undef pwrite
|
||||
@ -80,7 +80,7 @@ static void check_file_intact(int fd)
|
||||
if (ret == SUCCESS) {
|
||||
ret = external_agent_operation(agent, CLOSE, NULL);
|
||||
if (ret != SUCCESS) {
|
||||
diag("Agent failed to close tdb: %s",
|
||||
diag("Agent failed to close ntdb: %s",
|
||||
agent_return_name(ret));
|
||||
errors++;
|
||||
}
|
||||
@ -127,11 +127,11 @@ static int ftruncate_check(int fd, off_t length)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
const int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
int i;
|
||||
struct tdb_context *tdb;
|
||||
TDB_DATA key, data;
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA key, data;
|
||||
|
||||
plan_tests(sizeof(flags)/sizeof(flags[0]) * 5);
|
||||
agent = prepare_external_agent();
|
||||
@ -141,24 +141,24 @@ int main(int argc, char *argv[])
|
||||
unlock_callback = after_unlock;
|
||||
for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
|
||||
diag("Test with %s and %s\n",
|
||||
(flags[i] & TDB_CONVERT) ? "CONVERT" : "DEFAULT",
|
||||
(flags[i] & TDB_NOMMAP) ? "no mmap" : "mmap");
|
||||
(flags[i] & NTDB_CONVERT) ? "CONVERT" : "DEFAULT",
|
||||
(flags[i] & NTDB_NOMMAP) ? "no mmap" : "mmap");
|
||||
unlink(TEST_DBNAME);
|
||||
tdb = tdb_open(TEST_DBNAME, flags[i],
|
||||
ntdb = ntdb_open(TEST_DBNAME, flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
ok1(ntdb);
|
||||
|
||||
opened = true;
|
||||
ok1(tdb_transaction_start(tdb) == 0);
|
||||
key = tdb_mkdata("hi", strlen("hi"));
|
||||
data = tdb_mkdata("world", strlen("world"));
|
||||
ok1(ntdb_transaction_start(ntdb) == 0);
|
||||
key = ntdb_mkdata("hi", strlen("hi"));
|
||||
data = ntdb_mkdata("world", strlen("world"));
|
||||
|
||||
ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
|
||||
ok1(tdb_transaction_commit(tdb) == 0);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_transaction_commit(ntdb) == 0);
|
||||
ok(!errors, "We had %u open errors", errors);
|
||||
|
||||
opened = false;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
return exit_status();
|
@ -81,7 +81,7 @@ static void free_all(void)
|
||||
#define free free_noleak
|
||||
#define realloc realloc_noleak
|
||||
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
|
||||
#undef malloc
|
||||
#undef free
|
||||
@ -93,6 +93,7 @@ static void free_all(void)
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include <setjmp.h>
|
||||
#include "external-agent.h"
|
||||
#include "logging.h"
|
||||
@ -100,7 +101,7 @@ static void free_all(void)
|
||||
static bool in_transaction;
|
||||
static int target, current;
|
||||
static jmp_buf jmpbuf;
|
||||
#define TEST_DBNAME "run-57-die-during-transaction.tdb"
|
||||
#define TEST_DBNAME "run-57-die-during-transaction.ntdb"
|
||||
#define KEY_STRING "helloworld"
|
||||
|
||||
static void maybe_die(int fd)
|
||||
@ -153,24 +154,24 @@ static int ftruncate_check(int fd, off_t length)
|
||||
|
||||
static bool test_death(enum operation op, struct agent *agent)
|
||||
{
|
||||
struct tdb_context *tdb = NULL;
|
||||
TDB_DATA key;
|
||||
struct ntdb_context *ntdb = NULL;
|
||||
NTDB_DATA key;
|
||||
enum agent_return ret;
|
||||
int needed_recovery = 0;
|
||||
|
||||
current = target = 0;
|
||||
reset:
|
||||
unlink(TEST_DBNAME);
|
||||
tdb = tdb_open(TEST_DBNAME, TDB_NOMMAP,
|
||||
ntdb = ntdb_open(TEST_DBNAME, NTDB_NOMMAP,
|
||||
O_CREAT|O_TRUNC|O_RDWR, 0600, &tap_log_attr);
|
||||
if (!tdb) {
|
||||
diag("Failed opening TDB: %s", strerror(errno));
|
||||
if (!ntdb) {
|
||||
diag("Failed opening NTDB: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setjmp(jmpbuf) != 0) {
|
||||
/* We're partway through. Simulate our death. */
|
||||
close(tdb->file->fd);
|
||||
close(ntdb->file->fd);
|
||||
forget_locking();
|
||||
in_transaction = false;
|
||||
|
||||
@ -215,7 +216,7 @@ reset:
|
||||
/* Suppress logging as this tries to use closed fd. */
|
||||
suppress_logging = true;
|
||||
suppress_lockcheck = true;
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
suppress_logging = false;
|
||||
suppress_lockcheck = false;
|
||||
target++;
|
||||
@ -225,8 +226,8 @@ reset:
|
||||
}
|
||||
|
||||
/* Put key for agent to fetch. */
|
||||
key = tdb_mkdata(KEY_STRING, strlen(KEY_STRING));
|
||||
if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
|
||||
key = ntdb_mkdata(KEY_STRING, strlen(KEY_STRING));
|
||||
if (ntdb_store(ntdb, key, key, NTDB_INSERT) != 0)
|
||||
return false;
|
||||
|
||||
/* This is the key we insert in transaction. */
|
||||
@ -241,20 +242,20 @@ reset:
|
||||
errx(1, "Agent failed find key: %s", agent_return_name(ret));
|
||||
|
||||
in_transaction = true;
|
||||
if (tdb_transaction_start(tdb) != 0)
|
||||
if (ntdb_transaction_start(ntdb) != 0)
|
||||
return false;
|
||||
|
||||
if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
|
||||
if (ntdb_store(ntdb, key, key, NTDB_INSERT) != 0)
|
||||
return false;
|
||||
|
||||
if (tdb_transaction_commit(tdb) != 0)
|
||||
if (ntdb_transaction_commit(ntdb) != 0)
|
||||
return false;
|
||||
|
||||
in_transaction = false;
|
||||
|
||||
/* We made it! */
|
||||
diag("Completed %u runs", current);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
ret = external_agent_operation(agent, CLOSE, "");
|
||||
if (ret != SUCCESS) {
|
||||
diag("Step %u close failed = %s", current,
|
72
lib/ntdb/test/run-64-bit-tdb.c
Normal file
72
lib/ntdb/test/run-64-bit-tdb.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
if (sizeof(off_t) <= 4) {
|
||||
plan_tests(1);
|
||||
pass("No 64 bit off_t");
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
off_t old_size;
|
||||
NTDB_DATA k, d;
|
||||
struct hash_info h;
|
||||
struct ntdb_used_record rec;
|
||||
ntdb_off_t off;
|
||||
|
||||
ntdb = ntdb_open("run-64-bit-ntdb.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
old_size = ntdb->file->map_size;
|
||||
|
||||
/* This makes a sparse file */
|
||||
ok1(ftruncate(ntdb->file->fd, 0xFFFFFFF0) == 0);
|
||||
ok1(add_free_record(ntdb, old_size, 0xFFFFFFF0 - old_size,
|
||||
NTDB_LOCK_WAIT, false) == NTDB_SUCCESS);
|
||||
|
||||
/* Now add a little record past the 4G barrier. */
|
||||
ok1(ntdb_expand_file(ntdb, 100) == NTDB_SUCCESS);
|
||||
ok1(add_free_record(ntdb, 0xFFFFFFF0, 100, NTDB_LOCK_WAIT, false)
|
||||
== NTDB_SUCCESS);
|
||||
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
|
||||
/* Test allocation path. */
|
||||
k = ntdb_mkdata("key", 4);
|
||||
d = ntdb_mkdata("data", 5);
|
||||
ok1(ntdb_store(ntdb, k, d, NTDB_INSERT) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
|
||||
/* Make sure it put it at end as we expected. */
|
||||
off = find_and_lock(ntdb, k, F_RDLCK, &h, &rec, NULL);
|
||||
ok1(off >= 0xFFFFFFF0);
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
|
||||
|
||||
ok1(ntdb_fetch(ntdb, k, &d) == 0);
|
||||
ok1(d.dsize == 5);
|
||||
ok1(strcmp((char *)d.dptr, "data") == 0);
|
||||
free(d.dptr);
|
||||
|
||||
ok1(ntdb_delete(ntdb, k) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
/* We might get messages about mmap failing, so don't test
|
||||
* tap_log_messages */
|
||||
return exit_status();
|
||||
}
|
159
lib/ntdb/test/run-90-get-set-attributes.c
Normal file
159
lib/ntdb/test/run-90-get-set-attributes.c
Normal file
@ -0,0 +1,159 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
static int mylock(int fd, int rw, off_t off, off_t len, bool waitflag,
|
||||
void *unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int myunlock(int fd, int rw, off_t off, off_t len, void *unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t hash_fn(const void *key, size_t len, uint64_t seed,
|
||||
void *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
union ntdb_attribute seed_attr;
|
||||
union ntdb_attribute hash_attr;
|
||||
union ntdb_attribute lock_attr;
|
||||
|
||||
seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
seed_attr.base.next = &hash_attr;
|
||||
seed_attr.seed.seed = 100;
|
||||
|
||||
hash_attr.base.attr = NTDB_ATTRIBUTE_HASH;
|
||||
hash_attr.base.next = &lock_attr;
|
||||
hash_attr.hash.fn = hash_fn;
|
||||
hash_attr.hash.data = &hash_attr;
|
||||
|
||||
lock_attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
|
||||
lock_attr.base.next = &tap_log_attr;
|
||||
lock_attr.flock.lock = mylock;
|
||||
lock_attr.flock.unlock = myunlock;
|
||||
lock_attr.flock.data = &lock_attr;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 50);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
union ntdb_attribute attr;
|
||||
|
||||
/* First open with no attributes. */
|
||||
ntdb = ntdb_open("run-90-get-set-attributes.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, NULL);
|
||||
ok1(ntdb);
|
||||
|
||||
/* Get log on no attributes will fail */
|
||||
attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == NTDB_ERR_NOEXIST);
|
||||
/* These always work. */
|
||||
attr.base.attr = NTDB_ATTRIBUTE_HASH;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_HASH);
|
||||
ok1(attr.hash.fn == ntdb_jenkins_hash);
|
||||
attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_FLOCK);
|
||||
ok1(attr.flock.lock == ntdb_fcntl_lock);
|
||||
ok1(attr.flock.unlock == ntdb_fcntl_unlock);
|
||||
attr.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_SEED);
|
||||
/* This is possible, just astronomically unlikely. */
|
||||
ok1(attr.seed.seed != 0);
|
||||
|
||||
/* Unset attributes. */
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_LOG);
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
|
||||
|
||||
/* Set them. */
|
||||
ok1(ntdb_set_attribute(ntdb, &tap_log_attr) == 0);
|
||||
ok1(ntdb_set_attribute(ntdb, &lock_attr) == 0);
|
||||
/* These should fail. */
|
||||
ok1(ntdb_set_attribute(ntdb, &seed_attr) == NTDB_ERR_EINVAL);
|
||||
ok1(tap_log_messages == 1);
|
||||
ok1(ntdb_set_attribute(ntdb, &hash_attr) == NTDB_ERR_EINVAL);
|
||||
ok1(tap_log_messages == 2);
|
||||
tap_log_messages = 0;
|
||||
|
||||
/* Getting them should work as expected. */
|
||||
attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_LOG);
|
||||
ok1(attr.log.fn == tap_log_attr.log.fn);
|
||||
ok1(attr.log.data == tap_log_attr.log.data);
|
||||
|
||||
attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_FLOCK);
|
||||
ok1(attr.flock.lock == mylock);
|
||||
ok1(attr.flock.unlock == myunlock);
|
||||
ok1(attr.flock.data == &lock_attr);
|
||||
|
||||
/* Unset them again. */
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
|
||||
ok1(tap_log_messages == 0);
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_LOG);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
ntdb_close(ntdb);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
/* Now open with all attributes. */
|
||||
ntdb = ntdb_open("run-90-get-set-attributes.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600,
|
||||
&seed_attr);
|
||||
|
||||
ok1(ntdb);
|
||||
|
||||
/* Get will succeed */
|
||||
attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_LOG);
|
||||
ok1(attr.log.fn == tap_log_attr.log.fn);
|
||||
ok1(attr.log.data == tap_log_attr.log.data);
|
||||
|
||||
attr.base.attr = NTDB_ATTRIBUTE_HASH;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_HASH);
|
||||
ok1(attr.hash.fn == hash_fn);
|
||||
ok1(attr.hash.data == &hash_attr);
|
||||
|
||||
attr.base.attr = NTDB_ATTRIBUTE_FLOCK;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_FLOCK);
|
||||
ok1(attr.flock.lock == mylock);
|
||||
ok1(attr.flock.unlock == myunlock);
|
||||
ok1(attr.flock.data == &lock_attr);
|
||||
|
||||
attr.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
ok1(ntdb_get_attribute(ntdb, &attr) == 0);
|
||||
ok1(attr.base.attr == NTDB_ATTRIBUTE_SEED);
|
||||
ok1(attr.seed.seed == seed_attr.seed.seed);
|
||||
|
||||
/* Unset attributes. */
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_HASH);
|
||||
ok1(tap_log_messages == 1);
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_SEED);
|
||||
ok1(tap_log_messages == 2);
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_FLOCK);
|
||||
ntdb_unset_attribute(ntdb, NTDB_ATTRIBUTE_LOG);
|
||||
ok1(tap_log_messages == 2);
|
||||
tap_log_messages = 0;
|
||||
|
||||
ntdb_close(ntdb);
|
||||
|
||||
}
|
||||
return exit_status();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include <ccan/failtest/failtest_override.h>
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
#include "layout.h"
|
||||
@ -18,28 +18,28 @@ static size_t len_of(bool breaks_check, bool breaks_write, bool breaks_open)
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Creates a TDB with various capabilities. */
|
||||
static void create_tdb(const char *name,
|
||||
/* Creates a NTDB with various capabilities. */
|
||||
static void create_ntdb(const char *name,
|
||||
unsigned int cap,
|
||||
bool breaks_check,
|
||||
bool breaks_write,
|
||||
bool breaks_open, ...)
|
||||
{
|
||||
TDB_DATA key, data;
|
||||
NTDB_DATA key, data;
|
||||
va_list ap;
|
||||
struct tdb_layout *layout;
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_layout *layout;
|
||||
struct ntdb_context *ntdb;
|
||||
int fd;
|
||||
|
||||
key = tdb_mkdata("Hello", 5);
|
||||
data = tdb_mkdata("world", 5);
|
||||
key = ntdb_mkdata("Hello", 5);
|
||||
data = ntdb_mkdata("world", 5);
|
||||
|
||||
/* Create a TDB with some data, and some capabilities */
|
||||
layout = new_tdb_layout();
|
||||
tdb_layout_add_freetable(layout);
|
||||
tdb_layout_add_used(layout, key, data, 6);
|
||||
tdb_layout_add_free(layout, 80, 0);
|
||||
tdb_layout_add_capability(layout, cap,
|
||||
/* Create a NTDB with some data, and some capabilities */
|
||||
layout = new_ntdb_layout();
|
||||
ntdb_layout_add_freetable(layout);
|
||||
ntdb_layout_add_used(layout, key, data, 6);
|
||||
ntdb_layout_add_free(layout, 80, 0);
|
||||
ntdb_layout_add_capability(layout, cap,
|
||||
breaks_write, breaks_check, breaks_open,
|
||||
len_of(breaks_check, breaks_write, breaks_open));
|
||||
|
||||
@ -50,9 +50,9 @@ static void create_tdb(const char *name,
|
||||
breaks_open = va_arg(ap, int);
|
||||
|
||||
key.dsize--;
|
||||
tdb_layout_add_used(layout, key, data, 11 - key.dsize);
|
||||
tdb_layout_add_free(layout, 80, 0);
|
||||
tdb_layout_add_capability(layout, cap,
|
||||
ntdb_layout_add_used(layout, key, data, 11 - key.dsize);
|
||||
ntdb_layout_add_free(layout, 80, 0);
|
||||
ntdb_layout_add_capability(layout, cap,
|
||||
breaks_write, breaks_check,
|
||||
breaks_open,
|
||||
len_of(breaks_check, breaks_write,
|
||||
@ -61,23 +61,23 @@ static void create_tdb(const char *name,
|
||||
va_end(ap);
|
||||
|
||||
/* We open-code this, because we need to use the failtest write. */
|
||||
tdb = tdb_layout_get(layout, failtest_free, &tap_log_attr);
|
||||
ntdb = ntdb_layout_get(layout, failtest_free, &tap_log_attr);
|
||||
|
||||
fd = open(name, O_RDWR|O_TRUNC|O_CREAT, 0600);
|
||||
if (fd < 0)
|
||||
err(1, "opening %s for writing", name);
|
||||
if (write(fd, tdb->file->map_ptr, tdb->file->map_size)
|
||||
!= tdb->file->map_size)
|
||||
if (write(fd, ntdb->file->map_ptr, ntdb->file->map_size)
|
||||
!= ntdb->file->map_size)
|
||||
err(1, "writing %s", name);
|
||||
close(fd);
|
||||
tdb_close(tdb);
|
||||
tdb_layout_free(layout);
|
||||
ntdb_close(ntdb);
|
||||
ntdb_layout_free(layout);
|
||||
}
|
||||
|
||||
/* Note all the "goto out" early exits: they're to shorten failtest time. */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
char *summary;
|
||||
|
||||
failtest_init(argc, argv);
|
||||
@ -87,72 +87,72 @@ int main(int argc, char *argv[])
|
||||
|
||||
failtest_suppress = true;
|
||||
/* Capability says you can ignore it? */
|
||||
create_tdb("run-capabilities.tdb", 1, false, false, false, 0);
|
||||
create_ntdb("run-capabilities.ntdb", 1, false, false, false, 0);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
goto out;
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
ok1(tap_log_messages == 0);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* Two capabilitues say you can ignore them? */
|
||||
create_tdb("run-capabilities.tdb",
|
||||
create_ntdb("run-capabilities.ntdb",
|
||||
1, false, false, false,
|
||||
2, false, false, false, 0);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
goto out;
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
|
||||
ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
|
||||
ok1(strstr(summary, "Capability 1\n"));
|
||||
free(summary);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* Capability says you can't check. */
|
||||
create_tdb("run-capabilities.tdb",
|
||||
create_ntdb("run-capabilities.ntdb",
|
||||
1, false, false, false,
|
||||
2, true, false, false, 0);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
goto out;
|
||||
ok1(tap_log_messages == 0);
|
||||
ok1(tdb_get_flags(tdb) & TDB_CANT_CHECK);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_CANT_CHECK);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
/* We expect a warning! */
|
||||
ok1(tap_log_messages == 1);
|
||||
ok1(strstr(log_last, "capabilit"));
|
||||
ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
|
||||
ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
|
||||
ok1(strstr(summary, "Capability 1\n"));
|
||||
ok1(strstr(summary, "Capability 2 (uncheckable)\n"));
|
||||
free(summary);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* Capability says you can't write. */
|
||||
create_tdb("run-capabilities.tdb",
|
||||
create_ntdb("run-capabilities.ntdb",
|
||||
1, false, false, false,
|
||||
2, false, true, false, 0);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
/* We expect a message. */
|
||||
ok1(!tdb);
|
||||
ok1(!ntdb);
|
||||
if (!ok1(tap_log_messages == 2))
|
||||
goto out;
|
||||
if (!ok1(strstr(log_last, "unknown")))
|
||||
@ -161,48 +161,48 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* We can open it read-only though! */
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDONLY, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDONLY, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
goto out;
|
||||
ok1(tap_log_messages == 2);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
ok1(tap_log_messages == 2);
|
||||
ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
|
||||
ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
|
||||
ok1(strstr(summary, "Capability 1\n"));
|
||||
ok1(strstr(summary, "Capability 2 (read-only)\n"));
|
||||
free(summary);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* Capability says you can't open. */
|
||||
create_tdb("run-capabilities.tdb",
|
||||
create_ntdb("run-capabilities.ntdb",
|
||||
1, false, false, false,
|
||||
2, false, false, true, 0);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
/* We expect a message. */
|
||||
ok1(!tdb);
|
||||
ok1(!ntdb);
|
||||
if (!ok1(tap_log_messages == 3))
|
||||
goto out;
|
||||
if (!ok1(strstr(log_last, "unknown")))
|
||||
goto out;
|
||||
|
||||
/* Combine capabilities correctly. */
|
||||
create_tdb("run-capabilities.tdb",
|
||||
create_ntdb("run-capabilities.ntdb",
|
||||
1, false, false, false,
|
||||
2, true, false, false,
|
||||
3, false, true, false, 0);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
/* We expect a message. */
|
||||
ok1(!tdb);
|
||||
ok1(!ntdb);
|
||||
if (!ok1(tap_log_messages == 4))
|
||||
goto out;
|
||||
if (!ok1(strstr(log_last, "unknown")))
|
||||
@ -211,36 +211,36 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* We can open it read-only though! */
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDONLY, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDONLY, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
goto out;
|
||||
ok1(tap_log_messages == 4);
|
||||
ok1(tdb_get_flags(tdb) & TDB_CANT_CHECK);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_CANT_CHECK);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
/* We expect a warning! */
|
||||
ok1(tap_log_messages == 5);
|
||||
ok1(strstr(log_last, "unknown"));
|
||||
ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
|
||||
ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
|
||||
ok1(strstr(summary, "Capability 1\n"));
|
||||
ok1(strstr(summary, "Capability 2 (uncheckable)\n"));
|
||||
ok1(strstr(summary, "Capability 3 (read-only)\n"));
|
||||
free(summary);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* Two capability flags in one. */
|
||||
create_tdb("run-capabilities.tdb",
|
||||
create_ntdb("run-capabilities.ntdb",
|
||||
1, false, false, false,
|
||||
2, true, true, false,
|
||||
0);
|
||||
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
/* We expect a message. */
|
||||
ok1(!tdb);
|
||||
ok1(!ntdb);
|
||||
if (!ok1(tap_log_messages == 6))
|
||||
goto out;
|
||||
if (!ok1(strstr(log_last, "unknown")))
|
||||
@ -249,22 +249,22 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* We can open it read-only though! */
|
||||
failtest_suppress = false;
|
||||
tdb = tdb_open("run-capabilities.tdb", TDB_DEFAULT, O_RDONLY, 0,
|
||||
ntdb = ntdb_open("run-capabilities.ntdb", NTDB_DEFAULT, O_RDONLY, 0,
|
||||
&tap_log_attr);
|
||||
failtest_suppress = true;
|
||||
if (!ok1(tdb))
|
||||
if (!ok1(ntdb))
|
||||
goto out;
|
||||
ok1(tap_log_messages == 6);
|
||||
ok1(tdb_get_flags(tdb) & TDB_CANT_CHECK);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == TDB_SUCCESS);
|
||||
ok1(ntdb_get_flags(ntdb) & NTDB_CANT_CHECK);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == NTDB_SUCCESS);
|
||||
/* We expect a warning! */
|
||||
ok1(tap_log_messages == 7);
|
||||
ok1(strstr(log_last, "unknown"));
|
||||
ok1(tdb_summary(tdb, 0, &summary) == TDB_SUCCESS);
|
||||
ok1(ntdb_summary(ntdb, 0, &summary) == NTDB_SUCCESS);
|
||||
ok1(strstr(summary, "Capability 1\n"));
|
||||
ok1(strstr(summary, "Capability 2 (uncheckable,read-only)\n"));
|
||||
free(summary);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
out:
|
||||
failtest_exit(exit_status());
|
36
lib/ntdb/test/run-expand-in-transaction.c
Normal file
36
lib/ntdb/test/run-expand-in-transaction.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = ntdb_mkdata("key", 3);
|
||||
NTDB_DATA data = ntdb_mkdata("data", 4);
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
|
||||
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
size_t size;
|
||||
ntdb = ntdb_open("run-expand-in-transaction.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
size = ntdb->file->map_size;
|
||||
ok1(ntdb_transaction_start(ntdb) == 0);
|
||||
ok1(ntdb_store(ntdb, key, data, NTDB_INSERT) == 0);
|
||||
ok1(ntdb->file->map_size > size);
|
||||
ok1(ntdb_transaction_commit(ntdb) == 0);
|
||||
ok1(ntdb->file->map_size > size);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
||||
return exit_status();
|
||||
}
|
@ -1,60 +1,60 @@
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct tdb_context *tdb;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
struct tdb_data key = { (unsigned char *)&j, sizeof(j) };
|
||||
struct tdb_data data = { (unsigned char *)&j, sizeof(j) };
|
||||
struct ntdb_context *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
NTDB_DATA key = { (unsigned char *)&j, sizeof(j) };
|
||||
NTDB_DATA data = { (unsigned char *)&j, sizeof(j) };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 8 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
uint64_t features;
|
||||
tdb = tdb_open("run-features.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-features.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* Put some stuff in there. */
|
||||
for (j = 0; j < 100; j++) {
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
|
||||
fail("Storing in tdb");
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
fail("Storing in ntdb");
|
||||
}
|
||||
|
||||
/* Mess with features fields in hdr. */
|
||||
features = (~TDB_FEATURE_MASK ^ 1);
|
||||
ok1(tdb_write_convert(tdb, offsetof(struct tdb_header,
|
||||
features = (~NTDB_FEATURE_MASK ^ 1);
|
||||
ok1(ntdb_write_convert(ntdb, offsetof(struct ntdb_header,
|
||||
features_used),
|
||||
&features, sizeof(features)) == 0);
|
||||
ok1(tdb_write_convert(tdb, offsetof(struct tdb_header,
|
||||
ok1(ntdb_write_convert(ntdb, offsetof(struct ntdb_header,
|
||||
features_offered),
|
||||
&features, sizeof(features)) == 0);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
tdb = tdb_open("run-features.tdb", flags[i], O_RDWR, 0,
|
||||
ntdb = ntdb_open("run-features.ntdb", flags[i], O_RDWR, 0,
|
||||
&tap_log_attr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
/* Should not have changed features offered. */
|
||||
ok1(tdb_read_convert(tdb, offsetof(struct tdb_header,
|
||||
ok1(ntdb_read_convert(ntdb, offsetof(struct ntdb_header,
|
||||
features_offered),
|
||||
&features, sizeof(features)) == 0);
|
||||
ok1(features == (~TDB_FEATURE_MASK ^ 1));
|
||||
ok1(features == (~NTDB_FEATURE_MASK ^ 1));
|
||||
|
||||
/* Should have cleared unknown bits in features_used. */
|
||||
ok1(tdb_read_convert(tdb, offsetof(struct tdb_header,
|
||||
ok1(ntdb_read_convert(ntdb, offsetof(struct ntdb_header,
|
||||
features_used),
|
||||
&features, sizeof(features)) == 0);
|
||||
ok1(features == (1 & TDB_FEATURE_MASK));
|
||||
ok1(features == (1 & NTDB_FEATURE_MASK));
|
||||
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
@ -3,25 +3,24 @@
|
||||
#include "lock-tracking.h"
|
||||
|
||||
#define fcntl fcntl_with_lockcheck
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
|
||||
#include "tap-interface.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include "external-agent.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define TEST_DBNAME "run-lockall.tdb"
|
||||
#define TEST_DBNAME "run-lockall.ntdb"
|
||||
|
||||
#undef fcntl
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct agent *agent;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
int i;
|
||||
|
||||
plan_tests(13 * sizeof(flags)/sizeof(flags[0]) + 1);
|
||||
@ -31,38 +30,38 @@ int main(int argc, char *argv[])
|
||||
|
||||
for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
|
||||
enum agent_return ret;
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
|
||||
tdb = tdb_open(TEST_DBNAME, flags[i],
|
||||
ntdb = ntdb_open(TEST_DBNAME, flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
ok1(tdb);
|
||||
ok1(ntdb);
|
||||
|
||||
ret = external_agent_operation(agent, OPEN, TEST_DBNAME);
|
||||
ok1(ret == SUCCESS);
|
||||
|
||||
ok1(tdb_lockall(tdb) == TDB_SUCCESS);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
|
||||
ok1(external_agent_operation(agent, STORE, "key")
|
||||
== WOULD_HAVE_BLOCKED);
|
||||
ok1(external_agent_operation(agent, FETCH, "key")
|
||||
== WOULD_HAVE_BLOCKED);
|
||||
/* Test nesting. */
|
||||
ok1(tdb_lockall(tdb) == TDB_SUCCESS);
|
||||
tdb_unlockall(tdb);
|
||||
tdb_unlockall(tdb);
|
||||
ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
|
||||
ntdb_unlockall(ntdb);
|
||||
ntdb_unlockall(ntdb);
|
||||
|
||||
ok1(external_agent_operation(agent, STORE, "key") == SUCCESS);
|
||||
|
||||
ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
|
||||
ok1(ntdb_lockall_read(ntdb) == NTDB_SUCCESS);
|
||||
ok1(external_agent_operation(agent, STORE, "key")
|
||||
== WOULD_HAVE_BLOCKED);
|
||||
ok1(external_agent_operation(agent, FETCH, "key") == SUCCESS);
|
||||
ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
|
||||
tdb_unlockall_read(tdb);
|
||||
tdb_unlockall_read(tdb);
|
||||
ok1(ntdb_lockall_read(ntdb) == NTDB_SUCCESS);
|
||||
ntdb_unlockall_read(ntdb);
|
||||
ntdb_unlockall_read(ntdb);
|
||||
|
||||
ok1(external_agent_operation(agent, STORE, "key") == SUCCESS);
|
||||
ok1(external_agent_operation(agent, CLOSE, NULL) == SUCCESS);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
free_external_agent(agent);
|
@ -1,11 +1,11 @@
|
||||
#include "tdb2-source.h"
|
||||
/* We had a bug where we marked the tdb read-only for a tdb_traverse_read.
|
||||
* If we then expanded the tdb, we would remap read-only, and later SEGV. */
|
||||
#include "ntdb-source.h"
|
||||
/* We had a bug where we marked the ntdb read-only for a ntdb_traverse_read.
|
||||
* If we then expanded the ntdb, we would remap read-only, and later SEGV. */
|
||||
#include "tap-interface.h"
|
||||
#include "external-agent.h"
|
||||
#include "logging.h"
|
||||
|
||||
static bool file_larger(int fd, tdb_len_t size)
|
||||
static bool file_larger(int fd, ntdb_len_t size)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
@ -13,7 +13,7 @@ static bool file_larger(int fd, tdb_len_t size)
|
||||
return st.st_size != size;
|
||||
}
|
||||
|
||||
static unsigned add_records_to_grow(struct agent *agent, int fd, tdb_len_t size)
|
||||
static unsigned add_records_to_grow(struct agent *agent, int fd, ntdb_len_t size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@ -31,27 +31,27 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct agent *agent;
|
||||
struct tdb_context *tdb;
|
||||
struct tdb_data d = tdb_mkdata("hello", 5);
|
||||
const char filename[] = "run-remap-in-read_traverse.tdb";
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA d = ntdb_mkdata("hello", 5);
|
||||
const char filename[] = "run-remap-in-read_traverse.ntdb";
|
||||
|
||||
plan_tests(4);
|
||||
|
||||
agent = prepare_external_agent();
|
||||
|
||||
tdb = tdb_open(filename, TDB_DEFAULT,
|
||||
ntdb = ntdb_open(filename, NTDB_DEFAULT,
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
|
||||
ok1(external_agent_operation(agent, OPEN, filename) == SUCCESS);
|
||||
i = add_records_to_grow(agent, tdb->file->fd, tdb->file->map_size);
|
||||
i = add_records_to_grow(agent, ntdb->file->fd, ntdb->file->map_size);
|
||||
|
||||
/* Do a traverse. */
|
||||
ok1(tdb_traverse(tdb, NULL, NULL) == i);
|
||||
ok1(ntdb_traverse(ntdb, NULL, NULL) == i);
|
||||
|
||||
/* Now store something! */
|
||||
ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0);
|
||||
ok1(ntdb_store(ntdb, d, d, NTDB_INSERT) == 0);
|
||||
ok1(tap_log_messages == 0);
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
free_external_agent(agent);
|
||||
return exit_status();
|
||||
}
|
61
lib/ntdb/test/run-seed.c
Normal file
61
lib/ntdb/test/run-seed.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
static int log_count = 0;
|
||||
|
||||
/* Normally we get a log when setting random seed. */
|
||||
static void my_log_fn(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message, void *priv)
|
||||
{
|
||||
log_count++;
|
||||
}
|
||||
|
||||
static union ntdb_attribute log_attr = {
|
||||
.log = { .base = { .attr = NTDB_ATTRIBUTE_LOG },
|
||||
.fn = my_log_fn }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
struct ntdb_context *ntdb;
|
||||
union ntdb_attribute attr;
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
attr.seed.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
attr.seed.base.next = &log_attr;
|
||||
attr.seed.seed = 42;
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 4 + 4 * 3);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
struct ntdb_header hdr;
|
||||
int fd;
|
||||
ntdb = ntdb_open("run-seed.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &attr);
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(ntdb->hash_seed == 42);
|
||||
ok1(log_count == 0);
|
||||
ntdb_close(ntdb);
|
||||
|
||||
if (flags[i] & NTDB_INTERNAL)
|
||||
continue;
|
||||
|
||||
fd = open("run-seed.ntdb", O_RDONLY);
|
||||
ok1(fd >= 0);
|
||||
ok1(read(fd, &hdr, sizeof(hdr)) == sizeof(hdr));
|
||||
if (flags[i] & NTDB_CONVERT)
|
||||
ok1(bswap_64(hdr.hash_seed) == 42);
|
||||
else
|
||||
ok1(hdr.hash_seed == 42);
|
||||
close(fd);
|
||||
}
|
||||
return exit_status();
|
||||
}
|
52
lib/ntdb/test/run-tdb_errorstr.c
Normal file
52
lib/ntdb/test/run-tdb_errorstr.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
enum NTDB_ERROR e;
|
||||
plan_tests(NTDB_ERR_RDONLY*-1 + 2);
|
||||
|
||||
for (e = NTDB_SUCCESS; e >= NTDB_ERR_RDONLY; e--) {
|
||||
switch (e) {
|
||||
case NTDB_SUCCESS:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"Success"));
|
||||
break;
|
||||
case NTDB_ERR_IO:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"IO Error"));
|
||||
break;
|
||||
case NTDB_ERR_LOCK:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"Locking error"));
|
||||
break;
|
||||
case NTDB_ERR_OOM:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"Out of memory"));
|
||||
break;
|
||||
case NTDB_ERR_EXISTS:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"Record exists"));
|
||||
break;
|
||||
case NTDB_ERR_EINVAL:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"Invalid parameter"));
|
||||
break;
|
||||
case NTDB_ERR_NOEXIST:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"Record does not exist"));
|
||||
break;
|
||||
case NTDB_ERR_RDONLY:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"write not permitted"));
|
||||
break;
|
||||
case NTDB_ERR_CORRUPT:
|
||||
ok1(!strcmp(ntdb_errorstr(e),
|
||||
"Corrupt database"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok1(!strcmp(ntdb_errorstr(e), "Invalid error code"));
|
||||
|
||||
return exit_status();
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
static int drop_count(struct tdb_context *tdb, unsigned int *count)
|
||||
static int drop_count(struct ntdb_context *ntdb, unsigned int *count)
|
||||
{
|
||||
if (--(*count) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_found(struct tdb_context *tdb, bool found[3])
|
||||
static int set_found(struct ntdb_context *ntdb, bool found[3])
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
if (strcmp(tdb_name(tdb), "run-tdb_foreach0.tdb") == 0)
|
||||
if (strcmp(ntdb_name(ntdb), "run-ntdb_foreach0.ntdb") == 0)
|
||||
idx = 0;
|
||||
else if (strcmp(tdb_name(tdb), "run-tdb_foreach1.tdb") == 0)
|
||||
else if (strcmp(ntdb_name(ntdb), "run-ntdb_foreach1.ntdb") == 0)
|
||||
idx = 1;
|
||||
else if (strcmp(tdb_name(tdb), "run-tdb_foreach2.tdb") == 0)
|
||||
else if (strcmp(ntdb_name(ntdb), "run-ntdb_foreach2.ntdb") == 0)
|
||||
idx = 2;
|
||||
else
|
||||
abort();
|
||||
@ -32,52 +32,52 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, count;
|
||||
bool found[3];
|
||||
struct tdb_context *tdb0, *tdb1, *tdb2;
|
||||
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
|
||||
struct ntdb_context *ntdb0, *ntdb1, *ntdb;
|
||||
int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 8);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb0 = tdb_open("run-tdb_foreach0.tdb", flags[i],
|
||||
ntdb0 = ntdb_open("run-ntdb_foreach0.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
tdb1 = tdb_open("run-tdb_foreach1.tdb", flags[i],
|
||||
ntdb1 = ntdb_open("run-ntdb_foreach1.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
tdb2 = tdb_open("run-tdb_foreach2.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-ntdb_foreach2.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
|
||||
|
||||
memset(found, 0, sizeof(found));
|
||||
tdb_foreach(set_found, found);
|
||||
ntdb_foreach(set_found, found);
|
||||
ok1(found[0] && found[1] && found[2]);
|
||||
|
||||
/* Test premature iteration termination */
|
||||
count = 1;
|
||||
tdb_foreach(drop_count, &count);
|
||||
ntdb_foreach(drop_count, &count);
|
||||
ok1(count == 0);
|
||||
|
||||
tdb_close(tdb1);
|
||||
ntdb_close(ntdb1);
|
||||
memset(found, 0, sizeof(found));
|
||||
tdb_foreach(set_found, found);
|
||||
ntdb_foreach(set_found, found);
|
||||
ok1(found[0] && !found[1] && found[2]);
|
||||
|
||||
tdb_close(tdb2);
|
||||
ntdb_close(ntdb);
|
||||
memset(found, 0, sizeof(found));
|
||||
tdb_foreach(set_found, found);
|
||||
ntdb_foreach(set_found, found);
|
||||
ok1(found[0] && !found[1] && !found[2]);
|
||||
|
||||
tdb1 = tdb_open("run-tdb_foreach1.tdb", flags[i],
|
||||
ntdb1 = ntdb_open("run-ntdb_foreach1.ntdb", flags[i],
|
||||
O_RDWR, 0600, &tap_log_attr);
|
||||
memset(found, 0, sizeof(found));
|
||||
tdb_foreach(set_found, found);
|
||||
ntdb_foreach(set_found, found);
|
||||
ok1(found[0] && found[1] && !found[2]);
|
||||
|
||||
tdb_close(tdb0);
|
||||
ntdb_close(ntdb0);
|
||||
memset(found, 0, sizeof(found));
|
||||
tdb_foreach(set_found, found);
|
||||
ntdb_foreach(set_found, found);
|
||||
ok1(!found[0] && found[1] && !found[2]);
|
||||
|
||||
tdb_close(tdb1);
|
||||
ntdb_close(ntdb1);
|
||||
memset(found, 0, sizeof(found));
|
||||
tdb_foreach(set_found, found);
|
||||
ntdb_foreach(set_found, found);
|
||||
ok1(!found[0] && !found[1] && !found[2]);
|
||||
ok1(tap_log_messages == 0);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include "tdb2-source.h"
|
||||
#include "ntdb-source.h"
|
||||
#include "tap-interface.h"
|
||||
#include "logging.h"
|
||||
|
||||
@ -11,14 +11,14 @@ static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
|
||||
*(uint64_t *)p);
|
||||
}
|
||||
|
||||
static bool store_records(struct tdb_context *tdb)
|
||||
static bool store_records(struct ntdb_context *ntdb)
|
||||
{
|
||||
int i;
|
||||
struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
|
||||
struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA key = { (unsigned char *)&i, sizeof(i) };
|
||||
NTDB_DATA data = { (unsigned char *)&i, sizeof(i) };
|
||||
|
||||
for (i = 0; i < NUM_RECORDS; i++)
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
|
||||
if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -28,10 +28,10 @@ struct trav_data {
|
||||
int low, high;
|
||||
bool mismatch;
|
||||
bool delete;
|
||||
enum TDB_ERROR delete_error;
|
||||
enum NTDB_ERROR delete_error;
|
||||
};
|
||||
|
||||
static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
static int trav(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
|
||||
struct trav_data *td)
|
||||
{
|
||||
int val;
|
||||
@ -49,8 +49,8 @@ static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
td->high = val;
|
||||
|
||||
if (td->delete) {
|
||||
td->delete_error = tdb_delete(tdb, key);
|
||||
if (td->delete_error != TDB_SUCCESS) {
|
||||
td->delete_error = ntdb_delete(ntdb, key);
|
||||
if (td->delete_error != NTDB_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -64,10 +64,10 @@ struct trav_grow_data {
|
||||
unsigned int calls;
|
||||
unsigned int num_large;
|
||||
bool mismatch;
|
||||
enum TDB_ERROR error;
|
||||
enum NTDB_ERROR error;
|
||||
};
|
||||
|
||||
static int trav_grow(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
static int trav_grow(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
|
||||
struct trav_grow_data *tgd)
|
||||
{
|
||||
int val;
|
||||
@ -87,8 +87,8 @@ static int trav_grow(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
/* Make a big difference to the database. */
|
||||
dbuf.dptr = buffer;
|
||||
dbuf.dsize = sizeof(buffer);
|
||||
tgd->error = tdb_append(tdb, key, dbuf);
|
||||
if (tgd->error != TDB_SUCCESS) {
|
||||
tgd->error = ntdb_append(ntdb, key, dbuf);
|
||||
if (tgd->error != NTDB_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@ -100,12 +100,12 @@ int main(int argc, char *argv[])
|
||||
int num;
|
||||
struct trav_data td;
|
||||
struct trav_grow_data tgd;
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
uint64_t seed = 16014841315512641303ULL;
|
||||
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
|
||||
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
|
||||
TDB_NOMMAP|TDB_CONVERT };
|
||||
union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
|
||||
int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
|
||||
NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
|
||||
NTDB_NOMMAP|NTDB_CONVERT };
|
||||
union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
|
||||
.fn = fixedhash,
|
||||
.data = &seed } };
|
||||
|
||||
@ -113,16 +113,16 @@ int main(int argc, char *argv[])
|
||||
|
||||
plan_tests(sizeof(flags) / sizeof(flags[0]) * 32 + 1);
|
||||
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
|
||||
tdb = tdb_open("run-traverse.tdb", flags[i],
|
||||
ntdb = ntdb_open("run-traverse.ntdb", flags[i],
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
|
||||
ok1(tdb);
|
||||
if (!tdb)
|
||||
ok1(ntdb);
|
||||
if (!ntdb)
|
||||
continue;
|
||||
|
||||
ok1(tdb_traverse(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_traverse(ntdb, NULL, NULL) == 0);
|
||||
|
||||
ok1(store_records(tdb));
|
||||
num = tdb_traverse(tdb, NULL, NULL);
|
||||
ok1(store_records(ntdb));
|
||||
num = ntdb_traverse(ntdb, NULL, NULL);
|
||||
ok1(num == NUM_RECORDS);
|
||||
|
||||
/* Full traverse. */
|
||||
@ -133,7 +133,7 @@ int main(int argc, char *argv[])
|
||||
td.mismatch = false;
|
||||
td.delete = false;
|
||||
|
||||
num = tdb_traverse(tdb, trav, &td);
|
||||
num = ntdb_traverse(ntdb, trav, &td);
|
||||
ok1(num == NUM_RECORDS);
|
||||
ok1(!td.mismatch);
|
||||
ok1(td.calls == NUM_RECORDS);
|
||||
@ -148,13 +148,13 @@ int main(int argc, char *argv[])
|
||||
td.mismatch = false;
|
||||
td.delete = false;
|
||||
|
||||
num = tdb_traverse(tdb, trav, &td);
|
||||
num = ntdb_traverse(ntdb, trav, &td);
|
||||
ok1(num == NUM_RECORDS / 2);
|
||||
ok1(!td.mismatch);
|
||||
ok1(td.calls == NUM_RECORDS / 2);
|
||||
ok1(td.low <= NUM_RECORDS / 2);
|
||||
ok1(td.high > NUM_RECORDS / 2);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(tap_log_messages == 0);
|
||||
|
||||
/* Deleting traverse (delete everything). */
|
||||
@ -164,38 +164,38 @@ int main(int argc, char *argv[])
|
||||
td.high = INT_MIN;
|
||||
td.mismatch = false;
|
||||
td.delete = true;
|
||||
td.delete_error = TDB_SUCCESS;
|
||||
num = tdb_traverse(tdb, trav, &td);
|
||||
td.delete_error = NTDB_SUCCESS;
|
||||
num = ntdb_traverse(ntdb, trav, &td);
|
||||
ok1(num == NUM_RECORDS);
|
||||
ok1(td.delete_error == TDB_SUCCESS);
|
||||
ok1(td.delete_error == NTDB_SUCCESS);
|
||||
ok1(!td.mismatch);
|
||||
ok1(td.calls == NUM_RECORDS);
|
||||
ok1(td.low == 0);
|
||||
ok1(td.high == NUM_RECORDS - 1);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Now it's empty! */
|
||||
ok1(tdb_traverse(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_traverse(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Re-add. */
|
||||
ok1(store_records(tdb));
|
||||
ok1(tdb_traverse(tdb, NULL, NULL) == NUM_RECORDS);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(store_records(ntdb));
|
||||
ok1(ntdb_traverse(ntdb, NULL, NULL) == NUM_RECORDS);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
|
||||
/* Grow. This will cause us to be reshuffled. */
|
||||
tgd.calls = 0;
|
||||
tgd.num_large = 0;
|
||||
tgd.mismatch = false;
|
||||
tgd.error = TDB_SUCCESS;
|
||||
ok1(tdb_traverse(tdb, trav_grow, &tgd) > 1);
|
||||
tgd.error = NTDB_SUCCESS;
|
||||
ok1(ntdb_traverse(ntdb, trav_grow, &tgd) > 1);
|
||||
ok1(tgd.error == 0);
|
||||
ok1(!tgd.mismatch);
|
||||
ok1(tdb_check(tdb, NULL, NULL) == 0);
|
||||
ok1(ntdb_check(ntdb, NULL, NULL) == 0);
|
||||
ok1(tgd.num_large < tgd.calls);
|
||||
diag("growing db: %u calls, %u repeats",
|
||||
tgd.calls, tgd.num_large);
|
||||
|
||||
tdb_close(tdb);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
ok1(tap_log_messages == 0);
|
16
lib/ntdb/tools/Makefile
Normal file
16
lib/ntdb/tools/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
OBJS:=../../ntdb.o ../../hash.o ../../tally.o
|
||||
CFLAGS:=-I../../.. -I.. -Wall -g -O3 #-g -pg
|
||||
LDFLAGS:=-L../../..
|
||||
|
||||
default: ntdbtorture ntdbtool ntdbdump ntdbrestore mkntdb speed growtdb-bench
|
||||
|
||||
ntdbdump: ntdbdump.c $(OBJS)
|
||||
ntdbrestore: ntdbrestore.c $(OBJS)
|
||||
ntdbtorture: ntdbtorture.c $(OBJS)
|
||||
ntdbtool: ntdbtool.c $(OBJS)
|
||||
mkntdb: mkntdb.c $(OBJS)
|
||||
speed: speed.c $(OBJS)
|
||||
growtdb-bench: growtdb-bench.c $(OBJS)
|
||||
|
||||
clean:
|
||||
rm -f ntdbtorture ntdbdump ntdbrestore ntdbtool mkntdb speed growtdb-bench
|
114
lib/ntdb/tools/growtdb-bench.c
Normal file
114
lib/ntdb/tools/growtdb-bench.c
Normal file
@ -0,0 +1,114 @@
|
||||
#include "ntdb.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static void logfn(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data)
|
||||
{
|
||||
fprintf(stderr, "ntdb:%s:%s:%s\n",
|
||||
ntdb_name(ntdb), ntdb_errorstr(ecode), message);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j, users, groups;
|
||||
NTDB_DATA idxkey, idxdata;
|
||||
NTDB_DATA k, d, gk;
|
||||
char cmd[100];
|
||||
struct ntdb_context *ntdb;
|
||||
enum NTDB_ERROR ecode;
|
||||
union ntdb_attribute log;
|
||||
|
||||
if (argc != 3) {
|
||||
printf("Usage: growtdb-bench <users> <groups>\n");
|
||||
exit(1);
|
||||
}
|
||||
users = atoi(argv[1]);
|
||||
groups = atoi(argv[2]);
|
||||
|
||||
sprintf(cmd, "cat /proc/%i/statm", getpid());
|
||||
|
||||
log.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
log.base.next = NULL;
|
||||
log.log.fn = logfn;
|
||||
|
||||
ntdb = ntdb_open("/tmp/growtdb.ntdb", NTDB_DEFAULT,
|
||||
O_RDWR|O_CREAT|O_TRUNC, 0600, &log);
|
||||
|
||||
idxkey.dptr = (unsigned char *)"User index";
|
||||
idxkey.dsize = strlen("User index");
|
||||
idxdata.dsize = 51;
|
||||
idxdata.dptr = calloc(idxdata.dsize, 1);
|
||||
|
||||
/* Create users. */
|
||||
k.dsize = 48;
|
||||
k.dptr = calloc(k.dsize, 1);
|
||||
d.dsize = 64;
|
||||
d.dptr = calloc(d.dsize, 1);
|
||||
|
||||
ntdb_transaction_start(ntdb);
|
||||
for (i = 0; i < users; i++) {
|
||||
memcpy(k.dptr, &i, sizeof(i));
|
||||
ecode = ntdb_store(ntdb, k, d, NTDB_INSERT);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
errx(1, "ntdb insert failed: %s", ntdb_errorstr(ecode));
|
||||
|
||||
/* This simulates a growing index record. */
|
||||
ecode = ntdb_append(ntdb, idxkey, idxdata);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
errx(1, "ntdb append failed: %s", ntdb_errorstr(ecode));
|
||||
}
|
||||
if ((ecode = ntdb_transaction_commit(ntdb)) != 0)
|
||||
errx(1, "ntdb commit1 failed: %s", ntdb_errorstr(ecode));
|
||||
|
||||
if ((ecode = ntdb_check(ntdb, NULL, NULL)) != 0)
|
||||
errx(1, "ntdb_check failed after initial insert!");
|
||||
|
||||
system(cmd);
|
||||
|
||||
/* Now put them all in groups: add 32 bytes to each record for
|
||||
* a group. */
|
||||
gk.dsize = 48;
|
||||
gk.dptr = calloc(k.dsize, 1);
|
||||
gk.dptr[gk.dsize-1] = 1;
|
||||
|
||||
d.dsize = 32;
|
||||
for (i = 0; i < groups; i++) {
|
||||
ntdb_transaction_start(ntdb);
|
||||
/* Create the "group". */
|
||||
memcpy(gk.dptr, &i, sizeof(i));
|
||||
ecode = ntdb_store(ntdb, gk, d, NTDB_INSERT);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
errx(1, "ntdb insert failed: %s", ntdb_errorstr(ecode));
|
||||
|
||||
/* Now populate it. */
|
||||
for (j = 0; j < users; j++) {
|
||||
/* Append to the user. */
|
||||
memcpy(k.dptr, &j, sizeof(j));
|
||||
if ((ecode = ntdb_append(ntdb, k, d)) != 0)
|
||||
errx(1, "ntdb append failed: %s",
|
||||
ntdb_errorstr(ecode));
|
||||
|
||||
/* Append to the group. */
|
||||
if ((ecode = ntdb_append(ntdb, gk, d)) != 0)
|
||||
errx(1, "ntdb append failed: %s",
|
||||
ntdb_errorstr(ecode));
|
||||
}
|
||||
if ((ecode = ntdb_transaction_commit(ntdb)) != 0)
|
||||
errx(1, "ntdb commit2 failed: %s", ntdb_errorstr(ecode));
|
||||
if ((ecode = ntdb_check(ntdb, NULL, NULL)) != 0)
|
||||
errx(1, "ntdb_check failed after iteration %i!", i);
|
||||
system(cmd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
@ -7,21 +7,21 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, num_recs;
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
|
||||
if (argc != 3 || (num_recs = atoi(argv[2])) == 0)
|
||||
errx(1, "Usage: mktdb <tdbfile> <numrecords>");
|
||||
|
||||
tdb = tdb_open(argv[1], TDB_DEFAULT, O_CREAT|O_TRUNC|O_RDWR, 0600,NULL);
|
||||
if (!tdb)
|
||||
ntdb = ntdb_open(argv[1], NTDB_DEFAULT, O_CREAT|O_TRUNC|O_RDWR, 0600,NULL);
|
||||
if (!ntdb)
|
||||
err(1, "Opening %s", argv[1]);
|
||||
|
||||
for (i = 0; i < num_recs; i++) {
|
||||
TDB_DATA d;
|
||||
NTDB_DATA d;
|
||||
|
||||
d.dptr = (void *)&i;
|
||||
d.dsize = sizeof(i);
|
||||
if (tdb_store(tdb, d, d, TDB_INSERT) != 0)
|
||||
if (ntdb_store(ntdb, d, d, NTDB_INSERT) != 0)
|
||||
err(1, "Failed to store record %i", i);
|
||||
}
|
||||
printf("Done\n");
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
low level tdb backup and restore utility
|
||||
low level ntdb backup and restore utility
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -19,11 +19,11 @@
|
||||
|
||||
/*
|
||||
|
||||
This program is meant for backup/restore of tdb databases. Typical usage would be:
|
||||
tdbbackup *.tdb
|
||||
This program is meant for backup/restore of ntdb databases. Typical usage would be:
|
||||
tdbbackup *.ntdb
|
||||
when Samba shuts down cleanly, which will make a backup of all the local databases
|
||||
to *.bak files. Then on Samba startup you would use:
|
||||
tdbbackup -v *.tdb
|
||||
tdbbackup -v *.ntdb
|
||||
and this will check the databases for corruption and if corruption is detected then
|
||||
the backup will be restored.
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
#ifdef HAVE_GETOPT_H
|
||||
@ -50,13 +50,13 @@
|
||||
|
||||
static int failed;
|
||||
|
||||
static void tdb_log(struct tdb_context *tdb,
|
||||
enum tdb_log_level level,
|
||||
enum TDB_ERROR ecode,
|
||||
static void ntdb_log(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data)
|
||||
{
|
||||
fprintf(stderr, "%s:%s\n", tdb_errorstr(ecode), message);
|
||||
fprintf(stderr, "%s:%s\n", ntdb_errorstr(ecode), message);
|
||||
}
|
||||
|
||||
static char *add_suffix(const char *name, const char *suffix)
|
||||
@ -72,15 +72,15 @@ static char *add_suffix(const char *name, const char *suffix)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int copy_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
static int copy_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
|
||||
{
|
||||
struct tdb_context *tdb_new = (struct tdb_context *)state;
|
||||
enum TDB_ERROR err;
|
||||
struct ntdb_context *ntdb_new = (struct ntdb_context *)state;
|
||||
enum NTDB_ERROR err;
|
||||
|
||||
err = tdb_store(tdb_new, key, dbuf, TDB_INSERT);
|
||||
err = ntdb_store(ntdb_new, key, dbuf, NTDB_INSERT);
|
||||
if (err) {
|
||||
fprintf(stderr,"Failed to insert into %s: %s\n",
|
||||
tdb_name(tdb_new), tdb_errorstr(err));
|
||||
ntdb_name(ntdb_new), ntdb_errorstr(err));
|
||||
failed = 1;
|
||||
return 1;
|
||||
}
|
||||
@ -88,75 +88,75 @@ static int copy_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *s
|
||||
}
|
||||
|
||||
|
||||
static int test_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
static int test_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
carefully backup a tdb, validating the contents and
|
||||
carefully backup a ntdb, validating the contents and
|
||||
only doing the backup if its OK
|
||||
this function is also used for restore
|
||||
*/
|
||||
static int backup_tdb(const char *old_name, const char *new_name)
|
||||
static int backup_ntdb(const char *old_name, const char *new_name)
|
||||
{
|
||||
struct tdb_context *tdb;
|
||||
struct tdb_context *tdb_new;
|
||||
struct ntdb_context *ntdb;
|
||||
struct ntdb_context *ntdb_new;
|
||||
char *tmp_name;
|
||||
struct stat st;
|
||||
int count1, count2;
|
||||
enum TDB_ERROR err;
|
||||
union tdb_attribute log_attr;
|
||||
enum NTDB_ERROR err;
|
||||
union ntdb_attribute log_attr;
|
||||
|
||||
tmp_name = add_suffix(new_name, ".tmp");
|
||||
|
||||
/* stat the old tdb to find its permissions */
|
||||
/* stat the old ntdb to find its permissions */
|
||||
if (stat(old_name, &st) != 0) {
|
||||
perror(old_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_attr.base.attr = TDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.next = NULL;
|
||||
log_attr.log.fn = tdb_log;
|
||||
log_attr.log.fn = ntdb_log;
|
||||
|
||||
/* open the old tdb */
|
||||
tdb = tdb_open(old_name, TDB_DEFAULT, O_RDWR, 0, &log_attr);
|
||||
if (!tdb) {
|
||||
/* open the old ntdb */
|
||||
ntdb = ntdb_open(old_name, NTDB_DEFAULT, O_RDWR, 0, &log_attr);
|
||||
if (!ntdb) {
|
||||
printf("Failed to open %s\n", old_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unlink(tmp_name);
|
||||
tdb_new = tdb_open(tmp_name, TDB_DEFAULT,
|
||||
ntdb_new = ntdb_open(tmp_name, NTDB_DEFAULT,
|
||||
O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777,
|
||||
&log_attr);
|
||||
if (!tdb_new) {
|
||||
if (!ntdb_new) {
|
||||
perror(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = tdb_transaction_start(tdb);
|
||||
err = ntdb_transaction_start(ntdb);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to start transaction on old tdb: %s\n",
|
||||
tdb_errorstr(err));
|
||||
tdb_close(tdb);
|
||||
tdb_close(tdb_new);
|
||||
fprintf(stderr, "Failed to start transaction on old ntdb: %s\n",
|
||||
ntdb_errorstr(err));
|
||||
ntdb_close(ntdb);
|
||||
ntdb_close(ntdb_new);
|
||||
unlink(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* lock the backup tdb so that nobody else can change it */
|
||||
err = tdb_lockall(tdb_new);
|
||||
/* lock the backup ntdb so that nobody else can change it */
|
||||
err = ntdb_lockall(ntdb_new);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to lock backup tdb: %s\n",
|
||||
tdb_errorstr(err));
|
||||
tdb_close(tdb);
|
||||
tdb_close(tdb_new);
|
||||
fprintf(stderr, "Failed to lock backup ntdb: %s\n",
|
||||
ntdb_errorstr(err));
|
||||
ntdb_close(ntdb);
|
||||
ntdb_close(ntdb_new);
|
||||
unlink(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
@ -165,39 +165,39 @@ static int backup_tdb(const char *old_name, const char *new_name)
|
||||
failed = 0;
|
||||
|
||||
/* traverse and copy */
|
||||
count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
|
||||
count1 = ntdb_traverse(ntdb, copy_fn, (void *)ntdb_new);
|
||||
if (count1 < 0 || failed) {
|
||||
fprintf(stderr,"failed to copy %s\n", old_name);
|
||||
tdb_close(tdb);
|
||||
tdb_close(tdb_new);
|
||||
ntdb_close(ntdb);
|
||||
ntdb_close(ntdb_new);
|
||||
unlink(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* close the old tdb */
|
||||
tdb_close(tdb);
|
||||
/* close the old ntdb */
|
||||
ntdb_close(ntdb);
|
||||
|
||||
/* copy done, unlock the backup tdb */
|
||||
tdb_unlockall(tdb_new);
|
||||
/* copy done, unlock the backup ntdb */
|
||||
ntdb_unlockall(ntdb_new);
|
||||
|
||||
#ifdef HAVE_FDATASYNC
|
||||
if (fdatasync(tdb_fd(tdb_new)) != 0) {
|
||||
if (fdatasync(ntdb_fd(ntdb_new)) != 0) {
|
||||
#else
|
||||
if (fsync(tdb_fd(tdb_new)) != 0) {
|
||||
if (fsync(ntdb_fd(ntdb_new)) != 0) {
|
||||
#endif
|
||||
/* not fatal */
|
||||
fprintf(stderr, "failed to fsync backup file\n");
|
||||
}
|
||||
|
||||
/* close the new tdb and re-open read-only */
|
||||
tdb_close(tdb_new);
|
||||
/* close the new ntdb and re-open read-only */
|
||||
ntdb_close(ntdb_new);
|
||||
|
||||
/* we don't need the hash attr any more */
|
||||
log_attr.base.next = NULL;
|
||||
|
||||
tdb_new = tdb_open(tmp_name, TDB_DEFAULT, O_RDONLY, 0, &log_attr);
|
||||
if (!tdb_new) {
|
||||
ntdb_new = ntdb_open(tmp_name, NTDB_DEFAULT, O_RDONLY, 0, &log_attr);
|
||||
if (!ntdb_new) {
|
||||
fprintf(stderr,"failed to reopen %s\n", tmp_name);
|
||||
unlink(tmp_name);
|
||||
perror(tmp_name);
|
||||
@ -205,18 +205,18 @@ static int backup_tdb(const char *old_name, const char *new_name)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* traverse the new tdb to confirm */
|
||||
count2 = tdb_traverse(tdb_new, test_fn, NULL);
|
||||
/* traverse the new ntdb to confirm */
|
||||
count2 = ntdb_traverse(ntdb_new, test_fn, NULL);
|
||||
if (count2 != count1) {
|
||||
fprintf(stderr,"failed to copy %s\n", old_name);
|
||||
tdb_close(tdb_new);
|
||||
ntdb_close(ntdb_new);
|
||||
unlink(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* close the new tdb and rename it to .bak */
|
||||
tdb_close(tdb_new);
|
||||
/* close the new ntdb and rename it to .bak */
|
||||
ntdb_close(ntdb_new);
|
||||
if (rename(tmp_name, new_name) != 0) {
|
||||
perror(new_name);
|
||||
free(tmp_name);
|
||||
@ -229,31 +229,31 @@ static int backup_tdb(const char *old_name, const char *new_name)
|
||||
}
|
||||
|
||||
/*
|
||||
verify a tdb and if it is corrupt then restore from *.bak
|
||||
verify a ntdb and if it is corrupt then restore from *.bak
|
||||
*/
|
||||
static int verify_tdb(const char *fname, const char *bak_name)
|
||||
static int verify_ntdb(const char *fname, const char *bak_name)
|
||||
{
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
int count = -1;
|
||||
union tdb_attribute log_attr;
|
||||
union ntdb_attribute log_attr;
|
||||
|
||||
log_attr.base.attr = TDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.next = NULL;
|
||||
log_attr.log.fn = tdb_log;
|
||||
log_attr.log.fn = ntdb_log;
|
||||
|
||||
/* open the tdb */
|
||||
tdb = tdb_open(fname, TDB_DEFAULT, O_RDONLY, 0, &log_attr);
|
||||
/* open the ntdb */
|
||||
ntdb = ntdb_open(fname, NTDB_DEFAULT, O_RDONLY, 0, &log_attr);
|
||||
|
||||
/* traverse the tdb, then close it */
|
||||
if (tdb) {
|
||||
count = tdb_traverse(tdb, test_fn, NULL);
|
||||
tdb_close(tdb);
|
||||
/* traverse the ntdb, then close it */
|
||||
if (ntdb) {
|
||||
count = ntdb_traverse(ntdb, test_fn, NULL);
|
||||
ntdb_close(ntdb);
|
||||
}
|
||||
|
||||
/* count is < 0 means an error */
|
||||
if (count < 0) {
|
||||
printf("restoring %s\n", fname);
|
||||
return backup_tdb(bak_name, fname);
|
||||
return backup_ntdb(bak_name, fname);
|
||||
}
|
||||
|
||||
printf("%s : %d records\n", fname, count);
|
||||
@ -278,7 +278,7 @@ static int file_newer(const char *fname1, const char *fname2)
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: tdb2backup [options] <fname...>\n\n");
|
||||
printf("Usage: ntdbbackup [options] <fname...>\n\n");
|
||||
printf(" -h this help message\n");
|
||||
printf(" -v verify mode (restore if corrupt)\n");
|
||||
printf(" -s suffix set the backup suffix\n");
|
||||
@ -323,12 +323,12 @@ static void usage(void)
|
||||
bak_name = add_suffix(fname, suffix);
|
||||
|
||||
if (verify) {
|
||||
if (verify_tdb(fname, bak_name) != 0) {
|
||||
if (verify_ntdb(fname, bak_name) != 0) {
|
||||
ret = 1;
|
||||
}
|
||||
} else {
|
||||
if (file_newer(fname, bak_name) &&
|
||||
backup_tdb(fname, bak_name) != 0) {
|
||||
backup_ntdb(fname, bak_name) != 0) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
simple tdb2 dump util
|
||||
simple ntdb dump util
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Rusty Russell 2011
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#ifdef HAVE_LIBREPLACE
|
||||
#include <replace.h>
|
||||
#include <system/filesys.h>
|
||||
@ -32,7 +32,7 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
static void print_data(TDB_DATA d)
|
||||
static void print_data(NTDB_DATA d)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)d.dptr;
|
||||
int len = d.dsize;
|
||||
@ -46,7 +46,7 @@ static void print_data(TDB_DATA d)
|
||||
}
|
||||
}
|
||||
|
||||
static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
static int traverse_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
|
||||
{
|
||||
printf("{\n");
|
||||
printf("key(%d) = \"", (int)key.dsize);
|
||||
@ -59,22 +59,22 @@ static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, voi
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_tdb(const char *fname, const char *keyname)
|
||||
static int dump_ntdb(const char *fname, const char *keyname)
|
||||
{
|
||||
struct tdb_context *tdb;
|
||||
TDB_DATA key, value;
|
||||
struct ntdb_context *ntdb;
|
||||
NTDB_DATA key, value;
|
||||
|
||||
tdb = tdb_open(fname, 0, O_RDONLY, 0, NULL);
|
||||
if (!tdb) {
|
||||
ntdb = ntdb_open(fname, 0, O_RDONLY, 0, NULL);
|
||||
if (!ntdb) {
|
||||
printf("Failed to open %s\n", fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!keyname) {
|
||||
tdb_traverse(tdb, traverse_fn, NULL);
|
||||
ntdb_traverse(ntdb, traverse_fn, NULL);
|
||||
} else {
|
||||
key = tdb_mkdata(keyname, strlen(keyname));
|
||||
if (tdb_fetch(tdb, key, &value) != 0) {
|
||||
key = ntdb_mkdata(keyname, strlen(keyname));
|
||||
if (ntdb_fetch(ntdb, key, &value) != 0) {
|
||||
return 1;
|
||||
} else {
|
||||
print_data(value);
|
||||
@ -87,7 +87,7 @@ static int dump_tdb(const char *fname, const char *keyname)
|
||||
|
||||
static void usage( void)
|
||||
{
|
||||
printf( "Usage: tdb2dump [options] <filename>\n\n");
|
||||
printf( "Usage: ntdbdump [options] <filename>\n\n");
|
||||
printf( " -h this help message\n");
|
||||
printf( " -k keyname dumps value of keyname\n");
|
||||
}
|
||||
@ -98,7 +98,7 @@ static void usage( void)
|
||||
int c;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: tdb2dump <fname>\n");
|
||||
printf("Usage: ntdbdump <fname>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -118,5 +118,5 @@ static void usage( void)
|
||||
|
||||
fname = argv[optind];
|
||||
|
||||
return dump_tdb(fname, keyname);
|
||||
return dump_ntdb(fname, keyname);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
tdb2restore -- construct a tdb from tdbdump output.
|
||||
ntdbrestore -- construct a ntdb from tdbdump output.
|
||||
Copyright (C) Volker Lendecke 2010
|
||||
Copyright (C) Simon McVittie 2005
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_LIBREPLACE
|
||||
#include <replace.h>
|
||||
@ -88,7 +88,7 @@ static int read_hex(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static int read_data(FILE *f, struct tdb_data *d, size_t size) {
|
||||
static int read_data(FILE *f, NTDB_DATA *d, size_t size) {
|
||||
int c, low, high;
|
||||
int i;
|
||||
|
||||
@ -141,12 +141,12 @@ static int swallow(FILE *f, const char *s, int *eof)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool read_rec(FILE *f, struct tdb_context *tdb, int *eof)
|
||||
static bool read_rec(FILE *f, struct ntdb_context *ntdb, int *eof)
|
||||
{
|
||||
int length;
|
||||
struct tdb_data key, data;
|
||||
NTDB_DATA key, data;
|
||||
bool ret = false;
|
||||
enum TDB_ERROR e;
|
||||
enum NTDB_ERROR e;
|
||||
|
||||
key.dptr = NULL;
|
||||
data.dptr = NULL;
|
||||
@ -175,9 +175,9 @@ static bool read_rec(FILE *f, struct tdb_context *tdb, int *eof)
|
||||
|| (swallow(f, "}\n", NULL) == -1)) {
|
||||
goto fail;
|
||||
}
|
||||
e = tdb_store(tdb, key, data, TDB_INSERT);
|
||||
if (e != TDB_SUCCESS) {
|
||||
fprintf(stderr, "TDB error: %s\n", tdb_errorstr(e));
|
||||
e = ntdb_store(ntdb, key, data, NTDB_INSERT);
|
||||
if (e != NTDB_SUCCESS) {
|
||||
fprintf(stderr, "NTDB error: %s\n", ntdb_errorstr(e));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -188,28 +188,28 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int restore_tdb(const char *fname)
|
||||
static int restore_ntdb(const char *fname)
|
||||
{
|
||||
struct tdb_context *tdb;
|
||||
struct ntdb_context *ntdb;
|
||||
|
||||
tdb = tdb_open(fname, 0, O_RDWR|O_CREAT|O_EXCL, 0666, NULL);
|
||||
if (!tdb) {
|
||||
perror("tdb_open");
|
||||
ntdb = ntdb_open(fname, 0, O_RDWR|O_CREAT|O_EXCL, 0666, NULL);
|
||||
if (!ntdb) {
|
||||
perror("ntdb_open");
|
||||
fprintf(stderr, "Failed to open %s\n", fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int eof = 0;
|
||||
if (!read_rec(stdin, tdb, &eof)) {
|
||||
if (!read_rec(stdin, ntdb, &eof)) {
|
||||
if (eof) {
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (tdb_close(tdb)) {
|
||||
fprintf(stderr, "Error closing tdb\n");
|
||||
if (ntdb_close(ntdb)) {
|
||||
fprintf(stderr, "Error closing ntdb\n");
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "EOF\n");
|
||||
@ -227,5 +227,5 @@ int main(int argc, char *argv[])
|
||||
|
||||
fname = argv[1];
|
||||
|
||||
return restore_tdb(fname);
|
||||
return restore_ntdb(fname);
|
||||
}
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#ifdef HAVE_LIBREPLACE
|
||||
#include <replace.h>
|
||||
#include <system/filesys.h>
|
||||
@ -46,13 +46,13 @@ char *arg1, *arg2;
|
||||
size_t arg1len, arg2len;
|
||||
int bIterate = 0;
|
||||
char *line;
|
||||
TDB_DATA iterate_kbuf;
|
||||
NTDB_DATA iterate_kbuf;
|
||||
char cmdline[1024];
|
||||
static int disable_mmap;
|
||||
|
||||
enum commands {
|
||||
CMD_CREATE_TDB,
|
||||
CMD_OPEN_TDB,
|
||||
CMD_CREATE_NTDB,
|
||||
CMD_OPEN_NTDB,
|
||||
CMD_TRANSACTION_START,
|
||||
CMD_TRANSACTION_COMMIT,
|
||||
CMD_TRANSACTION_CANCEL,
|
||||
@ -86,8 +86,8 @@ typedef struct {
|
||||
} COMMAND_TABLE;
|
||||
|
||||
COMMAND_TABLE cmd_table[] = {
|
||||
{"create", CMD_CREATE_TDB},
|
||||
{"open", CMD_OPEN_TDB},
|
||||
{"create", CMD_CREATE_NTDB},
|
||||
{"open", CMD_OPEN_NTDB},
|
||||
#if 0
|
||||
{"transaction_start", CMD_TRANSACTION_START},
|
||||
{"transaction_commit", CMD_TRANSACTION_COMMIT},
|
||||
@ -134,23 +134,23 @@ static double _end_timer(void)
|
||||
(tp2.tv_usec - tp1.tv_usec)*1.0e-6);
|
||||
}
|
||||
|
||||
static void tdb_log(struct tdb_context *tdb,
|
||||
enum tdb_log_level level,
|
||||
enum TDB_ERROR ecode,
|
||||
static void ntdb_log(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data)
|
||||
{
|
||||
fprintf(stderr, "tdb:%s:%s:%s\n",
|
||||
tdb_name(tdb), tdb_errorstr(ecode), message);
|
||||
fprintf(stderr, "ntdb:%s:%s:%s\n",
|
||||
ntdb_name(ntdb), ntdb_errorstr(ecode), message);
|
||||
}
|
||||
|
||||
/* a tdb tool for manipulating a tdb database */
|
||||
/* a ntdb tool for manipulating a ntdb database */
|
||||
|
||||
static struct tdb_context *tdb;
|
||||
static struct ntdb_context *ntdb;
|
||||
|
||||
static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
|
||||
static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
|
||||
static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
|
||||
static int print_rec(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
|
||||
static int print_key(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
|
||||
static int print_hexkey(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
|
||||
|
||||
static void print_asc(const char *buf,int len)
|
||||
{
|
||||
@ -214,7 +214,7 @@ static void help(void)
|
||||
" hexkeys : dump the database keys as hex values\n"
|
||||
" info : print summary info about the database\n"
|
||||
" insert key data : insert a record\n"
|
||||
" move key file : move a record to a destination tdb\n"
|
||||
" move key file : move a record to a destination ntdb\n"
|
||||
" store key data : store a record (replace)\n"
|
||||
" show key : show a record by key\n"
|
||||
" delete key : delete a record by key\n"
|
||||
@ -232,51 +232,51 @@ static void help(void)
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void terror(enum TDB_ERROR err, const char *why)
|
||||
static void terror(enum NTDB_ERROR err, const char *why)
|
||||
{
|
||||
if (err != TDB_SUCCESS)
|
||||
printf("%s:%s\n", tdb_errorstr(err), why);
|
||||
if (err != NTDB_SUCCESS)
|
||||
printf("%s:%s\n", ntdb_errorstr(err), why);
|
||||
else
|
||||
printf("%s\n", why);
|
||||
}
|
||||
|
||||
static void create_tdb(const char *tdbname)
|
||||
static void create_ntdb(const char *tdbname)
|
||||
{
|
||||
union tdb_attribute log_attr;
|
||||
log_attr.base.attr = TDB_ATTRIBUTE_LOG;
|
||||
union ntdb_attribute log_attr;
|
||||
log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.next = NULL;
|
||||
log_attr.log.fn = tdb_log;
|
||||
log_attr.log.fn = ntdb_log;
|
||||
|
||||
if (tdb) tdb_close(tdb);
|
||||
tdb = tdb_open(tdbname, (disable_mmap?TDB_NOMMAP:0),
|
||||
if (ntdb) ntdb_close(ntdb);
|
||||
ntdb = ntdb_open(tdbname, (disable_mmap?NTDB_NOMMAP:0),
|
||||
O_RDWR | O_CREAT | O_TRUNC, 0600, &log_attr);
|
||||
if (!tdb) {
|
||||
if (!ntdb) {
|
||||
printf("Could not create %s: %s\n", tdbname, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void open_tdb(const char *tdbname)
|
||||
static void open_ntdb(const char *tdbname)
|
||||
{
|
||||
union tdb_attribute log_attr;
|
||||
log_attr.base.attr = TDB_ATTRIBUTE_LOG;
|
||||
union ntdb_attribute log_attr;
|
||||
log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.next = NULL;
|
||||
log_attr.log.fn = tdb_log;
|
||||
log_attr.log.fn = ntdb_log;
|
||||
|
||||
if (tdb) tdb_close(tdb);
|
||||
tdb = tdb_open(tdbname, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600,
|
||||
if (ntdb) ntdb_close(ntdb);
|
||||
ntdb = ntdb_open(tdbname, disable_mmap?NTDB_NOMMAP:0, O_RDWR, 0600,
|
||||
&log_attr);
|
||||
if (!tdb) {
|
||||
if (!ntdb) {
|
||||
printf("Could not open %s: %s\n", tdbname, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
|
||||
static void insert_ntdb(char *keyname, size_t keylen, char* data, size_t datalen)
|
||||
{
|
||||
TDB_DATA key, dbuf;
|
||||
enum TDB_ERROR ecode;
|
||||
NTDB_DATA key, dbuf;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if ((keyname == NULL) || (keylen == 0)) {
|
||||
terror(TDB_SUCCESS, "need key");
|
||||
terror(NTDB_SUCCESS, "need key");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -285,24 +285,24 @@ static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
|
||||
dbuf.dptr = (unsigned char *)data;
|
||||
dbuf.dsize = datalen;
|
||||
|
||||
ecode = tdb_store(tdb, key, dbuf, TDB_INSERT);
|
||||
ecode = ntdb_store(ntdb, key, dbuf, NTDB_INSERT);
|
||||
if (ecode) {
|
||||
terror(ecode, "insert failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
|
||||
static void store_ntdb(char *keyname, size_t keylen, char* data, size_t datalen)
|
||||
{
|
||||
TDB_DATA key, dbuf;
|
||||
enum TDB_ERROR ecode;
|
||||
NTDB_DATA key, dbuf;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if ((keyname == NULL) || (keylen == 0)) {
|
||||
terror(TDB_SUCCESS, "need key");
|
||||
terror(NTDB_SUCCESS, "need key");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data == NULL) || (datalen == 0)) {
|
||||
terror(TDB_SUCCESS, "need data");
|
||||
terror(NTDB_SUCCESS, "need data");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -312,52 +312,52 @@ static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
|
||||
dbuf.dsize = datalen;
|
||||
|
||||
printf("Storing key:\n");
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
print_rec(ntdb, key, dbuf, NULL);
|
||||
|
||||
ecode = tdb_store(tdb, key, dbuf, TDB_REPLACE);
|
||||
ecode = ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
|
||||
if (ecode) {
|
||||
terror(ecode, "store failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void show_tdb(char *keyname, size_t keylen)
|
||||
static void show_ntdb(char *keyname, size_t keylen)
|
||||
{
|
||||
TDB_DATA key, dbuf;
|
||||
enum TDB_ERROR ecode;
|
||||
NTDB_DATA key, dbuf;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if ((keyname == NULL) || (keylen == 0)) {
|
||||
terror(TDB_SUCCESS, "need key");
|
||||
terror(NTDB_SUCCESS, "need key");
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)keyname;
|
||||
key.dsize = keylen;
|
||||
|
||||
ecode = tdb_fetch(tdb, key, &dbuf);
|
||||
ecode = ntdb_fetch(ntdb, key, &dbuf);
|
||||
if (ecode) {
|
||||
terror(ecode, "fetch failed");
|
||||
return;
|
||||
}
|
||||
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
print_rec(ntdb, key, dbuf, NULL);
|
||||
|
||||
free( dbuf.dptr );
|
||||
}
|
||||
|
||||
static void delete_tdb(char *keyname, size_t keylen)
|
||||
static void delete_ntdb(char *keyname, size_t keylen)
|
||||
{
|
||||
TDB_DATA key;
|
||||
enum TDB_ERROR ecode;
|
||||
NTDB_DATA key;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if ((keyname == NULL) || (keylen == 0)) {
|
||||
terror(TDB_SUCCESS, "need key");
|
||||
terror(NTDB_SUCCESS, "need key");
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)keyname;
|
||||
key.dsize = keylen;
|
||||
|
||||
ecode = tdb_delete(tdb, key);
|
||||
ecode = ntdb_delete(ntdb, key);
|
||||
if (ecode) {
|
||||
terror(ecode, "delete failed");
|
||||
}
|
||||
@ -365,47 +365,47 @@ static void delete_tdb(char *keyname, size_t keylen)
|
||||
|
||||
static void move_rec(char *keyname, size_t keylen, char* tdbname)
|
||||
{
|
||||
TDB_DATA key, dbuf;
|
||||
struct tdb_context *dst_tdb;
|
||||
enum TDB_ERROR ecode;
|
||||
NTDB_DATA key, dbuf;
|
||||
struct ntdb_context *dst_ntdb;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
if ((keyname == NULL) || (keylen == 0)) {
|
||||
terror(TDB_SUCCESS, "need key");
|
||||
terror(NTDB_SUCCESS, "need key");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !tdbname ) {
|
||||
terror(TDB_SUCCESS, "need destination tdb name");
|
||||
terror(NTDB_SUCCESS, "need destination ntdb name");
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)keyname;
|
||||
key.dsize = keylen;
|
||||
|
||||
ecode = tdb_fetch(tdb, key, &dbuf);
|
||||
ecode = ntdb_fetch(ntdb, key, &dbuf);
|
||||
if (ecode) {
|
||||
terror(ecode, "fetch failed");
|
||||
return;
|
||||
}
|
||||
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
print_rec(ntdb, key, dbuf, NULL);
|
||||
|
||||
dst_tdb = tdb_open(tdbname, 0, O_RDWR, 0600, NULL);
|
||||
if ( !dst_tdb ) {
|
||||
terror(TDB_SUCCESS, "unable to open destination tdb");
|
||||
dst_ntdb = ntdb_open(tdbname, 0, O_RDWR, 0600, NULL);
|
||||
if ( !dst_ntdb ) {
|
||||
terror(NTDB_SUCCESS, "unable to open destination ntdb");
|
||||
return;
|
||||
}
|
||||
|
||||
ecode = tdb_store( dst_tdb, key, dbuf, TDB_REPLACE);
|
||||
ecode = ntdb_store( dst_ntdb, key, dbuf, NTDB_REPLACE);
|
||||
if (ecode)
|
||||
terror(ecode, "failed to move record");
|
||||
else
|
||||
printf("record moved\n");
|
||||
|
||||
tdb_close( dst_tdb );
|
||||
ntdb_close( dst_ntdb );
|
||||
}
|
||||
|
||||
static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
static int print_rec(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
|
||||
{
|
||||
printf("\nkey %d bytes\n", (int)key.dsize);
|
||||
print_asc((const char *)key.dptr, key.dsize);
|
||||
@ -414,7 +414,7 @@ static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, v
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
static int print_key(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
|
||||
{
|
||||
printf("key %d bytes: ", (int)key.dsize);
|
||||
print_asc((const char *)key.dptr, key.dsize);
|
||||
@ -422,7 +422,7 @@ static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, v
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
static int print_hexkey(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
|
||||
{
|
||||
printf("key %d bytes\n", (int)key.dsize);
|
||||
print_data((const char *)key.dptr, key.dsize);
|
||||
@ -432,18 +432,18 @@ static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf
|
||||
|
||||
static int total_bytes;
|
||||
|
||||
static int traverse_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
static int traverse_fn(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
|
||||
{
|
||||
total_bytes += dbuf.dsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void info_tdb(void)
|
||||
static void info_ntdb(void)
|
||||
{
|
||||
enum TDB_ERROR ecode;
|
||||
enum NTDB_ERROR ecode;
|
||||
char *summary;
|
||||
|
||||
ecode = tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &summary);
|
||||
ecode = ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &summary);
|
||||
|
||||
if (ecode) {
|
||||
terror(ecode, "Getting summary");
|
||||
@ -453,7 +453,7 @@ static void info_tdb(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void speed_tdb(const char *tlimit)
|
||||
static void speed_ntdb(const char *tlimit)
|
||||
{
|
||||
unsigned timelimit = tlimit?atoi(tlimit):0;
|
||||
double t;
|
||||
@ -465,11 +465,11 @@ static void speed_tdb(const char *tlimit)
|
||||
_start_timer();
|
||||
do {
|
||||
long int r = random();
|
||||
TDB_DATA key, dbuf;
|
||||
key = tdb_mkdata("store test", strlen("store test"));
|
||||
NTDB_DATA key, dbuf;
|
||||
key = ntdb_mkdata("store test", strlen("store test"));
|
||||
dbuf.dptr = (unsigned char *)&r;
|
||||
dbuf.dsize = sizeof(r);
|
||||
tdb_store(tdb, key, dbuf, TDB_REPLACE);
|
||||
ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
|
||||
t = _end_timer();
|
||||
ops++;
|
||||
} while (t < timelimit);
|
||||
@ -480,11 +480,11 @@ static void speed_tdb(const char *tlimit)
|
||||
_start_timer();
|
||||
do {
|
||||
long int r = random();
|
||||
TDB_DATA key, dbuf;
|
||||
key = tdb_mkdata("store test", strlen("store test"));
|
||||
NTDB_DATA key, dbuf;
|
||||
key = ntdb_mkdata("store test", strlen("store test"));
|
||||
dbuf.dptr = (unsigned char *)&r;
|
||||
dbuf.dsize = sizeof(r);
|
||||
tdb_fetch(tdb, key, &dbuf);
|
||||
ntdb_fetch(ntdb, key, &dbuf);
|
||||
t = _end_timer();
|
||||
ops++;
|
||||
} while (t < timelimit);
|
||||
@ -495,13 +495,13 @@ static void speed_tdb(const char *tlimit)
|
||||
_start_timer();
|
||||
do {
|
||||
long int r = random();
|
||||
TDB_DATA key, dbuf;
|
||||
key = tdb_mkdata("transaction test", strlen("transaction test"));
|
||||
NTDB_DATA key, dbuf;
|
||||
key = ntdb_mkdata("transaction test", strlen("transaction test"));
|
||||
dbuf.dptr = (unsigned char *)&r;
|
||||
dbuf.dsize = sizeof(r);
|
||||
tdb_transaction_start(tdb);
|
||||
tdb_store(tdb, key, dbuf, TDB_REPLACE);
|
||||
tdb_transaction_commit(tdb);
|
||||
ntdb_transaction_start(ntdb);
|
||||
ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
|
||||
ntdb_transaction_commit(ntdb);
|
||||
t = _end_timer();
|
||||
ops++;
|
||||
} while (t < timelimit);
|
||||
@ -511,7 +511,7 @@ static void speed_tdb(const char *tlimit)
|
||||
printf("Testing traverse speed for %u seconds\n", timelimit);
|
||||
_start_timer();
|
||||
do {
|
||||
tdb_traverse(tdb, traverse_fn, NULL);
|
||||
ntdb_traverse(ntdb, traverse_fn, NULL);
|
||||
t = _end_timer();
|
||||
ops++;
|
||||
} while (t < timelimit);
|
||||
@ -528,7 +528,7 @@ static void toggle_mmap(void)
|
||||
}
|
||||
}
|
||||
|
||||
static char *tdb_getline(const char *prompt)
|
||||
static char *ntdb_getline(const char *prompt)
|
||||
{
|
||||
static char thisline[1024];
|
||||
char *p;
|
||||
@ -540,45 +540,45 @@ static char *tdb_getline(const char *prompt)
|
||||
return p?thisline:NULL;
|
||||
}
|
||||
|
||||
static int do_delete_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
static int do_delete_fn(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
return tdb_delete(the_tdb, key);
|
||||
return ntdb_delete(the_ntdb, key);
|
||||
}
|
||||
|
||||
static void first_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
|
||||
static void first_record(struct ntdb_context *the_ntdb, NTDB_DATA *pkey)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
enum TDB_ERROR ecode;
|
||||
ecode = tdb_firstkey(the_tdb, pkey);
|
||||
NTDB_DATA dbuf;
|
||||
enum NTDB_ERROR ecode;
|
||||
ecode = ntdb_firstkey(the_ntdb, pkey);
|
||||
if (!ecode)
|
||||
ecode = tdb_fetch(the_tdb, *pkey, &dbuf);
|
||||
ecode = ntdb_fetch(the_ntdb, *pkey, &dbuf);
|
||||
if (ecode) terror(ecode, "fetch failed");
|
||||
else {
|
||||
print_rec(the_tdb, *pkey, dbuf, NULL);
|
||||
print_rec(the_ntdb, *pkey, dbuf, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void next_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
|
||||
static void next_record(struct ntdb_context *the_ntdb, NTDB_DATA *pkey)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
enum TDB_ERROR ecode;
|
||||
ecode = tdb_nextkey(the_tdb, pkey);
|
||||
NTDB_DATA dbuf;
|
||||
enum NTDB_ERROR ecode;
|
||||
ecode = ntdb_nextkey(the_ntdb, pkey);
|
||||
|
||||
if (!ecode)
|
||||
ecode = tdb_fetch(the_tdb, *pkey, &dbuf);
|
||||
ecode = ntdb_fetch(the_ntdb, *pkey, &dbuf);
|
||||
if (ecode)
|
||||
terror(ecode, "fetch failed");
|
||||
else
|
||||
print_rec(the_tdb, *pkey, dbuf, NULL);
|
||||
print_rec(the_ntdb, *pkey, dbuf, NULL);
|
||||
}
|
||||
|
||||
static void check_db(struct tdb_context *the_tdb)
|
||||
static void check_db(struct ntdb_context *the_ntdb)
|
||||
{
|
||||
if (!the_tdb) {
|
||||
if (!the_ntdb) {
|
||||
printf("Error: No database opened!\n");
|
||||
} else {
|
||||
if (tdb_check(the_tdb, NULL, NULL) != 0)
|
||||
if (ntdb_check(the_ntdb, NULL, NULL) != 0)
|
||||
printf("Integrity check for the opened database failed.\n");
|
||||
else
|
||||
printf("Database integrity is OK.\n");
|
||||
@ -605,54 +605,54 @@ static int do_command(void)
|
||||
}
|
||||
|
||||
switch (mycmd) {
|
||||
case CMD_CREATE_TDB:
|
||||
case CMD_CREATE_NTDB:
|
||||
bIterate = 0;
|
||||
create_tdb(arg1);
|
||||
create_ntdb(arg1);
|
||||
return 0;
|
||||
case CMD_OPEN_TDB:
|
||||
case CMD_OPEN_NTDB:
|
||||
bIterate = 0;
|
||||
open_tdb(arg1);
|
||||
open_ntdb(arg1);
|
||||
return 0;
|
||||
case CMD_SYSTEM:
|
||||
/* Shell command */
|
||||
if (system(arg1) == -1) {
|
||||
terror(TDB_SUCCESS, "system() call failed\n");
|
||||
terror(NTDB_SUCCESS, "system() call failed\n");
|
||||
}
|
||||
return 0;
|
||||
case CMD_QUIT:
|
||||
return 1;
|
||||
default:
|
||||
/* all the rest require a open database */
|
||||
if (!tdb) {
|
||||
if (!ntdb) {
|
||||
bIterate = 0;
|
||||
terror(TDB_SUCCESS, "database not open");
|
||||
terror(NTDB_SUCCESS, "database not open");
|
||||
help();
|
||||
return 0;
|
||||
}
|
||||
switch (mycmd) {
|
||||
case CMD_TRANSACTION_START:
|
||||
bIterate = 0;
|
||||
tdb_transaction_start(tdb);
|
||||
ntdb_transaction_start(ntdb);
|
||||
return 0;
|
||||
case CMD_TRANSACTION_COMMIT:
|
||||
bIterate = 0;
|
||||
tdb_transaction_commit(tdb);
|
||||
ntdb_transaction_commit(ntdb);
|
||||
return 0;
|
||||
case CMD_TRANSACTION_CANCEL:
|
||||
bIterate = 0;
|
||||
tdb_transaction_cancel(tdb);
|
||||
ntdb_transaction_cancel(ntdb);
|
||||
return 0;
|
||||
case CMD_ERASE:
|
||||
bIterate = 0;
|
||||
tdb_traverse(tdb, do_delete_fn, NULL);
|
||||
ntdb_traverse(ntdb, do_delete_fn, NULL);
|
||||
return 0;
|
||||
case CMD_DUMP:
|
||||
bIterate = 0;
|
||||
tdb_traverse(tdb, print_rec, NULL);
|
||||
ntdb_traverse(ntdb, print_rec, NULL);
|
||||
return 0;
|
||||
case CMD_INSERT:
|
||||
bIterate = 0;
|
||||
insert_tdb(arg1, arg1len,arg2,arg2len);
|
||||
insert_ntdb(arg1, arg1len,arg2,arg2len);
|
||||
return 0;
|
||||
case CMD_MOVE:
|
||||
bIterate = 0;
|
||||
@ -660,55 +660,55 @@ static int do_command(void)
|
||||
return 0;
|
||||
case CMD_STORE:
|
||||
bIterate = 0;
|
||||
store_tdb(arg1,arg1len,arg2,arg2len);
|
||||
store_ntdb(arg1,arg1len,arg2,arg2len);
|
||||
return 0;
|
||||
case CMD_SHOW:
|
||||
bIterate = 0;
|
||||
show_tdb(arg1, arg1len);
|
||||
show_ntdb(arg1, arg1len);
|
||||
return 0;
|
||||
case CMD_KEYS:
|
||||
tdb_traverse(tdb, print_key, NULL);
|
||||
ntdb_traverse(ntdb, print_key, NULL);
|
||||
return 0;
|
||||
case CMD_HEXKEYS:
|
||||
tdb_traverse(tdb, print_hexkey, NULL);
|
||||
ntdb_traverse(ntdb, print_hexkey, NULL);
|
||||
return 0;
|
||||
case CMD_DELETE:
|
||||
bIterate = 0;
|
||||
delete_tdb(arg1,arg1len);
|
||||
delete_ntdb(arg1,arg1len);
|
||||
return 0;
|
||||
#if 0
|
||||
case CMD_LIST_HASH_FREE:
|
||||
tdb_dump_all(tdb);
|
||||
ntdb_dump_all(ntdb);
|
||||
return 0;
|
||||
case CMD_LIST_FREE:
|
||||
tdb_printfreelist(tdb);
|
||||
ntdb_printfreelist(ntdb);
|
||||
return 0;
|
||||
#endif
|
||||
case CMD_INFO:
|
||||
info_tdb();
|
||||
info_ntdb();
|
||||
return 0;
|
||||
case CMD_SPEED:
|
||||
speed_tdb(arg1);
|
||||
speed_ntdb(arg1);
|
||||
return 0;
|
||||
case CMD_MMAP:
|
||||
toggle_mmap();
|
||||
return 0;
|
||||
case CMD_FIRST:
|
||||
bIterate = 1;
|
||||
first_record(tdb, &iterate_kbuf);
|
||||
first_record(ntdb, &iterate_kbuf);
|
||||
return 0;
|
||||
case CMD_NEXT:
|
||||
if (bIterate)
|
||||
next_record(tdb, &iterate_kbuf);
|
||||
next_record(ntdb, &iterate_kbuf);
|
||||
return 0;
|
||||
case CMD_CHECK:
|
||||
check_db(tdb);
|
||||
check_db(ntdb);
|
||||
return 0;
|
||||
case CMD_HELP:
|
||||
help();
|
||||
return 0;
|
||||
case CMD_CREATE_TDB:
|
||||
case CMD_OPEN_TDB:
|
||||
case CMD_CREATE_NTDB:
|
||||
case CMD_OPEN_NTDB:
|
||||
case CMD_SYSTEM:
|
||||
case CMD_QUIT:
|
||||
/*
|
||||
@ -773,7 +773,7 @@ int main(int argc, char *argv[])
|
||||
case 1:
|
||||
case 2:
|
||||
/* Interactive mode */
|
||||
while ((cmdname = tdb_getline("tdb> "))) {
|
||||
while ((cmdname = ntdb_getline("ntdb> "))) {
|
||||
arg2 = arg1 = NULL;
|
||||
if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
|
||||
arg1++;
|
||||
@ -804,7 +804,7 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
|
||||
if (tdb) tdb_close(tdb);
|
||||
if (ntdb) ntdb_close(ntdb);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
/* this tests tdb by doing lots of ops from several simultaneous
|
||||
/* this tests ntdb by doing lots of ops from several simultaneous
|
||||
writers - that stresses the locking code.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
#include <ccan/err/err.h>
|
||||
#ifdef HAVE_LIBREPLACE
|
||||
#include <replace.h>
|
||||
@ -35,7 +35,7 @@
|
||||
#define KEYLEN 3
|
||||
#define DATALEN 100
|
||||
|
||||
static struct tdb_context *db;
|
||||
static struct ntdb_context *db;
|
||||
static int in_transaction;
|
||||
static int in_traverse;
|
||||
static int error_count;
|
||||
@ -44,17 +44,17 @@ static int always_transaction = 0;
|
||||
#endif
|
||||
static int loopnum;
|
||||
static int count_pipe;
|
||||
static union tdb_attribute log_attr;
|
||||
static union tdb_attribute seed_attr;
|
||||
static union ntdb_attribute log_attr;
|
||||
static union ntdb_attribute seed_attr;
|
||||
|
||||
static void tdb_log(struct tdb_context *tdb,
|
||||
enum tdb_log_level level,
|
||||
enum TDB_ERROR ecode,
|
||||
static void ntdb_log(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data)
|
||||
{
|
||||
printf("tdb:%s:%s:%s\n",
|
||||
tdb_name(tdb), tdb_errorstr(ecode), message);
|
||||
printf("ntdb:%s:%s:%s\n",
|
||||
ntdb_name(ntdb), ntdb_errorstr(ecode), message);
|
||||
fflush(stdout);
|
||||
#if 0
|
||||
{
|
||||
@ -80,10 +80,10 @@ static void segv_handler(int sig, siginfo_t *info, void *p)
|
||||
_exit(11);
|
||||
}
|
||||
|
||||
static void fatal(struct tdb_context *tdb, const char *why)
|
||||
static void fatal(struct ntdb_context *ntdb, const char *why)
|
||||
{
|
||||
fprintf(stderr, "%u:%s:%s\n", getpid(), why,
|
||||
tdb ? tdb_errorstr(tdb_error(tdb)) : "(no tdb)");
|
||||
ntdb ? ntdb_errorstr(ntdb_error(ntdb)) : "(no ntdb)");
|
||||
error_count++;
|
||||
}
|
||||
|
||||
@ -101,12 +101,12 @@ static char *randbuf(int len)
|
||||
}
|
||||
|
||||
static void addrec_db(void);
|
||||
static int modify_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
static int modify_traverse(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
#if CULL_PROB
|
||||
if (random() % CULL_PROB == 0) {
|
||||
tdb_delete(tdb, key);
|
||||
ntdb_delete(ntdb, key);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -128,7 +128,7 @@ static void addrec_db(void)
|
||||
{
|
||||
int klen, dlen;
|
||||
char *k, *d;
|
||||
TDB_DATA key, data;
|
||||
NTDB_DATA key, data;
|
||||
|
||||
klen = 1 + (rand() % KEYLEN);
|
||||
dlen = 1 + (rand() % DATALEN);
|
||||
@ -144,34 +144,34 @@ static void addrec_db(void)
|
||||
|
||||
#if REOPEN_PROB
|
||||
if (in_traverse == 0 && in_transaction == 0 && random() % REOPEN_PROB == 0) {
|
||||
tdb_reopen_all(0);
|
||||
ntdb_reopen_all(0);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TRANSACTION_PROB
|
||||
if (in_traverse == 0 && in_transaction == 0 && (always_transaction || random() % TRANSACTION_PROB == 0)) {
|
||||
if (tdb_transaction_start(db) != 0) {
|
||||
fatal(db, "tdb_transaction_start failed");
|
||||
if (ntdb_transaction_start(db) != 0) {
|
||||
fatal(db, "ntdb_transaction_start failed");
|
||||
}
|
||||
in_transaction++;
|
||||
goto next;
|
||||
}
|
||||
if (in_traverse == 0 && in_transaction && random() % TRANSACTION_PROB == 0) {
|
||||
if (random() % TRANSACTION_PREPARE_PROB == 0) {
|
||||
if (tdb_transaction_prepare_commit(db) != 0) {
|
||||
fatal(db, "tdb_transaction_prepare_commit failed");
|
||||
if (ntdb_transaction_prepare_commit(db) != 0) {
|
||||
fatal(db, "ntdb_transaction_prepare_commit failed");
|
||||
}
|
||||
}
|
||||
if (tdb_transaction_commit(db) != 0) {
|
||||
fatal(db, "tdb_transaction_commit failed");
|
||||
if (ntdb_transaction_commit(db) != 0) {
|
||||
fatal(db, "ntdb_transaction_commit failed");
|
||||
}
|
||||
in_transaction--;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (in_traverse == 0 && in_transaction && random() % TRANSACTION_PROB == 0) {
|
||||
tdb_transaction_cancel(db);
|
||||
ntdb_transaction_cancel(db);
|
||||
in_transaction--;
|
||||
goto next;
|
||||
}
|
||||
@ -179,15 +179,15 @@ static void addrec_db(void)
|
||||
|
||||
#if DELETE_PROB
|
||||
if (random() % DELETE_PROB == 0) {
|
||||
tdb_delete(db, key);
|
||||
ntdb_delete(db, key);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STORE_PROB
|
||||
if (random() % STORE_PROB == 0) {
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal(db, "tdb_store failed");
|
||||
if (ntdb_store(db, key, data, NTDB_REPLACE) != 0) {
|
||||
fatal(db, "ntdb_store failed");
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
@ -195,8 +195,8 @@ static void addrec_db(void)
|
||||
|
||||
#if APPEND_PROB
|
||||
if (random() % APPEND_PROB == 0) {
|
||||
if (tdb_append(db, key, data) != 0) {
|
||||
fatal(db, "tdb_append failed");
|
||||
if (ntdb_append(db, key, data) != 0) {
|
||||
fatal(db, "ntdb_append failed");
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
@ -204,16 +204,16 @@ static void addrec_db(void)
|
||||
|
||||
#if LOCKSTORE_PROB
|
||||
if (random() % LOCKSTORE_PROB == 0) {
|
||||
tdb_chainlock(db, key);
|
||||
if (tdb_fetch(db, key, &data) != TDB_SUCCESS) {
|
||||
ntdb_chainlock(db, key);
|
||||
if (ntdb_fetch(db, key, &data) != NTDB_SUCCESS) {
|
||||
data.dsize = 0;
|
||||
data.dptr = NULL;
|
||||
}
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal(db, "tdb_store failed");
|
||||
if (ntdb_store(db, key, data, NTDB_REPLACE) != 0) {
|
||||
fatal(db, "ntdb_store failed");
|
||||
}
|
||||
if (data.dptr) free(data.dptr);
|
||||
tdb_chainunlock(db, key);
|
||||
ntdb_chainunlock(db, key);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
@ -222,13 +222,13 @@ static void addrec_db(void)
|
||||
/* FIXME: recursive traverses break transactions? */
|
||||
if (in_traverse == 0 && random() % TRAVERSE_PROB == 0) {
|
||||
in_traverse++;
|
||||
tdb_traverse(db, modify_traverse, NULL);
|
||||
ntdb_traverse(db, modify_traverse, NULL);
|
||||
in_traverse--;
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tdb_fetch(db, key, &data) == TDB_SUCCESS)
|
||||
if (ntdb_fetch(db, key, &data) == NTDB_SUCCESS)
|
||||
free(data.dptr);
|
||||
|
||||
next:
|
||||
@ -236,16 +236,16 @@ next:
|
||||
free(d);
|
||||
}
|
||||
|
||||
static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
static int traverse_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
tdb_delete(tdb, key);
|
||||
ntdb_delete(ntdb, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: tdb2torture"
|
||||
printf("Usage: ntdbtorture"
|
||||
#if TRANSACTION_PROB
|
||||
" [-t]"
|
||||
#endif
|
||||
@ -263,13 +263,13 @@ static void send_count_and_suicide(int sig)
|
||||
}
|
||||
|
||||
static int run_child(const char *filename, int i, int seed, unsigned num_loops,
|
||||
unsigned start, int tdb_flags)
|
||||
unsigned start, int ntdb_flags)
|
||||
{
|
||||
struct sigaction act = { .sa_sigaction = segv_handler,
|
||||
.sa_flags = SA_SIGINFO };
|
||||
sigaction(11, &act, NULL);
|
||||
|
||||
db = tdb_open(filename, tdb_flags, O_RDWR | O_CREAT, 0600,
|
||||
db = ntdb_open(filename, ntdb_flags, O_RDWR | O_CREAT, 0600,
|
||||
&log_attr);
|
||||
if (!db) {
|
||||
fatal(NULL, "db open failed");
|
||||
@ -295,29 +295,29 @@ static int run_child(const char *filename, int i, int seed, unsigned num_loops,
|
||||
}
|
||||
|
||||
if (error_count == 0) {
|
||||
tdb_traverse(db, NULL, NULL);
|
||||
ntdb_traverse(db, NULL, NULL);
|
||||
#if TRANSACTION_PROB
|
||||
if (always_transaction) {
|
||||
while (in_transaction) {
|
||||
tdb_transaction_cancel(db);
|
||||
ntdb_transaction_cancel(db);
|
||||
in_transaction--;
|
||||
}
|
||||
if (tdb_transaction_start(db) != 0)
|
||||
fatal(db, "tdb_transaction_start failed");
|
||||
if (ntdb_transaction_start(db) != 0)
|
||||
fatal(db, "ntdb_transaction_start failed");
|
||||
}
|
||||
#endif
|
||||
tdb_traverse(db, traverse_fn, NULL);
|
||||
tdb_traverse(db, traverse_fn, NULL);
|
||||
ntdb_traverse(db, traverse_fn, NULL);
|
||||
ntdb_traverse(db, traverse_fn, NULL);
|
||||
|
||||
#if TRANSACTION_PROB
|
||||
if (always_transaction) {
|
||||
if (tdb_transaction_commit(db) != 0)
|
||||
fatal(db, "tdb_transaction_commit failed");
|
||||
if (ntdb_transaction_commit(db) != 0)
|
||||
fatal(db, "ntdb_transaction_commit failed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
tdb_close(db);
|
||||
ntdb_close(db);
|
||||
|
||||
return (error_count < 100 ? error_count : 100);
|
||||
}
|
||||
@ -350,13 +350,13 @@ int main(int argc, char * const *argv)
|
||||
pid_t *pids;
|
||||
int kill_random = 0;
|
||||
int *done;
|
||||
int tdb_flags = TDB_DEFAULT;
|
||||
char *test_tdb;
|
||||
int ntdb_flags = NTDB_DEFAULT;
|
||||
char *test_ntdb;
|
||||
|
||||
log_attr.base.attr = TDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
log_attr.base.next = &seed_attr;
|
||||
log_attr.log.fn = tdb_log;
|
||||
seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
|
||||
log_attr.log.fn = ntdb_log;
|
||||
seed_attr.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
seed_attr.base.next = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv, "n:l:s:thkS")) != -1) {
|
||||
@ -371,7 +371,7 @@ int main(int argc, char * const *argv)
|
||||
seed = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'S':
|
||||
tdb_flags = TDB_NOSYNC;
|
||||
ntdb_flags = NTDB_NOSYNC;
|
||||
break;
|
||||
case 't':
|
||||
#if TRANSACTION_PROB
|
||||
@ -389,9 +389,9 @@ int main(int argc, char * const *argv)
|
||||
}
|
||||
}
|
||||
|
||||
test_tdb = test_path("torture.tdb2");
|
||||
test_ntdb = test_path("torture.ntdb");
|
||||
|
||||
unlink(test_tdb);
|
||||
unlink(test_ntdb);
|
||||
|
||||
if (seed == -1) {
|
||||
seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
|
||||
@ -400,8 +400,8 @@ int main(int argc, char * const *argv)
|
||||
|
||||
if (num_procs == 1 && !kill_random) {
|
||||
/* Don't fork for this case, makes debugging easier. */
|
||||
error_count = run_child(test_tdb, 0, seed, num_loops, 0,
|
||||
tdb_flags);
|
||||
error_count = run_child(test_ntdb, 0, seed, num_loops, 0,
|
||||
ntdb_flags);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -427,8 +427,8 @@ int main(int argc, char * const *argv)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
exit(run_child(test_tdb, i, seed, num_loops, 0,
|
||||
tdb_flags));
|
||||
exit(run_child(test_ntdb, i, seed, num_loops, 0,
|
||||
ntdb_flags));
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,9 +484,9 @@ int main(int argc, char * const *argv)
|
||||
}
|
||||
pids[j] = fork();
|
||||
if (pids[j] == 0)
|
||||
exit(run_child(test_tdb, j, seed,
|
||||
exit(run_child(test_ntdb, j, seed,
|
||||
num_loops, done[j],
|
||||
tdb_flags));
|
||||
ntdb_flags));
|
||||
printf("Restarting child %i for %u-%u\n",
|
||||
j, done[j], num_loops);
|
||||
continue;
|
||||
@ -510,20 +510,20 @@ int main(int argc, char * const *argv)
|
||||
|
||||
done:
|
||||
if (error_count == 0) {
|
||||
db = tdb_open(test_tdb, TDB_DEFAULT, O_RDWR | O_CREAT,
|
||||
db = ntdb_open(test_ntdb, NTDB_DEFAULT, O_RDWR | O_CREAT,
|
||||
0600, &log_attr);
|
||||
if (!db) {
|
||||
fatal(db, "db open failed");
|
||||
exit(1);
|
||||
}
|
||||
if (tdb_check(db, NULL, NULL) != 0) {
|
||||
if (ntdb_check(db, NULL, NULL) != 0) {
|
||||
fatal(db, "db check failed");
|
||||
exit(1);
|
||||
}
|
||||
tdb_close(db);
|
||||
ntdb_close(db);
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
free(test_tdb);
|
||||
free(test_ntdb);
|
||||
return error_count;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Simple speed test for TDB */
|
||||
/* Simple speed test for NTDB */
|
||||
#include <ccan/err/err.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
@ -10,7 +10,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "tdb2.h"
|
||||
#include "ntdb.h"
|
||||
|
||||
/* Nanoseconds per operation */
|
||||
static size_t normalize(const struct timeval *start,
|
||||
@ -30,31 +30,31 @@ static size_t file_size(void)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat("/tmp/speed.tdb", &st) != 0)
|
||||
if (stat("/tmp/speed.ntdb", &st) != 0)
|
||||
return -1;
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
static int count_record(struct tdb_context *tdb,
|
||||
TDB_DATA key, TDB_DATA data, void *p)
|
||||
static int count_record(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key, NTDB_DATA data, void *p)
|
||||
{
|
||||
int *total = p;
|
||||
*total += *(int *)data.dptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_and_clear_stats(struct tdb_context **tdb,
|
||||
static void dump_and_clear_stats(struct ntdb_context **ntdb,
|
||||
int flags,
|
||||
union tdb_attribute *attr)
|
||||
union ntdb_attribute *attr)
|
||||
{
|
||||
union tdb_attribute stats;
|
||||
enum TDB_ERROR ecode;
|
||||
union ntdb_attribute stats;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
stats.base.attr = TDB_ATTRIBUTE_STATS;
|
||||
stats.base.attr = NTDB_ATTRIBUTE_STATS;
|
||||
stats.stats.size = sizeof(stats.stats);
|
||||
ecode = tdb_get_attribute(*tdb, &stats);
|
||||
if (ecode != TDB_SUCCESS)
|
||||
errx(1, "Getting stats: %s", tdb_errorstr(ecode));
|
||||
ecode = ntdb_get_attribute(*ntdb, &stats);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
errx(1, "Getting stats: %s", ntdb_errorstr(ecode));
|
||||
|
||||
printf("allocs = %llu\n",
|
||||
(unsigned long long)stats.stats.allocs);
|
||||
@ -122,43 +122,43 @@ static void dump_and_clear_stats(struct tdb_context **tdb,
|
||||
(unsigned long long)stats.stats.lock_nonblock_fail);
|
||||
|
||||
/* Now clear. */
|
||||
tdb_close(*tdb);
|
||||
*tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR, 0, attr);
|
||||
ntdb_close(*ntdb);
|
||||
*ntdb = ntdb_open("/tmp/speed.ntdb", flags, O_RDWR, 0, attr);
|
||||
}
|
||||
|
||||
static void tdb_log(struct tdb_context *tdb,
|
||||
enum tdb_log_level level,
|
||||
enum TDB_ERROR ecode,
|
||||
static void ntdb_log(struct ntdb_context *ntdb,
|
||||
enum ntdb_log_level level,
|
||||
enum NTDB_ERROR ecode,
|
||||
const char *message,
|
||||
void *data)
|
||||
{
|
||||
fprintf(stderr, "tdb:%s:%s:%s\n",
|
||||
tdb_name(tdb), tdb_errorstr(ecode), message);
|
||||
fprintf(stderr, "ntdb:%s:%s:%s\n",
|
||||
ntdb_name(ntdb), ntdb_errorstr(ecode), message);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i, j, num = 1000, stage = 0, stopat = -1;
|
||||
int flags = TDB_DEFAULT;
|
||||
int flags = NTDB_DEFAULT;
|
||||
bool transaction = false, summary = false;
|
||||
TDB_DATA key, data;
|
||||
struct tdb_context *tdb;
|
||||
NTDB_DATA key, data;
|
||||
struct ntdb_context *ntdb;
|
||||
struct timeval start, stop;
|
||||
union tdb_attribute seed, log;
|
||||
union ntdb_attribute seed, log;
|
||||
bool do_stats = false;
|
||||
enum TDB_ERROR ecode;
|
||||
enum NTDB_ERROR ecode;
|
||||
|
||||
/* Try to keep benchmarks even. */
|
||||
seed.base.attr = TDB_ATTRIBUTE_SEED;
|
||||
seed.base.attr = NTDB_ATTRIBUTE_SEED;
|
||||
seed.base.next = NULL;
|
||||
seed.seed.seed = 0;
|
||||
|
||||
log.base.attr = TDB_ATTRIBUTE_LOG;
|
||||
log.base.attr = NTDB_ATTRIBUTE_LOG;
|
||||
log.base.next = &seed;
|
||||
log.log.fn = tdb_log;
|
||||
log.log.fn = ntdb_log;
|
||||
|
||||
if (argv[1] && strcmp(argv[1], "--internal") == 0) {
|
||||
flags = TDB_INTERNAL;
|
||||
flags = NTDB_INTERNAL;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
@ -168,7 +168,7 @@ int main(int argc, char *argv[])
|
||||
argv++;
|
||||
}
|
||||
if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
|
||||
flags |= TDB_NOSYNC;
|
||||
flags |= NTDB_NOSYNC;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
@ -183,10 +183,10 @@ int main(int argc, char *argv[])
|
||||
argv++;
|
||||
}
|
||||
|
||||
tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
|
||||
ntdb = ntdb_open("/tmp/speed.ntdb", flags, O_RDWR|O_CREAT|O_TRUNC,
|
||||
0600, &log);
|
||||
if (!tdb)
|
||||
err(1, "Opening /tmp/speed.tdb");
|
||||
if (!ntdb)
|
||||
err(1, "Opening /tmp/speed.ntdb");
|
||||
|
||||
key.dptr = (void *)&i;
|
||||
key.dsize = sizeof(i);
|
||||
@ -206,199 +206,199 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* Add 1000 records. */
|
||||
printf("Adding %u records: ", num); fflush(stdout);
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
gettimeofday(&start, NULL);
|
||||
for (i = 0; i < num; i++)
|
||||
if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
|
||||
errx(1, "Inserting key %u in tdb: %s",
|
||||
i, tdb_errorstr(ecode));
|
||||
if ((ecode = ntdb_store(ntdb, key, data, NTDB_INSERT)) != 0)
|
||||
errx(1, "Inserting key %u in ntdb: %s",
|
||||
i, ntdb_errorstr(ecode));
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
if (do_stats)
|
||||
dump_and_clear_stats(&tdb, flags, &log);
|
||||
dump_and_clear_stats(&ntdb, flags, &log);
|
||||
|
||||
if (++stage == stopat)
|
||||
exit(0);
|
||||
|
||||
/* Finding 1000 records. */
|
||||
printf("Finding %u records: ", num); fflush(stdout);
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
gettimeofday(&start, NULL);
|
||||
for (i = 0; i < num; i++) {
|
||||
struct tdb_data dbuf;
|
||||
if ((ecode = tdb_fetch(tdb, key, &dbuf)) != TDB_SUCCESS
|
||||
NTDB_DATA dbuf;
|
||||
if ((ecode = ntdb_fetch(ntdb, key, &dbuf)) != NTDB_SUCCESS
|
||||
|| *(int *)dbuf.dptr != i) {
|
||||
errx(1, "Fetching key %u in tdb gave %u",
|
||||
errx(1, "Fetching key %u in ntdb gave %u",
|
||||
i, ecode ? ecode : *(int *)dbuf.dptr);
|
||||
}
|
||||
}
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
if (do_stats)
|
||||
dump_and_clear_stats(&tdb, flags, &log);
|
||||
dump_and_clear_stats(&ntdb, flags, &log);
|
||||
if (++stage == stopat)
|
||||
exit(0);
|
||||
|
||||
/* Missing 1000 records. */
|
||||
printf("Missing %u records: ", num); fflush(stdout);
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
gettimeofday(&start, NULL);
|
||||
for (i = num; i < num*2; i++) {
|
||||
struct tdb_data dbuf;
|
||||
ecode = tdb_fetch(tdb, key, &dbuf);
|
||||
if (ecode != TDB_ERR_NOEXIST)
|
||||
errx(1, "Fetching key %u in tdb gave %s",
|
||||
i, tdb_errorstr(ecode));
|
||||
NTDB_DATA dbuf;
|
||||
ecode = ntdb_fetch(ntdb, key, &dbuf);
|
||||
if (ecode != NTDB_ERR_NOEXIST)
|
||||
errx(1, "Fetching key %u in ntdb gave %s",
|
||||
i, ntdb_errorstr(ecode));
|
||||
}
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
if (do_stats)
|
||||
dump_and_clear_stats(&tdb, flags, &log);
|
||||
dump_and_clear_stats(&ntdb, flags, &log);
|
||||
if (++stage == stopat)
|
||||
exit(0);
|
||||
|
||||
/* Traverse 1000 records. */
|
||||
printf("Traversing %u records: ", num); fflush(stdout);
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
i = 0;
|
||||
gettimeofday(&start, NULL);
|
||||
if (tdb_traverse(tdb, count_record, &i) != num)
|
||||
if (ntdb_traverse(ntdb, count_record, &i) != num)
|
||||
errx(1, "Traverse returned wrong number of records");
|
||||
if (i != (num - 1) * (num / 2))
|
||||
errx(1, "Traverse tallied to %u", i);
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
if (do_stats)
|
||||
dump_and_clear_stats(&tdb, flags, &log);
|
||||
dump_and_clear_stats(&ntdb, flags, &log);
|
||||
if (++stage == stopat)
|
||||
exit(0);
|
||||
|
||||
/* Delete 1000 records (not in order). */
|
||||
printf("Deleting %u records: ", num); fflush(stdout);
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
gettimeofday(&start, NULL);
|
||||
for (j = 0; j < num; j++) {
|
||||
i = (j + 100003) % num;
|
||||
if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
|
||||
errx(1, "Deleting key %u in tdb: %s",
|
||||
i, tdb_errorstr(ecode));
|
||||
if ((ecode = ntdb_delete(ntdb, key)) != NTDB_SUCCESS)
|
||||
errx(1, "Deleting key %u in ntdb: %s",
|
||||
i, ntdb_errorstr(ecode));
|
||||
}
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
if (do_stats)
|
||||
dump_and_clear_stats(&tdb, flags, &log);
|
||||
dump_and_clear_stats(&ntdb, flags, &log);
|
||||
if (++stage == stopat)
|
||||
exit(0);
|
||||
|
||||
/* Re-add 1000 records (not in order). */
|
||||
printf("Re-adding %u records: ", num); fflush(stdout);
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
gettimeofday(&start, NULL);
|
||||
for (j = 0; j < num; j++) {
|
||||
i = (j + 100003) % num;
|
||||
if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
|
||||
errx(1, "Inserting key %u in tdb: %s",
|
||||
i, tdb_errorstr(ecode));
|
||||
if ((ecode = ntdb_store(ntdb, key, data, NTDB_INSERT)) != 0)
|
||||
errx(1, "Inserting key %u in ntdb: %s",
|
||||
i, ntdb_errorstr(ecode));
|
||||
}
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
if (do_stats)
|
||||
dump_and_clear_stats(&tdb, flags, &log);
|
||||
dump_and_clear_stats(&ntdb, flags, &log);
|
||||
if (++stage == stopat)
|
||||
exit(0);
|
||||
|
||||
/* Append 1000 records. */
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
printf("Appending %u records: ", num); fflush(stdout);
|
||||
gettimeofday(&start, NULL);
|
||||
for (i = 0; i < num; i++)
|
||||
if ((ecode = tdb_append(tdb, key, data)) != TDB_SUCCESS)
|
||||
errx(1, "Appending key %u in tdb: %s",
|
||||
i, tdb_errorstr(ecode));
|
||||
if ((ecode = ntdb_append(ntdb, key, data)) != NTDB_SUCCESS)
|
||||
errx(1, "Appending key %u in ntdb: %s",
|
||||
i, ntdb_errorstr(ecode));
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
@ -406,36 +406,36 @@ int main(int argc, char *argv[])
|
||||
exit(0);
|
||||
|
||||
/* Churn 1000 records: not in order! */
|
||||
if (transaction && (ecode = tdb_transaction_start(tdb)))
|
||||
errx(1, "starting transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_start(ntdb)))
|
||||
errx(1, "starting transaction: %s", ntdb_errorstr(ecode));
|
||||
printf("Churning %u records: ", num); fflush(stdout);
|
||||
gettimeofday(&start, NULL);
|
||||
for (j = 0; j < num; j++) {
|
||||
i = (j + 1000019) % num;
|
||||
if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
|
||||
errx(1, "Deleting key %u in tdb: %s",
|
||||
i, tdb_errorstr(ecode));
|
||||
if ((ecode = ntdb_delete(ntdb, key)) != NTDB_SUCCESS)
|
||||
errx(1, "Deleting key %u in ntdb: %s",
|
||||
i, ntdb_errorstr(ecode));
|
||||
i += num;
|
||||
if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
|
||||
errx(1, "Inserting key %u in tdb: %s",
|
||||
i, tdb_errorstr(ecode));
|
||||
if ((ecode = ntdb_store(ntdb, key, data, NTDB_INSERT)) != 0)
|
||||
errx(1, "Inserting key %u in ntdb: %s",
|
||||
i, ntdb_errorstr(ecode));
|
||||
}
|
||||
gettimeofday(&stop, NULL);
|
||||
if (transaction && (ecode = tdb_transaction_commit(tdb)))
|
||||
errx(1, "committing transaction: %s", tdb_errorstr(ecode));
|
||||
if (transaction && (ecode = ntdb_transaction_commit(ntdb)))
|
||||
errx(1, "committing transaction: %s", ntdb_errorstr(ecode));
|
||||
printf(" %zu ns (%zu bytes)\n",
|
||||
normalize(&start, &stop, num), file_size());
|
||||
|
||||
if (tdb_check(tdb, NULL, NULL))
|
||||
errx(1, "tdb_check failed!");
|
||||
if (ntdb_check(ntdb, NULL, NULL))
|
||||
errx(1, "ntdb_check failed!");
|
||||
if (summary) {
|
||||
char *sumstr = NULL;
|
||||
tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &sumstr);
|
||||
printf("%s\n", sumstr);
|
||||
free(sumstr);
|
||||
}
|
||||
if (do_stats)
|
||||
dump_and_clear_stats(&tdb, flags, &log);
|
||||
dump_and_clear_stats(&ntdb, flags, &log);
|
||||
if (++stage == stopat)
|
||||
exit(0);
|
||||
|
1322
lib/ntdb/transaction.c
Normal file
1322
lib/ntdb/transaction.c
Normal file
File diff suppressed because it is too large
Load Diff
99
lib/ntdb/traverse.c
Normal file
99
lib/ntdb/traverse.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Trivial Database 2: traverse function.
|
||||
Copyright (C) Rusty Russell 2010
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "private.h"
|
||||
#include <ccan/likely/likely.h>
|
||||
|
||||
_PUBLIC_ int64_t ntdb_traverse_(struct ntdb_context *ntdb,
|
||||
int (*fn)(struct ntdb_context *,
|
||||
NTDB_DATA, NTDB_DATA, void *),
|
||||
void *p)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
struct traverse_info tinfo;
|
||||
NTDB_DATA k, d;
|
||||
int64_t count = 0;
|
||||
|
||||
k.dptr = NULL;
|
||||
for (ecode = first_in_hash(ntdb, &tinfo, &k, &d.dsize);
|
||||
ecode == NTDB_SUCCESS;
|
||||
ecode = next_in_hash(ntdb, &tinfo, &k, &d.dsize)) {
|
||||
d.dptr = k.dptr + k.dsize;
|
||||
|
||||
count++;
|
||||
if (fn && fn(ntdb, k, d, p)) {
|
||||
free(k.dptr);
|
||||
ntdb->last_error = NTDB_SUCCESS;
|
||||
return count;
|
||||
}
|
||||
free(k.dptr);
|
||||
}
|
||||
|
||||
if (ecode != NTDB_ERR_NOEXIST) {
|
||||
return NTDB_ERR_TO_OFF(ntdb->last_error = ecode);
|
||||
}
|
||||
ntdb->last_error = NTDB_SUCCESS;
|
||||
return count;
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_firstkey(struct ntdb_context *ntdb, NTDB_DATA *key)
|
||||
{
|
||||
struct traverse_info tinfo;
|
||||
|
||||
return ntdb->last_error = first_in_hash(ntdb, &tinfo, key, NULL);
|
||||
}
|
||||
|
||||
/* We lock twice, not very efficient. We could keep last key & tinfo cached. */
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_nextkey(struct ntdb_context *ntdb, NTDB_DATA *key)
|
||||
{
|
||||
struct traverse_info tinfo;
|
||||
struct hash_info h;
|
||||
struct ntdb_used_record rec;
|
||||
|
||||
tinfo.prev = find_and_lock(ntdb, *key, F_RDLCK, &h, &rec, &tinfo);
|
||||
free(key->dptr);
|
||||
if (NTDB_OFF_IS_ERR(tinfo.prev)) {
|
||||
return ntdb->last_error = NTDB_OFF_TO_ERR(tinfo.prev);
|
||||
}
|
||||
ntdb_unlock_hashes(ntdb, h.hlock_start, h.hlock_range, F_RDLCK);
|
||||
|
||||
return ntdb->last_error = next_in_hash(ntdb, &tinfo, key, NULL);
|
||||
}
|
||||
|
||||
static int wipe_one(struct ntdb_context *ntdb,
|
||||
NTDB_DATA key, NTDB_DATA data, enum NTDB_ERROR *ecode)
|
||||
{
|
||||
*ecode = ntdb_delete(ntdb, key);
|
||||
return (*ecode != NTDB_SUCCESS);
|
||||
}
|
||||
|
||||
_PUBLIC_ enum NTDB_ERROR ntdb_wipe_all(struct ntdb_context *ntdb)
|
||||
{
|
||||
enum NTDB_ERROR ecode;
|
||||
int64_t count;
|
||||
|
||||
ecode = ntdb_allrecord_lock(ntdb, F_WRLCK, NTDB_LOCK_WAIT, false);
|
||||
if (ecode != NTDB_SUCCESS)
|
||||
return ntdb->last_error = ecode;
|
||||
|
||||
/* FIXME: Be smarter. */
|
||||
count = ntdb_traverse(ntdb, wipe_one, &ecode);
|
||||
if (count < 0)
|
||||
ecode = NTDB_OFF_TO_ERR(count);
|
||||
ntdb_allrecord_unlock(ntdb, F_WRLCK);
|
||||
return ntdb->last_error = ecode;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user