diff --git a/Cargo.toml b/Cargo.toml index 0e76f9b..f10fb61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -widestring = "1.0.2" -memoffset = "0.6.4" -windows = { version = "0.33.0", features = [ - "alloc", +widestring = "1.1.0" +windows = { version = "0.52.0", features = [ "Win32_Foundation", "Win32_Storage_CloudFilters", "Win32_System_SystemServices", @@ -30,8 +28,9 @@ windows = { version = "0.33.0", features = [ "Storage_Streams", "Win32_System_Ioctl", "Win32_Security", + "Win32_System_Variant", ] } -globset = { version = "0.4.9", optional = true } +globset = { version = "0.4.14", optional = true } [features] # Enable globs in the `info::FetchPlaceholders` struct. diff --git a/examples/sftp/src/main.rs b/examples/sftp/src/main.rs index b34d9fe..bfc7fc0 100644 --- a/examples/sftp/src/main.rs +++ b/examples/sftp/src/main.rs @@ -327,7 +327,7 @@ impl SyncFilter for Filter { let parent = absolute.strip_prefix(&client_path).unwrap(); let dirs = self.sftp.readdir(parent).unwrap(); - let placeholders = dirs + let mut placeholders = dirs .into_iter() .filter(|(path, _)| !Path::new(&client_path).join(path).exists()) .map(|(path, stat)| { @@ -354,7 +354,7 @@ impl SyncFilter for Filter { }) .collect::>(); - ticket.pass_with_placeholder(placeholders).unwrap(); + ticket.pass_with_placeholder(&mut placeholders).unwrap(); } fn closed(&self, request: Request, info: info::Closed) { diff --git a/src/command/executor.rs b/src/command/executor.rs index c3af841..2ec4ba7 100644 --- a/src/command/executor.rs +++ b/src/command/executor.rs @@ -1,6 +1,8 @@ -use std::{mem, ptr}; +use std::{ + mem::{self, offset_of}, + ptr, +}; -use memoffset::offset_of; use windows::{ core, Win32::Storage::CloudFilters::{ diff --git a/src/ext/file.rs b/src/ext/file.rs index fd85a7f..6ef6396 100644 --- a/src/ext/file.rs +++ b/src/ext/file.rs @@ -3,7 +3,6 @@ use std::{ mem::{self, MaybeUninit}, ops::{Bound, Range, RangeBounds}, os::windows::{io::AsRawHandle, prelude::RawHandle}, - ptr, }; use widestring::U16CStr; @@ -58,13 +57,11 @@ pub trait FileExt: AsRawHandle { unsafe { CfConvertToPlaceholder( HANDLE(self.as_raw_handle() as isize), - options - .blob - .map_or(ptr::null(), |blob| blob.as_ptr() as *const _), + options.blob.map(|blob| blob.as_ptr() as *const _), options.blob.map_or(0, |blob| blob.len() as u32), options.flags, - usn.as_mut_ptr(), - ptr::null_mut(), + Some(usn.as_mut_ptr()), + None, ) .map(|_| usn.assume_init() as Usn) } @@ -82,7 +79,7 @@ pub trait FileExt: AsRawHandle { CfRevertPlaceholder( HANDLE(self.as_raw_handle() as isize), CloudFilters::CF_REVERT_FLAG_NONE, - ptr::null_mut(), + None, ) } } @@ -104,19 +101,18 @@ pub trait FileExt: AsRawHandle { /// * The handle must have write access. /// * [CloudErrorKind::AccessDenied][crate::CloudErrorKind::AccessDenied] // TODO: this could be split into multiple functions to make common patterns easier - fn update(&self, usn: Usn, mut options: UpdateOptions) -> core::Result { + fn update(&self, usn: Usn, options: UpdateOptions) -> core::Result { let mut usn = usn as i64; unsafe { CfUpdatePlaceholder( HANDLE(self.as_raw_handle() as isize), - options.metadata.map_or(ptr::null(), |x| &x.0 as *const _), - options.blob.map_or(ptr::null(), |x| x.as_ptr() as *const _), + options.metadata.map(|x| &x.0 as *const _), + options.blob.map(|x| x.as_ptr() as *const _), options.blob.map_or(0, |x| x.len() as u32), - options.dehydrate_range.as_mut_ptr(), - options.dehydrate_range.len() as u32, + Some(&options.dehydrate_range), options.flags, - &mut usn as *mut _, - ptr::null_mut(), + Some(&mut usn as *mut _), + None, ) .map(|_| usn as Usn) } @@ -140,7 +136,7 @@ pub trait FileExt: AsRawHandle { Bound::Unbounded => -1, }, CloudFilters::CF_HYDRATE_FLAG_NONE, - ptr::null_mut(), + None, ) } } @@ -168,7 +164,7 @@ pub trait FileExt: AsRawHandle { buffer.len() as i64, buffer as *mut _ as *mut _, buffer.len() as u32, - &mut length as *mut _, + Some(&mut length as *mut _), ) } .map(|_| length) @@ -190,7 +186,7 @@ pub trait FileExt: AsRawHandle { CloudFilters::CF_PLACEHOLDER_INFO_STANDARD, data.as_mut_ptr() as *mut _, data.len() as u32, - ptr::null_mut(), + None, )?; } @@ -215,8 +211,7 @@ pub trait FileExt: AsRawHandle { FileSystem::FileAttributeTagInfo, info.as_mut_ptr() as *mut _, mem::size_of::() as u32, - ) - .ok()?; + )?; PlaceholderState::try_from_win32(CfGetPlaceholderStateFromFileInfo( &info.assume_init() as *const _ as *const _, @@ -232,7 +227,7 @@ pub trait FileExt: AsRawHandle { HANDLE(self.as_raw_handle() as isize), state.into(), options.0, - ptr::null_mut(), + None, ) } } @@ -275,7 +270,7 @@ pub trait FileExt: AsRawHandle { CF_SYNC_ROOT_INFO_STANDARD, data.as_mut_ptr() as *mut _, data.len() as u32, - ptr::null_mut(), + None, )?; } @@ -309,7 +304,7 @@ fn mark_sync_state(handle: RawHandle, sync: bool, usn: Usn) -> core::Result CloudFilters::CF_IN_SYNC_STATE_NOT_IN_SYNC }, CloudFilters::CF_SET_IN_SYNC_FLAG_NONE, - &mut usn as *mut _, + Some(&mut usn as *mut _), ) .map(|_| usn as u64) } @@ -341,7 +336,7 @@ fn dehydrate>( } else { CloudFilters::CF_DEHYDRATE_FLAG_BACKGROUND }, - ptr::null_mut(), + None, ) } } diff --git a/src/ext/path.rs b/src/ext/path.rs index 84d3326..e7febcc 100644 --- a/src/ext/path.rs +++ b/src/ext/path.rs @@ -25,8 +25,8 @@ where /// Information about the sync root that the path is located in. fn sync_root_info(&self) -> core::Result { StorageProviderSyncRootManager::GetSyncRootInformationForFolder( - StorageFolder::GetFolderFromPathAsync( - &U16String::from_os_str(self.as_ref().as_os_str()).to_hstring(), + &StorageFolder::GetFolderFromPathAsync( + &U16String::from_os_str(self.as_ref().as_os_str()).to_hstring()?, )? .get()?, ) diff --git a/src/lib.rs b/src/lib.rs index 8effa2f..e024740 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,12 @@ pub mod command; mod error; /// Contains traits extending common structs from the [std][std]. pub mod ext; -pub mod filter; -pub mod placeholder; -pub mod placeholder_file; -pub mod request; -pub mod root; -pub mod usn; +mod filter; +mod placeholder; +mod placeholder_file; +mod request; +mod root; +mod usn; mod utility; pub use error::CloudErrorKind; diff --git a/src/placeholder.rs b/src/placeholder.rs index 62da5d0..34b04f9 100644 --- a/src/placeholder.rs +++ b/src/placeholder.rs @@ -3,12 +3,11 @@ use std::{ mem::ManuallyDrop, ops::Range, path::{Path, PathBuf}, - ptr, }; use widestring::U16CString; use windows::{ - core::{self, GUID}, + core::{self, GUID, PCWSTR}, Win32::{ Storage::{ CloudFilters::{self, CfReportProviderProgress, CF_CONNECTION_KEY}, @@ -16,15 +15,14 @@ use windows::{ }, System::{ Com::StructuredStorage::{ - PROPVARIANT, PROPVARIANT_0, PROPVARIANT_0_0, PROPVARIANT_0_0_0, + InitPropVariantFromUInt64Vector, PROPVARIANT, PROPVARIANT_0, PROPVARIANT_0_0, + PROPVARIANT_0_0_0, }, - Ole::VT_UI4, + Variant::VT_UI4, }, UI::Shell::{ self, IShellItem2, - PropertiesSystem::{ - self, IPropertyStore, InitPropVariantFromUInt64Vector, PROPERTYKEY, - }, + PropertiesSystem::{self, IPropertyStore, PROPERTYKEY}, SHChangeNotify, SHCreateItemFromParsingName, }, }, @@ -143,25 +141,32 @@ impl Placeholder { completed as i64, )?; - let item: IShellItem2 = SHCreateItemFromParsingName(self.path.as_os_str(), None)?; + let item: IShellItem2 = SHCreateItemFromParsingName( + PCWSTR::from_raw( + U16CString::from_os_str(self.path.as_os_str()) + .unwrap() + .as_ptr(), + ), + None, + )?; let store: IPropertyStore = item.GetPropertyStore( PropertiesSystem::GPS_READWRITE | PropertiesSystem::GPS_VOLATILEPROPERTIESONLY, )?; - let progress = InitPropVariantFromUInt64Vector(&mut [completed, total] as *mut _, 2)?; + let progress = InitPropVariantFromUInt64Vector(Some(&[completed, total]))?; store.SetValue( &STORAGE_PROVIDER_TRANSFER_PROGRESS as *const _, &progress as *const _, )?; let status = InitPropVariantFromUInt32(if completed < total { - PropertiesSystem::STS_TRANSFERRING.0 + PropertiesSystem::STS_TRANSFERRING.0 as u32 } else { - PropertiesSystem::STS_NONE.0 + PropertiesSystem::STS_NONE.0 as u32 }); store.SetValue( &EnhancedStorage::PKEY_SyncTransferStatus as *const _, - &status as *const _, + &status, )?; store.Commit()?; @@ -169,8 +174,8 @@ impl Placeholder { SHChangeNotify( Shell::SHCNE_UPDATEITEM, Shell::SHCNF_PATHW, - U16CString::from_os_str_unchecked(self.path.as_os_str()).as_ptr() as *const _, - ptr::null_mut(), + Some(U16CString::from_os_str_unchecked(self.path.as_os_str()).as_ptr() as *const _), + None, ); Ok(()) @@ -320,7 +325,7 @@ fn InitPropVariantFromUInt32(ulVal: u32) -> PROPVARIANT { PROPVARIANT { Anonymous: PROPVARIANT_0 { Anonymous: ManuallyDrop::new(PROPVARIANT_0_0 { - vt: VT_UI4.0 as u16, + vt: VT_UI4, Anonymous: PROPVARIANT_0_0_0 { ulVal }, ..Default::default() }), diff --git a/src/placeholder_file.rs b/src/placeholder_file.rs index ebbec02..2ba35ca 100644 --- a/src/placeholder_file.rs +++ b/src/placeholder_file.rs @@ -1,4 +1,4 @@ -use std::{fs, os::windows::prelude::MetadataExt, path::Path, ptr, slice}; +use std::{fs, os::windows::prelude::MetadataExt, path::Path, slice}; use widestring::U16CString; use windows::{ @@ -18,7 +18,7 @@ use crate::usn::Usn; // TODO: this struct could probably have a better name to represent files/dirs /// A builder for creating new placeholder files/directories. -#[repr(C)] +#[repr(transparent)] #[derive(Debug)] pub struct PlaceholderFile(CF_PLACEHOLDER_CREATE_INFO); @@ -120,14 +120,13 @@ impl PlaceholderFile { /// /// If you need to create placeholders from the [SyncFilter::fetch_placeholders][crate::SyncFilter::fetch_placeholders] callback, do not use this method. Instead, use /// [FetchPlaceholders::pass_with_placeholders][crate::ticket::FetchPlaceholders::pass_with_placeholders]. - pub fn create>(mut self, parent: impl AsRef) -> core::Result { + pub fn create>(self, parent: impl AsRef) -> core::Result { unsafe { CfCreatePlaceholders( - parent.as_ref().as_os_str(), - &mut self as *mut _ as *mut _, - 1, + PCWSTR::from_raw(U16CString::from_os_str(parent.as_ref()).unwrap().as_ptr()), + &mut [self.0], CloudFilters::CF_CREATE_FLAG_NONE, - ptr::null_mut(), + None, )?; } @@ -161,11 +160,14 @@ impl BatchCreate for [PlaceholderFile] { fn create>(&mut self, path: P) -> core::Result>> { unsafe { CfCreatePlaceholders( - path.as_ref().as_os_str(), - self.as_mut_ptr() as *mut CF_PLACEHOLDER_CREATE_INFO, - self.len() as u32, + PCWSTR::from_raw(U16CString::from_os_str(path.as_ref()).unwrap().as_ptr()), + // TODO: we should be able to cast it directly to its inner type + &mut self + .iter() + .map(|placeholder| placeholder.0) + .collect::>(), CloudFilters::CF_CREATE_FLAG_NONE, - ptr::null_mut(), + None, )?; } diff --git a/src/request.rs b/src/request.rs index 9433195..5d64346 100644 --- a/src/request.rs +++ b/src/request.rs @@ -5,7 +5,7 @@ use windows::Win32::Storage::CloudFilters::{CF_CALLBACK_INFO, CF_PROCESS_INFO}; use crate::placeholder::Placeholder; -pub type RawConnectionKey = isize; +pub type RawConnectionKey = i64; pub type RawTransferKey = i64; /// A struct containing various information for the current file operation. diff --git a/src/root/register.rs b/src/root/register.rs index 548fa53..5368c39 100644 --- a/src/root/register.rs +++ b/src/root/register.rs @@ -15,9 +15,8 @@ use windows::{ Streams::DataWriter, }, Win32::Storage::CloudFilters::{ - self, CF_HYDRATION_POLICY_MODIFIER_USHORT, CF_HYDRATION_POLICY_PRIMARY, - CF_HYDRATION_POLICY_PRIMARY_USHORT, CF_INSYNC_POLICY, CF_POPULATION_POLICY_PRIMARY, - CF_POPULATION_POLICY_PRIMARY_USHORT, + self, CF_HYDRATION_POLICY_MODIFIER, CF_HYDRATION_POLICY_PRIMARY, CF_INSYNC_POLICY, + CF_POPULATION_POLICY_PRIMARY, }, }; @@ -156,11 +155,11 @@ impl<'a> Registration<'a> { info.SetHydrationPolicyModifier(self.hydration_policy.0)?; info.SetPopulationPolicy(self.population_type.into())?; info.SetInSyncPolicy(self.supported_attributes.0)?; - info.SetDisplayNameResource(self.display_name.to_hstring())?; - info.SetIconResource(self.icon.to_hstring())?; + info.SetDisplayNameResource(&self.display_name.to_hstring()?)?; + info.SetIconResource(&self.icon.to_hstring()?)?; info.SetPath( - StorageFolder::GetFolderFromPathAsync( - &U16String::from_os_str(path.as_ref().as_os_str()).to_hstring(), + &StorageFolder::GetFolderFromPathAsync( + &U16String::from_os_str(path.as_ref().as_os_str()).to_hstring()?, )? .get()?, )?; @@ -175,20 +174,20 @@ impl<'a> Registration<'a> { info.SetProviderId(provider_id)?; } if let Some(version) = &self.version { - info.SetVersion(version.to_hstring())?; + info.SetVersion(&version.to_hstring()?)?; } if let Some(uri) = &self.recycle_bin_uri { - info.SetRecycleBinUri(Uri::CreateUri(uri.to_hstring())?)?; + info.SetRecycleBinUri(&Uri::CreateUri(&uri.to_hstring()?)?)?; } if let Some(blob) = &self.blob { // TODO: implement IBuffer interface for slices to avoid a copy let writer = DataWriter::new()?; writer.WriteBytes(blob)?; - info.SetContext(writer.DetachBuffer()?)?; + info.SetContext(&writer.DetachBuffer()?)?; } - StorageProviderSyncRootManager::Register(info) + StorageProviderSyncRootManager::Register(&info) } } @@ -226,9 +225,9 @@ impl From for StorageProviderHydrationPolicy { } } -impl From for HydrationType { - fn from(primary: CF_HYDRATION_POLICY_PRIMARY_USHORT) -> Self { - match CF_HYDRATION_POLICY_PRIMARY(primary.us) { +impl From for HydrationType { + fn from(primary: CF_HYDRATION_POLICY_PRIMARY) -> Self { + match primary { CloudFilters::CF_HYDRATION_POLICY_PARTIAL => HydrationType::Partial, CloudFilters::CF_HYDRATION_POLICY_PROGRESSIVE => HydrationType::Progressive, CloudFilters::CF_HYDRATION_POLICY_FULL => HydrationType::Full, @@ -276,9 +275,9 @@ impl Default for HydrationPolicy { } } -impl From for HydrationPolicy { - fn from(primary: CF_HYDRATION_POLICY_MODIFIER_USHORT) -> Self { - Self(StorageProviderHydrationPolicyModifier(primary.us as u32)) +impl From for HydrationPolicy { + fn from(primary: CF_HYDRATION_POLICY_MODIFIER) -> Self { + Self(StorageProviderHydrationPolicyModifier(primary.0 as u32)) } } @@ -297,9 +296,9 @@ impl From for StorageProviderPopulationPolicy { } } -impl From for PopulationType { - fn from(primary: CF_POPULATION_POLICY_PRIMARY_USHORT) -> Self { - match CF_POPULATION_POLICY_PRIMARY(primary.us) { +impl From for PopulationType { + fn from(primary: CF_POPULATION_POLICY_PRIMARY) -> Self { + match primary { CloudFilters::CF_POPULATION_POLICY_FULL => PopulationType::Full, CloudFilters::CF_POPULATION_POLICY_ALWAYS_FULL => PopulationType::AlwaysFull, _ => unreachable!(), diff --git a/src/root/session.rs b/src/root/session.rs index 96cd381..806231c 100644 --- a/src/root/session.rs +++ b/src/root/session.rs @@ -4,8 +4,9 @@ use std::{ sync::{Arc, Weak}, }; +use widestring::{u16cstr, U16CString}; use windows::{ - core, + core::{self, PCWSTR}, Win32::{ Storage::CloudFilters::{self, CfConnectSyncRoot, CF_CONNECT_FLAGS}, System::{ @@ -55,11 +56,15 @@ impl Session { let callbacks = filter::callbacks::(); unsafe { CfConnectSyncRoot( - path.as_ref().as_os_str(), + PCWSTR::from_raw( + U16CString::from_os_str(path.as_ref().as_os_str()) + .unwrap() + .as_ptr(), + ), callbacks.as_ptr(), // create a weak arc so that it could be upgraded when it's being used and when the // connection is closed, the filter could be freed - Weak::into_raw(Arc::downgrade(&filter)) as *const _, + Some(Weak::into_raw(Arc::downgrade(&filter)) as *const _), // This is enabled by default to remove the Option requirement around various fields of the // [Request][crate::Request] struct self.0 @@ -85,13 +90,18 @@ fn index_path(path: &Path) -> core::Result<()> { Com::CLSCTX_SERVER, )?; - let catalog: ISearchCatalogManager = searcher.GetCatalog("SystemIndex")?; + let catalog: ISearchCatalogManager = + searcher.GetCatalog(PCWSTR::from_raw(u16cstr!("SystemIndex").as_ptr()))?; let mut url = OsString::from("file:///"); url.push(path); let crawler = catalog.GetCrawlScopeManager()?; - crawler.AddDefaultScopeRule(url, true, Search::FF_INDEXCOMPLEXURLS.0 as u32)?; + crawler.AddDefaultScopeRule( + PCWSTR::from_raw(U16CString::from_os_str(url).unwrap().as_ptr()), + true, + Search::FF_INDEXCOMPLEXURLS.0 as u32, + )?; crawler.SaveAll() } } diff --git a/src/root/sync_root.rs b/src/root/sync_root.rs index 6feaeb3..0f6ad5e 100644 --- a/src/root/sync_root.rs +++ b/src/root/sync_root.rs @@ -5,10 +5,9 @@ use windows::{ core::{self, HSTRING, PWSTR}, Storage::Provider::StorageProviderSyncRootManager, Win32::{ - Foundation::{self, GetLastError, HANDLE}, + Foundation::{self, GetLastError, LocalFree, HANDLE, HLOCAL}, Security::{self, Authorization::ConvertSidToStringSidW, GetTokenInformation, TOKEN_USER}, Storage::CloudFilters, - System::Memory::LocalFree, }, }; @@ -74,15 +73,15 @@ impl SyncRootIdBuilder { } /// Constructs a [SyncRootId][crate::SyncRootId] from the builder. - pub fn build(self) -> SyncRootId { - SyncRootId(HSTRING::from_wide( + pub fn build(self) -> core::Result { + Ok(SyncRootId(HSTRING::from_wide( &[ self.provider_name.as_slice(), self.user_security_id.0.as_slice(), self.account_name.as_slice(), ] .join(&SyncRootId::SEPARATOR), - )) + )?)) } } @@ -113,7 +112,7 @@ impl SyncRootId { Ok( match StorageProviderSyncRootManager::GetSyncRootInformationForId(&self.0) { Ok(_) => true, - Err(err) => err.win32_error() != Some(Foundation::ERROR_NOT_FOUND), + Err(err) => err.code() != Foundation::ERROR_NOT_FOUND.to_hresult(), }, ) } @@ -178,32 +177,33 @@ impl SecurityId { let mut token_size = 0; let mut token = MaybeUninit::::uninit(); - if !GetTokenInformation( + if GetTokenInformation( Self::CURRENT_THREAD_EFFECTIVE_TOKEN, Security::TokenUser, - ptr::null_mut(), + None, 0, &mut token_size, ) - .as_bool() - && GetLastError() == Foundation::ERROR_INSUFFICIENT_BUFFER + .is_err() + && GetLastError().is_err_and(|err| { + err.code() == Foundation::ERROR_INSUFFICIENT_BUFFER.to_hresult() + }) { GetTokenInformation( Self::CURRENT_THREAD_EFFECTIVE_TOKEN, Security::TokenUser, - &mut token as *mut _ as *mut _, + Some(&mut token as *mut _ as *mut _), token_size, &mut token_size, - ) - .ok()?; + )?; } let token = token.assume_init(); let mut sid = PWSTR(ptr::null_mut()); - ConvertSidToStringSidW(token.User.Sid, &mut sid as *mut _).ok()?; + ConvertSidToStringSidW(token.User.Sid, &mut sid as *mut _)?; let string_sid = U16CString::from_ptr_str(sid.0).into_ustring(); - LocalFree(sid.0 as isize); + LocalFree(HLOCAL(sid.0 as *mut _))?; Ok(SecurityId::new_unchecked(string_sid)) } diff --git a/src/utility.rs b/src/utility.rs index a1f4907..755cf43 100644 --- a/src/utility.rs +++ b/src/utility.rs @@ -1,4 +1,4 @@ -use windows::core::HSTRING; +use windows::core::{self, HSTRING}; // TODO: add something to convert an Option to a *const T and *mut T @@ -7,7 +7,7 @@ where Self: AsRef<[u16]>, { /// Converts a 16-bit buffer to a Windows reference-counted [HSTRING][windows::core::HSTRING]. - fn to_hstring(&self) -> HSTRING { + fn to_hstring(&self) -> core::Result { HSTRING::from_wide(self.as_ref()) } }