Anchor the folder encryption probe on a unit boundary
probe_folder_encryption sampled AACS units starting from the largest extent anywhere in the disc. AACS units are 3 sectors and a unit boundary is only guaranteed at the START of a clip, so that anchor is only correct when the largest extent happens to be a clip first extent. For any clip over ~2 GiB it is not. The planner caps an allocation descriptor at MAX_AD_BYTES = 524287 sectors, so every full piece of a split file ties on sector_count and max_by_key returns the LAST tie — an extent beginning (k-1)*524287 sectors into the file. 524287 % 3 == 1, so that start misses the unit boundary for two file sizes in three. The 6144-byte sample windows then begin mid-source-packet and the byte read as the CPI flag is content. Both verdicts fail in a costly direction: a decrypted folder is rejected as encrypted (DirImageEncrypted on something perfectly rippable — the exact case scan_dir was added to rescue), or real ciphertext reads as clear and the mux writes it out as video at exit 0. is_unit_aligned cannot catch it: it measures against the same wrong base. Now the largest TITLE first extent, which is unit-aligned by construction and is also the more meaningful sample — the main feature rather than whichever fragment happened to be biggest. The existing tests could not reach this: their fixture m2ts is 786,432 bytes, a single extent, which is always its own first.
This commit is contained in:
+24
-2
@@ -508,11 +508,33 @@ fn probe_folder_encryption(reader: &mut dyn SectorSource, disc: &Disc) -> Result
|
|||||||
use crate::consts::SECTOR_BYTES;
|
use crate::consts::SECTOR_BYTES;
|
||||||
|
|
||||||
const UNIT_SECTORS: u32 = 3;
|
const UNIT_SECTORS: u32 = 3;
|
||||||
|
// Anchor on the largest TITLE's FIRST extent, not on the largest extent
|
||||||
|
// anywhere.
|
||||||
|
//
|
||||||
|
// AACS units are 3 sectors, and a unit boundary is only guaranteed at the
|
||||||
|
// START of a clip. `max_by_key` over every extent picked a mid-file one for
|
||||||
|
// any clip big enough to be split: the planner caps an allocation
|
||||||
|
// descriptor at MAX_AD_BYTES = 524287 sectors, every full piece of a split
|
||||||
|
// file therefore ties on sector_count, and `max_by_key` returns the LAST
|
||||||
|
// tie — an extent starting (k-1)*524287 sectors in. 524287 % 3 == 1, so
|
||||||
|
// that start is off the unit boundary for two file sizes in three.
|
||||||
|
//
|
||||||
|
// The sampling then reads 6144-byte windows that begin mid-source-packet,
|
||||||
|
// so the CPI byte it thinks it is testing is content. Both verdicts are
|
||||||
|
// wrong in a costly direction: a decrypted folder gets rejected as
|
||||||
|
// encrypted (DirImageEncrypted on something perfectly rippable), or
|
||||||
|
// genuine ciphertext reads as clear and the mux writes it out as video at
|
||||||
|
// exit 0. `is_unit_aligned` cannot catch it, because it measures against
|
||||||
|
// this same wrong base.
|
||||||
let Some(extent) = disc
|
let Some(extent) = disc
|
||||||
.titles
|
.titles
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|t| t.extents.iter())
|
.max_by_key(|t| {
|
||||||
.max_by_key(|e| e.sector_count)
|
t.extents
|
||||||
|
.iter()
|
||||||
|
.fold(0u64, |a, e| a.saturating_add(e.sector_count as u64))
|
||||||
|
})
|
||||||
|
.and_then(|t| t.extents.first())
|
||||||
else {
|
else {
|
||||||
// No content to judge. A folder with an AACS directory and no titles
|
// No content to judge. A folder with an AACS directory and no titles
|
||||||
// has nothing to rip either way; leave the structural verdict alone.
|
// has nothing to rip either way; leave the structural verdict alone.
|
||||||
|
|||||||
Reference in New Issue
Block a user