0.18 round 2: adopt DecryptingSectorSource decorator at sweep + patch + DiscStream
This commit is contained in:
+35
-35
@@ -1372,6 +1372,7 @@ impl Disc {
|
|||||||
opts: &SweepOptions,
|
opts: &SweepOptions,
|
||||||
) -> Result<CopyResult> {
|
) -> Result<CopyResult> {
|
||||||
use crate::io::{DEFAULT_PIPELINE_DEPTH, Pipeline};
|
use crate::io::{DEFAULT_PIPELINE_DEPTH, Pipeline};
|
||||||
|
use crate::sector::{DecryptingSectorSource, SectorSource};
|
||||||
use sweep::{ProgressSnapshot, SweepSink, WorkItem, try_recv_progress};
|
use sweep::{ProgressSnapshot, SweepSink, WorkItem, try_recv_progress};
|
||||||
|
|
||||||
let total_bytes = self.capacity_sectors as u64 * 2048;
|
let total_bytes = self.capacity_sectors as u64 * 2048;
|
||||||
@@ -1381,6 +1382,15 @@ impl Disc {
|
|||||||
crate::decrypt::DecryptKeys::None
|
crate::decrypt::DecryptKeys::None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wrap the producer-side reader once so every read_sectors call
|
||||||
|
// yields plaintext. `DecryptKeys::None` makes the decorator a
|
||||||
|
// pass-through, so the wrapping is cheap when --raw / unencrypted
|
||||||
|
// discs are being swept and we keep the pipeline shape uniform.
|
||||||
|
// Replaces the inline `decrypt::decrypt_sectors` calls that used
|
||||||
|
// to live in this loop and in the bisect inner loop below.
|
||||||
|
let mut reader = DecryptingSectorSource::new(reader, keys);
|
||||||
|
let reader = &mut reader;
|
||||||
|
|
||||||
// Mapfile: load if resuming, else wipe + recreate.
|
// Mapfile: load if resuming, else wipe + recreate.
|
||||||
let mapfile_path = self.mapfile_for(path);
|
let mapfile_path = self.mapfile_for(path);
|
||||||
if !opts.resume {
|
if !opts.resume {
|
||||||
@@ -1528,14 +1538,10 @@ impl Disc {
|
|||||||
}
|
}
|
||||||
read_ctx.bridge_degradation_count = 0;
|
read_ctx.bridge_degradation_count = 0;
|
||||||
|
|
||||||
// Decrypt on producer; consumer expects plaintext.
|
// Plaintext: the wrapped reader (DecryptingSectorSource)
|
||||||
if opts.decrypt {
|
// applied AACS / CSS in-place during read_sectors above.
|
||||||
crate::decrypt::decrypt_sectors(
|
// The consumer thread sees decrypted bytes; the
|
||||||
&mut buf[..block_bytes as usize],
|
// pre-0.18 inline decrypt_sectors call lived here.
|
||||||
&keys,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the batch into the channel via fresh
|
// Move the batch into the channel via fresh
|
||||||
// owned Vec. The producer's `buf` is reused
|
// owned Vec. The producer's `buf` is reused
|
||||||
@@ -1591,16 +1597,9 @@ impl Disc {
|
|||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
read_ctx.on_success();
|
read_ctx.on_success();
|
||||||
// Decrypt single sector before send. Pre-split
|
// Plaintext via the wrapping
|
||||||
// bisect path silently skipped this — encrypted
|
// DecryptingSectorSource — same
|
||||||
// bytes were written for bisect-recovered sectors.
|
// decrypt path the batch read takes.
|
||||||
if opts.decrypt {
|
|
||||||
crate::decrypt::decrypt_sectors(
|
|
||||||
&mut sector_buf,
|
|
||||||
&keys,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
if pipe
|
if pipe
|
||||||
.send(WorkItem::BisectGood {
|
.send(WorkItem::BisectGood {
|
||||||
pos: write_pos,
|
pos: write_pos,
|
||||||
@@ -1959,6 +1958,7 @@ impl Disc {
|
|||||||
opts: &PatchOpts,
|
opts: &PatchOpts,
|
||||||
) -> Result<PatchOutcome> {
|
) -> Result<PatchOutcome> {
|
||||||
use crate::io::pipeline::{Pipeline, WRITE_THROUGH_DEPTH};
|
use crate::io::pipeline::{Pipeline, WRITE_THROUGH_DEPTH};
|
||||||
|
use crate::sector::{DecryptingSectorSource, SectorSource};
|
||||||
use patch::{PatchItem, PatchSink};
|
use patch::{PatchItem, PatchSink};
|
||||||
|
|
||||||
const BRIDGE_DEGRADATION_PAUSE_SECS: u64 = 10;
|
const BRIDGE_DEGRADATION_PAUSE_SECS: u64 = 10;
|
||||||
@@ -1987,6 +1987,15 @@ impl Disc {
|
|||||||
crate::decrypt::DecryptKeys::None
|
crate::decrypt::DecryptKeys::None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wrap the producer-side reader once so every read_sectors
|
||||||
|
// call (the main recovery read, the backtrack read, and the
|
||||||
|
// non-NOT_READY retry read) yields plaintext. Replaces three
|
||||||
|
// inline decrypt_sectors call sites that all keyed off the
|
||||||
|
// same `keys`. `DecryptKeys::None` keeps the unencrypted /
|
||||||
|
// --raw path a pass-through.
|
||||||
|
let mut reader = DecryptingSectorSource::new(reader, keys);
|
||||||
|
let reader = &mut reader;
|
||||||
|
|
||||||
let is_regular = std::fs::metadata(path)
|
let is_regular = std::fs::metadata(path)
|
||||||
.map(|m| m.file_type().is_file())
|
.map(|m| m.file_type().is_file())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
@@ -2412,9 +2421,9 @@ impl Disc {
|
|||||||
"Read succeeded"
|
"Read succeeded"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if opts.decrypt {
|
// Plaintext: DecryptingSectorSource applied AACS / CSS
|
||||||
crate::decrypt::decrypt_sectors(&mut buf[..bytes], &keys, 0)?;
|
// in-place during the read_sectors call above. The
|
||||||
}
|
// pre-0.18 inline decrypt_sectors call lived here.
|
||||||
let write_start = std::time::Instant::now();
|
let write_start = std::time::Instant::now();
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
target: "freemkv::disc",
|
target: "freemkv::disc",
|
||||||
@@ -2503,13 +2512,9 @@ impl Disc {
|
|||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
blocks_read_ok += 1;
|
blocks_read_ok += 1;
|
||||||
if opts.decrypt {
|
// Plaintext via DecryptingSectorSource
|
||||||
crate::decrypt::decrypt_sectors(
|
// wrapping; same path the main read
|
||||||
&mut buf[..bt_bytes],
|
// takes above.
|
||||||
&keys,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
send_or_abort(
|
send_or_abort(
|
||||||
&pipe,
|
&pipe,
|
||||||
PatchItem::Recovered {
|
PatchItem::Recovered {
|
||||||
@@ -2652,13 +2657,8 @@ impl Disc {
|
|||||||
"Retry succeeded after non-NOT_READY error"
|
"Retry succeeded after non-NOT_READY error"
|
||||||
);
|
);
|
||||||
|
|
||||||
if opts.decrypt {
|
// Plaintext via DecryptingSectorSource;
|
||||||
crate::decrypt::decrypt_sectors(
|
// same path the original read takes.
|
||||||
&mut buf[..bytes],
|
|
||||||
&keys,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
let write_start = std::time::Instant::now();
|
let write_start = std::time::Instant::now();
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
target: "freemkv::disc",
|
target: "freemkv::disc",
|
||||||
|
|||||||
+25
-9
@@ -8,7 +8,7 @@
|
|||||||
use crate::disc::{Disc, DiscTitle, Extent};
|
use crate::disc::{Disc, DiscTitle, Extent};
|
||||||
use crate::drive::extract_scsi_context;
|
use crate::drive::extract_scsi_context;
|
||||||
use crate::event::{BatchSizeReason, Event, EventKind};
|
use crate::event::{BatchSizeReason, Event, EventKind};
|
||||||
use crate::sector::SectorReader;
|
use crate::sector::{DecryptingSectorSource, SectorReader, SectorSource};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
@@ -99,9 +99,19 @@ impl AdaptiveBatch {
|
|||||||
/// Sources: physical drive, ISO file, or any SectorReader.
|
/// Sources: physical drive, ISO file, or any SectorReader.
|
||||||
/// Decrypt, demux, and codec parsing happen internally.
|
/// Decrypt, demux, and codec parsing happen internally.
|
||||||
pub struct DiscStream {
|
pub struct DiscStream {
|
||||||
reader: Box<dyn SectorReader>,
|
/// Underlying sector source wrapped in the 0.18
|
||||||
|
/// [`DecryptingSectorSource`] decorator. Every `read_sectors`
|
||||||
|
/// call yields plaintext, so `fill_extents` no longer needs an
|
||||||
|
/// inline `decrypt::decrypt_sectors` step. `DecryptKeys::None`
|
||||||
|
/// (raw / unencrypted disc) makes the decorator a pass-through.
|
||||||
|
reader: DecryptingSectorSource<Box<dyn SectorReader>>,
|
||||||
title: DiscTitle,
|
title: DiscTitle,
|
||||||
disc: Option<Disc>,
|
disc: Option<Disc>,
|
||||||
|
/// Mirror of the keys handed in at construction. The decorator
|
||||||
|
/// owns the cryptographic state; this field is kept for
|
||||||
|
/// metadata-side callers (`info()` and friends) that want to
|
||||||
|
/// know whether the disc was encrypted, without reaching through
|
||||||
|
/// the wrapper.
|
||||||
decrypt_keys: crate::decrypt::DecryptKeys,
|
decrypt_keys: crate::decrypt::DecryptKeys,
|
||||||
|
|
||||||
// Extents to read
|
// Extents to read
|
||||||
@@ -189,7 +199,11 @@ impl DiscStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
reader,
|
// Wrap the input reader in DecryptingSectorSource so the
|
||||||
|
// internal fill_extents path sees plaintext bytes. For
|
||||||
|
// DecryptKeys::None (unencrypted / raw / test fixtures)
|
||||||
|
// the decorator is a pass-through.
|
||||||
|
reader: DecryptingSectorSource::new(reader, decrypt_keys.clone()),
|
||||||
title,
|
title,
|
||||||
disc: None,
|
disc: None,
|
||||||
decrypt_keys,
|
decrypt_keys,
|
||||||
@@ -241,9 +255,12 @@ impl DiscStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Skip decryption — return raw encrypted bytes.
|
/// Skip decryption — return raw encrypted bytes. Updates both
|
||||||
|
/// the metadata-side key field and the wrapped reader's keys so
|
||||||
|
/// subsequent `read_sectors` calls become a pass-through.
|
||||||
pub fn set_raw(&mut self) {
|
pub fn set_raw(&mut self) {
|
||||||
self.decrypt_keys = crate::decrypt::DecryptKeys::None;
|
self.decrypt_keys = crate::decrypt::DecryptKeys::None;
|
||||||
|
self.reader.set_keys(crate::decrypt::DecryptKeys::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the scanned Disc (for listing all titles).
|
/// Get the scanned Disc (for listing all titles).
|
||||||
@@ -420,11 +437,10 @@ impl crate::pes::Stream for DiscStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let bytes = self.buf_valid;
|
let bytes = self.buf_valid;
|
||||||
if let Err(e) =
|
// Plaintext: the wrapped reader (DecryptingSectorSource)
|
||||||
crate::decrypt::decrypt_sectors(&mut self.read_buf[..bytes], &self.decrypt_keys, 0)
|
// applied AACS / CSS in-place during fill_extents'
|
||||||
{
|
// read_sectors call. The pre-0.18 inline decrypt step
|
||||||
return Err(e.into());
|
// lived here.
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref mut demuxer) = self.ts_demuxer {
|
if let Some(ref mut demuxer) = self.ts_demuxer {
|
||||||
let packets = demuxer.feed(&self.read_buf[..bytes]);
|
let packets = demuxer.feed(&self.read_buf[..bytes]);
|
||||||
|
|||||||
@@ -53,6 +53,16 @@ impl<S: SectorSource> DecryptingSectorSource<S> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace the configured keys without unwrapping the decorator.
|
||||||
|
/// Used by `DiscStream::set_raw()` to flip from encrypted-disc
|
||||||
|
/// decryption to a pass-through after the inner reader is already
|
||||||
|
/// owned by the wrapper. For new construction prefer [`new`].
|
||||||
|
///
|
||||||
|
/// [`new`]: Self::new
|
||||||
|
pub fn set_keys(&mut self, keys: DecryptKeys) {
|
||||||
|
self.keys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
/// Borrow the inner source. Useful for tests and for adapters
|
/// Borrow the inner source. Useful for tests and for adapters
|
||||||
/// that want to introspect the underlying drive / file without
|
/// that want to introspect the underlying drive / file without
|
||||||
/// unwrapping the decorator.
|
/// unwrapping the decorator.
|
||||||
|
|||||||
@@ -144,6 +144,54 @@ impl<T: SectorReader + ?Sized> SectorSource for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forwarding impls so callers can wrap `&mut dyn SectorReader` /
|
||||||
|
// `Box<dyn SectorReader>` in [`DecryptingSectorSource`] without
|
||||||
|
// having to unbox or re-borrow inside the lib's hot paths. The
|
||||||
|
// generic `&mut T` / `Box<T>` blankets would conflict with the
|
||||||
|
// `SectorReader → SectorSource` blanket above (a downstream crate
|
||||||
|
// could `impl SectorReader for &mut U`); the specific
|
||||||
|
// `dyn SectorReader` instantiations are unambiguous because
|
||||||
|
// `SectorReader` is the very trait whose `dyn` we're targeting.
|
||||||
|
impl SectorSource for &mut (dyn SectorReader + '_) {
|
||||||
|
fn capacity_sectors(&self) -> u32 {
|
||||||
|
<dyn SectorReader as SectorReader>::capacity(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_sectors(
|
||||||
|
&mut self,
|
||||||
|
lba: u32,
|
||||||
|
count: u16,
|
||||||
|
buf: &mut [u8],
|
||||||
|
recovery: bool,
|
||||||
|
) -> Result<usize> {
|
||||||
|
<dyn SectorReader as SectorReader>::read_sectors(*self, lba, count, buf, recovery)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_speed(&mut self, kbs: u16) {
|
||||||
|
<dyn SectorReader as SectorReader>::set_speed(*self, kbs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SectorSource for Box<dyn SectorReader> {
|
||||||
|
fn capacity_sectors(&self) -> u32 {
|
||||||
|
<dyn SectorReader as SectorReader>::capacity(&**self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_sectors(
|
||||||
|
&mut self,
|
||||||
|
lba: u32,
|
||||||
|
count: u16,
|
||||||
|
buf: &mut [u8],
|
||||||
|
recovery: bool,
|
||||||
|
) -> Result<usize> {
|
||||||
|
<dyn SectorReader as SectorReader>::read_sectors(&mut **self, lba, count, buf, recovery)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_speed(&mut self, kbs: u16) {
|
||||||
|
<dyn SectorReader as SectorReader>::set_speed(&mut **self, kbs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub use decrypting::DecryptingSectorSource;
|
pub use decrypting::DecryptingSectorSource;
|
||||||
pub use file::{FileSectorSink, FileSectorSource};
|
pub use file::{FileSectorSink, FileSectorSource};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user