cloud_filter/filter/request.rs
1use std::{ffi::OsString, path::PathBuf, slice};
2
3use widestring::{u16cstr, U16CStr};
4use windows::Win32::Storage::CloudFilters::{CF_CALLBACK_INFO, CF_PROCESS_INFO};
5
6pub type RawConnectionKey = i64;
7pub type RawTransferKey = i64;
8
9/// A struct containing standard information for the current file operation.
10pub struct Request(CF_CALLBACK_INFO);
11
12impl Request {
13 pub(crate) fn new(info: CF_CALLBACK_INFO) -> Self {
14 Self(info)
15 }
16
17 /// The GUID path of the current volume.
18 ///
19 /// The returned value comes in the form `\?\Volume{GUID}`.
20 pub fn volume_guid_path(&self) -> OsString {
21 unsafe { U16CStr::from_ptr_str(self.0.VolumeGuidName.0) }.to_os_string()
22 }
23
24 /// The letter of the current volume.
25 ///
26 /// The returned value comes in the form `X:`, where `X` is the drive letter.
27 pub fn volume_letter(&self) -> OsString {
28 unsafe { U16CStr::from_ptr_str(self.0.VolumeDosName.0) }.to_os_string()
29 }
30
31 /// The serial number of the current volume.
32 pub fn volume_serial_number(&self) -> u32 {
33 self.0.VolumeSerialNumber
34 }
35
36 /// Information of the user process that triggered the callback.
37 pub fn process(&self) -> Process {
38 Process(unsafe { *self.0.ProcessInfo })
39 }
40
41 /// The NTFS file ID of the sync root folder under which the placeholder being operated on
42 /// resides.
43 pub fn sync_root_file_id(&self) -> i64 {
44 self.0.SyncRootFileId
45 }
46
47 /// The NTFS file ID of the placeholder file/directory.
48 pub fn file_id(&self) -> i64 {
49 self.0.FileId
50 }
51
52 /// The logical size of the placeholder file.
53 ///
54 /// If the placeholder is a directory, this value will always equal 0.
55 pub fn file_size(&self) -> u64 {
56 self.0.FileSize as u64
57 }
58
59 // For now this should be cached on creation
60 /// The absolute path of the placeholder file/directory starting from the root directory of the
61 /// volume.
62 ///
63 /// [Read here for more information on this
64 /// function.](https://docs.microsoft.com/en-us/windows/win32/api/cfapi/ns-cfapi-cf_callback_info#remarks)
65 pub fn path(&self) -> PathBuf {
66 let mut path =
67 PathBuf::from(unsafe { U16CStr::from_ptr_str(self.0.VolumeDosName.0) }.to_os_string());
68 path.push(unsafe { U16CStr::from_ptr_str(self.0.NormalizedPath.0) }.to_os_string());
69
70 path
71 }
72
73 /// A numeric scale ranging from
74 /// 0-[15](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Storage/CloudFilters/constant.CF_MAX_PRIORITY_HINT.html)
75 /// to describe the priority of the file operation.
76 ///
77 /// [Currently, this value does not
78 /// change.](https://docs.microsoft.com/en-us/answers/questions/798674/priority-in-cf-callback-info.html)
79 pub fn priority_hint(&self) -> u8 {
80 self.0.PriorityHint
81 }
82
83 // https://docs.microsoft.com/en-us/answers/questions/749979/what-is-a-requestkey-cfapi.html
84 // pub fn request_key(&self) -> i64 {
85 // self.0.RequestKey
86 // }
87
88 /// The byte slice assigned to the current placeholder file/directory.
89 pub fn file_blob(&self) -> &[u8] {
90 unsafe {
91 slice::from_raw_parts(
92 self.0.FileIdentity as *mut u8,
93 self.0.FileIdentityLength as usize,
94 )
95 }
96 }
97
98 /// The byte slice assigned to the current sync root on registration.
99 pub fn register_blob(&self) -> &[u8] {
100 unsafe {
101 slice::from_raw_parts(
102 self.0.SyncRootIdentity as *mut u8,
103 self.0.SyncRootIdentityLength as usize,
104 )
105 }
106 }
107
108 // // https://docs.microsoft.com/en-us/windows/win32/api/cfapi/ne-cfapi-cf_callback_type#remarks
109 // // after 60 seconds of no report, windows will cancel the request with an error,
110 // // this function is a "hack" to avoid the timeout
111 // // https://docs.microsoft.com/en-us/windows/win32/api/cfapi/nf-cfapi-cfexecute#remarks
112 // // CfExecute will reset any timers as stated
113 // /// By default, the operating system will invalidate the callback after 60 seconds of no
114 // /// activity (meaning, no placeholder methods are invoked). If you are prone to this issue,
115 // /// consider calling this method or call placeholder methods more frequently.
116 // pub fn reset_timeout() {}
117
118 /// A raw connection key used to identify the connection.
119 pub(crate) fn connection_key(&self) -> RawConnectionKey {
120 self.0.ConnectionKey.0
121 }
122
123 /// A raw transfer key used to identify the current file operation.
124 pub(crate) fn transfer_key(&self) -> RawTransferKey {
125 self.0.TransferKey
126 }
127}
128
129impl std::fmt::Debug for Request {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.debug_struct("Request")
132 .field("volume_guid_path", &self.volume_guid_path())
133 .field("volume_letter", &self.volume_letter())
134 .field("volume_serial_number", &self.volume_serial_number())
135 .field("process", &self.process())
136 .field("sync_root_file_id", &self.sync_root_file_id())
137 .field("file_id", &self.file_id())
138 .field("file_size", &self.file_size())
139 .field("path", &self.path())
140 .field("priority_hint", &self.priority_hint())
141 .finish()
142 }
143}
144
145/// Information about the calling process.
146pub struct Process(CF_PROCESS_INFO);
147
148impl Process {
149 /// The application's package name.
150 pub fn name(&self) -> OsString {
151 unsafe { U16CStr::from_ptr_str(self.0.PackageName.0) }.to_os_string()
152 }
153
154 /// The ID of the user process.
155 pub fn id(&self) -> u32 {
156 self.0.ProcessId
157 }
158
159 /// The ID of the session where the user process resides.
160 ///
161 /// ## Note
162 ///
163 /// [Process::session_id] is valid in versions 1803 and later.
164 pub fn session_id(&self) -> u32 {
165 self.0.SessionId
166 }
167
168 /// The application's ID.
169 pub fn application_id(&self) -> OsString {
170 unsafe { U16CStr::from_ptr_str(self.0.ApplicationId.0) }.to_os_string()
171 }
172
173 /// The exact command used to initialize the user process.
174 ///
175 /// ## Note
176 ///
177 /// [Process::command_line] is valid in versions 1803 and later.
178 pub fn command_line(&self) -> Option<OsString> {
179 let cmd = unsafe { U16CStr::from_ptr_str(self.0.ImagePath.0) };
180 (cmd != u16cstr!("UNKNOWN")).then(|| cmd.to_os_string())
181 }
182
183 // TODO: Could be optimized
184 /// The absolute path to the main executable file of the process in the format of an NT path.
185 ///
186 /// This function returns [None][std::option::Option::None] when the operating system failed to
187 /// retrieve the path.
188 pub fn path(&self) -> Option<PathBuf> {
189 let path = unsafe { U16CStr::from_ptr_str(self.0.ImagePath.0) };
190 (path != u16cstr!("UNKNOWN")).then(|| PathBuf::from(path.to_os_string()))
191 }
192}
193
194impl std::fmt::Debug for Process {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 f.debug_struct("Process")
197 .field("name", &self.name())
198 .field("id", &self.id())
199 .field("session_id", &self.session_id())
200 .field("application_id", &self.application_id())
201 .field("command_line", &self.command_line())
202 .field("path", &self.path())
203 .finish()
204 }
205}