Skip to main content

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}