1
0
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:
Rusty Russell 2012-06-18 22:30:26 +09:30
parent 76758b9767
commit 16cc345d4f
148 changed files with 13585 additions and 13727 deletions

View 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
View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

View File

@ -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
View 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
View 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
View 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);
}

View File

@ -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;
}

View File

@ -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);

View 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();
}

View 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();
}

View 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();
}

View 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();
}

View 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();
}

View File

@ -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();

View 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();
}

View File

@ -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();

View File

@ -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);

View File

@ -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();

View 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();
}

View File

@ -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);

View 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();
}

View File

@ -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);

View File

@ -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 *)&num;
k.dsize = sizeof(num);
num = 0;
ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
ok1(tdb_firstkey(tdb, &k) == TDB_SUCCESS);
ok1(ntdb_store(ntdb, k, k, NTDB_INSERT) == 0);
ok1(ntdb_firstkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(num));
ok1(memcmp(k.dptr, &num, sizeof(num)) == 0);
ok1(tdb_nextkey(tdb, &k) == TDB_ERR_NOEXIST);
ok1(ntdb_nextkey(ntdb, &k) == NTDB_ERR_NOEXIST);
/* Two entries. */
k.dptr = (unsigned char *)&num;
k.dsize = sizeof(num);
num = 1;
ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
ok1(tdb_firstkey(tdb, &k) == TDB_SUCCESS);
ok1(ntdb_store(ntdb, k, k, NTDB_INSERT) == 0);
ok1(ntdb_firstkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(num));
memcpy(&num, k.dptr, sizeof(num));
ok1(num == 0 || num == 1);
ok1(tdb_nextkey(tdb, &k) == TDB_SUCCESS);
ok1(ntdb_nextkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(j));
memcpy(&j, k.dptr, sizeof(j));
ok1(j == 0 || j == 1);
ok1(j != num);
ok1(tdb_nextkey(tdb, &k) == TDB_ERR_NOEXIST);
ok1(ntdb_nextkey(ntdb, &k) == NTDB_ERR_NOEXIST);
/* Clean up. */
k.dptr = (unsigned char *)&num;
k.dsize = sizeof(num);
num = 0;
ok1(tdb_delete(tdb, k) == 0);
ok1(ntdb_delete(ntdb, k) == 0);
num = 1;
ok1(tdb_delete(tdb, k) == 0);
ok1(ntdb_delete(ntdb, k) == 0);
/* Now lots of records. */
ok1(store_records(tdb));
ok1(store_records(ntdb));
td.calls = 0;
num = tdb_traverse(tdb, trav, &td);
num = ntdb_traverse(ntdb, trav, &td);
ok1(num == NUM_RECORDS);
ok1(td.calls == NUM_RECORDS);
/* Simple loop should match tdb_traverse */
for (j = 0, ecode = tdb_firstkey(tdb, &k); j < td.calls; j++) {
/* Simple loop should match ntdb_traverse */
for (j = 0, ecode = ntdb_firstkey(ntdb, &k); j < td.calls; j++) {
int val;
ok1(ecode == TDB_SUCCESS);
ok1(ecode == NTDB_SUCCESS);
ok1(k.dsize == sizeof(val));
memcpy(&val, k.dptr, k.dsize);
ok1(td.records[j] == val);
ecode = tdb_nextkey(tdb, &k);
ecode = ntdb_nextkey(ntdb, &k);
}
/* But arbitrary orderings should work too. */
@ -132,26 +132,26 @@ int main(int argc, char *argv[])
k.dptr = (unsigned char *)&td.records[j-1];
k.dsize = sizeof(td.records[j-1]);
k = dup_key(k);
ok1(tdb_nextkey(tdb, &k) == TDB_SUCCESS);
ok1(ntdb_nextkey(ntdb, &k) == NTDB_SUCCESS);
ok1(k.dsize == sizeof(td.records[j]));
ok1(memcmp(k.dptr, &td.records[j], k.dsize) == 0);
free(k.dptr);
}
/* Even delete should work. */
for (j = 0, ecode = tdb_firstkey(tdb, &k);
ecode != TDB_ERR_NOEXIST;
for (j = 0, ecode = ntdb_firstkey(ntdb, &k);
ecode != NTDB_ERR_NOEXIST;
j++) {
ok1(ecode == TDB_SUCCESS);
ok1(ecode == NTDB_SUCCESS);
ok1(k.dsize == 4);
ok1(tdb_delete(tdb, k) == 0);
ecode = tdb_nextkey(tdb, &k);
ok1(ntdb_delete(ntdb, k) == 0);
ecode = ntdb_nextkey(ntdb, &k);
}
diag("delete using first/nextkey gave %u of %u records",
j, NUM_RECORDS);
ok1(j == NUM_RECORDS);
tdb_close(tdb);
ntdb_close(ntdb);
}
ok1(tap_log_messages == 0);

View File

@ -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();

View File

@ -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();

View File

@ -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);

View 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();
}

View File

@ -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);

View 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();
}

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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 */

View 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;
}

View 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;
}

View 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);
}

View File

@ -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 */

View File

@ -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)

View File

@ -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
View 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 */

View File

@ -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"

View File

@ -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();

View File

@ -1,4 +1,4 @@
#include "tdb2-source.h"
#include "ntdb-source.h"
#include "tap-interface.h"
static unsigned int dumb_fls(uint64_t num)

View File

@ -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;
}

View 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());
}

View 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();
}

View 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();
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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);

View 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();
}

View 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();
}

View 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();
}

View File

@ -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());
}

View 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();
}

View File

@ -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();

View File

@ -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,

View 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();
}

View 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();
}

View File

@ -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());

View 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();
}

View File

@ -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);

View File

@ -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);

View File

@ -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
View 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();
}

View 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();
}

View File

@ -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);
}

View File

@ -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
View 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

View 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;
}

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

99
lib/ntdb/traverse.c Normal file
View 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