//! SectorReader — trait for reading 2048-byte disc sectors. //! //! Implemented by Drive (SCSI) and IsoFile (file-backed). //! Used by UDF parser, disc scanner, label parsers — anything that //! reads sectors doesn't need to know where they come from. use crate::error::Result; /// Read 2048-byte sectors from a disc or disc image. pub trait SectorReader: Send { /// 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 readers ignore the flag. fn read_sectors( &mut self, lba: u32, count: u16, buf: &mut [u8], recovery: bool, ) -> Result; /// Total capacity in sectors, if known. fn capacity(&self) -> u32 { 0 } } /// SectorReader backed by a file (ISO image). /// Seeks to lba * 2048, reads count * 2048 bytes. pub struct FileSectorReader { file: std::io::BufReader, capacity: u32, } impl FileSectorReader { pub fn open(path: &str) -> std::io::Result { let file = std::fs::File::open(path)?; let len = file.metadata()?.len(); let sectors = len / 2048; if sectors > u32::MAX as u64 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("{path}: image too large, max ~8 TB"), )); } let capacity = sectors as u32; Ok(Self { file: std::io::BufReader::with_capacity(4 * 1024 * 1024, file), capacity, }) } } impl SectorReader for FileSectorReader { fn read_sectors( &mut self, lba: u32, count: u16, buf: &mut [u8], _recovery: bool, ) -> Result { use std::io::{Read, Seek, SeekFrom}; let offset = lba as u64 * 2048; let bytes = count as usize * 2048; self.file .seek(SeekFrom::Start(offset)) .map_err(|e| crate::error::Error::IoError { source: e })?; self.file .read_exact(&mut buf[..bytes]) .map_err(|e| crate::error::Error::IoError { source: e })?; Ok(bytes) } fn capacity(&self) -> u32 { self.capacity } }