Add scan_iso entry point for file-backed ISO scans

Introduce libfreemkv::scan_iso(path, opts) -> (Disc, Box<dyn SectorSource>),
the file-backed counterpart to DiscSession::scan. It is the single place
that opens a FileSectorSource, reads its capacity, and runs Disc::scan_image,
returning the scanned Disc plus a reusable reader over the same image so
consumers stop hand-rolling that triple.

Add an integration test that materialises a minimal synthetic UDF image to a
real file, asserts scan_iso matches the manual open+scan_image composition,
and confirms the returned reader is still usable (capacity + sector read).
Also covers open-failure and scan-failure error propagation.
This commit is contained in:
Matthew Jackson
2026-07-23 23:48:50 -07:00
parent 7ed798e386
commit 6f53767e8b
3 changed files with 213 additions and 3 deletions
+1 -1
View File
@@ -146,7 +146,7 @@ pub use drive::{Drive, DriveStatus, find_drive};
// stop hand-rolling `open → wait_ready → init → probe_disc → identify → scan`.
// Owns the `Drive` by value; forwards consumer-built key material into
// `ScanOptions` (the library derives no certs — see `KeySpec`).
pub use session::{DeviceTarget, DiscSession, KeySpec};
pub use session::{DeviceTarget, DiscSession, KeySpec, scan_iso};
// ─── Errors ─────────────────────────────────────────────────────────────────
//
+25 -2
View File
@@ -17,8 +17,8 @@ use crate::disc::{Disc, DiscId, DriveCredentials, ScanOptions};
use crate::drive::{Drive, find_drive};
use crate::error::{Error, Result};
use crate::keysource::KeySource;
use crate::sector::SectorSource;
use std::path::PathBuf;
use crate::sector::{FileSectorSource, SectorSource};
use std::path::{Path, PathBuf};
/// Which optical device a [`DiscSession`] should open.
pub enum DeviceTarget {
@@ -190,6 +190,29 @@ impl DiscSession {
}
}
/// Scan an ISO image's structure from a file path, returning the scanned
/// [`Disc`] together with a reusable [`SectorSource`] over the same file.
///
/// This is the file-backed counterpart to [`DiscSession::scan`]: it is the one
/// place that opens a [`FileSectorSource`], reads its capacity, and runs
/// [`Disc::scan_image`], so consumers (CLI, autorip) stop hand-rolling that
/// triple and stop constructing the low-level reader themselves. No SCSI, no
/// handshake, no key resolution — AACS resolution during the scan uses only
/// whatever `opts` already carries (mirroring how `Disc::scan_image` forwards
/// `ScanOptions`).
///
/// The returned reader is a fresh handle positioned at the start of the image;
/// callers that need to sample ciphertext (key resolution) or feed a mux can
/// reuse it directly rather than re-opening the file. `Disc::scan_image` reads
/// only through the same reader, and all reads are LBA-addressed, so the
/// handle is fully reusable afterward.
pub fn scan_iso(path: &Path, opts: ScanOptions) -> Result<(Disc, Box<dyn SectorSource>)> {
let mut reader = FileSectorSource::open(path)?;
let capacity = reader.capacity_sectors();
let disc = Disc::scan_image(&mut reader, capacity, &opts)?;
Ok((disc, Box::new(reader)))
}
#[cfg(test)]
mod tests {
use super::*;