v0.13.43: Pass 1 transport-failure recovery loop

This commit is contained in:
MattJackson
2026-04-29 08:53:56 -07:00
parent bd744171e4
commit 1e6eb0698d
4 changed files with 23 additions and 22 deletions
+11
View File
@@ -1,5 +1,16 @@
# Changelog # 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) ## 0.13.42 (2026-04-29)
### Fix: transport failure skips instead of aborting ### Fix: transport failure skips instead of aborting
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "libfreemkv" name = "libfreemkv"
version = "0.13.42" version = "0.13.43"
edition = "2024" edition = "2024"
rust-version = "1.86" rust-version = "1.86"
license = "AGPL-3.0-only" license = "AGPL-3.0-only"
+5 -21
View File
@@ -1281,7 +1281,6 @@ impl Disc {
let mut buf = vec![0u8; batch as usize * 2048]; let mut buf = vec![0u8; batch as usize * 2048];
let mut bytes_done = 0u64; let mut bytes_done = 0u64;
let mut halt_requested = false; let mut halt_requested = false;
let mut consecutive_transport_failures: u32 = 0;
let copy_t0 = std::time::Instant::now(); let copy_t0 = std::time::Instant::now();
let mut iter_count: u64 = 0; let mut iter_count: u64 = 0;
let mut read_ok_count: u64 = 0; let mut read_ok_count: u64 = 0;
@@ -1362,7 +1361,6 @@ impl Disc {
if read_result.is_ok() { if read_result.is_ok() {
read_ok_count += 1; read_ok_count += 1;
consecutive_transport_failures = 0;
if opts.decrypt { if opts.decrypt {
crate::decrypt::decrypt_sectors( crate::decrypt::decrypt_sectors(
&mut buf[..block_bytes as usize], &mut buf[..block_bytes as usize],
@@ -1392,32 +1390,18 @@ impl Disc {
let err = read_result.err().unwrap(); let err = read_result.err().unwrap();
read_err_count += 1; read_err_count += 1;
let is_transport = err.is_scsi_transport_failure(); if err.is_scsi_transport_failure() {
if is_transport {
consecutive_transport_failures += 1;
tracing::warn!( tracing::warn!(
target: "freemkv::disc", target: "freemkv::disc",
phase = "transport_failure", phase = "transport_failure",
lba = block_lba, lba = block_lba,
consecutive = consecutive_transport_failures, error = %err,
"transport failure (bridge crash); skipping block" "transport failure (bridge crash); aborting copy — caller should USB reset + resume"
); );
if consecutive_transport_failures >= 3 { return Err(err);
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 !is_transport if !err.is_marginal_read()
&& !err.is_marginal_read()
&& err.scsi_sense().is_none_or(|s| !s.is_medium_error()) && err.scsi_sense().is_none_or(|s| !s.is_medium_error())
{ {
return Err(err); return Err(err);
+6
View File
@@ -507,6 +507,12 @@ impl Error {
status: crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE, status: crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE,
.. ..
} }
) || matches!(
self,
Error::DiscRead {
status: Some(crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE),
..
}
) )
} }