cloud_filter/
placeholder_file.rs1use std::{path::Path, ptr, slice};
2
3use widestring::U16CString;
4use windows::{
5 core::{self, PCWSTR},
6 Win32::{
7 Foundation,
8 Storage::CloudFilters::{self, CfCreatePlaceholders, CF_PLACEHOLDER_CREATE_INFO},
9 },
10};
11
12use crate::{metadata::Metadata, sealed, usn::Usn};
13
14#[derive(Debug)]
16pub struct PlaceholderFile(CF_PLACEHOLDER_CREATE_INFO);
17
18impl PlaceholderFile {
19 pub fn new(relative_path: impl AsRef<Path>) -> Self {
21 Self(CF_PLACEHOLDER_CREATE_INFO {
22 RelativeFileName: PCWSTR(
23 U16CString::from_os_str(relative_path.as_ref())
24 .unwrap()
25 .into_raw(),
26 ),
27 Flags: CloudFilters::CF_PLACEHOLDER_CREATE_FLAG_NONE,
28 Result: Foundation::S_FALSE,
29 ..Default::default()
30 })
31 }
32
33 pub fn has_no_children(mut self) -> Self {
38 self.0.Flags |= CloudFilters::CF_PLACEHOLDER_CREATE_FLAG_DISABLE_ON_DEMAND_POPULATION;
39 self
40 }
41
42 pub fn mark_in_sync(mut self) -> Self {
48 self.0.Flags |= CloudFilters::CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC;
49 self
50 }
51
52 pub fn overwrite(mut self) -> Self {
54 self.0.Flags |= CloudFilters::CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE;
55 self
56 }
57
58 pub fn block_dehydration(mut self) -> Self {
62 self.0.Flags |= CloudFilters::CF_PLACEHOLDER_CREATE_FLAG_ALWAYS_FULL;
63 self
64 }
65
66 pub fn metadata(mut self, metadata: Metadata) -> Self {
68 self.0.FsMetadata = metadata.0;
69 self
70 }
71
72 pub fn blob(mut self, blob: Vec<u8>) -> Self {
79 assert!(
80 blob.len() <= CloudFilters::CF_PLACEHOLDER_MAX_FILE_IDENTITY_LENGTH as usize,
81 "blob size must not exceed {} bytes, got {} bytes",
82 CloudFilters::CF_PLACEHOLDER_MAX_FILE_IDENTITY_LENGTH,
83 blob.len()
84 );
85
86 if blob.is_empty() {
87 self.0.FileIdentity = ptr::null();
88 self.0.FileIdentityLength = 0;
89 return self;
90 }
91
92 let leaked_blob = Box::leak(blob.into_boxed_slice());
93 self.0.FileIdentity = leaked_blob.as_ptr() as *const _;
94 self.0.FileIdentityLength = leaked_blob.len() as _;
95
96 self
97 }
98
99 pub fn result(&self) -> core::Result<Usn> {
100 self.0.Result.ok().map(|_| self.0.CreateUsn as _)
101 }
102
103 pub fn create<P: AsRef<Path>>(self, parent: impl AsRef<Path>) -> core::Result<Usn> {
116 unsafe {
117 CfCreatePlaceholders(
118 PCWSTR(U16CString::from_os_str(parent.as_ref()).unwrap().as_ptr()),
119 &mut [self.0],
120 CloudFilters::CF_CREATE_FLAG_NONE,
121 None,
122 )?;
123 }
124
125 self.result()
126 }
127}
128
129impl Drop for PlaceholderFile {
130 fn drop(&mut self) {
131 drop(unsafe { U16CString::from_ptr_str(self.0.RelativeFileName.0) });
133
134 if !self.0.FileIdentity.is_null() {
135 drop(unsafe {
137 Box::from_raw(slice::from_raw_parts_mut(
138 self.0.FileIdentity as *mut u8,
139 self.0.FileIdentityLength as _,
140 ))
141 });
142 }
143 }
144}
145
146pub trait BatchCreate: sealed::Sealed {
148 fn create<P: AsRef<Path>>(&mut self, path: P) -> core::Result<()>;
149}
150
151impl BatchCreate for [PlaceholderFile] {
152 fn create<P: AsRef<Path>>(&mut self, path: P) -> core::Result<()> {
153 unsafe {
154 CfCreatePlaceholders(
155 PCWSTR(U16CString::from_os_str(path.as_ref()).unwrap().as_ptr()),
156 slice::from_raw_parts_mut(self.as_mut_ptr() as *mut _, self.len()),
157 CloudFilters::CF_CREATE_FLAG_NONE,
158 None,
159 )
160 }
161 }
162}
163
164impl sealed::Sealed for [PlaceholderFile] {}