From 1e6eb0698d35a4685086dd91e6867f24b79d0008 Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Wed, 29 Apr 2026 08:53:56 -0700 Subject: [PATCH] v0.13.43: Pass 1 transport-failure recovery loop --- CHANGELOG.md | 11 +++++++++++ Cargo.toml | 2 +- src/disc/mod.rs | 26 +++++--------------------- src/error.rs | 6 ++++++ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f879cf6..02f0579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.13.43 (2026-04-29) + +### Pass 1 transport-failure recovery loop + +- Transport failure (USB bridge crash) no longer kills the entire rip. +- Autorip re-discovers the drive after USB re-enumeration and resumes + from the mapfile. Up to 10 attempts. +- `Error::is_scsi_transport_failure()` now matches DiscRead with + status 0xFF (bridge crash) in addition to ScsiError. +- DriveSession tracks device_path for re-discovery. + ## 0.13.42 (2026-04-29) ### Fix: transport failure skips instead of aborting diff --git a/Cargo.toml b/Cargo.toml index 0edfca3..672e32d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfreemkv" -version = "0.13.42" +version = "0.13.43" edition = "2024" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/disc/mod.rs b/src/disc/mod.rs index 34f57ab..44227f0 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -1281,7 +1281,6 @@ 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; @@ -1362,7 +1361,6 @@ 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], @@ -1392,32 +1390,18 @@ impl Disc { let err = read_result.err().unwrap(); read_err_count += 1; - let is_transport = err.is_scsi_transport_failure(); - - if is_transport { - consecutive_transport_failures += 1; + if err.is_scsi_transport_failure() { tracing::warn!( target: "freemkv::disc", phase = "transport_failure", lba = block_lba, - consecutive = consecutive_transport_failures, - "transport failure (bridge crash); skipping block" + error = %err, + "transport failure (bridge crash); aborting copy — caller should USB reset + resume" ); - 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; + return Err(err); } - if !is_transport - && !err.is_marginal_read() + if !err.is_marginal_read() && err.scsi_sense().is_none_or(|s| !s.is_medium_error()) { return Err(err); diff --git a/src/error.rs b/src/error.rs index 43794e6..def4385 100644 --- a/src/error.rs +++ b/src/error.rs @@ -507,6 +507,12 @@ impl Error { status: crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE, .. } + ) || matches!( + self, + Error::DiscRead { + status: Some(crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE), + .. + } ) }