fuse_backend_rs/api/filesystem/
async_io.rs

1// Copyright (C) 2021-2022 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::ffi::CStr;
5use std::future::Future;
6use std::io;
7use std::ops::Deref;
8use std::pin::Pin;
9use std::sync::Arc;
10use std::time::Duration;
11
12use async_trait::async_trait;
13
14use super::{Context, Entry, FileSystem, ZeroCopyReader, ZeroCopyWriter};
15use crate::abi::fuse_abi::{stat64, CreateIn, OpenOptions, SetattrValid};
16use crate::file_traits::AsyncFileReadWriteVolatile;
17
18/// A trait for directly copying data from the fuse transport into a `File` without first storing it
19/// in an intermediate buffer in asynchronous mode.
20#[async_trait(?Send)]
21pub trait AsyncZeroCopyReader: ZeroCopyReader {
22    /// Copies at most `count` bytes from `self` directly into `f` at offset `off` without storing
23    /// it in any intermediate buffers. If the return value is `Ok(n)` then it must be guaranteed
24    /// that `0 <= n <= count`. If `n` is `0`, then it can indicate one of 3 possibilities:
25    ///
26    /// 1. There is no more data left in `self`.
27    /// 2. There is no more space in `f`.
28    /// 3. `count` was `0`.
29    ///
30    /// # Errors
31    ///
32    /// If any error is returned then the implementation must guarantee that no bytes were copied
33    /// from `self`. If the underlying write to `f` returns `0` then the implementation must return
34    /// an error of the kind `io::ErrorKind::WriteZero`.
35    async fn async_read_to(
36        &mut self,
37        f: Arc<dyn AsyncFileReadWriteVolatile>,
38        count: usize,
39        off: u64,
40    ) -> io::Result<usize>;
41}
42
43/// A trait for directly copying data from a `RawFd` into the fuse transport without first storing
44/// it in an intermediate buffer in asynchronous mode.
45#[async_trait(?Send)]
46pub trait AsyncZeroCopyWriter: ZeroCopyWriter {
47    /// Copies at most `count` bytes from `f` at offset `off` directly into `self` without storing
48    /// it in any intermediate buffers. If the return value is `Ok(n)` then it must be guaranteed
49    /// that `0 <= n <= count`. If `n` is `0`, then it can indicate one of 3 possibilities:
50    ///
51    /// 1. There is no more data left in `f`.
52    /// 2. There is no more space in `self`.
53    /// 3. `count` was `0`.
54    ///
55    /// # Errors
56    ///
57    /// If any error is returned then the implementation must guarantee that no bytes were copied
58    /// from `f`. If the underlying read from `f` returns `0` then the implementation must return an
59    /// error of the kind `io::ErrorKind::UnexpectedEof`.
60    async fn async_write_from(
61        &mut self,
62        f: Arc<dyn AsyncFileReadWriteVolatile>,
63        count: usize,
64        off: u64,
65    ) -> io::Result<usize>;
66}
67
68/// The main trait that connects a file system with a transport with asynchronous IO.
69#[allow(unused_variables)]
70#[async_trait]
71pub trait AsyncFileSystem: FileSystem {
72    /// Look up a directory entry by name and get its attributes.
73    ///
74    /// If this call is successful then the lookup count of the `Inode` associated with the returned
75    /// `Entry` must be increased by 1.
76    async fn async_lookup(
77        &self,
78        ctx: &Context,
79        parent: Self::Inode,
80        name: &CStr,
81    ) -> io::Result<Entry>;
82
83    /*
84    /// Forget about an inode.
85    ///
86    /// Called when the kernel removes an inode from its internal caches. `count` indicates the
87    /// amount by which the lookup count for the inode should be decreased. If reducing the lookup
88    /// count by `count` causes it to go to zero, then the implementation may delete the `Inode`.
89    async fn async_forget(&self, ctx: Context, inode: Self::Inode, count: u64);
90
91    /// Forget about multiple inodes.
92    ///
93    /// `requests` is a vector of `(inode, count)` pairs. See the documentation for `forget` for
94    /// more information.
95    async fn async_batch_forget(&self, ctx: Context, requests: Vec<(Self::Inode, u64)>);
96     */
97
98    /// Get attributes for a file / directory.
99    ///
100    /// If `handle` is not `None`, then it contains the handle previously returned by the
101    /// implementation after a call to `open` or `opendir`. However, implementations should still
102    /// take care to verify the handle if they do not trust the client (e.g., virtio-fs).
103    ///
104    /// If writeback caching is enabled (`FsOptions::WRITEBACK_CACHE`), then the kernel module
105    /// likely has a better idea of the length of the file than the file system (for
106    /// example, if there was a write that extended the size of the file but has not yet been
107    /// flushed). In this case, the `st_size` field of the returned struct is ignored.
108    ///
109    /// The returned `Duration` indicates how long the returned attributes should be considered
110    /// valid by the client. If the attributes are only changed via the FUSE kernel module (i.e.,
111    /// the kernel module has exclusive access), then this should be a very large value.
112    async fn async_getattr(
113        &self,
114        ctx: &Context,
115        inode: Self::Inode,
116        handle: Option<Self::Handle>,
117    ) -> io::Result<(stat64, Duration)>;
118
119    /// Set attributes for a file / directory.
120    ///
121    /// If `handle` is not `None`, then it contains the handle previously returned by the
122    /// implementation after a call to `open` or `opendir`. However, implementations should still
123    /// take care to verify the handle if they do not trust the client (e.g., virtio-fs).
124    ///
125    /// The `valid` parameter indicates the fields of `attr` that may be considered valid and should
126    /// be set by the file system. The content of all other fields in `attr` is undefined.
127    ///
128    /// If the `FsOptions::HANDLE_KILLPRIV` was set during `init`, then the implementation is
129    /// expected to reset the setuid and setgid bits if the file size or owner is being changed.
130    ///
131    /// This method returns the new attributes after making the modifications requested by the
132    /// client. The returned `Duration` indicates how long the returned attributes should be
133    /// considered valid by the client. If the attributes are only changed via the FUSE kernel
134    /// module (i.e., the kernel module has exclusive access), then this should be a very large
135    /// value.
136    async fn async_setattr(
137        &self,
138        ctx: &Context,
139        inode: Self::Inode,
140        attr: stat64,
141        handle: Option<Self::Handle>,
142        valid: SetattrValid,
143    ) -> io::Result<(stat64, Duration)>;
144
145    /*
146        /// Read a symbolic link.
147        fn readlink(&self, ctx: Context, inode: Self::Inode) -> io::Result<Vec<u8>> {
148            Err(io::Error::from_raw_os_error(libc::ENOSYS))
149        }
150
151        /// Create a symbolic link.
152        ///
153        /// The file system must create a symbolic link named `name` in the directory represented by
154        /// `parent`, which contains the string `linkname`. Returns an `Entry` for the newly created
155        /// symlink.
156        ///
157        /// If this call is successful then the lookup count of the `Inode` associated with the returned
158        /// `Entry` must be increased by 1.
159        fn symlink(
160            &self,
161            ctx: Context,
162            linkname: &CStr,
163            parent: Self::Inode,
164            name: &CStr,
165        ) -> io::Result<Entry> {
166            Err(io::Error::from_raw_os_error(libc::ENOSYS))
167        }
168
169        /// Create a file node.
170        ///
171        /// Create a regular file, character device, block device, fifo, or socket node named `name` in
172        /// the directory represented by `inode`. Valid values for `mode` and `rdev` are the same as
173        /// those accepted by the `mknod(2)` system call. Returns an `Entry` for the newly created node.
174        ///
175        /// When the `FsOptions::DONT_MASK` feature is set, the file system is responsible for setting
176        /// the permissions of the created node to `mode & !umask`.
177        ///
178        /// If this call is successful then the lookup count of the `Inode` associated with the returned
179        /// `Entry` must be increased by 1.
180        fn mknod(
181            &self,
182            ctx: Context,
183            inode: Self::Inode,
184            name: &CStr,
185            mode: u32,
186            rdev: u32,
187            umask: u32,
188        ) -> io::Result<Entry> {
189            Err(io::Error::from_raw_os_error(libc::ENOSYS))
190        }
191
192        /// Create a directory.
193        ///
194        /// When the `FsOptions::DONT_MASK` feature is set, the file system is responsible for setting
195        /// the permissions of the created directory to `mode & !umask`. Returns an `Entry` for the
196        /// newly created directory.
197        ///
198        /// If this call is successful then the lookup count of the `Inode` associated with the returned
199        /// `Entry` must be increased by 1.
200        fn mkdir(
201            &self,
202            ctx: Context,
203            parent: Self::Inode,
204            name: &CStr,
205            mode: u32,
206            umask: u32,
207        ) -> io::Result<Entry> {
208            Err(io::Error::from_raw_os_error(libc::ENOSYS))
209        }
210
211        /// Remove a file.
212        ///
213        /// If the file's inode lookup count is non-zero, then the file system is expected to delay
214        /// removal of the inode until the lookup count goes to zero. See the documentation of the
215        /// `forget` function for more information.
216        fn unlink(&self, ctx: Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
217            Err(io::Error::from_raw_os_error(libc::ENOSYS))
218        }
219
220        /// Remove a directory.
221        ///
222        /// If the directory's inode lookup count is non-zero, then the file system is expected to delay
223        /// removal of the inode until the lookup count goes to zero. See the documentation of the
224        /// `forget` function for more information.
225        fn rmdir(&self, ctx: Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
226            Err(io::Error::from_raw_os_error(libc::ENOSYS))
227        }
228
229        /// Rename a file / directory.
230        ///
231        /// If the destination exists, it should be atomically replaced. If the destination's inode
232        /// lookup count is non-zero, then the file system is expected to delay removal of the inode
233        /// until the lookup count goes to zero. See the documentation of the `forget` function for more
234        /// information.
235        ///
236        /// `flags` may be `libc::RENAME_EXCHANGE` or `libc::RENAME_NOREPLACE`. If
237        /// `libc::RENAME_NOREPLACE` is specified, the implementation must not overwrite `newname` if it
238        /// exists and must return an error instead. If `libc::RENAME_EXCHANGE` is specified, the
239        /// implementation must atomically exchange the two files, i.e., both must exist and neither may
240        /// be deleted.
241        fn rename(
242            &self,
243            ctx: Context,
244            olddir: Self::Inode,
245            oldname: &CStr,
246            newdir: Self::Inode,
247            newname: &CStr,
248            flags: u32,
249        ) -> io::Result<()> {
250            Err(io::Error::from_raw_os_error(libc::ENOSYS))
251        }
252
253        /// Create a hard link.
254        ///
255        /// Create a hard link from `inode` to `newname` in the directory represented by `newparent`.
256        ///
257        /// If this call is successful then the lookup count of the `Inode` associated with the returned
258        /// `Entry` must be increased by 1.
259        fn link(
260            &self,
261            ctx: Context,
262            inode: Self::Inode,
263            newparent: Self::Inode,
264            newname: &CStr,
265        ) -> io::Result<Entry> {
266            Err(io::Error::from_raw_os_error(libc::ENOSYS))
267        }
268    */
269
270    /// Open a file.
271    ///
272    /// Open the file associated with `inode` for reading / writing. All values accepted by the
273    /// `open(2)` system call are valid values for `flags` and must be handled by the file system.
274    /// However, there are some additional rules:
275    ///
276    /// * Creation flags (`libc::O_CREAT`, `libc::O_EXCL`, `libc::O_NOCTTY`) will be filtered out
277    ///   and handled by the kernel.
278    ///
279    /// * The file system should check the access modes (`libc::O_RDONLY`, `libc::O_WRONLY`,
280    ///   `libc::O_RDWR`) to determine if the operation is permitted. If the file system was mounted
281    ///   with the `-o default_permissions` mount option, then this check will also be carried out
282    ///   by the kernel before sending the open request.
283    ///
284    /// * When writeback caching is enabled (`FsOptions::WRITEBACK_CACHE`) the kernel may send read
285    ///   requests even for files opened with `libc::O_WRONLY`. The file system should be prepared
286    ///   to handle this.
287    ///
288    /// * When writeback caching is enabled, the kernel will handle the `libc::O_APPEND` flag.
289    ///   However, this will not work reliably unless the kernel has exclusive access to the file.
290    ///   In this case the file system may either ignore the `libc::O_APPEND` flag or return an
291    ///   error to indicate that reliable `libc::O_APPEND` handling is not available.
292    ///
293    /// * When writeback caching is disabled, the file system is expected to properly handle
294    ///   `libc::O_APPEND` and ensure that each write is appended to the end of the file.
295    ///
296    /// The file system may choose to return a `Handle` to refer to the newly opened file. The
297    /// kernel will then use this `Handle` for all operations on the content of the file (`read`,
298    /// `write`, `flush`, `release`, `fsync`). If the file system does not return a
299    /// `Handle` then the kernel will use the `Inode` for the file to operate on its contents. In
300    /// this case the file system may wish to enable the `FsOptions::ZERO_MESSAGE_OPEN` feature if
301    /// it is supported by the kernel (see below).
302    ///
303    /// The returned `OpenOptions` allow the file system to change the way the opened file is
304    /// handled by the kernel. See the documentation of `OpenOptions` for more information.
305    ///
306    /// If the `FsOptions::ZERO_MESSAGE_OPEN` feature is enabled by both the file system
307    /// implementation and the kernel, then the file system may return an error of `ENOSYS`. This
308    /// will be interpreted by the kernel as success and future calls to `open` and `release` will
309    /// be handled by the kernel without being passed on to the file system.
310    async fn async_open(
311        &self,
312        ctx: &Context,
313        inode: Self::Inode,
314        flags: u32,
315        fuse_flags: u32,
316    ) -> io::Result<(Option<Self::Handle>, OpenOptions)>;
317
318    /// Create and open a file.
319    ///
320    /// If the file does not already exist, the file system should create it with the specified
321    /// `mode`. When the `FsOptions::DONT_MASK` feature is set, the file system is responsible for
322    /// setting the permissions of the created file to `mode & !umask`.
323    ///
324    /// If the file system returns an `ENOSYS` error, then the kernel will treat this method as
325    /// unimplemented and all future calls to `create` will be handled by calling the `mknod` and
326    /// `open` methods instead.
327    ///
328    /// See the documentation for the `open` method for more information about opening the file. In
329    /// addition to the optional `Handle` and the `OpenOptions`, the file system must also return an
330    /// `Entry` for the file. This increases the lookup count for the `Inode` associated with the
331    /// file by 1.
332    async fn async_create(
333        &self,
334        ctx: &Context,
335        parent: Self::Inode,
336        name: &CStr,
337        args: CreateIn,
338    ) -> io::Result<(Entry, Option<Self::Handle>, OpenOptions)>;
339
340    /// Read data from a file.
341    ///
342    /// Returns `size` bytes of data starting from offset `off` from the file associated with
343    /// `inode` or `handle`.
344    ///
345    /// `flags` contains the flags used to open the file. Similarly, `handle` is the `Handle`
346    /// returned by the file system from the `open` method, if any. If the file system
347    /// implementation did not return a `Handle` from `open` then the contents of `handle` are
348    /// undefined.
349    ///
350    /// This method should return exactly the number of bytes requested by the kernel, except in the
351    /// case of error or EOF. Otherwise, the kernel will substitute the rest of the data with
352    /// zeroes. An exception to this rule is if the file was opened with the "direct I/O" option
353    /// (`libc::O_DIRECT`), in which case the kernel will forward the return code from this method
354    /// to the userspace application that made the system call.
355    #[allow(clippy::too_many_arguments)]
356    async fn async_read(
357        &self,
358        ctx: &Context,
359        inode: Self::Inode,
360        handle: Self::Handle,
361        w: &mut (dyn AsyncZeroCopyWriter + Send),
362        size: u32,
363        offset: u64,
364        lock_owner: Option<u64>,
365        flags: u32,
366    ) -> io::Result<usize>;
367
368    /// Write data to a file.
369    ///
370    /// Writes `size` bytes of data starting from offset `off` to the file associated with `inode`
371    /// or `handle`.
372    ///
373    /// `flags` contains the flags used to open the file. Similarly, `handle` is the `Handle`
374    /// returned by the file system from the `open` method, if any. If the file system
375    /// implementation did not return a `Handle` from `open` then the contents of `handle` are
376    /// undefined.
377    ///
378    /// If the `FsOptions::HANDLE_KILLPRIV` feature is not enabled then then the file system is
379    /// expected to clear the setuid and setgid bits.
380    ///
381    /// If `delayed_write` is true then it indicates that this is a write for buffered data.
382    ///
383    /// This method should return exactly the number of bytes requested by the kernel, except in the
384    /// case of error. An exception to this rule is if the file was opened with the "direct I/O"
385    /// option (`libc::O_DIRECT`), in which case the kernel will forward the return code from this
386    /// method to the userspace application that made the system call.
387    #[allow(clippy::too_many_arguments)]
388    async fn async_write(
389        &self,
390        ctx: &Context,
391        inode: Self::Inode,
392        handle: Self::Handle,
393        r: &mut (dyn AsyncZeroCopyReader + Send),
394        size: u32,
395        offset: u64,
396        lock_owner: Option<u64>,
397        delayed_write: bool,
398        flags: u32,
399        fuse_flags: u32,
400    ) -> io::Result<usize>;
401
402    /*
403        /// Flush the contents of a file.
404        ///
405        /// This method is called on every `close()` of a file descriptor. Since it is possible to
406        /// duplicate file descriptors there may be many `flush` calls for one call to `open`.
407        ///
408        /// File systems should not make any assumptions about when `flush` will be
409        /// called or even if it will be called at all.
410        ///
411        /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
412        /// file system did not return a `Handle` from `open` then the contents of `handle` are
413        /// undefined.
414        ///
415        /// Unlike `fsync`, the file system is not required to flush pending writes. One reason to flush
416        /// data is if the file system wants to return write errors during close. However, this is not
417        /// portable because POSIX does not require `close` to wait for delayed I/O to complete.
418        ///
419        /// If the `FsOptions::POSIX_LOCKS` feature is enabled, then the file system must remove all
420        /// locks belonging to `lock_owner`.
421        ///
422        /// If this method returns an `ENOSYS` error then the kernel will treat it as success and all
423        /// subsequent calls to `flush` will be handled by the kernel without being forwarded to the
424        /// file system.
425        fn flush(
426            &self,
427            ctx: Context,
428            inode: Self::Inode,
429            handle: Self::Handle,
430            lock_owner: u64,
431        ) -> io::Result<()> {
432            Err(io::Error::from_raw_os_error(libc::ENOSYS))
433        }
434    */
435
436    /// Synchronize file contents.
437    ///
438    /// File systems must ensure that the file contents have been flushed to disk before returning
439    /// from this method. If `datasync` is true then only the file data (but not the metadata) needs
440    /// to be flushed.
441    ///
442    /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
443    /// file system did not return a `Handle` from `open` then the contents of
444    /// `handle` are undefined.
445    ///
446    /// If this method returns an `ENOSYS` error then the kernel will treat it as success and all
447    /// subsequent calls to `fsync` will be handled by the kernel without being forwarded to the
448    /// file system.
449    async fn async_fsync(
450        &self,
451        ctx: &Context,
452        inode: Self::Inode,
453        datasync: bool,
454        handle: Self::Handle,
455    ) -> io::Result<()>;
456
457    /// Allocate requested space for file data.
458    ///
459    /// If this function returns success, then the file sytem must guarantee that it is possible to
460    /// write up to `length` bytes of data starting at `offset` without failing due to a lack of
461    /// free space on the disk.
462    ///
463    /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
464    /// file system did not return a `Handle` from `open` then the contents of `handle` are
465    /// undefined.
466    ///
467    /// If this method returns an `ENOSYS` error then the kernel will treat that as a permanent
468    /// failure: all future calls to `fallocate` will fail with `EOPNOTSUPP` without being forwarded
469    /// to the file system.
470    async fn async_fallocate(
471        &self,
472        ctx: &Context,
473        inode: Self::Inode,
474        handle: Self::Handle,
475        mode: u32,
476        offset: u64,
477        length: u64,
478    ) -> io::Result<()>;
479
480    /*
481        /// Release an open file.
482        ///
483        /// This method is called when there are no more references to an open file: all file
484        /// descriptors are closed and all memory mappings are unmapped.
485        ///
486        /// For every `open` call there will be exactly one `release` call (unless the file system is
487        /// force-unmounted).
488        ///
489        /// The file system may reply with an error, but error values are not returned to the `close()`
490        /// or `munmap()` which triggered the release.
491        ///
492        /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
493        /// file system did not return a `Handle` from `open` then the contents of
494        /// `handle` are undefined.
495        ///
496        /// If `flush` is `true` then the contents of the file should also be flushed to disk.
497        #[allow(clippy::too_many_arguments)]
498        fn release(
499            &self,
500            ctx: Context,
501            inode: Self::Inode,
502            flags: u32,
503            handle: Self::Handle,
504            flush: bool,
505            flock_release: bool,
506            lock_owner: Option<u64>,
507        ) -> io::Result<()> {
508            Err(io::Error::from_raw_os_error(libc::ENOSYS))
509        }
510
511        /// Get information about the file system.
512        fn statfs(&self, ctx: Context, inode: Self::Inode) -> io::Result<libc::statvfs64> {
513            // Safe because we are zero-initializing a struct with only POD fields.
514            let mut st: libc::statvfs64 = unsafe { mem::zeroed() };
515
516            // This matches the behavior of libfuse as it returns these values if the
517            // filesystem doesn't implement this method.
518            st.f_namemax = 255;
519            st.f_bsize = 512;
520
521            Ok(st)
522        }
523
524        /// Set an extended attribute.
525        ///
526        /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
527        /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `setxattr` without
528        /// forwarding them to the file system.
529        ///
530        /// Valid values for flags are the same as those accepted by the `setxattr(2)` system call and
531        /// have the same behavior.
532        fn setxattr(
533            &self,
534            ctx: Context,
535            inode: Self::Inode,
536            name: &CStr,
537            value: &[u8],
538            flags: u32,
539        ) -> io::Result<()> {
540            Err(io::Error::from_raw_os_error(libc::ENOSYS))
541        }
542
543        /// Get an extended attribute.
544        ///
545        /// If `size` is 0, then the file system should respond with `GetxattrReply::Count` and the
546        /// number of bytes needed to hold the value. If `size` is large enough to hold the value, then
547        /// the file system should reply with `GetxattrReply::Value` and the value of the extended
548        /// attribute. If `size` is not 0 but is also not large enough to hold the value, then the file
549        /// system should reply with an `ERANGE` error.
550        ///
551        /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
552        /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `getxattr` without
553        /// forwarding them to the file system.
554        fn getxattr(
555            &self,
556            ctx: Context,
557            inode: Self::Inode,
558            name: &CStr,
559            size: u32,
560        ) -> io::Result<GetxattrReply> {
561            Err(io::Error::from_raw_os_error(libc::ENOSYS))
562        }
563
564        /// List extended attribute names.
565        ///
566        /// If `size` is 0, then the file system should respond with `ListxattrReply::Count` and the
567        /// number of bytes needed to hold a `\0` byte separated list of the names of all the extended
568        /// attributes. If `size` is large enough to hold the `\0` byte separated list of the attribute
569        /// names, then the file system should reply with `ListxattrReply::Names` and the list. If
570        /// `size` is not 0 but is also not large enough to hold the list, then the file system should
571        /// reply with an `ERANGE` error.
572        ///
573        /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
574        /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `listxattr` without
575        /// forwarding them to the file system.
576        fn listxattr(&self, ctx: Context, inode: Self::Inode, size: u32) -> io::Result<ListxattrReply> {
577            Err(io::Error::from_raw_os_error(libc::ENOSYS))
578        }
579
580        /// Remove an extended attribute.
581        ///
582        /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
583        /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `removexattr` without
584        /// forwarding them to the file system.
585        fn removexattr(&self, ctx: Context, inode: Self::Inode, name: &CStr) -> io::Result<()> {
586            Err(io::Error::from_raw_os_error(libc::ENOSYS))
587        }
588
589        /// Open a directory for reading.
590        ///
591        /// The file system may choose to return a `Handle` to refer to the newly opened directory. The
592        /// kernel will then use this `Handle` for all operations on the content of the directory
593        /// (`readdir`, `readdirplus`, `fsyncdir`, `releasedir`). If the file system does not return a
594        /// `Handle` then the kernel will use the `Inode` for the directory to operate on its contents.
595        /// In this case the file system may wish to enable the `FsOptions::ZERO_MESSAGE_OPENDIR`
596        /// feature if it is supported by the kernel (see below).
597        ///
598        /// The returned `OpenOptions` allow the file system to change the way the opened directory is
599        /// handled by the kernel. See the documentation of `OpenOptions` for more information.
600        ///
601        /// If the `FsOptions::ZERO_MESSAGE_OPENDIR` feature is enabled by both the file system
602        /// implementation and the kernel, then the file system may return an error of `ENOSYS`. This
603        /// will be interpreted by the kernel as success and future calls to `opendir` and `releasedir`
604        /// will be handled by the kernel without being passed on to the file system.
605        fn opendir(
606            &self,
607            ctx: Context,
608            inode: Self::Inode,
609            flags: u32,
610        ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
611            // Matches the behavior of libfuse.
612            Ok((None, OpenOptions::empty()))
613        }
614
615        /// Read a directory.
616        ///
617        /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
618        /// the file system did not return a `Handle` from `opendir` then the contents of `handle` are
619        /// undefined.
620        ///
621        /// `size` indicates the maximum number of bytes that should be returned by this method.
622        ///
623        /// If `offset` is non-zero then it corresponds to one of the `offset` values from a `DirEntry`
624        /// that was previously returned by a call to `readdir` for the same handle. In this case the
625        /// file system should skip over the entries before the position defined by the `offset` value.
626        /// If entries were added or removed while the `Handle` is open then the file system may still
627        /// include removed entries or skip newly created entries. However, adding or removing entries
628        /// should never cause the file system to skip over unrelated entries or include an entry more
629        /// than once. This means that `offset` cannot be a simple index and must include sufficient
630        /// information to uniquely determine the next entry in the list even when the set of entries is
631        /// being changed.
632        ///
633        /// The file system may return entries for the current directory (".") and parent directory
634        /// ("..") but is not required to do so. If the file system does not return these entries, then
635        /// they are implicitly added by the kernel.
636        ///
637        /// The lookup count for `Inode`s associated with the returned directory entries is **NOT**
638        /// affected by this method.
639        ///
640        // TODO(chirantan): Change method signature to return `Iterator<DirEntry>` rather than using an
641        // `FnMut` for adding entries.
642        fn readdir(
643            &self,
644            ctx: Context,
645            inode: Self::Inode,
646            handle: Self::Handle,
647            size: u32,
648            offset: u64,
649            add_entry: &mut dyn FnMut(DirEntry) -> io::Result<usize>,
650        ) -> io::Result<()> {
651            Err(io::Error::from_raw_os_error(libc::ENOSYS))
652        }
653
654        /// Read a directory with entry attributes.
655        ///
656        /// Like `readdir` but also includes the attributes for each directory entry.
657        ///
658        /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
659        /// the file system did not return a `Handle` from `opendir` then the contents of `handle` are
660        /// undefined.
661        ///
662        /// `size` indicates the maximum number of bytes that should be returned by this method.
663        ///
664        /// Unlike `readdir`, the lookup count for `Inode`s associated with the returned directory
665        /// entries **IS** affected by this method (since it returns an `Entry` for each `DirEntry`).
666        /// The count for each `Inode` should be increased by 1.
667        ///
668        /// File systems that implement this method should enable the `FsOptions::DO_READDIRPLUS`
669        /// feature when supported by the kernel. The kernel will not call this method unless that
670        /// feature is enabled.
671        ///
672        /// Additionally, file systems that implement both `readdir` and `readdirplus` should enable the
673        /// `FsOptions::READDIRPLUS_AUTO` feature to allow the kernel to issue both `readdir` and
674        /// `readdirplus` requests, depending on how much information is expected to be required.
675        ///
676        /// TODO(chirantan): Change method signature to return `Iterator<(DirEntry, Entry)>` rather than
677        /// using an `FnMut` for adding entries.
678        fn readdirplus(
679            &self,
680            ctx: Context,
681            inode: Self::Inode,
682            handle: Self::Handle,
683            size: u32,
684            offset: u64,
685            add_entry: &mut dyn FnMut(DirEntry, Entry) -> io::Result<usize>,
686        ) -> io::Result<()> {
687            Err(io::Error::from_raw_os_error(libc::ENOSYS))
688        }
689    */
690
691    /// Synchronize the contents of a directory.
692    ///
693    /// File systems must ensure that the directory contents have been flushed to disk before
694    /// returning from this method. If `datasync` is true then only the directory data (but not the
695    /// metadata) needs to be flushed.
696    ///
697    /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
698    /// the file system did not return a `Handle` from `opendir` then the contents of
699    /// `handle` are undefined.
700    ///
701    /// If this method returns an `ENOSYS` error then the kernel will treat it as success and all
702    /// subsequent calls to `fsyncdir` will be handled by the kernel without being forwarded to the
703    /// file system.
704    async fn async_fsyncdir(
705        &self,
706        ctx: &Context,
707        inode: Self::Inode,
708        datasync: bool,
709        handle: Self::Handle,
710    ) -> io::Result<()>;
711
712    /*
713    /// Release an open directory.
714    ///
715    /// For every `opendir` call there will be exactly one `releasedir` call (unless the file system
716    /// is force-unmounted).
717    ///
718    /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
719    /// the file system did not return a `Handle` from `opendir` then the contents of `handle` are
720    /// undefined.
721    ///
722    /// `flags` contains used the flags used to open the directory in `opendir`.
723    fn releasedir(
724        &self,
725        ctx: Context,
726        inode: Self::Inode,
727        flags: u32,
728        handle: Self::Handle,
729    ) -> io::Result<()> {
730        Err(io::Error::from_raw_os_error(libc::ENOSYS))
731    }
732
733    #[cfg(feature = "virtiofs")]
734    /// Setup a mapping so that guest can access files in DAX style.
735    ///
736    /// The virtio-fs DAX Window allows bypassing guest page cache and allows mapping host
737    /// page cache directly in guest address space.
738    ///
739    /// When a page of file is needed, guest sends a request to map that page (in host page cache)
740    /// in VMM address space. Inside guest this is a physical memory range controlled by virtiofs
741    /// device. And guest directly maps this physical address range using DAX and hence gets
742    /// access to file data on host.
743    ///
744    /// This can speed up things considerably in many situations. Also this can result in
745    /// substantial memory savings as file data does not have to be copied in guest and it is
746    /// directly accessed from host page cache.
747    #[allow(clippy::too_many_arguments)]
748    fn setupmapping(
749        &self,
750        _ctx: Context,
751        inode: Self::Inode,
752        handle: Self::Handle,
753        foffset: u64,
754        len: u64,
755        flags: u64,
756        moffset: u64,
757        vu_req: &mut dyn FsCacheReqHandler,
758    ) -> io::Result<()> {
759        Err(io::Error::from_raw_os_error(libc::ENOSYS))
760    }
761
762    #[cfg(feature = "virtiofs")]
763    /// Teardown a mapping which was setup for guest DAX style access.
764    fn removemapping(
765        &self,
766        _ctx: Context,
767        _inode: Self::Inode,
768        requests: Vec<RemovemappingOne>,
769        vu_req: &mut dyn FsCacheReqHandler,
770    ) -> io::Result<()> {
771        Err(io::Error::from_raw_os_error(libc::ENOSYS))
772    }
773
774    /// Check file access permissions.
775    ///
776    /// This method is called when a userspace process in the client makes an `access()` or
777    /// `chdir()` system call. If the file system was mounted with the `-o default_permissions`
778    /// mount option, then the kernel will perform these checks itself and this method will not be
779    /// called.
780    ///
781    /// If this method returns an `ENOSYS` error, then the kernel will treat it as a permanent
782    /// success: all future calls to `access` will return success without being forwarded to the
783    /// file system.
784    fn access(&self, ctx: Context, inode: Self::Inode, mask: u32) -> io::Result<()> {
785        Err(io::Error::from_raw_os_error(libc::ENOSYS))
786    }
787
788    /// Reposition read/write file offset.
789    fn lseek(
790        &self,
791        ctx: Context,
792        inode: Self::Inode,
793        handle: Self::Handle,
794        offset: u64,
795        whence: u32,
796    ) -> io::Result<u64> {
797        Err(io::Error::from_raw_os_error(libc::ENOSYS))
798    }
799
800    /// TODO: support this
801    fn getlk(&self) -> io::Result<()> {
802        Err(io::Error::from_raw_os_error(libc::ENOSYS))
803    }
804
805    /// TODO: support this
806    fn setlk(&self) -> io::Result<()> {
807        Err(io::Error::from_raw_os_error(libc::ENOSYS))
808    }
809
810    /// TODO: support this
811    fn setlkw(&self) -> io::Result<()> {
812        Err(io::Error::from_raw_os_error(libc::ENOSYS))
813    }
814
815    /// TODO: support this
816    fn ioctl(&self) -> io::Result<()> {
817        Err(io::Error::from_raw_os_error(libc::ENOSYS))
818    }
819
820    /// TODO: support this
821    fn bmap(&self) -> io::Result<()> {
822        Err(io::Error::from_raw_os_error(libc::ENOSYS))
823    }
824
825    /// TODO: support this
826    fn poll(&self) -> io::Result<()> {
827        Err(io::Error::from_raw_os_error(libc::ENOSYS))
828    }
829
830    /// TODO: support this
831    fn notify_reply(&self) -> io::Result<()> {
832        Err(io::Error::from_raw_os_error(libc::ENOSYS))
833    }
834    */
835}
836
837type AttrFuture<'async_trait> =
838    Box<dyn Future<Output = io::Result<(stat64, Duration)>> + Send + 'async_trait>;
839type OpenFuture<'async_trait, H> =
840    Box<dyn Future<Output = io::Result<(Option<H>, OpenOptions)>> + Send + 'async_trait>;
841type CreateFuture<'async_trait, H> =
842    Box<dyn Future<Output = io::Result<(Entry, Option<H>, OpenOptions)>> + Send + 'async_trait>;
843
844impl<FS: AsyncFileSystem> AsyncFileSystem for Arc<FS> {
845    fn async_lookup<'a, 'b, 'c, 'async_trait>(
846        &'a self,
847        ctx: &'b Context,
848        parent: Self::Inode,
849        name: &'c CStr,
850    ) -> Pin<Box<dyn Future<Output = io::Result<Entry>> + Send + 'async_trait>>
851    where
852        'a: 'async_trait,
853        'b: 'async_trait,
854        'c: 'async_trait,
855        Self: 'async_trait,
856    {
857        self.deref().async_lookup(ctx, parent, name)
858    }
859
860    fn async_getattr<'a, 'b, 'async_trait>(
861        &'a self,
862        ctx: &'b Context,
863        inode: Self::Inode,
864        handle: Option<Self::Handle>,
865    ) -> Pin<AttrFuture<'async_trait>>
866    where
867        'a: 'async_trait,
868        'b: 'async_trait,
869        Self: 'async_trait,
870    {
871        self.deref().async_getattr(ctx, inode, handle)
872    }
873
874    fn async_setattr<'a, 'b, 'async_trait>(
875        &'a self,
876        ctx: &'b Context,
877        inode: Self::Inode,
878        attr: stat64,
879        handle: Option<Self::Handle>,
880        valid: SetattrValid,
881    ) -> Pin<AttrFuture<'async_trait>>
882    where
883        'a: 'async_trait,
884        'b: 'async_trait,
885        Self: 'async_trait,
886    {
887        self.deref().async_setattr(ctx, inode, attr, handle, valid)
888    }
889
890    fn async_open<'a, 'b, 'async_trait>(
891        &'a self,
892        ctx: &'b Context,
893        inode: Self::Inode,
894        flags: u32,
895        fuse_flags: u32,
896    ) -> Pin<OpenFuture<'async_trait, Self::Handle>>
897    where
898        'a: 'async_trait,
899        'b: 'async_trait,
900        Self: 'async_trait,
901    {
902        self.deref().async_open(ctx, inode, flags, fuse_flags)
903    }
904
905    fn async_create<'a, 'b, 'c, 'async_trait>(
906        &'a self,
907        ctx: &'b Context,
908        parent: Self::Inode,
909        name: &'c CStr,
910        args: CreateIn,
911    ) -> Pin<CreateFuture<'async_trait, Self::Handle>>
912    where
913        'a: 'async_trait,
914        'b: 'async_trait,
915        'c: 'async_trait,
916        Self: 'async_trait,
917    {
918        self.deref().async_create(ctx, parent, name, args)
919    }
920
921    fn async_read<'a, 'b, 'c, 'async_trait>(
922        &'a self,
923        ctx: &'b Context,
924        inode: Self::Inode,
925        handle: Self::Handle,
926        w: &'c mut (dyn AsyncZeroCopyWriter + Send),
927        size: u32,
928        offset: u64,
929        lock_owner: Option<u64>,
930        flags: u32,
931    ) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'async_trait>>
932    where
933        'a: 'async_trait,
934        'b: 'async_trait,
935        'c: 'async_trait,
936        Self: 'async_trait,
937    {
938        self.deref()
939            .async_read(ctx, inode, handle, w, size, offset, lock_owner, flags)
940    }
941
942    fn async_write<'a, 'b, 'c, 'async_trait>(
943        &'a self,
944        ctx: &'b Context,
945        inode: Self::Inode,
946        handle: Self::Handle,
947        r: &'c mut (dyn AsyncZeroCopyReader + Send),
948        size: u32,
949        offset: u64,
950        lock_owner: Option<u64>,
951        delayed_write: bool,
952        flags: u32,
953        fuse_flags: u32,
954    ) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'async_trait>>
955    where
956        'a: 'async_trait,
957        'b: 'async_trait,
958        'c: 'async_trait,
959        Self: 'async_trait,
960    {
961        self.deref().async_write(
962            ctx,
963            inode,
964            handle,
965            r,
966            size,
967            offset,
968            lock_owner,
969            delayed_write,
970            flags,
971            fuse_flags,
972        )
973    }
974
975    fn async_fsync<'a, 'b, 'async_trait>(
976        &'a self,
977        ctx: &'b Context,
978        inode: Self::Inode,
979        datasync: bool,
980        handle: Self::Handle,
981    ) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'async_trait>>
982    where
983        'a: 'async_trait,
984        'b: 'async_trait,
985        Self: 'async_trait,
986    {
987        self.deref().async_fsync(ctx, inode, datasync, handle)
988    }
989
990    fn async_fallocate<'a, 'b, 'async_trait>(
991        &'a self,
992        ctx: &'b Context,
993        inode: Self::Inode,
994        handle: Self::Handle,
995        mode: u32,
996        offset: u64,
997        length: u64,
998    ) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'async_trait>>
999    where
1000        'a: 'async_trait,
1001        'b: 'async_trait,
1002        Self: 'async_trait,
1003    {
1004        self.deref()
1005            .async_fallocate(ctx, inode, handle, mode, offset, length)
1006    }
1007
1008    fn async_fsyncdir<'a, 'b, 'async_trait>(
1009        &'a self,
1010        ctx: &'b Context,
1011        inode: Self::Inode,
1012        datasync: bool,
1013        handle: Self::Handle,
1014    ) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'async_trait>>
1015    where
1016        'a: 'async_trait,
1017        'b: 'async_trait,
1018        Self: 'async_trait,
1019    {
1020        self.deref().async_fsyncdir(ctx, inode, datasync, handle)
1021    }
1022}