+ size_t *num_found,
+ int (*check)(TDB_DATA, TDB_DATA, void *),
+ void *private_data);
+
+static bool check_hash_chain(struct tdb_context *tdb,
+ tdb_off_t off,
+ uint64_t hash,
+ tdb_off_t used[],
+ size_t num_used,
+ size_t *num_found,
+ int (*check)(TDB_DATA, TDB_DATA, void *),
+ void *private_data)
+{
+ struct tdb_used_record rec;
+
+ if (tdb_read_convert(tdb, off, &rec, sizeof(rec)) == -1)
+ return false;
+
+ if (rec_magic(&rec) != TDB_CHAIN_MAGIC) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
+ "tdb_check: Bad hash chain magic %llu",
+ (long long)rec_magic(&rec));
+ return false;
+ }
+
+ if (rec_data_length(&rec) != sizeof(struct tdb_chain)) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
+ "tdb_check: Bad hash chain length %llu vs %zu",
+ (long long)rec_data_length(&rec),
+ sizeof(struct tdb_chain));
+ return false;
+ }
+ if (rec_key_length(&rec) != 0) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
+ "tdb_check: Bad hash chain key length %llu",
+ (long long)rec_key_length(&rec));
+ return false;
+ }
+ if (rec_hash(&rec) != 0) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
+ "tdb_check: Bad hash chain hash value %llu",
+ (long long)rec_hash(&rec));
+ return false;
+ }
+
+ off += sizeof(rec);
+ if (!check_hash_tree(tdb, off, 0, hash, 64,
+ used, num_used, num_found, check, private_data))
+ return false;
+
+ off = tdb_read_off(tdb, off + offsetof(struct tdb_chain, next));
+ if (off == TDB_OFF_ERR)
+ return false;
+ if (off == 0)
+ return true;
+ (*num_found)++;
+ return check_hash_chain(tdb, off, hash, used, num_used, num_found,
+ check, private_data);
+}