v0.11.9: fast verify reads — 5s timeout, no recovery loop
This commit is contained in:
@@ -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
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
@@ -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<usize> {
|
||||
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<u32> {
|
||||
let cdb = [
|
||||
@@ -643,6 +669,10 @@ impl SectorReader for Drive {
|
||||
fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result<usize> {
|
||||
self.read(lba, count, buf)
|
||||
}
|
||||
|
||||
fn read_sectors_fast(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result<usize> {
|
||||
self.read_fast(lba, count, buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find all optical drives connected to this system.
|
||||
|
||||
@@ -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<usize>;
|
||||
|
||||
/// 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<usize> {
|
||||
self.read_sectors(lba, count, buf)
|
||||
}
|
||||
|
||||
/// Total capacity in sectors, if known.
|
||||
fn capacity(&self) -> u32 {
|
||||
0
|
||||
|
||||
+4
-4
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user