diff --git a/examples/sftp/src/main.rs b/examples/sftp/src/main.rs index e73171e..b34d9fe 100644 --- a/examples/sftp/src/main.rs +++ b/examples/sftp/src/main.rs @@ -328,7 +328,7 @@ impl SyncFilter for Filter { let dirs = self.sftp.readdir(parent).unwrap(); let placeholders = dirs - .iter() + .into_iter() .filter(|(path, _)| !Path::new(&client_path).join(path).exists()) .map(|(path, stat)| { println!("path: {:?}, stat {:?}", path, stat); @@ -350,11 +350,11 @@ impl SyncFilter for Filter { ) .overwrite() // .mark_sync() // need this? - .blob(path.as_os_str().as_encoded_bytes()) + .blob(path.into_os_string().into_encoded_bytes()) }) .collect::>(); - ticket.pass_with_placeholder(&placeholders).unwrap(); + ticket.pass_with_placeholder(placeholders).unwrap(); } fn closed(&self, request: Request, info: info::Closed) { diff --git a/src/command/commands.rs b/src/command/commands.rs index 58d8fb1..1d1552a 100644 --- a/src/command/commands.rs +++ b/src/command/commands.rs @@ -22,7 +22,7 @@ use crate::{ }; /// Read data from a placeholder file. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Read<'a> { /// The buffer of data to read into. pub buffer: &'a mut [u8], @@ -54,7 +54,7 @@ impl Command for Read<'_> { } /// Write data to a placeholder file. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct Write<'a> { /// The buffer of data to write into the file. pub buffer: &'a [u8], @@ -109,7 +109,7 @@ impl Fallible for Write<'_> { } /// Update various properties on a placeholder. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct Update<'a> { /// Whether or not to mark the placeholder as "synced." pub mark_sync: bool, @@ -148,10 +148,10 @@ impl Command for Update<'_> { } /// Create placeholder files/directories. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct CreatePlaceholders<'a> { /// The placeholders to create. - pub placeholders: &'a [PlaceholderFile<'a>], // FIXME: placeholder should be mutable + pub placeholders: &'a mut [PlaceholderFile], /// The total amount of placeholders that are a child of the current directory. pub total: u64, } @@ -203,7 +203,7 @@ impl Command for CreatePlaceholders<'_> { } } -impl<'a> Fallible for CreatePlaceholders<'a> { +impl Fallible for CreatePlaceholders<'_> { fn fail( connection_key: RawConnectionKey, transfer_key: RawTransferKey, @@ -228,7 +228,7 @@ impl<'a> Fallible for CreatePlaceholders<'a> { } /// Validate the data range in the placeholder file is valid. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct Validate { /// The range of data to validate as "good." pub range: Range, @@ -276,7 +276,7 @@ impl Fallible for Validate { } /// Confirm dehydration of the placeholder file and optionally update its blob. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct Dehydrate<'a> { /// Optional file blob to update. pub blob: Option<&'a [u8]>, @@ -326,7 +326,7 @@ impl Fallible for Dehydrate<'_> { } /// Confirm deletion of the placeholder. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct Delete; impl Command for Delete { @@ -367,7 +367,7 @@ impl Fallible for Delete { } /// Confirm rename/move of the placeholder. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct Rename; impl Command for Rename { diff --git a/src/filter/ticket.rs b/src/filter/ticket.rs index aafcfcc..febe9d7 100644 --- a/src/filter/ticket.rs +++ b/src/filter/ticket.rs @@ -80,13 +80,13 @@ impl FetchPlaceholders { /// Creates a list of placeholder files/directorys on the file system. /// /// The value returned is the final [Usn][crate::Usn] (and if they succeeded) after each placeholder is created. - pub fn pass_with_placeholder<'a>( + pub fn pass_with_placeholder( &self, - placeholders: &'a [PlaceholderFile<'a>], + placeholders: &mut [PlaceholderFile], ) -> core::Result>> { command::CreatePlaceholders { - placeholders, total: placeholders.len() as _, + placeholders, } .execute(self.connection_key, self.transfer_key) } diff --git a/src/placeholder.rs b/src/placeholder.rs index d5e7b88..62da5d0 100644 --- a/src/placeholder.rs +++ b/src/placeholder.rs @@ -259,7 +259,7 @@ impl Seek for Placeholder { } /// Various properties to update a placeholder in batch. -#[derive(Debug, Clone, Default)] +#[derive(Debug)] pub struct UpdateOptions<'a>(Update<'a>); impl<'a> UpdateOptions<'a> { @@ -303,6 +303,16 @@ impl<'a> UpdateOptions<'a> { } } +impl<'a> Default for UpdateOptions<'a> { + fn default() -> Self { + Self(Update { + mark_sync: false, + metadata: None, + blob: None, + }) + } +} + // Equivalent to https://docs.microsoft.com/en-us/windows/win32/api/propvarutil/nf-propvarutil-initpropvariantfromuint32 // windows-rs doesn't provide bindings to inlined functions #[allow(non_snake_case)] diff --git a/src/placeholder_file.rs b/src/placeholder_file.rs index 1ebd5a4..ebbec02 100644 --- a/src/placeholder_file.rs +++ b/src/placeholder_file.rs @@ -1,4 +1,4 @@ -use std::{fs, marker::PhantomData, os::windows::prelude::MetadataExt, path::Path, ptr}; +use std::{fs, os::windows::prelude::MetadataExt, path::Path, ptr, slice}; use widestring::U16CString; use windows::{ @@ -20,24 +20,21 @@ use crate::usn::Usn; /// A builder for creating new placeholder files/directories. #[repr(C)] #[derive(Debug)] -pub struct PlaceholderFile<'a>(CF_PLACEHOLDER_CREATE_INFO, PhantomData<&'a ()>); +pub struct PlaceholderFile(CF_PLACEHOLDER_CREATE_INFO); -impl<'a> PlaceholderFile<'a> { +impl PlaceholderFile { /// Creates a new [PlaceholderFile][crate::PlaceholderFile]. pub fn new(relative_path: impl AsRef) -> Self { - Self( - CF_PLACEHOLDER_CREATE_INFO { - RelativeFileName: PCWSTR( - U16CString::from_os_str(relative_path.as_ref()) - .unwrap() - .into_raw(), - ), - Flags: CloudFilters::CF_PLACEHOLDER_CREATE_FLAG_NONE, - Result: Foundation::S_OK, - ..Default::default() - }, - PhantomData, - ) + Self(CF_PLACEHOLDER_CREATE_INFO { + RelativeFileName: PCWSTR( + U16CString::from_os_str(relative_path.as_ref()) + .unwrap() + .into_raw(), + ), + Flags: CloudFilters::CF_PLACEHOLDER_CREATE_FLAG_NONE, + Result: Foundation::S_OK, + ..Default::default() + }) } /// Marks this [PlaceholderFile][crate::PlaceholderFile] as having no child placeholders on @@ -93,15 +90,22 @@ impl<'a> PlaceholderFile<'a> { /// /// The buffer must not exceed /// [4KiB](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Storage/CloudFilters/constant.CF_PLACEHOLDER_MAX_FILE_IDENTITY_LENGTH.html). - pub fn blob(mut self, blob: &'a [u8]) -> Self { + pub fn blob(mut self, blob: Vec) -> Self { assert!( blob.len() <= CloudFilters::CF_PLACEHOLDER_MAX_FILE_IDENTITY_LENGTH as usize, "blob size must not exceed {} bytes, got {} bytes", CloudFilters::CF_PLACEHOLDER_MAX_FILE_IDENTITY_LENGTH, blob.len() ); - self.0.FileIdentity = blob.as_ptr() as *mut _; - self.0.FileIdentityLength = blob.len() as u32; + + if blob.is_empty() { + return self; + } + + let leaked_blob = Box::leak(blob.into_boxed_slice()); + + self.0.FileIdentity = leaked_blob.as_ptr() as *const _; + self.0.FileIdentityLength = leaked_blob.len() as _; self } @@ -131,10 +135,20 @@ impl<'a> PlaceholderFile<'a> { } } -impl Drop for PlaceholderFile<'_> { +impl Drop for PlaceholderFile { fn drop(&mut self) { // Safety: `self.0.RelativeFileName.0` is a valid pointer to a valid UTF-16 string - drop(unsafe { U16CString::from_ptr_str(self.0.RelativeFileName.0) }) + drop(unsafe { U16CString::from_ptr_str(self.0.RelativeFileName.0) }); + + if !self.0.FileIdentity.is_null() { + // Safety: `self.0.FileIdentity` is a valid pointer to a valid slice + drop(unsafe { + Box::from_raw(slice::from_raw_parts_mut( + self.0.FileIdentity as *mut u8, + self.0.FileIdentityLength as _, + )) + }); + } } } @@ -143,7 +157,7 @@ pub trait BatchCreate { fn create>(&mut self, path: P) -> core::Result>>; } -impl BatchCreate for [PlaceholderFile<'_>] { +impl BatchCreate for [PlaceholderFile] { fn create>(&mut self, path: P) -> core::Result>> { unsafe { CfCreatePlaceholders(