async_fusex/
fuse_fs.rs

1use crate::error::AsyncFusexError;
2use crate::file_system::FileSystem;
3use crate::fs_util::*;
4use crate::fuse_reply::{
5    ReplyAttr, ReplyBMap, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry,
6    ReplyLock, ReplyOpen, ReplyStatFs, ReplyWrite, ReplyXAttr,
7};
8use crate::fuse_request::Request;
9use crate::{VirtualFs, fs_util};
10use async_trait::async_trait;
11use clippy_utilities::{Cast, OverflowArithmetic};
12use nix::errno::Errno;
13use nix::sys::stat::SFlag;
14use std::path::Path;
15use std::sync::Arc;
16use tracing::{debug, instrument};
17
18pub struct FuseFs {
19    virtual_fs: Arc<dyn VirtualFs>,
20}
21
22impl FuseFs {
23    pub fn new(virtual_fs: Arc<dyn VirtualFs>) -> Self {
24        Self { virtual_fs }
25    }
26}
27
28#[async_trait]
29impl FileSystem for FuseFs {
30
31    /// Initialize filesystem.
32    /// Called before any other filesystem method.
33    async fn init(&self, req: &Request<'_>) -> nix::Result<()> {
34        debug!("init(req={:?}), cache size={}", req, 0_i32);
35        Ok(())
36    }
37
38    /// Clean up filesystem.
39    /// Called on filesystem exit.
40    async fn destroy(&self, req: &Request<'_>) {
41        debug!("destroy(req={:?}), cache size={}", req, 0_i32);
42    }
43
44    /// Look up a directory entry by name and get its attributes.
45    async fn lookup(
46        &self,
47        req: &Request<'_>,
48        parent: INum,
49        name: &str,
50        reply: ReplyEntry<'_>,
51    ) -> nix::Result<usize> {
52        debug!("lookup(parent={}, name={:?}, req={:?})", parent, name, req,);
53
54        let lookup_res = self
55            .virtual_fs
56            .lookup(req.uid(), req.gid(), parent, name)
57            .await;
58
59        match lookup_res {
60            Ok((ttl, file_attr, generation)) => {
61                debug!(
62                    "fusefilesystem call lookup() successfully got the attr={:?} of parent={} and name={:?}",
63                    file_attr, parent, name,
64                );
65                let fuse_attr = fs_util::convert_to_fuse_attr(file_attr);
66                reply.entry(ttl, fuse_attr, generation).await
67            }
68            Err(e) => {
69                debug!("lookup() failed, the error is: {:?}", e);
70                reply.error(e).await
71            }
72        }
73    }
74
75    /// Get file attributes.
76    async fn getattr(&self, req: &Request<'_>, reply: ReplyAttr<'_>) -> nix::Result<usize> {
77        let ino = req.nodeid();
78        debug!("getattr(ino={}, req={:?})", ino, req);
79
80        let getattr_res = self.virtual_fs.getattr(ino).await;
81
82        match getattr_res {
83            Ok((ttl, file_attr)) => {
84                debug!(
85                    "fusefilesystem call getattr() successfully got the attr={:?} of ino={}",
86                    file_attr, ino,
87                );
88                let fuse_attr = fs_util::convert_to_fuse_attr(file_attr);
89                reply.attr(ttl, fuse_attr).await
90            }
91            Err(err) => {
92                // In the previous version ,this panic will never happen.
93                debug!("getattr() failed to get the attr of ino={ino}, the error is: {err}",);
94                reply.error(err).await
95            }
96        }
97    }
98
99    /// Open a file.
100    /// Open flags (with the exception of `O_CREAT`, `O_EXCL`, `O_NOCTTY` and
101    /// `O_TRUNC`) are available in flags. Filesystem may store an arbitrary
102    /// file handle (pointer, index, etc) in fh, and use self in other all
103    /// other file operations (read, write, flush, release, fsync).
104    /// Filesystem may also implement stateless file I/O and not store
105    /// anything in fh. There are also some flags (`direct_io`, `keep_cache`)
106    /// which the filesystem may set, to change the way the file is opened.
107    /// See `fuse_file_info` structure in `fuse_common.h` for more details.
108    async fn open(
109        &self,
110        req: &Request<'_>,
111        flags: u32,
112        reply: ReplyOpen<'_>,
113    ) -> nix::Result<usize> {
114        let ino = req.nodeid();
115        debug!("open(ino={}, flags={}, req={:?})", ino, flags, req);
116
117        let open_res = self.virtual_fs.open(req.uid(), req.gid(), ino, flags).await;
118
119        match open_res {
120            Ok(fd) => {
121                debug!(
122                    "fusefilesystem call open() successfully opened ino={} with flags={:?}, the fd={}",
123                    ino, flags, fd,
124                );
125                reply.opened(fd, flags).await
126            }
127            Err(e) => {
128                debug!(
129                    "fusefilesystem call open() failed to open ino={} with flags={:?}, the error is: {:?}",
130                    ino, flags, e,
131                );
132                reply.error(e).await
133            }
134        }
135    }
136
137    /// Forget about an inode.
138    /// The nlookup parameter indicates the number of lookups previously
139    /// performed on self inode. If the filesystem implements inode
140    /// lifetimes, it is recommended that inodes acquire a single reference
141    /// on each lookup, and lose nlookup references on each forget. The
142    /// filesystem may ignore forget calls, if the inodes don't need to have
143    /// a limited lifetime. On unmount it is not guaranteed, that all referenced
144    /// inodes will receive a forget message.
145    #[instrument(level = "debug", skip(self))]
146    async fn forget(&self, req: &Request<'_>, nlookup: u64) {
147        let ino = req.nodeid();
148        debug!("forget(ino={}, nlookup={}, req={:?})", ino, nlookup, req,);
149        self.virtual_fs.forget(ino, nlookup).await;
150    }
151
152    /// Set file attributes.
153    async fn setattr(
154        &self,
155        req: &Request<'_>,
156        param: SetAttrParam,
157        reply: ReplyAttr<'_>,
158    ) -> nix::Result<usize> {
159        let ino = req.nodeid();
160        debug!("setattr(ino={}, param={:?}, req={:?})", ino, param, req,);
161
162        let set_res = self
163            .virtual_fs
164            .setattr(req.uid(), req.gid(), ino, param)
165            .await;
166
167        match set_res {
168            Ok((ttl, file_attr)) => {
169                debug!(
170                    "fusefilesystem call setattr() successfully set the attr={:?} of ino={}",
171                    file_attr, ino,
172                );
173                let fuse_attr = fs_util::convert_to_fuse_attr(file_attr);
174                reply.attr(ttl, fuse_attr).await
175            }
176            Err(e) => {
177                debug!(
178                    "fusefilesystem call setattr() failed to set the attr of ino={}, the error is: {:?}",
179                    ino, e,
180                );
181                reply.error(e).await
182            }
183        }
184    }
185
186    /// Create file node.
187    /// Create a regular file, character device, block device, fifo or socket
188    /// node.
189    async fn mknod(
190        &self,
191        req: &Request<'_>,
192        param: CreateParam,
193        reply: ReplyEntry<'_>,
194    ) -> nix::Result<usize> {
195        let ino = req.nodeid();
196        let name = param.name.clone();
197        let mode = param.mode;
198        let parent = param.parent;
199        debug!(
200            "mknod(ino={} parent={}, name={:?}, mode={}, req={:?})",
201            ino, parent, name, mode, req,
202        );
203
204        let mknod_res = self.virtual_fs.mknod(param).await;
205
206        match mknod_res {
207            Ok((ttl, file_attr, generation)) => {
208                debug!(
209                    "fusefilesystem call mknod() successfully created a file name={:?} and mode={:?} under parent ino={} with attr={:?}",
210                    name, mode, parent, file_attr,
211                );
212                let fuse_attr = fs_util::convert_to_fuse_attr(file_attr);
213                reply.entry(ttl, fuse_attr, generation).await
214            }
215            Err(e) => {
216                debug!(
217                    "fusefilesystem call mknod() failed to create a file name={:?} and mode={:?} under parent ino={}, the error is: {:?}",
218                    name, mode, parent, e,
219                );
220                reply.error(e).await
221            }
222        }
223    }
224
225    /// Create a directory.
226    async fn mkdir(
227        &self,
228        req: &Request<'_>,
229        parent: INum,
230        name: &str,
231        mode: u32,
232        reply: ReplyEntry<'_>,
233    ) -> nix::Result<usize> {
234        debug!(
235            "mkdir(parent={}, name={:?}, mode={}, req={:?})",
236            parent, name, mode, req,
237        );
238
239        // TODO: this mkdir param is different from mknod one, need to unify them
240        let param = CreateParam {
241            parent,
242            name: name.to_owned(),
243            mode,
244            rdev: 0,
245            uid: req.uid(),
246            gid: req.gid(),
247            node_type: SFlag::S_IFDIR,
248            link: None,
249        };
250
251        let mkdir_res = self.virtual_fs.mkdir(param).await;
252
253        match mkdir_res {
254            Ok((ttl, file_attr, generation)) => {
255                debug!(
256                    "fusefilesystem call mkdir() successfully created a directory name={:?} and mode={:?} under parent ino={} with attr={:?}",
257                    name, mode, parent, file_attr,
258                );
259                let fuse_attr = fs_util::convert_to_fuse_attr(file_attr);
260                reply.entry(ttl, fuse_attr, generation).await
261            }
262            Err(e) => {
263                debug!(
264                    "fusefilesystem call mkdir() failed to create a directory name={:?} and mode={:?} under parent ino={}, \
265                        the error is: {:?}",
266                    name, mode, parent, e,
267                );
268                reply.error(e).await
269            }
270        }
271    }
272
273    /// Remove a file.
274    #[instrument(level = "debug", skip(self), err, ret)]
275    async fn unlink(
276        &self,
277        req: &Request<'_>,
278        parent: INum,
279        name: &str,
280        reply: ReplyEmpty<'_>,
281    ) -> nix::Result<usize> {
282        debug!("unlink(parent={}, name={:?}, req={:?}", parent, name, req,);
283
284        let unlink_res = self
285            .virtual_fs
286            .unlink(req.uid(), req.gid(), parent, name)
287            .await;
288
289        match unlink_res {
290            Ok(()) => {
291                debug!(
292                    "fusefilesystem call unlink() successfully removed a file name={:?} under parent ino={}",
293                    name, parent,
294                );
295                reply.ok().await
296            }
297            Err(e) => {
298                debug!(
299                    "fusefilesystem call unlink() failed to remove a file name={:?} under parent ino={}, the error is: {:?}",
300                    name, parent, e,
301                );
302                reply.error(e).await
303            }
304        }
305    }
306
307    /// Remove a directory.
308    #[instrument(level = "debug", skip(self), err, ret)]
309    async fn rmdir(
310        &self,
311        req: &Request<'_>,
312        parent: INum,
313        dir_name: &str,
314        reply: ReplyEmpty<'_>,
315    ) -> nix::Result<usize> {
316        let rmdir_res = self
317            .virtual_fs
318            .rmdir(req.uid(), req.gid(), parent, dir_name)
319            .await;
320
321        match rmdir_res {
322            Ok(_) => {
323                debug!(
324                    "fusefilesystem call rmdir() successfully removed a directory name={:?} under parent ino={}",
325                    dir_name, parent,
326                );
327                reply.ok().await
328            }
329            Err(e) => {
330                debug!(
331                    "fusefilesystem call rmdir() failed to remove a directory name={:?} under parent ino={}, the error is: {:?}",
332                    dir_name, parent, e,
333                );
334                reply.error(e).await
335            }
336        }
337    }
338
339    /// Rename a file
340    ///
341    /// If the target exists it should be atomically replaced. If
342    /// the target's inode's lookup count is non-zero, the file
343    /// system is expected to postpone any removal of the inode
344    /// until the lookup count reaches zero (see description of the
345    /// forget function).
346    ///
347    /// *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If
348    /// `RENAME_NOREPLACE` is specified, the filesystem must not
349    /// overwrite *newname* if it exists and return an error
350    /// instead. If `RENAME_EXCHANGE` is specified, the filesystem
351    /// must atomically exchange the two files, i.e. both must
352    /// exist and neither may be deleted.
353    async fn rename(
354        &self,
355        req: &Request<'_>,
356        param: RenameParam,
357        reply: ReplyEmpty<'_>,
358    ) -> nix::Result<usize> {
359        let ino = req.nodeid();
360        let old_parent = param.old_parent;
361        let old_name = param.old_name.clone();
362        let new_parent = param.new_parent;
363        let new_name = param.new_name.clone();
364        debug!(
365            "rename(ino={} oldparent={}, oldname={:?}, newparent={}, newname={:?}, req={:?})",
366            ino, old_parent, old_name, new_parent, new_name, req,
367        );
368
369        let rename_res = self.virtual_fs.rename(req.uid(), req.gid(), param).await;
370
371        match rename_res {
372            Ok(()) => {
373                debug!(
374                    "fusefilesystem call rename() successfully renamed the file name={:?} under parent ino={}",
375                    old_name, old_parent,
376                );
377                reply.ok().await
378            }
379            Err(e) => {
380                debug!(
381                    "fusefilesystem call rename() failed to rename the file name={:?} under parent ino={}, the error is: {:?}",
382                    old_name, old_parent, e,
383                );
384                reply.error(e).await
385            }
386        }
387    }
388
389    /// Read data.
390    /// Read should send exactly the number of bytes requested except on EOF or
391    /// error, otherwise the rest of the data will be substituted with
392    /// zeroes. An exception to self is when the file has been opened in
393    /// `direct_io` mode, in which case the return value of the read system
394    /// call will reflect the return value of self operation. fh will
395    /// contain the value set by the open method, or will be undefined
396    /// if the open method didn't set any value.
397    async fn read(
398        &self,
399        req: &Request<'_>,
400        fh: u64,
401        offset: i64,
402        size: u32,
403        reply: ReplyData<'_>,
404    ) -> nix::Result<usize> {
405        let ino = req.nodeid();
406        let offset: u64 = offset.cast();
407        debug!(
408            "read(ino={}, fh={}, offset={}, size={}, req={:?})",
409            ino, fh, offset, size, req,
410        );
411
412        // Try to use the buffer size as the size of the read buffer
413        let mut buf = Vec::new();
414
415        let read_res = self.virtual_fs.read(ino, offset, size, &mut buf).await;
416
417        // Check the load result
418        match read_res {
419            Ok(content_size) => {
420                debug!(
421                    "fusefilesystem call read() successfully read the content of ino={} with size={}",
422                    ino, content_size,
423                );
424                reply.data(buf.clone()).await
425            }
426            Err(e) => {
427                debug!(
428                    "fusefilesystem call read() failed to read the content of ino={}, the error is: {:?}",
429                    ino, e,
430                );
431                reply.error(e).await
432            }
433        }
434    }
435
436    /// Write data.
437    /// Write should return exactly the number of bytes requested except on
438    /// error. An exception to self is when the file has been opened in
439    /// `direct_io` mode, in which case the return value of the write system
440    /// call will reflect the return value of self operation. fh will
441    /// contain the value set by the open method, or will be undefined if
442    /// the open method did not set any value.
443    #[instrument(level = "debug", skip(self, data, req), err, ret)]
444    async fn write(
445        &self,
446        req: &Request<'_>,
447        fh: u64,
448        offset: i64,
449        data: Vec<u8>,
450        flags: u32,
451        reply: ReplyWrite<'_>,
452    ) -> nix::Result<usize> {
453        let ino = req.nodeid();
454        let data_len: u64 = data.len().cast();
455        debug!(
456            "write(ino={}, fh={}, offset={}, data_len={}, req={:?})",
457            ino, fh, offset, data_len, req,
458        );
459
460        let write_result = self.virtual_fs.write(ino, offset, &data, flags).await;
461
462        match write_result {
463            Ok(()) => {
464                debug!(
465                    "fusefilesystem call write() successfully wrote the content of ino={} with size={}",
466                    ino, data_len,
467                );
468                reply.written(data_len.cast()).await
469            }
470            Err(e) => {
471                debug!(
472                    "fusefilesystem call write() failed to write the content of ino={} with size={}, the error is: {:?}",
473                    ino, data_len, e,
474                );
475                reply.error(e).await
476            }
477        }
478    }
479
480    /// Flush method.
481    /// This is called on each close() of the opened file. Since file
482    /// descriptors can be duplicated (dup, dup2, fork), for one open call
483    /// there may be many flush calls. Filesystems should not assume that
484    /// flush will always be called after some writes, or that if will be
485    /// called at all. fh will contain the value set by the open method, or
486    /// will be undefined if the open method did not set any value.
487    /// NOTE: the name of the method is misleading, since (unlike fsync) the
488    /// filesystem is not forced to flush pending writes. One reason to
489    /// flush data, is if the filesystem wants to return write errors. If
490    /// the filesystem supports file locking operations (setlk, getlk) it
491    /// should remove all locks belonging to `lock_owner`.
492    #[instrument(level = "debug", skip(self), err, ret)]
493    async fn flush(
494        &self,
495        req: &Request<'_>,
496        fh: u64,
497        lock_owner: u64,
498        reply: ReplyEmpty<'_>,
499    ) -> nix::Result<usize> {
500        let ino = req.nodeid();
501        debug!(
502            "flush(ino={}, fh={}, lock_owner={}, req={:?})",
503            ino, fh, lock_owner, req,
504        );
505
506        // This is called from every close on an open file, so call the
507        // close on the underlying filesystem.	But since flush may be
508        // called multiple times for an open file, self must not really
509        // close the file. This is important if used on a network
510        // filesystem like NFS which flush the data/metadata on close()
511        let flush_res = self.virtual_fs.flush(ino, lock_owner).await;
512
513        match flush_res {
514            Ok(()) => {
515                debug!(
516                    "fusefilesystem call flush() successfully flushed the content of ino={} with fh={}",
517                    ino, fh,
518                );
519                reply.ok().await
520            }
521            Err(e) => {
522                debug!(
523                    "fusefilesystem call flush() failed to flush the content of ino={} with fh={}, the error is: {:?}",
524                    ino, fh, e,
525                );
526                reply.error(e).await
527            }
528        }
529    }
530
531    /// Release an open file.
532    /// Release is called when there are no more references to an open file: all
533    /// file descriptors are closed and all memory mappings are unmapped.
534    /// For every open call there will be exactly one release call. The
535    /// filesystem may reply with an error, but error values are not
536    /// returned to close() or munmap() which triggered the release. fh will
537    /// contain the value set by the open method, or will be undefined
538    /// if the open method didn't set any value. flags will contain the same
539    /// flags as for open.
540    #[instrument(level = "debug", skip(self), err, ret)]
541    async fn release(
542        &self,
543        req: &Request<'_>,
544        fh: u64,
545        flags: u32, // same as the open flags
546        lock_owner: u64,
547        flush: bool,
548        reply: ReplyEmpty<'_>,
549    ) -> nix::Result<usize> {
550        let ino = req.nodeid();
551        debug!(
552            "release(ino={}, fh={}, flags={}, lock_owner={}, flush={}, req={:?})",
553            ino, fh, flags, lock_owner, flush, req,
554        );
555
556        let release_res = self.virtual_fs.release(ino, flags, lock_owner, flush).await;
557
558        match release_res {
559            Ok(()) => {
560                debug!(
561                    "fusefilesystem call release() successfully released the content of ino={} with fh={}",
562                    ino, fh,
563                );
564                reply.ok().await
565            }
566            Err(e) => {
567                debug!(
568                    "fusefilesystem call release() failed to release the content of ino={} with fh={}, the error is: {:?}",
569                    ino, fh, e,
570                );
571                reply.error(e).await
572            }
573        }
574    }
575
576    /// Synchronize file contents.
577    /// If the datasync parameter is non-zero, then only the user data should be
578    /// flushed, not the meta data.
579    #[instrument(level = "debug", skip(self), err, ret)]
580    async fn fsync(
581        &self,
582        req: &Request<'_>,
583        fh: u64,
584        datasync: bool,
585        reply: ReplyEmpty<'_>,
586    ) -> nix::Result<usize> {
587        let ino = req.nodeid();
588        debug!("fsync(ino={}, fh={}, req={:?})", ino, fh, req,);
589
590        let fsync_res = self.virtual_fs.fsync(ino, datasync).await;
591
592        match fsync_res {
593            Ok(()) => {
594                debug!(
595                    "fusefilesystem call fsync() successfully flushed the content of ino={} with fh={}",
596                    ino, fh,
597                );
598                reply.ok().await
599            }
600            Err(e) => {
601                debug!(
602                    "fusefilesystem call fsync() failed to flush the content of ino={} with fh={}, the error is: {:?}",
603                    ino, fh, e,
604                );
605                reply.error(e).await
606            }
607        }
608    }
609
610    /// Open a directory.
611    /// Filesystem may store an arbitrary file handle (pointer, index, etc) in
612    /// fh, and use self in other all other directory stream operations
613    /// (readdir, releasedir, fsyncdir). Filesystem may also implement
614    /// stateless directory I/O and not store anything in fh, though that
615    /// makes it impossible to implement standard conforming
616    /// directory stream operations in case the contents of the directory can
617    /// change between opendir and releasedir.
618    async fn opendir(
619        &self,
620        req: &Request<'_>,
621        flags: u32,
622        reply: ReplyOpen<'_>,
623    ) -> nix::Result<usize> {
624        let ino = req.nodeid();
625        debug!("opendir(ino={}, flags={}, req={:?})", ino, flags, req,);
626
627        let opendir_res = self
628            .virtual_fs
629            .opendir(req.uid(), req.gid(), ino, flags)
630            .await;
631
632        match opendir_res {
633            Ok(new_fd) => {
634                debug!(
635                    "fusefilesystem call opendir() successfully duplicated the file handler of ino={} with flags={:?}",
636                    ino, flags,
637                );
638                reply.opened(new_fd, flags).await
639            }
640            Err(e) => {
641                debug!(
642                    "fusefilesystem call opendir() failed to duplicate the file handler of ino={} with flags={:?}, the error is: {:?}",
643                    ino, flags, e,
644                );
645                reply.error(e).await
646            }
647        }
648    }
649
650    /// Read directory.
651    /// Send a buffer filled using buffer.fill(), with size not exceeding the
652    /// requested size. Send an empty buffer on end of stream. fh will contain
653    /// the value set by the opendir method, or will be undefined if the
654    /// opendir method didn't set any value.
655    async fn readdir(
656        &self,
657        req: &Request<'_>,
658        fh: u64,
659        offset: i64,
660        mut reply: ReplyDirectory<'_>,
661    ) -> nix::Result<usize> {
662        let ino = req.nodeid();
663        debug!(
664            "readdir(ino={}, fh={}, offset={}, req={:?})",
665            ino, fh, offset, req,
666        );
667
668        let readdir_res = self
669            .virtual_fs
670            .readdir(req.uid(), req.gid(), ino, fh, offset)
671            .await;
672
673        match readdir_res {
674            Ok(dir_entries) => {
675                for (i, dir_etnry) in dir_entries.iter().enumerate().skip(offset.cast()) {
676                    reply.add(
677                        dir_etnry.ino(),
678                        offset.overflow_add(i.cast()).overflow_add(1), /* i + 1 means the index of
679                                                                        * the next entry */
680                        dir_etnry.file_type().into(),
681                        dir_etnry.name(),
682                    );
683                }
684
685                debug!(
686                    "fusefilesystem call readdir() successfully read the content of ino={} with fh={}",
687                    ino, fh,
688                );
689                reply.ok().await
690            }
691            Err(e) => {
692                debug!(
693                    "fusefilesystem call readdir() failed to read the content of ino={} with fh={}, the error is: {:?}",
694                    ino, fh, e,
695                );
696                reply.error(e).await
697            }
698        }
699    }
700
701    /// Release an open directory.
702    /// For every opendir call there will be exactly one releasedir call. fh
703    /// will contain the value set by the opendir method, or will be
704    /// undefined if the opendir method didn't set any value.
705    async fn releasedir(
706        &self,
707        req: &Request<'_>,
708        fh: u64,
709        flags: u32,
710        reply: ReplyEmpty<'_>,
711    ) -> nix::Result<usize> {
712        let ino = req.nodeid();
713        debug!(
714            "releasedir(ino={}, fh={}, flags={}, req={:?})",
715            ino, fh, flags, req,
716        );
717
718        // TODO: handle flags
719        let releasedir_res = self.virtual_fs.releasedir(ino, fh, flags).await;
720
721        match releasedir_res {
722            Ok(()) => {
723                debug!(
724                    "fusefilesystem call releasedir() successfully released the content of ino={} with fh={}",
725                    ino, fh,
726                );
727                reply.ok().await
728            }
729            Err(e) => {
730                debug!(
731                    "fusefilesystem call releasedir() failed to release the content of ino={} with fh={}, the error is: {:?}",
732                    ino, fh, e,
733                );
734                reply.error(e).await
735            }
736        }
737    }
738
739    /// Synchronize directory contents.
740    /// If the datasync parameter is set, then only the directory contents
741    /// should be flushed, not the meta data. fh will contain the value set
742    /// by the opendir method, or will be undefined if the opendir method
743    /// didn't set any value.
744    #[instrument(level = "debug", skip(self), err, ret)]
745    async fn fsyncdir(
746        &self,
747        req: &Request<'_>,
748        fh: u64,
749        datasync: bool,
750        reply: ReplyEmpty<'_>,
751    ) -> nix::Result<usize> {
752        let ino = req.nodeid();
753        debug!(
754            "fsyncdir(ino={}, fh={}, datasync={}, req={:?})",
755            ino, fh, datasync, req,
756        );
757
758        let fsyncdir_res = self.virtual_fs.fsyncdir(ino, fh, datasync).await;
759
760        match fsyncdir_res {
761            Ok(()) => {
762                debug!(
763                    "fusefilesystem call fsyncdir() successfully flushed the content of ino={} with fh={}",
764                    ino, fh,
765                );
766            }
767            Err(e) => {
768                debug!(
769                    "fusefilesystem call fsyncdir() failed to flush the content of ino={} with fh={}, the error is: {:?}",
770                    ino, fh, e,
771                );
772            }
773        }
774
775        // Similarity to rmdir, we don't store dir information in the persistent
776        // storage, so we don't need to flush it
777        reply.ok().await
778    }
779
780    /// Get file system statistics.
781    /// The `f_favail`, `f_fsid` and `f_flag` fields are ignored
782    async fn statfs(&self, req: &Request<'_>, reply: ReplyStatFs<'_>) -> nix::Result<usize> {
783        let ino = req.nodeid();
784        debug!("statfs(ino={}, req={:?})", ino, req,);
785
786        let statfs_res = self.virtual_fs.statfs(req.uid(), req.gid(), ino).await;
787
788        match statfs_res {
789            Ok(statvfs) => {
790                debug!(
791                    "fusefilesystem call statfs() successfully read the statvfs of ino={}",
792                    ino,
793                );
794                reply.statfs(statvfs).await
795            }
796            Err(e) => {
797                debug!(
798                    "fusefilesystem call statfs() failed to read the statvfs of ino={}, the error is: {:?}",
799                    ino, e,
800                );
801                reply.error(e).await
802            }
803        }
804    }
805
806    /// Read symbolic link.
807    async fn readlink(&self, req: &Request<'_>, reply: ReplyData<'_>) -> nix::Result<usize> {
808        let ino = req.nodeid();
809        debug!("readlink(ino={}, req={:?})", ino, req,);
810
811        let readlink_res = self.virtual_fs.readlink(ino).await;
812
813        match readlink_res {
814            Ok(target_path) => {
815                debug!(
816                    "fusefilesystem call readlink() successfully read the target path of ino={}",
817                    ino,
818                );
819                reply.data(target_path).await
820            }
821            Err(e) => {
822                debug!(
823                    "fusefilesystem call readlink() failed to read the target path of ino={}, the error is: {:?}",
824                    ino, e,
825                );
826                reply.error(e).await
827            }
828        }
829    }
830
831    /// Create a symbolic link.
832    async fn symlink(
833        &self,
834        req: &Request<'_>,
835        parent: INum,
836        name: &str,
837        target_path: &Path,
838        reply: ReplyEntry<'_>,
839    ) -> nix::Result<usize> {
840        let ino = req.nodeid();
841        debug!(
842            "symlink(ino={} parent={}, name={:?}, target_path={:?}, req={:?})",
843            ino, parent, name, target_path, req,
844        );
845
846        let symlink_res = self
847            .virtual_fs
848            .symlink(req.uid(), req.gid(), parent, name, target_path)
849            .await;
850
851        match symlink_res {
852            Ok((ttl, file_attr, generation)) => {
853                debug!(
854                    "fusefilesystem call symlink() successfully created a symlink name={:?} to target path={:?} under parent ino={}",
855                    name, target_path, parent,
856                );
857                let fuse_attr = fs_util::convert_to_fuse_attr(file_attr);
858                reply.entry(ttl, fuse_attr, generation).await
859            }
860            Err(e) => {
861                debug!(
862                    "fusefilesystem call symlink() failed to create a symlink name={:?} to target path={:?} under parent ino={}, \
863                        the error is: {:?}",
864                    name, target_path, parent, e,
865                );
866                reply.error(e).await
867            }
868        }
869    }
870
871    // Un-implemented FUSE operations
872
873    /// Interrupt another FUSE request
874    async fn interrupt(&self, req: &Request<'_>, unique: u64) {
875        debug!("interrupt(req={:?}, unique={})", req, unique);
876
877        let interrupt_res = self.virtual_fs.interrupt(unique).await;
878
879        debug!(
880            "fusefilesystem call interrupt() with unique={} {:?}",
881            unique, interrupt_res,
882        );
883    }
884
885    /// Create a hard link.
886    async fn link(
887        &self,
888        req: &Request<'_>,
889        newparent: u64,
890        newname: &str,
891        reply: ReplyEntry<'_>,
892    ) -> nix::Result<usize> {
893        debug!(
894            "link(newparent={}, newname={:?}, req={:?})",
895            newparent, newname, req,
896        );
897
898        let link_res = self.virtual_fs.link(newparent, newname).await;
899
900        match link_res {
901            Ok(()) => {
902                debug!(
903                    "fusefilesystem call link() successfully created a hard link name={:?} under parent ino={}",
904                    newname, newparent,
905                );
906                // TODO: change the entry to the real one
907                // reply.entry(0, Default::default(), 0).await
908                reply.error_code(Errno::ENOSYS).await
909            }
910            Err(AsyncFusexError::Unimplemented { context }) => {
911                debug!(
912                    "fusefilesystem call link() failed to create a hard link name={:?} under parent ino={}, the error is: {:?}",
913                    newname, newparent, context,
914                );
915                reply.error_code(Errno::ENOSYS).await
916            }
917            Err(e) => {
918                debug!(
919                    "fusefilesystem call link() failed to create a hard link name={:?} under parent ino={}, the error is: {:?}",
920                    newname, newparent, e,
921                );
922                reply.error(e).await
923            }
924        }
925    }
926
927    /// Set an extended attribute.
928    async fn setxattr(
929        &self,
930        req: &Request<'_>,
931        name: &str,
932        value: &[u8],
933        flags: u32,
934        position: u32,
935        reply: ReplyEmpty<'_>,
936    ) -> nix::Result<usize> {
937        let ino = req.nodeid();
938        debug!(
939            "setxattr(ino={}, name={:?}, value={:?}, flags={}, position={}, req={:?})",
940            ino, name, value, flags, position, req,
941        );
942
943        let setxattr_res = self
944            .virtual_fs
945            .setxattr(ino, name, value, flags, position)
946            .await;
947
948        match setxattr_res {
949            Ok(()) => {
950                debug!(
951                    "fusefilesystem call setxattr() successfully set the extended attribute name={:?}",
952                    name,
953                );
954                reply.ok().await
955            }
956            Err(AsyncFusexError::Unimplemented { context }) => {
957                debug!(
958                    "fusefilesystem call setxattr() failed to set the extended attribute name={:?}, the error is: {:?}",
959                    name, context,
960                );
961                reply.error_code(Errno::ENOSYS).await
962            }
963            Err(e) => {
964                debug!(
965                    "fusefilesystem call setxattr() failed to set the extended attribute name={:?}, the error is: {:?}",
966                    name, e,
967                );
968                reply.error(e).await
969            }
970        }
971    }
972
973    /// Get an extended attribute.
974    /// If `size` is 0, the size of the value should be sent with
975    /// `reply.size()`. If `size` is not 0, and the value fits, send it with
976    /// `reply.data()`, or `reply.error(ERANGE)` if it doesn't.
977    async fn getxattr(
978        &self,
979        req: &Request<'_>,
980        name: &str,
981        size: u32,
982        reply: ReplyXAttr<'_>,
983    ) -> nix::Result<usize> {
984        let ino = req.nodeid();
985        debug!(
986            "getxattr(ino={}, name={:?}, size={}, req={:?})",
987            ino, name, size, req,
988        );
989
990        let getxattr_res = self.virtual_fs.getxattr(ino, name, size).await;
991
992        match getxattr_res {
993            Ok(()) => {
994                debug!(
995                    "fusefilesystem call getxattr() successfully get the extended attribute name={:?}",
996                    name,
997                );
998                // TODO: change the entry to the real one
999                // reply.data(&value).await
1000                reply.error_code(Errno::ENOSYS).await
1001            }
1002            Err(AsyncFusexError::Unimplemented { context }) => {
1003                debug!(
1004                    "fusefilesystem call getxattr() failed to get the extended attribute name={:?}, the error is: {:?}",
1005                    name, context,
1006                );
1007                reply.error_code(Errno::ENOSYS).await
1008            }
1009            Err(e) => {
1010                debug!(
1011                    "fusefilesystem call getxattr() failed to get the extended attribute name={:?}, the error is: {:?}",
1012                    name, e,
1013                );
1014                reply.error(e).await
1015            }
1016        }
1017    }
1018
1019    /// List extended attribute names.
1020    /// If `size` is 0, the size of the value should be sent with
1021    /// `reply.size()`. If `size` is not 0, and the value fits, send it with
1022    /// `reply.data()`, or `reply.error(ERANGE)` if it doesn't.
1023    async fn listxattr(
1024        &self,
1025        req: &Request<'_>,
1026        size: u32,
1027        reply: ReplyXAttr<'_>,
1028    ) -> nix::Result<usize> {
1029        let ino = req.nodeid();
1030        debug!("listxattr(ino={}, size={}, req={:?})", ino, size, req,);
1031
1032        let listxattr_res = self.virtual_fs.listxattr(ino, size).await;
1033
1034        match listxattr_res {
1035            Ok(()) => {
1036                debug!(
1037                    "fusefilesystem call listxattr() successfully list the extended attribute names",
1038                );
1039                // TODO: change the entry to the real one
1040                // reply.data(&value).await
1041                reply.error_code(Errno::ENOSYS).await
1042            }
1043            Err(AsyncFusexError::Unimplemented { context }) => {
1044                debug!(
1045                    "fusefilesystem call listxattr() failed to list the extended attribute names, the error is: {:?}",
1046                    context,
1047                );
1048                reply.error_code(Errno::ENOSYS).await
1049            }
1050            Err(e) => {
1051                debug!(
1052                    "fusefilesystem call listxattr() failed to list the extended attribute names, the error is: {:?}",
1053                    e,
1054                );
1055                reply.error(e).await
1056            }
1057        }
1058    }
1059
1060    /// Remove an extended attribute.
1061    async fn removexattr(
1062        &self,
1063        req: &Request<'_>,
1064        name: &str,
1065        reply: ReplyEmpty<'_>,
1066    ) -> nix::Result<usize> {
1067        let ino = req.nodeid();
1068        debug!("removexattr(ino={}, name={:?}, req={:?})", ino, name, req,);
1069
1070        let removexattr_res = self.virtual_fs.removexattr(ino, name).await;
1071
1072        match removexattr_res {
1073            Ok(()) => {
1074                debug!(
1075                    "fusefilesystem call removexattr() successfully removed the extended attribute name={:?}",
1076                    name,
1077                );
1078                reply.ok().await
1079            }
1080            Err(AsyncFusexError::Unimplemented { context }) => {
1081                debug!(
1082                    "fusefilesystem call removexattr() failed to remove the extended attribute name={:?}, the error is: {:?}",
1083                    name, context,
1084                );
1085                reply.error_code(Errno::ENOSYS).await
1086            }
1087            Err(e) => {
1088                debug!(
1089                    "fusefilesystem call removexattr() failed to remove the extended attribute name={:?}, the error is: {:?}",
1090                    name, e,
1091                );
1092                reply.error(e).await
1093            }
1094        }
1095    }
1096
1097    /// Check file access permissions.
1098    /// This will be called for the `access()` system call. If the
1099    /// `default_permissions` mount option is given, self method is not
1100    /// called. This method is not called under Linux kernel versions 2.4.x
1101    async fn access(
1102        &self,
1103        req: &Request<'_>,
1104        mask: u32,
1105        reply: ReplyEmpty<'_>,
1106    ) -> nix::Result<usize> {
1107        let ino = req.nodeid();
1108        debug!("access(ino={}, mask={}, req={:?})", ino, mask, req,);
1109
1110        let access_res = self
1111            .virtual_fs
1112            .access(req.uid(), req.gid(), ino, mask)
1113            .await;
1114
1115        match access_res {
1116            Ok(()) => {
1117                debug!(
1118                    "fusefilesystem call access() successfully checked the access permission with mask={}",
1119                    mask,
1120                );
1121                reply.ok().await
1122            }
1123            Err(AsyncFusexError::Unimplemented { context }) => {
1124                debug!(
1125                    "fusefilesystem call access() failed to check the access permission with mask={}, the error is: {:?}",
1126                    mask, context,
1127                );
1128                reply.error_code(Errno::ENOSYS).await
1129            }
1130            Err(e) => {
1131                debug!(
1132                    "fusefilesystem call access() failed to check the access permission with mask={}, the error is: {:?}",
1133                    mask, e,
1134                );
1135                reply.error(e).await
1136            }
1137        }
1138    }
1139
1140    /// Create and open a file.
1141    /// If the file does not exist, first create it with the specified mode, and
1142    /// then open it. Open flags (with the exception of `O_NOCTTY`) are
1143    /// available in flags. Filesystem may store an arbitrary file handle
1144    /// (pointer, index, etc) in fh, and use self in other all other file
1145    /// operations (read, write, flush, release, fsync). There are also some
1146    /// flags (`direct_io`, `keep_cache`) which the filesystem may set, to
1147    /// change the way the file is opened. See `fuse_file_info` structure in
1148    /// `fuse_common.h` for more details. If self method is not implemented
1149    /// or under Linux kernel versions earlier than 2.6.15, the mknod()
1150    /// and open() methods will be called instead.
1151    async fn create(
1152        &self,
1153        req: &Request<'_>,
1154        parent: u64,
1155        name: &str,
1156        mode: u32,
1157        flags: u32,
1158        reply: ReplyCreate<'_>,
1159    ) -> nix::Result<usize> {
1160        let ino = req.nodeid();
1161        debug!(
1162            "create(ino={}, parent={}, name={:?}, mode={}, flags={}, req={:?})",
1163            ino, parent, name, mode, flags, req,
1164        );
1165
1166        let create_res = self
1167            .virtual_fs
1168            .create(req.uid(), req.gid(), ino, parent, name, mode, flags)
1169            .await;
1170
1171        match create_res {
1172            Ok(()) => {
1173                debug!(
1174                    "fusefilesystem call create() successfully created a file name={:?} under parent ino={}",
1175                    name, parent,
1176                );
1177                // TODO: change the entry to the real one
1178                // reply.created(ttl, fuse_attr, generation, fh, flags).await
1179                reply.error_code(Errno::ENOSYS).await
1180            }
1181            Err(AsyncFusexError::Unimplemented { context }) => {
1182                debug!(
1183                    "fusefilesystem call create() failed to create a file name={:?} under parent ino={}, the error is: {:?}",
1184                    name, parent, context,
1185                );
1186                reply.error_code(Errno::ENOSYS).await
1187            }
1188            Err(e) => {
1189                debug!(
1190                    "fusefilesystem call create() failed to create a file name={:?} under parent ino={}, the error is: {:?}",
1191                    name, parent, e,
1192                );
1193                reply.error(e).await
1194            }
1195        }
1196    }
1197
1198    /// Test for a POSIX file lock.
1199    async fn getlk(
1200        &self,
1201        req: &Request<'_>,
1202        lk_param: FileLockParam,
1203        reply: ReplyLock<'_>,
1204    ) -> nix::Result<usize> {
1205        let ino = req.nodeid();
1206        debug!("getlk(ino={}, lk_param={:?}, req={:?})", ino, lk_param, req,);
1207
1208        let getlk_res = self
1209            .virtual_fs
1210            .getlk(req.uid(), req.gid(), ino, lk_param)
1211            .await;
1212
1213        match getlk_res {
1214            Ok(()) => {
1215                debug!(
1216                    "fusefilesystem call getlk() successfully get the file lock of ino={}",
1217                    ino,
1218                );
1219                // TODO: reply the lock
1220                // reply.lock(file_lock).await
1221                reply.error_code(Errno::ENOSYS).await
1222            }
1223            Err(AsyncFusexError::Unimplemented { context }) => {
1224                debug!(
1225                    "fusefilesystem call getlk() failed to get the file lock of ino={}, the error is: {:?}",
1226                    ino, context,
1227                );
1228                reply.error_code(Errno::ENOSYS).await
1229            }
1230            Err(e) => {
1231                debug!(
1232                    "fusefilesystem call getlk() failed to get the file lock of ino={}, the error is: {:?}",
1233                    ino, e,
1234                );
1235                reply.error(e).await
1236            }
1237        }
1238    }
1239
1240    /// Acquire, modify or release a POSIX file lock.
1241    /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner,
1242    /// but otherwise self is not always the case.  For checking lock
1243    /// ownership, `fi->owner` must be used. The `l_pid` field in `struct
1244    /// flock` should only be used to fill in self field in `getlk()`. Note:
1245    /// if the locking methods are not implemented, the kernel will still
1246    /// allow file locking to work locally. Hence these are only interesting
1247    /// for network filesystems and similar.
1248    async fn setlk(
1249        &self,
1250        req: &Request<'_>,
1251        lk_param: FileLockParam,
1252        sleep: bool,
1253        reply: ReplyEmpty<'_>,
1254    ) -> nix::Result<usize> {
1255        let ino = req.nodeid();
1256        debug!(
1257            "setlk(ino={}, lk_param={:?}, sleep={}, req={:?})",
1258            ino, lk_param, sleep, req,
1259        );
1260
1261        let setlk_res = self
1262            .virtual_fs
1263            .setlk(req.uid(), req.gid(), ino, lk_param, sleep)
1264            .await;
1265
1266        match setlk_res {
1267            Ok(()) => {
1268                debug!(
1269                    "fusefilesystem call setlk() successfully set the file lock of ino={}",
1270                    ino,
1271                );
1272                reply.ok().await
1273            }
1274            Err(AsyncFusexError::Unimplemented { context }) => {
1275                debug!(
1276                    "fusefilesystem call setlk() failed to set the file lock of ino={}, the error is: {:?}",
1277                    ino, context,
1278                );
1279                reply.error_code(Errno::ENOSYS).await
1280            }
1281            Err(e) => {
1282                debug!(
1283                    "fusefilesystem call setlk() failed to set the file lock of ino={}, the error is: {:?}",
1284                    ino, e,
1285                );
1286                reply.error(e).await
1287            }
1288        }
1289    }
1290
1291    /// Map block index within file to block index within device.
1292    /// Note: This makes sense only for block device backed filesystems mounted
1293    /// with the `blkdev` option
1294    async fn bmap(
1295        &self,
1296        req: &Request<'_>,
1297        blocksize: u32,
1298        idx: u64,
1299        reply: ReplyBMap<'_>,
1300    ) -> nix::Result<usize> {
1301        let ino = req.nodeid();
1302        debug!(
1303            "bmap(ino={}, blocksize={}, idx={}, req={:?})",
1304            ino, blocksize, idx, req,
1305        );
1306
1307        let bmap_res = self
1308            .virtual_fs
1309            .bmap(req.uid(), req.gid(), ino, blocksize, idx)
1310            .await;
1311
1312        match bmap_res {
1313            Ok(()) => {
1314                debug!(
1315                    "fusefilesystem call bmap() successfully mapped the block index of ino={} to block index of device",
1316                    ino,
1317                );
1318                // TODO: reply the block
1319                // reply.bmap(block).await
1320                reply.error_code(Errno::ENOSYS).await
1321            }
1322            Err(AsyncFusexError::Unimplemented { context }) => {
1323                debug!(
1324                    "fusefilesystem call bmap() failed to map the block index of ino={} to block index of device, the error is: {:?}",
1325                    ino, context,
1326                );
1327                reply.error_code(Errno::ENOSYS).await
1328            }
1329            Err(e) => {
1330                debug!(
1331                    "fusefilesystem call bmap() failed to map the block index of ino={} to block index of device, the error is: {:?}",
1332                    ino, e,
1333                );
1334                reply.error(e).await
1335            }
1336        }
1337    }
1338}