X-Git-Url: https://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb2%2Fcheck.c;h=97347920291a6376f3010ec6b7289f7190607827;hp=54f96bb0ea8283583faf91693decd8d467970423;hb=5e30abc662990449444769c71cf98ca788db4117;hpb=ec88af5dd11a003beb8cd3fbe420f9d5e5dcf5d9;ds=sidebyside diff --git a/ccan/tdb2/check.c b/ccan/tdb2/check.c index 54f96bb0..97347920 100644 --- a/ccan/tdb2/check.c +++ b/ccan/tdb2/check.c @@ -77,7 +77,9 @@ static bool check_hash_tree(struct tdb_context *tdb, 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, @@ -85,7 +87,9 @@ static bool check_hash_record(struct tdb_context *tdb, 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; @@ -117,7 +121,7 @@ static bool check_hash_record(struct tdb_context *tdb, 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) @@ -141,7 +145,9 @@ static bool check_hash_tree(struct tdb_context *tdb, 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; @@ -188,7 +194,8 @@ static bool check_hash_tree(struct tdb_context *tdb, hprefix_bits + group_bits + TDB_HASH_GROUP_BITS, - used, num_used, num_found)) + used, num_used, num_found, + check, private_data)) goto fail; continue; } @@ -256,6 +263,22 @@ static bool check_hash_tree(struct tdb_context *tdb, (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); @@ -268,14 +291,17 @@ fail: 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) { @@ -289,37 +315,37 @@ static bool check_hash(struct tdb_context *tdb, 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; @@ -327,6 +353,7 @@ static bool check_free(struct tdb_context *tdb, 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) @@ -358,7 +385,7 @@ static bool check_free_list(struct tdb_context *tdb, 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 */ @@ -410,13 +437,17 @@ static bool check_linear(struct tdb_context *tdb, 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; @@ -436,6 +467,9 @@ static bool check_linear(struct tdb_context *tdb, (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" @@ -443,11 +477,23 @@ static bool check_linear(struct tdb_context *tdb, (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" @@ -509,7 +555,6 @@ static bool check_linear(struct tdb_context *tdb, 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) @@ -535,19 +580,20 @@ int tdb_check(struct tdb_context *tdb, 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);