use std::cell::UnsafeCell;
use std::ffi::OsStr;
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::ptr::null_mut;
use windows::Win32::Foundation::NTSTATUS;
use windows::core::HSTRING;
use windows::core::Result;
use winfsp_sys::{
FSP_FILE_SYSTEM, FSP_FILE_SYSTEM_INTERFACE,
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE,
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE,
FspFileSystemCreate, FspFileSystemDelete, FspFileSystemRemoveMountPoint,
FspFileSystemSetMountPoint, FspFileSystemSetOperationGuardStrategyF,
FspFileSystemStartDispatcher, FspFileSystemStopDispatcher,
};
#[cfg(feature = "async-io")]
use crate::filesystem::AsyncFileSystemContext;
use crate::filesystem::FileSystemContext;
use crate::host::interface::{FileSystemUserContext, Interface};
use crate::host::{DebugMode, VolumeParams};
use crate::notify::NotifyingFileSystemContext;
use crate::notify::Timer;
pub enum MountPoint<'a> {
MountPoint(&'a OsStr),
NextFreeDrive,
}
impl<'a, S> From<&'a S> for MountPoint<'a>
where
S: AsRef<OsStr> + ?Sized,
{
fn from(s: &'a S) -> Self {
Self::MountPoint(s.as_ref())
}
}
impl<'short, 'long, 'middle> From<&'short MountPoint<'long>> for MountPoint<'middle>
where
'long: 'middle,
{
fn from(s: &'short MountPoint<'long>) -> Self {
match s {
MountPoint::MountPoint(v) => MountPoint::MountPoint(v),
MountPoint::NextFreeDrive => MountPoint::NextFreeDrive,
}
}
}
pub enum OperationGuardStrategy {
Fine,
Coarse,
}
pub struct FileSystemParams {
pub use_dir_info_by_name: bool,
pub volume_params: VolumeParams,
pub guard_strategy: OperationGuardStrategy,
pub debug_mode: DebugMode,
}
impl FileSystemParams {
pub fn default_params(volume_params: VolumeParams) -> Self {
Self {
use_dir_info_by_name: false,
volume_params,
guard_strategy: OperationGuardStrategy::Fine,
debug_mode: Default::default(),
}
}
pub fn default_params_debug(volume_params: VolumeParams, debug_mode: DebugMode) -> Self {
Self {
use_dir_info_by_name: false,
volume_params,
guard_strategy: OperationGuardStrategy::Fine,
debug_mode,
}
}
}
pub struct FileSystemHost<T: FileSystemContext> {
fsp_struct: NonNull<FSP_FILE_SYSTEM>,
#[allow(dead_code)]
timer: Option<Timer>,
phantom: PhantomData<T>,
}
#[cfg(feature = "async-io")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "async-io")))]
impl<T: FileSystemContext + AsyncFileSystemContext> FileSystemHost<T>
where
<T as FileSystemContext>::FileContext: Sync,
{
fn new_filesystem_inner_async(
options: FileSystemParams,
context: T,
) -> Result<NonNull<FSP_FILE_SYSTEM>> {
#[allow(unused_variables)]
let FileSystemParams {
use_dir_info_by_name,
volume_params,
guard_strategy,
debug_mode,
} = options;
let interface = if use_dir_info_by_name {
Interface::create_with_dirinfo_by_name_async::<T>()
} else {
Interface::create_with_read_directory_async::<T>()
};
Self::new_filesystem_inner_iface(
interface,
volume_params,
guard_strategy,
debug_mode,
context,
)
}
pub fn new_async(volume_params: VolumeParams, context: T) -> Result<Self> {
Self::new_with_options_async(
FileSystemParams {
use_dir_info_by_name: false,
volume_params,
guard_strategy: OperationGuardStrategy::Fine,
debug_mode: DebugMode::none(),
},
context,
)
}
pub fn new_with_options_async(options: FileSystemParams, context: T) -> Result<Self> {
let fsp_struct = Self::new_filesystem_inner_async(options, context)?;
Ok(FileSystemHost {
fsp_struct,
timer: None,
phantom: PhantomData,
})
}
#[cfg_attr(
feature = "docsrs",
doc(cfg(all(feature = "notify", feature = "async-io")))
)]
#[cfg(all(feature = "notify", feature = "async-io"))]
pub fn new_with_timer_async<R, const INTERVAL: u32>(
options: FileSystemParams,
context: T,
) -> Result<Self>
where
T: NotifyingFileSystemContext<R>,
{
let fsp_struct = Self::new_filesystem_inner_async(options, context)?;
let timer = Timer::create::<R, T, INTERVAL>(fsp_struct)?;
Ok(FileSystemHost {
fsp_struct,
timer: Some(timer),
phantom: PhantomData,
})
}
}
impl<T: FileSystemContext> FileSystemHost<T> {
#[allow(unused_variables)]
fn new_filesystem_inner_iface(
interface: Interface,
volume_params: VolumeParams,
guard_strategy: OperationGuardStrategy,
debug_mode: DebugMode,
context: T,
) -> Result<NonNull<FSP_FILE_SYSTEM>> {
let mut fsp_struct = std::ptr::null_mut();
let interface: FSP_FILE_SYSTEM_INTERFACE = interface.into();
let interface = Box::into_raw(Box::new(UnsafeCell::new(interface)));
let result = unsafe {
FspFileSystemCreate(
volume_params.get_winfsp_device_name(),
&volume_params.0,
interface.cast(),
&mut fsp_struct,
)
};
let result = NTSTATUS(result);
result.ok()?;
#[cfg(feature = "debug")]
unsafe {
use windows::Win32::System::Console::{GetStdHandle, STD_ERROR_HANDLE};
winfsp_sys::FspDebugLogSetHandle(
GetStdHandle(STD_ERROR_HANDLE).unwrap().0 as *mut std::ffi::c_void,
);
winfsp_sys::FspFileSystemSetDebugLogF(fsp_struct, debug_mode.into());
}
unsafe {
(*fsp_struct).UserContext = Box::into_raw(Box::new(UnsafeCell::new(
FileSystemUserContext::new(context),
))) as *mut _;
match guard_strategy {
OperationGuardStrategy::Fine => FspFileSystemSetOperationGuardStrategyF(fsp_struct, FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE),
OperationGuardStrategy::Coarse => FspFileSystemSetOperationGuardStrategyF(fsp_struct, FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE),
}
}
assert!(!fsp_struct.is_null());
Ok(NonNull::new(fsp_struct).expect("FSP_FILE_SYSTEM pointer was created but was null!"))
}
fn new_filesystem_inner(
options: FileSystemParams,
context: T,
) -> Result<NonNull<FSP_FILE_SYSTEM>> {
#[allow(unused_variables)]
let FileSystemParams {
use_dir_info_by_name,
volume_params,
guard_strategy,
debug_mode,
} = options;
let interface = if use_dir_info_by_name {
Interface::create_with_dirinfo_by_name::<T>()
} else {
Interface::create_with_read_directory::<T>()
};
Self::new_filesystem_inner_iface(
interface,
volume_params,
guard_strategy,
debug_mode,
context,
)
}
pub fn new(volume_params: VolumeParams, context: T) -> Result<Self> {
Self::new_with_options(
FileSystemParams {
use_dir_info_by_name: false,
volume_params,
guard_strategy: OperationGuardStrategy::Fine,
debug_mode: DebugMode::none(),
},
context,
)
}
pub fn new_with_options(options: FileSystemParams, context: T) -> Result<Self> {
let fsp_struct = Self::new_filesystem_inner(options, context)?;
Ok(FileSystemHost {
fsp_struct,
timer: None,
phantom: PhantomData,
})
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "notify")))]
#[cfg(feature = "notify")]
pub fn new_with_timer<R, const INTERVAL: u32>(
options: FileSystemParams,
context: T,
) -> Result<Self>
where
T: NotifyingFileSystemContext<R>,
{
let fsp_struct = Self::new_filesystem_inner(options, context)?;
let timer = Timer::create::<R, T, INTERVAL>(fsp_struct)?;
Ok(FileSystemHost {
fsp_struct,
timer: Some(timer),
phantom: PhantomData,
})
}
pub fn start(&mut self) -> Result<()> {
self.start_with_threads(0)
}
pub fn start_with_threads(&mut self, num_threads: u32) -> Result<()> {
let result = unsafe { FspFileSystemStartDispatcher(self.fsp_struct.as_ptr(), num_threads) };
let result = NTSTATUS(result);
result.ok()
}
pub fn stop(&mut self) {
unsafe { FspFileSystemStopDispatcher(self.fsp_struct.as_ptr()) }
}
pub fn mount<S>(&mut self, mount: S) -> Result<()>
where
for<'b> &'b S: Into<MountPoint<'b>>,
{
let mount_str: HSTRING;
let mount_ptr = match <&S as Into<MountPoint<'_>>>::into(&mount) {
MountPoint::MountPoint(mount) => {
mount_str = HSTRING::from(mount);
mount_str.as_ptr().cast_mut()
}
MountPoint::NextFreeDrive => null_mut(),
};
let result = unsafe { FspFileSystemSetMountPoint(self.fsp_struct.as_ptr(), mount_ptr) };
let result = NTSTATUS(result);
result.ok()
}
pub fn unmount(&mut self) {
unsafe { FspFileSystemRemoveMountPoint(self.fsp_struct.as_ptr()) }
}
}
impl<T: FileSystemContext> Drop for FileSystemHost<T> {
fn drop(&mut self) {
self.unmount();
self.stop();
unsafe {
let user_context = self.fsp_struct.as_ref().UserContext as *mut UnsafeCell<T>;
let interface = self.fsp_struct.as_ref().Interface as *mut UnsafeCell<Interface>;
FspFileSystemDelete(self.fsp_struct.as_ptr());
let user_context = Box::<UnsafeCell<T>>::from_raw(user_context);
drop(user_context);
let interface = Box::<UnsafeCell<Interface>>::from_raw(interface);
drop(interface);
};
}
}
unsafe impl<T: FileSystemContext + Send> Send for FileSystemHost<T> {}