Inspired by balena.io's drivelist, I create a Rust version. The idea is simple, using Windows' Setup API to read all connected storages list (with mount points/drive letters), using winapi crate
///file main.rs
use core::{slice, ffi};
use std::{ptr::{null_mut, addr_of, null}, mem::{zeroed, size_of, MaybeUninit, align_of, transmute}, str::from_utf8};
use winapi::{um::{setupapi::{SetupDiGetClassDevsA, DIGCF_PRESENT, DIGCF_DEVICEINTERFACE, SP_DEVINFO_DATA, SetupDiEnumDeviceInfo, HDEVINFO, PSP_DEVINFO_DATA, SetupDiGetDeviceRegistryPropertyW, SPDRP_FRIENDLYNAME, SPDRP_REMOVAL_POLICY, SPDRP_ENUMERATOR_NAME, SetupDiDestroyDeviceInfoList, SP_DEVICE_INTERFACE_DATA, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetailW, PSP_DEVICE_INTERFACE_DETAIL_DATA_W, SP_DEVICE_INTERFACE_DETAIL_DATA_W}, winioctl::{GUID_DEVINTERFACE_DISK, VOLUME_DISK_EXTENTS, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, PVOLUME_DISK_EXTENTS}, handleapi::{INVALID_HANDLE_VALUE, CloseHandle}, cfgmgr32::{CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL, CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL}, errhandlingapi::GetLastError, fileapi::{CreateFileW, OPEN_EXISTING}, winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_ATTRIBUTE_NORMAL}, ioapiset::DeviceIoControl}, shared::{minwindef::MAX_PATH, winerror::{ERROR_NO_MORE_ITEMS, ERROR_INSUFFICIENT_BUFFER}}, ctypes::c_void};
fn get_detail_data(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA)
{
let mut h_device=INVALID_HANDLE_VALUE;
let mut index=0_u32;
unsafe{
loop {
if h_device!= INVALID_HANDLE_VALUE {
CloseHandle(h_device);
h_device=INVALID_HANDLE_VALUE;
}
let mut device_interface_data:SP_DEVICE_INTERFACE_DATA=zeroed();
device_interface_data.cbSize=size_of::<SP_DEVICE_INTERFACE_DATA>() as _;
if SetupDiEnumDeviceInterfaces(h_dev_info, device_info_data, &GUID_DEVINTERFACE_DISK, index, &mut device_interface_data) == 0 {
let error_code=GetLastError();
if error_code!=ERROR_NO_MORE_ITEMS {
panic!("SetupDiEnumDeviceInterfaces: Error {}",error_code);
}
break;
} else {
let mut size={
let mut required_size=MaybeUninit::<u32>::uninit();
if SetupDiGetDeviceInterfaceDetailW(h_dev_info, &mut device_interface_data, null_mut(), 0, required_size.as_mut_ptr(), null_mut())==0 {
if GetLastError()==ERROR_INSUFFICIENT_BUFFER {
required_size.assume_init()
} else {
panic!("Error SetupDiGetDeviceInterfaceDetailW");
}
} else {
0
}
};
let mut buf:Vec<u8>=Vec::with_capacity(TryInto::<usize>::try_into(size).unwrap() + align_of::<SP_DEVICE_INTERFACE_DETAIL_DATA_W>()-1);
let align_offset=buf.as_mut_ptr().align_offset(align_of::<SP_DEVICE_INTERFACE_DETAIL_DATA_W>());
let device_iface_detail =&mut *(buf.as_mut_ptr().offset(align_offset.try_into().unwrap()) as *mut MaybeUninit<SP_DEVICE_INTERFACE_DETAIL_DATA_W>);
device_iface_detail.write(SP_DEVICE_INTERFACE_DETAIL_DATA_W {
cbSize: size_of::<SP_DEVICE_INTERFACE_DETAIL_DATA_W>().try_into().unwrap(),
DevicePath: [0],
});
if SetupDiGetDeviceInterfaceDetailW(h_dev_info, &mut device_interface_data, device_iface_detail.as_mut_ptr(), size, &mut size, null_mut())==0 {
println!("Error {}, Couldn't SetupDiGetDeviceInterfaceDetailW",GetLastError());
break;
}
let device_detail_data=device_iface_detail.assume_init_ref();
h_device=CreateFileW(device_detail_data.DevicePath.as_ptr(), 0, FILE_SHARE_READ, null_mut(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, null_mut());
if h_device==INVALID_HANDLE_VALUE {
println!("Couldn't open handle to device: Error {}",GetLastError());
break;
}
get_device_number(h_device);
}
index+=1;
}
if h_device!= INVALID_HANDLE_VALUE {
CloseHandle(h_device);
h_device=INVALID_HANDLE_VALUE;
}
}
}
fn get_device_number(h_device:*mut c_void)
{
unsafe {
let mut size=0_u32;
let mut disk_extents=MaybeUninit::<VOLUME_DISK_EXTENTS>::uninit();
disk_extents.write(zeroed());
let result=DeviceIoControl(h_device, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, null_mut(), 0, disk_extents.as_mut_ptr() as _, size_of::<VOLUME_DISK_EXTENTS>() as _, &mut size, null_mut());
/* It returns 0!! */
println!("Bytes returned: {}",size);
if result!=0 {
println!("Success");
} else {
/* This will be executed */
println!("get_device_number fail. Error {}",GetLastError());
}
}
}
fn get_enumerator_name(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA) -> String
{
unsafe {
let mut buffer:[u8;MAX_PATH]=zeroed();
if SetupDiGetDeviceRegistryPropertyW(h_dev_info, device_info_data, SPDRP_ENUMERATOR_NAME, null_mut(), &mut buffer as _, (size_of::<u8>() * MAX_PATH) as _, null_mut()) != 0 {
ansi_to_string(&buffer)
} else {
"".to_string()
}
}
}
fn get_friendly_name(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA) -> String
{
unsafe {
let mut buffer:[u8;MAX_PATH]=zeroed();
if SetupDiGetDeviceRegistryPropertyW(h_dev_info, device_info_data, SPDRP_FRIENDLYNAME, null_mut(), &mut buffer as _, (size_of::<u8>() * MAX_PATH) as _, null_mut()) != 0 {
ansi_to_string(&buffer)
} else {
"".to_string()
}
}
}
fn is_removable(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA)->bool
{
unsafe {
let mut result=0_u8;
SetupDiGetDeviceRegistryPropertyW(h_dev_info, device_info_data, SPDRP_REMOVAL_POLICY, null_mut(), &mut result as _, size_of::<u32>() as _, null_mut());
match result as u32
{
CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL|CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL =>true,
_=>false
}
}
}
fn is_usb_drive(enumerator_name:&str) -> bool
{
["USBSTOR", "UASPSTOR", "VUSBSTOR","RTUSER", "CMIUCR", "EUCR","ETRONSTOR", "ASUSSTPT"].contains(&enumerator_name)
}
fn ansi_to_string(unsafe_utf8:&[u8])->String
{
match from_utf8(&unsafe_utf8.iter().filter(|c| **c != 0).map(|c| *c).collect::<Vec<u8>>() as _)
{
Err(err)=>{
println!("Error {}",err);
"".to_string()
},
Ok(res)=>res.trim().to_string()
}
}
fn main() {
unsafe {
let h_device_info=SetupDiGetClassDevsA(&GUID_DEVINTERFACE_DISK, null_mut(), null_mut(), DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if h_device_info!=INVALID_HANDLE_VALUE {
let mut i=0;
let mut device_info_data:SP_DEVINFO_DATA=zeroed();
device_info_data.cbSize=size_of::<SP_DEVINFO_DATA>() as _;
while SetupDiEnumDeviceInfo(h_device_info, i, &mut device_info_data)!=0
{
let enumerator_name=get_enumerator_name(h_device_info, &mut device_info_data);
let friendly_name=get_friendly_name(h_device_info, &mut device_info_data);
if friendly_name.is_empty() {
continue;
}
println!("Name: {}",friendly_name);
println!("Is USB drive: {}",is_usb_drive(&enumerator_name));
println!("Is removable: {}",is_removable(h_device_info, &mut device_info_data));
get_detail_data(h_device_info, &mut device_info_data);
i+=1;
}
}
SetupDiDestroyDeviceInfoList(h_device_info);
}
}
Terminal output:
Name: SKHynix_HFM512GDHTNI-87A0B
Is USB drive: false
Is removable: false
DevicePath: \\?\scsi#disk&ven_nvme&prod_skhynix_hfm512gd#5&8980ef4&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
Bytes returned: 0
get_device_number fail. Error 1
Everything went fine until DeviceIoControl
always returns 0 and GetLastError() returns 1 (inside get_device_number()
function). Can someone guide me figuring out what went wrong?
Thanks in advance