Audit v3 fixes: all 3 tiers (19 findings)

Tier 1 (compilation + correctness):
- Fix nightly-only is_multiple_of → % 2 != 0 (stable Rust compat)
- Fix parse_sample_rate: check 192 before 96 (was returning wrong rate)
- macOS drive discovery: split unix.rs → linux.rs + macos.rs
- Linux: EACCES returns DevicePermission not DeviceNotFound
- CLI pipe.rs: Ctrl+C signal handler added

Tier 2 (correctness + security):
- MkvStream: reset demuxer after scanning→streaming transition
- Windows SPTI: zero data buffer before ioctl
- AACS cert verification: documented why silently skipped
- KEYDB: HOME + USERPROFILE fallback for Windows
- Library modules: pub(crate) for internal modules
- AACS: explicit re-exports, AES primitives pub(crate)

Tier 3 (performance + polish):
- IsoStream: batch 64-sector reads (was 1 sector at a time)
- DiscStream: buffer swap instead of copy in decrypt_and_buffer
- Vec capacity hints in TS/PS demuxer hot paths
- NetworkStream: TLS warning documented
- Batch rip: per-title progress display
- cargo fmt: 0 violations

319 tests, 0 fmt violations.
This commit is contained in:
MattJackson
2026-04-11 19:24:25 +00:00
parent 43f81e4eae
commit a74d395f68
22 changed files with 174 additions and 73 deletions
+9 -13
View File
@@ -9,9 +9,8 @@
use super::IOStream;
use crate::disc::{
ContentFormat, Disc, DiscTitle, Extent,
MIN_BATCH_SECTORS, RAMP_BATCH_AFTER, RAMP_SPEED_AFTER,
SLOW_SPEED_AFTER, detect_max_batch_sectors,
detect_max_batch_sectors, ContentFormat, Disc, DiscTitle, Extent, MIN_BATCH_SECTORS,
RAMP_BATCH_AFTER, RAMP_SPEED_AFTER, SLOW_SPEED_AFTER,
};
use crate::drive::DriveSession;
use crate::error::Error;
@@ -187,8 +186,7 @@ impl DiscStream {
if self.batch_sectors < self.max_batch_sectors
&& self.ok_streak >= RAMP_BATCH_AFTER
{
self.batch_sectors =
(self.batch_sectors * 2).min(self.max_batch_sectors);
self.batch_sectors = (self.batch_sectors * 2).min(self.max_batch_sectors);
self.ok_streak = 0;
}
@@ -220,14 +218,12 @@ impl DiscStream {
}
if self.batch_sectors > MIN_BATCH_SECTORS {
self.batch_sectors =
(self.batch_sectors / 2).max(MIN_BATCH_SECTORS);
self.batch_sectors = (self.batch_sectors / 2).max(MIN_BATCH_SECTORS);
std::thread::sleep(std::time::Duration::from_millis(100));
} else {
// At minimum batch -- retry once with longer pause
std::thread::sleep(std::time::Duration::from_millis(500));
self.read_buf
.resize(MIN_BATCH_SECTORS as usize * 2048, 0);
self.read_buf.resize(MIN_BATCH_SECTORS as usize * 2048, 0);
if self.read_sectors(lba, MIN_BATCH_SECTORS).is_ok() {
self.error_streak = 0;
self.current_offset += MIN_BATCH_SECTORS as u32;
@@ -243,8 +239,7 @@ impl DiscStream {
self.current_extent += 1;
self.current_offset = 0;
}
self.read_buf
.resize(crate::aacs::ALIGNED_UNIT_LEN, 0);
self.read_buf.resize(crate::aacs::ALIGNED_UNIT_LEN, 0);
self.read_buf.fill(0);
return Ok(true);
}
@@ -283,8 +278,9 @@ impl DiscStream {
}
// No encryption: read_buf is already plaintext
self.batch_buf.clear();
self.batch_buf.extend_from_slice(&self.read_buf[..total_bytes]);
// Swap buffers instead of copying — the old batch_buf becomes
// read_buf and will be overwritten on the next read.
std::mem::swap(&mut self.batch_buf, &mut self.read_buf);
self.batch_pos = 0;
}
}