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}