use std::fmt;
use std::io;
use std::marker::PhantomData;
use win_shared_os_owned_handle::SharedHandle;
use windows_namespace_request_sys::open_by_id::OpenFileByIdentifier;
use windows_sys::Win32::Storage::FileSystem::{
FILE_FLAG_OPEN_NO_RECALL, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_OVERLAPPED,
FILE_FLAG_RANDOM_ACCESS, FILE_FLAG_SEQUENTIAL_SCAN,
};
use durable_ioring::{DataAccess, NoData, ReadWrite};
use super::{Buffering, Cached, OpenedBy, OpenedFile, data_access, data_rights, refine_sharing_by};
use crate::windows_backend::file_id::{FileId, FileIdentifier};
use crate::windows_backend::file_rights::FileRights;
use crate::windows_backend::share_mode::ShareMode;
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug)]
#[must_use]
pub struct ByIdOptions<A: DataAccess = NoData> {
access: PhantomData<A>,
rights: FileRights,
share: ShareMode,
open_reparse_point: bool,
open_no_recall: bool,
overlapped: bool,
random_access: bool,
sequential_scan: bool,
}
impl Default for ByIdOptions<NoData> {
fn default() -> Self {
Self::new()
}
}
impl ByIdOptions<NoData> {
pub fn new() -> Self {
Self {
access: PhantomData,
rights: FileRights::NONE,
share: ShareMode::ALL,
open_reparse_point: false,
open_no_recall: false,
overlapped: false,
random_access: false,
sequential_scan: false,
}
}
}
impl<A: DataAccess> ByIdOptions<A> {
pub fn read(self) -> ByIdOptions<A::WithRead> {
self.with_access()
}
pub fn write(self) -> ByIdOptions<A::WithWrite> {
self.with_access()
}
pub fn rights(mut self, rights: FileRights) -> Self {
self.rights = rights;
self
}
pub fn share(mut self, share: ShareMode) -> Self {
self.share = share;
self
}
pub fn open_reparse_point(mut self, open_reparse_point: bool) -> Self {
self.open_reparse_point = open_reparse_point;
self
}
pub fn open_no_recall(mut self, open_no_recall: bool) -> Self {
self.open_no_recall = open_no_recall;
self
}
pub fn overlapped(mut self, overlapped: bool) -> Self {
self.overlapped = overlapped;
self
}
pub fn random_access(mut self, random_access: bool) -> Self {
self.random_access = random_access;
self
}
pub fn sequential_scan(mut self, sequential_scan: bool) -> Self {
self.sequential_scan = sequential_scan;
self
}
fn with_access<A2: DataAccess>(self) -> ByIdOptions<A2> {
ByIdOptions {
access: PhantomData,
rights: self.rights,
share: self.share,
open_reparse_point: self.open_reparse_point,
open_no_recall: self.open_no_recall,
overlapped: self.overlapped,
random_access: self.random_access,
sequential_scan: self.sequential_scan,
}
}
fn access(self) -> u32 {
data_rights::<A>() | self.rights.bits()
}
fn flags(self, mode: u32) -> u32 {
let mut flags = mode;
if self.open_reparse_point {
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
}
if self.open_no_recall {
flags |= FILE_FLAG_OPEN_NO_RECALL;
}
if self.overlapped {
flags |= FILE_FLAG_OVERLAPPED;
}
if self.random_access {
flags |= FILE_FLAG_RANDOM_ACCESS;
}
if self.sequential_scan {
flags |= FILE_FLAG_SEQUENTIAL_SCAN;
}
flags
}
}
#[derive(Debug)]
#[must_use = "an unperformed request opens nothing"]
pub struct ByIdRequest<B: Buffering = Cached, A: DataAccess = ReadWrite> {
request: OpenFileByIdentifier,
identifier: FileIdentifier,
rights: FileRights,
share: ShareMode,
mode: PhantomData<fn() -> (B, A)>,
}
impl<B: Buffering, A: DataAccess> ByIdRequest<B, A> {
#[must_use]
pub fn identifier(&self) -> FileIdentifier {
self.identifier
}
#[must_use]
pub fn volume_hint(&self) -> &SharedHandle {
self.request.volume_hint()
}
pub fn perform(&self) -> io::Result<OpenedFile<B, A>> {
let handle = self.request.perform().map_err(|error| {
refine_sharing_by(
error.to_io_error(),
|| OpenedBy::Identifier(self.identifier),
data_access::<A>(),
self.rights,
self.share,
)
})?;
let handle = SharedHandle::from(handle);
if let FileIdentifier::Extended(asked) = self.identifier {
let opened = FileId::of(&handle)?;
if opened != asked {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
IdentityMismatch { asked, opened },
));
}
}
OpenedFile::admit(handle)
}
}
impl<B: Buffering, A: DataAccess> OpenedFile<B, A> {
pub fn open_by_id(
volume_hint: impl Into<SharedHandle>,
identifier: impl Into<FileIdentifier>,
options: ByIdOptions<A>,
) -> io::Result<Self> {
Self::request_by_id(volume_hint, identifier, options).perform()
}
pub fn request_by_id(
volume_hint: impl Into<SharedHandle>,
identifier: impl Into<FileIdentifier>,
options: ByIdOptions<A>,
) -> ByIdRequest<B, A> {
let identifier = identifier.into();
let request = OpenFileByIdentifier::new(volume_hint, identifier.to_request())
.with_desired_access(options.access())
.with_share_mode(options.share.bits())
.with_flags_and_attributes(options.flags(B::FLAGS));
ByIdRequest {
request,
identifier,
rights: options.rights,
share: options.share,
mode: PhantomData,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct IdentityMismatch {
pub asked: FileId,
pub opened: FileId,
}
impl fmt::Display for IdentityMismatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"the open by identifier asked for {:?} and opened {:?}: the volume hint is not on the \
volume the identity names",
self.asked, self.opened
)
}
}
impl std::error::Error for IdentityMismatch {}