v0.13.3: broaden is_wedge_signature — fix dead-code wedge recovery
0.13.2's is_wedge_signature gated on opcode=SCSI_INQUIRY (0x12), but drive_has_disc issues TEST UNIT READY (0x00). Production wedge errors (E4000: 0x00/0xff/0x00) never matched → SCSI reset + USB reset escalation never fired. Drop the opcode gate. Status byte 0xFF is synthesised by our own execute() path on poll() timeout — it's the ground-truth wedge marker for any opcode. Linux-only; macOS/Windows use sense-key-based wedge detection.
This commit is contained in:
@@ -1,5 +1,25 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.13.3 (2026-04-24)
|
||||||
|
|
||||||
|
### Bug fix — `drive_has_disc` wedge recovery was dead code for TUR errors
|
||||||
|
|
||||||
|
The wedge-signature predicate introduced in 0.13.2 gated on
|
||||||
|
`opcode == SCSI_INQUIRY (0x12)` — a holdover from when enumerate-time
|
||||||
|
INQUIRY was the only path wedges surfaced on. `drive_has_disc` issues
|
||||||
|
`TEST UNIT READY (0x00)`, so its wedge errors (`E4000: 0x00/0xff/0x00`)
|
||||||
|
never matched the predicate and the SCSI-reset + USB-reset escalation
|
||||||
|
never fired. Production result: the BU40N USB BD-RE stayed wedged
|
||||||
|
indefinitely, with autorip logging `recovery exhausted` on the raw
|
||||||
|
pass-through error while no recovery had actually been attempted.
|
||||||
|
|
||||||
|
Fix: drop the opcode constraint. Status byte `0xFF` is synthesised by
|
||||||
|
our own `execute()` path when `poll()` on the SG fd times out; it's
|
||||||
|
the ground-truth wedge marker regardless of which opcode was in flight.
|
||||||
|
Doc comments on `is_wedge_signature` and `WEDGE_STATUS_BYTE` updated
|
||||||
|
accordingly. Linux-only — macOS / Windows use sense-key-based wedge
|
||||||
|
detection and are unaffected.
|
||||||
|
|
||||||
## 0.13.2 (2026-04-24)
|
## 0.13.2 (2026-04-24)
|
||||||
|
|
||||||
### Public discovery + presence APIs; SCSI/USB primitives no longer
|
### Public discovery + presence APIs; SCSI/USB primitives no longer
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libfreemkv"
|
name = "libfreemkv"
|
||||||
version = "0.13.2"
|
version = "0.13.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.86"
|
rust-version = "1.86"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
|
|||||||
+10
-9
@@ -694,25 +694,26 @@ fn recover_then_probe(path: &Path, original: Error) -> Result<bool> {
|
|||||||
Err(original)
|
Err(original)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wedge signature: `Error::ScsiError` with INQUIRY opcode (0x12) and
|
/// Wedge signature: `Error::ScsiError` with status byte 0xFF, for any
|
||||||
/// status byte 0xFF. 0xFF isn't a valid SCSI status — the kernel synthesises
|
/// opcode. 0xFF isn't a real SCSI status — our own `execute()` path
|
||||||
/// it when the device gives no answer, which is the real-world signature
|
/// synthesises it when poll() times out waiting for the kernel to
|
||||||
/// of a USB Mass Storage layer wedge.
|
/// deliver a response. That timeout is the ground-truth signature of
|
||||||
|
/// the USB Mass Storage layer wedging; opcode-in-flight is incidental.
|
||||||
fn is_wedge_signature(err: &Error) -> bool {
|
fn is_wedge_signature(err: &Error) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
err,
|
err,
|
||||||
Error::ScsiError {
|
Error::ScsiError {
|
||||||
opcode: crate::scsi::SCSI_INQUIRY,
|
|
||||||
status: WEDGE_STATUS_BYTE,
|
status: WEDGE_STATUS_BYTE,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synthesised SCSI status byte returned by the Linux SG driver when
|
/// Synthesised SCSI status byte returned by our own transport when
|
||||||
/// the kernel got no useful response from the device — the wedge
|
/// poll() on the SG fd times out — the wedge signature. Real SCSI
|
||||||
/// signature. Real SCSI statuses are GOOD (0x00), CHECK_CONDITION (0x02),
|
/// statuses are GOOD (0x00), CHECK_CONDITION (0x02), BUSY (0x08), etc.;
|
||||||
/// BUSY (0x08), etc.; 0xFF is reserved/invalid in the spec.
|
/// 0xFF is reserved/invalid in the spec, so we can't collide with a
|
||||||
|
/// real device response. Applies to any opcode (TUR, INQUIRY, READ, …).
|
||||||
const WEDGE_STATUS_BYTE: u8 = 0xFF;
|
const WEDGE_STATUS_BYTE: u8 = 0xFF;
|
||||||
|
|
||||||
/// Settle time after `USBDEVFS_RESET` returns. The kernel re-enumerates
|
/// Settle time after `USBDEVFS_RESET` returns. The kernel re-enumerates
|
||||||
|
|||||||
Reference in New Issue
Block a user