- } pad, *p;
- p = tdb_get(tdb, off, &pad, sizeof(pad));
- if (!p)
- goto fail;
- if (p->f.magic == TDB_FREE_MAGIC) {
- /* This record is free! */
- if (!append(&free, &num_free, off))
- goto fail;
- len = sizeof(p->u) + p->f.data_len;
- if (tdb->methods->oob(tdb, off + len, false))
- goto fail;
+ struct tdb_recovery_record r;
+ } rec;
+ /* r is larger: only get that if we need to. */
+ if (tdb_read_convert(tdb, off, &rec, sizeof(rec.f)) == -1)
+ return false;
+
+ /* If we crash after ftruncate, we can get zeroes or fill. */
+ if (rec.r.magic == TDB_RECOVERY_INVALID_MAGIC
+ || rec.r.magic == 0x4343434343434343ULL) {
+ if (tdb_read_convert(tdb, off, &rec, sizeof(rec.r)))
+ return false;
+
+ if (recovery == off) {
+ found_recovery = true;
+ len = sizeof(rec.r) + rec.r.max_len;
+ } else {
+ len = dead_space(tdb, off);
+ if (len < sizeof(rec.r)) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT,
+ TDB_DEBUG_ERROR,
+ "tdb_check: invalid dead"
+ " space at %zu",
+ (size_t)off);
+ return false;
+ }
+
+ tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_WARNING,
+ "Dead space at %zu-%zu (of %zu)",
+ (size_t)off, (size_t)(off + len),
+ (size_t)tdb->map_size);
+ }
+ } else if (rec.r.magic == TDB_RECOVERY_MAGIC) {
+ if (tdb_read_convert(tdb, off, &rec, sizeof(rec.r)))
+ return false;
+ if (recovery != off) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT,
+ TDB_DEBUG_ERROR,
+ "tdb_check: unexpected recovery"
+ " record at offset %zu",
+ (size_t)off);
+ return false;
+ }
+ if (rec.r.len > rec.r.max_len) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT,
+ TDB_DEBUG_ERROR,
+ "tdb_check: invalid recovery length"
+ " %zu", (size_t)rec.r.len);
+ return false;
+ }
+ if (rec.r.eof > tdb->map_size) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT,
+ TDB_DEBUG_ERROR,
+ "tdb_check: invalid old EOF"
+ " %zu", (size_t)rec.r.eof);
+ return false;
+ }
+ found_recovery = true;
+ len = sizeof(rec.r) + rec.r.max_len;
+ } else if (frec_magic(&rec.f) == TDB_FREE_MAGIC) {
+ len = sizeof(rec.u) + frec_len(&rec.f);
+ if (off + len > tdb->map_size) {
+ tdb_logerr(tdb, TDB_ERR_CORRUPT,
+ TDB_DEBUG_ERROR,
+ "tdb_check: free overlength %llu"
+ " at offset %llu",
+ (long long)len, (long long)off);
+ return false;
+ }
+ /* This record should be in free lists. */
+ if (frec_flist(&rec.f) != TDB_FLIST_NONE
+ && !append(free, num_free, off))
+ return false;