From 2ce4c202217013432492878d60d0042fa92349fe Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Mon, 27 Apr 2026 20:44:59 -0700 Subject: [PATCH] disc: skip MEDIUM ERROR sectors instead of bailing 0.13.28 - when drive returns MEDIUM ERROR (bad sector), skip the sector and continue instead of retrying or bailing. Write zero-fill, mark as Unreadable for pass 2+ recovery. Closes: (internal)#20260427 --- Cargo.toml | 2 +- src/disc/mod.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8245495..dcdd235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfreemkv" -version = "0.13.27" +version = "0.13.28" edition = "2024" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/disc/mod.rs b/src/disc/mod.rs index fabb351..07f45b9 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -1414,6 +1414,32 @@ impl Disc { status: Some(status), sense, }); + } else if block_result + .as_ref() + .err() + .map(|e| e.scsi_sense().map(|s| s.is_medium_error()).unwrap_or(false)) + .unwrap_or(false) + { + // 0.13.28: MEDIUM ERROR (bad sector) — skip this sector + // and continue. Retry won't recover it. Fill in pass 2+. + let err = block_result.err().unwrap(); + tracing::trace!( + target: "freemkv::disc", + phase = "skip_bad_sector", + lba = block_lba, + error = %err, + "MEDIUM ERROR; skipping sector" + ); + // Record as bad and zero-fill + let zero = vec![0u8; block_bytes as usize]; + file.seek(SeekFrom::Start(pos)) + .map_err(|e| Error::IoError { source: e })?; + file.write_all(&zero[..block_bytes as usize]) + .map_err(|e| Error::IoError { source: e })?; + map.record(pos, block_bytes, mapfile::SectorStatus::Unreadable) + .map_err(|e| Error::IoError { source: e })?; + bytes_done = bytes_done.saturating_add(block_bytes); + mode_single = true; // stay in single for subsequent } else if !block_result .as_ref() .err() @@ -1482,7 +1508,24 @@ impl Disc { // 60 sectors either — bail with full sense info // rather than chewing through bpt=1 timeouts. if let Err(ref e) = one_result { - if !e.is_marginal_read() { + if e.scsi_sense().map(|s| s.is_medium_error()).unwrap_or(false) { + // 0.13.28: MEDIUM ERROR in single mode — skip sector + tracing::trace!( + target: "freemkv::disc", + phase = "skip_bad_sector", + lba = s_lba, + "MEDIUM ERROR in single mode; skipping" + ); + let zero = vec![0u8; one_bytes]; + file.seek(SeekFrom::Start(s_pos)) + .map_err(|e| Error::IoError { source: e })?; + file.write_all(&zero[..one_bytes]) + .map_err(|e| Error::IoError { source: e })?; + map.record(s_pos, 2048, mapfile::SectorStatus::Unreadable) + .map_err(|e| Error::IoError { source: e })?; + bytes_done = bytes_done.saturating_add(2048); + continue; + } else if !e.is_marginal_read() { let err = one_result.err().unwrap(); tracing::trace!( target: "freemkv::disc",