fuse_backend_rs/api/filesystem/
sync_io.rs

1// Copyright (C) 2021-2022 Alibaba Cloud. All rights reserved.
2// Copyright 2019 The Chromium OS Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE-BSD-3-Clause file.
5
6use std::ffi::CStr;
7use std::io;
8use std::mem;
9use std::ops::Deref;
10use std::sync::Arc;
11use std::time::Duration;
12
13use super::{
14    Context, DirEntry, Entry, FileLock, GetxattrReply, IoctlData, ListxattrReply, ZeroCopyReader,
15    ZeroCopyWriter,
16};
17use crate::abi::fuse_abi::{stat64, statvfs64, CreateIn, FsOptions, OpenOptions, SetattrValid};
18#[cfg(feature = "virtiofs")]
19pub use crate::abi::virtio_fs::RemovemappingOne;
20#[cfg(feature = "virtiofs")]
21use crate::transport::FsCacheReqHandler;
22
23/// The main trait that connects a file system with a transport.
24#[allow(unused_variables)]
25pub trait FileSystem {
26    /// Represents a location in the filesystem tree and can be used to perform operations that act
27    /// on the metadata of a file/directory (e.g., `getattr` and `setattr`). Can also be used as the
28    /// starting point for looking up paths in the filesystem tree. An `Inode` may support operating
29    /// directly on the content of the path that to which it points. `FileSystem` implementations
30    /// that support this should set the `FsOptions::ZERO_MESSAGE_OPEN` option in the return value
31    /// of the `init` function. On linux based systems, an `Inode` is equivalent to opening a file
32    /// or directory with the `libc::O_PATH` flag.
33    ///
34    /// # Lookup Count
35    ///
36    /// The `FileSystem` implementation is required to keep a "lookup count" for every `Inode`.
37    /// Every time an `Entry` is returned by a `FileSystem` trait method, this lookup count should
38    /// increase by 1. The lookup count for an `Inode` decreases when the kernel sends a `forget`
39    /// request. `Inode`s with a non-zero lookup count may receive requests from the kernel even
40    /// after calls to `unlink`, `rmdir` or (when overwriting an existing file) `rename`.
41    /// `FileSystem` implementations must handle such requests properly and it is recommended to
42    /// defer removal of the `Inode` until the lookup count reaches zero. Calls to `unlink`, `rmdir`
43    /// or `rename` will be followed closely by `forget` unless the file or directory is open, in
44    /// which case the kernel issues `forget` only after the `release` or `releasedir` calls.
45    ///
46    /// Note that if a file system will be exported over NFS the `Inode`'s lifetime must extend even
47    /// beyond `forget`. See the `generation` field in `Entry`.
48    type Inode: From<u64> + Into<u64>;
49
50    /// Represents a file or directory that is open for reading/writing.
51    type Handle: From<u64> + Into<u64>;
52
53    /// Initialize the file system.
54    ///
55    /// This method is called when a connection to the FUSE kernel module is first established. The
56    /// `capable` parameter indicates the features that are supported by the kernel module. The
57    /// implementation should return the options that it supports. Any options set in the returned
58    /// `FsOptions` that are not also set in `capable` are silently dropped.
59    fn init(&self, capable: FsOptions) -> io::Result<FsOptions> {
60        Ok(FsOptions::empty())
61    }
62
63    /// Clean up the file system.
64    ///
65    /// Called when the filesystem exits. All open `Handle`s should be closed and the lookup count
66    /// for all open `Inode`s implicitly goes to zero. At this point the connection to the FUSE
67    /// kernel module may already be gone so implementations should not rely on being able to
68    /// communicate with the kernel.
69    fn destroy(&self) {}
70
71    /// Look up a directory entry by name and get its attributes.
72    ///
73    /// If this call is successful then the lookup count of the `Inode` associated with the returned
74    /// `Entry` must be increased by 1.
75    fn lookup(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<Entry> {
76        Err(io::Error::from_raw_os_error(libc::ENOSYS))
77    }
78
79    /// Forget about an inode.
80    ///
81    /// Called when the kernel removes an inode from its internal caches. `count` indicates the
82    /// amount by which the lookup count for the inode should be decreased. If reducing the lookup
83    /// count by `count` causes it to go to zero, then the implementation may delete the `Inode`.
84    fn forget(&self, ctx: &Context, inode: Self::Inode, count: u64) {}
85
86    /// Forget about multiple inodes.
87    ///
88    /// `requests` is a vector of `(inode, count)` pairs. See the documentation for `forget` for
89    /// more information.
90    fn batch_forget(&self, ctx: &Context, requests: Vec<(Self::Inode, u64)>) {
91        for (inode, count) in requests {
92            self.forget(ctx, inode, count)
93        }
94    }
95
96    /// Get attributes for a file / directory.
97    ///
98    /// If `handle` is not `None`, then it contains the handle previously returned by the
99    /// implementation after a call to `open` or `opendir`. However, implementations should still
100    /// take care to verify the handle if they do not trust the client (e.g., virtio-fs).
101    ///
102    /// If writeback caching is enabled (`FsOptions::WRITEBACK_CACHE`), then the kernel module
103    /// likely has a better idea of the length of the file than the file system (for
104    /// example, if there was a write that extended the size of the file but has not yet been
105    /// flushed). In this case, the `st_size` field of the returned struct is ignored.
106    ///
107    /// The returned `Duration` indicates how long the returned attributes should be considered
108    /// valid by the client. If the attributes are only changed via the FUSE kernel module (i.e.,
109    /// the kernel module has exclusive access), then this should be a very large value.
110    fn getattr(
111        &self,
112        ctx: &Context,
113        inode: Self::Inode,
114        handle: Option<Self::Handle>,
115    ) -> io::Result<(stat64, Duration)> {
116        Err(io::Error::from_raw_os_error(libc::ENOSYS))
117    }
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    fn 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        Err(io::Error::from_raw_os_error(libc::ENOSYS))
145    }
146
147    /// Read a symbolic link.
148    fn readlink(&self, ctx: &Context, inode: Self::Inode) -> io::Result<Vec<u8>> {
149        Err(io::Error::from_raw_os_error(libc::ENOSYS))
150    }
151
152    /// Create a symbolic link.
153    ///
154    /// The file system must create a symbolic link named `name` in the directory represented by
155    /// `parent`, which contains the string `linkname`. Returns an `Entry` for the newly created
156    /// symlink.
157    ///
158    /// If this call is successful then the lookup count of the `Inode` associated with the returned
159    /// `Entry` must be increased by 1.
160    fn symlink(
161        &self,
162        ctx: &Context,
163        linkname: &CStr,
164        parent: Self::Inode,
165        name: &CStr,
166    ) -> io::Result<Entry> {
167        Err(io::Error::from_raw_os_error(libc::ENOSYS))
168    }
169
170    /// Create a file node.
171    ///
172    /// Create a regular file, character device, block device, fifo, or socket node named `name` in
173    /// the directory represented by `inode`. Valid values for `mode` and `rdev` are the same as
174    /// those accepted by the `mknod(2)` system call. Returns an `Entry` for the newly created node.
175    ///
176    /// When the `FsOptions::DONT_MASK` feature is set, the file system is responsible for setting
177    /// the permissions of the created node to `mode & !umask`.
178    ///
179    /// If this call is successful then the lookup count of the `Inode` associated with the returned
180    /// `Entry` must be increased by 1.
181    fn mknod(
182        &self,
183        ctx: &Context,
184        inode: Self::Inode,
185        name: &CStr,
186        mode: u32,
187        rdev: u32,
188        umask: u32,
189    ) -> io::Result<Entry> {
190        Err(io::Error::from_raw_os_error(libc::ENOSYS))
191    }
192
193    /// Create a directory.
194    ///
195    /// When the `FsOptions::DONT_MASK` feature is set, the file system is responsible for setting
196    /// the permissions of the created directory to `mode & !umask`. Returns an `Entry` for the
197    /// newly created directory.
198    ///
199    /// If this call is successful then the lookup count of the `Inode` associated with the returned
200    /// `Entry` must be increased by 1.
201    fn mkdir(
202        &self,
203        ctx: &Context,
204        parent: Self::Inode,
205        name: &CStr,
206        mode: u32,
207        umask: u32,
208    ) -> io::Result<Entry> {
209        Err(io::Error::from_raw_os_error(libc::ENOSYS))
210    }
211
212    /// Remove a file.
213    ///
214    /// If the file's inode lookup count is non-zero, then the file system is expected to delay
215    /// removal of the inode until the lookup count goes to zero. See the documentation of the
216    /// `forget` function for more information.
217    fn unlink(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
218        Err(io::Error::from_raw_os_error(libc::ENOSYS))
219    }
220
221    /// Remove a directory.
222    ///
223    /// If the directory's inode lookup count is non-zero, then the file system is expected to delay
224    /// removal of the inode until the lookup count goes to zero. See the documentation of the
225    /// `forget` function for more information.
226    fn rmdir(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
227        Err(io::Error::from_raw_os_error(libc::ENOSYS))
228    }
229
230    /// Rename a file / directory.
231    ///
232    /// If the destination exists, it should be atomically replaced. If the destination's inode
233    /// lookup count is non-zero, then the file system is expected to delay removal of the inode
234    /// until the lookup count goes to zero. See the documentation of the `forget` function for more
235    /// information.
236    ///
237    /// `flags` may be `libc::RENAME_EXCHANGE` or `libc::RENAME_NOREPLACE`. If
238    /// `libc::RENAME_NOREPLACE` is specified, the implementation must not overwrite `newname` if it
239    /// exists and must return an error instead. If `libc::RENAME_EXCHANGE` is specified, the
240    /// implementation must atomically exchange the two files, i.e., both must exist and neither may
241    /// be deleted.
242    fn rename(
243        &self,
244        ctx: &Context,
245        olddir: Self::Inode,
246        oldname: &CStr,
247        newdir: Self::Inode,
248        newname: &CStr,
249        flags: u32,
250    ) -> io::Result<()> {
251        Err(io::Error::from_raw_os_error(libc::ENOSYS))
252    }
253
254    /// Create a hard link.
255    ///
256    /// Create a hard link from `inode` to `newname` in the directory represented by `newparent`.
257    ///
258    /// If this call is successful then the lookup count of the `Inode` associated with the returned
259    /// `Entry` must be increased by 1.
260    fn link(
261        &self,
262        ctx: &Context,
263        inode: Self::Inode,
264        newparent: Self::Inode,
265        newname: &CStr,
266    ) -> io::Result<Entry> {
267        Err(io::Error::from_raw_os_error(libc::ENOSYS))
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    fn open(
311        &self,
312        ctx: &Context,
313        inode: Self::Inode,
314        flags: u32,
315        fuse_flags: u32,
316    ) -> io::Result<(Option<Self::Handle>, OpenOptions, Option<u32>)> {
317        // Matches the behavior of libfuse.
318        Ok((None, OpenOptions::empty(), None))
319    }
320
321    /// Create and open a file.
322    ///
323    /// If the file does not already exist, the file system should create it with the specified
324    /// `mode`. When the `FsOptions::DONT_MASK` feature is set, the file system is responsible for
325    /// setting the permissions of the created file to `mode & !umask`.
326    ///
327    /// If the file system returns an `ENOSYS` error, then the kernel will treat this method as
328    /// unimplemented and all future calls to `create` will be handled by calling the `mknod` and
329    /// `open` methods instead.
330    ///
331    /// See the documentation for the `open` method for more information about opening the file. In
332    /// addition to the optional `Handle` and the `OpenOptions`, the file system must also return an
333    /// `Entry` for the file. This increases the lookup count for the `Inode` associated with the
334    /// file by 1.
335    #[allow(clippy::type_complexity)]
336    fn create(
337        &self,
338        ctx: &Context,
339        parent: Self::Inode,
340        name: &CStr,
341        args: CreateIn,
342    ) -> io::Result<(Entry, Option<Self::Handle>, OpenOptions, Option<u32>)> {
343        Err(io::Error::from_raw_os_error(libc::ENOSYS))
344    }
345
346    /// Read data from a file.
347    ///
348    /// Returns `size` bytes of data starting from offset `off` from the file associated with
349    /// `inode` or `handle`.
350    ///
351    /// `flags` contains the flags used to open the file. Similarly, `handle` is the `Handle`
352    /// returned by the file system from the `open` method, if any. If the file system
353    /// implementation did not return a `Handle` from `open` then the contents of `handle` are
354    /// undefined.
355    ///
356    /// This method should return exactly the number of bytes requested by the kernel, except in the
357    /// case of error or EOF. Otherwise, the kernel will substitute the rest of the data with
358    /// zeroes. An exception to this rule is if the file was opened with the "direct I/O" option
359    /// (`libc::O_DIRECT`), in which case the kernel will forward the return code from this method
360    /// to the userspace application that made the system call.
361    #[allow(clippy::too_many_arguments)]
362    fn read(
363        &self,
364        ctx: &Context,
365        inode: Self::Inode,
366        handle: Self::Handle,
367        w: &mut dyn ZeroCopyWriter,
368        size: u32,
369        offset: u64,
370        lock_owner: Option<u64>,
371        flags: u32,
372    ) -> io::Result<usize> {
373        Err(io::Error::from_raw_os_error(libc::ENOSYS))
374    }
375
376    /// Write data to a file.
377    ///
378    /// Writes `size` bytes of data starting from offset `off` to the file associated with `inode`
379    /// or `handle`.
380    ///
381    /// `flags` contains the flags used to open the file. Similarly, `handle` is the `Handle`
382    /// returned by the file system from the `open` method, if any. If the file system
383    /// implementation did not return a `Handle` from `open` then the contents of `handle` are
384    /// undefined.
385    ///
386    /// If the `FsOptions::HANDLE_KILLPRIV` feature is not enabled then then the file system is
387    /// expected to clear the setuid and setgid bits.
388    ///
389    /// If `delayed_write` is true then it indicates that this is a write for buffered data.
390    ///
391    /// This method should return exactly the number of bytes requested by the kernel, except in the
392    /// case of error. An exception to this rule is if the file was opened with the "direct I/O"
393    /// option (`libc::O_DIRECT`), in which case the kernel will forward the return code from this
394    /// method to the userspace application that made the system call.
395    #[allow(clippy::too_many_arguments)]
396    fn write(
397        &self,
398        ctx: &Context,
399        inode: Self::Inode,
400        handle: Self::Handle,
401        r: &mut dyn ZeroCopyReader,
402        size: u32,
403        offset: u64,
404        lock_owner: Option<u64>,
405        delayed_write: bool,
406        flags: u32,
407        fuse_flags: u32,
408    ) -> io::Result<usize> {
409        Err(io::Error::from_raw_os_error(libc::ENOSYS))
410    }
411
412    /// Flush the contents of a file.
413    ///
414    /// This method is called on every `close()` of a file descriptor. Since it is possible to
415    /// duplicate file descriptors there may be many `flush` calls for one call to `open`.
416    ///
417    /// File systems should not make any assumptions about when `flush` will be
418    /// called or even if it will be called at all.
419    ///
420    /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
421    /// file system did not return a `Handle` from `open` then the contents of `handle` are
422    /// undefined.
423    ///
424    /// Unlike `fsync`, the file system is not required to flush pending writes. One reason to flush
425    /// data is if the file system wants to return write errors during close. However, this is not
426    /// portable because POSIX does not require `close` to wait for delayed I/O to complete.
427    ///
428    /// If the `FsOptions::POSIX_LOCKS` feature is enabled, then the file system must remove all
429    /// locks belonging to `lock_owner`.
430    ///
431    /// If this method returns an `ENOSYS` error then the kernel will treat it as success and all
432    /// subsequent calls to `flush` will be handled by the kernel without being forwarded to the
433    /// file system.
434    fn flush(
435        &self,
436        ctx: &Context,
437        inode: Self::Inode,
438        handle: Self::Handle,
439        lock_owner: u64,
440    ) -> io::Result<()> {
441        Err(io::Error::from_raw_os_error(libc::ENOSYS))
442    }
443
444    /// Synchronize file contents.
445    ///
446    /// File systems must ensure that the file contents have been flushed to disk before returning
447    /// from this method. If `datasync` is true then only the file data (but not the metadata) needs
448    /// to be flushed.
449    ///
450    /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
451    /// file system did not return a `Handle` from `open` then the contents of
452    /// `handle` are undefined.
453    ///
454    /// If this method returns an `ENOSYS` error then the kernel will treat it as success and all
455    /// subsequent calls to `fsync` will be handled by the kernel without being forwarded to the
456    /// file system.
457    fn fsync(
458        &self,
459        ctx: &Context,
460        inode: Self::Inode,
461        datasync: bool,
462        handle: Self::Handle,
463    ) -> io::Result<()> {
464        Err(io::Error::from_raw_os_error(libc::ENOSYS))
465    }
466
467    /// Allocate requested space for file data.
468    ///
469    /// If this function returns success, then the file sytem must guarantee that it is possible to
470    /// write up to `length` bytes of data starting at `offset` without failing due to a lack of
471    /// free space on the disk.
472    ///
473    /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
474    /// file system did not return a `Handle` from `open` then the contents of `handle` are
475    /// undefined.
476    ///
477    /// If this method returns an `ENOSYS` error then the kernel will treat that as a permanent
478    /// failure: all future calls to `fallocate` will fail with `EOPNOTSUPP` without being forwarded
479    /// to the file system.
480    fn fallocate(
481        &self,
482        ctx: &Context,
483        inode: Self::Inode,
484        handle: Self::Handle,
485        mode: u32,
486        offset: u64,
487        length: u64,
488    ) -> io::Result<()> {
489        Err(io::Error::from_raw_os_error(libc::ENOSYS))
490    }
491
492    /// Release an open file.
493    ///
494    /// This method is called when there are no more references to an open file: all file
495    /// descriptors are closed and all memory mappings are unmapped.
496    ///
497    /// For every `open` call there will be exactly one `release` call (unless the file system is
498    /// force-unmounted).
499    ///
500    /// The file system may reply with an error, but error values are not returned to the `close()`
501    /// or `munmap()` which triggered the release.
502    ///
503    /// `handle` is the `Handle` returned by the file system from the `open` method, if any. If the
504    /// file system did not return a `Handle` from `open` then the contents of
505    /// `handle` are undefined.
506    ///
507    /// If `flush` is `true` then the contents of the file should also be flushed to disk.
508    #[allow(clippy::too_many_arguments)]
509    fn release(
510        &self,
511        ctx: &Context,
512        inode: Self::Inode,
513        flags: u32,
514        handle: Self::Handle,
515        flush: bool,
516        flock_release: bool,
517        lock_owner: Option<u64>,
518    ) -> io::Result<()> {
519        Err(io::Error::from_raw_os_error(libc::ENOSYS))
520    }
521
522    /// Get information about the file system.
523    fn statfs(&self, ctx: &Context, inode: Self::Inode) -> io::Result<statvfs64> {
524        // Safe because we are zero-initializing a struct with only POD fields.
525        let mut st: statvfs64 = unsafe { mem::zeroed() };
526
527        // This matches the behavior of libfuse as it returns these values if the
528        // filesystem doesn't implement this method.
529        st.f_namemax = 255;
530        st.f_bsize = 512;
531
532        Ok(st)
533    }
534
535    /// Set an extended attribute.
536    ///
537    /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
538    /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `setxattr` without
539    /// forwarding them to the file system.
540    ///
541    /// Valid values for flags are the same as those accepted by the `setxattr(2)` system call and
542    /// have the same behavior.
543    fn setxattr(
544        &self,
545        ctx: &Context,
546        inode: Self::Inode,
547        name: &CStr,
548        value: &[u8],
549        flags: u32,
550    ) -> io::Result<()> {
551        Err(io::Error::from_raw_os_error(libc::ENOSYS))
552    }
553
554    /// Get an extended attribute.
555    ///
556    /// If `size` is 0, then the file system should respond with `GetxattrReply::Count` and the
557    /// number of bytes needed to hold the value. If `size` is large enough to hold the value, then
558    /// the file system should reply with `GetxattrReply::Value` and the value of the extended
559    /// attribute. If `size` is not 0 but is also not large enough to hold the value, then the file
560    /// system should reply with an `ERANGE` error.
561    ///
562    /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
563    /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `getxattr` without
564    /// forwarding them to the file system.
565    fn getxattr(
566        &self,
567        ctx: &Context,
568        inode: Self::Inode,
569        name: &CStr,
570        size: u32,
571    ) -> io::Result<GetxattrReply> {
572        Err(io::Error::from_raw_os_error(libc::ENOSYS))
573    }
574
575    /// List extended attribute names.
576    ///
577    /// If `size` is 0, then the file system should respond with `ListxattrReply::Count` and the
578    /// number of bytes needed to hold a `\0` byte separated list of the names of all the extended
579    /// attributes. If `size` is large enough to hold the `\0` byte separated list of the attribute
580    /// names, then the file system should reply with `ListxattrReply::Names` and the list. If
581    /// `size` is not 0 but is also not large enough to hold the list, then the file system should
582    /// reply with an `ERANGE` error.
583    ///
584    /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
585    /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `listxattr` without
586    /// forwarding them to the file system.
587    fn listxattr(
588        &self,
589        ctx: &Context,
590        inode: Self::Inode,
591        size: u32,
592    ) -> io::Result<ListxattrReply> {
593        Err(io::Error::from_raw_os_error(libc::ENOSYS))
594    }
595
596    /// Remove an extended attribute.
597    ///
598    /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
599    /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `removexattr` without
600    /// forwarding them to the file system.
601    fn removexattr(&self, ctx: &Context, inode: Self::Inode, name: &CStr) -> io::Result<()> {
602        Err(io::Error::from_raw_os_error(libc::ENOSYS))
603    }
604
605    /// Open a directory for reading.
606    ///
607    /// The file system may choose to return a `Handle` to refer to the newly opened directory. The
608    /// kernel will then use this `Handle` for all operations on the content of the directory
609    /// (`readdir`, `readdirplus`, `fsyncdir`, `releasedir`). If the file system does not return a
610    /// `Handle` then the kernel will use the `Inode` for the directory to operate on its contents.
611    /// In this case the file system may wish to enable the `FsOptions::ZERO_MESSAGE_OPENDIR`
612    /// feature if it is supported by the kernel (see below).
613    ///
614    /// The returned `OpenOptions` allow the file system to change the way the opened directory is
615    /// handled by the kernel. See the documentation of `OpenOptions` for more information.
616    ///
617    /// If the `FsOptions::ZERO_MESSAGE_OPENDIR` feature is enabled by both the file system
618    /// implementation and the kernel, then the file system may return an error of `ENOSYS`. This
619    /// will be interpreted by the kernel as success and future calls to `opendir` and `releasedir`
620    /// will be handled by the kernel without being passed on to the file system.
621    fn opendir(
622        &self,
623        ctx: &Context,
624        inode: Self::Inode,
625        flags: u32,
626    ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
627        // Matches the behavior of libfuse.
628        Ok((None, OpenOptions::empty()))
629    }
630
631    /// Read a directory.
632    ///
633    /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
634    /// the file system did not return a `Handle` from `opendir` then the contents of `handle` are
635    /// undefined.
636    ///
637    /// `size` indicates the maximum number of bytes that should be returned by this method.
638    ///
639    /// If `offset` is non-zero then it corresponds to one of the `offset` values from a `DirEntry`
640    /// that was previously returned by a call to `readdir` for the same handle. In this case the
641    /// file system should skip over the entries before the position defined by the `offset` value.
642    /// If entries were added or removed while the `Handle` is open then the file system may still
643    /// include removed entries or skip newly created entries. However, adding or removing entries
644    /// should never cause the file system to skip over unrelated entries or include an entry more
645    /// than once. This means that `offset` cannot be a simple index and must include sufficient
646    /// information to uniquely determine the next entry in the list even when the set of entries is
647    /// being changed.
648    ///
649    /// The file system may return entries for the current directory (".") and parent directory
650    /// ("..") but is not required to do so. If the file system does not return these entries, then
651    /// they are implicitly added by the kernel.
652    ///
653    /// The lookup count for `Inode`s associated with the returned directory entries is **NOT**
654    /// affected by this method.
655    ///
656    // TODO(chirantan): Change method signature to return `Iterator<DirEntry>` rather than using an
657    // `FnMut` for adding entries.
658    fn readdir(
659        &self,
660        ctx: &Context,
661        inode: Self::Inode,
662        handle: Self::Handle,
663        size: u32,
664        offset: u64,
665        add_entry: &mut dyn FnMut(DirEntry) -> io::Result<usize>,
666    ) -> io::Result<()> {
667        Err(io::Error::from_raw_os_error(libc::ENOSYS))
668    }
669
670    /// Read a directory with entry attributes.
671    ///
672    /// Like `readdir` but also includes the attributes for each directory entry.
673    ///
674    /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
675    /// the file system did not return a `Handle` from `opendir` then the contents of `handle` are
676    /// undefined.
677    ///
678    /// `size` indicates the maximum number of bytes that should be returned by this method.
679    ///
680    /// Unlike `readdir`, the lookup count for `Inode`s associated with the returned directory
681    /// entries **IS** affected by this method (since it returns an `Entry` for each `DirEntry`).
682    /// The count for each `Inode` should be increased by 1.
683    ///
684    /// File systems that implement this method should enable the `FsOptions::DO_READDIRPLUS`
685    /// feature when supported by the kernel. The kernel will not call this method unless that
686    /// feature is enabled.
687    ///
688    /// Additionally, file systems that implement both `readdir` and `readdirplus` should enable the
689    /// `FsOptions::READDIRPLUS_AUTO` feature to allow the kernel to issue both `readdir` and
690    /// `readdirplus` requests, depending on how much information is expected to be required.
691    ///
692    /// TODO(chirantan): Change method signature to return `Iterator<(DirEntry, Entry)>` rather than
693    /// using an `FnMut` for adding entries.
694    fn readdirplus(
695        &self,
696        ctx: &Context,
697        inode: Self::Inode,
698        handle: Self::Handle,
699        size: u32,
700        offset: u64,
701        add_entry: &mut dyn FnMut(DirEntry, Entry) -> io::Result<usize>,
702    ) -> io::Result<()> {
703        Err(io::Error::from_raw_os_error(libc::ENOSYS))
704    }
705
706    /// Synchronize the contents of a directory.
707    ///
708    /// File systems must ensure that the directory contents have been flushed to disk before
709    /// returning from this method. If `datasync` is true then only the directory data (but not the
710    /// metadata) needs to be flushed.
711    ///
712    /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
713    /// the file system did not return a `Handle` from `opendir` then the contents of
714    /// `handle` are undefined.
715    ///
716    /// If this method returns an `ENOSYS` error then the kernel will treat it as success and all
717    /// subsequent calls to `fsyncdir` will be handled by the kernel without being forwarded to the
718    /// file system.
719    fn fsyncdir(
720        &self,
721        ctx: &Context,
722        inode: Self::Inode,
723        datasync: bool,
724        handle: Self::Handle,
725    ) -> io::Result<()> {
726        Err(io::Error::from_raw_os_error(libc::ENOSYS))
727    }
728
729    /// Release an open directory.
730    ///
731    /// For every `opendir` call there will be exactly one `releasedir` call (unless the file system
732    /// is force-unmounted).
733    ///
734    /// `handle` is the `Handle` returned by the file system from the `opendir` method, if any. If
735    /// the file system did not return a `Handle` from `opendir` then the contents of `handle` are
736    /// undefined.
737    ///
738    /// `flags` contains used the flags used to open the directory in `opendir`.
739    fn releasedir(
740        &self,
741        ctx: &Context,
742        inode: Self::Inode,
743        flags: u32,
744        handle: Self::Handle,
745    ) -> io::Result<()> {
746        Err(io::Error::from_raw_os_error(libc::ENOSYS))
747    }
748
749    #[cfg(feature = "virtiofs")]
750    /// Setup a mapping so that guest can access files in DAX style.
751    ///
752    /// The virtio-fs DAX Window allows bypassing guest page cache and allows mapping host
753    /// page cache directly in guest address space.
754    ///
755    /// When a page of file is needed, guest sends a request to map that page (in host page cache)
756    /// in VMM address space. Inside guest this is a physical memory range controlled by virtiofs
757    /// device. And guest directly maps this physical address range using DAX and hence gets
758    /// access to file data on host.
759    ///
760    /// This can speed up things considerably in many situations. Also this can result in
761    /// substantial memory savings as file data does not have to be copied in guest and it is
762    /// directly accessed from host page cache.
763    #[allow(clippy::too_many_arguments)]
764    fn setupmapping(
765        &self,
766        _ctx: &Context,
767        inode: Self::Inode,
768        handle: Self::Handle,
769        foffset: u64,
770        len: u64,
771        flags: u64,
772        moffset: u64,
773        vu_req: &mut dyn FsCacheReqHandler,
774    ) -> io::Result<()> {
775        Err(io::Error::from_raw_os_error(libc::ENOSYS))
776    }
777
778    #[cfg(feature = "virtiofs")]
779    /// Teardown a mapping which was setup for guest DAX style access.
780    fn removemapping(
781        &self,
782        _ctx: &Context,
783        _inode: Self::Inode,
784        requests: Vec<RemovemappingOne>,
785        vu_req: &mut dyn FsCacheReqHandler,
786    ) -> io::Result<()> {
787        Err(io::Error::from_raw_os_error(libc::ENOSYS))
788    }
789
790    /// Check file access permissions.
791    ///
792    /// This method is called when a userspace process in the client makes an `access()` or
793    /// `chdir()` system call. If the file system was mounted with the `-o default_permissions`
794    /// mount option, then the kernel will perform these checks itself and this method will not be
795    /// called.
796    ///
797    /// If this method returns an `ENOSYS` error, then the kernel will treat it as a permanent
798    /// success: all future calls to `access` will return success without being forwarded to the
799    /// file system.
800    fn access(&self, ctx: &Context, inode: Self::Inode, mask: u32) -> io::Result<()> {
801        Err(io::Error::from_raw_os_error(libc::ENOSYS))
802    }
803
804    /// Reposition read/write file offset.
805    fn lseek(
806        &self,
807        ctx: &Context,
808        inode: Self::Inode,
809        handle: Self::Handle,
810        offset: u64,
811        whence: u32,
812    ) -> io::Result<u64> {
813        Err(io::Error::from_raw_os_error(libc::ENOSYS))
814    }
815
816    /// Query file lock status
817    fn getlk(
818        &self,
819        ctx: &Context,
820        inode: Self::Inode,
821        handle: Self::Handle,
822        owner: u64,
823        lock: FileLock,
824        flags: u32,
825    ) -> io::Result<FileLock> {
826        Err(io::Error::from_raw_os_error(libc::ENOSYS))
827    }
828
829    /// Grab a file read lock
830    fn setlk(
831        &self,
832        ctx: &Context,
833        inode: Self::Inode,
834        handle: Self::Handle,
835        owner: u64,
836        lock: FileLock,
837        flags: u32,
838    ) -> io::Result<()> {
839        Err(io::Error::from_raw_os_error(libc::ENOSYS))
840    }
841
842    /// Grab a file write lock
843    fn setlkw(
844        &self,
845        ctx: &Context,
846        inode: Self::Inode,
847        handle: Self::Handle,
848        owner: u64,
849        lock: FileLock,
850        flags: u32,
851    ) -> io::Result<()> {
852        Err(io::Error::from_raw_os_error(libc::ENOSYS))
853    }
854
855    /// send ioctl to the file
856    #[allow(clippy::too_many_arguments)]
857    fn ioctl(
858        &self,
859        ctx: &Context,
860        inode: Self::Inode,
861        handle: Self::Handle,
862        flags: u32,
863        cmd: u32,
864        data: IoctlData,
865        out_size: u32,
866    ) -> io::Result<IoctlData> {
867        // Rather than ENOSYS, let's return ENOTTY so simulate that the ioctl call is implemented
868        // but no ioctl number is supported.
869        Err(io::Error::from_raw_os_error(libc::ENOTTY))
870    }
871
872    /// Query a file's block mapping info
873    fn bmap(
874        &self,
875        ctx: &Context,
876        inode: Self::Inode,
877        block: u64,
878        blocksize: u32,
879    ) -> io::Result<u64> {
880        Err(io::Error::from_raw_os_error(libc::ENOSYS))
881    }
882
883    /// Poll a file's events
884    fn poll(
885        &self,
886        ctx: &Context,
887        inode: Self::Inode,
888        handle: Self::Handle,
889        khandle: Self::Handle,
890        flags: u32,
891        events: u32,
892    ) -> io::Result<u32> {
893        Err(io::Error::from_raw_os_error(libc::ENOSYS))
894    }
895
896    /// TODO: support this
897    fn notify_reply(&self) -> io::Result<()> {
898        Err(io::Error::from_raw_os_error(libc::ENOSYS))
899    }
900
901    /// Remap the external IDs in context to internal IDs.
902    fn id_remap(&self, ctx: &mut Context) -> io::Result<()> {
903        Ok(())
904    }
905}
906
907impl<FS: FileSystem> FileSystem for Arc<FS> {
908    type Inode = FS::Inode;
909    type Handle = FS::Handle;
910
911    fn init(&self, capable: FsOptions) -> io::Result<FsOptions> {
912        self.deref().init(capable)
913    }
914
915    fn destroy(&self) {
916        self.deref().destroy()
917    }
918
919    fn lookup(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<Entry> {
920        self.deref().lookup(ctx, parent, name)
921    }
922
923    fn forget(&self, ctx: &Context, inode: Self::Inode, count: u64) {
924        self.deref().forget(ctx, inode, count)
925    }
926
927    fn batch_forget(&self, ctx: &Context, requests: Vec<(Self::Inode, u64)>) {
928        self.deref().batch_forget(ctx, requests)
929    }
930
931    fn getattr(
932        &self,
933        ctx: &Context,
934        inode: Self::Inode,
935        handle: Option<Self::Handle>,
936    ) -> io::Result<(stat64, Duration)> {
937        self.deref().getattr(ctx, inode, handle)
938    }
939
940    fn setattr(
941        &self,
942        ctx: &Context,
943        inode: Self::Inode,
944        attr: stat64,
945        handle: Option<Self::Handle>,
946        valid: SetattrValid,
947    ) -> io::Result<(stat64, Duration)> {
948        self.deref().setattr(ctx, inode, attr, handle, valid)
949    }
950
951    fn readlink(&self, ctx: &Context, inode: Self::Inode) -> io::Result<Vec<u8>> {
952        self.deref().readlink(ctx, inode)
953    }
954
955    fn symlink(
956        &self,
957        ctx: &Context,
958        linkname: &CStr,
959        parent: Self::Inode,
960        name: &CStr,
961    ) -> io::Result<Entry> {
962        self.deref().symlink(ctx, linkname, parent, name)
963    }
964
965    fn mknod(
966        &self,
967        ctx: &Context,
968        inode: Self::Inode,
969        name: &CStr,
970        mode: u32,
971        rdev: u32,
972        umask: u32,
973    ) -> io::Result<Entry> {
974        self.deref().mknod(ctx, inode, name, mode, rdev, umask)
975    }
976
977    fn mkdir(
978        &self,
979        ctx: &Context,
980        parent: Self::Inode,
981        name: &CStr,
982        mode: u32,
983        umask: u32,
984    ) -> io::Result<Entry> {
985        self.deref().mkdir(ctx, parent, name, mode, umask)
986    }
987
988    fn unlink(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
989        self.deref().unlink(ctx, parent, name)
990    }
991
992    fn rmdir(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
993        self.deref().rmdir(ctx, parent, name)
994    }
995
996    fn rename(
997        &self,
998        ctx: &Context,
999        olddir: Self::Inode,
1000        oldname: &CStr,
1001        newdir: Self::Inode,
1002        newname: &CStr,
1003        flags: u32,
1004    ) -> io::Result<()> {
1005        self.deref()
1006            .rename(ctx, olddir, oldname, newdir, newname, flags)
1007    }
1008
1009    fn link(
1010        &self,
1011        ctx: &Context,
1012        inode: Self::Inode,
1013        newparent: Self::Inode,
1014        newname: &CStr,
1015    ) -> io::Result<Entry> {
1016        self.deref().link(ctx, inode, newparent, newname)
1017    }
1018
1019    fn open(
1020        &self,
1021        ctx: &Context,
1022        inode: Self::Inode,
1023        flags: u32,
1024        fuse_flags: u32,
1025    ) -> io::Result<(Option<Self::Handle>, OpenOptions, Option<u32>)> {
1026        self.deref().open(ctx, inode, flags, fuse_flags)
1027    }
1028
1029    fn create(
1030        &self,
1031        ctx: &Context,
1032        parent: Self::Inode,
1033        name: &CStr,
1034        args: CreateIn,
1035    ) -> io::Result<(Entry, Option<Self::Handle>, OpenOptions, Option<u32>)> {
1036        self.deref().create(ctx, parent, name, args)
1037    }
1038
1039    fn read(
1040        &self,
1041        ctx: &Context,
1042        inode: Self::Inode,
1043        handle: Self::Handle,
1044        w: &mut dyn ZeroCopyWriter,
1045        size: u32,
1046        offset: u64,
1047        lock_owner: Option<u64>,
1048        flags: u32,
1049    ) -> io::Result<usize> {
1050        self.deref()
1051            .read(ctx, inode, handle, w, size, offset, lock_owner, flags)
1052    }
1053
1054    #[allow(clippy::too_many_arguments)]
1055    fn write(
1056        &self,
1057        ctx: &Context,
1058        inode: Self::Inode,
1059        handle: Self::Handle,
1060        r: &mut dyn ZeroCopyReader,
1061        size: u32,
1062        offset: u64,
1063        lock_owner: Option<u64>,
1064        delayed_write: bool,
1065        flags: u32,
1066        fuse_flags: u32,
1067    ) -> io::Result<usize> {
1068        self.deref().write(
1069            ctx,
1070            inode,
1071            handle,
1072            r,
1073            size,
1074            offset,
1075            lock_owner,
1076            delayed_write,
1077            flags,
1078            fuse_flags,
1079        )
1080    }
1081
1082    fn flush(
1083        &self,
1084        ctx: &Context,
1085        inode: Self::Inode,
1086        handle: Self::Handle,
1087        lock_owner: u64,
1088    ) -> io::Result<()> {
1089        self.deref().flush(ctx, inode, handle, lock_owner)
1090    }
1091
1092    fn fsync(
1093        &self,
1094        ctx: &Context,
1095        inode: Self::Inode,
1096        datasync: bool,
1097        handle: Self::Handle,
1098    ) -> io::Result<()> {
1099        self.deref().fsync(ctx, inode, datasync, handle)
1100    }
1101
1102    fn fallocate(
1103        &self,
1104        ctx: &Context,
1105        inode: Self::Inode,
1106        handle: Self::Handle,
1107        mode: u32,
1108        offset: u64,
1109        length: u64,
1110    ) -> io::Result<()> {
1111        self.deref()
1112            .fallocate(ctx, inode, handle, mode, offset, length)
1113    }
1114
1115    #[allow(clippy::too_many_arguments)]
1116    fn release(
1117        &self,
1118        ctx: &Context,
1119        inode: Self::Inode,
1120        flags: u32,
1121        handle: Self::Handle,
1122        flush: bool,
1123        flock_release: bool,
1124        lock_owner: Option<u64>,
1125    ) -> io::Result<()> {
1126        self.deref()
1127            .release(ctx, inode, flags, handle, flush, flock_release, lock_owner)
1128    }
1129
1130    fn statfs(&self, ctx: &Context, inode: Self::Inode) -> io::Result<statvfs64> {
1131        self.deref().statfs(ctx, inode)
1132    }
1133
1134    fn setxattr(
1135        &self,
1136        ctx: &Context,
1137        inode: Self::Inode,
1138        name: &CStr,
1139        value: &[u8],
1140        flags: u32,
1141    ) -> io::Result<()> {
1142        self.deref().setxattr(ctx, inode, name, value, flags)
1143    }
1144
1145    fn getxattr(
1146        &self,
1147        ctx: &Context,
1148        inode: Self::Inode,
1149        name: &CStr,
1150        size: u32,
1151    ) -> io::Result<GetxattrReply> {
1152        self.deref().getxattr(ctx, inode, name, size)
1153    }
1154
1155    fn listxattr(
1156        &self,
1157        ctx: &Context,
1158        inode: Self::Inode,
1159        size: u32,
1160    ) -> io::Result<ListxattrReply> {
1161        self.deref().listxattr(ctx, inode, size)
1162    }
1163
1164    fn removexattr(&self, ctx: &Context, inode: Self::Inode, name: &CStr) -> io::Result<()> {
1165        self.deref().removexattr(ctx, inode, name)
1166    }
1167
1168    fn opendir(
1169        &self,
1170        ctx: &Context,
1171        inode: Self::Inode,
1172        flags: u32,
1173    ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
1174        self.deref().opendir(ctx, inode, flags)
1175    }
1176
1177    fn readdir(
1178        &self,
1179        ctx: &Context,
1180        inode: Self::Inode,
1181        handle: Self::Handle,
1182        size: u32,
1183        offset: u64,
1184        add_entry: &mut dyn FnMut(DirEntry) -> io::Result<usize>,
1185    ) -> io::Result<()> {
1186        self.deref()
1187            .readdir(ctx, inode, handle, size, offset, add_entry)
1188    }
1189
1190    fn readdirplus(
1191        &self,
1192        ctx: &Context,
1193        inode: Self::Inode,
1194        handle: Self::Handle,
1195        size: u32,
1196        offset: u64,
1197        add_entry: &mut dyn FnMut(DirEntry, Entry) -> io::Result<usize>,
1198    ) -> io::Result<()> {
1199        self.deref()
1200            .readdirplus(ctx, inode, handle, size, offset, add_entry)
1201    }
1202
1203    fn fsyncdir(
1204        &self,
1205        ctx: &Context,
1206        inode: Self::Inode,
1207        datasync: bool,
1208        handle: Self::Handle,
1209    ) -> io::Result<()> {
1210        self.deref().fsyncdir(ctx, inode, datasync, handle)
1211    }
1212
1213    fn releasedir(
1214        &self,
1215        ctx: &Context,
1216        inode: Self::Inode,
1217        flags: u32,
1218        handle: Self::Handle,
1219    ) -> io::Result<()> {
1220        self.deref().releasedir(ctx, inode, flags, handle)
1221    }
1222
1223    #[cfg(feature = "virtiofs")]
1224    #[allow(clippy::too_many_arguments)]
1225    fn setupmapping(
1226        &self,
1227        ctx: &Context,
1228        inode: Self::Inode,
1229        handle: Self::Handle,
1230        foffset: u64,
1231        len: u64,
1232        flags: u64,
1233        moffset: u64,
1234        vu_req: &mut dyn FsCacheReqHandler,
1235    ) -> io::Result<()> {
1236        self.deref()
1237            .setupmapping(ctx, inode, handle, foffset, len, flags, moffset, vu_req)
1238    }
1239
1240    #[cfg(feature = "virtiofs")]
1241    fn removemapping(
1242        &self,
1243        ctx: &Context,
1244        inode: Self::Inode,
1245        requests: Vec<RemovemappingOne>,
1246        vu_req: &mut dyn FsCacheReqHandler,
1247    ) -> io::Result<()> {
1248        self.deref().removemapping(ctx, inode, requests, vu_req)
1249    }
1250
1251    fn access(&self, ctx: &Context, inode: Self::Inode, mask: u32) -> io::Result<()> {
1252        self.deref().access(ctx, inode, mask)
1253    }
1254
1255    fn lseek(
1256        &self,
1257        ctx: &Context,
1258        inode: Self::Inode,
1259        handle: Self::Handle,
1260        offset: u64,
1261        whence: u32,
1262    ) -> io::Result<u64> {
1263        self.deref().lseek(ctx, inode, handle, offset, whence)
1264    }
1265
1266    /// Query file lock status
1267    fn getlk(
1268        &self,
1269        ctx: &Context,
1270        inode: Self::Inode,
1271        handle: Self::Handle,
1272        owner: u64,
1273        lock: FileLock,
1274        flags: u32,
1275    ) -> io::Result<FileLock> {
1276        self.deref().getlk(ctx, inode, handle, owner, lock, flags)
1277    }
1278
1279    /// Grab a file read lock
1280    fn setlk(
1281        &self,
1282        ctx: &Context,
1283        inode: Self::Inode,
1284        handle: Self::Handle,
1285        owner: u64,
1286        lock: FileLock,
1287        flags: u32,
1288    ) -> io::Result<()> {
1289        self.deref().setlk(ctx, inode, handle, owner, lock, flags)
1290    }
1291
1292    /// Grab a file write lock
1293    fn setlkw(
1294        &self,
1295        ctx: &Context,
1296        inode: Self::Inode,
1297        handle: Self::Handle,
1298        owner: u64,
1299        lock: FileLock,
1300        flags: u32,
1301    ) -> io::Result<()> {
1302        self.deref().setlkw(ctx, inode, handle, owner, lock, flags)
1303    }
1304
1305    /// send ioctl to the file
1306    #[allow(clippy::too_many_arguments)]
1307    fn ioctl(
1308        &self,
1309        ctx: &Context,
1310        inode: Self::Inode,
1311        handle: Self::Handle,
1312        flags: u32,
1313        cmd: u32,
1314        data: IoctlData,
1315        out_size: u32,
1316    ) -> io::Result<IoctlData> {
1317        self.deref()
1318            .ioctl(ctx, inode, handle, flags, cmd, data, out_size)
1319    }
1320
1321    /// Query a file's block mapping info
1322    fn bmap(
1323        &self,
1324        ctx: &Context,
1325        inode: Self::Inode,
1326        block: u64,
1327        blocksize: u32,
1328    ) -> io::Result<u64> {
1329        self.deref().bmap(ctx, inode, block, blocksize)
1330    }
1331
1332    /// Poll a file's events
1333    fn poll(
1334        &self,
1335        ctx: &Context,
1336        inode: Self::Inode,
1337        handle: Self::Handle,
1338        khandle: Self::Handle,
1339        flags: u32,
1340        events: u32,
1341    ) -> io::Result<u32> {
1342        self.deref()
1343            .poll(ctx, inode, handle, khandle, flags, events)
1344    }
1345
1346    /// Send notify reply.
1347    fn notify_reply(&self) -> io::Result<()> {
1348        self.deref().notify_reply()
1349    }
1350
1351    #[inline]
1352    fn id_remap(&self, ctx: &mut Context) -> io::Result<()> {
1353        self.deref().id_remap(ctx)
1354    }
1355}