use std::iter;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use windows_sys::Win32::Storage::FileSystem::{GetVolumeInformationW, GetVolumePathNameW};
use crate::error::Result;
use super::{case_sensitive_by_name, classify_by_name, classify_io_error, FilesystemCapabilities};
pub(super) fn probe(path: &Path) -> Result<FilesystemCapabilities> {
let wide_path = to_wide(path);
let mut root_buf = [0u16; 261];
let resolved = unsafe {
GetVolumePathNameW(
wide_path.as_ptr(),
root_buf.as_mut_ptr(),
root_buf.len() as u32,
)
};
if resolved == 0 {
return Err(classify_io_error(
std::io::Error::last_os_error(),
path.to_path_buf(),
));
}
let mut fs_name_buf = [0u16; 261];
let mut max_component_len: u32 = 0;
let mut flags: u32 = 0;
let ok = unsafe {
GetVolumeInformationW(
root_buf.as_ptr(),
std::ptr::null_mut(),
0,
std::ptr::null_mut(),
&mut max_component_len,
&mut flags,
fs_name_buf.as_mut_ptr(),
fs_name_buf.len() as u32,
)
};
if ok == 0 {
return Err(classify_io_error(
std::io::Error::last_os_error(),
path.to_path_buf(),
));
}
let name = from_wide(&fs_name_buf).to_lowercase();
let case_sensitive = case_sensitive_by_name(&name);
let (max_file_size, windows_naming_rules, timestamp_granularity, write_integrity_risk) =
classify_by_name(&name, false);
Ok(FilesystemCapabilities {
name,
case_sensitive,
max_file_size,
windows_naming_rules,
timestamp_granularity,
write_integrity_risk,
})
}
fn to_wide(path: &Path) -> Vec<u16> {
path.as_os_str()
.encode_wide()
.chain(iter::once(0))
.collect()
}
fn from_wide(buf: &[u16]) -> String {
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
String::from_utf16_lossy(&buf[..len])
}