From bcfee7b299f3f4022420b3686fa7856810189564 Mon Sep 17 00:00:00 2001 From: Jonathan Giroux Date: Thu, 2 Jun 2022 19:02:00 +0200 Subject: [PATCH] apply formatting rules --- dokan-sys/build.rs | 30 +- dokan-sys/src/lib.rs | 334 ++++++------ dokan/examples/memfs/err_utils.rs | 4 +- dokan/examples/memfs/main.rs | 435 ++++++++++------ dokan/examples/memfs/path.rs | 40 +- dokan/examples/memfs/security.rs | 57 +- dokan/src/lib.rs | 486 ++++++++++++------ dokan/src/tests.rs | 828 ++++++++++++++++++++++-------- 8 files changed, 1502 insertions(+), 712 deletions(-) diff --git a/dokan-sys/build.rs b/dokan-sys/build.rs index 4ecf773..ef8ed00 100644 --- a/dokan-sys/build.rs +++ b/dokan-sys/build.rs @@ -44,7 +44,8 @@ fn run_generator(compiler: &Tool) -> String { assert!(compiler_cmd.output().unwrap().status.success()); let generate_output = Command::new(format!("{}/generate_version.exe", out_dir)) .current_dir(&out_dir) - .output().unwrap(); + .output() + .unwrap(); assert!(generate_output.status.success()); println!("cargo:rerun-if-changed=src/generate_version.c"); @@ -63,16 +64,26 @@ fn check_dokan_env(version_major: &str) -> bool { println!("cargo:rustc-link-search=native={}", lib_path); true } else { - println!("cargo:warning=Environment variable {} not found, building Dokan from source.", env_name); + println!( + "cargo:warning=Environment variable {} not found, building Dokan from source.", + env_name + ); false } } fn build_dokan(compiler: &Tool, version_major: &str) { let out_dir = env::var("OUT_DIR").unwrap(); - let src = fs::read_dir("src/dokany/dokan").unwrap() + let src = fs::read_dir("src/dokany/dokan") + .unwrap() .map(|d| d.unwrap().path()) - .filter(|p| if let Some(ext) = p.extension() { ext == "c" } else { false }); + .filter(|p| { + if let Some(ext) = p.extension() { + ext == "c" + } else { + false + } + }); let dll_name = format!("dokan{}.dll", version_major); let dll_path = format!("{}/{}", out_dir, dll_name); let mut compiler_cmd = compiler.to_command(); @@ -103,7 +114,10 @@ fn build_dokan(compiler: &Tool, version_major: &str) { .arg("-shared") .arg(format!("-o{}", dll_path)) .args(src) - .arg(format!("-Wl,--out-implib,{}/dokan{}.lib", out_dir, version_major)) + .arg(format!( + "-Wl,--out-implib,{}/dokan{}.lib", + out_dir, version_major + )) }; assert!(compiler_cmd.output().unwrap().status.success()); if let Ok(output_path) = env::var("DOKAN_DLL_OUTPUT_PATH") { @@ -120,7 +134,11 @@ fn main() { let version = run_generator(&compiler); assert_eq!( format!("dokan{}", version), - env::var("CARGO_PKG_VERSION").unwrap().split('+').last().unwrap(), + env::var("CARGO_PKG_VERSION") + .unwrap() + .split('+') + .last() + .unwrap(), "Mismatch detected between crate version and bundled Dokan source version.", ); let version_major = &version[..1]; diff --git a/dokan-sys/src/lib.rs b/dokan-sys/src/lib.rs index 124400d..f971103 100644 --- a/dokan-sys/src/lib.rs +++ b/dokan-sys/src/lib.rs @@ -16,7 +16,10 @@ extern crate winapi; use libc::c_int; use winapi::shared::basetsd::ULONG64; use winapi::shared::minwindef::{BOOL, DWORD, FILETIME, LPCVOID, LPDWORD, LPVOID, MAX_PATH}; -use winapi::shared::ntdef::{BOOLEAN, HANDLE, LONGLONG, LPCWSTR, LPWSTR, NTSTATUS, PULONG, PULONGLONG, PVOID64, UCHAR, ULONG, UNICODE_STRING, USHORT, WCHAR}; +use winapi::shared::ntdef::{ + BOOLEAN, HANDLE, LONGLONG, LPCWSTR, LPWSTR, NTSTATUS, PULONG, PULONGLONG, PVOID64, UCHAR, + ULONG, UNICODE_STRING, USHORT, WCHAR, +}; use winapi::um::fileapi::LPBY_HANDLE_FILE_INFORMATION; use winapi::um::minwinbase::PWIN32_FIND_DATAW; use winapi::um::winnt::{ACCESS_MASK, PSECURITY_DESCRIPTOR, PSECURITY_INFORMATION}; @@ -40,7 +43,7 @@ pub const DOKAN_OPTION_ENABLE_NOTIFICATION_API: ULONG = 512; pub const DOKAN_OPTION_ENABLE_FCB_GARBAGE_COLLECTION: ULONG = 2048; pub const DOKAN_OPTION_CASE_SENSITIVE: ULONG = 4096; pub const DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE: ULONG = 8192; -pub const DOKAN_OPTION_DISPATCH_DRIVER_LOGS: ULONG = 16384; +pub const DOKAN_OPTION_DISPATCH_DRIVER_LOGS: ULONG = 16384; #[repr(C)] #[derive(Debug)] @@ -76,7 +79,8 @@ pub struct DOKAN_FILE_INFO { pub type PDOKAN_FILE_INFO = *mut DOKAN_FILE_INFO; pub type PFillFindData = unsafe extern "stdcall" fn(PWIN32_FIND_DATAW, PDOKAN_FILE_INFO) -> c_int; -pub type PFillFindStreamData = unsafe extern "stdcall" fn(PWIN32_FIND_STREAM_DATA, PDOKAN_FILE_INFO) -> c_int; +pub type PFillFindStreamData = + unsafe extern "stdcall" fn(PWIN32_FIND_STREAM_DATA, PDOKAN_FILE_INFO) -> c_int; #[repr(C)] pub struct DOKAN_ACCESS_STATE { @@ -106,150 +110,170 @@ pub type PDOKAN_IO_SECURITY_CONTEXT = *mut DOKAN_IO_SECURITY_CONTEXT; #[repr(C)] #[derive(Clone)] pub struct DOKAN_OPERATIONS { - pub ZwCreateFile: Option NTSTATUS>, - pub Cleanup: Option, - pub CloseFile: Option, - pub ReadFile: Option NTSTATUS>, - pub WriteFile: Option NTSTATUS>, - pub FlushFileBuffers: Option NTSTATUS>, - pub GetFileInformation: Option NTSTATUS>, - pub FindFiles: Option NTSTATUS>, - pub FindFilesWithPattern: Option NTSTATUS>, - pub SetFileAttributes: Option NTSTATUS>, - pub SetFileTime: Option NTSTATUS>, - pub DeleteFile: Option NTSTATUS>, - pub DeleteDirectory: Option NTSTATUS>, - pub MoveFile: Option NTSTATUS>, - pub SetEndOfFile: Option NTSTATUS>, - pub SetAllocationSize: Option NTSTATUS>, - pub LockFile: Option NTSTATUS>, - pub UnlockFile: Option NTSTATUS>, - pub GetDiskFreeSpace: Option NTSTATUS>, - pub GetVolumeInformation: Option NTSTATUS>, - pub Mounted: Option NTSTATUS>, - pub Unmounted: Option NTSTATUS>, - pub GetFileSecurity: Option NTSTATUS>, - pub SetFileSecurity: Option NTSTATUS>, - pub FindStreams: Option NTSTATUS>, + pub ZwCreateFile: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + SecurityContext: PDOKAN_IO_SECURITY_CONTEXT, + DesiredAccess: ACCESS_MASK, + FileAttributes: ULONG, + ShareAccess: ULONG, + CreateDisposition: ULONG, + CreateOptions: ULONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub Cleanup: Option, + pub CloseFile: Option, + pub ReadFile: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + Buffer: LPVOID, + BufferLength: DWORD, + ReadLength: LPDWORD, + Offset: LONGLONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub WriteFile: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + Buffer: LPCVOID, + NumberOfBytesToWrite: DWORD, + NumberOfBytesWritten: LPDWORD, + Offset: LONGLONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub FlushFileBuffers: + Option NTSTATUS>, + pub GetFileInformation: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + Buffer: LPBY_HANDLE_FILE_INFORMATION, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub FindFiles: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + FillFindData: PFillFindData, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub FindFilesWithPattern: Option< + extern "stdcall" fn( + PathName: LPCWSTR, + SearchPattern: LPCWSTR, + FillFindData: PFillFindData, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub SetFileAttributes: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + FileAttributes: DWORD, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub SetFileTime: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + creation_time: *const FILETIME, + last_access_time: *const FILETIME, + last_write_time: *const FILETIME, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub DeleteFile: + Option NTSTATUS>, + pub DeleteDirectory: + Option NTSTATUS>, + pub MoveFile: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + NewFileName: LPCWSTR, + ReplaceIfExisting: BOOL, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub SetEndOfFile: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + ByteOffset: LONGLONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub SetAllocationSize: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + AllocSize: LONGLONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub LockFile: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + ByteOffset: LONGLONG, + Length: LONGLONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub UnlockFile: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + ByteOffset: LONGLONG, + Length: LONGLONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub GetDiskFreeSpace: Option< + extern "stdcall" fn( + FreeBytesAvailable: PULONGLONG, + TotalNumberOfBytes: PULONGLONG, + TotalNumberOfFreeBytes: PULONGLONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub GetVolumeInformation: Option< + extern "stdcall" fn( + VolumeNameBuffer: LPWSTR, + VolumeNameSize: DWORD, + VolumeSerialNumber: LPDWORD, + MaximumComponentLength: LPDWORD, + FileSystemFlags: LPDWORD, + FileSystemNameBuffer: LPWSTR, + FileSystemNameSize: DWORD, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub Mounted: Option NTSTATUS>, + pub Unmounted: Option NTSTATUS>, + pub GetFileSecurity: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + PSECURITY_INFORMATION: PSECURITY_INFORMATION, + PSECURITY_DESCRIPTOR: PSECURITY_DESCRIPTOR, + BufferLength: ULONG, + LengthNeeded: PULONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub SetFileSecurity: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + SecurityInformation: PSECURITY_INFORMATION, + SecurityDescriptor: PSECURITY_DESCRIPTOR, + BufferLength: ULONG, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, + pub FindStreams: Option< + extern "stdcall" fn( + FileName: LPCWSTR, + FillFindStreamData: PFillFindStreamData, + DokanFileInfo: PDOKAN_FILE_INFO, + ) -> NTSTATUS, + >, } pub type PDOKAN_OPERATIONS = *mut DOKAN_OPERATIONS; @@ -287,14 +311,24 @@ extern "stdcall" { pub fn DokanGetMountPointList(uncOnly: BOOL, nbRead: PULONG) -> PDOKAN_CONTROL; pub fn DokanReleaseMountPointList(list: PDOKAN_CONTROL); pub fn DokanMapKernelToUserCreateFileFlags( - DesiredAccess: ACCESS_MASK, FileAttributes: ULONG, CreateOptions: ULONG, CreateDisposition: ULONG, - outDesiredAccess: *mut ACCESS_MASK, outFileAttributesAndFlags: *mut DWORD, outCreationDisposition: *mut DWORD, + DesiredAccess: ACCESS_MASK, + FileAttributes: ULONG, + CreateOptions: ULONG, + CreateDisposition: ULONG, + outDesiredAccess: *mut ACCESS_MASK, + outFileAttributesAndFlags: *mut DWORD, + outCreationDisposition: *mut DWORD, ); pub fn DokanNotifyCreate(FilePath: LPCWSTR, IsDirectory: BOOL) -> BOOL; pub fn DokanNotifyDelete(FilePath: LPCWSTR, IsDirectory: BOOL) -> BOOL; pub fn DokanNotifyUpdate(FilePath: LPCWSTR) -> BOOL; pub fn DokanNotifyXAttrUpdate(FilePath: LPCWSTR) -> BOOL; - pub fn DokanNotifyRename(OldPath: LPCWSTR, NewPath: LPCWSTR, IsDirectory: BOOL, IsInSameDirectory: BOOL) -> BOOL; + pub fn DokanNotifyRename( + OldPath: LPCWSTR, + NewPath: LPCWSTR, + IsDirectory: BOOL, + IsInSameDirectory: BOOL, + ) -> BOOL; pub fn DokanNtStatusFromWin32(Error: DWORD) -> NTSTATUS; pub fn DokanUseStdErr(Status: BOOL); pub fn DokanDebugMode(Status: BOOL); diff --git a/dokan/examples/memfs/err_utils.rs b/dokan/examples/memfs/err_utils.rs index b586773..8257dbe 100644 --- a/dokan/examples/memfs/err_utils.rs +++ b/dokan/examples/memfs/err_utils.rs @@ -11,9 +11,7 @@ pub fn nt_res(stat: ntdef::NTSTATUS) -> Result { } fn win32_last_err() -> OperationError { - unsafe { - OperationError::Win32(errhandlingapi::GetLastError()) - } + unsafe { OperationError::Win32(errhandlingapi::GetLastError()) } } pub fn win32_last_res() -> Result { diff --git a/dokan/examples/memfs/main.rs b/dokan/examples/memfs/main.rs index f78b513..9ea9d95 100644 --- a/dokan/examples/memfs/main.rs +++ b/dokan/examples/memfs/main.rs @@ -7,23 +7,23 @@ use std::borrow::Borrow; use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::os::windows::io::AsRawHandle; -use std::sync::{Arc, Mutex, RwLock, Weak}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex, RwLock, Weak}; use std::time::SystemTime; use clap::{App, Arg}; use dokan::*; -use widestring::{U16CStr, U16Str, U16CString, U16String}; +use widestring::{U16CStr, U16CString, U16Str, U16String}; use winapi::shared::{ntdef, ntstatus::*}; use winapi::um::winnt; -mod security; mod err_utils; mod path; +mod security; -use security::SecurityDescriptor; use err_utils::*; use path::FullName; +use security::SecurityDescriptor; #[derive(Debug)] struct AltStream { @@ -49,10 +49,16 @@ struct Attributes { impl Attributes { fn new(attrs: u32) -> Self { - const SUPPORTED_ATTRS: u32 = winnt::FILE_ATTRIBUTE_ARCHIVE | winnt::FILE_ATTRIBUTE_HIDDEN - | winnt::FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | winnt::FILE_ATTRIBUTE_OFFLINE | winnt::FILE_ATTRIBUTE_READONLY - | winnt::FILE_ATTRIBUTE_SYSTEM | winnt::FILE_ATTRIBUTE_TEMPORARY; - Self { value: attrs & SUPPORTED_ATTRS } + const SUPPORTED_ATTRS: u32 = winnt::FILE_ATTRIBUTE_ARCHIVE + | winnt::FILE_ATTRIBUTE_HIDDEN + | winnt::FILE_ATTRIBUTE_NOT_CONTENT_INDEXED + | winnt::FILE_ATTRIBUTE_OFFLINE + | winnt::FILE_ATTRIBUTE_READONLY + | winnt::FILE_ATTRIBUTE_SYSTEM + | winnt::FILE_ATTRIBUTE_TEMPORARY; + Self { + value: attrs & SUPPORTED_ATTRS, + } } fn get_output_attrs(&self, is_dir: bool) -> u32 { @@ -67,7 +73,6 @@ impl Attributes { } } - #[derive(Debug)] struct Stat { id: u64, @@ -115,7 +120,9 @@ struct EntryNameRef(U16Str); fn u16_tolower(c: u16) -> u16 { if c >= 'A' as u16 && c <= 'Z' as u16 { c + 'a' as u16 - 'A' as u16 - } else { c } + } else { + c + } } impl Hash for EntryNameRef { @@ -128,8 +135,13 @@ impl Hash for EntryNameRef { impl PartialEq for EntryNameRef { fn eq(&self, other: &Self) -> bool { - if self.0.len() != other.0.len() { false } else { - self.0.as_slice().iter().zip(other.0.as_slice()) + if self.0.len() != other.0.len() { + false + } else { + self.0 + .as_slice() + .iter() + .zip(other.0.as_slice()) .all(|(c1, c2)| u16_tolower(*c1) == u16_tolower(*c2)) } } @@ -262,7 +274,11 @@ struct EntryHandle { } impl EntryHandle { - fn new(entry: Entry, alt_stream: Option>>, delete_on_close: bool) -> Self { + fn new( + entry: Entry, + alt_stream: Option>>, + delete_on_close: bool, + ) -> Self { entry.stat().write().unwrap().handle_count += 1; if let Some(s) = &alt_stream { s.write().unwrap().handle_count += 1; @@ -280,7 +296,11 @@ impl EntryHandle { } fn is_dir(&self) -> bool { - if self.alt_stream.read().unwrap().is_some() { false } else { self.entry.is_dir() } + if self.alt_stream.read().unwrap().is_some() { + false + } else { + self.entry.is_dir() + } } fn update_atime(&self, stat: &mut Stat, atime: SystemTime) { @@ -303,8 +323,7 @@ impl Drop for EntryHandle { // create_file. let parent = self.entry.stat().read().unwrap().parent.upgrade(); // Lock parent before checking. This avoids racing with create_file. - let parent_children = parent.as_ref() - .map(|p| p.children.write().unwrap()); + let parent_children = parent.as_ref().map(|p| p.children.write().unwrap()); let mut stat = self.entry.stat().write().unwrap(); if self.delete_on_close && self.alt_stream.read().unwrap().is_none() { stat.delete_pending = true; @@ -313,12 +332,22 @@ impl Drop for EntryHandle { if stat.delete_pending && stat.handle_count == 0 { // The result of upgrade() can be safely unwrapped here because the root directory is the only case when the // reference can be null, which has been handled in delete_directory. - parent.as_ref().unwrap().stat.write().unwrap().update_mtime(SystemTime::now()); + parent + .as_ref() + .unwrap() + .stat + .write() + .unwrap() + .update_mtime(SystemTime::now()); let mut parent_children = parent_children.unwrap(); - let key = parent_children.iter().find_map(|(k, v)| { - if &self.entry == v { Some(k) } else { None } - }).unwrap().clone(); - parent_children.remove(Borrow::::borrow(&key)).unwrap(); + let key = parent_children + .iter() + .find_map(|(k, v)| if &self.entry == v { Some(k) } else { None }) + .unwrap() + .clone(); + parent_children + .remove(Borrow::::borrow(&key)) + .unwrap(); } else { // Ignore root directory. stat.delete_pending = false @@ -332,10 +361,21 @@ impl Drop for EntryHandle { } stream_locked.handle_count -= 1; if stream_locked.delete_pending && stream_locked.handle_count == 0 { - let key = stat.alt_streams.iter().find_map(|(k, v)| { - if Arc::ptr_eq(stream, v) { Some(k) } else { None } - }).unwrap().clone(); - stat.alt_streams.remove(Borrow::::borrow(&key)).unwrap(); + let key = stat + .alt_streams + .iter() + .find_map(|(k, v)| { + if Arc::ptr_eq(stream, v) { + Some(k) + } else { + None + } + }) + .unwrap() + .clone(); + stat.alt_streams + .remove(Borrow::::borrow(&key)) + .unwrap(); self.update_atime(&mut stat, SystemTime::now()); } } @@ -353,7 +393,8 @@ impl MemFsHandler { Self { id_counter: AtomicU64::new(1), root: Arc::new(DirEntry::new(Stat::new( - 0, 0, + 0, + 0, SecurityDescriptor::new_default().unwrap(), Weak::new(), ))), @@ -383,19 +424,26 @@ impl MemFsHandler { attrs, SecurityDescriptor::new_inherited( &parent.stat.read().unwrap().sec_desc, - creator_desc, token, is_dir, + creator_desc, + token, + is_dir, )?, Arc::downgrade(&parent), ); let stream = if let Some(stream_info) = &name.stream_info { - if stream_info.check_default(is_dir)? { None } else { + if stream_info.check_default(is_dir)? { + None + } else { let stream = Arc::new(RwLock::new(AltStream::new())); - assert!(stat.alt_streams + assert!(stat + .alt_streams .insert(EntryName(stream_info.name.to_owned()), Arc::clone(&stream)) .is_none()); Some(stream) } - } else { None }; + } else { + None + }; let entry = if is_dir { Entry::Directory(Arc::new(DirEntry::new(stat))) } else { @@ -460,13 +508,14 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { if let Some(entry) = children.get(EntryNameRef::new(name.file_name)) { let stat = entry.stat().read().unwrap(); let is_readonly = stat.attrs.value & winnt::FILE_ATTRIBUTE_READONLY > 0; - let is_hidden_system = stat.attrs.value & winnt::FILE_ATTRIBUTE_HIDDEN > 0 && - stat.attrs.value & winnt::FILE_ATTRIBUTE_SYSTEM > 0 && - !(file_attributes & winnt::FILE_ATTRIBUTE_HIDDEN > 0 && - file_attributes & winnt::FILE_ATTRIBUTE_SYSTEM > 0); - if is_readonly && - (desired_access & winnt::FILE_WRITE_DATA > 0 || - desired_access & winnt::FILE_APPEND_DATA > 0) { + let is_hidden_system = stat.attrs.value & winnt::FILE_ATTRIBUTE_HIDDEN > 0 + && stat.attrs.value & winnt::FILE_ATTRIBUTE_SYSTEM > 0 + && !(file_attributes & winnt::FILE_ATTRIBUTE_HIDDEN > 0 + && file_attributes & winnt::FILE_ATTRIBUTE_SYSTEM > 0); + if is_readonly + && (desired_access & winnt::FILE_WRITE_DATA > 0 + || desired_access & winnt::FILE_APPEND_DATA > 0) + { return nt_res(STATUS_ACCESS_DENIED); } if stat.delete_pending { @@ -477,12 +526,13 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { } std::mem::drop(stat); let ret = if let Some(stream_info) = &name.stream_info { - if stream_info.check_default(entry.is_dir())? { None } else { + if stream_info.check_default(entry.is_dir())? { + None + } else { let mut stat = entry.stat().write().unwrap(); let stream_name = EntryNameRef::new(stream_info.name); - if let Some(stream) = stat.alt_streams - .get(stream_name) - .map(|s| Arc::clone(s)) + if let Some(stream) = + stat.alt_streams.get(stream_name).map(|s| Arc::clone(s)) { if stream.read().unwrap().delete_pending { return nt_res(STATUS_DELETE_PENDING); @@ -501,7 +551,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { } Some((stream, false)) } else { - if create_disposition == FILE_OPEN || create_disposition == FILE_OVERWRITE { + if create_disposition == FILE_OPEN + || create_disposition == FILE_OVERWRITE + { return nt_res(STATUS_OBJECT_NAME_NOT_FOUND); } if is_readonly { @@ -509,13 +561,16 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { } let stream = Arc::new(RwLock::new(AltStream::new())); stat.update_atime(SystemTime::now()); - assert!(stat.alt_streams + assert!(stat + .alt_streams .insert(EntryName(stream_info.name.to_owned()), Arc::clone(&stream)) .is_none()); Some((stream, true)) } } - } else { None }; + } else { + None + }; if let Some((stream, new_file_created)) = ret { return Ok(CreateFileInfo { context: EntryHandle::new(entry.clone(), Some(stream), delete_on_close), @@ -530,19 +585,27 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { } match create_disposition { FILE_SUPERSEDE | FILE_OVERWRITE | FILE_OVERWRITE_IF => { - if create_disposition != FILE_SUPERSEDE && is_readonly || is_hidden_system { + if create_disposition != FILE_SUPERSEDE && is_readonly + || is_hidden_system + { return nt_res(STATUS_ACCESS_DENIED); } file.data.write().unwrap().clear(); let mut stat = file.stat.write().unwrap(); - stat.attrs = Attributes::new(file_attributes | winnt::FILE_ATTRIBUTE_ARCHIVE); + stat.attrs = Attributes::new( + file_attributes | winnt::FILE_ATTRIBUTE_ARCHIVE, + ); stat.update_mtime(SystemTime::now()); } FILE_CREATE => return nt_res(STATUS_OBJECT_NAME_COLLISION), _ => (), } Ok(CreateFileInfo { - context: EntryHandle::new(Entry::File(Arc::clone(file)), None, delete_on_close), + context: EntryHandle::new( + Entry::File(Arc::clone(file)), + None, + delete_on_close, + ), is_dir: false, new_file_created: false, }) @@ -552,13 +615,15 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { return nt_res(STATUS_FILE_IS_A_DIRECTORY); } match create_disposition { - FILE_OPEN | FILE_OPEN_IF => { - Ok(CreateFileInfo { - context: EntryHandle::new(Entry::Directory(Arc::clone(dir)), None, delete_on_close), - is_dir: true, - new_file_created: false, - }) - } + FILE_OPEN | FILE_OPEN_IF => Ok(CreateFileInfo { + context: EntryHandle::new( + Entry::Directory(Arc::clone(dir)), + None, + delete_on_close, + ), + is_dir: true, + new_file_created: false, + }), FILE_CREATE => nt_res(STATUS_OBJECT_NAME_COLLISION), _ => nt_res(STATUS_INVALID_PARAMETER), } @@ -571,18 +636,16 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { let token = info.requester_token().unwrap(); if create_options & FILE_DIRECTORY_FILE > 0 { match create_disposition { - FILE_CREATE | FILE_OPEN_IF => { - self.create_new( - &name, - file_attributes, - delete_on_close, - security_context.AccessState.SecurityDescriptor, - token.as_raw_handle(), - &parent, - &mut children, - true, - ) - } + FILE_CREATE | FILE_OPEN_IF => self.create_new( + &name, + file_attributes, + delete_on_close, + security_context.AccessState.SecurityDescriptor, + token.as_raw_handle(), + &parent, + &mut children, + true, + ), FILE_OPEN => nt_res(STATUS_OBJECT_NAME_NOT_FOUND), _ => nt_res(STATUS_INVALID_PARAMETER), } @@ -611,7 +674,8 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { Ok(CreateFileInfo { context: EntryHandle::new( Entry::Directory(Arc::clone(&self.root)), - None, info.delete_on_close(), + None, + info.delete_on_close(), ), is_dir: true, new_file_created: false, @@ -675,7 +739,11 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { context: &'a Self::Context, ) -> Result { let do_write = |data: &mut Vec<_>| { - let offset = if info.write_to_eof() { data.len() } else { offset as usize }; + let offset = if info.write_to_eof() { + data.len() + } else { + offset as usize + }; let len = buffer.len(); if offset + len > data.len() { data.resize(offset + len, 0); @@ -795,17 +863,17 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { context: &'a Self::Context, ) -> Result<(), OperationError> { let mut stat = context.entry.stat().write().unwrap(); - let process_time_info = |time_info: &FileTimeInfo, time: &mut SystemTime, flag: &AtomicBool| { - match time_info { - FileTimeInfo::SetTime(new_time) => + let process_time_info = + |time_info: &FileTimeInfo, time: &mut SystemTime, flag: &AtomicBool| match time_info { + FileTimeInfo::SetTime(new_time) => { if flag.load(Ordering::Relaxed) { *time = *new_time - }, + } + } FileTimeInfo::DisableUpdate => flag.store(false, Ordering::Relaxed), FileTimeInfo::ResumeUpdate => flag.store(true, Ordering::Relaxed), FileTimeInfo::DontChange => (), - } - }; + }; process_time_info(&creation_time, &mut stat.ctime, &context.ctime_enabled); process_time_info(&last_write_time, &mut stat.mtime, &context.mtime_enabled); process_time_info(&last_access_time, &mut stat.atime, &context.atime_enabled); @@ -830,7 +898,6 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { Ok(()) } - fn delete_directory( &'b self, _file_name: &U16CStr, @@ -868,24 +935,41 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { context: &'a Self::Context, ) -> Result<(), OperationError> { let src_path = file_name.as_slice(); - let offset = src_path.iter().rposition(|x| *x == '\\' as u16) + let offset = src_path + .iter() + .rposition(|x| *x == '\\' as u16) .ok_or(nt_err(STATUS_INVALID_PARAMETER))?; let src_name = U16Str::from_slice(&src_path[offset + 1..]); - let src_parent = context.entry.stat().read().unwrap().parent.upgrade() + let src_parent = context + .entry + .stat() + .read() + .unwrap() + .parent + .upgrade() .ok_or(nt_err(STATUS_INVALID_DEVICE_REQUEST))?; if new_file_name.as_slice().first() == Some(&(':' as u16)) { let src_stream_info = FullName::new(src_name)?.stream_info; - let dst_stream_info = FullName::new(U16Str::from_slice(new_file_name.as_slice()))?.stream_info; + let dst_stream_info = + FullName::new(U16Str::from_slice(new_file_name.as_slice()))?.stream_info; let src_is_default = context.alt_stream.read().unwrap().is_none(); let dst_is_default = if let Some(stream_info) = &dst_stream_info { stream_info.check_default(context.entry.is_dir())? - } else { true }; - let check_can_move = |streams: &mut HashMap>>, name: &U16Str| { + } else { + true + }; + let check_can_move = |streams: &mut HashMap>>, + name: &U16Str| { let name_ref = EntryNameRef::new(name); if let Some(stream) = streams.get(name_ref) { - if context.alt_stream.read().unwrap().as_ref() + if context + .alt_stream + .read() + .unwrap() + .as_ref() .map(|s| Arc::ptr_eq(s, stream)) - .unwrap_or(false) { + .unwrap_or(false) + { Ok(()) } else if !replace_if_existing { nt_res(STATUS_OBJECT_NAME_COLLISION) @@ -895,56 +979,71 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { streams.remove(name_ref).unwrap(); Ok(()) } - } else { Ok(()) } + } else { + Ok(()) + } }; let mut stat = context.entry.stat().write().unwrap(); match (src_is_default, dst_is_default) { - (true, true) => if context.entry.is_dir() { - return nt_res(STATUS_OBJECT_NAME_INVALID); - }, - (true, false) => if let Entry::File(file) = &context.entry { - let dst_name = dst_stream_info.unwrap().name; - check_can_move(&mut stat.alt_streams, dst_name)?; - let mut stream = AltStream::new(); - let mut data = file.data.write().unwrap(); - stream.handle_count = 1; - stream.delete_pending = stat.delete_pending; - stat.delete_pending = false; - stream.data = data.clone(); - data.clear(); - let stream = Arc::new(RwLock::new(stream)); - assert!(stat.alt_streams - .insert(EntryName(dst_name.to_owned()), Arc::clone(&stream)) - .is_none()); - *context.alt_stream.write().unwrap() = Some(stream); - } else { - return nt_res(STATUS_OBJECT_NAME_INVALID); + (true, true) => { + if context.entry.is_dir() { + return nt_res(STATUS_OBJECT_NAME_INVALID); + } } - (false, true) => if let Entry::File(file) = &context.entry { - let mut context_stream = context.alt_stream.write().unwrap(); - let src_stream = context_stream.as_ref().unwrap(); - let mut src_stream_locked = src_stream.write().unwrap(); - if src_stream_locked.handle_count > 1 { - return nt_res(STATUS_SHARING_VIOLATION); + (true, false) => { + if let Entry::File(file) = &context.entry { + let dst_name = dst_stream_info.unwrap().name; + check_can_move(&mut stat.alt_streams, dst_name)?; + let mut stream = AltStream::new(); + let mut data = file.data.write().unwrap(); + stream.handle_count = 1; + stream.delete_pending = stat.delete_pending; + stat.delete_pending = false; + stream.data = data.clone(); + data.clear(); + let stream = Arc::new(RwLock::new(stream)); + assert!(stat + .alt_streams + .insert(EntryName(dst_name.to_owned()), Arc::clone(&stream)) + .is_none()); + *context.alt_stream.write().unwrap() = Some(stream); + } else { + return nt_res(STATUS_OBJECT_NAME_INVALID); } - if !replace_if_existing { - return nt_res(STATUS_OBJECT_NAME_COLLISION); + } + (false, true) => { + if let Entry::File(file) = &context.entry { + let mut context_stream = context.alt_stream.write().unwrap(); + let src_stream = context_stream.as_ref().unwrap(); + let mut src_stream_locked = src_stream.write().unwrap(); + if src_stream_locked.handle_count > 1 { + return nt_res(STATUS_SHARING_VIOLATION); + } + if !replace_if_existing { + return nt_res(STATUS_OBJECT_NAME_COLLISION); + } + src_stream_locked.handle_count -= 1; + stat.delete_pending = src_stream_locked.delete_pending; + src_stream_locked.delete_pending = false; + *file.data.write().unwrap() = src_stream_locked.data.clone(); + stat.alt_streams + .remove(EntryNameRef::new(src_stream_info.unwrap().name)) + .unwrap(); + std::mem::drop(src_stream_locked); + *context_stream = None; + } else { + return nt_res(STATUS_OBJECT_NAME_INVALID); } - src_stream_locked.handle_count -= 1; - stat.delete_pending = src_stream_locked.delete_pending; - src_stream_locked.delete_pending = false; - *file.data.write().unwrap() = src_stream_locked.data.clone(); - stat.alt_streams.remove(EntryNameRef::new(src_stream_info.unwrap().name)).unwrap(); - std::mem::drop(src_stream_locked); - *context_stream = None; - } else { - return nt_res(STATUS_OBJECT_NAME_INVALID); } (false, false) => { let dst_name = dst_stream_info.unwrap().name; check_can_move(&mut stat.alt_streams, dst_name)?; - let stream = stat.alt_streams.remove(EntryNameRef::new(src_stream_info.unwrap().name)).unwrap(); - stat.alt_streams.insert(EntryName(dst_name.to_owned()), Arc::clone(&stream)); + let stream = stat + .alt_streams + .remove(EntryNameRef::new(src_stream_info.unwrap().name)) + .unwrap(); + stat.alt_streams + .insert(EntryName(dst_name.to_owned()), Arc::clone(&stream)); *context.alt_stream.write().unwrap() = Some(stream); } } @@ -971,8 +1070,8 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { nt_res(STATUS_ACCESS_DENIED) } else { let stat = entry.stat().read().unwrap(); - let can_replace = stat.handle_count > 0 || - stat.attrs.value & winnt::FILE_ATTRIBUTE_READONLY > 0; + let can_replace = stat.handle_count > 0 + || stat.attrs.value & winnt::FILE_ATTRIBUTE_READONLY > 0; std::mem::drop(stat); if can_replace { nt_res(STATUS_ACCESS_DENIED) @@ -981,14 +1080,18 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { Ok(()) } } - } else { Ok(()) } + } else { + Ok(()) + } }; if Arc::ptr_eq(&src_parent, &dst_parent) { let mut children = src_parent.children.write().unwrap(); check_can_move(&mut children)?; // Remove first in case moving to the same name. let entry = children.remove(src_name_ref).unwrap(); - assert!(children.insert(EntryName(dst_name.file_name.to_owned()), entry).is_none()); + assert!(children + .insert(EntryName(dst_name.file_name.to_owned()), entry) + .is_none()); src_parent.stat.write().unwrap().update_mtime(now); context.update_atime(&mut context.entry.stat().write().unwrap(), now); } else { @@ -996,7 +1099,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { let mut dst_children = dst_parent.children.write().unwrap(); check_can_move(&mut dst_children)?; let entry = src_children.remove(src_name_ref).unwrap(); - assert!(dst_children.insert(EntryName(dst_name.file_name.to_owned()), entry).is_none()); + assert!(dst_children + .insert(EntryName(dst_name.file_name.to_owned()), entry) + .is_none()); src_parent.stat.write().unwrap().update_mtime(now); dst_parent.stat.write().unwrap().update_mtime(now); let mut stat = context.entry.stat().write().unwrap(); @@ -1025,7 +1130,10 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { nt_res(STATUS_INVALID_DEVICE_REQUEST) }; if ret.is_ok() { - context.update_mtime(&mut context.entry.stat().write().unwrap(), SystemTime::now()); + context.update_mtime( + &mut context.entry.stat().write().unwrap(), + SystemTime::now(), + ); } ret } @@ -1061,7 +1169,10 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { nt_res(STATUS_INVALID_DEVICE_REQUEST) }; if ret.is_ok() { - context.update_mtime(&mut context.entry.stat().write().unwrap(), SystemTime::now()); + context.update_mtime( + &mut context.entry.stat().write().unwrap(), + SystemTime::now(), + ); } ret } @@ -1085,8 +1196,11 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { name: U16CString::from_str("dokan-rust memfs").unwrap(), serial_number: 0, max_component_length: path::MAX_COMPONENT_LENGTH, - fs_flags: winnt::FILE_CASE_PRESERVED_NAMES | winnt::FILE_CASE_SENSITIVE_SEARCH | winnt::FILE_UNICODE_ON_DISK - | winnt::FILE_PERSISTENT_ACLS | winnt::FILE_NAMED_STREAMS, + fs_flags: winnt::FILE_CASE_PRESERVED_NAMES + | winnt::FILE_CASE_SENSITIVE_SEARCH + | winnt::FILE_UNICODE_ON_DISK + | winnt::FILE_PERSISTENT_ACLS + | winnt::FILE_NAMED_STREAMS, // Custom names don't play well with UAC. fs_name: U16CString::from_str("NTFS").unwrap(), }) @@ -1101,11 +1215,13 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { _info: &OperationInfo<'a, 'b, Self>, context: &'a Self::Context, ) -> Result { - context.entry.stat().read().unwrap().sec_desc.get_security_info( - security_information, - security_descriptor, - buffer_length, - ) + context + .entry + .stat() + .read() + .unwrap() + .sec_desc + .get_security_info(security_information, security_descriptor, buffer_length) } fn set_file_security( @@ -1118,10 +1234,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { context: &'a Self::Context, ) -> Result<(), OperationError> { let mut stat = context.entry.stat().write().unwrap(); - let ret = stat.sec_desc.set_security_info( - security_information, - security_descriptor, - ); + let ret = stat + .sec_desc + .set_security_info(security_information, security_descriptor); if ret.is_ok() { context.update_atime(&mut stat, SystemTime::now()); } @@ -1159,16 +1274,36 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for MemFsHandler { fn main() -> Result<(), Box> { let matches = App::new("dokan-rust memfs example") .author(env!("CARGO_PKG_AUTHORS")) - .arg(Arg::with_name("mount_point").short("m").long("mount-point") - .takes_value(true).value_name("MOUNT_POINT").required(true) - .help("Mount point path.")) - .arg(Arg::with_name("thread_count").short("t").long("threads") - .takes_value(true).value_name("THREAD_COUNT").default_value("0") - .help("Thread count. Use \"0\" to let Dokan choose it automatically.")) - .arg(Arg::with_name("dokan_debug").short("d").long("dokan-debug") - .help("Enable Dokan's debug output.")) - .arg(Arg::with_name("removable").short("r").long("removable") - .help("Mount as a removable drive.")) + .arg( + Arg::with_name("mount_point") + .short("m") + .long("mount-point") + .takes_value(true) + .value_name("MOUNT_POINT") + .required(true) + .help("Mount point path."), + ) + .arg( + Arg::with_name("thread_count") + .short("t") + .long("threads") + .takes_value(true) + .value_name("THREAD_COUNT") + .default_value("0") + .help("Thread count. Use \"0\" to let Dokan choose it automatically."), + ) + .arg( + Arg::with_name("dokan_debug") + .short("d") + .long("dokan-debug") + .help("Enable Dokan's debug output."), + ) + .arg( + Arg::with_name("removable") + .short("r") + .long("removable") + .help("Mount as a removable drive."), + ) .get_matches(); let mount_point = U16CString::from_str(matches.value_of("mount_point").unwrap())?; let mut flags = MountFlags::ALT_STREAM; diff --git a/dokan/examples/memfs/path.rs b/dokan/examples/memfs/path.rs index a99bb23..5816be5 100644 --- a/dokan/examples/memfs/path.rs +++ b/dokan/examples/memfs/path.rs @@ -5,8 +5,8 @@ use dokan::OperationError; use widestring::{U16CStr, U16Str, U16String}; use winapi::shared::ntstatus::*; -use crate::{DirEntry, Entry, EntryName, EntryNameRef}; use crate::err_utils::*; +use crate::{DirEntry, Entry, EntryName, EntryNameRef}; // Use the same value as NTFS. pub const MAX_COMPONENT_LENGTH: u32 = 255; @@ -27,7 +27,9 @@ pub struct StreamInfo<'a> { impl StreamInfo<'_> { pub fn check_default(&self, is_dir: bool) -> Result { if is_dir { - if self.name.is_empty() || EntryNameRef::new(self.name) == EntryName(U16String::from_str("$I30")).borrow() { + if self.name.is_empty() + || EntryNameRef::new(self.name) == EntryName(U16String::from_str("$I30")).borrow() + { if self.type_ == StreamType::IndexAllocation { Ok(true) } else { @@ -59,10 +61,15 @@ impl<'a> FullName<'a> { let file_name = U16Str::from_slice(&name_slice[..offset1]); let stream_info = &name_slice[offset1 + 1..]; if let Some(offset2) = stream_info.iter().position(|x| *x == ':' as u16) { - let stream_type_str = EntryNameRef::new(U16Str::from_slice(&stream_info[offset2 + 1..])); - let stream_type = if stream_type_str == EntryName(U16String::from_str("$DATA")).borrow() { + let stream_type_str = + EntryNameRef::new(U16Str::from_slice(&stream_info[offset2 + 1..])); + let stream_type = if stream_type_str + == EntryName(U16String::from_str("$DATA")).borrow() + { StreamType::Data - } else if stream_type_str == EntryName(U16String::from_str("$INDEX_ALLOCATION")).borrow() { + } else if stream_type_str + == EntryName(U16String::from_str("$INDEX_ALLOCATION")).borrow() + { StreamType::IndexAllocation } else if stream_type_str == EntryName(U16String::from_str("$BITMAP")).borrow() { StreamType::Bitmap @@ -94,14 +101,22 @@ impl<'a> FullName<'a> { } } -fn find_dir_entry(cur_entry: &Arc, path: &[&U16Str]) -> Result, OperationError> { +fn find_dir_entry( + cur_entry: &Arc, + path: &[&U16Str], +) -> Result, OperationError> { if let Some(name) = path.get(0) { if name.len() > MAX_COMPONENT_LENGTH as usize { return nt_res(STATUS_OBJECT_NAME_INVALID); } - match cur_entry.children.read().unwrap().get(EntryNameRef::new(name)) { + match cur_entry + .children + .read() + .unwrap() + .get(EntryNameRef::new(name)) + { Some(Entry::Directory(dir)) => find_dir_entry(dir, &path[1..]), - _ => nt_res(STATUS_OBJECT_PATH_NOT_FOUND) + _ => nt_res(STATUS_OBJECT_PATH_NOT_FOUND), } } else { Ok(Arc::clone(cur_entry)) @@ -112,19 +127,22 @@ pub fn split_path<'a>( root: &Arc, path: &'a U16CStr, ) -> Result, Arc)>, OperationError> { - let path = path.as_slice() + let path = path + .as_slice() .split(|x| *x == '\\' as u16) .filter(|s| !s.is_empty()) .map(|s| U16Str::from_slice(s)) .collect::>(); - if path.is_empty() { Ok(None) } else { + if path.is_empty() { + Ok(None) + } else { let name = *path.iter().last().unwrap(); if name.len() > MAX_COMPONENT_LENGTH as usize { return nt_res(STATUS_OBJECT_NAME_INVALID); } Ok(Some(( FullName::new(name)?, - find_dir_entry(root, &path[..path.len() - 1])? + find_dir_entry(root, &path[..path.len() - 1])?, ))) } } diff --git a/dokan/examples/memfs/security.rs b/dokan/examples/memfs/security.rs index 9f20b52..d615285 100644 --- a/dokan/examples/memfs/security.rs +++ b/dokan/examples/memfs/security.rs @@ -1,5 +1,5 @@ -use std::{mem, ptr}; use std::pin::Pin; +use std::{mem, ptr}; use dokan::OperationError; use winapi::shared::{minwindef, ntdef, ntstatus::*, winerror}; @@ -14,9 +14,7 @@ struct PrivateObjectSecurity { impl PrivateObjectSecurity { unsafe fn from_raw(ptr: winnt::PSECURITY_DESCRIPTOR) -> Self { - Self { - value: ptr, - } + Self { value: ptr } } } @@ -30,7 +28,7 @@ impl Drop for PrivateObjectSecurity { #[derive(Debug)] pub struct SecurityDescriptor { - desc_ptr: winnt::PSECURITY_DESCRIPTOR + desc_ptr: winnt::PSECURITY_DESCRIPTOR, } unsafe impl Sync for SecurityDescriptor {} @@ -39,7 +37,8 @@ unsafe impl Send for SecurityDescriptor {} fn get_well_known_sid(sid_type: winnt::WELL_KNOWN_SID_TYPE) -> Result, OperationError> { unsafe { - let mut sid = vec![0u8; mem::size_of::() + mem::size_of::() * 7].into_boxed_slice(); + let mut sid = + vec![0u8; mem::size_of::() + mem::size_of::() * 7].into_boxed_slice(); let mut len = sid.len() as u32; let ret = securitybaseapi::CreateWellKnownSid( sid_type, @@ -47,7 +46,11 @@ fn get_well_known_sid(sid_type: winnt::WELL_KNOWN_SID_TYPE) -> Result, sid.as_mut_ptr() as winnt::PSID, &mut len, ); - if ret == minwindef::TRUE { Ok(sid) } else { win32_last_res() } + if ret == minwindef::TRUE { + Ok(sid) + } else { + win32_last_res() + } } } @@ -60,10 +63,14 @@ fn create_default_dacl() -> Result, OperationError> { let acl_len = mem::size_of::() + (mem::size_of::() - mem::size_of::()) * 4 - + admins_sid.len() + system_sid.len() + auth_sid.len() + users_sid.len(); + + admins_sid.len() + + system_sid.len() + + auth_sid.len() + + users_sid.len(); let mut acl = vec![0u8; acl_len].into_boxed_slice(); let ret = securitybaseapi::InitializeAcl( - acl.as_mut_ptr() as winnt::PACL, acl_len as u32, + acl.as_mut_ptr() as winnt::PACL, + acl_len as u32, winnt::ACL_REVISION as u32, ); if ret == minwindef::FALSE { @@ -95,7 +102,10 @@ fn create_default_dacl() -> Result, OperationError> { acl.as_mut_ptr() as winnt::PACL, winnt::ACL_REVISION as u32, flags, - winnt::FILE_GENERIC_READ | winnt::FILE_GENERIC_WRITE | winnt::FILE_GENERIC_EXECUTE | winnt::DELETE, + winnt::FILE_GENERIC_READ + | winnt::FILE_GENERIC_WRITE + | winnt::FILE_GENERIC_EXECUTE + | winnt::DELETE, auth_sid.as_ptr() as winnt::PSID, ); if ret == minwindef::FALSE { @@ -131,7 +141,9 @@ impl SecurityDescriptor { is_dir: bool, ) -> Result { unsafe { - if !creator_desc.is_null() && securitybaseapi::IsValidSecurityDescriptor(creator_desc) == minwindef::FALSE { + if !creator_desc.is_null() + && securitybaseapi::IsValidSecurityDescriptor(creator_desc) == minwindef::FALSE + { return nt_res(STATUS_INVALID_PARAMETER); } @@ -149,7 +161,6 @@ impl SecurityDescriptor { } let priv_desc = PrivateObjectSecurity::from_raw(priv_desc); - let heap = heapapi::GetProcessHeap(); if heap.is_null() { return win32_last_res(); @@ -182,21 +193,25 @@ impl SecurityDescriptor { let ret = securitybaseapi::SetSecurityDescriptorOwner( abs_desc_ptr, - owner_sid.as_ptr() as winnt::PSID, minwindef::FALSE, + owner_sid.as_ptr() as winnt::PSID, + minwindef::FALSE, ); if ret == minwindef::FALSE { return win32_last_res(); } let ret = securitybaseapi::SetSecurityDescriptorGroup( abs_desc_ptr, - group_sid.as_ptr() as winnt::PSID, minwindef::FALSE, + group_sid.as_ptr() as winnt::PSID, + minwindef::FALSE, ); if ret == minwindef::FALSE { return win32_last_res(); } let ret = securitybaseapi::SetSecurityDescriptorDacl( abs_desc_ptr, - minwindef::TRUE, dacl.as_ptr() as winnt::PACL, minwindef::FALSE, + minwindef::TRUE, + dacl.as_ptr() as winnt::PACL, + minwindef::FALSE, ); if ret == minwindef::FALSE { return win32_last_res(); @@ -245,7 +260,11 @@ impl SecurityDescriptor { sec_desc_len, &mut ret_len, ); - if ret == minwindef::TRUE { Ok(len) } else { win32_last_res() } + if ret == minwindef::TRUE { + Ok(len) + } else { + win32_last_res() + } } } @@ -267,7 +286,11 @@ impl SecurityDescriptor { &FILE_GENERIC_MAPPING as *const _ as *mut _, ptr::null_mut(), ); - if ret == minwindef::TRUE { Ok(()) } else { win32_last_res() } + if ret == minwindef::TRUE { + Ok(()) + } else { + win32_last_res() + } } } } diff --git a/dokan/src/lib.rs b/dokan/src/lib.rs index a2aa2e2..89bdc2b 100644 --- a/dokan/src/lib.rs +++ b/dokan/src/lib.rs @@ -31,14 +31,19 @@ use std::fmt::{self, Display, Formatter}; use std::marker::PhantomData; use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use std::{ptr, mem, panic, slice}; +use std::{mem, panic, ptr, slice}; -use dokan_sys::{*, win32::*}; +use dokan_sys::{win32::*, *}; use widestring::{U16CStr, U16CString}; use winapi::ctypes::c_int; -use winapi::shared::minwindef::{BOOL, DWORD, FALSE, FILETIME, LPCVOID, LPVOID, LPDWORD, MAX_PATH, PULONG, TRUE, ULONG}; -use winapi::shared::ntdef::{HANDLE, NTSTATUS, LONGLONG, LPCWSTR, LPWSTR, PULONGLONG}; -use winapi::shared::ntstatus::{STATUS_BUFFER_OVERFLOW, STATUS_INTERNAL_ERROR, STATUS_NOT_IMPLEMENTED, STATUS_OBJECT_NAME_COLLISION, STATUS_SUCCESS}; +use winapi::shared::minwindef::{ + BOOL, DWORD, FALSE, FILETIME, LPCVOID, LPDWORD, LPVOID, MAX_PATH, PULONG, TRUE, ULONG, +}; +use winapi::shared::ntdef::{HANDLE, LONGLONG, LPCWSTR, LPWSTR, NTSTATUS, PULONGLONG}; +use winapi::shared::ntstatus::{ + STATUS_BUFFER_OVERFLOW, STATUS_INTERNAL_ERROR, STATUS_NOT_IMPLEMENTED, + STATUS_OBJECT_NAME_COLLISION, STATUS_SUCCESS, +}; use winapi::um::fileapi::{BY_HANDLE_FILE_INFORMATION, LPBY_HANDLE_FILE_INFORMATION}; use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; use winapi::um::minwinbase::WIN32_FIND_DATAW; @@ -59,12 +64,16 @@ pub use dokan_sys::DOKAN_VERSION as WRAPPER_VERSION; /// /// The returned value is the version number without dots. For example, it returns `131` if Dokan /// v1.3.1 is loaded. -pub fn lib_version() -> u32 { unsafe { DokanVersion() } } +pub fn lib_version() -> u32 { + unsafe { DokanVersion() } +} /// Gets version of the Dokan driver installed on the current system. /// /// The returned value is the version number without dots. -pub fn driver_version() -> u32 { unsafe { DokanDriverVersion() } } +pub fn driver_version() -> u32 { + unsafe { DokanDriverVersion() } +} /// Checks whether the `name` matches the specified `expression`. /// @@ -177,7 +186,9 @@ struct MountPointListWrapper { impl Drop for MountPointListWrapper { fn drop(&mut self) { if !self.list_ptr.is_null() { - unsafe { DokanReleaseMountPointList(self.list_ptr); } + unsafe { + DokanReleaseMountPointList(self.list_ptr); + } } } } @@ -189,23 +200,39 @@ pub fn get_mount_point_list(unc_only: bool) -> Option> { unsafe { let mut count: ULONG = 0; let ffi_list = MountPointListWrapper { - list_ptr: DokanGetMountPointList(unc_only.into(), &mut count) + list_ptr: DokanGetMountPointList(unc_only.into(), &mut count), }; - if ffi_list.list_ptr.is_null() { None } else { + if ffi_list.list_ptr.is_null() { + None + } else { let count = count as usize; let mut list = Vec::with_capacity(count); for control in slice::from_raw_parts(ffi_list.list_ptr, count) { - let mount_point = if control.MountPoint[0] == 0 { None } else { - Some(U16CStr::from_slice_with_nul(&control.MountPoint).unwrap().to_owned()) + let mount_point = if control.MountPoint[0] == 0 { + None + } else { + Some( + U16CStr::from_slice_with_nul(&control.MountPoint) + .unwrap() + .to_owned(), + ) }; - let unc_name = if control.UNCName[0] == 0 { None } else { - Some(U16CStr::from_slice_with_nul(&control.UNCName).unwrap().to_owned()) + let unc_name = if control.UNCName[0] == 0 { + None + } else { + Some( + U16CStr::from_slice_with_nul(&control.UNCName) + .unwrap() + .to_owned(), + ) }; list.push(MountPointInfo { device_type: control.Type, mount_point, unc_name, - device_name: U16CStr::from_slice_with_nul(&control.DeviceName).unwrap().to_owned(), + device_name: U16CStr::from_slice_with_nul(&control.DeviceName) + .unwrap() + .to_owned(), session_id: control.SessionId, }) } @@ -252,11 +279,18 @@ pub fn notify_xattr_update(path: impl AsRef) -> bool { /// /// Returns `true` on success. #[must_use] -pub fn notify_rename(old_path: impl AsRef, new_path: impl AsRef, is_dir: bool, is_same_dir: bool) -> bool { +pub fn notify_rename( + old_path: impl AsRef, + new_path: impl AsRef, + is_dir: bool, + is_same_dir: bool, +) -> bool { unsafe { DokanNotifyRename( - old_path.as_ref().as_ptr(), new_path.as_ref().as_ptr(), - is_dir.into(), is_same_dir.into(), + old_path.as_ref().as_ptr(), + new_path.as_ref().as_ptr(), + is_dir.into(), + is_same_dir.into(), ) == TRUE } } @@ -276,7 +310,11 @@ pub enum DebugStream { /// Set the output stream to write debug messages to. pub fn set_debug_stream(stream: DebugStream) { unsafe { - DokanUseStdErr(if let DebugStream::Stdout = stream { TRUE } else { FALSE }); + DokanUseStdErr(if let DebugStream::Stdout = stream { + TRUE + } else { + FALSE + }); } } @@ -292,9 +330,7 @@ pub fn set_lib_debug_mode(enabled: bool) { /// Returns `true` on success. #[must_use] pub fn set_driver_debug_mode(enabled: bool) -> bool { - unsafe { - DokanSetDebugMode(if enabled { TRUE } else { FALSE }) == TRUE - } + unsafe { DokanSetDebugMode(if enabled { TRUE } else { FALSE }) == TRUE } } bitflags! { @@ -397,7 +433,9 @@ impl IntoRawHandle for TokenHandle { impl Drop for TokenHandle { fn drop(&mut self) { if self.value != INVALID_HANDLE_VALUE { - unsafe { CloseHandle(self.value); } + unsafe { + CloseHandle(self.value); + } } } } @@ -447,32 +485,50 @@ impl<'a, 'b: 'a, 'c: 'b, T: FileSystemHandler<'b, 'c> + 'c> OperationInfo<'b, 'c } /// Gets process ID of the calling process. - pub fn pid(&self) -> u32 { self.file_info().ProcessId } + pub fn pid(&self) -> u32 { + self.file_info().ProcessId + } /// Gets whether the target file is a directory. - pub fn is_dir(&self) -> bool { self.file_info().IsDirectory != 0 } + pub fn is_dir(&self) -> bool { + self.file_info().IsDirectory != 0 + } /// Gets whether the file should be deleted when it is closed. - pub fn delete_on_close(&self) -> bool { self.file_info().DeleteOnClose != 0 } + pub fn delete_on_close(&self) -> bool { + self.file_info().DeleteOnClose != 0 + } /// Gets whether it is a paging I/O operation. - pub fn paging_io(&self) -> bool { self.file_info().PagingIo != 0 } + pub fn paging_io(&self) -> bool { + self.file_info().PagingIo != 0 + } /// Gets whether it is a synchronous I/O operation. - pub fn synchronous_io(&self) -> bool { self.file_info().SynchronousIo != 0 } + pub fn synchronous_io(&self) -> bool { + self.file_info().SynchronousIo != 0 + } /// Gets whether it is a non-cached I/O operation. - pub fn no_cache(&self) -> bool { self.file_info().Nocache != 0 } + pub fn no_cache(&self) -> bool { + self.file_info().Nocache != 0 + } /// Gets whether the current write operation should write to end of file instead of the /// position specified by the offset argument. - pub fn write_to_eof(&self) -> bool { self.file_info().WriteToEndOfFile != 0 } + pub fn write_to_eof(&self) -> bool { + self.file_info().WriteToEndOfFile != 0 + } /// Gets the number of threads used to handle file system operations. - pub fn thread_count(&self) -> u16 { self.mount_options().ThreadCount } + pub fn thread_count(&self) -> u16 { + self.mount_options().ThreadCount + } /// Gets flags that controls behavior of the mounted volume. - pub fn mount_flags(&self) -> MountFlags { MountFlags::from_bits_truncate(self.mount_options().Options) } + pub fn mount_flags(&self) -> MountFlags { + MountFlags::from_bits_truncate(self.mount_options().Options) + } /// Gets mount point path. pub fn mount_point(&self) -> Option<&U16CStr> { @@ -499,13 +555,19 @@ impl<'a, 'b: 'a, 'c: 'b, T: FileSystemHandler<'b, 'c> + 'c> OperationInfo<'b, 'c /// See [`Drive::timeout`] for more information. /// /// [`Drive::timeout`]: struct.Drive.html#method.timeout - pub fn timeout(&self) -> Duration { Duration::from_millis(self.mount_options().Timeout.into()) } + pub fn timeout(&self) -> Duration { + Duration::from_millis(self.mount_options().Timeout.into()) + } /// Gets allocation unit size of the volume. - pub fn allocation_unit_size(&self) -> u32 { self.mount_options().AllocationUnitSize } + pub fn allocation_unit_size(&self) -> u32 { + self.mount_options().AllocationUnitSize + } /// Gets sector size of the volume. - pub fn sector_size(&self) -> u32 { self.mount_options().SectorSize } + pub fn sector_size(&self) -> u32 { + self.mount_options().SectorSize + } /// Temporarily extend the timeout of the current operation. /// @@ -555,7 +617,12 @@ impl Display for OperationError { write!(f, "Dokan operation failed: ")?; match self { OperationError::NtStatus(e) => write!(f, "NTSTATUS 0x{:08x}", e), - OperationError::Win32(e) => write!(f, "Win32 error {} (converted to NTSTATUS 0x{:08x})", e, self.ntstatus()), + OperationError::Win32(e) => write!( + f, + "Win32 error {} (converted to NTSTATUS 0x{:08x})", + e, + self.ntstatus() + ), } } } @@ -594,8 +661,10 @@ trait ToFileTime { impl ToFileTime for SystemTime { fn to_filetime(&self) -> FILETIME { - let intervals = self.duration_since(UNIX_EPOCH - FILETIME_OFFSET) - .unwrap_or(Duration::from_secs(0)).as_nanos() / 100; + let intervals = self + .duration_since(UNIX_EPOCH - FILETIME_OFFSET) + .unwrap_or(Duration::from_secs(0)) + .as_nanos() / 100; FILETIME { dwLowDateTime: intervals as u32, dwHighDateTime: (intervals >> 32) as u32, @@ -626,8 +695,11 @@ impl FileTimeInfo { -2 => FileTimeInfo::ResumeUpdate, _ => { let time_val = time_val as u64; - FileTimeInfo::SetTime(UNIX_EPOCH - FILETIME_OFFSET - + Duration::from_micros(time_val / 10) + Duration::from_nanos(time_val % 10 * 100)) + FileTimeInfo::SetTime( + UNIX_EPOCH - FILETIME_OFFSET + + Duration::from_micros(time_val / 10) + + Duration::from_nanos(time_val % 10 * 100), + ) } } } @@ -926,7 +998,8 @@ pub trait FileSystemHandler<'a, 'b: 'a>: Sync + Sized + 'b { _file_name: &U16CStr, _info: &OperationInfo<'a, 'b, Self>, _context: &'a Self::Context, - ) {} + ) { + } /// Called when the last handle for the handle object has been closed and released. /// @@ -943,7 +1016,8 @@ pub trait FileSystemHandler<'a, 'b: 'a>: Sync + Sized + 'b { _file_name: &U16CStr, _info: &OperationInfo<'a, 'b, Self>, _context: &'a Self::Context, - ) {} + ) { + } /// Reads data from the file. /// @@ -1254,18 +1328,12 @@ pub trait FileSystemHandler<'a, 'b: 'a>: Sync + Sized + 'b { } /// Called when Dokan has successfully mounted the volume. - fn mounted( - &'b self, - _info: &OperationInfo<'a, 'b, Self>, - ) -> Result<(), OperationError> { + fn mounted(&'b self, _info: &OperationInfo<'a, 'b, Self>) -> Result<(), OperationError> { Err(OperationError::NtStatus(STATUS_NOT_IMPLEMENTED)) } /// Called when Dokan is unmounting the volume. - fn unmounted( - &'b self, - _info: &OperationInfo<'a, 'b, Self>, - ) -> Result<(), OperationError> { + fn unmounted(&'b self, _info: &OperationInfo<'a, 'b, Self>) -> Result<(), OperationError> { Err(OperationError::NtStatus(STATUS_NOT_IMPLEMENTED)) } @@ -1358,28 +1426,34 @@ extern "stdcall" fn create_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( let file_name = U16CStr::from_ptr_str(file_name); let mut info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); info.drop_context(); - info.handler().create_file( - file_name, - &*security_context, - desired_access, - file_attributes, - share_access, - create_disposition, - create_options, - &mut info, - ).and_then(|create_info| { - (&mut *dokan_file_info).Context = Box::into_raw(Box::new(create_info.context)) as u64; - (&mut *dokan_file_info).IsDirectory = create_info.is_dir.into(); - if (create_disposition == FILE_OPEN_IF || - create_disposition == FILE_OVERWRITE_IF || - create_disposition == FILE_SUPERSEDE) && - !create_info.new_file_created { - Err(OperationError::NtStatus(STATUS_OBJECT_NAME_COLLISION)) - } else { - Ok(()) - } - }).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .create_file( + file_name, + &*security_context, + desired_access, + file_attributes, + share_access, + create_disposition, + create_options, + &mut info, + ) + .and_then(|create_info| { + (&mut *dokan_file_info).Context = + Box::into_raw(Box::new(create_info.context)) as u64; + (&mut *dokan_file_info).IsDirectory = create_info.is_dir.into(); + if (create_disposition == FILE_OPEN_IF + || create_disposition == FILE_OVERWRITE_IF + || create_disposition == FILE_SUPERSEDE) + && !create_info.new_file_created + { + Err(OperationError::NtStatus(STATUS_OBJECT_NAME_COLLISION)) + } else { + Ok(()) + } + }) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } #[allow(unused_must_use)] @@ -1420,13 +1494,15 @@ extern "stdcall" fn read_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); let buffer = slice::from_raw_parts_mut(buffer as *mut _, buffer_length as usize); - let result = info.handler() + let result = info + .handler() .read_file(file_name, offset, buffer, &info, info.context()); if let Ok(bytes_read) = result { *read_length = bytes_read; } result.ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn write_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1442,13 +1518,15 @@ extern "stdcall" fn write_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); let buffer = slice::from_raw_parts(buffer as *mut _, number_of_bytes_to_write as usize); - let result = info.handler() + let result = info + .handler() .write_file(file_name, offset, buffer, &info, info.context()); if let Ok(bytes_written) = result { *number_of_bytes_written = bytes_written; } result.ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn flush_file_buffers<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1458,8 +1536,11 @@ extern "stdcall" fn flush_file_buffers<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().flush_file_buffers(file_name, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .flush_file_buffers(file_name, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn get_file_information<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1475,8 +1556,10 @@ extern "stdcall" fn get_file_information<'a, 'b: 'a, T: FileSystemHandler<'a, 'b .and_then(|file_info| { *buffer = file_info.to_raw_struct(); Ok(()) - }).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + }) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn find_files<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1488,8 +1571,11 @@ extern "stdcall" fn find_files<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( let file_name = U16CStr::from_ptr_str(file_name); let fill_wrapper = fill_data_wrapper::<_, FindData>(fill_find_data, dokan_file_info); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().find_files(file_name, fill_wrapper, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .find_files(file_name, fill_wrapper, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn find_files_with_pattern<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1503,8 +1589,17 @@ extern "stdcall" fn find_files_with_pattern<'a, 'b: 'a, T: FileSystemHandler<'a, let search_pattern = U16CStr::from_ptr_str(search_pattern); let fill_wrapper = fill_data_wrapper(fill_find_data, dokan_file_info); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().find_files_with_pattern(file_name, search_pattern, fill_wrapper, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .find_files_with_pattern( + file_name, + search_pattern, + fill_wrapper, + &info, + info.context(), + ) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn set_file_attributes<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1515,8 +1610,11 @@ extern "stdcall" fn set_file_attributes<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().set_file_attributes(file_name, file_attributes, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .set_file_attributes(file_name, file_attributes, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn set_file_time<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1532,8 +1630,18 @@ extern "stdcall" fn set_file_time<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b> let creation_time = FileTimeInfo::from_filetime(*creation_time); let last_access_time = FileTimeInfo::from_filetime(*last_access_time); let last_write_time = FileTimeInfo::from_filetime(*last_write_time); - info.handler().set_file_time(file_name, creation_time, last_access_time, last_write_time, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .set_file_time( + file_name, + creation_time, + last_access_time, + last_write_time, + &info, + info.context(), + ) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn delete_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1543,8 +1651,11 @@ extern "stdcall" fn delete_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().delete_file(file_name, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .delete_file(file_name, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn delete_directory<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1554,8 +1665,11 @@ extern "stdcall" fn delete_directory<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().delete_directory(file_name, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .delete_directory(file_name, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn move_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1568,8 +1682,17 @@ extern "stdcall" fn move_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( let file_name = U16CStr::from_ptr_str(file_name); let new_file_name = U16CStr::from_ptr_str(new_file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().move_file(file_name, new_file_name, replace_if_existing == TRUE, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .move_file( + file_name, + new_file_name, + replace_if_existing == TRUE, + &info, + info.context(), + ) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn set_end_of_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1580,8 +1703,11 @@ extern "stdcall" fn set_end_of_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + ' panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().set_end_of_file(file_name, byte_offset, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .set_end_of_file(file_name, byte_offset, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn set_allocation_size<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1592,8 +1718,11 @@ extern "stdcall" fn set_allocation_size<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().set_allocation_size(file_name, alloc_size, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .set_allocation_size(file_name, alloc_size, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } // Extern stdcall functions with similar bodies but not called directly with trigger a compiler bug when built in @@ -1604,13 +1733,29 @@ fn lock_unlock_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( byte_offset: LONGLONG, length: LONGLONG, dokan_file_info: PDOKAN_FILE_INFO, - func: fn(&'b T, &U16CStr, i64, i64, &OperationInfo<'a, 'b, T>, &'a T::Context) -> Result<(), OperationError>, + func: fn( + &'b T, + &U16CStr, + i64, + i64, + &OperationInfo<'a, 'b, T>, + &'a T::Context, + ) -> Result<(), OperationError>, ) -> NTSTATUS { panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - func(info.handler(), file_name, byte_offset, length, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + func( + info.handler(), + file_name, + byte_offset, + length, + &info, + info.context(), + ) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn lock_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1619,7 +1764,13 @@ extern "stdcall" fn lock_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( length: LONGLONG, dokan_file_info: PDOKAN_FILE_INFO, ) -> NTSTATUS { - lock_unlock_file(file_name, byte_offset, length, dokan_file_info, T::lock_file) + lock_unlock_file( + file_name, + byte_offset, + length, + dokan_file_info, + T::lock_file, + ) } extern "stdcall" fn unlock_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1628,7 +1779,13 @@ extern "stdcall" fn unlock_file<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( length: LONGLONG, dokan_file_info: PDOKAN_FILE_INFO, ) -> NTSTATUS { - lock_unlock_file(file_name, byte_offset, length, dokan_file_info, T::unlock_file) + lock_unlock_file( + file_name, + byte_offset, + length, + dokan_file_info, + T::unlock_file, + ) } extern "stdcall" fn get_disk_free_space<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1639,19 +1796,23 @@ extern "stdcall" fn get_disk_free_space<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> ) -> NTSTATUS { panic::catch_unwind(|| { let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().get_disk_free_space(&info).and_then(|space_info| unsafe { - if !free_bytes_available.is_null() { - *free_bytes_available = space_info.available_byte_count; - } - if !total_number_of_bytes.is_null() { - *total_number_of_bytes = space_info.byte_count; - } - if !total_number_of_free_bytes.is_null() { - *total_number_of_free_bytes = space_info.free_byte_count; - } - Ok(()) - }).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .get_disk_free_space(&info) + .and_then(|space_info| unsafe { + if !free_bytes_available.is_null() { + *free_bytes_available = space_info.available_byte_count; + } + if !total_number_of_bytes.is_null() { + *total_number_of_bytes = space_info.byte_count; + } + if !total_number_of_free_bytes.is_null() { + *total_number_of_free_bytes = space_info.free_byte_count; + } + Ok(()) + }) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn get_volume_information<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1666,27 +1827,31 @@ extern "stdcall" fn get_volume_information<'a, 'b: 'a, T: FileSystemHandler<'a, ) -> NTSTATUS { panic::catch_unwind(|| { let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().get_volume_information(&info).and_then(|volume_info| unsafe { - volume_name_buffer.copy_from_nonoverlapping( - volume_info.name.as_ptr(), - (volume_info.name.len() + 1).min(volume_name_size as usize), - ); - if !volume_serial_number.is_null() { - *volume_serial_number = volume_info.serial_number; - } - if !maximum_component_length.is_null() { - *maximum_component_length = volume_info.max_component_length; - } - if !file_system_flags.is_null() { - *file_system_flags = volume_info.fs_flags; - } - file_system_name_buffer.copy_from_nonoverlapping( - volume_info.fs_name.as_ptr(), - (volume_info.fs_name.len() + 1).min(file_system_name_size as usize), - ); - Ok(()) - }).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .get_volume_information(&info) + .and_then(|volume_info| unsafe { + volume_name_buffer.copy_from_nonoverlapping( + volume_info.name.as_ptr(), + (volume_info.name.len() + 1).min(volume_name_size as usize), + ); + if !volume_serial_number.is_null() { + *volume_serial_number = volume_info.serial_number; + } + if !maximum_component_length.is_null() { + *maximum_component_length = volume_info.max_component_length; + } + if !file_system_flags.is_null() { + *file_system_flags = volume_info.fs_flags; + } + file_system_name_buffer.copy_from_nonoverlapping( + volume_info.fs_name.as_ptr(), + (volume_info.fs_name.len() + 1).min(file_system_name_size as usize), + ); + Ok(()) + }) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } // Same rationale as lock_unlock_file. @@ -1697,14 +1862,19 @@ fn mounted_unmounted<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( panic::catch_unwind(|| { let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); func(info.handler(), &info).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } -extern "stdcall" fn mounted<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>(dokan_file_info: PDOKAN_FILE_INFO) -> NTSTATUS { +extern "stdcall" fn mounted<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( + dokan_file_info: PDOKAN_FILE_INFO, +) -> NTSTATUS { mounted_unmounted(dokan_file_info, T::mounted) } -extern "stdcall" fn unmounted<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>(dokan_file_info: PDOKAN_FILE_INFO) -> NTSTATUS { +extern "stdcall" fn unmounted<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( + dokan_file_info: PDOKAN_FILE_INFO, +) -> NTSTATUS { mounted_unmounted(dokan_file_info, T::unmounted) } @@ -1737,7 +1907,8 @@ extern "stdcall" fn get_file_security<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + } else { result.ntstatus() } - }).unwrap_or(STATUS_INTERNAL_ERROR) + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn set_file_security<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1750,15 +1921,18 @@ extern "stdcall" fn set_file_security<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + panic::catch_unwind(|| unsafe { let file_name = U16CStr::from_ptr_str(file_name); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().set_file_security( - file_name, - *security_information, - security_descriptor, - buffer_length, - &info, - info.context(), - ).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .set_file_security( + file_name, + *security_information, + security_descriptor, + buffer_length, + &info, + info.context(), + ) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } extern "stdcall" fn find_streams<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( @@ -1770,8 +1944,11 @@ extern "stdcall" fn find_streams<'a, 'b: 'a, T: FileSystemHandler<'a, 'b> + 'b>( let file_name = U16CStr::from_ptr_str(file_name); let fill_wrapper = fill_data_wrapper(fill_find_stream_data, dokan_file_info); let info = OperationInfo::<'a, 'b, T>::new(dokan_file_info); - info.handler().find_streams(file_name, fill_wrapper, &info, info.context()).ntstatus() - }).unwrap_or(STATUS_INTERNAL_ERROR) + info.handler() + .find_streams(file_name, fill_wrapper, &info, info.context()) + .ntstatus() + }) + .unwrap_or(STATUS_INTERNAL_ERROR) } /// The error type for [`Drive::mount`]. @@ -1904,7 +2081,10 @@ impl<'a> Drive<'a> { } /// Mounts the volume and blocks the current thread until the volume gets unmounted. - pub fn mount<'b, 'c: 'b, T: FileSystemHandler<'b, 'c> + 'c>(&mut self, handler: &'c T) -> Result<(), MountError> { + pub fn mount<'b, 'c: 'b, T: FileSystemHandler<'b, 'c> + 'c>( + &mut self, + handler: &'c T, + ) -> Result<(), MountError> { let mut operations = DOKAN_OPERATIONS { ZwCreateFile: Some(create_file::<'b, 'c, T>), Cleanup: Some(cleanup::<'b, 'c, T>), diff --git a/dokan/src/tests.rs b/dokan/src/tests.rs index 8acbf6a..943d9f9 100644 --- a/dokan/src/tests.rs +++ b/dokan/src/tests.rs @@ -4,7 +4,7 @@ extern crate regex; use std::pin::Pin; use std::process; -use std::sync::mpsc::{self, SyncSender, Receiver}; +use std::sync::mpsc::{self, Receiver, SyncSender}; use std::thread; use parking_lot::Mutex; @@ -13,7 +13,10 @@ use winapi::shared::minwindef::{FALSE, HLOCAL}; use winapi::shared::ntdef::{HANDLE, NULL}; use winapi::shared::ntstatus::{STATUS_ACCESS_DENIED, STATUS_NOT_IMPLEMENTED}; use winapi::shared::sddl::ConvertSidToStringSidW; -use winapi::shared::winerror::{ERROR_HANDLE_EOF, ERROR_INSUFFICIENT_BUFFER, ERROR_INTERNAL_ERROR, ERROR_IO_PENDING, ERROR_NO_MORE_FILES, ERROR_SUCCESS}; +use winapi::shared::winerror::{ + ERROR_HANDLE_EOF, ERROR_INSUFFICIENT_BUFFER, ERROR_INTERNAL_ERROR, ERROR_IO_PENDING, + ERROR_NO_MORE_FILES, ERROR_SUCCESS, +}; use winapi::um::errhandlingapi::GetLastError; use winapi::um::fileapi::*; use winapi::um::ioapiset::GetOverlappedResult; @@ -46,12 +49,36 @@ fn convert_str(s: impl AsRef) -> U16CString { #[test] fn test_name_in_expression() { - assert!(is_name_in_expression(convert_str("foo"), convert_str("foo"), true)); - assert!(is_name_in_expression(convert_str("*"), convert_str("foo"), true)); - assert!(is_name_in_expression(convert_str("?"), convert_str("x"), true)); - assert!(!is_name_in_expression(convert_str("?"), convert_str("foo"), true)); - assert!(is_name_in_expression(convert_str("F*"), convert_str("foo"), true)); - assert!(!is_name_in_expression(convert_str("F*"), convert_str("foo"), false)); + assert!(is_name_in_expression( + convert_str("foo"), + convert_str("foo"), + true + )); + assert!(is_name_in_expression( + convert_str("*"), + convert_str("foo"), + true + )); + assert!(is_name_in_expression( + convert_str("?"), + convert_str("x"), + true + )); + assert!(!is_name_in_expression( + convert_str("?"), + convert_str("foo"), + true + )); + assert!(is_name_in_expression( + convert_str("F*"), + convert_str("foo"), + true + )); + assert!(!is_name_in_expression( + convert_str("F*"), + convert_str("foo"), + false + )); } #[test] @@ -60,23 +87,39 @@ fn test_map_flags() { FILE_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL, FILE_WRITE_THROUGH, - FILE_OPEN); - assert_eq!(result.desired_access, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); - assert_eq!(result.flags_and_attributes, FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL); + FILE_OPEN, + ); + assert_eq!( + result.desired_access, + GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL + ); + assert_eq!( + result.flags_and_attributes, + FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL + ); assert_eq!(result.creation_disposition, OPEN_EXISTING); } #[test] fn test_ntstatus() { - assert_eq!(OperationError::NtStatus(STATUS_SUCCESS).ntstatus(), STATUS_INTERNAL_ERROR); - assert_eq!(OperationError::Win32(ERROR_SUCCESS).ntstatus(), STATUS_INTERNAL_ERROR); + assert_eq!( + OperationError::NtStatus(STATUS_SUCCESS).ntstatus(), + STATUS_INTERNAL_ERROR + ); + assert_eq!( + OperationError::Win32(ERROR_SUCCESS).ntstatus(), + STATUS_INTERNAL_ERROR + ); let err_nt = OperationError::NtStatus(STATUS_INTERNAL_ERROR); let err_win32 = OperationError::Win32(ERROR_INTERNAL_ERROR); assert_eq!(err_nt.ntstatus(), err_win32.ntstatus()); assert_eq!(Ok::<(), OperationError>(()).ntstatus(), STATUS_SUCCESS); - assert_eq!(Err::<(), OperationError>(err_nt).ntstatus(), STATUS_INTERNAL_ERROR); + assert_eq!( + Err::<(), OperationError>(err_nt).ntstatus(), + STATUS_INTERNAL_ERROR + ); } #[test] @@ -173,16 +216,22 @@ fn get_descriptor_owner(desc: PSECURITY_DESCRIPTOR) -> (U16CString, BOOL) { fn get_user_info(token: HANDLE) -> Pin>> { unsafe { let mut user_info_len = 0; - assert_eq!(GetTokenInformation(token, TokenUser, ptr::null_mut(), 0, &mut user_info_len), FALSE); + assert_eq!( + GetTokenInformation(token, TokenUser, ptr::null_mut(), 0, &mut user_info_len), + FALSE + ); assert_eq!(GetLastError(), ERROR_INSUFFICIENT_BUFFER); let mut user_info_buffer = Box::pin(vec![0; user_info_len as usize]); - assert_eq!(GetTokenInformation( - token, - TokenUser, - user_info_buffer.as_mut_ptr() as LPVOID, - user_info_len, - &mut user_info_len, - ), TRUE); + assert_eq!( + GetTokenInformation( + token, + TokenUser, + user_info_buffer.as_mut_ptr() as LPVOID, + user_info_len, + &mut user_info_len, + ), + TRUE + ); assert_eq!(user_info_len as usize, user_info_buffer.len()); user_info_buffer } @@ -191,7 +240,10 @@ fn get_user_info(token: HANDLE) -> Pin>> { fn get_current_user_info() -> Pin>> { unsafe { let mut token = ptr::null_mut(); - assert_eq!(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token), TRUE); + assert_eq!( + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token), + TRUE + ); let info = get_user_info(token); assert_eq!(CloseHandle(token), TRUE); info @@ -204,17 +256,29 @@ fn create_test_descriptor() -> Vec { let user_info = &*(user_info_buffer.as_mut_ptr() as PTOKEN_USER); let mut abs_desc = mem::zeroed::(); let abs_desc_ptr = &mut abs_desc as *mut _ as PSECURITY_DESCRIPTOR; - assert_eq!(InitializeSecurityDescriptor(abs_desc_ptr, SECURITY_DESCRIPTOR_REVISION), TRUE); - assert_eq!(SetSecurityDescriptorOwner(abs_desc_ptr, user_info.User.Sid, FALSE), TRUE); + assert_eq!( + InitializeSecurityDescriptor(abs_desc_ptr, SECURITY_DESCRIPTOR_REVISION), + TRUE + ); + assert_eq!( + SetSecurityDescriptorOwner(abs_desc_ptr, user_info.User.Sid, FALSE), + TRUE + ); let mut rel_desc_len = 0; - assert_eq!(MakeSelfRelativeSD(abs_desc_ptr, ptr::null_mut(), &mut rel_desc_len), FALSE); + assert_eq!( + MakeSelfRelativeSD(abs_desc_ptr, ptr::null_mut(), &mut rel_desc_len), + FALSE + ); assert_eq!(GetLastError(), ERROR_INSUFFICIENT_BUFFER); let mut rel_desc_buffer = vec![0; rel_desc_len as usize]; - assert_eq!(MakeSelfRelativeSD( - abs_desc_ptr, - rel_desc_buffer.as_mut_ptr() as PSECURITY_DESCRIPTOR, - &mut rel_desc_len, - ), TRUE); + assert_eq!( + MakeSelfRelativeSD( + abs_desc_ptr, + rel_desc_buffer.as_mut_ptr() as PSECURITY_DESCRIPTOR, + &mut rel_desc_len, + ), + TRUE + ); assert_eq!(rel_desc_len as usize, rel_desc_buffer.len()); rel_desc_buffer } @@ -236,21 +300,38 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { ) -> Result, OperationError> { let file_name = file_name.to_string_lossy(); match file_name.as_ref() { - "\\test_file_io" | "\\test_get_file_information" | "\\test_set_file_attributes" | "\\test_set_file_time" | "\\test_delete_file" | "\\test_move_file" | "\\test_set_end_of_file" | "\\test_set_allocation_size" | "\\test_lock_unlock_file" | "\\test_get_file_security" | "\\test_get_file_security_overflow" | "\\test_set_file_security" | "\\test_find_streams" => Ok(CreateFileInfo { + "\\test_file_io" + | "\\test_get_file_information" + | "\\test_set_file_attributes" + | "\\test_set_file_time" + | "\\test_delete_file" + | "\\test_move_file" + | "\\test_set_end_of_file" + | "\\test_set_allocation_size" + | "\\test_lock_unlock_file" + | "\\test_get_file_security" + | "\\test_get_file_security_overflow" + | "\\test_set_file_security" + | "\\test_find_streams" => Ok(CreateFileInfo { context: None, is_dir: false, new_file_created: false, }), - "\\" | "\\test_delete_directory" | "\\test_find_files" | "\\test_find_files_with_pattern" => { - Ok(CreateFileInfo { - context: None, - is_dir: true, - new_file_created: false, - }) - } + "\\" + | "\\test_delete_directory" + | "\\test_find_files" + | "\\test_find_files_with_pattern" => Ok(CreateFileInfo { + context: None, + is_dir: true, + new_file_created: false, + }), "\\test_open_requester_token" => { let token = info.requester_token().unwrap(); - self.tx.send(HandlerSignal::OpenRequesterToken(get_user_info(token.as_raw_handle()))).unwrap(); + self.tx + .send(HandlerSignal::OpenRequesterToken(get_user_info( + token.as_raw_handle(), + ))) + .unwrap(); Ok(CreateFileInfo { context: None, is_dir: false, @@ -268,22 +349,24 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { }) } "\\test_operation_info" => { - self.tx.send(HandlerSignal::OperationInfo(OperationInfoDump { - pid: info.pid(), - is_dir: info.is_dir(), - delete_on_close: info.delete_on_close(), - paging_io: info.paging_io(), - synchronous_io: info.synchronous_io(), - no_cache: info.no_cache(), - write_to_eof: info.write_to_eof(), - thread_count: info.thread_count(), - mount_flags: info.mount_flags(), - mount_point: info.mount_point().map(|s| s.to_owned()), - unc_name: info.unc_name().map(|s| s.to_owned()), - timeout: info.timeout(), - allocation_unit_size: info.allocation_unit_size(), - sector_size: info.sector_size(), - })).unwrap(); + self.tx + .send(HandlerSignal::OperationInfo(OperationInfoDump { + pid: info.pid(), + is_dir: info.is_dir(), + delete_on_close: info.delete_on_close(), + paging_io: info.paging_io(), + synchronous_io: info.synchronous_io(), + no_cache: info.no_cache(), + write_to_eof: info.write_to_eof(), + thread_count: info.thread_count(), + mount_flags: info.mount_flags(), + mount_point: info.mount_point().map(|s| s.to_owned()), + unc_name: info.unc_name().map(|s| s.to_owned()), + timeout: info.timeout(), + allocation_unit_size: info.allocation_unit_size(), + sector_size: info.sector_size(), + })) + .unwrap(); Ok(CreateFileInfo { context: None, is_dir: false, @@ -291,13 +374,15 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { }) } "\\test_create_file" => { - self.tx.send(HandlerSignal::CreateFile( - desired_access, - file_attributes, - share_access, - create_disposition, - create_options, - )).unwrap(); + self.tx + .send(HandlerSignal::CreateFile( + desired_access, + file_attributes, + share_access, + create_disposition, + create_options, + )) + .unwrap(); Ok(CreateFileInfo { context: None, is_dir: false, @@ -306,7 +391,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { } "\\test_panic" => panic!(), "\\test_close_file" => Ok(CreateFileInfo { - context: Some(TestContext { tx: self.tx.clone() }), + context: Some(TestContext { + tx: self.tx.clone(), + }), is_dir: false, new_file_created: false, }), @@ -352,7 +439,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { let data = "test data".as_bytes(); assert!(data.len() <= buffer.len()); buffer[..data.len()].copy_from_slice(data); - self.tx.send(HandlerSignal::ReadFile(offset, buffer.len())).unwrap(); + self.tx + .send(HandlerSignal::ReadFile(offset, buffer.len())) + .unwrap(); Ok(data.len() as u32) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -370,7 +459,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { check_pid(info.pid())?; let file_name = file_name.to_string_lossy(); if &file_name == "\\test_file_io" { - self.tx.send(HandlerSignal::WriteFile(offset, Vec::from(buffer))).unwrap(); + self.tx + .send(HandlerSignal::WriteFile(offset, Vec::from(buffer))) + .unwrap(); Ok(buffer.len() as u32) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -401,7 +492,11 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { ) -> Result { check_pid(info.pid())?; Ok(FileInfo { - attributes: if info.is_dir() { FILE_ATTRIBUTE_DIRECTORY } else { FILE_ATTRIBUTE_NORMAL }, + attributes: if info.is_dir() { + FILE_ATTRIBUTE_DIRECTORY + } else { + FILE_ATTRIBUTE_NORMAL + }, creation_time: UNIX_EPOCH, last_access_time: UNIX_EPOCH + Duration::from_secs(1), last_write_time: UNIX_EPOCH + Duration::from_secs(2), @@ -457,7 +552,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { file_size: (1 << 32) + 2, file_name: convert_str("test_inner_file_with_pattern"), })?; - self.tx.send(HandlerSignal::FindFilesWithPattern(pattern.to_owned())).unwrap(); + self.tx + .send(HandlerSignal::FindFilesWithPattern(pattern.to_owned())) + .unwrap(); Ok(()) } _ => Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)), @@ -475,7 +572,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { let file_name = file_name.to_string_lossy(); match file_name.as_ref() { "\\test_set_file_attributes" => { - self.tx.send(HandlerSignal::SetFileAttributes(file_attributes)).unwrap(); + self.tx + .send(HandlerSignal::SetFileAttributes(file_attributes)) + .unwrap(); Ok(()) } "\\test_set_file_time" => Ok(()), @@ -496,7 +595,13 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { let file_name = file_name.to_string_lossy(); match file_name.as_ref() { "\\test_set_file_time" => { - self.tx.send(HandlerSignal::SetFileTime(creation_time, last_access_time, last_write_time)).unwrap(); + self.tx + .send(HandlerSignal::SetFileTime( + creation_time, + last_access_time, + last_write_time, + )) + .unwrap(); Ok(()) } "\\test_set_file_attributes" => Ok(()), @@ -513,7 +618,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { check_pid(info.pid())?; let file_name = file_name.to_string_lossy(); if &file_name == "\\test_delete_file" { - self.tx.send(HandlerSignal::DeleteFile(info.delete_on_close())).unwrap(); + self.tx + .send(HandlerSignal::DeleteFile(info.delete_on_close())) + .unwrap(); Ok(()) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -529,7 +636,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { check_pid(info.pid())?; let file_name = file_name.to_string_lossy(); if &file_name == "\\test_delete_directory" { - self.tx.send(HandlerSignal::DeleteDirectory(info.delete_on_close())).unwrap(); + self.tx + .send(HandlerSignal::DeleteDirectory(info.delete_on_close())) + .unwrap(); Ok(()) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -547,7 +656,12 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { check_pid(info.pid())?; let file_name = file_name.to_string_lossy(); if &file_name == "\\test_move_file" { - self.tx.send(HandlerSignal::MoveFile(new_file_name.to_owned(), replace_if_existing)).unwrap(); + self.tx + .send(HandlerSignal::MoveFile( + new_file_name.to_owned(), + replace_if_existing, + )) + .unwrap(); Ok(()) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -583,7 +697,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { check_pid(info.pid())?; let file_name = file_name.to_string_lossy(); if &file_name == "\\test_set_allocation_size" { - self.tx.send(HandlerSignal::SetAllocationSize(alloc_size)).unwrap(); + self.tx + .send(HandlerSignal::SetAllocationSize(alloc_size)) + .unwrap(); Ok(()) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -601,7 +717,9 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { check_pid(info.pid())?; let file_name = file_name.to_string_lossy(); if &file_name == "\\test_lock_unlock_file" { - self.tx.send(HandlerSignal::LockFile(offset, length)).unwrap(); + self.tx + .send(HandlerSignal::LockFile(offset, length)) + .unwrap(); Ok(()) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -619,14 +737,19 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { check_pid(info.pid())?; let file_name = file_name.to_string_lossy(); if &file_name == "\\test_lock_unlock_file" { - self.tx.send(HandlerSignal::UnlockFile(offset, length)).unwrap(); + self.tx + .send(HandlerSignal::UnlockFile(offset, length)) + .unwrap(); Ok(()) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) } } - fn get_disk_free_space(&'b self, _info: &OperationInfo<'a, 'b, Self>) -> Result { + fn get_disk_free_space( + &'b self, + _info: &OperationInfo<'a, 'b, Self>, + ) -> Result { Ok(DiskSpaceInfo { byte_count: 2 * 1024 * 1024, free_byte_count: 1024 * 1024, @@ -634,12 +757,18 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { }) } - fn get_volume_information(&'b self, _info: &OperationInfo<'a, 'b, Self>) -> Result { + fn get_volume_information( + &'b self, + _info: &OperationInfo<'a, 'b, Self>, + ) -> Result { Ok(VolumeInfo { name: convert_str("Test Drive"), serial_number: 1, max_component_length: 255, - fs_flags: FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH | FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS, + fs_flags: FILE_CASE_PRESERVED_NAMES + | FILE_CASE_SENSITIVE_SEARCH + | FILE_UNICODE_ON_DISK + | FILE_NAMED_STREAMS, fs_name: convert_str("TESTFS"), }) } @@ -667,15 +796,18 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { let file_name = file_name.to_string_lossy(); match file_name.as_ref() { "\\test_get_file_security" => { - self.tx.send(HandlerSignal::GetFileSecurity(security_information, buffer_length)).unwrap(); + self.tx + .send(HandlerSignal::GetFileSecurity( + security_information, + buffer_length, + )) + .unwrap(); let desc = create_test_descriptor(); let result = Ok(desc.len() as u32); if desc.len() <= buffer_length as usize { unsafe { - desc.as_ptr().copy_to_nonoverlapping( - security_descriptor as *mut _, - desc.len(), - ); + desc.as_ptr() + .copy_to_nonoverlapping(security_descriptor as *mut _, desc.len()); } } result @@ -698,7 +830,14 @@ impl<'a, 'b: 'a> FileSystemHandler<'a, 'b> for TestHandler { let file_name = file_name.to_string_lossy(); if &file_name == "\\test_set_file_security" { let (sid, owner_defaulted) = get_descriptor_owner(security_descriptor); - self.tx.send(HandlerSignal::SetFileSecurity(buffer_length, security_information, sid, owner_defaulted)).unwrap(); + self.tx + .send(HandlerSignal::SetFileSecurity( + buffer_length, + security_information, + sid, + owner_defaulted, + )) + .unwrap(); Ok(()) } else { Err(OperationError::NtStatus(STATUS_ACCESS_DENIED)) @@ -750,7 +889,12 @@ fn with_test_drive(f: impl FnOnce(&Receiver)) { let handle = thread::spawn(move || { Drive::new() .thread_count(4) - .flags(MountFlags::CURRENT_SESSION | MountFlags::FILELOCK_USER_MODE | MountFlags::ALT_STREAM | MountFlags::ENABLE_NOTIFICATION_API) + .flags( + MountFlags::CURRENT_SESSION + | MountFlags::FILELOCK_USER_MODE + | MountFlags::ALT_STREAM + | MountFlags::ENABLE_NOTIFICATION_API, + ) .mount_point(&convert_str("Z:\\")) // Min value specified by DOKAN_IRP_PENDING_TIMEOUT. .timeout(Duration::from_secs(15)) @@ -775,7 +919,8 @@ fn test_get_mount_point_list() { assert_eq!(info.mount_point, Some(convert_str("\\DosDevices\\Z:"))); assert_eq!(info.unc_name, None); assert!( - Regex::new("^\\\\Device\\\\Volume\\{[0-9a-z]{8}-([0-9a-z]{4}-){3}[0-9a-z]{12}}$").unwrap() + Regex::new("^\\\\Device\\\\Volume\\{[0-9a-z]{8}-([0-9a-z]{4}-){3}[0-9a-z]{12}}$") + .unwrap() .is_match(&info.device_name.to_string_lossy()) ); let mut session_id = 0; @@ -788,7 +933,18 @@ fn test_get_mount_point_list() { fn test_panic() { with_test_drive(|_rx| unsafe { let path = convert_str("Z:\\test_panic"); - assert_eq!(CreateFileW(path.as_ptr(), 0, 0, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut()), INVALID_HANDLE_VALUE); + assert_eq!( + CreateFileW( + path.as_ptr(), + 0, + 0, + ptr::null_mut(), + OPEN_EXISTING, + 0, + ptr::null_mut() + ), + INVALID_HANDLE_VALUE + ); assert_eq!(GetLastError(), ERROR_INTERNAL_ERROR); }); } @@ -802,21 +958,36 @@ fn test_get_volume_information() { let mut serial_number = 0; let mut max_component_length = 0; let mut fs_flags = 0; - assert_ne!(GetVolumeInformationW( - path.as_ptr(), - volume_name.as_mut_ptr(), - volume_name.len() as u32, - &mut serial_number, - &mut max_component_length, - &mut fs_flags, - fs_name.as_mut_ptr(), - fs_name.len() as u32, - ), 0); - assert_eq!(U16CStr::from_slice_with_nul(&volume_name).unwrap(), convert_str("Test Drive").as_ref()); - assert_eq!(U16CStr::from_slice_with_nul(&fs_name).unwrap(), convert_str("TESTFS").as_ref()); + assert_ne!( + GetVolumeInformationW( + path.as_ptr(), + volume_name.as_mut_ptr(), + volume_name.len() as u32, + &mut serial_number, + &mut max_component_length, + &mut fs_flags, + fs_name.as_mut_ptr(), + fs_name.len() as u32, + ), + 0 + ); + assert_eq!( + U16CStr::from_slice_with_nul(&volume_name).unwrap(), + convert_str("Test Drive").as_ref() + ); + assert_eq!( + U16CStr::from_slice_with_nul(&fs_name).unwrap(), + convert_str("TESTFS").as_ref() + ); assert_eq!(serial_number, 1); assert_eq!(max_component_length, 255); - assert_eq!(fs_flags, FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH | FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS); + assert_eq!( + fs_flags, + FILE_CASE_PRESERVED_NAMES + | FILE_CASE_SENSITIVE_SEARCH + | FILE_UNICODE_ON_DISK + | FILE_NAMED_STREAMS + ); }); } @@ -827,12 +998,15 @@ fn test_get_disk_free_space() { let mut free_bytes_available = 0u64; let mut total_number_of_bytes = 0u64; let mut total_number_of_free_bytes = 0u64; - assert_eq!(GetDiskFreeSpaceExW( - path.as_ptr(), - &mut free_bytes_available as *mut _ as PULARGE_INTEGER, - &mut total_number_of_bytes as *mut _ as PULARGE_INTEGER, - &mut total_number_of_free_bytes as *mut _ as PULARGE_INTEGER, - ), TRUE); + assert_eq!( + GetDiskFreeSpaceExW( + path.as_ptr(), + &mut free_bytes_available as *mut _ as PULARGE_INTEGER, + &mut total_number_of_bytes as *mut _ as PULARGE_INTEGER, + &mut total_number_of_free_bytes as *mut _ as PULARGE_INTEGER, + ), + TRUE + ); assert_eq!(free_bytes_available, 512 * 1024); assert_eq!(total_number_of_bytes, 2 * 1024 * 1024); assert_eq!(total_number_of_free_bytes, 1024 * 1024); @@ -861,13 +1035,16 @@ fn test_create_file() { with_test_drive(|rx| unsafe { let hf = open_file("Z:\\test_create_file"); assert_eq!(CloseHandle(hf), TRUE); - assert_eq!(rx.recv().unwrap(), HandlerSignal::CreateFile( - FILE_ALL_ACCESS, - FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ, - FILE_OPEN, - FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, - )); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::CreateFile( + FILE_ALL_ACCESS, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, + ) + ); }); } @@ -888,13 +1065,37 @@ fn test_file_io() { let hf = open_file("Z:\\test_file_io"); let mut buf = [0u8; 255]; let mut len = 0; - assert_eq!(ReadFile(hf, buf.as_mut_ptr() as LPVOID, buf.len() as u32, &mut len, ptr::null_mut()), TRUE); - assert_eq!(String::from_utf8(Vec::from(&buf[..len as usize])).unwrap(), "test data"); + assert_eq!( + ReadFile( + hf, + buf.as_mut_ptr() as LPVOID, + buf.len() as u32, + &mut len, + ptr::null_mut() + ), + TRUE + ); + assert_eq!( + String::from_utf8(Vec::from(&buf[..len as usize])).unwrap(), + "test data" + ); assert_eq!(rx.recv().unwrap(), HandlerSignal::ReadFile(0, buf.len())); let mut bytes_written = 0; - assert_eq!(WriteFile(hf, buf.as_ptr() as LPCVOID, len, &mut bytes_written, ptr::null_mut()), TRUE); + assert_eq!( + WriteFile( + hf, + buf.as_ptr() as LPCVOID, + len, + &mut bytes_written, + ptr::null_mut() + ), + TRUE + ); assert_eq!(bytes_written, len); - assert_eq!(rx.recv().unwrap(), HandlerSignal::WriteFile(len as i64, Vec::from(&buf[0..len as usize]))); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::WriteFile(len as i64, Vec::from(&buf[0..len as usize])) + ); assert_eq!(FlushFileBuffers(hf), TRUE); assert_eq!(rx.recv().unwrap(), HandlerSignal::FlushFileBuffers); assert_eq!(CloseHandle(hf), TRUE); @@ -913,9 +1114,18 @@ fn test_get_file_information() { assert_eq!(info.dwFileAttributes, FILE_ATTRIBUTE_NORMAL); assert_eq!(info.ftCreationTime.dwLowDateTime, ft_epoch.dwLowDateTime); assert_eq!(info.ftCreationTime.dwHighDateTime, ft_epoch.dwHighDateTime); - assert_eq!(info.ftLastAccessTime.dwLowDateTime, ft_epoch.dwLowDateTime + 1000 * 1000 * 10); - assert_eq!(info.ftLastAccessTime.dwHighDateTime, ft_epoch.dwHighDateTime); - assert_eq!(info.ftLastWriteTime.dwLowDateTime, ft_epoch.dwLowDateTime + 2000 * 1000 * 10); + assert_eq!( + info.ftLastAccessTime.dwLowDateTime, + ft_epoch.dwLowDateTime + 1000 * 1000 * 10 + ); + assert_eq!( + info.ftLastAccessTime.dwHighDateTime, + ft_epoch.dwHighDateTime + ); + assert_eq!( + info.ftLastWriteTime.dwLowDateTime, + ft_epoch.dwLowDateTime + 2000 * 1000 * 10 + ); assert_eq!(info.ftLastWriteTime.dwHighDateTime, ft_epoch.dwHighDateTime); assert_eq!(info.dwVolumeSerialNumber, 1); assert_eq!(info.nFileSizeLow, 2); @@ -933,23 +1143,44 @@ fn check_dir_content(pattern: &str, file_name: &str) { let hf = FindFirstFileW(pattern.as_ptr(), &mut data); let ft_epoch = UNIX_EPOCH.to_filetime(); assert_ne!(hf, INVALID_HANDLE_VALUE); - assert_eq!(U16CStr::from_slice_with_nul(&data.cFileName).unwrap(), convert_str(".").as_ref()); + assert_eq!( + U16CStr::from_slice_with_nul(&data.cFileName).unwrap(), + convert_str(".").as_ref() + ); assert_eq!(FindNextFileW(hf, &mut data), TRUE); - assert_eq!(U16CStr::from_slice_with_nul(&data.cFileName).unwrap(), convert_str("..").as_ref()); + assert_eq!( + U16CStr::from_slice_with_nul(&data.cFileName).unwrap(), + convert_str("..").as_ref() + ); assert_eq!(FindNextFileW(hf, &mut data), TRUE); assert_eq!(data.dwFileAttributes, FILE_ATTRIBUTE_NORMAL); assert_eq!(data.ftCreationTime.dwLowDateTime, ft_epoch.dwLowDateTime); assert_eq!(data.ftCreationTime.dwHighDateTime, ft_epoch.dwHighDateTime); - assert_eq!(data.ftLastAccessTime.dwLowDateTime, ft_epoch.dwLowDateTime + 1000 * 1000 * 10); - assert_eq!(data.ftLastAccessTime.dwHighDateTime, ft_epoch.dwHighDateTime); - assert_eq!(data.ftLastWriteTime.dwLowDateTime, ft_epoch.dwLowDateTime + 2000 * 1000 * 10); + assert_eq!( + data.ftLastAccessTime.dwLowDateTime, + ft_epoch.dwLowDateTime + 1000 * 1000 * 10 + ); + assert_eq!( + data.ftLastAccessTime.dwHighDateTime, + ft_epoch.dwHighDateTime + ); + assert_eq!( + data.ftLastWriteTime.dwLowDateTime, + ft_epoch.dwLowDateTime + 2000 * 1000 * 10 + ); assert_eq!(data.ftLastWriteTime.dwHighDateTime, ft_epoch.dwHighDateTime); assert_eq!(data.nFileSizeLow, 2); assert_eq!(data.nFileSizeHigh, 1); - assert_eq!(U16CStr::from_slice_with_nul(&data.cFileName).unwrap(), convert_str(file_name).as_ref()); + assert_eq!( + U16CStr::from_slice_with_nul(&data.cFileName).unwrap(), + convert_str(file_name).as_ref() + ); assert_eq!(data.dwReserved0, 0); assert_eq!(data.dwReserved1, 0); - assert_eq!(U16CStr::from_slice_with_nul(&data.cAlternateFileName).unwrap(), convert_str("").as_ref()); + assert_eq!( + U16CStr::from_slice_with_nul(&data.cAlternateFileName).unwrap(), + convert_str("").as_ref() + ); assert_eq!(FindNextFileW(hf, &mut data), FALSE); assert_eq!(GetLastError(), ERROR_NO_MORE_FILES); assert_eq!(FindClose(hf), TRUE); @@ -960,8 +1191,14 @@ fn check_dir_content(pattern: &str, file_name: &str) { fn test_find_files() { with_test_drive(|rx| { check_dir_content("Z:\\test_find_files\\*", "test_inner_file"); - check_dir_content("Z:\\test_find_files_with_pattern\\*", "test_inner_file_with_pattern"); - assert_eq!(rx.recv().unwrap(), HandlerSignal::FindFilesWithPattern(convert_str("*"))); + check_dir_content( + "Z:\\test_find_files_with_pattern\\*", + "test_inner_file_with_pattern", + ); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::FindFilesWithPattern(convert_str("*")) + ); }); } @@ -969,8 +1206,14 @@ fn test_find_files() { fn test_set_file_attributes() { with_test_drive(|rx| unsafe { let path = convert_str("Z:\\test_set_file_attributes"); - assert_eq!(SetFileAttributesW(path.as_ptr(), FILE_ATTRIBUTE_READONLY), TRUE); - assert_eq!(rx.recv().unwrap(), HandlerSignal::SetFileAttributes(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY)); + assert_eq!( + SetFileAttributesW(path.as_ptr(), FILE_ATTRIBUTE_READONLY), + TRUE + ); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::SetFileAttributes(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY) + ); }); } @@ -981,21 +1224,43 @@ fn test_set_file_time() { let ctime = UNIX_EPOCH; let atime = UNIX_EPOCH + Duration::from_secs(1); let mtime = UNIX_EPOCH + Duration::from_secs(2); - assert_eq!(SetFileTime(hf, &ctime.to_filetime(), &atime.to_filetime(), &mtime.to_filetime()), TRUE); - assert_eq!(rx.recv().unwrap(), HandlerSignal::SetFileTime( - FileTimeInfo::SetTime(ctime), - FileTimeInfo::SetTime(atime), - FileTimeInfo::SetTime(mtime), - )); + assert_eq!( + SetFileTime( + hf, + &ctime.to_filetime(), + &atime.to_filetime(), + &mtime.to_filetime() + ), + TRUE + ); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::SetFileTime( + FileTimeInfo::SetTime(ctime), + FileTimeInfo::SetTime(atime), + FileTimeInfo::SetTime(mtime), + ) + ); let time_dont_change = mem::transmute(0i64); let time_disable_update = mem::transmute(-1i64); let time_resume_update = mem::transmute(-2i64); - assert_eq!(SetFileTime(hf, &time_dont_change, &time_disable_update, &time_resume_update), TRUE); - assert_eq!(rx.recv().unwrap(), HandlerSignal::SetFileTime( - FileTimeInfo::DontChange, - FileTimeInfo::DisableUpdate, - FileTimeInfo::ResumeUpdate, - )); + assert_eq!( + SetFileTime( + hf, + &time_dont_change, + &time_disable_update, + &time_resume_update + ), + TRUE + ); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::SetFileTime( + FileTimeInfo::DontChange, + FileTimeInfo::DisableUpdate, + FileTimeInfo::ResumeUpdate, + ) + ); assert_eq!(CloseHandle(hf), TRUE); }); } @@ -1023,8 +1288,14 @@ fn test_move_file() { with_test_drive(|rx| unsafe { let path = convert_str("Z:\\test_move_file"); let new_path = convert_str("Z:\\test_move_file_new"); - assert_eq!(MoveFileExW(path.as_ptr(), new_path.as_ptr(), MOVEFILE_REPLACE_EXISTING), TRUE); - assert_eq!(rx.recv().unwrap(), HandlerSignal::MoveFile(convert_str("\\test_move_file_new"), true)); + assert_eq!( + MoveFileExW(path.as_ptr(), new_path.as_ptr(), MOVEFILE_REPLACE_EXISTING), + TRUE + ); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::MoveFile(convert_str("\\test_move_file_new"), true) + ); }); } @@ -1047,7 +1318,10 @@ fn test_set_allocation_size() { assert_eq!(SetFilePointer(hf, dist_low, &mut dist_high, FILE_BEGIN), 42); assert_eq!(dist_high, 42); assert_eq!(SetEndOfFile(hf), TRUE); - assert_eq!(rx.recv().unwrap(), HandlerSignal::SetAllocationSize(dist_low as i64 + ((dist_high as i64) << 32))); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::SetAllocationSize(dist_low as i64 + ((dist_high as i64) << 32)) + ); assert_eq!(CloseHandle(hf), TRUE); }); } @@ -1070,19 +1344,37 @@ fn test_get_file_security() { let expected_desc = create_test_descriptor(); let path = convert_str("Z:\\test_get_file_security"); let mut desc_len = 0; - assert_eq!(GetFileSecurityW(path.as_ptr(), OWNER_SECURITY_INFORMATION, ptr::null_mut(), 0, &mut desc_len), FALSE); + assert_eq!( + GetFileSecurityW( + path.as_ptr(), + OWNER_SECURITY_INFORMATION, + ptr::null_mut(), + 0, + &mut desc_len + ), + FALSE + ); assert_eq!(GetLastError(), ERROR_INSUFFICIENT_BUFFER); - assert_eq!(rx.recv().unwrap(), HandlerSignal::GetFileSecurity(OWNER_SECURITY_INFORMATION, 0)); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::GetFileSecurity(OWNER_SECURITY_INFORMATION, 0) + ); let mut desc = vec![0u8; desc_len as usize]; - assert_eq!(GetFileSecurityW( - path.as_ptr(), - OWNER_SECURITY_INFORMATION, - desc.as_mut_ptr() as PSECURITY_DESCRIPTOR, - desc.len() as u32, - &mut desc_len, - ), TRUE); + assert_eq!( + GetFileSecurityW( + path.as_ptr(), + OWNER_SECURITY_INFORMATION, + desc.as_mut_ptr() as PSECURITY_DESCRIPTOR, + desc.len() as u32, + &mut desc_len, + ), + TRUE + ); assert_eq!(desc.len(), desc_len as usize); - assert_eq!(rx.recv().unwrap(), HandlerSignal::GetFileSecurity(OWNER_SECURITY_INFORMATION, desc_len)); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::GetFileSecurity(OWNER_SECURITY_INFORMATION, desc_len) + ); assert_eq!(desc, expected_desc); }); } @@ -1092,13 +1384,16 @@ fn test_get_file_security_overflow() { with_test_drive(|_rx| unsafe { let path = convert_str("Z:\\test_get_file_security_overflow"); let mut ret_len = 0; - assert_eq!(GetFileSecurityW( - path.as_ptr(), - OWNER_SECURITY_INFORMATION, - ptr::null_mut(), - 0, - &mut ret_len, - ), FALSE); + assert_eq!( + GetFileSecurityW( + path.as_ptr(), + OWNER_SECURITY_INFORMATION, + ptr::null_mut(), + 0, + &mut ret_len, + ), + FALSE + ); assert_eq!(ret_len, 1); assert_eq!(GetLastError(), ERROR_INSUFFICIENT_BUFFER); }); @@ -1110,9 +1405,20 @@ fn test_set_file_security() { let path = convert_str("Z:\\test_set_file_security"); let mut desc = create_test_descriptor(); let desc_ptr = desc.as_mut_ptr() as PSECURITY_DESCRIPTOR; - assert_eq!(SetFileSecurityW(path.as_ptr(), OWNER_SECURITY_INFORMATION, desc_ptr), TRUE); + assert_eq!( + SetFileSecurityW(path.as_ptr(), OWNER_SECURITY_INFORMATION, desc_ptr), + TRUE + ); let (sid, owner_defaulted) = get_descriptor_owner(desc_ptr); - assert_eq!(rx.recv().unwrap(), HandlerSignal::SetFileSecurity(desc.len() as u32, OWNER_SECURITY_INFORMATION, sid, owner_defaulted)); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::SetFileSecurity( + desc.len() as u32, + OWNER_SECURITY_INFORMATION, + sid, + owner_defaulted + ) + ); }); } @@ -1129,7 +1435,10 @@ fn test_find_streams() { ); assert_ne!(hf, INVALID_HANDLE_VALUE); assert_eq!(data.StreamSize.QuadPart(), &42); - assert_eq!(U16CStr::from_slice_with_nul(&data.cStreamName).unwrap(), convert_str("::$DATA").as_ref()); + assert_eq!( + U16CStr::from_slice_with_nul(&data.cStreamName).unwrap(), + convert_str("::$DATA").as_ref() + ); assert_eq!(FindNextStreamW(hf, &mut data as *mut _ as LPVOID), FALSE); assert_eq!(GetLastError(), ERROR_HANDLE_EOF); assert_eq!(FindClose(hf), TRUE); @@ -1141,7 +1450,15 @@ fn test_find_streams() { fn test_reset_timeout() { with_test_drive(|_rx| unsafe { let path = convert_str("Z:\\test_reset_timeout"); - let hf = CreateFileW(path.as_ptr(), 0, 0, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut()); + let hf = CreateFileW( + path.as_ptr(), + 0, + 0, + ptr::null_mut(), + OPEN_EXISTING, + 0, + ptr::null_mut(), + ); assert_ne!(hf, INVALID_HANDLE_VALUE); assert_eq!(CloseHandle(hf), TRUE); }); @@ -1169,22 +1486,28 @@ fn test_operation_info() { with_test_drive(|rx| unsafe { let hf = open_file("Z:\\test_operation_info"); assert_eq!(CloseHandle(hf), TRUE); - assert_eq!(rx.recv().unwrap(), HandlerSignal::OperationInfo(OperationInfoDump { - pid: process::id(), - is_dir: false, - delete_on_close: false, - paging_io: false, - synchronous_io: false, - no_cache: false, - write_to_eof: false, - thread_count: 4, - mount_flags: MountFlags::CURRENT_SESSION | MountFlags::FILELOCK_USER_MODE | MountFlags::ALT_STREAM | MountFlags::ENABLE_NOTIFICATION_API, - mount_point: Some(convert_str("Z:\\")), - unc_name: None, - timeout: Duration::from_secs(15), - allocation_unit_size: 1024, - sector_size: 1024, - })); + assert_eq!( + rx.recv().unwrap(), + HandlerSignal::OperationInfo(OperationInfoDump { + pid: process::id(), + is_dir: false, + delete_on_close: false, + paging_io: false, + synchronous_io: false, + no_cache: false, + write_to_eof: false, + thread_count: 4, + mount_flags: MountFlags::CURRENT_SESSION + | MountFlags::FILELOCK_USER_MODE + | MountFlags::ALT_STREAM + | MountFlags::ENABLE_NOTIFICATION_API, + mount_point: Some(convert_str("Z:\\")), + unc_name: None, + timeout: Duration::from_secs(15), + allocation_unit_size: 1024, + sector_size: 1024, + }) + ); }); } @@ -1192,22 +1515,28 @@ fn test_operation_info() { fn test_output_ptr_null() { with_test_drive(|_rx| unsafe { let path = convert_str("Z:\\"); - assert_eq!(GetDiskFreeSpaceExW( - path.as_ptr(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ), TRUE); - assert_eq!(GetVolumeInformationW( - path.as_ptr(), - ptr::null_mut(), - 0, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - 0, - ), TRUE); + assert_eq!( + GetDiskFreeSpaceExW( + path.as_ptr(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ), + TRUE + ); + assert_eq!( + GetVolumeInformationW( + path.as_ptr(), + ptr::null_mut(), + 0, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ), + TRUE + ); }) } @@ -1225,16 +1554,26 @@ impl ToRawStruct<()> for ToRawStructStub { } } -extern "stdcall" fn fill_data_stub(_data: *mut (), _info: PDOKAN_FILE_INFO) -> c_int { 0 } +extern "stdcall" fn fill_data_stub(_data: *mut (), _info: PDOKAN_FILE_INFO) -> c_int { + 0 +} -extern "stdcall" fn failing_fill_data_stub(_data: *mut (), _info: PDOKAN_FILE_INFO) -> c_int { 1 } +extern "stdcall" fn failing_fill_data_stub(_data: *mut (), _info: PDOKAN_FILE_INFO) -> c_int { + 1 +} #[test] fn test_fill_data_error() { let mut wrapper = fill_data_wrapper(fill_data_stub, ptr::null_mut()); - assert_eq!(wrapper(&ToRawStructStub { should_fail: true }), Err(FillDataError::NameTooLong)); + assert_eq!( + wrapper(&ToRawStructStub { should_fail: true }), + Err(FillDataError::NameTooLong) + ); let mut wrapper = fill_data_wrapper(failing_fill_data_stub, ptr::null_mut()); - assert_eq!(wrapper(&ToRawStructStub { should_fail: false }), Err(FillDataError::BufferFull)); + assert_eq!( + wrapper(&ToRawStructStub { should_fail: false }), + Err(FillDataError::BufferFull) + ); } struct DirectoryChangeIterator { @@ -1263,7 +1602,10 @@ impl DirectoryChangeIterator { assert_ne!(he, INVALID_HANDLE_VALUE); let mut result = DirectoryChangeIterator { hd: TokenHandle::from_raw_handle(hd), - buf: Box::pin(vec![0; mem::size_of::() + MAX_PATH]), + buf: Box::pin(vec![ + 0; + mem::size_of::() + MAX_PATH + ]), offset: 0, he: TokenHandle::from_raw_handle(he), overlapped: Box::pin(mem::zeroed()), @@ -1301,20 +1643,33 @@ impl Iterator for DirectoryChangeIterator { unsafe { if self.offset == 0 { let mut ret_len = 0; - assert_eq!(GetOverlappedResult( - self.hd.as_raw_handle(), - &mut *self.overlapped, - &mut ret_len, - TRUE, - ), TRUE); + assert_eq!( + GetOverlappedResult( + self.hd.as_raw_handle(), + &mut *self.overlapped, + &mut ret_len, + TRUE, + ), + TRUE + ); assert_eq!(self.overlapped.Internal, STATUS_SUCCESS as usize); assert_eq!(self.overlapped.InternalHigh, ret_len as usize); assert_ne!(ret_len, 0); } - let info = &*(self.buf.as_ptr().offset(self.offset as isize) as *const FILE_NOTIFY_INFORMATION); - self.offset = if info.NextEntryOffset == 0 { 0 } else { self.offset + info.NextEntryOffset as usize }; - if self.offset == 0 { self.begin_read(); } - Some((info.Action, U16CStr::from_ptr_str(info.FileName.as_ptr()).to_owned())) + let info = &*(self.buf.as_ptr().offset(self.offset as isize) + as *const FILE_NOTIFY_INFORMATION); + self.offset = if info.NextEntryOffset == 0 { + 0 + } else { + self.offset + info.NextEntryOffset as usize + }; + if self.offset == 0 { + self.begin_read(); + } + Some(( + info.Action, + U16CStr::from_ptr_str(info.FileName.as_ptr()).to_owned(), + )) } } } @@ -1333,21 +1688,50 @@ fn test_notify() { }); assert_eq!(rx.recv().unwrap(), None); assert!(notify_create(convert_str("Z:\\test_notify_create"), false)); - assert_eq!(rx.recv().unwrap(), Some((FILE_ACTION_ADDED, convert_str("test_notify_create")))); + assert_eq!( + rx.recv().unwrap(), + Some((FILE_ACTION_ADDED, convert_str("test_notify_create"))) + ); assert!(notify_delete(convert_str("Z:\\test_notify_delete"), false)); - assert_eq!(rx.recv().unwrap(), Some((FILE_ACTION_REMOVED, convert_str("test_notify_delete")))); + assert_eq!( + rx.recv().unwrap(), + Some((FILE_ACTION_REMOVED, convert_str("test_notify_delete"))) + ); assert!(notify_update(convert_str("Z:\\test_notify_update"))); - assert_eq!(rx.recv().unwrap(), Some((FILE_ACTION_MODIFIED, convert_str("test_notify_update")))); - assert!(notify_xattr_update(convert_str("Z:\\test_notify_xattr_update"))); - assert_eq!(rx.recv().unwrap(), Some((FILE_ACTION_MODIFIED, convert_str("test_notify_xattr_update")))); + assert_eq!( + rx.recv().unwrap(), + Some((FILE_ACTION_MODIFIED, convert_str("test_notify_update"))) + ); + assert!(notify_xattr_update(convert_str( + "Z:\\test_notify_xattr_update" + ))); + assert_eq!( + rx.recv().unwrap(), + Some(( + FILE_ACTION_MODIFIED, + convert_str("test_notify_xattr_update") + )) + ); assert!(notify_rename( convert_str("Z:\\test_notify_rename_old"), convert_str("Z:\\test_notify_rename_new"), false, true, )); - assert_eq!(rx.recv().unwrap(), Some((FILE_ACTION_RENAMED_OLD_NAME, convert_str("test_notify_rename_old")))); - assert_eq!(rx.recv().unwrap(), Some((FILE_ACTION_RENAMED_NEW_NAME, convert_str("test_notify_rename_new")))); + assert_eq!( + rx.recv().unwrap(), + Some(( + FILE_ACTION_RENAMED_OLD_NAME, + convert_str("test_notify_rename_old") + )) + ); + assert_eq!( + rx.recv().unwrap(), + Some(( + FILE_ACTION_RENAMED_NEW_NAME, + convert_str("test_notify_rename_new") + )) + ); handle.join().unwrap(); }) }