From 4a76deadeba98260ad8f392c0f0d76e209a83ed1 Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Tue, 23 Jun 2026 03:45:34 -0700 Subject: [PATCH] scsi(windows): fix StorageAdapterDescriptor.BusType width (u8 -> u32) STORAGE_ADAPTER_DESCRIPTOR.BusType is STORAGE_BUS_TYPE, an int-sized (4-byte) enum, but the repr(C) struct declared it as u8. The total size stayed 32 bytes by coincidence of alignment padding, and the two fields actually read today (MaximumTransferLength at offset 8 and AlignmentMask at offset 16) sit ahead of BusType, so there was no runtime impact. But BusMajorVersion and BusMinorVersion landed at offsets 26 and 28 instead of the SDK's 28 and 30, so any future reader of those fields would have gotten wrong values. Widen BusType to u32 so BusMajorVersion/BusMinorVersion fall at the correct offsets, and add a layout regression test asserting every field offset and the 32-byte total against the winioctl.h layout. --- src/scsi/windows.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/scsi/windows.rs b/src/scsi/windows.rs index 8e9babc..ff4c4a2 100644 --- a/src/scsi/windows.rs +++ b/src/scsi/windows.rs @@ -109,7 +109,13 @@ struct StorageAdapterDescriptor { AdapterScansDown: u8, CommandQueueing: u8, AcceleratedTransfer: u8, - BusType: u8, + // STORAGE_BUS_TYPE is an `int`-sized enum (4 bytes), not a byte. With the + // four preceding `BOOLEAN`s filling offsets 20..24, `BusType` sits at + // offset 24 and the two `USHORT` version fields follow at 28 and 30 — + // matching winioctl.h. (Declaring this `u8` total-sized to 32 by luck but + // pushed BusMajor/BusMinorVersion to offsets 26/28, so any reader of those + // fields got garbage.) + BusType: u32, BusMajorVersion: u16, BusMinorVersion: u16, } @@ -615,4 +621,39 @@ mod tests { assert_ne!(IOCTL_STORAGE_RESET_DEVICE, 0x002D_1004); assert_ne!(IOCTL_STORAGE_RESET_DEVICE, 0x002D_D000); } + + /// Regression guard for the `StorageAdapterDescriptor` layout. It must + /// match `STORAGE_ADAPTER_DESCRIPTOR` (winioctl.h) field-for-field so a + /// driver-filled buffer is interpreted at the correct offsets. `BusType` + /// is `STORAGE_BUS_TYPE`, an `int`-sized (4-byte) enum, NOT a byte; a + /// previous `u8` declaration kept the total size at 32 by coincidence but + /// shifted `BusMajorVersion`/`BusMinorVersion` to offsets 26/28 (vs the + /// SDK's 28/30), so any reader of those fields got wrong values. + #[test] + fn storage_adapter_descriptor_matches_sdk_layout() { + use std::mem::{offset_of, size_of}; + assert_eq!(offset_of!(StorageAdapterDescriptor, Version), 0); + assert_eq!(offset_of!(StorageAdapterDescriptor, Size), 4); + assert_eq!( + offset_of!(StorageAdapterDescriptor, MaximumTransferLength), + 8 + ); + assert_eq!( + offset_of!(StorageAdapterDescriptor, MaximumPhysicalPages), + 12 + ); + assert_eq!(offset_of!(StorageAdapterDescriptor, AlignmentMask), 16); + assert_eq!(offset_of!(StorageAdapterDescriptor, AdapterUsesPio), 20); + assert_eq!(offset_of!(StorageAdapterDescriptor, AdapterScansDown), 21); + assert_eq!(offset_of!(StorageAdapterDescriptor, CommandQueueing), 22); + assert_eq!( + offset_of!(StorageAdapterDescriptor, AcceleratedTransfer), + 23 + ); + // The fields that were misplaced by the old `u8` BusType. + assert_eq!(offset_of!(StorageAdapterDescriptor, BusType), 24); + assert_eq!(offset_of!(StorageAdapterDescriptor, BusMajorVersion), 28); + assert_eq!(offset_of!(StorageAdapterDescriptor, BusMinorVersion), 30); + assert_eq!(size_of::(), 32); + } }