winfsp/filesystem/context.rs
1use crate::U16CStr;
2use crate::error::Result;
3use crate::filesystem::{DirInfo, DirMarker, FileInfo, OpenFileInfo, VolumeInfo};
4use std::ffi::c_void;
5use windows::Win32::Foundation::STATUS_INVALID_DEVICE_REQUEST;
6
7use winfsp_sys::{
8 FILE_ACCESS_RIGHTS, FILE_FLAGS_AND_ATTRIBUTES, FSP_FSCTL_TRANSACT_REQ, FSP_FSCTL_TRANSACT_RSP,
9 PSECURITY_DESCRIPTOR,
10};
11
12/// A security descriptor pointer used to update an existing security descriptor.
13/// The internal layout of this struct should not be relied on.
14#[repr(transparent)]
15pub struct ModificationDescriptor(pub(crate) PSECURITY_DESCRIPTOR);
16
17impl ModificationDescriptor {
18 /// Returns a `PSECURITY_DESCRIPTOR` pointer to the descriptor.
19 pub fn as_mut_ptr(&self) -> PSECURITY_DESCRIPTOR {
20 self.0
21 }
22}
23
24#[derive(Debug)]
25/// The return value of a request to [`FileSystemContext::get_security_by_name`](crate::filesystem::FileSystemContext::get_security_by_name).
26pub struct FileSecurity {
27 /// When a file name containing reparse points anywhere but the final path component is encountered,
28 /// this should be true.
29 pub reparse: bool,
30
31 /// The size of the security descriptor needed to hold security information about the file.
32 pub sz_security_descriptor: u64,
33
34 /// The file attributes of the file.
35 pub attributes: u32,
36}
37
38#[allow(unused_variables)]
39/// The core trait that implements file system operations for a WinFSP file system.
40///
41/// If an implementor of this trait panics in any of the methods,
42/// the caller will receive `STATUS_NONCONTINUABLE_EXCEPTION` (0xC0000025).
43///
44/// Any non-implemented optional methods will return `STATUS_INVALID_DEVICE_REQUEST` (0xC0000010).
45///
46/// Notice that once created, `FileContext` is only accessible through a shared, immutable
47/// reference. This may be an obstacle when the `FileContext` needs to be mutated. This is because the filesystem
48/// driver can call any of the trait methods on any thread, and `&mut` aliasing rules can not be guaranteed.
49/// Instead, interior mutability wrappers should be used whenever a `FileContext` field needs to be mutated.
50/// As an example, [`DirBuffer`](crate::filesystem::DirBuffer) implements interior mutability for the filesystem
51/// driver managed directory buffer.
52pub trait FileSystemContext: Sized {
53 /// The user context that represents an open handle in the file system.
54 ///
55 /// The semantics of `FileContext` vary depending on the volume parameters
56 /// used to mount the file system. See [`FileContextMode`](crate::host::FileContextMode)
57 /// for more information.
58 type FileContext: Sized;
59
60 /// Get security information and attributes for a file or directory by its file name.
61 ///
62 /// If the file system supports reparse points, `reparse_point_resolver` should be
63 /// called with the input file_name. If a reparse point is found at any point
64 /// in the path, the result can be immediately returned like so the following.
65 ///
66 /// ```rust,ignore
67 /// if let Some(security) = resolve_reparse_points(file_name.as_ref()) {
68 /// Ok(security)
69 /// }
70 /// ```
71 fn get_security_by_name(
72 &self,
73 file_name: &U16CStr,
74 security_descriptor: Option<&mut [c_void]>,
75 reparse_point_resolver: impl FnOnce(&U16CStr) -> Option<FileSecurity>,
76 ) -> Result<FileSecurity>;
77
78 /// Opens a file or a directory.
79 fn open(
80 &self,
81 file_name: &U16CStr,
82 create_options: u32,
83 granted_access: FILE_ACCESS_RIGHTS,
84 file_info: &mut OpenFileInfo,
85 ) -> Result<Self::FileContext>;
86
87 /// Close a file or directory handle.
88 fn close(&self, context: Self::FileContext);
89
90 #[allow(clippy::too_many_arguments)]
91 /// Create a new file or directory.
92 fn create(
93 &self,
94 file_name: &U16CStr,
95 create_options: u32,
96 granted_access: FILE_ACCESS_RIGHTS,
97 file_attributes: FILE_FLAGS_AND_ATTRIBUTES,
98 security_descriptor: Option<&[c_void]>,
99 allocation_size: u64,
100 extra_buffer: Option<&[u8]>,
101 extra_buffer_is_reparse_point: bool,
102 file_info: &mut OpenFileInfo,
103 ) -> Result<Self::FileContext> {
104 Err(STATUS_INVALID_DEVICE_REQUEST.into())
105 }
106
107 /// Clean up a file.
108 fn cleanup(&self, context: &Self::FileContext, file_name: Option<&U16CStr>, flags: u32) {}
109
110 /// Flush a file or volume.
111 ///
112 /// If `context` is `None`, the request is to flush the entire volume.
113 fn flush(&self, context: Option<&Self::FileContext>, file_info: &mut FileInfo) -> Result<()> {
114 Err(STATUS_INVALID_DEVICE_REQUEST.into())
115 }
116
117 /// Get file or directory information.
118 fn get_file_info(&self, context: &Self::FileContext, file_info: &mut FileInfo) -> Result<()> {
119 Err(STATUS_INVALID_DEVICE_REQUEST.into())
120 }
121
122 /// Get file or directory security descriptor.
123 fn get_security(
124 &self,
125 context: &Self::FileContext,
126 security_descriptor: Option<&mut [c_void]>,
127 ) -> Result<u64> {
128 Err(STATUS_INVALID_DEVICE_REQUEST.into())
129 }
130
131 /// Set file or directory security descriptor.
132 fn set_security(
133 &self,
134 context: &Self::FileContext,
135 security_information: u32,
136 modification_descriptor: ModificationDescriptor,
137 ) -> Result<()> {
138 Err(STATUS_INVALID_DEVICE_REQUEST.into())
139 }
140
141 /// Overwrite a file.
142 fn overwrite(
143 &self,
144 context: &Self::FileContext,
145 file_attributes: FILE_FLAGS_AND_ATTRIBUTES,
146 replace_file_attributes: bool,
147 allocation_size: u64,
148 extra_buffer: Option<&[u8]>,
149 file_info: &mut FileInfo,
150 ) -> Result<()> {
151 Err(STATUS_INVALID_DEVICE_REQUEST.into())
152 }
153
154 /// Read directory entries from a directory handle.
155 fn read_directory(
156 &self,
157 context: &Self::FileContext,
158 pattern: Option<&U16CStr>,
159 marker: DirMarker,
160 buffer: &mut [u8],
161 ) -> Result<u32> {
162 Err(STATUS_INVALID_DEVICE_REQUEST.into())
163 }
164
165 /// Renames a file or directory.
166 fn rename(
167 &self,
168 context: &Self::FileContext,
169 file_name: &U16CStr,
170 new_file_name: &U16CStr,
171 replace_if_exists: bool,
172 ) -> Result<()> {
173 Err(STATUS_INVALID_DEVICE_REQUEST.into())
174 }
175
176 /// Set file or directory basic information.
177 #[allow(clippy::too_many_arguments)]
178 fn set_basic_info(
179 &self,
180 context: &Self::FileContext,
181 file_attributes: u32,
182 creation_time: u64,
183 last_access_time: u64,
184 last_write_time: u64,
185 last_change_time: u64,
186 file_info: &mut FileInfo,
187 ) -> Result<()> {
188 Err(STATUS_INVALID_DEVICE_REQUEST.into())
189 }
190
191 /// Set the file delete flag.
192 ///
193 /// ## Safety
194 /// The file should **never** be deleted in this function. Instead,
195 /// set a flag to indicate that the file is to be deleted later by
196 /// [`FileSystemContext::cleanup`](crate::filesystem::FileSystemContext::cleanup).
197 fn set_delete(
198 &self,
199 context: &Self::FileContext,
200 file_name: &U16CStr,
201 delete_file: bool,
202 ) -> Result<()> {
203 Err(STATUS_INVALID_DEVICE_REQUEST.into())
204 }
205
206 /// Set the file or allocation size.
207 fn set_file_size(
208 &self,
209 context: &Self::FileContext,
210 new_size: u64,
211 set_allocation_size: bool,
212 file_info: &mut FileInfo,
213 ) -> Result<()> {
214 Err(STATUS_INVALID_DEVICE_REQUEST.into())
215 }
216
217 /// Read from a file. Return the number of bytes read,
218 fn read(&self, context: &Self::FileContext, buffer: &mut [u8], offset: u64) -> Result<u32> {
219 Err(STATUS_INVALID_DEVICE_REQUEST.into())
220 }
221
222 /// Write to a file. Return the number of bytes written.
223 fn write(
224 &self,
225 context: &Self::FileContext,
226 buffer: &[u8],
227 offset: u64,
228 write_to_eof: bool,
229 constrained_io: bool,
230 file_info: &mut FileInfo,
231 ) -> Result<u32> {
232 Err(STATUS_INVALID_DEVICE_REQUEST.into())
233 }
234
235 /// Get directory information for a single file or directory within a parent directory.
236 ///
237 /// This method is only called when [VolumeParams::pass_query_directory_filename](crate::host::VolumeParams::pass_query_directory_filename)
238 /// is set to true, and the file system was created with [FileSystemParams::use_dir_info_by_name](crate::host::FileSystemParams).
239 /// set to true.
240 fn get_dir_info_by_name(
241 &self,
242 context: &Self::FileContext,
243 file_name: &U16CStr,
244 out_dir_info: &mut DirInfo,
245 ) -> Result<()> {
246 Err(STATUS_INVALID_DEVICE_REQUEST.into())
247 }
248
249 /// Get information about the volume.
250 fn get_volume_info(&self, out_volume_info: &mut VolumeInfo) -> Result<()> {
251 Err(STATUS_INVALID_DEVICE_REQUEST.into())
252 }
253
254 /// Set the volume label.
255 fn set_volume_label(&self, volume_label: &U16CStr, volume_info: &mut VolumeInfo) -> Result<()> {
256 Err(STATUS_INVALID_DEVICE_REQUEST.into())
257 }
258
259 /// Get information about named streams.
260 fn get_stream_info(&self, context: &Self::FileContext, buffer: &mut [u8]) -> Result<u32> {
261 Err(STATUS_INVALID_DEVICE_REQUEST.into())
262 }
263
264 /// Get reparse point information by its name.
265 ///
266 /// In the WinFSP C API, this method is usually called manually by the interface method
267 /// `ResolveReparsePoints`. winfsp-rs automatically handles resolution of reparse points
268 /// if this method is implemented properly.
269 fn get_reparse_point_by_name(
270 &self,
271 file_name: &U16CStr,
272 is_directory: bool,
273 buffer: &mut [u8],
274 ) -> Result<u64> {
275 Err(STATUS_INVALID_DEVICE_REQUEST.into())
276 }
277
278 /// Get reparse point information.
279 fn get_reparse_point(
280 &self,
281 context: &Self::FileContext,
282 file_name: &U16CStr,
283 buffer: &mut [u8],
284 ) -> Result<u64> {
285 Err(STATUS_INVALID_DEVICE_REQUEST.into())
286 }
287
288 /// Set reparse point information.
289 fn set_reparse_point(
290 &self,
291 context: &Self::FileContext,
292 file_name: &U16CStr,
293 buffer: &[u8],
294 ) -> Result<()> {
295 Err(STATUS_INVALID_DEVICE_REQUEST.into())
296 }
297
298 /// Delete reparse point information.
299 fn delete_reparse_point(
300 &self,
301 context: &Self::FileContext,
302 file_name: &U16CStr,
303 buffer: &[u8],
304 ) -> Result<()> {
305 Err(STATUS_INVALID_DEVICE_REQUEST.into())
306 }
307
308 /// Get extended attribute information.
309 fn get_extended_attributes(
310 &self,
311 context: &Self::FileContext,
312 buffer: &mut [u8],
313 ) -> Result<u32> {
314 Err(STATUS_INVALID_DEVICE_REQUEST.into())
315 }
316
317 /// Set extended attribute information.
318 fn set_extended_attributes(
319 &self,
320 context: &Self::FileContext,
321 buffer: &[u8],
322 file_info: &mut FileInfo,
323 ) -> Result<()> {
324 Err(STATUS_INVALID_DEVICE_REQUEST.into())
325 }
326
327 /// Process a control code from the [`DeviceIoControl`](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol) API.
328 fn control(
329 &self,
330 context: &Self::FileContext,
331 control_code: u32,
332 input: &[u8],
333 output: &mut [u8],
334 ) -> Result<u32> {
335 Err(STATUS_INVALID_DEVICE_REQUEST.into())
336 }
337
338 /// Inform the file system that its dispatcher has been stopped.
339 ///
340 /// If the dispatcher was stopped via the driver being unloaded, or
341 /// otherwise some non-normal situation, `normally` will be false.
342 ///
343 /// Do not attempt to call [`FspFileSystemStopServiceIfNecessary`](winfsp_sys::FspFileSystemStopServiceIfNecessary),
344 /// it will be called after this function ends. All cleanup done within this function
345 /// should be user-mode only.
346 fn dispatcher_stopped(&self, normally: bool) {}
347
348 /// Get the context response of the current FSP interface operation.
349 ///
350 /// ## Safety
351 /// This function may be used only when servicing one of the `FileSystemContext` operations.
352 /// The current operation context is stored in thread local storage.
353 ///
354 /// ## Warning
355 /// If implementing a filesystem, the default implementation should be sufficient for most if not
356 /// all cases. Be careful if providing your own implementation.
357 unsafe fn with_operation_response<T, F>(&self, f: F) -> Option<T>
358 where
359 F: FnOnce(&mut FSP_FSCTL_TRANSACT_RSP) -> T,
360 {
361 unsafe {
362 if let Some(context) = winfsp_sys::FspFileSystemGetOperationContext().as_ref() {
363 if let Some(response) = context.Response.as_mut() {
364 return Some(f(response));
365 }
366 }
367 }
368 None
369 }
370
371 /// Get the context request of the current FSP interface operation.
372 ///
373 /// ## Safety
374 /// This function may be used only when servicing one of the `FileSystemContext` operations.
375 /// The current operation context is stored in thread local storage.
376 ///
377 /// ## Warning
378 /// If implementing a filesystem, the default implementation should be sufficient for most if not
379 /// all cases. Be careful if providing your own implementation.
380 unsafe fn with_operation_request<T, F>(&self, f: F) -> Option<T>
381 where
382 F: FnOnce(&FSP_FSCTL_TRANSACT_REQ) -> T,
383 {
384 unsafe {
385 if let Some(context) = winfsp_sys::FspFileSystemGetOperationContext().as_ref() {
386 if let Some(request) = context.Request.as_ref() {
387 return Some(f(request));
388 }
389 }
390 }
391 None
392 }
393}
394
395#[cfg_attr(feature = "docsrs", doc(cfg(feature = "async-io")))]
396#[cfg(feature = "async-io")]
397
398/// The trait that implements asynchronous file system operations for a WinFSP file system.
399///
400/// If an implementor of this trait panics in any of the methods,
401/// the caller will receive `STATUS_NONCONTINUABLE_EXCEPTION` (0xC0000025).
402///
403/// Any non-implemented optional methods will return `STATUS_INVALID_DEVICE_REQUEST` (0xC0000010).
404///
405/// The default implementations simply call [`read`](FileSystemContext::read), [`write`](FileSystemContext::write),
406/// and [`read_directory`](FileSystemContext::read_directory) on the base
407/// `FileSystemContext` trait.
408///
409/// The filesystem host must be created with [`FileSystemHost::new_async`](crate::host::FileSystemHost::new_async)
410/// or [`FileSystemHost::new_with_options_async`](crate::host::FileSystemHost::new_with_options_async), otherwise
411/// the synchronous implementations will be used.
412///
413/// `AsyncFileSystemContext` is executor agnostic. Futures produced by [`read_async`](AsyncFileSystemContext::read_async),
414/// [`write_async`](AsyncFileSystemContext::read_async), and
415/// [`read_directory_async`](AsyncFileSystemContext::read_directory_async) will be passed to [`AsyncFileSystemContext::spawn_task`],
416/// which is responsible for dispatching the task to the executor of the implementor's choice. It is recommended, but not required,
417/// that the task executes on a separate thread, executing on the same thread may cause deadlocks.
418#[allow(unused_variables)]
419pub trait AsyncFileSystemContext: FileSystemContext + 'static + Sync
420where
421 <Self as FileSystemContext>::FileContext: Sync,
422{
423 /// Read from a file asynchronously. Return the number of bytes read.
424 ///
425 /// The default implementation for this simply calls the base `FileSystemContext::read`
426 /// implementation.
427 fn read_async(
428 &self,
429 context: &Self::FileContext,
430 buffer: &mut [u8],
431 offset: u64,
432 ) -> impl std::future::Future<Output = Result<u32>> + Send {
433 async move { Self::read(self, context, buffer, offset) }
434 }
435
436 /// Write to a file asynchronously. Return the number of bytes written.
437 ///
438 /// The default implementation for this simply calls the base `FileSystemContext::write`
439 /// implementation.
440 fn write_async(
441 &self,
442 context: &Self::FileContext,
443 buffer: &[u8],
444 offset: u64,
445 write_to_eof: bool,
446 constrained_io: bool,
447 file_info: &mut FileInfo,
448 ) -> impl std::future::Future<Output = Result<u32>> + Send {
449 async move {
450 Self::write(
451 self,
452 context,
453 buffer,
454 offset,
455 write_to_eof,
456 constrained_io,
457 file_info,
458 )
459 }
460 }
461
462 /// Read directory entries from a directory handle asynchronously.
463 fn read_directory_async(
464 &self,
465 context: &Self::FileContext,
466 pattern: Option<&U16CStr>,
467 marker: DirMarker<'_>,
468 buffer: &mut [u8],
469 ) -> impl std::future::Future<Output = Result<u32>> + Send {
470 async move { Self::read_directory(self, context, pattern, marker, buffer) }
471 }
472
473 /// Spawn a task onto the local executor.
474 ///
475 /// The implementations of `read_async`, `write_async`, and `read_directory_async` must
476 /// be compatible with the executor the future is spawned on.
477 fn spawn_task(&self, future: impl std::future::Future<Output = ()> + Send + 'static);
478}