unsigned hprefix_bits,
tdb_off_t used[],
size_t num_used,
- size_t *num_found);
+ size_t *num_found,
+ int (*check)(TDB_DATA, TDB_DATA, void *),
+ void *private_data);
static bool check_hash_record(struct tdb_context *tdb,
tdb_off_t off,
unsigned hprefix_bits,
tdb_off_t used[],
size_t num_used,
- size_t *num_found)
+ size_t *num_found,
+ int (*check)(TDB_DATA, TDB_DATA, void *),
+ void *private_data)
{
struct tdb_used_record rec;
return check_hash_tree(tdb, off,
TDB_SUBLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
hprefix, hprefix_bits,
- used, num_used, num_found);
+ used, num_used, num_found, check, private_data);
}
static int off_cmp(const tdb_off_t *a, const tdb_off_t *b)
unsigned hprefix_bits,
tdb_off_t used[],
size_t num_used,
- size_t *num_found)
+ size_t *num_found,
+ int (*check)(TDB_DATA, TDB_DATA, void *),
+ void *private_data)
{
unsigned int g, b;
const tdb_off_t *hash;
hprefix_bits
+ group_bits
+ TDB_HASH_GROUP_BITS,
- used, num_used, num_found))
+ used, num_used, num_found,
+ check, private_data))
goto fail;
continue;
}
(long long)rec_hash(&rec));
goto fail;
}
+
+ if (check) {
+ TDB_DATA key, data;
+ key.dsize = rec_key_length(&rec);
+ data.dsize = rec_data_length(&rec);
+ key.dptr = (void *)tdb_access_read(tdb,
+ off + sizeof(rec),
+ key.dsize + data.dsize,
+ false);
+ if (!key.dptr)
+ goto fail;
+ data.dptr = key.dptr + key.dsize;
+ if (check(key, data, private_data) != 0)
+ goto fail;
+ tdb_access_release(tdb, key.dptr);
+ }
}
}
tdb_access_release(tdb, hash);
static bool check_hash(struct tdb_context *tdb,
tdb_off_t used[],
- size_t num_used, size_t num_flists)
+ size_t num_used, size_t num_flists,
+ int (*check)(TDB_DATA, TDB_DATA, void *),
+ void *private_data)
{
/* Free lists also show up as used. */
size_t num_found = num_flists;
if (!check_hash_tree(tdb, offsetof(struct tdb_header, hashtable),
TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
- 0, 0, used, num_used, &num_found))
+ 0, 0, used, num_used, &num_found,
+ check, private_data))
return false;
if (num_found != num_used) {
static bool check_free(struct tdb_context *tdb,
tdb_off_t off,
const struct tdb_free_record *frec,
- tdb_off_t prev, tdb_off_t flist_off, unsigned int bucket)
+ tdb_off_t prev, unsigned int flist, unsigned int bucket)
{
if (frec_magic(frec) != TDB_FREE_MAGIC) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: offset %llu bad magic 0x%llx\n",
- (long long)off, (long long)frec->magic_and_meta);
+ (long long)off, (long long)frec->magic_and_prev);
return false;
}
- if (frec_flist(frec) != flist_off) {
+ if (frec_flist(frec) != flist) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
- "tdb_check: offset %llu bad freelist 0x%llx\n",
- (long long)off, (long long)frec_flist(frec));
+ "tdb_check: offset %llu bad freelist %u\n",
+ (long long)off, frec_flist(frec));
return false;
}
if (tdb->methods->oob(tdb, off
- + frec->data_len+sizeof(struct tdb_used_record),
+ + frec_len(frec) + sizeof(struct tdb_used_record),
false))
return false;
- if (size_to_bucket(frec->data_len) != bucket) {
+ if (size_to_bucket(frec_len(frec)) != bucket) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: offset %llu in wrong bucket %u vs %u\n",
(long long)off,
- bucket, size_to_bucket(frec->data_len));
+ bucket, size_to_bucket(frec_len(frec)));
return false;
}
- if (prev != frec->prev) {
+ if (prev != frec_prev(frec)) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: offset %llu bad prev %llu vs %llu\n",
(long long)off,
- (long long)prev, (long long)frec->prev);
+ (long long)prev, (long long)frec_len(frec));
return false;
}
return true;
static bool check_free_list(struct tdb_context *tdb,
tdb_off_t flist_off,
+ unsigned flist_num,
tdb_off_t free[],
size_t num_free,
size_t *num_found)
return false;
if (tdb_read_convert(tdb, off, &f, sizeof(f)))
return false;
- if (!check_free(tdb, off, &f, prev, flist_off, i))
+ if (!check_free(tdb, off, &f, prev, flist_num, i))
return false;
/* FIXME: Check hash bits */
}
/* Slow, but should be very rare. */
-static size_t dead_space(struct tdb_context *tdb, tdb_off_t off)
+size_t dead_space(struct tdb_context *tdb, tdb_off_t off)
{
size_t len;
struct tdb_free_record f;
struct tdb_recovery_record r;
} pad, *p;
- p = tdb_get(tdb, off, &pad, sizeof(pad));
+ /* r is larger: only get that if we need to. */
+ p = tdb_get(tdb, off, &pad, sizeof(pad.f));
if (!p)
return false;
/* If we crash after ftruncate, we can get zeroes or fill. */
if (p->r.magic == TDB_RECOVERY_INVALID_MAGIC
|| p->r.magic == 0x4343434343434343ULL) {
+ p = tdb_get(tdb, off, &pad, sizeof(pad.r));
+ if (!p)
+ return false;
if (recovery == off) {
found_recovery = true;
len = sizeof(p->r) + p->r.max_len;
(size_t)tdb->map_size);
}
} else if (p->r.magic == TDB_RECOVERY_MAGIC) {
+ p = tdb_get(tdb, off, &pad, sizeof(pad.r));
+ if (!p)
+ return false;
if (recovery != off) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: unexpected recovery"
(size_t)off);
return false;
}
+ if (p->r.len > p->r.max_len) {
+ tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
+ "tdb_check: invalid recovery length"
+ " %zu\n", (size_t)p->r.len);
+ return false;
+ }
+ if (p->r.eof > tdb->map_size) {
+ tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
+ "tdb_check: invalid old EOF"
+ " %zu\n", (size_t)p->r.eof);
+ return false;
+ }
found_recovery = true;
len = sizeof(p->r) + p->r.max_len;
} else if (frec_magic(&p->f) == TDB_FREE_MAGIC
|| frec_magic(&p->f) == TDB_COALESCING_MAGIC) {
- len = sizeof(p->u) + p->f.data_len;
+ len = sizeof(p->u) + frec_len(&p->f);
if (off + len > tdb->map_size) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: free overlength %llu"
return true;
}
-/* FIXME: call check() function. */
int tdb_check(struct tdb_context *tdb,
int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
void *private_data)
for (flist = first_flist(tdb); flist; flist = next_flist(tdb, flist)) {
if (flist == TDB_OFF_ERR)
goto fail;
- if (!check_free_list(tdb, flist, free, num_free, &num_found))
+ if (!check_free_list(tdb, flist, num_flists, free, num_free,
+ &num_found))
goto fail;
num_flists++;
}
/* FIXME: Check key uniqueness? */
- if (!check_hash(tdb, used, num_used, num_flists))
+ if (!check_hash(tdb, used, num_used, num_flists, check, private_data))
goto fail;
if (num_found != num_free) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: Not all entries are in free table\n");
- return false;
+ return -1;
}
tdb_allrecord_unlock(tdb, F_RDLCK);