use std::io;
use std::marker::PhantomData;
use win_shared_os_owned_handle::SharedHandle;
use windows_namespace_request_sys::reopen::ReOpenFile;
use windows_sys::Win32::Storage::FileSystem::{
FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_OPEN_NO_RECALL, FILE_FLAG_OPEN_REPARSE_POINT,
FILE_FLAG_OVERLAPPED, FILE_FLAG_POSIX_SEMANTICS, 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_rights::FileRights;
use crate::windows_backend::share_mode::ShareMode;
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug)]
#[must_use]
pub struct ReopenOptions<A: DataAccess = NoData> {
access: PhantomData<A>,
rights: FileRights,
share: ShareMode,
open_reparse_point: bool,
delete_on_close: bool,
open_no_recall: bool,
posix_semantics: bool,
overlapped: bool,
random_access: bool,
sequential_scan: bool,
}
impl Default for ReopenOptions<NoData> {
fn default() -> Self {
Self::new()
}
}
impl ReopenOptions<NoData> {
pub fn new() -> Self {
Self {
access: PhantomData,
rights: FileRights::NONE,
share: ShareMode::ALL,
open_reparse_point: false,
delete_on_close: false,
open_no_recall: false,
posix_semantics: false,
overlapped: false,
random_access: false,
sequential_scan: false,
}
}
}
impl<A: DataAccess> ReopenOptions<A> {
pub fn read(self) -> ReopenOptions<A::WithRead> {
self.with_access()
}
pub fn write(self) -> ReopenOptions<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 delete_on_close(mut self, delete_on_close: bool) -> Self {
self.delete_on_close = delete_on_close;
self
}
pub fn open_no_recall(mut self, open_no_recall: bool) -> Self {
self.open_no_recall = open_no_recall;
self
}
pub fn posix_semantics(mut self, posix_semantics: bool) -> Self {
self.posix_semantics = posix_semantics;
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) -> ReopenOptions<A2> {
ReopenOptions {
access: PhantomData,
rights: self.rights,
share: self.share,
open_reparse_point: self.open_reparse_point,
delete_on_close: self.delete_on_close,
open_no_recall: self.open_no_recall,
posix_semantics: self.posix_semantics,
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.delete_on_close {
flags |= FILE_FLAG_DELETE_ON_CLOSE;
}
if self.open_no_recall {
flags |= FILE_FLAG_OPEN_NO_RECALL;
}
if self.posix_semantics {
flags |= FILE_FLAG_POSIX_SEMANTICS;
}
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(Clone, Debug)]
#[must_use = "an unperformed request reopens nothing"]
pub struct ReopenRequest<B: Buffering = Cached, A: DataAccess = ReadWrite> {
request: ReOpenFile,
rights: FileRights,
share: ShareMode,
mode: PhantomData<fn() -> (B, A)>,
}
impl<B: Buffering, A: DataAccess> ReopenRequest<B, A> {
#[must_use]
pub fn original(&self) -> &SharedHandle {
self.request.original()
}
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::Reopen,
data_access::<A>(),
self.rights,
self.share,
)
})?;
OpenedFile::admit(SharedHandle::from(handle))
}
}
impl<B: Buffering, A: DataAccess> OpenedFile<B, A> {
pub fn reopen(
original: impl Into<SharedHandle>,
options: ReopenOptions<A>,
) -> io::Result<Self> {
Self::request_reopen(original, options).perform()
}
pub fn request_reopen(
original: impl Into<SharedHandle>,
options: ReopenOptions<A>,
) -> ReopenRequest<B, A> {
let request = ReOpenFile::new(original)
.with_desired_access(options.access())
.with_share_mode(options.share.bits())
.with_flags_and_attributes(options.flags(B::FLAGS));
ReopenRequest {
request,
rights: options.rights,
share: options.share,
mode: PhantomData,
}
}
}