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}