use std::ffi::c_void;
use std::os::windows::io::{FromRawHandle, OwnedHandle};
use std::ptr;
use windows_sys::Win32::Storage::FileSystem::{
ExtendedFileIdType, FILE_FLAGS_AND_ATTRIBUTES, FILE_ID_128, FILE_ID_DESCRIPTOR,
FILE_ID_DESCRIPTOR_0, FILE_ID_TYPE, FILE_SHARE_MODE, FileIdType, ObjectIdType, OpenFileById,
};
use windows_sys::core::GUID;
use crate::handle::{CapturedHandle, HandleCaptureError};
use crate::outcome::{Outcome, perform_handle};
use crate::security::SecurityAttributes;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum FileIdentifier {
FileId(u64),
ObjectId(u128),
ExtendedFileId([u8; 16]),
}
impl FileIdentifier {
#[must_use]
pub fn id_type(self) -> FILE_ID_TYPE {
match self {
Self::FileId(_) => FileIdType,
Self::ObjectId(_) => ObjectIdType,
Self::ExtendedFileId(_) => ExtendedFileIdType,
}
}
fn to_descriptor(self) -> FILE_ID_DESCRIPTOR {
let anonymous = match self {
Self::FileId(id) => FILE_ID_DESCRIPTOR_0 {
FileId: id.cast_signed(),
},
Self::ObjectId(id) => FILE_ID_DESCRIPTOR_0 {
ObjectId: GUID::from_u128(id),
},
Self::ExtendedFileId(id) => FILE_ID_DESCRIPTOR_0 {
ExtendedFileId: FILE_ID_128 { Identifier: id },
},
};
FILE_ID_DESCRIPTOR {
dwSize: u32::try_from(size_of::<FILE_ID_DESCRIPTOR>())
.expect("this fixed, small struct's size always fits a u32"),
Type: self.id_type(),
Anonymous: anonymous,
}
}
}
#[derive(Debug)]
#[must_use = "an unperformed request opens nothing"]
pub struct OpenFileByIdentifier {
volume_hint: CapturedHandle,
identifier: FileIdentifier,
desired_access: u32,
share_mode: FILE_SHARE_MODE,
security: Option<SecurityAttributes>,
flags_and_attributes: FILE_FLAGS_AND_ATTRIBUTES,
}
impl OpenFileByIdentifier {
pub fn new(volume_hint: CapturedHandle, identifier: FileIdentifier) -> Self {
Self {
volume_hint,
identifier,
desired_access: 0,
share_mode: 0,
security: None,
flags_and_attributes: 0,
}
}
pub fn with_desired_access(mut self, desired_access: u32) -> Self {
self.desired_access = desired_access;
self
}
pub fn with_share_mode(mut self, share_mode: FILE_SHARE_MODE) -> Self {
self.share_mode = share_mode;
self
}
pub fn with_security(mut self, security: Option<SecurityAttributes>) -> Self {
self.security = security;
self
}
pub fn with_flags_and_attributes(
mut self,
flags_and_attributes: FILE_FLAGS_AND_ATTRIBUTES,
) -> Self {
self.flags_and_attributes = flags_and_attributes;
self
}
pub fn volume_hint(&self) -> &CapturedHandle {
&self.volume_hint
}
#[must_use]
pub fn identifier(&self) -> FileIdentifier {
self.identifier
}
#[must_use]
pub fn desired_access(&self) -> u32 {
self.desired_access
}
#[must_use]
pub fn share_mode(&self) -> FILE_SHARE_MODE {
self.share_mode
}
#[must_use]
pub fn security(&self) -> Option<&SecurityAttributes> {
self.security.as_ref()
}
#[must_use]
pub fn flags_and_attributes(&self) -> FILE_FLAGS_AND_ATTRIBUTES {
self.flags_and_attributes
}
pub fn try_clone(&self) -> Result<Self, HandleCaptureError> {
Ok(Self {
volume_hint: self.volume_hint.try_clone()?,
identifier: self.identifier,
desired_access: self.desired_access,
share_mode: self.share_mode,
security: self.security.clone(),
flags_and_attributes: self.flags_and_attributes,
})
}
pub fn perform(&self) -> Outcome<OwnedHandle> {
let descriptor = self.identifier.to_descriptor();
let attributes = self.security.as_ref().map(SecurityAttributes::to_raw);
let attributes_ptr = attributes.as_ref().map_or(ptr::null(), ptr::from_ref);
let raw = perform_handle(|| {
unsafe {
OpenFileById(
self.volume_hint.raw(),
&raw const descriptor,
self.desired_access,
self.share_mode,
attributes_ptr,
self.flags_and_attributes,
)
}
})?;
Ok(unsafe { OwnedHandle::from_raw_handle(raw.cast::<c_void>()) })
}
}
impl crate::request::Request for OpenFileByIdentifier {
type Error = crate::Win32Error;
type Output = OwnedHandle;
fn perform(&self) -> Outcome<OwnedHandle> {
Self::perform(self)
}
}
#[cfg(test)]
mod tests;