Splits the unidirectional read trait from a (planned) write trait at the sector level, eliminating runtime "wrong direction" potential. Keeps SectorReader alive as a pre-deprecation alias via blanket impl so existing callers compile unchanged through the migration window. Adds DecryptingSectorSource decorator: wrap any SectorSource in this to get plaintext sectors out. Replaces the duplicate decrypt code paths in sweep_pipeline and DiscStream (those migrations are follow-up commits). The formal #[deprecated] attribute on SectorReader is held back to a follow-up commit because internal call sites in disc/, udf/, mux/, and verify/ still go through the legacy trait, and the CI gauntlet treats deprecation lints as errors. Behavioural intent — "this trait is going away" — is documented on the trait itself. See freemkv-private/memory/0_18_redesign.md. Single contributor: MattJackson.
155 lines
5.9 KiB
Rust
155 lines
5.9 KiB
Rust
//! Sector-level I/O traits.
|
|
//!
|
|
//! 0.18 splits the unidirectional read trait from a write trait at
|
|
//! the sector layer, so the type system catches "wrong direction"
|
|
//! mistakes at compile time instead of runtime. See
|
|
//! `freemkv-private/memory/0_18_redesign.md`.
|
|
//!
|
|
//! - [`SectorSource`] reads 2048-byte sectors. Implemented by
|
|
//! `Drive` (via the legacy [`SectorReader`] alias) and
|
|
//! [`FileSectorSource`] (ISO-backed).
|
|
//! - [`SectorSink`] writes 2048-byte sectors. Implemented by
|
|
//! [`FileSectorSink`] (ISO-backed) and, in later commits, by
|
|
//! sweep/patch consumer adapters.
|
|
//! - [`DecryptingSectorSource`] is a decorator that wraps any
|
|
//! `SectorSource` and applies the existing AACS / CSS in-place
|
|
//! decrypt to plaintext-out.
|
|
//!
|
|
//! [`SectorReader`] is the 0.17 read trait. It stays on through
|
|
//! the 0.18 migration window so existing call sites
|
|
//! (`Drive`, `IsoSectorReader`, `BufferedSectorReader`,
|
|
//! `DiscStream`, `verify`) compile unchanged. A blanket impl
|
|
//! forwards every `SectorReader` impl to `SectorSource`, so new
|
|
//! code should target `SectorSource` / `SectorSink` directly. The
|
|
//! formal `#[deprecated]` attribute lands once the internal
|
|
//! callers have migrated; see the comment on `SectorReader` for
|
|
//! why this commit holds it back.
|
|
|
|
pub mod decrypting;
|
|
pub mod file;
|
|
|
|
use crate::error::Result;
|
|
|
|
/// Read 2048-byte sectors from a disc, image, or composed source.
|
|
///
|
|
/// Direction-typed: a `SectorSource` cannot be written to. Wrap the
|
|
/// inner source in [`DecryptingSectorSource`] to get plaintext
|
|
/// sectors out of an encrypted disc.
|
|
pub trait SectorSource: Send {
|
|
/// Total capacity in sectors, if known. Returns 0 when unknown
|
|
/// (e.g. live drives that haven't completed `READ CAPACITY` yet).
|
|
fn capacity_sectors(&self) -> u32;
|
|
|
|
/// Read `count` sectors starting at `lba` into `buf`.
|
|
/// `buf` must be at least `count * 2048` bytes.
|
|
/// `recovery`: true = full retry/reset loop (ripping),
|
|
/// false = single attempt (verify). File-backed sources ignore
|
|
/// the flag.
|
|
///
|
|
/// Returns the number of bytes written into `buf` on success.
|
|
fn read_sectors(
|
|
&mut self,
|
|
lba: u32,
|
|
count: u16,
|
|
buf: &mut [u8],
|
|
recovery: bool,
|
|
) -> Result<usize>;
|
|
|
|
/// Optional speed control for sources that map to a physical
|
|
/// drive. No-op for everything else.
|
|
fn set_speed(&mut self, _kbs: u16) {}
|
|
}
|
|
|
|
/// Write 2048-byte sectors to a disc image or composed sink.
|
|
///
|
|
/// Direction-typed: a `SectorSink` cannot be read from. The
|
|
/// terminal [`finish`] takes `Box<Self>` so it can run on `dyn
|
|
/// SectorSink` and consume the sink (`fsync` + close).
|
|
///
|
|
/// [`finish`]: SectorSink::finish
|
|
pub trait SectorSink: Send {
|
|
/// Write the sectors in `buf` starting at `lba`. `buf.len()`
|
|
/// must be a multiple of 2048; the implementation seeks to
|
|
/// `lba * 2048` before writing.
|
|
fn write_sectors(&mut self, lba: u32, buf: &[u8]) -> Result<()>;
|
|
|
|
/// Flush, fsync, and close. Consumes the sink. Always called
|
|
/// last; subsequent operations are not defined.
|
|
fn finish(self: Box<Self>) -> Result<()>;
|
|
}
|
|
|
|
/// 0.17 read trait. Slated for removal once internal call sites
|
|
/// migrate to [`SectorSource`] in follow-up commits; until then
|
|
/// it remains the trait that `Drive`, `IsoSectorReader`,
|
|
/// `BufferedSectorReader`, and existing `&mut dyn SectorReader`
|
|
/// signatures use unchanged.
|
|
///
|
|
/// New code should implement [`SectorSource`] directly. The
|
|
/// blanket impl below makes any `SectorReader` automatically
|
|
/// usable wherever a `SectorSource` is expected, so a one-way
|
|
/// migration off `SectorReader` is possible per-callsite without
|
|
/// touching the impls.
|
|
//
|
|
// NOTE: not marked `#[deprecated]` in this commit — `cargo clippy
|
|
// -- -D warnings` (the CI gauntlet) treats deprecation as an
|
|
// error, and the existing `Drive` / `udf::BufferedSectorReader` /
|
|
// `mux::DiscStream` / `verify` call sites all go through this
|
|
// trait. The deprecation attribute lands together with the
|
|
// migration commits that move those call sites to
|
|
// `SectorSource`. The behavioural contract — "this trait is
|
|
// going away in 0.18" — is documented above and tracked in
|
|
// `freemkv-private/memory/0_18_redesign.md`.
|
|
pub trait SectorReader: Send {
|
|
/// Read `count` sectors starting at `lba` into `buf`.
|
|
/// See [`SectorSource::read_sectors`] for semantics.
|
|
fn read_sectors(
|
|
&mut self,
|
|
lba: u32,
|
|
count: u16,
|
|
buf: &mut [u8],
|
|
recovery: bool,
|
|
) -> Result<usize>;
|
|
|
|
/// Total capacity in sectors, if known.
|
|
fn capacity(&self) -> u32 {
|
|
0
|
|
}
|
|
|
|
fn set_speed(&mut self, _kbs: u16) {}
|
|
}
|
|
|
|
// Blanket impl: anything implementing the legacy `SectorReader`
|
|
// trait automatically satisfies `SectorSource`. This is what keeps
|
|
// existing impls (`Drive`, `IsoSectorReader`, `BufferedSectorReader`,
|
|
// etc.) compiling without source changes during the migration. The
|
|
// reverse direction (impl SectorReader for SectorSource) is
|
|
// intentionally NOT provided — new code targets the new trait.
|
|
impl<T: SectorReader + ?Sized> SectorSource for T {
|
|
fn capacity_sectors(&self) -> u32 {
|
|
<T as SectorReader>::capacity(self)
|
|
}
|
|
|
|
fn read_sectors(
|
|
&mut self,
|
|
lba: u32,
|
|
count: u16,
|
|
buf: &mut [u8],
|
|
recovery: bool,
|
|
) -> Result<usize> {
|
|
<T as SectorReader>::read_sectors(self, lba, count, buf, recovery)
|
|
}
|
|
|
|
fn set_speed(&mut self, kbs: u16) {
|
|
<T as SectorReader>::set_speed(self, kbs)
|
|
}
|
|
}
|
|
|
|
pub use decrypting::DecryptingSectorSource;
|
|
pub use file::{FileSectorSink, FileSectorSource};
|
|
|
|
// Backwards-compat alias for the public API. `FileSectorReader` is
|
|
// the 0.17 name; new code uses `FileSectorSource`. Both point at
|
|
// the same type. The `#[deprecated]` attribute lands together with
|
|
// the migration commits that retire the alias from internal uses.
|
|
pub type FileSectorReader = FileSectorSource;
|