Fix Windows multi-drive selection, disk:// alias, and READ chunking

Three fixes for a Windows ASUS Blu-ray drive that failed/spammed errors:

- resolve.rs: accept disk:// as an alias for disc:// (identical behavior;
  empty = auto-detect, path = device). Windows users commonly type
  disk://i: after the drive-letter convention.

- drive::find_drive: prefer a drive that reports media present. Enumerate
  all optical drives, query Drive::drive_status() (GET EVENT STATUS, works
  regardless of firmware), and return the first reporting DiscPresent;
  fall back to the first enumerated drive when none report a disc so
  single-drive / quirky setups don't regress. Selection policy split into
  select_drive_with_media() for unit testing.

- READ chunking: add ScsiTransport::max_transfer_bytes() (default 1 MiB).
  Windows SPTI overrides it with the adapter MaximumTransferLength queried
  via IOCTL_STORAGE_QUERY_PROPERTY / StorageAdapterProperty, clamped to a
  64 KiB floor (fallback on query failure). Drive::read now caps each
  READ(10) to that limit: small reads take the unchanged single-CDB path,
  larger reads loop over read_one() chunks, reporting the failing chunk's
  LBA on error. This stops the 16 MiB single read that exceeded the
  adapter limit, made DeviceIoControl fail, and spammed transport-failure
  warnings with slow tiny-read fallbacks.

Tests added for the disk:// alias, media-preference selection, and READ
chunk decomposition / per-chunk error LBA.
This commit is contained in:
Matthew Jackson
2026-06-22 15:02:24 -07:00
parent 4f606ae9a3
commit c73a3dbcb6
4 changed files with 411 additions and 6 deletions
+17
View File
@@ -319,6 +319,23 @@ pub trait ScsiTransport: Send {
data: &mut [u8],
timeout_ms: u32,
) -> Result<ScsiResult>;
/// Maximum number of bytes the transport can carry in a single SCSI
/// data-in transfer. A READ that requests more than this must be split
/// into chunks by the caller ([`crate::Drive::read`]) — otherwise the
/// transport fails the whole command.
///
/// The default is a conservative 1 MiB, safe on every platform. The
/// Windows backend overrides this with the adapter's real
/// `MaximumTransferLength` (queried via `IOCTL_STORAGE_QUERY_PROPERTY`):
/// a 16 MiB READ that exceeds the adapter limit makes
/// `DeviceIoControl` fail outright, which freemkv then mis-reads as a
/// transport failure and falls back to slow, log-spamming tiny reads.
/// Chunking to this limit fixes that. Linux/macOS keep the 1 MiB
/// default (well within any real `max_sectors_kb`).
fn max_transfer_bytes(&self) -> usize {
1 << 20
}
}
// ── Platform-agnostic open / reset ──────────────────────────────────────────