From 8870efa77b7ee7feeb72bf21da8604b9ba59dacd Mon Sep 17 00:00:00 2001 From: Matt Jackson <1085847+MattJackson@users.noreply.github.com> Date: Mon, 20 Apr 2026 01:01:44 +0000 Subject: [PATCH] =?UTF-8?q?v0.11.9:=20fast=20verify=20reads=20=E2=80=94=20?= =?UTF-8?q?5s=20timeout,=20no=20recovery=20loop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- src/drive/mod.rs | 30 ++++++++++++++++++++++++++++++ src/sector.rs | 6 ++++++ src/verify.rs | 8 ++++---- 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80aecba..22b4e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.11.9 (2026-04-20) + +### Fast verify reads +- **read_sectors_fast()** — single-attempt 5s timeout SCSI read for verify. No recovery loop. Bad sectors detected in seconds instead of 10+ minutes. +- **SectorReader trait** — added read_sectors_fast() with default fallback to read_sectors(). + ## 0.11.8 (2026-04-20) ### Disc verify diff --git a/Cargo.toml b/Cargo.toml index f9e22a9..fa1a67b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfreemkv" -version = "0.11.8" +version = "0.11.9" edition = "2021" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/drive/mod.rs b/src/drive/mod.rs index 16e0ee7..1421abc 100644 --- a/src/drive/mod.rs +++ b/src/drive/mod.rs @@ -542,6 +542,32 @@ impl Drive { Err(Error::DiscRead { sector: lba as u64 }) } + /// Fast single-attempt read for verification — no recovery, short timeout. + /// Returns Ok on success, Err on any failure. Does not retry. + pub fn read_fast(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result { + let cdb = [ + crate::scsi::SCSI_READ_10, + 0x00, + (lba >> 24) as u8, + (lba >> 16) as u8, + (lba >> 8) as u8, + lba as u8, + 0x00, + (count >> 8) as u8, + count as u8, + 0x00, + ]; + match self.scsi.as_mut().execute( + &cdb, + crate::scsi::DataDirection::FromDevice, + buf, + 5_000, // 5 second timeout — enough for healthy reads, fast fail for bad sectors + ) { + Ok(result) => Ok(result.bytes_transferred), + Err(_) => Err(Error::DiscRead { sector: lba as u64 }), + } + } + /// Read the disc capacity in sectors (2048 bytes each). pub fn read_capacity(&mut self) -> Result { let cdb = [ @@ -643,6 +669,10 @@ impl SectorReader for Drive { fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result { self.read(lba, count, buf) } + + fn read_sectors_fast(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result { + self.read_fast(lba, count, buf) + } } /// Find all optical drives connected to this system. diff --git a/src/sector.rs b/src/sector.rs index 4754677..b875680 100644 --- a/src/sector.rs +++ b/src/sector.rs @@ -12,6 +12,12 @@ pub trait SectorReader: Send { /// `buf` must be at least `count * 2048` bytes. fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result; + /// Fast single-attempt read — no recovery, short timeout. + /// Default implementation calls read_sectors (ISO files don't need fast mode). + fn read_sectors_fast(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result { + self.read_sectors(lba, count, buf) + } + /// Total capacity in sectors, if known. fn capacity(&self) -> u32 { 0 diff --git a/src/verify.rs b/src/verify.rs index cf7a8ad..a8d2fbb 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -105,7 +105,7 @@ pub fn verify_title( let batch_start = Instant::now(); let batch_ok = reader - .read_sectors(lba, count, &mut buf[..bytes]) + .read_sectors_fast(lba, count, &mut buf[..bytes]) .is_ok(); let batch_ms = batch_start.elapsed().as_millis(); @@ -144,7 +144,7 @@ pub fn verify_title( let s1 = Instant::now(); let first_ok = reader - .read_sectors(sector_lba, 1, &mut buf[sector_offset..sector_offset + 2048]) + .read_sectors_fast(sector_lba, 1, &mut buf[sector_offset..sector_offset + 2048]) .is_ok(); let s1_ms = s1.elapsed().as_millis(); @@ -155,10 +155,10 @@ pub fn verify_title( slow += 1; SectorStatus::Slow } else { - // Retry once more + // Retry once more after brief pause std::thread::sleep(std::time::Duration::from_secs(2)); if reader - .read_sectors(sector_lba, 1, &mut buf[sector_offset..sector_offset + 2048]) + .read_sectors_fast(sector_lba, 1, &mut buf[sector_offset..sector_offset + 2048]) .is_ok() { recovered += 1;