use std::fmt;
use windows_sys::Win32::Storage::FileSystem::GetVolumeInformationByHandleW;
use wtf_string::Wtf16String;
use crate::handle::{CapturedHandle, HandleCaptureError};
use crate::outcome::{Outcome, perform_bool};
const LABEL_CAPACITY: usize = 261;
const FILESYSTEM_NAME_CAPACITY: usize = 261;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VolumeInformation {
label: Wtf16String,
serial_number: u32,
maximum_component_length: u32,
flags: u32,
filesystem_name: Wtf16String,
}
impl VolumeInformation {
#[must_use]
pub fn label(&self) -> &Wtf16String {
&self.label
}
#[must_use]
pub fn serial_number(&self) -> u32 {
self.serial_number
}
#[must_use]
pub fn maximum_component_length(&self) -> u32 {
self.maximum_component_length
}
#[must_use]
pub fn flags(&self) -> u32 {
self.flags
}
#[must_use]
pub fn filesystem_name(&self) -> &Wtf16String {
&self.filesystem_name
}
}
impl fmt::Display for VolumeInformation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} volume {:08X}",
self.filesystem_name.to_string_lossy(),
self.serial_number
)
}
}
#[derive(Debug)]
#[must_use = "an unperformed request queries nothing"]
pub struct QueryVolumeInformation {
handle: CapturedHandle,
}
impl QueryVolumeInformation {
pub fn new(handle: CapturedHandle) -> Self {
Self { handle }
}
pub fn handle(&self) -> &CapturedHandle {
&self.handle
}
pub fn try_clone(&self) -> Result<Self, HandleCaptureError> {
Ok(Self {
handle: self.handle.try_clone()?,
})
}
pub fn perform(&self) -> Outcome<VolumeInformation> {
let mut label = Wtf16String::with_capacity(LABEL_CAPACITY);
let mut filesystem_name = Wtf16String::with_capacity(FILESYSTEM_NAME_CAPACITY);
let mut serial_number = 0_u32;
let mut maximum_component_length = 0_u32;
let mut flags = 0_u32;
perform_bool(|| {
unsafe {
GetVolumeInformationByHandleW(
self.handle.raw(),
label.as_mut_ptr(),
u32::try_from(LABEL_CAPACITY).expect("a small constant fits a u32"),
&raw mut serial_number,
&raw mut maximum_component_length,
&raw mut flags,
filesystem_name.as_mut_ptr(),
u32::try_from(FILESYSTEM_NAME_CAPACITY).expect("a small constant fits a u32"),
)
}
})?;
unsafe {
set_len_to_terminator(&mut label, LABEL_CAPACITY);
set_len_to_terminator(&mut filesystem_name, FILESYSTEM_NAME_CAPACITY);
}
Ok(VolumeInformation {
label,
serial_number,
maximum_component_length,
flags,
filesystem_name,
})
}
}
unsafe fn set_len_to_terminator(buffer: &mut Wtf16String, capacity: usize) {
let base = buffer.as_mut_ptr();
let mut length = 0;
while length < capacity {
if unsafe { base.add(length).read() } == 0 {
break;
}
length += 1;
}
if length == capacity {
length = 0;
}
unsafe { buffer.set_len_from_ffi(length) };
}
impl crate::request::Request for QueryVolumeInformation {
type Error = crate::Win32Error;
type Output = VolumeInformation;
fn perform(&self) -> Outcome<VolumeInformation> {
Self::perform(self)
}
}
#[cfg(test)]
mod tests;