diff --git a/src/resolve.cpp b/src/resolve.cpp index 2cf11c1..4643fba 100644 --- a/src/resolve.cpp +++ b/src/resolve.cpp @@ -27,6 +27,16 @@ std::vector resolve(std::vector candidates, size_t file_size, const std::set& coalesce_types) { std::sort(candidates.begin(), candidates.end(), less); + // A partition table is authoritative about the medium's layout, so a finding + // starting exactly on a partition boundary is a real region, not interior + // noise — even when an earlier finding's self-declared size overruns it + // (see scan.cpp). Otherwise a stale superblock recording a pre-repartition + // size demotes every partition it spans to a footnote on itself. + std::set part_starts; + for (const auto& f : candidates) + for (const auto& m : f.members) + if (m.offset != SIZE_MAX) part_starts.insert(m.offset); + std::vector kept; size_t owner_end = 0; // end of the span owned by the current owner int owner = -1; // index in `kept` of the owning region, or -1 @@ -38,7 +48,7 @@ std::vector resolve(std::vector candidates, size_t file_size, continue; } // Starts inside a region already owned by a confident, sized finding. - if (f.offset < owner_end) { + if (f.offset < owner_end && !part_starts.count(f.offset)) { if (owner >= 0) kept[owner].also_matched.push_back(demote(f)); continue; } diff --git a/src/scan.cpp b/src/scan.cpp index a4b98c2..803aae9 100644 --- a/src/scan.cpp +++ b/src/scan.cpp @@ -125,6 +125,13 @@ std::vector scan(const Reader& r, const std::vector& sigs) { // Scan with skip-ahead: once a confident, sized finding claims a region, // restart the automaton past it so we neither validate nor traverse its // interior (the compressed blocks inside a squashfs, etc.). + // That size is self-declared, though, and can overrun a region the partition + // table already accounts for (a stale superblock in unpartitioned space + // still recording its pre-repartition size). Skipping the whole extent would + // step over those partitions' superblocks without ever validating them, so + // they would be missed entirely rather than demoted. Clamp the jump at the + // next partition start. + std::set part_starts; // absolute partition offsets seen so far std::vector candidates; size_t next_scan = 0; while (next_scan < n) { @@ -138,9 +145,18 @@ std::vector scan(const Reader& r, const std::vector& sigs) { if (!f) return true; const bool owns_region = f->confidence >= static_cast(Confidence::Structural) && f->size > 0; - const size_t end = f->offset + f->size; + const size_t foff = f->offset; + const size_t end = foff + f->size; + for (const auto& m : f->members) + if (m.offset != SIZE_MAX) part_starts.insert(m.offset); candidates.push_back(std::move(*f)); - if (owns_region) { skip_to = end; return false; } + if (owns_region) { + size_t lim = end; + auto it = part_starts.upper_bound(foff); // first boundary after it + if (it != part_starts.end() && *it < lim) lim = *it; + skip_to = lim; // > foff >= next_scan, so the loop still advances + return false; + } return true; }); if (skip_to > next_scan)