From b14f9adabda2a8375ba7ecc9e5ed3f0d241c923f Mon Sep 17 00:00:00 2001 From: Emmanuel Leblond Date: Tue, 6 Aug 2024 18:43:34 +0200 Subject: [PATCH 1/5] Add missing pub structs exposed in lib.rs --- winfsp_wrs/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/winfsp_wrs/src/lib.rs b/winfsp_wrs/src/lib.rs index b84d5f8..9b31eda 100644 --- a/winfsp_wrs/src/lib.rs +++ b/winfsp_wrs/src/lib.rs @@ -12,14 +12,15 @@ pub use callback::{FileContextKind, FileSystemContext}; #[cfg(feature = "icon")] pub use file_system::set_folder_icon; pub use file_system::{ - pin_to_quick_access, unpin_to_quick_access, FileContextMode, FileSystem, Params, VolumeParams, + pin_to_quick_access, unpin_to_quick_access, FileContextMode, FileSystem, + OperationGuardStrategy, Params, VolumeParams, }; pub use filetime::{filetime_from_utc, filetime_now}; pub use flags::{ CleanupFlags, CreateOptions, FileAccessRights, FileAttributes, FileCreationDisposition, FileShareMode, }; -pub use info::{CreateFileInfo, DirInfo, FileInfo, VolumeInfo, WriteMode}; +pub use info::{CreateFileInfo, DirInfo, FileInfo, VolumeInfo, VolumeLabelNameTooLong, WriteMode}; pub use init::{init, InitError}; pub use security::{PSecurityDescriptor, SecurityDescriptor}; From f5043809236b94ea69472727293317cadb3c6d3f Mon Sep 17 00:00:00 2001 From: Emmanuel Leblond Date: Wed, 7 Aug 2024 04:12:37 +0200 Subject: [PATCH 2/5] Fix incorrect cfg! target_arch for x86 --- winfsp_wrs/src/init.rs | 2 +- winfsp_wrs_build/src/lib.rs | 2 +- winfsp_wrs_sys/build.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/winfsp_wrs/src/init.rs b/winfsp_wrs/src/init.rs index ba07620..d2cc4a8 100644 --- a/winfsp_wrs/src/init.rs +++ b/winfsp_wrs/src/init.rs @@ -51,7 +51,7 @@ fn get_lplibfilename() -> Result { if cfg!(target_arch = "x86_64") { path.push("winfsp-x64.dll"); - } else if cfg!(target_arch = "i686") { + } else if cfg!(target_arch = "x86") { path.push("winfsp-x86.dll"); } else if cfg!(target_arch = "aarch64") { path.push("winfsp-a64.dll") diff --git a/winfsp_wrs_build/src/lib.rs b/winfsp_wrs_build/src/lib.rs index 4cd8e0a..7f10387 100644 --- a/winfsp_wrs_build/src/lib.rs +++ b/winfsp_wrs_build/src/lib.rs @@ -11,7 +11,7 @@ pub fn build() { if cfg!(target_arch = "x86_64") { println!("cargo:rustc-link-lib=dylib=delayimp"); println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x64.dll"); - } else if cfg!(target_arch = "i686") { + } else if cfg!(target_arch = "x86") { println!("cargo:rustc-link-lib=dylib=delayimp"); println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x86.dll"); } else if cfg!(target_arch = "aarch64") { diff --git a/winfsp_wrs_sys/build.rs b/winfsp_wrs_sys/build.rs index 1b97695..4fc6673 100644 --- a/winfsp_wrs_sys/build.rs +++ b/winfsp_wrs_sys/build.rs @@ -26,7 +26,7 @@ fn main() { if cfg!(all(target_os = "windows", target_env = "msvc")) { if cfg!(target_arch = "x86_64") { println!("cargo:rustc-link-lib=dylib=winfsp-x64"); - } else if cfg!(target_arch = "i686") { + } else if cfg!(target_arch = "x86") { println!("cargo:rustc-link-lib=dylib=winfsp-x86"); } else if cfg!(target_arch = "aarch64") { println!("cargo:rustc-link-lib=dylib=winfsp-a64"); From e73325aeea7b97042539583b0f388e9a7b55e211 Mon Sep 17 00:00:00 2001 From: Emmanuel Leblond Date: Wed, 7 Aug 2024 04:47:35 +0200 Subject: [PATCH 3/5] Fix clippy smells --- winfsp_wrs/src/callback.rs | 8 ++++---- winfsp_wrs/src/file_system.rs | 31 +++++++++++++++++++------------ winfsp_wrs/src/security.rs | 5 ++++- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/winfsp_wrs/src/callback.rs b/winfsp_wrs/src/callback.rs index f8ea2a0..e027085 100644 --- a/winfsp_wrs/src/callback.rs +++ b/winfsp_wrs/src/callback.rs @@ -99,10 +99,10 @@ pub trait FileSystemContext { /// - reparse (false if `reparse_point` is not supported) /// /// [help]: - /// - find_reparse_point (optional, can be ignored): helper to find reparse - /// points (`get_reparse_point_by_name` should be implemented) - /// if reparse point is found, return the `FileAttributes` and `reparse` should be - /// set to `true`. + /// - find_reparse_point (optional, can be ignored): Helper to find reparse + /// points (`get_reparse_point_by_name` should be implemented). + /// If reparse point is found, return the `FileAttributes` and `reparse` should be + /// set to `true`. fn get_security_by_name( &self, file_name: &U16CStr, diff --git a/winfsp_wrs/src/file_system.rs b/winfsp_wrs/src/file_system.rs index 0e2ef33..3e270fc 100644 --- a/winfsp_wrs/src/file_system.rs +++ b/winfsp_wrs/src/file_system.rs @@ -31,13 +31,15 @@ use crate::{FileAccessRights, FileAttributes, FileCreationDisposition, FileShare pub enum OperationGuardStrategy { #[default] /// A fine-grained concurrency model where file system NAMESPACE accesses are - /// guarded using an exclusive-shared (read-write) lock. File I/O is not - /// guarded and concurrent reads/writes/etc. are possible. [Note that the FSD - /// will still apply an exclusive-shared lock PER INDIVIDUAL FILE, but it will - /// not limit I/O operations for different files.] The fine-grained concurrency - /// model applies the exclusive-shared lock as follows: + /// guarded using an exclusive-shared (read-write) lock. + /// + /// File I/O is not guarded and concurrent reads/writes/etc. are possible. (Note + /// that the FSD will still apply an exclusive-shared lock PER INDIVIDUAL FILE, + /// but it will not limit I/O operations for different files.) + /// + /// The fine-grained concurrency model applies the exclusive-shared lock as follows: /// - EXCL: SetVolumeLabel, Flush(Volume), Create, Cleanup(Delete), - /// SetInformation(Rename) + /// SetInformation(Rename) /// - SHRD: GetVolumeInfo, Open, SetInformation(Disposition), ReadDirectory /// - NONE: all other operations Fine = FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE, @@ -336,12 +338,14 @@ impl FileSystem { &mut self.params.volume_params } - /// - Create a file system object. - /// - Set file system locking strategy. - /// - Set the mount point for a file system. - /// A value of None means that the file system should use the next available - /// drive letter counting downwards from Z: as its mount point. - /// - Start the file system dispatcher. + /// Start the mountpoint, i.e.: + /// - Create a file system object (`FspFileSystemCreate`). + /// - Set file system locking strategy (`FspFileSystemSetOperationGuardStrategyF`). + /// - Set the mount point for a file system (`FspFileSystemSetMountPoint`). + /// - Start the file system dispatcher (`FspFileSystemStartDispatcher`). + /// + /// A value of `None` for `mountpoint` means that the file system should use + /// the next available drive letter counting downwards from `Z:`. pub fn new( mut params: Params, mountpoint: Option<&U16CStr>, @@ -476,6 +480,9 @@ impl FileSystem { } } + /// Stop the mountpoint, i.e.: + /// - Stop the file system dispatcher (`FspFileSystemStopDispatcher`). + /// - Remove the mount point for the file system (`FspFileSystemRemoveMountPoint`). pub fn stop(mut self) { unsafe { FspFileSystemStopDispatcher(&mut self.inner); diff --git a/winfsp_wrs/src/security.rs b/winfsp_wrs/src/security.rs index 26e56ca..4d96161 100644 --- a/winfsp_wrs/src/security.rs +++ b/winfsp_wrs/src/security.rs @@ -135,7 +135,10 @@ impl SecurityDescriptor { // Free psd FspDeleteSecurityDescriptor( psd, - Some(std::mem::transmute(FspSetSecurityDescriptor as *const ())), + Some(std::mem::transmute::< + *const (), + unsafe extern "C" fn() -> NTSTATUS, + >(FspSetSecurityDescriptor as *const ())), ); Ok(sd) From 12eeff01a934427eb5e02764baee3c461ff19a00 Mon Sep 17 00:00:00 2001 From: Emmanuel Leblond Date: Wed, 7 Aug 2024 09:16:25 +0200 Subject: [PATCH 4/5] Allow compilation of `winfsp_wrs_sys` on non-Windows platforms Compilation on non-Windows platform will obviously fail (as WinFSP is only available on Windows). However keeping the Rust part platform agnostic is still useful given it allows linter & IDE to work correctly when groking into the code from a non-Windows machine. --- winfsp_wrs_sys/build.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/winfsp_wrs_sys/build.rs b/winfsp_wrs_sys/build.rs index 4fc6673..2b14a87 100644 --- a/winfsp_wrs_sys/build.rs +++ b/winfsp_wrs_sys/build.rs @@ -1,22 +1,18 @@ -use std::path::PathBuf; - -fn get_winfsp_install_dir() -> PathBuf { +#[cfg(windows)] +fn get_winfsp_install_dir() -> std::path::PathBuf { let winfsp_install = registry::Hive::LocalMachine .open("SOFTWARE\\WOW6432Node\\WinFsp", registry::Security::Read) .ok() .and_then(|u| u.value("InstallDir").ok()) .expect("WinFsp installation directory not found."); match winfsp_install { - registry::Data::String(path) => PathBuf::from(path.to_os_string()), + registry::Data::String(path) => std::path::PathBuf::from(path.to_os_string()), _ => panic!("unexpected install directory"), } } +#[cfg(windows)] fn main() { - if !cfg!(windows) { - panic!("WinFSP is only supported on Windows."); - } - let winfsp_install_dir = get_winfsp_install_dir(); println!( "cargo:rustc-link-search={}/lib", @@ -37,3 +33,10 @@ fn main() { panic!("unsupported triple {}", std::env::var("TARGET").unwrap()) }; } + +// Compilation on non-Windows platform will obviously fail (as WinFSP is only available +// on Windows). +// However keeping the Rust part platform agnostic is still useful given it allows +// linter & IDE to work correctly when groking into the code from a non-Windows machine. +#[cfg(not(windows))] +fn main() {} From 3ed8240d3ecfa7e3a5449fb196103f5a9499534f Mon Sep 17 00:00:00 2001 From: Emmanuel Leblond Date: Wed, 7 Aug 2024 09:22:41 +0200 Subject: [PATCH 5/5] Rework FileSystemContext - Rename `FileSystemContext` to `FileSystemInterface` (better shows it is the high level equivalent of `FSP_FILE_SYSTEM_INTERFACE`) - Expose associated const boolean in the trait to explicitly indicate which function pointer in `FSP_FILE_SYSTEM_INTERFACE` should be set - Remove default implementations of trait methods that was somewhat usable as-is (i.e. could be passed to WinFSP as function pointer, but created unexpected behavior given NULL function pointer != function pointer returning `Err(STATUS_NOT_IMPLEMENTED)`) - Fix handling of empty buffer converted to to slice (due to `std::slice::from_raw_parts_mut` requesting non-NULL buffer) --- examples/memfs/src/main.rs | 32 +- examples/minimal/src/main.rs | 9 +- winfsp_wrs/src/callback.rs | 567 ++++++++++++++++++++++++++-------- winfsp_wrs/src/file_system.rs | 10 +- winfsp_wrs/src/lib.rs | 4 +- 5 files changed, 475 insertions(+), 147 deletions(-) diff --git a/examples/memfs/src/main.rs b/examples/memfs/src/main.rs index a679259..0a118f0 100644 --- a/examples/memfs/src/main.rs +++ b/examples/memfs/src/main.rs @@ -6,11 +6,11 @@ use std::{ }; use winfsp_wrs::{ filetime_now, u16cstr, u16str, CleanupFlags, CreateFileInfo, CreateOptions, DirInfo, - FileAccessRights, FileAttributes, FileInfo, FileSystem, FileSystemContext, PSecurityDescriptor, - Params, SecurityDescriptor, U16CStr, U16CString, U16Str, VolumeInfo, VolumeParams, WriteMode, - NTSTATUS, STATUS_ACCESS_DENIED, STATUS_DIRECTORY_NOT_EMPTY, STATUS_END_OF_FILE, - STATUS_MEDIA_WRITE_PROTECTED, STATUS_NOT_A_DIRECTORY, STATUS_OBJECT_NAME_COLLISION, - STATUS_OBJECT_NAME_NOT_FOUND, + FileAccessRights, FileAttributes, FileInfo, FileSystem, FileSystemInterface, + PSecurityDescriptor, Params, SecurityDescriptor, U16CStr, U16CString, U16Str, VolumeInfo, + VolumeParams, WriteMode, NTSTATUS, STATUS_ACCESS_DENIED, STATUS_DIRECTORY_NOT_EMPTY, + STATUS_END_OF_FILE, STATUS_MEDIA_WRITE_PROTECTED, STATUS_NOT_A_DIRECTORY, + STATUS_OBJECT_NAME_COLLISION, STATUS_OBJECT_NAME_NOT_FOUND, }; macro_rules! debug { @@ -246,17 +246,17 @@ impl MemFs { } } -impl FileSystemContext for MemFs { +impl FileSystemInterface for MemFs { type FileContext = Arc>; - const SET_DELETE_DEFINED: bool = true; - + const GET_VOLUME_INFO_DEFINED: bool = true; fn get_volume_info(&self) -> Result { debug!("get_volume_info()"); Ok(self.volume_info.lock().unwrap().clone()) } + const SET_VOLUME_LABEL_DEFINED: bool = true; fn set_volume_label(&self, volume_label: &U16CStr) -> Result { debug!("set_volume_label(volume_label: {:?})", volume_label); @@ -269,6 +269,7 @@ impl FileSystemContext for MemFs { Ok(guard.clone()) } + const GET_SECURITY_BY_NAME_DEFINED: bool = true; fn get_security_by_name( &self, file_name: &U16CStr, @@ -298,6 +299,7 @@ impl FileSystemContext for MemFs { } } + const CREATE_EX_DEFINED: bool = true; fn create_ex( &self, file_name: &U16CStr, @@ -349,6 +351,7 @@ impl FileSystemContext for MemFs { Ok((file_context, file_info)) } + const OPEN_DEFINED: bool = true; fn open( &self, file_name: &U16CStr, @@ -372,6 +375,7 @@ impl FileSystemContext for MemFs { } } + const OVERWRITE_EX_DEFINED: bool = true; fn overwrite_ex( &self, file_context: Self::FileContext, @@ -416,6 +420,7 @@ impl FileSystemContext for MemFs { self.get_file_info_from_obj(&fc) } + const CLEANUP_DEFINED: bool = true; fn cleanup( &self, file_context: Self::FileContext, @@ -479,6 +484,7 @@ impl FileSystemContext for MemFs { } } + const READ_DEFINED: bool = true; fn read( &self, file_context: Self::FileContext, @@ -505,6 +511,7 @@ impl FileSystemContext for MemFs { } } + const WRITE_DEFINED: bool = true; fn write( &self, file_context: Self::FileContext, @@ -539,6 +546,7 @@ impl FileSystemContext for MemFs { Ok((written, self.get_file_info_from_obj(&fc)?)) } + const FLUSH_DEFINED: bool = true; fn flush(&self, file_context: Self::FileContext) -> Result { let fc = file_context.lock().unwrap(); debug!("[WinFSP] flush(file_context: {:?})", fc); @@ -546,6 +554,7 @@ impl FileSystemContext for MemFs { self.get_file_info_from_obj(&fc) } + const GET_FILE_INFO_DEFINED: bool = true; fn get_file_info(&self, file_context: Self::FileContext) -> Result { let fc = file_context.lock().unwrap(); debug!("[WinFSP] get_file_info(file_context: {:?})", fc); @@ -556,6 +565,7 @@ impl FileSystemContext for MemFs { } } + const SET_BASIC_INFO_DEFINED: bool = true; fn set_basic_info( &self, file_context: Self::FileContext, @@ -615,6 +625,7 @@ impl FileSystemContext for MemFs { self.get_file_info_from_obj(&fc) } + const SET_FILE_SIZE_DEFINED: bool = true; fn set_file_size( &self, file_context: Self::FileContext, @@ -647,6 +658,7 @@ impl FileSystemContext for MemFs { self.get_file_info_from_obj(&fc) } + const RENAME_DEFINED: bool = true; fn rename( &self, file_context: Self::FileContext, @@ -699,6 +711,7 @@ impl FileSystemContext for MemFs { Ok(()) } + const GET_SECURITY_DEFINED: bool = true; fn get_security( &self, file_context: Self::FileContext, @@ -712,6 +725,7 @@ impl FileSystemContext for MemFs { } } + const SET_SECURITY_DEFINED: bool = true; fn set_security( &self, file_context: Self::FileContext, @@ -743,6 +757,7 @@ impl FileSystemContext for MemFs { Ok(()) } + const READ_DIRECTORY_DEFINED: bool = true; fn read_directory( &self, file_context: Self::FileContext, @@ -808,6 +823,7 @@ impl FileSystemContext for MemFs { } } + const SET_DELETE_DEFINED: bool = true; fn set_delete( &self, file_context: Self::FileContext, diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 44215ab..0ecb897 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use winfsp_wrs::{ filetime_now, u16cstr, u16str, CreateOptions, DirInfo, FileAccessRights, FileAttributes, - FileInfo, FileSystem, FileSystemContext, PSecurityDescriptor, Params, SecurityDescriptor, + FileInfo, FileSystem, FileSystemInterface, PSecurityDescriptor, Params, SecurityDescriptor, U16CStr, U16Str, VolumeInfo, VolumeParams, NTSTATUS, }; @@ -48,9 +48,10 @@ impl MemFs { } } -impl FileSystemContext for MemFs { +impl FileSystemInterface for MemFs { type FileContext = Arc; + const GET_SECURITY_BY_NAME_DEFINED: bool = true; fn get_security_by_name( &self, _file_name: &U16CStr, @@ -63,6 +64,7 @@ impl FileSystemContext for MemFs { )) } + const OPEN_DEFINED: bool = true; fn open( &self, _file_name: &U16CStr, @@ -74,14 +76,17 @@ impl FileSystemContext for MemFs { Ok((file_context, file_info)) } + const GET_FILE_INFO_DEFINED: bool = true; fn get_file_info(&self, _file_context: Self::FileContext) -> Result { Ok(self.file_context.info) } + const GET_VOLUME_INFO_DEFINED: bool = true; fn get_volume_info(&self) -> Result { Ok(self.volume_info.clone()) } + const READ_DIRECTORY_DEFINED: bool = true; fn read_directory( &self, _file_context: Self::FileContext, diff --git a/winfsp_wrs/src/callback.rs b/winfsp_wrs/src/callback.rs index e027085..0b2c1ee 100644 --- a/winfsp_wrs/src/callback.rs +++ b/winfsp_wrs/src/callback.rs @@ -1,8 +1,6 @@ use std::sync::Arc; use widestring::U16CStr; -use windows_sys::Win32::Foundation::{ - STATUS_BUFFER_OVERFLOW, STATUS_NOT_IMPLEMENTED, STATUS_REPARSE, STATUS_SUCCESS, -}; +use windows_sys::Win32::Foundation::{STATUS_BUFFER_OVERFLOW, STATUS_REPARSE, STATUS_SUCCESS}; use winfsp_wrs_sys::{ FspFileSystemAddDirInfo, FspFileSystemFindReparsePoint, FspFileSystemResolveReparsePoints, FspFileSystemStopServiceIfNecessary, BOOLEAN, FSP_FILE_SYSTEM, FSP_FILE_SYSTEM_INTERFACE, @@ -76,19 +74,91 @@ impl FileContextKind for usize { } } -pub trait FileSystemContext { +/// High level interface over `FSP_FILE_SYSTEM_INTERFACE`. +/// +/// This trait requires to overwrite all WinFSP callbacks you need and it corresponding +/// `xxx_DEFINED` associated const boolean. +/// +/// This is needed to properly build the `FSP_FILE_SYSTEM_INTERFACE` struct, as +/// a callback pointer set to `NULL` (i.e. if `xxx_DEFINED=false`) leads to a different +/// behavior that a callback pointer containing a mock implementation (e.g. +/// returning `Err(STATUS_NOT_IMPLEMENTED)`). +/// +/// So the way to work with this trait is to overwrite the method and `xxx_DEFINED` for +/// each function pointer you will need in `FSP_FILE_SYSTEM_INTERFACE`: +/// +/// ```rust +/// struct MyFS; +/// impl FileSystemInterface for MyFS { +/// type FileContext: usize; +/// // `CREATE_DEFINED` not overwritten, hence `FSP_FILE_SYSTEM_INTERFACE.Create == NULL` +/// const CREATE_EX_DEFINED: bool = true; // i.e. `FSP_FILE_SYSTEM_INTERFACE.CreateEx != NULL` +/// fn create_ex( +/// &self, +/// file_name: &U16CStr, +/// create_file_info: CreateFileInfo, +/// security_descriptor: SecurityDescriptor, +/// buffer: &[u8], +/// extra_buffer_is_reparse_point: bool, +/// ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { +/// ... +/// } +/// } +/// ``` +/// +/// *Notes*: +/// - Associated method and `xxx_DEFINED` const must be overwritten together, as the +/// method is simply ignored if `xxx_DEFINED` is not set, and setting `xxx_DEFINED` +/// without overwritting the method means the function pointer relies on the method +/// default implementation that panics whenever used (ah !). +/// - If your are curious about the reason for using a trait here instead of a struct (or +/// associated const fields with `Option` type in the trait instead of methods), it +/// all boils down to the fact some methods have an `impl Fn` function pointer as argument, +/// which is only possible in trait method. +pub trait FileSystemInterface { type FileContext: FileContextKind; - /// `SetDelete` takes precedence over `CanDelete` if both are defined in `FSP_FILE_SYSTEM_INTERFACE`. - /// If this boolean is not set, `FSP_FILE_SYSTEM_INTERFACE`'s `SetDelete` function will not be set. + const GET_VOLUME_INFO_DEFINED: bool = false; + const SET_VOLUME_LABEL_DEFINED: bool = false; + const GET_SECURITY_BY_NAME_DEFINED: bool = false; + const CREATE_DEFINED: bool = false; + const CREATE_EX_DEFINED: bool = false; + const OPEN_DEFINED: bool = false; + const OVERWRITE_DEFINED: bool = false; + const OVERWRITE_EX_DEFINED: bool = false; + const CLEANUP_DEFINED: bool = false; + const CLOSE_DEFINED: bool = false; + const READ_DEFINED: bool = false; + const WRITE_DEFINED: bool = false; + const FLUSH_DEFINED: bool = false; + const GET_FILE_INFO_DEFINED: bool = false; + const SET_BASIC_INFO_DEFINED: bool = false; + const SET_FILE_SIZE_DEFINED: bool = false; + const CAN_DELETE_DEFINED: bool = false; + const RENAME_DEFINED: bool = false; + const GET_SECURITY_DEFINED: bool = false; + const SET_SECURITY_DEFINED: bool = false; + const READ_DIRECTORY_DEFINED: bool = false; + const GET_REPARSE_POINT_DEFINED: bool = false; + const SET_REPARSE_POINT_DEFINED: bool = false; + const DELETE_REPARSE_POINT_DEFINED: bool = false; + const GET_STREAM_INFO_DEFINED: bool = false; + const GET_DIR_INFO_BY_NAME_DEFINED: bool = false; + const CONTROL_DEFINED: bool = false; const SET_DELETE_DEFINED: bool = false; + const GET_EA_DEFINED: bool = false; + const SET_EA_DEFINED: bool = false; + const DISPATCHER_STOPPED_DEFINED: bool = false; + const RESOLVE_REPARSE_POINTS_DEFINED: bool = false; /// Get volume information. - fn get_volume_info(&self) -> Result; + fn get_volume_info(&self) -> Result { + unreachable!("To be used, trait method must be overwritten !"); + } /// Set volume label. fn set_volume_label(&self, _volume_label: &U16CStr) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Get file or directory attributes and security descriptor given a file name. @@ -105,11 +175,30 @@ pub trait FileSystemContext { /// set to `true`. fn get_security_by_name( &self, - file_name: &U16CStr, - find_reparse_point: impl Fn() -> Option, - ) -> Result<(FileAttributes, PSecurityDescriptor, bool), NTSTATUS>; + _file_name: &U16CStr, + _find_reparse_point: impl Fn() -> Option, + ) -> Result<(FileAttributes, PSecurityDescriptor, bool), NTSTATUS> { + unreachable!("To be used, trait method must be overwritten !"); + } + + /// Create new file or directory. + /// + /// Note: `FileSystemContext::create_ex` takes precedence over `FileSystemContext::create` + fn create( + &self, + _file_name: &U16CStr, + _create_file_info: CreateFileInfo, + _security_descriptor: SecurityDescriptor, + ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { + unreachable!("To be used, trait method must be overwritten !"); + } /// Create new file or directory. + /// + /// This function works like `create`, except that it also accepts an extra buffer + /// that may contain extended attributes or a reparse point. + /// + /// Note: `FileSystemContext::create_ex` takes precedence over `FileSystemContext::create` fn create_ex( &self, _file_name: &U16CStr, @@ -118,18 +207,37 @@ pub trait FileSystemContext { _buffer: &[u8], _extra_buffer_is_reparse_point: bool, ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Open a file or directory. fn open( &self, - file_name: &U16CStr, - create_options: CreateOptions, - granted_access: FileAccessRights, - ) -> Result<(Self::FileContext, FileInfo), NTSTATUS>; + _file_name: &U16CStr, + _create_options: CreateOptions, + _granted_access: FileAccessRights, + ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { + unreachable!("To be used, trait method must be overwritten !"); + } /// Overwrite a file. + /// + /// Note: `FileSystemContext::overwrite_ex` takes precedence over `FileSystemContext::overwrite` + fn overwrite( + &self, + _file_context: Self::FileContext, + _file_attributes: FileAttributes, + _replace_file_attributes: bool, + _allocation_size: u64, + ) -> Result { + unreachable!("To be used, trait method must be overwritten !"); + } + + /// Overwrite a file. + /// + /// This function works like `overwrite`, except that it also accepts EA (extended attributes). + /// + /// Note: `FileSystemContext::overwrite_ex` takes precedence over `FileSystemContext::overwrite` fn overwrite_ex( &self, _file_context: Self::FileContext, @@ -138,7 +246,7 @@ pub trait FileSystemContext { _allocation_size: u64, _buffer: &[u8], ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Cleanup a file. @@ -148,10 +256,13 @@ pub trait FileSystemContext { _file_name: Option<&U16CStr>, _flags: CleanupFlags, ) { + unreachable!("To be used, trait method must be overwritten !"); } /// Close a file. - fn close(&self, _file_context: Self::FileContext) {} + fn close(&self, _file_context: Self::FileContext) { + unreachable!("To be used, trait method must be overwritten !"); + } /// Read a file. fn read( @@ -160,7 +271,7 @@ pub trait FileSystemContext { _buffer: &mut [u8], _offset: u64, ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Write a file. @@ -170,16 +281,18 @@ pub trait FileSystemContext { _buffer: &[u8], _mode: WriteMode, ) -> Result<(usize, FileInfo), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Flush a file or volume. fn flush(&self, _file_context: Self::FileContext) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Get file or directory information. - fn get_file_info(&self, file_context: Self::FileContext) -> Result; + fn get_file_info(&self, _file_context: Self::FileContext) -> Result { + unreachable!("To be used, trait method must be overwritten !"); + } /// Set file or directory basic information. fn set_basic_info( @@ -191,7 +304,7 @@ pub trait FileSystemContext { _last_write_time: u64, _change_time: u64, ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Set file/allocation size. @@ -201,16 +314,18 @@ pub trait FileSystemContext { _new_size: u64, _set_allocation_size: bool, ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Determine whether a file or directory can be deleted. + /// + /// Note: `FileSystemContext::set_delete` takes precedence over `FileSystemContext::can_delete` fn can_delete( &self, _file_context: Self::FileContext, _file_name: &U16CStr, ) -> Result<(), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Renames a file or directory. @@ -221,7 +336,7 @@ pub trait FileSystemContext { _new_file_name: &U16CStr, _replace_if_exists: bool, ) -> Result<(), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Get file or directory security descriptor. @@ -229,7 +344,7 @@ pub trait FileSystemContext { &self, _file_context: Self::FileContext, ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Set file or directory security descriptor. @@ -239,7 +354,7 @@ pub trait FileSystemContext { _security_information: u32, _modification_descriptor: PSecurityDescriptor, ) -> Result<(), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Read a directory. @@ -247,10 +362,12 @@ pub trait FileSystemContext { /// `add_dir_info` returns `false` if there is no more space left to add elements. fn read_directory( &self, - file_context: Self::FileContext, - marker: Option<&U16CStr>, - add_dir_info: impl FnMut(DirInfo) -> bool, - ) -> Result<(), NTSTATUS>; + _file_context: Self::FileContext, + _marker: Option<&U16CStr>, + _add_dir_info: impl FnMut(DirInfo) -> bool, + ) -> Result<(), NTSTATUS> { + unreachable!("To be used, trait method must be overwritten !"); + } /// Get reparse point. fn get_reparse_point( @@ -259,7 +376,7 @@ pub trait FileSystemContext { _file_name: &U16CStr, _buffer: &mut [u8], ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Set reparse point. @@ -269,7 +386,7 @@ pub trait FileSystemContext { _file_name: &U16CStr, _buffer: &mut [u8], ) -> Result<(), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Delete reparse point. @@ -279,7 +396,7 @@ pub trait FileSystemContext { _file_name: &U16CStr, _buffer: &mut [u8], ) -> Result<(), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Get named streams information. @@ -288,7 +405,7 @@ pub trait FileSystemContext { _file_context: Self::FileContext, _buffer: &mut [u8], ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Get directory information for a single file or directory within a parent @@ -298,7 +415,7 @@ pub trait FileSystemContext { _file_context: Self::FileContext, _file_name: &U16CStr, ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Process control code. @@ -309,22 +426,24 @@ pub trait FileSystemContext { _input_buffer: &[u8], _output_buffer: &mut [u8], ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Set the file delete flag. + /// + /// Note: `FileSystemContext::set_delete` takes precedence over `FileSystemContext::can_delete` fn set_delete( &self, _file_context: Self::FileContext, _file_name: &U16CStr, _delete_file: bool, ) -> Result<(), NTSTATUS> { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Get extended attributes. fn get_ea(&self, _file_context: Self::FileContext, _buffer: &[u8]) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } /// Set extended attributes. @@ -333,30 +452,38 @@ pub trait FileSystemContext { _file_context: Self::FileContext, _buffer: &[u8], ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); + } + + fn dispatcher_stopped(&self, _normally: bool) { + unreachable!("To be used, trait method must be overwritten !"); } /// Get reparse point given a file name. + /// + /// This method is used as a callback parameter to `FspFileSystemFindReparsePoint` & + /// `FspFileSystemResolveReparsePoints` helpers to respectively implement + /// `FSP_FILE_SYSTEM_INTERFACE`'s `GetSecurityByName` & `ResolveReparsePoints`. fn get_reparse_point_by_name( &self, _file_name: &U16CStr, _is_directory: bool, _buffer: Option<&mut [u8]>, ) -> Result { - Err(STATUS_NOT_IMPLEMENTED) + unreachable!("To be used, trait method must be overwritten !"); } - - fn dispatcher_stopped(&self, _normally: bool) {} } -pub(crate) struct Interface; +/// `TrampolineInterface` fills the gap between the high level `FileSystemInterface` +/// and the `FSP_FILE_SYSTEM_INTERFACE` C struct that WinFSP expects from us. +pub(crate) struct TrampolineInterface; -impl Interface { +impl TrampolineInterface { /// Get volume information. /// - FileSystem - The file system on which this request is posted. /// - VolumeInfo - [out] Pointer to a structure that will receive the volume /// information on successful return from this call. - unsafe extern "C" fn get_volume_info_ext( + unsafe extern "C" fn get_volume_info_ext( file_system: *mut FSP_FILE_SYSTEM, volume_info: *mut FSP_FSCTL_VOLUME_INFO, ) -> NTSTATUS { @@ -376,7 +503,7 @@ impl Interface { /// - VolumeLabel - The new label for the volume. /// - VolumeInfo - [out] Pointer to a structure that will receive the volume /// information on successful return from this call. - unsafe extern "C" fn set_volume_label_w_ext( + unsafe extern "C" fn set_volume_label_w_ext( file_system: *mut FSP_FILE_SYSTEM, volume_label: PWSTR, volume_info: *mut FSP_FSCTL_VOLUME_INFO, @@ -413,7 +540,7 @@ impl Interface { /// Remarks: STATUS_REPARSE should be returned by file systems that support /// reparse points when they encounter a FileName that contains reparse points /// anywhere but the final path component. - unsafe extern "C" fn get_security_by_name_ext( + unsafe extern "C" fn get_security_by_name_ext( file_system: *mut FSP_FILE_SYSTEM, file_name: PWSTR, p_file_attributes: PUINT32, @@ -494,7 +621,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn open_ext( + unsafe extern "C" fn open_ext( file_system: *mut FSP_FILE_SYSTEM, file_name: PWSTR, create_options: UINT32, @@ -527,7 +654,7 @@ impl Interface { /// Delete is requested. /// - Flags - These flags determine whether the file was modified and whether /// to delete the file. - unsafe extern "C" fn cleanup_ext( + unsafe extern "C" fn cleanup_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -548,7 +675,7 @@ impl Interface { /// Close a file. /// - FileSystem - The file system on which this request is posted. /// - FileContext - The file context of the file or directory to be closed. - unsafe extern "C" fn close_ext( + unsafe extern "C" fn close_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, ) { @@ -566,7 +693,7 @@ impl Interface { /// - Length - Length of data to read. /// - PBytesTransferred - [out] Pointer to a memory location that will receive /// the actual number of bytes read. - unsafe extern "C" fn read_ext( + unsafe extern "C" fn read_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, buffer: PVOID, @@ -576,7 +703,11 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); - let buffer = std::slice::from_raw_parts_mut(buffer.cast(), length as usize); + let buffer = if !buffer.is_null() { + std::slice::from_raw_parts_mut(buffer.cast(), length as usize) + } else { + &mut [] + }; match C::read(fs, fctx, buffer, offset) { Ok(bytes_transferred) => { @@ -602,7 +733,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn write_ext( + unsafe extern "C" fn write_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, buffer: PVOID, @@ -615,7 +746,11 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); - let buffer = std::slice::from_raw_parts(buffer.cast(), length as usize); + let buffer = if !buffer.is_null() { + std::slice::from_raw_parts(buffer.cast(), length as usize) + } else { + &[] + }; let mode = match (write_to_end_of_file != 0, constrained_io != 0) { (false, false) => WriteMode::Normal { offset }, @@ -645,7 +780,7 @@ impl Interface { /// information on successful return from this call. This information /// includes file attributes, file times, etc. Used when flushing file (not /// volume). - unsafe extern "C" fn flush_ext( + unsafe extern "C" fn flush_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_info: *mut FSP_FSCTL_FILE_INFO, @@ -669,7 +804,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn get_file_info_ext( + unsafe extern "C" fn get_file_info_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_info: *mut FSP_FSCTL_FILE_INFO, @@ -704,7 +839,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn set_basic_info_ext( + unsafe extern "C" fn set_basic_info_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_attributes: UINT32, @@ -744,7 +879,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn set_file_size_ext( + unsafe extern "C" fn set_file_size_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, new_size: UINT64, @@ -770,7 +905,7 @@ impl Interface { /// Delete is requested. /// - Flags - These flags determine whether the file was modified and whether /// to delete the file. - unsafe extern "C" fn can_delete_ext( + unsafe extern "C" fn can_delete_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -792,7 +927,7 @@ impl Interface { /// - NewFileName - The new name for the file or directory. /// - ReplaceIfExists - Whether to replace a file that already exists at /// NewFileName. - unsafe extern "C" fn rename_ext( + unsafe extern "C" fn rename_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -820,7 +955,7 @@ impl Interface { /// buffer size. On input it contains the size of the security descriptor /// buffer. On output it will contain the actual size of the security /// descriptor copied into the security descriptor buffer. Cannot be NULL. - unsafe extern "C" fn get_security_ext( + unsafe extern "C" fn get_security_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, security_descriptor: PSECURITY_DESCRIPTOR, @@ -855,7 +990,7 @@ impl Interface { /// security descriptor should be modified. /// - ModificationDescriptor - Describes the modifications to apply to the file /// or directory security descriptor. - unsafe extern "C" fn set_security_ext( + unsafe extern "C" fn set_security_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, security_information: SECURITY_INFORMATION, @@ -887,7 +1022,7 @@ impl Interface { /// - Length - Length of data to read. /// - PBytesTransferred - [out] Pointer to a memory location that will receive /// the actual number of bytes read. - unsafe extern "C" fn read_directory_ext( + unsafe extern "C" fn read_directory_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, _pattern: PWSTR, @@ -936,7 +1071,7 @@ impl Interface { } } - unsafe extern "C" fn get_reparse_point_by_name_ext( + unsafe extern "C" fn get_reparse_point_by_name_ext( file_system: *mut FSP_FILE_SYSTEM, _context: PVOID, file_name: PWSTR, @@ -946,13 +1081,13 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let file_name = U16CStr::from_ptr_str_mut(file_name); - let buffer = if buffer.is_null() { - None - } else { + let buffer = if !buffer.is_null() { Some(std::slice::from_raw_parts_mut( buffer.cast(), psize.read() as usize, )) + } else { + None }; match C::get_reparse_point_by_name(fs, file_name, is_directory != 0, buffer) { @@ -982,7 +1117,7 @@ impl Interface { /// - PSize - [in,out] Pointer to the buffer size. On input it contains the /// size of the buffer. On output it will contain the actual size of data /// copied. - unsafe extern "C" fn resolve_reparse_points_ext( + unsafe extern "C" fn resolve_reparse_points_ext( file_system: *mut FSP_FILE_SYSTEM, file_name: PWSTR, reparse_point_index: UINT32, @@ -1014,7 +1149,7 @@ impl Interface { /// - PSize - [in,out] Pointer to the buffer size. On input it contains the /// size of the buffer. On output it will contain the actual size of data /// copied. - unsafe extern "C" fn get_reparse_point_ext( + unsafe extern "C" fn get_reparse_point_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -1024,7 +1159,11 @@ impl Interface { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); let file_name = U16CStr::from_ptr_str(file_name); - let buffer = std::slice::from_raw_parts_mut(buffer.cast(), *p_size as usize); + let buffer = if !buffer.is_null() { + std::slice::from_raw_parts_mut(buffer.cast(), *p_size as usize) + } else { + &mut [] + }; match C::get_reparse_point(fs, fctx, file_name, buffer) { Ok(byte_transferred) => { @@ -1043,7 +1182,7 @@ impl Interface { /// If this buffer contains a symbolic link path, it should not be assumed to /// be NULL terminated. /// - Size - Size of data to write. - unsafe extern "C" fn set_reparse_point_ext( + unsafe extern "C" fn set_reparse_point_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -1053,7 +1192,11 @@ impl Interface { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); let file_name = U16CStr::from_ptr_str(file_name); - let buffer = std::slice::from_raw_parts_mut(buffer.cast(), size as usize); + let buffer = if !buffer.is_null() { + std::slice::from_raw_parts_mut(buffer.cast(), size as usize) + } else { + &mut [] + }; match C::set_reparse_point(fs, fctx, file_name, buffer) { Ok(()) => STATUS_SUCCESS, @@ -1067,7 +1210,7 @@ impl Interface { /// - FileName - The file name of the reparse point. /// - Buffer - Pointer to a buffer that contains the data for this operation. /// - Size - Size of data to write. - unsafe extern "C" fn delete_reparse_point_ext( + unsafe extern "C" fn delete_reparse_point_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -1077,7 +1220,11 @@ impl Interface { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); let file_name = U16CStr::from_ptr_str(file_name); - let buffer = std::slice::from_raw_parts_mut(buffer.cast(), size as usize); + let buffer = if !buffer.is_null() { + std::slice::from_raw_parts_mut(buffer.cast(), size as usize) + } else { + &mut [] + }; match C::delete_reparse_point(fs, fctx, file_name, buffer) { Ok(()) => STATUS_SUCCESS, @@ -1093,7 +1240,7 @@ impl Interface { /// - Length - Length of buffer. /// - PBytesTransferred - [out] Pointer to a memory location that will receive /// the actual number of bytes stored. - unsafe extern "C" fn get_stream_info_ext( + unsafe extern "C" fn get_stream_info_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, buffer: PVOID, @@ -1102,7 +1249,11 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); - let buffer = std::slice::from_raw_parts_mut(buffer.cast(), length as usize); + let buffer = if !buffer.is_null() { + std::slice::from_raw_parts_mut(buffer.cast(), length as usize) + } else { + &mut [] + }; match C::get_stream_info(fs, fctx, buffer) { Ok(bytes_transferred) => { @@ -1122,7 +1273,7 @@ impl Interface { /// - DirInfo - [out] Pointer to a structure that will receive the directory /// information on successful return from this call. This information /// includes the file name, but also file attributes, file times, etc. - unsafe extern "C" fn get_dir_info_by_name_ext( + unsafe extern "C" fn get_dir_info_by_name_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -1160,7 +1311,7 @@ impl Interface { /// - OutputBufferLength - Output data length. /// - PBytesTransferred - [out] Pointer to a memory location that will receive /// the actual number of bytes transferred. - unsafe extern "C" fn control_ext( + unsafe extern "C" fn control_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, control_code: UINT32, @@ -1172,9 +1323,16 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); - let input = std::slice::from_raw_parts(input_buffer.cast(), input_buffer_length as usize); - let output = - std::slice::from_raw_parts_mut(output_buffer.cast(), output_buffer_length as usize); + let input = if !input_buffer.is_null() { + std::slice::from_raw_parts(input_buffer.cast(), input_buffer_length as usize) + } else { + &[] + }; + let output = if !output_buffer.is_null() { + std::slice::from_raw_parts_mut(output_buffer.cast(), output_buffer_length as usize) + } else { + &mut [] + }; match C::control(fs, fctx, control_code, input, output) { Ok(bytes_transferred) => { @@ -1194,7 +1352,7 @@ impl Interface { /// deleted on Cleanup; otherwise it will not be deleted. It is legal to /// receive multiple SetDelete calls for the same file with different /// DeleteFile parameters. - unsafe extern "C" fn set_delete_ext( + unsafe extern "C" fn set_delete_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_name: PWSTR, @@ -1210,6 +1368,69 @@ impl Interface { } } + /// Create new file or directory. + /// - FileSystem - The file system on which this request is posted. + /// - FileName - The name of the file or directory to be created. + /// - CreateOptions - Create options for this request. This parameter has the + /// same meaning as the CreateOptions parameter of the NtCreateFile API. User + /// mode file systems should typically only be concerned with the flag + /// FILE_DIRECTORY_FILE, which is an instruction to create a directory rather + /// than a file. Some file systems may also want to pay attention to the + /// FILE_NO_INTERMEDIATE_BUFFERING and FILE_WRITE_THROUGH flags, although + /// these are typically handled by the FSD component. + /// - GrantedAccess - Determines the specific access rights that have been + /// granted for this request. Upon receiving this call all access checks have + /// been performed and the user mode file system need not perform any + /// additional checks. However this parameter may be useful to a user mode + /// file system; for example the WinFsp-FUSE layer uses this parameter to + /// determine which flags to use in its POSIX open() call. + /// - FileAttributes - File attributes to apply to the newly created file or + /// directory. + /// - SecurityDescriptor - Security descriptor to apply to the newly created + /// file or directory. This security descriptor will always be in + /// self-relative format. Its length can be retrieved using the Windows + /// GetSecurityDescriptorLength API. Will be NULL for named streams. + /// - AllocationSize - Allocation size for the newly created file. + /// - PFileContext - [out] Pointer that will receive the file context on + /// successful return from this call. + /// - FileInfo - [out] Pointer to a structure that will receive the file + /// information on successful return from this call. This information + /// includes file attributes, file times, etc. + unsafe extern "C" fn create_ext( + file_system: *mut FSP_FILE_SYSTEM, + file_name: PWSTR, + create_options: UINT32, + granted_access: UINT32, + file_attributes: UINT32, + security_descriptor: PSECURITY_DESCRIPTOR, + allocation_size: UINT64, + p_file_context: *mut PVOID, + file_info: *mut FSP_FSCTL_FILE_INFO, + ) -> NTSTATUS { + let fs = &*(*file_system).UserContext.cast::(); + let file_name = U16CStr::from_ptr_str(file_name); + let sd = SecurityDescriptor::from_ptr(security_descriptor); + + match C::create( + fs, + file_name, + CreateFileInfo { + create_options: CreateOptions(create_options), + granted_access: FileAccessRights(granted_access), + file_attributes: FileAttributes(file_attributes), + allocation_size, + }, + sd, + ) { + Ok((fctx, finfo)) => { + C::FileContext::write(fctx, p_file_context); + *file_info = finfo.0; + STATUS_SUCCESS + } + Err(e) => e, + } + } + /// Create new file or directory. /// - FileSystem - The file system on which this request is posted. /// - FileName - The name of the file or directory to be created. @@ -1242,7 +1463,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn create_ex_ext( + unsafe extern "C" fn create_ex_ext( file_system: *mut FSP_FILE_SYSTEM, file_name: PWSTR, create_options: UINT32, @@ -1259,7 +1480,11 @@ impl Interface { let fs = &*(*file_system).UserContext.cast::(); let file_name = U16CStr::from_ptr_str(file_name); let sd = SecurityDescriptor::from_ptr(security_descriptor); - let buffer = std::slice::from_raw_parts(extra_buffer.cast(), extra_length as usize); + let buffer = if !extra_buffer.is_null() { + std::slice::from_raw_parts(extra_buffer.cast(), extra_length as usize) + } else { + &[] + }; match C::create_ex( fs, @@ -1283,6 +1508,43 @@ impl Interface { } } + /// Overwrite a file. + /// - FileSystem - The file system on which this request is posted. + /// - FileContext - The file context of the file to overwrite. + /// - FileAttributes - File attributes to apply to the overwritten file. + /// - ReplaceFileAttributes - When TRUE the existing file attributes should be + /// replaced with the new ones. When FALSE the existing file attributes + /// should be merged (or'ed) with the new ones. + /// - AllocationSize - Allocation size for the overwritten file. + /// - FileInfo - [out] Pointer to a structure that will receive the file + /// information on successful return from this call. This information + /// includes file attributes, file times, etc. + unsafe extern "C" fn overwrite_ext( + file_system: *mut FSP_FILE_SYSTEM, + file_context: PVOID, + file_attributes: UINT32, + replace_file_attributes: BOOLEAN, + allocation_size: UINT64, + file_info: *mut FSP_FSCTL_FILE_INFO, + ) -> NTSTATUS { + let fs = &*(*file_system).UserContext.cast::(); + let fctx = C::FileContext::access(file_context); + + match C::overwrite( + fs, + fctx, + FileAttributes(file_attributes), + replace_file_attributes != 0, + allocation_size, + ) { + Ok(finfo) => { + *file_info = finfo.0; + STATUS_SUCCESS + } + Err(e) => e, + } + } + /// Overwrite a file. /// - FileSystem - The file system on which this request is posted. /// - FileContext - The file context of the file to overwrite. @@ -1296,7 +1558,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn overwrite_ex_ext( + unsafe extern "C" fn overwrite_ex_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, file_attributes: UINT32, @@ -1308,7 +1570,11 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); - let buffer = std::slice::from_raw_parts(ea.cast(), ea_length as usize); + let buffer = if !ea.is_null() { + std::slice::from_raw_parts(ea.cast(), ea_length as usize) + } else { + &[] + }; match C::overwrite_ex( fs, @@ -1334,7 +1600,7 @@ impl Interface { /// - EaLength - Extended attributes buffer length. /// - PBytesTransferred - [out] Pointer to a memory location that will receive /// the actual number of bytes transferred. - unsafe extern "C" fn get_ea_ext( + unsafe extern "C" fn get_ea_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, ea: PFILE_FULL_EA_INFORMATION, @@ -1343,7 +1609,11 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); - let buffer = std::slice::from_raw_parts(ea.cast(), ea_length as usize); + let buffer = if !ea.is_null() { + std::slice::from_raw_parts(ea.cast(), ea_length as usize) + } else { + &[] + }; match C::get_ea(fs, fctx, buffer) { Ok(bytes_transfered) => { @@ -1363,7 +1633,7 @@ impl Interface { /// - FileInfo - [out] Pointer to a structure that will receive the file /// information on successful return from this call. This information /// includes file attributes, file times, etc. - unsafe extern "C" fn set_ea_ext( + unsafe extern "C" fn set_ea_ext( file_system: *mut FSP_FILE_SYSTEM, file_context: PVOID, ea: PFILE_FULL_EA_INFORMATION, @@ -1372,7 +1642,11 @@ impl Interface { ) -> NTSTATUS { let fs = &*(*file_system).UserContext.cast::(); let fctx = C::FileContext::access(file_context); - let buffer = std::slice::from_raw_parts(ea.cast(), ea_length as usize); + let buffer = if !ea.is_null() { + std::slice::from_raw_parts(ea.cast(), ea_length as usize) + } else { + &[] + }; match C::set_ea(fs, fctx, buffer) { Ok(info) => { @@ -1383,7 +1657,7 @@ impl Interface { } } - unsafe extern "C" fn dispatcher_stopped_ext( + unsafe extern "C" fn dispatcher_stopped_ext( file_system: *mut FSP_FILE_SYSTEM, normally: BOOLEAN, ) { @@ -1394,43 +1668,76 @@ impl Interface { FspFileSystemStopServiceIfNecessary(file_system, normally) } - pub(crate) fn interface() -> FSP_FILE_SYSTEM_INTERFACE { - let mut fsp_interface = FSP_FILE_SYSTEM_INTERFACE { - CanDelete: Some(Self::can_delete_ext::), - Cleanup: Some(Self::cleanup_ext::), - Close: Some(Self::close_ext::), - Control: Some(Self::control_ext::), - CreateEx: Some(Self::create_ex_ext::), - DeleteReparsePoint: Some(Self::delete_reparse_point_ext::), - DispatcherStopped: Some(Self::dispatcher_stopped_ext::), - Flush: Some(Self::flush_ext::), - GetDirInfoByName: Some(Self::get_dir_info_by_name_ext::), - GetEa: Some(Self::get_ea_ext::), - GetFileInfo: Some(Self::get_file_info_ext::), - GetReparsePoint: Some(Self::get_reparse_point_ext::), - GetSecurity: Some(Self::get_security_ext::), - GetSecurityByName: Some(Self::get_security_by_name_ext::), - GetStreamInfo: Some(Self::get_stream_info_ext::), - GetVolumeInfo: Some(Self::get_volume_info_ext::), - Open: Some(Self::open_ext::), - OverwriteEx: Some(Self::overwrite_ex_ext::), - Read: Some(Self::read_ext::), - ReadDirectory: Some(Self::read_directory_ext::), - Rename: Some(Self::rename_ext::), - ResolveReparsePoints: Some(Self::resolve_reparse_points_ext::), - SetBasicInfo: Some(Self::set_basic_info_ext::), - SetDelete: None, - SetEa: Some(Self::set_ea_ext::), - SetFileSize: Some(Self::set_file_size_ext::), - SetReparsePoint: Some(Self::set_reparse_point_ext::), - SetSecurity: Some(Self::set_security_ext::), - SetVolumeLabelW: Some(Self::set_volume_label_w_ext::), - Write: Some(Self::write_ext::), - ..Default::default() - }; - if Ctx::SET_DELETE_DEFINED { - fsp_interface.SetDelete = Some(Self::set_delete_ext::); + pub(crate) fn interface() -> FSP_FILE_SYSTEM_INTERFACE { + macro_rules! set_fn_pointer_or_null { + ($flag_name:ident, $fn_ext_name:ident) => { + if Ctx::$flag_name { + Some(Self::$fn_ext_name::) + } else { + None + } + }; + } + + FSP_FILE_SYSTEM_INTERFACE { + GetVolumeInfo: set_fn_pointer_or_null!(GET_VOLUME_INFO_DEFINED, get_volume_info_ext), + SetVolumeLabelW: set_fn_pointer_or_null!( + SET_VOLUME_LABEL_DEFINED, + set_volume_label_w_ext + ), + GetSecurityByName: set_fn_pointer_or_null!( + GET_SECURITY_BY_NAME_DEFINED, + get_security_by_name_ext + ), + Create: set_fn_pointer_or_null!(CREATE_DEFINED, create_ext), + CreateEx: set_fn_pointer_or_null!(CREATE_EX_DEFINED, create_ex_ext), + Open: set_fn_pointer_or_null!(OPEN_DEFINED, open_ext), + Overwrite: set_fn_pointer_or_null!(OVERWRITE_DEFINED, overwrite_ext), + OverwriteEx: set_fn_pointer_or_null!(OVERWRITE_EX_DEFINED, overwrite_ex_ext), + Cleanup: set_fn_pointer_or_null!(CLEANUP_DEFINED, cleanup_ext), + Close: set_fn_pointer_or_null!(CLOSE_DEFINED, close_ext), + Read: set_fn_pointer_or_null!(READ_DEFINED, read_ext), + Write: set_fn_pointer_or_null!(WRITE_DEFINED, write_ext), + Flush: set_fn_pointer_or_null!(FLUSH_DEFINED, flush_ext), + GetFileInfo: set_fn_pointer_or_null!(GET_FILE_INFO_DEFINED, get_file_info_ext), + SetBasicInfo: set_fn_pointer_or_null!(SET_BASIC_INFO_DEFINED, set_basic_info_ext), + SetFileSize: set_fn_pointer_or_null!(SET_FILE_SIZE_DEFINED, set_file_size_ext), + CanDelete: set_fn_pointer_or_null!(CAN_DELETE_DEFINED, can_delete_ext), + Rename: set_fn_pointer_or_null!(RENAME_DEFINED, rename_ext), + GetSecurity: set_fn_pointer_or_null!(GET_SECURITY_DEFINED, get_security_ext), + SetSecurity: set_fn_pointer_or_null!(SET_SECURITY_DEFINED, set_security_ext), + ReadDirectory: set_fn_pointer_or_null!(READ_DIRECTORY_DEFINED, read_directory_ext), + GetReparsePoint: set_fn_pointer_or_null!( + GET_REPARSE_POINT_DEFINED, + get_reparse_point_ext + ), + SetReparsePoint: set_fn_pointer_or_null!( + SET_REPARSE_POINT_DEFINED, + set_reparse_point_ext + ), + DeleteReparsePoint: set_fn_pointer_or_null!( + DELETE_REPARSE_POINT_DEFINED, + delete_reparse_point_ext + ), + GetStreamInfo: set_fn_pointer_or_null!(GET_STREAM_INFO_DEFINED, get_stream_info_ext), + GetDirInfoByName: set_fn_pointer_or_null!( + GET_DIR_INFO_BY_NAME_DEFINED, + get_dir_info_by_name_ext + ), + Control: set_fn_pointer_or_null!(CONTROL_DEFINED, control_ext), + SetDelete: set_fn_pointer_or_null!(SET_DELETE_DEFINED, set_delete_ext), + GetEa: set_fn_pointer_or_null!(GET_EA_DEFINED, get_ea_ext), + SetEa: set_fn_pointer_or_null!(SET_EA_DEFINED, set_ea_ext), + DispatcherStopped: set_fn_pointer_or_null!( + DISPATCHER_STOPPED_DEFINED, + dispatcher_stopped_ext + ), + ResolveReparsePoints: set_fn_pointer_or_null!( + RESOLVE_REPARSE_POINTS_DEFINED, + resolve_reparse_points_ext + ), + + ..Default::default() // Initializing `Obsolete0` & `Reserved` fields } - fsp_interface } } diff --git a/winfsp_wrs/src/file_system.rs b/winfsp_wrs/src/file_system.rs index 3e270fc..4b8aac8 100644 --- a/winfsp_wrs/src/file_system.rs +++ b/winfsp_wrs/src/file_system.rs @@ -20,7 +20,7 @@ use winfsp_wrs_sys::{ FSP_FSCTL_VOLUME_PARAMS, NTSTATUS, }; -use crate::{FileContextKind, FileSystemContext, Interface}; +use crate::{FileContextKind, FileSystemInterface, TrampolineInterface}; #[cfg(feature = "icon")] use crate::{FileAccessRights, FileAttributes, FileCreationDisposition, FileShareMode}; @@ -317,7 +317,7 @@ pub struct Params { } #[derive(Debug, Clone)] -pub struct FileSystem { +pub struct FileSystem { // FileSystem inner value inner: FSP_FILE_SYSTEM, pub params: Params, @@ -327,9 +327,9 @@ pub struct FileSystem { // SAFETY: FSP_FILE_SYSTEM contains `*mut c_void` pointers that cannot be send between threads // by default. However this structure is only used by WinFSP (and not exposed to the user) which // is deep in C++ land where Rust safety rules do not apply. -unsafe impl Send for FileSystem {} +unsafe impl Send for FileSystem {} -impl FileSystem { +impl FileSystem { pub fn volume_params(&self) -> &VolumeParams { &self.params.volume_params } @@ -353,7 +353,7 @@ impl FileSystem { ) -> Result { unsafe { let mut p_inner = std::ptr::null_mut(); - let interface = Box::into_raw(Box::new(Interface::interface::())); + let interface = Box::into_raw(Box::new(TrampolineInterface::interface::())); params .volume_params diff --git a/winfsp_wrs/src/lib.rs b/winfsp_wrs/src/lib.rs index 9b31eda..49a1b93 100644 --- a/winfsp_wrs/src/lib.rs +++ b/winfsp_wrs/src/lib.rs @@ -6,9 +6,9 @@ mod info; mod init; mod security; -pub(crate) use callback::Interface; +pub(crate) use callback::TrampolineInterface; -pub use callback::{FileContextKind, FileSystemContext}; +pub use callback::{FileContextKind, FileSystemInterface}; #[cfg(feature = "icon")] pub use file_system::set_folder_icon; pub use file_system::{