use std::cell::UnsafeCell;
use std::ffi::OsStr;
use std::ptr::NonNull;
use windows::core::Result;
use windows::core::HSTRING;
use windows::Win32::Foundation::NTSTATUS;
use winfsp_sys::{
FspFileSystemCreate, FspFileSystemRemoveMountPoint, FspFileSystemSetMountPoint,
FspFileSystemSetOperationGuardStrategyF, FspFileSystemStartDispatcher,
FspFileSystemStopDispatcher, 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,
};
use crate::filesystem::FileSystemContext;
use crate::host::interface::Interface;
use crate::host::{DebugMode, VolumeParams};
use crate::notify::NotifyingFileSystemContext;
use crate::notify::Timer;
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(NonNull<FSP_FILE_SYSTEM>, Option<Timer>);
impl FileSystemHost {
fn new_filesystem_inner<T: FileSystemContext>(
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 mut fsp_struct = std::ptr::null_mut();
let interface = if use_dir_info_by_name {
Interface::create_with_dirinfo_by_name::<T>()
} else {
Interface::create_with_read_directory::<T>()
};
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(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!"))
}
pub fn new<T: FileSystemContext>(volume_params: VolumeParams, context: T) -> Result<Self> {
Self::new_with_options::<T>(
FileSystemParams {
use_dir_info_by_name: false,
volume_params,
guard_strategy: OperationGuardStrategy::Fine,
debug_mode: DebugMode::none(),
},
context,
)
}
pub fn new_with_options<T: FileSystemContext>(
options: FileSystemParams,
context: T,
) -> Result<Self> {
let fsp_struct = Self::new_filesystem_inner(options, context)?;
Ok(FileSystemHost(fsp_struct, None))
}
#[cfg(feature = "notify")]
pub fn new_with_timer<
T: FileSystemContext + NotifyingFileSystemContext<R>,
R,
const INTERVAL: u32,
>(
options: FileSystemParams,
context: T,
) -> Result<Self> {
let fsp_struct = Self::new_filesystem_inner(options, context)?;
let timer = Timer::create::<R, T, INTERVAL>(fsp_struct);
Ok(FileSystemHost(fsp_struct, Some(timer)))
}
pub fn start(&mut self) -> Result<()> {
let result = unsafe { FspFileSystemStartDispatcher(self.0.as_ptr(), 0) };
let result = NTSTATUS(result);
result.ok()
}
pub fn stop(&mut self) {
unsafe { FspFileSystemStopDispatcher(self.0.as_ptr()) }
}
pub fn mount<S: AsRef<OsStr>>(&mut self, mount: S) -> Result<()> {
let mount = HSTRING::from(mount.as_ref());
let result =
unsafe { FspFileSystemSetMountPoint(self.0.as_ptr(), mount.as_ptr().cast_mut()) };
let result = NTSTATUS(result);
result.ok()
}
pub fn unmount(&mut self) {
unsafe { FspFileSystemRemoveMountPoint(self.0.as_ptr()) }
}
}