diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a121e..f879cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.13.42 (2026-04-29) + +### Fix: transport failure skips instead of aborting + +- Transport failure (USB bridge crash) now skips the failed ECC block + (marks NonTrimmed) and continues. 3 consecutive transport failures + still abort the copy. Previously, a single transport failure killed + the entire rip. + ## 0.13.41 (2026-04-29) ### Debug logging for sector-0 regression diagnosis diff --git a/Cargo.toml b/Cargo.toml index 6f83aa1..0edfca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfreemkv" -version = "0.13.41" +version = "0.13.42" edition = "2024" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/disc/mod.rs b/src/disc/mod.rs index 7d7d490..34f57ab 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -1281,6 +1281,7 @@ impl Disc { let mut buf = vec![0u8; batch as usize * 2048]; let mut bytes_done = 0u64; let mut halt_requested = false; + let mut consecutive_transport_failures: u32 = 0; let copy_t0 = std::time::Instant::now(); let mut iter_count: u64 = 0; let mut read_ok_count: u64 = 0; @@ -1361,6 +1362,7 @@ impl Disc { if read_result.is_ok() { read_ok_count += 1; + consecutive_transport_failures = 0; if opts.decrypt { crate::decrypt::decrypt_sectors( &mut buf[..block_bytes as usize], @@ -1390,18 +1392,32 @@ impl Disc { let err = read_result.err().unwrap(); read_err_count += 1; - if err.is_scsi_transport_failure() { + let is_transport = err.is_scsi_transport_failure(); + + if is_transport { + consecutive_transport_failures += 1; tracing::warn!( target: "freemkv::disc", phase = "transport_failure", lba = block_lba, - error = %err, - "transport failure (bridge crash); aborting copy" + consecutive = consecutive_transport_failures, + "transport failure (bridge crash); skipping block" ); - return Err(err); + if consecutive_transport_failures >= 3 { + tracing::warn!( + target: "freemkv::disc", + phase = "transport_failure_abort", + consecutive = consecutive_transport_failures, + "3 consecutive transport failures; aborting copy" + ); + return Err(err); + } + } else { + consecutive_transport_failures = 0; } - if !err.is_marginal_read() + if !is_transport + && !err.is_marginal_read() && err.scsi_sense().is_none_or(|s| !s.is_medium_error()) { return Err(err);