diff --git a/examples/memfs/src/main.rs b/examples/memfs/src/main.rs index d8364e3..3fe8413 100644 --- a/examples/memfs/src/main.rs +++ b/examples/memfs/src/main.rs @@ -5,10 +5,10 @@ use std::{ sync::{Arc, Mutex}, }; use winfsp_wrs::{ - filetime_now, u16cstr, u16str, CleanupFlags, CreateFileInfo, CreateOptions, 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, + 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, }; @@ -285,7 +285,7 @@ impl FileSystemContext for MemFs { security_descriptor: SecurityDescriptor, _buffer: &[u8], _extra_buffer_is_reparse_point: bool, - ) -> Result { + ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { if self.read_only { return Err(STATUS_MEDIA_WRITE_PROTECTED); } @@ -299,7 +299,7 @@ impl FileSystemContext for MemFs { return Err(STATUS_OBJECT_NAME_COLLISION); } - let file_obj = Arc::new(Mutex::new( + let file_context = Arc::new(Mutex::new( if create_file_info .create_options .is(CreateOptions::FILE_DIRECTORY_FILE) @@ -319,9 +319,11 @@ impl FileSystemContext for MemFs { }, )); - entries.insert(file_name, file_obj.clone()); + entries.insert(file_name, file_context.clone()); - Ok(file_obj) + let file_info = self.get_file_info(file_context.clone())?; + + Ok((file_context, file_info)) } fn open( @@ -329,11 +331,15 @@ impl FileSystemContext for MemFs { file_name: &U16CStr, _create_options: CreateOptions, _granted_access: FileAccessRights, - ) -> Result { + ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { let file_name = PathBuf::from(file_name.to_os_string()); match self.entries.lock().unwrap().get(&file_name) { - Some(entry) => Ok(entry.clone()), + Some(entry) => { + let file_context = entry.clone(); + let file_info = self.get_file_info(file_context.clone())?; + Ok((file_context, file_info)) + } None => Err(STATUS_OBJECT_NAME_NOT_FOUND), } } @@ -345,7 +351,7 @@ impl FileSystemContext for MemFs { replace_file_attributes: bool, allocation_size: u64, _buffer: &[u8], - ) -> Result<(), NTSTATUS> { + ) -> Result { if self.read_only { return Err(STATUS_MEDIA_WRITE_PROTECTED); } @@ -369,11 +375,11 @@ impl FileSystemContext for MemFs { file_obj.info.set_last_access_time(now); file_obj.info.set_last_write_time(now); file_obj.info.set_change_time(now); - - Ok(()) } else { unreachable!() } + + self.get_file_info(file_context) } fn cleanup( @@ -396,9 +402,9 @@ impl FileSystemContext for MemFs { // Set archive bit if flags.is(CleanupFlags::SET_ARCHIVE_BIT) { - file_obj.info.set_file_attributes( - FileAttributes::ARCHIVE | file_obj.info.file_attributes(), - ); + file_obj + .info + .set_file_attributes(FileAttributes::ARCHIVE | file_obj.info.file_attributes()); } let now = filetime_now(); @@ -455,29 +461,32 @@ impl FileSystemContext for MemFs { &self, file_context: Self::FileContext, buffer: &[u8], - offset: u64, mode: WriteMode, - ) -> Result { + ) -> Result<(usize, FileInfo), NTSTATUS> { if self.read_only { return Err(STATUS_MEDIA_WRITE_PROTECTED); } - if let Obj::File(file_obj) = file_context.lock().unwrap().deref_mut() { + let written = if let Obj::File(file_obj) = file_context.lock().unwrap().deref_mut() { match mode { - WriteMode::Constrained => Ok(file_obj.constrained_write(buffer, offset as usize)), - WriteMode::Normal => Ok(file_obj.write(buffer, offset as usize)), - WriteMode::StartEOF => { + WriteMode::Normal { offset } => file_obj.write(buffer, offset as usize), + WriteMode::ConstrainedIO { offset } => { + file_obj.constrained_write(buffer, offset as usize) + } + WriteMode::WriteToEOF => { let offset = file_obj.info.file_size(); - Ok(file_obj.write(buffer, offset as usize)) + file_obj.write(buffer, offset as usize) } } } else { unreachable!() - } + }; + + Ok((written, self.get_file_info(file_context)?)) } - fn flush(&self, _file_context: Self::FileContext) -> Result<(), NTSTATUS> { - Ok(()) + fn flush(&self, file_context: Self::FileContext) -> Result { + self.get_file_info(file_context) } fn get_file_info(&self, file_context: Self::FileContext) -> Result { @@ -495,7 +504,7 @@ impl FileSystemContext for MemFs { last_access_time: u64, last_write_time: u64, change_time: u64, - ) -> Result<(), NTSTATUS> { + ) -> Result { if self.read_only { return Err(STATUS_MEDIA_WRITE_PROTECTED); } @@ -537,7 +546,7 @@ impl FileSystemContext for MemFs { } } - Ok(()) + self.get_file_info(file_context) } fn set_file_size( @@ -545,7 +554,7 @@ impl FileSystemContext for MemFs { file_context: Self::FileContext, new_size: u64, set_allocation_size: bool, - ) -> Result<(), NTSTATUS> { + ) -> Result { if self.read_only { return Err(STATUS_MEDIA_WRITE_PROTECTED); } @@ -563,7 +572,7 @@ impl FileSystemContext for MemFs { } } - Ok(()) + self.get_file_info(file_context) } fn rename( @@ -655,7 +664,8 @@ impl FileSystemContext for MemFs { &self, file_context: Self::FileContext, marker: Option<&U16CStr>, - ) -> Result, NTSTATUS> { + mut add_dir_info: impl FnMut(DirInfo) -> bool, + ) -> Result<(), NTSTATUS> { let entries = self.entries.lock().unwrap(); match file_context.lock().unwrap().deref() { @@ -683,7 +693,7 @@ impl FileSystemContext for MemFs { res_entries.push(( U16CString::from_os_str(entry_path.file_name().unwrap()).unwrap(), FileInfo::from(entry_obj.deref()), - )) + )); } res_entries.sort_by(|x, y| y.0.cmp(&x.0)); @@ -697,7 +707,14 @@ impl FileSystemContext for MemFs { res_entries.reverse(); - Ok(res_entries) + for (file_name, file_info) in res_entries { + let dir_info = DirInfo::new(file_info, &file_name); + if !add_dir_info(dir_info) { + break; + } + } + + Ok(()) } } } diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 1c929c4..44215ab 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,9 +1,9 @@ use std::sync::Arc; use winfsp_wrs::{ - filetime_now, u16cstr, u16str, CreateOptions, FileAccessRights, FileAttributes, FileInfo, - FileSystem, FileSystemContext, PSecurityDescriptor, Params, SecurityDescriptor, U16CStr, - U16CString, U16Str, VolumeInfo, VolumeParams, NTSTATUS, + filetime_now, u16cstr, u16str, CreateOptions, DirInfo, FileAccessRights, FileAttributes, + FileInfo, FileSystem, FileSystemContext, PSecurityDescriptor, Params, SecurityDescriptor, + U16CStr, U16Str, VolumeInfo, VolumeParams, NTSTATUS, }; #[derive(Debug, Clone)] @@ -68,8 +68,10 @@ impl FileSystemContext for MemFs { _file_name: &U16CStr, _create_options: CreateOptions, _granted_access: FileAccessRights, - ) -> Result { - Ok(Arc::new(self.file_context.clone())) + ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { + let file_context = Arc::new(self.file_context.clone()); + let file_info = self.file_context.info; + Ok((file_context, file_info)) } fn get_file_info(&self, _file_context: Self::FileContext) -> Result { @@ -84,8 +86,9 @@ impl FileSystemContext for MemFs { &self, _file_context: Self::FileContext, _marker: Option<&U16CStr>, - ) -> Result, NTSTATUS> { - Ok(vec![]) + _add_dir_info: impl FnMut(DirInfo) -> bool, + ) -> Result<(), NTSTATUS> { + Ok(()) } } diff --git a/winfsp_wrs/src/callback.rs b/winfsp_wrs/src/callback.rs index d0b2071..37698ea 100644 --- a/winfsp_wrs/src/callback.rs +++ b/winfsp_wrs/src/callback.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use widestring::{U16CStr, U16CString}; +use widestring::U16CStr; use windows_sys::Win32::Foundation::{ STATUS_BUFFER_OVERFLOW, STATUS_NOT_IMPLEMENTED, STATUS_REPARSE, STATUS_SUCCESS, }; @@ -114,7 +114,7 @@ pub trait FileSystemContext { _security_descriptor: SecurityDescriptor, _buffer: &[u8], _extra_buffer_is_reparse_point: bool, - ) -> Result { + ) -> Result<(Self::FileContext, FileInfo), NTSTATUS> { Err(STATUS_NOT_IMPLEMENTED) } @@ -124,7 +124,7 @@ pub trait FileSystemContext { file_name: &U16CStr, create_options: CreateOptions, granted_access: FileAccessRights, - ) -> Result; + ) -> Result<(Self::FileContext, FileInfo), NTSTATUS>; /// Overwrite a file. fn overwrite_ex( @@ -134,7 +134,7 @@ pub trait FileSystemContext { _replace_file_attributes: bool, _allocation_size: u64, _buffer: &[u8], - ) -> Result<(), NTSTATUS> { + ) -> Result { Err(STATUS_NOT_IMPLEMENTED) } @@ -165,14 +165,13 @@ pub trait FileSystemContext { &self, _file_context: Self::FileContext, _buffer: &[u8], - _offset: u64, _mode: WriteMode, - ) -> Result { + ) -> Result<(usize, FileInfo), NTSTATUS> { Err(STATUS_NOT_IMPLEMENTED) } /// Flush a file or volume. - fn flush(&self, _file_context: Self::FileContext) -> Result<(), NTSTATUS> { + fn flush(&self, _file_context: Self::FileContext) -> Result { Err(STATUS_NOT_IMPLEMENTED) } @@ -188,7 +187,7 @@ pub trait FileSystemContext { _last_access_time: u64, _last_write_time: u64, _change_time: u64, - ) -> Result<(), NTSTATUS> { + ) -> Result { Err(STATUS_NOT_IMPLEMENTED) } @@ -198,7 +197,7 @@ pub trait FileSystemContext { _file_context: Self::FileContext, _new_size: u64, _set_allocation_size: bool, - ) -> Result<(), NTSTATUS> { + ) -> Result { Err(STATUS_NOT_IMPLEMENTED) } @@ -241,11 +240,14 @@ pub trait FileSystemContext { } /// Read a directory. + /// + /// `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>, - ) -> Result, NTSTATUS>; + add_dir_info: impl FnMut(DirInfo) -> bool, + ) -> Result<(), NTSTATUS>; /// Get reparse point. fn get_reparse_point( @@ -358,11 +360,11 @@ impl Interface { let fs = &*(*file_system).UserContext.cast::(); match C::get_volume_info(fs) { - Err(e) => e, Ok(vi) => { *volume_info = vi.0; STATUS_SUCCESS } + Err(e) => e, } } @@ -379,11 +381,11 @@ impl Interface { let fs = &*(*file_system).UserContext.cast::(); match C::set_volume_label(fs, U16CStr::from_ptr_str(volume_label)) { - Err(e) => e, Ok(vi) => { *volume_info = vi.0; STATUS_SUCCESS } + Err(e) => e, } } @@ -438,7 +440,6 @@ impl Interface { let file_name = U16CStr::from_ptr_str(file_name); match C::get_security_by_name(fs, file_name, find_reparse_point) { - Err(e) => e, Ok((fa, sd, reparse)) => { if !p_file_attributes.is_null() { p_file_attributes.write(fa.0) @@ -466,6 +467,7 @@ impl Interface { STATUS_SUCCESS } } + Err(e) => e, } } @@ -506,11 +508,12 @@ impl Interface { CreateOptions(create_options), FileAccessRights(granted_access), ) { - Err(e) => e, - Ok(fctx) => { + Ok((fctx, finfo)) => { C::FileContext::write(fctx, p_file_context); - Self::get_file_info_ext::(file_system, *p_file_context, file_info) + *file_info = finfo.0; + STATUS_SUCCESS } + Err(e) => e, } } @@ -573,11 +576,11 @@ impl Interface { let buffer = std::slice::from_raw_parts_mut(buffer.cast(), length as usize); match C::read(fs, fctx, buffer, offset) { - Err(e) => e, Ok(bytes_transferred) => { *p_bytes_transferred = bytes_transferred as ULONG; STATUS_SUCCESS } + Err(e) => e, } } @@ -612,22 +615,22 @@ impl Interface { let buffer = std::slice::from_raw_parts(buffer.cast(), length as usize); let mode = match (write_to_end_of_file != 0, constrained_io != 0) { - (false, false) => WriteMode::Normal, - (false, true) => WriteMode::Constrained, - (true, false) => WriteMode::StartEOF, + (false, false) => WriteMode::Normal { offset }, + (false, true) => WriteMode::ConstrainedIO { offset }, + (true, false) => WriteMode::WriteToEOF, (true, true) => { *p_bytes_transferred = 0; return Self::get_file_info_ext::(file_system, file_context, file_info); } }; - match C::write(fs, fctx, buffer, offset, mode) { - Err(e) => e, - Ok(bytes_transfered) => { + match C::write(fs, fctx, buffer, mode) { + Ok((bytes_transfered, finfo)) => { *p_bytes_transferred = bytes_transfered as ULONG; - - Self::get_file_info_ext::(file_system, file_context, file_info) + *file_info = finfo.0; + STATUS_SUCCESS } + Err(e) => e, } } @@ -648,8 +651,11 @@ impl Interface { let fctx = C::FileContext::access(file_context); match C::flush(fs, fctx) { + Ok(finfo) => { + *file_info = finfo.0; + STATUS_SUCCESS + } Err(e) => e, - Ok(()) => Self::get_file_info_ext::(file_system, file_context, file_info), } } @@ -669,11 +675,11 @@ impl Interface { let fctx = C::FileContext::access(file_context); match C::get_file_info(fs, fctx) { - Err(e) => e, Ok(ret) => { *file_info = ret.0; STATUS_SUCCESS } + Err(e) => e, } } @@ -717,8 +723,11 @@ impl Interface { last_write_time, change_time, ) { + Ok(finfo) => { + *file_info = finfo.0; + STATUS_SUCCESS + } Err(e) => e, - Ok(()) => Self::get_file_info_ext::(file_system, file_context, file_info), } } @@ -743,8 +752,11 @@ impl Interface { let fctx = C::FileContext::access(file_context); match C::set_file_size(fs, fctx, new_size, set_allocation_size != 0) { + Ok(finfo) => { + *file_info = finfo.0; + STATUS_SUCCESS + } Err(e) => e, - Ok(()) => Self::get_file_info_ext::(file_system, file_context, file_info), } } @@ -765,8 +777,8 @@ impl Interface { let file_name = U16CStr::from_ptr_str(file_name); match C::can_delete(fs, fctx, file_name) { - Err(e) => e, Ok(()) => STATUS_SUCCESS, + Err(e) => e, } } @@ -790,8 +802,8 @@ impl Interface { let new_file_name = U16CStr::from_ptr_str(new_file_name); match C::rename(fs, fctx, file_name, new_file_name, replace_if_exists != 0) { - Err(e) => e, Ok(()) => STATUS_SUCCESS, + Err(e) => e, } } @@ -815,7 +827,6 @@ impl Interface { let fctx = C::FileContext::access(file_context); match C::get_security(fs, fctx) { - Err(e) => e, Ok(sd) => { if !p_security_descriptor_size.is_null() { if sd.len() as SIZE_T > p_security_descriptor_size.read() { @@ -829,6 +840,7 @@ impl Interface { STATUS_SUCCESS } + Err(e) => e, } } @@ -852,8 +864,8 @@ impl Interface { let modification_descriptor = PSecurityDescriptor::from_ptr(modification_descriptor); match C::set_security(fs, fctx, security_information, modification_descriptor) { - Err(e) => e, Ok(()) => STATUS_SUCCESS, + Err(e) => e, } } @@ -890,29 +902,22 @@ impl Interface { Some(U16CStr::from_ptr_str(marker)) }; - match C::read_directory(fs, fctx, marker) { - Err(e) => e, - Ok(entries_info) => { - for (file_name, file_info) in entries_info { - // FSP_FSCTL_DIR_INFO base struct + WCHAR[] string - // Note: Windows does not use NULL-terminated string - let dir_info = &mut DirInfo::new(file_info, &file_name); - - if FspFileSystemAddDirInfo( - (dir_info as *mut DirInfo).cast(), - buffer, - length, - p_bytes_transferred, - ) == 0 - { - return STATUS_SUCCESS; - } - } + let add_dir_info = |mut dir_info: DirInfo| { + FspFileSystemAddDirInfo( + (&mut dir_info as *mut DirInfo).cast(), + buffer, + length, + p_bytes_transferred, + ) != 0 + }; + match C::read_directory(fs, fctx, marker, add_dir_info) { + Ok(()) => { // EOF marker FspFileSystemAddDirInfo(std::ptr::null_mut(), buffer, length, p_bytes_transferred); STATUS_SUCCESS } + Err(e) => e, } } @@ -936,11 +941,11 @@ impl Interface { }; match C::get_reparse_point_by_name(fs, file_name, is_directory != 0, buffer) { - Err(e) => e, Ok(bytes_transferred) => { psize.write(bytes_transferred as SIZE_T); STATUS_SUCCESS } + Err(e) => e, } } @@ -1007,11 +1012,11 @@ impl Interface { let buffer = std::slice::from_raw_parts_mut(buffer.cast(), *p_size as usize); match C::get_reparse_point(fs, fctx, file_name, buffer) { - Err(e) => e, Ok(byte_transferred) => { p_size.write(byte_transferred as SIZE_T); STATUS_SUCCESS } + Err(e) => e, } } @@ -1036,8 +1041,8 @@ impl Interface { let buffer = std::slice::from_raw_parts_mut(buffer.cast(), size as usize); match C::set_reparse_point(fs, fctx, file_name, buffer) { - Err(e) => e, Ok(()) => STATUS_SUCCESS, + Err(e) => e, } } @@ -1060,8 +1065,8 @@ impl Interface { let buffer = std::slice::from_raw_parts_mut(buffer.cast(), size as usize); match C::delete_reparse_point(fs, fctx, file_name, buffer) { - Err(e) => e, Ok(()) => STATUS_SUCCESS, + Err(e) => e, } } @@ -1085,11 +1090,11 @@ impl Interface { let buffer = std::slice::from_raw_parts_mut(buffer.cast(), length as usize); match C::get_stream_info(fs, fctx, buffer) { - Err(e) => e, Ok(bytes_transferred) => { p_bytes_transferred.write(bytes_transferred as ULONG); STATUS_SUCCESS } + Err(e) => e, } } @@ -1113,11 +1118,10 @@ impl Interface { let file_name = U16CStr::from_ptr_str(file_name); match C::get_dir_info_by_name(fs, fctx, file_name) { - Err(e) => e, - Ok(file_info) => { + Ok(finfo) => { (*dir_info).Size = (std::mem::size_of::() + file_name.len() * 2) as u16; - (*dir_info).FileInfo = file_info.0; + (*dir_info).FileInfo = finfo.0; std::ptr::copy( file_name.as_ptr(), (*dir_info).FileNameBuf.as_mut_ptr(), @@ -1125,6 +1129,7 @@ impl Interface { ); STATUS_SUCCESS } + Err(e) => e, } } @@ -1157,11 +1162,11 @@ impl Interface { std::slice::from_raw_parts_mut(output_buffer.cast(), output_buffer_length as usize); match C::control(fs, fctx, control_code, input, output) { - Err(e) => e, Ok(bytes_transferred) => { p_bytes_transferred.write(bytes_transferred as ULONG); STATUS_SUCCESS } + Err(e) => e, } } @@ -1185,8 +1190,8 @@ impl Interface { let file_name = U16CStr::from_ptr_str(file_name); match C::set_delete(fs, fctx, file_name, delete_file_w != 0) { - Err(e) => e, Ok(()) => STATUS_SUCCESS, + Err(e) => e, } } @@ -1254,11 +1259,12 @@ impl Interface { buffer, extra_buffer_is_reparse_point != 0, ) { - Err(e) => e, - Ok(fctx) => { + Ok((fctx, finfo)) => { C::FileContext::write(fctx, p_file_context); - Self::get_file_info_ext::(file_system, *p_file_context, file_info) + *file_info = finfo.0; + STATUS_SUCCESS } + Err(e) => e, } } @@ -1297,8 +1303,11 @@ impl Interface { allocation_size, buffer, ) { + Ok(finfo) => { + *file_info = finfo.0; + STATUS_SUCCESS + } Err(e) => e, - Ok(()) => Self::get_file_info_ext::(file_system, file_context, file_info), } } @@ -1322,11 +1331,11 @@ impl Interface { let buffer = std::slice::from_raw_parts(ea.cast(), ea_length as usize); match C::get_ea(fs, fctx, buffer) { - Err(e) => e, Ok(bytes_transfered) => { p_bytes_transferred.write(bytes_transfered as ULONG); STATUS_SUCCESS } + Err(e) => e, } } @@ -1351,11 +1360,11 @@ impl Interface { let buffer = std::slice::from_raw_parts(ea.cast(), ea_length as usize); match C::set_ea(fs, fctx, buffer) { - Err(e) => e, Ok(info) => { file_info.write(info.0); STATUS_SUCCESS } + Err(e) => e, } } diff --git a/winfsp_wrs/src/flags.rs b/winfsp_wrs/src/flags.rs index 182bc6a..e07043d 100644 --- a/winfsp_wrs/src/flags.rs +++ b/winfsp_wrs/src/flags.rs @@ -2,23 +2,12 @@ use std::ops::{BitOr, BitOrAssign}; use windows_sys::{ Wdk::Storage::FileSystem::{ - FILE_DIRECTORY_FILE, - FILE_NON_DIRECTORY_FILE, - FILE_WRITE_THROUGH, - FILE_SEQUENTIAL_ONLY, - FILE_RANDOM_ACCESS, - FILE_NO_INTERMEDIATE_BUFFERING, - FILE_SYNCHRONOUS_IO_ALERT, - FILE_SYNCHRONOUS_IO_NONALERT, - FILE_CREATE_TREE_CONNECTION, - FILE_NO_EA_KNOWLEDGE, - FILE_OPEN_REPARSE_POINT, - FILE_DELETE_ON_CLOSE, - FILE_OPEN_BY_FILE_ID, - FILE_OPEN_FOR_BACKUP_INTENT, - FILE_RESERVE_OPFILTER, - FILE_OPEN_REQUIRING_OPLOCK, - FILE_COMPLETE_IF_OPLOCKED, + FILE_COMPLETE_IF_OPLOCKED, FILE_CREATE_TREE_CONNECTION, FILE_DELETE_ON_CLOSE, + FILE_DIRECTORY_FILE, FILE_NON_DIRECTORY_FILE, FILE_NO_EA_KNOWLEDGE, + FILE_NO_INTERMEDIATE_BUFFERING, FILE_OPEN_BY_FILE_ID, FILE_OPEN_FOR_BACKUP_INTENT, + FILE_OPEN_REPARSE_POINT, FILE_OPEN_REQUIRING_OPLOCK, FILE_RANDOM_ACCESS, + FILE_RESERVE_OPFILTER, FILE_SEQUENTIAL_ONLY, FILE_SYNCHRONOUS_IO_ALERT, + FILE_SYNCHRONOUS_IO_NONALERT, FILE_WRITE_THROUGH, }, Win32::Storage::FileSystem::{ CREATE_ALWAYS, CREATE_NEW, DELETE, FILE_ACCESS_RIGHTS, FILE_ADD_FILE, @@ -50,7 +39,9 @@ macro_rules! impl_debug_flags { ($name:ident) => { impl std::fmt::Debug for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("FileAttributes").field(&format_args!("0x{:X}", self.0)).finish() + f.debug_tuple("FileAttributes") + .field(&format_args!("0x{:X}", self.0)) + .finish() } } }; @@ -242,7 +233,8 @@ impl CreateOptions { /// The file cannot be cached or buffered in a driver's internal buffers. This /// flag is incompatible with the DesiredAccess FILE_APPEND_DATA flag. - pub const FILE_NO_INTERMEDIATE_BUFFERING: CreateOptions = CreateOptions(FILE_NO_INTERMEDIATE_BUFFERING); + pub const FILE_NO_INTERMEDIATE_BUFFERING: CreateOptions = + CreateOptions(FILE_NO_INTERMEDIATE_BUFFERING); /// All operations on the file are performed synchronously. Any wait on behalf /// of the caller is subject to premature termination from alerts. This flag @@ -254,11 +246,13 @@ impl CreateOptions { /// to synchronize I/O queuing and completion are not subject to alerts. This /// flag also causes the I/O system to maintain the file position context. If /// this flag is set, the DesiredAccess SYNCHRONIZE flag also must be set. - pub const FILE_SYNCHRONOUS_IO_NONALERT: CreateOptions = CreateOptions(FILE_SYNCHRONOUS_IO_NONALERT); + pub const FILE_SYNCHRONOUS_IO_NONALERT: CreateOptions = + CreateOptions(FILE_SYNCHRONOUS_IO_NONALERT); /// Create a tree connection for this file in order to open it over the network. /// This flag is not used by device and intermediate drivers. - pub const FILE_CREATE_TREE_CONNECTION: CreateOptions = CreateOptions(FILE_CREATE_TREE_CONNECTION); + pub const FILE_CREATE_TREE_CONNECTION: CreateOptions = + CreateOptions(FILE_CREATE_TREE_CONNECTION); /// If the extended attributes on an existing file being opened indicate that /// the caller must understand EAs to properly interpret the file, fail this @@ -286,7 +280,8 @@ impl CreateOptions { /// check for certain access rights and grant the caller the appropriate access /// to the file before checking the DesiredAccess parameter against the file's /// security descriptor. This flag not used by device and intermediate drivers. - pub const FILE_OPEN_FOR_BACKUP_INTENT: CreateOptions = CreateOptions(FILE_OPEN_FOR_BACKUP_INTENT); + pub const FILE_OPEN_FOR_BACKUP_INTENT: CreateOptions = + CreateOptions(FILE_OPEN_FOR_BACKUP_INTENT); /// This flag allows an application to request a filter opportunistic lock /// (oplock) to prevent other applications from getting share violations. If @@ -491,7 +486,7 @@ impl_debug_flags!(FileShareMode); // Documentation taken from https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea impl FileShareMode { /// Prevents other processes from opening a file or device if they request delete, - /// read, or write access. + /// read, or write access. pub const NONE: Self = Self(FILE_SHARE_NONE); /// Enables subsequent open operations on a file or device to request delete access. diff --git a/winfsp_wrs/src/info.rs b/winfsp_wrs/src/info.rs index 59d795d..7939201 100644 --- a/winfsp_wrs/src/info.rs +++ b/winfsp_wrs/src/info.rs @@ -205,7 +205,7 @@ pub struct DirInfo { } impl DirInfo { - pub(crate) fn new(file_info: FileInfo, file_name: &U16CStr) -> Self { + pub fn new(file_info: FileInfo, file_name: &U16CStr) -> Self { let mut buf = [0; 255]; buf[..file_name.len()].copy_from_slice(file_name.as_slice()); @@ -216,11 +216,54 @@ impl DirInfo { file_name: buf, } } + + pub fn from_str(file_info: FileInfo, file_name: &str) -> Self { + let mut info = Self { + size: 0, + file_info, + _padding: [0; 24], + file_name: [0; 255], + }; + + let mut i = 0; + for c in file_name.encode_utf16() { + info.file_name[i] = c; + i += 1; + } + info.size = + (std::mem::size_of::() + i * std::mem::size_of::()) as u16; + + info + } + + pub fn from_osstr(file_info: FileInfo, file_name: &std::ffi::OsStr) -> Self { + use std::os::windows::ffi::OsStrExt; + + let mut info = Self { + size: 0, + file_info, + _padding: [0; 24], + file_name: [0; 255], + }; + + let mut i = 0; + for c in file_name.encode_wide() { + info.file_name[i] = c; + i += 1; + } + info.size = + (std::mem::size_of::() + i * std::mem::size_of::()) as u16; + + info + } } #[derive(Debug, Clone, Copy)] pub enum WriteMode { - Normal, - Constrained, - StartEOF, + /// Regular write mode: start at the offset and extend the file as much as needed. + Normal { offset: u64 }, + /// The file system must not extend the file (i.e. change the file size). + ConstrainedIO { offset: u64 }, + /// The file system must write to the current end of file. + WriteToEOF, } diff --git a/winfsp_wrs/src/init.rs b/winfsp_wrs/src/init.rs index b10f864..ba07620 100644 --- a/winfsp_wrs/src/init.rs +++ b/winfsp_wrs/src/init.rs @@ -1,12 +1,27 @@ use std::path::PathBuf; use widestring::{U16CStr, U16CString}; -use windows_sys::{ - w, - Win32::Foundation::{ERROR_DELAY_LOAD_FAILED, ERROR_FILE_NOT_FOUND}, - Win32::System::LibraryLoader::LoadLibraryW, -}; +use windows_sys::{w, Win32::System::LibraryLoader::LoadLibraryW}; -fn get_lplibfilename() -> Result { +#[derive(Debug)] +pub enum InitError { + WinFSPNotFound, + CannotLoadDLL { dll_path: U16CString }, +} + +impl std::error::Error for InitError {} + +impl std::fmt::Display for InitError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InitError::WinFSPNotFound => write!(f, "Cannot find WinFSP install directory."), + InitError::CannotLoadDLL { dll_path } => { + write!(f, "Cannot load WinFSP DLL {}.", dll_path.to_string_lossy()) + } + } + } +} + +fn get_lplibfilename() -> Result { use windows_sys::Win32::Foundation::MAX_PATH; use windows_sys::Win32::System::Registry::{RegGetValueW, HKEY_LOCAL_MACHINE, RRF_RT_REG_SZ}; let mut path = [0u16; MAX_PATH as usize]; @@ -25,7 +40,7 @@ fn get_lplibfilename() -> Result { }; if winfsp_install != 0 { - return Err(ERROR_FILE_NOT_FOUND); + return Err(InitError::WinFSPNotFound); } let path = U16CStr::from_slice(&path[0..(size as usize) / std::mem::size_of::()]) @@ -54,12 +69,12 @@ fn get_lplibfilename() -> Result { /// delayload, which is needed because `winfsp_wrs` depends on `WinFSP's dll` /// which is not in Windows path or at the same location of your binary. /// # Note: This funcion is idempotent, hence calling it multiple times is safe. -pub fn init() -> Result<(), u32> { - unsafe { - if LoadLibraryW(get_lplibfilename()?.as_ptr().cast_mut()) == 0 { - Err(ERROR_DELAY_LOAD_FAILED) - } else { - Ok(()) - } +pub fn init() -> Result<(), InitError> { + let dll_path = get_lplibfilename()?; + let outcome = unsafe { LoadLibraryW(dll_path.as_ptr().cast_mut()) }; + if outcome != 0 { + Ok(()) + } else { + Err(InitError::CannotLoadDLL { dll_path }) } } diff --git a/winfsp_wrs/src/lib.rs b/winfsp_wrs/src/lib.rs index e97d168..c8be151 100644 --- a/winfsp_wrs/src/lib.rs +++ b/winfsp_wrs/src/lib.rs @@ -21,7 +21,7 @@ pub use flags::{ FileShareMode, }; pub use info::{CreateFileInfo, DirInfo, FileInfo, VolumeInfo, WriteMode}; -pub use init::init; +pub use init::{init, InitError}; pub use security::{PSecurityDescriptor, SecurityDescriptor}; // Reexport