unlock/patch: preserve SCSI sense across the bridge; init aborts on dead bus; slow-retry first patch failure
Audit fixes (v1.1.0..HEAD regressions in the unlock migration + adaptive patch
speed):
- unlock_bridge ScsiAdapter: libfreemkv's transport returns Err on ANY non-zero
SCSI status (a normal CHECK CONDITION), not only transport faults. The adapter
was collapsing every such Err to { status: 0xFF, sense: None }, which discarded
the parsed sense and defeated the AACS handshake's ILLEGAL_REQUEST wedge guard
(so it kept hammering the drive — hard-rule #2) and inverted its
transport-vs-rejection diagnosis. Now reconstruct status + the 32-byte sense
buffer (sense_key@2, asc@12, ascq@13) and only emit 0xFF/None for a genuine
transport fault.
- Drive::init: a genuine transport fault during the drive-prep unlock means the
bus is dead — propagate it (the v1.1.0 invariant) instead of silently
swallowing it via `if let Ok`. Other errors (no matching unlocker) still fall
through to stock mode. SET CD SPEED max now runs only when the bus is alive.
- disc::patch: on the first read failure in a range, drop to slow recovery speed
and RE-ATTEMPT the same position at slow speed before marking it. A
single-sector range's first failing sector was being marked from a MAX-speed
read it never got to recover.
- docs: lib.rs architecture diagram (handshake → host_certs) and README (stale
pluggable-unlock-seam / register-unlocker / crates.io / docs.rs references).
This commit is contained in:
+17
-5
@@ -2085,20 +2085,32 @@ impl Disc {
|
||||
}
|
||||
Err(err) => {
|
||||
// First failure in this range: the fast-batched pass over
|
||||
// the clean overshoot is done; drop to the slow recovery
|
||||
// speed for the rest of the range and arm the cooldown.
|
||||
// Idempotent — only the first failure issues SET CD SPEED.
|
||||
if !range_slowed {
|
||||
// the clean overshoot is done. A genuine transport fault
|
||||
// (bridge crash) is NOT a recoverable bad sector — let
|
||||
// handle_read_failure abort the pass immediately rather
|
||||
// than burn a slow re-read on a wedged bus.
|
||||
if !range_slowed && !err.is_scsi_transport_failure() {
|
||||
// Drop to the slow recovery speed, arm the cooldown,
|
||||
// and RE-ATTEMPT the same position at slow speed before
|
||||
// marking it. The drive's deep recovery (long re-reads
|
||||
// / ECC) only engages at the slow speed; the failure so
|
||||
// far is a fast-read miss. Hold the cursor (don't
|
||||
// advance, don't count damage) and retry — only a
|
||||
// slow-speed result reaches handle_read_failure below.
|
||||
// Without this, a single-sector range's first failing
|
||||
// sector was marked from a MAX-speed read it never got
|
||||
// to recover. range_slowed gates this to once per range.
|
||||
reader.set_speed(0x0000);
|
||||
tracing::info!(
|
||||
target: "freemkv::disc",
|
||||
phase = "patch_speed",
|
||||
lba,
|
||||
speed = "0x0000",
|
||||
"patch: range dropped to slow recovery speed on first read failure"
|
||||
"patch: range dropped to slow recovery speed; retrying the failing read at slow speed before marking"
|
||||
);
|
||||
range_slowed = true;
|
||||
cooldown_pending = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
match handle_read_failure(
|
||||
|
||||
Reference in New Issue
Block a user