Skip to main content

async_fuser/
lib.rs

1//! FUSE userspace library implementation
2//!
3//! This is an improved rewrite of the FUSE userspace library (lowlevel interface) to fully take
4//! advantage of Rust's architecture. The only thing we rely on in the real libfuse are mount
5//! and unmount calls which are needed to establish a fd to talk to the kernel driver.
6
7#![warn(
8    missing_docs,
9    missing_debug_implementations,
10    rust_2018_idioms,
11    unreachable_pub
12)]
13
14use std::cmp::max;
15use std::cmp::min;
16use std::convert::AsRef;
17use std::ffi::OsStr;
18use std::io;
19use std::os::unix::fs::FileTypeExt;
20use std::path::Path;
21use std::time::Duration;
22use std::time::SystemTime;
23
24use log::warn;
25#[cfg(target_os = "macos")]
26pub use reply::ReplyXTimes;
27#[cfg(feature = "serializable")]
28use serde::Deserialize;
29#[cfg(feature = "serializable")]
30use serde::Serialize;
31
32pub use crate::access_flags::AccessFlags;
33pub use crate::bsd_file_flags::BsdFileFlags;
34use crate::forget_one::ForgetOne;
35#[cfg(feature = "async")]
36pub use crate::lib_async::{AsyncFilesystem, mount_async};
37pub use crate::ll::Errno;
38pub use crate::ll::Generation;
39pub use crate::ll::RequestId;
40pub use crate::ll::TimeOrNow;
41pub use crate::ll::flags::copy_file_range_flags::CopyFileRangeFlags;
42pub use crate::ll::flags::fopen_flags::FopenFlags;
43pub use crate::ll::flags::init_flags::InitFlags;
44pub use crate::ll::flags::ioctl_flags::IoctlFlags;
45pub use crate::ll::flags::poll_flags::PollFlags;
46pub use crate::ll::flags::write_flags::WriteFlags;
47pub use crate::ll::fuse_abi::consts;
48#[cfg(feature = "async")]
49pub use crate::ll::reply_async;
50pub use crate::ll::request::FileHandle;
51pub use crate::ll::request::INodeNo;
52pub use crate::ll::request::LockOwner;
53pub use crate::ll::request::Version;
54pub use crate::mnt::mount_options::Config;
55pub use crate::mnt::mount_options::MountOption;
56pub use crate::notify::Notifier;
57pub use crate::notify::PollHandle;
58pub use crate::notify::PollNotifier;
59pub use crate::open_flags::OpenAccMode;
60pub use crate::open_flags::OpenFlags;
61pub use crate::passthrough::BackingId;
62pub use crate::poll_events::PollEvents;
63pub use crate::rename_flags::RenameFlags;
64pub use crate::reply::ReplyAttr;
65pub use crate::reply::ReplyBmap;
66pub use crate::reply::ReplyCreate;
67pub use crate::reply::ReplyData;
68pub use crate::reply::ReplyDirectory;
69pub use crate::reply::ReplyDirectoryPlus;
70pub use crate::reply::ReplyEmpty;
71pub use crate::reply::ReplyEntry;
72pub use crate::reply::ReplyIoctl;
73pub use crate::reply::ReplyLock;
74pub use crate::reply::ReplyLseek;
75pub use crate::reply::ReplyOpen;
76pub use crate::reply::ReplyPoll;
77pub use crate::reply::ReplyStatfs;
78pub use crate::reply::ReplyWrite;
79pub use crate::reply::ReplyXattr;
80pub use crate::request_param::Request;
81pub use crate::session::BackgroundSession;
82use crate::session::MAX_WRITE_SIZE;
83pub use crate::session::Session;
84pub use crate::session::SessionACL;
85pub use crate::session::SessionUnmounter;
86#[cfg(feature = "async")]
87pub use crate::session_async::{AsyncSession, AsyncSessionBuilder};
88
89mod access_flags;
90mod bsd_file_flags;
91mod channel;
92#[cfg(feature = "async")]
93mod channel_async;
94mod dev_fuse;
95#[cfg(feature = "async")]
96mod dev_fuse_async;
97#[cfg(feature = "experimental")]
98pub mod experimental;
99mod forget_one;
100#[cfg(feature = "async")]
101pub mod lib_async;
102mod ll;
103mod mnt;
104mod notify;
105mod open_flags;
106mod passthrough;
107mod poll_events;
108mod read_buf;
109mod rename_flags;
110mod reply;
111mod request;
112mod request_param;
113mod session;
114#[cfg(feature = "async")]
115mod session_async;
116mod time;
117
118/// We generally support async reads
119#[cfg(not(target_os = "macos"))]
120const INIT_FLAGS: InitFlags = InitFlags::FUSE_ASYNC_READ.union(InitFlags::FUSE_BIG_WRITES);
121// TODO: Add FUSE_EXPORT_SUPPORT
122
123/// On macOS, we additionally support case insensitiveness, volume renames and xtimes
124/// TODO: we should eventually let the filesystem implementation decide which flags to set
125#[cfg(target_os = "macos")]
126const INIT_FLAGS: InitFlags = InitFlags::FUSE_ASYNC_READ
127    .union(InitFlags::FUSE_CASE_INSENSITIVE)
128    .union(InitFlags::FUSE_VOL_RENAME)
129    .union(InitFlags::FUSE_XTIMES);
130// TODO: Add FUSE_EXPORT_SUPPORT and FUSE_BIG_WRITES (requires ABI 7.10)
131
132fn default_init_flags(capabilities: InitFlags) -> InitFlags {
133    let mut flags = INIT_FLAGS;
134    if capabilities.contains(InitFlags::FUSE_MAX_PAGES) {
135        flags |= InitFlags::FUSE_MAX_PAGES;
136    }
137    flags
138}
139
140/// File types
141#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
142#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
143pub enum FileType {
144    /// Named pipe (`S_IFIFO`)
145    NamedPipe,
146    /// Character device (`S_IFCHR`)
147    CharDevice,
148    /// Block device (`S_IFBLK`)
149    BlockDevice,
150    /// Directory (`S_IFDIR`)
151    Directory,
152    /// Regular file (`S_IFREG`)
153    RegularFile,
154    /// Symbolic link (`S_IFLNK`)
155    Symlink,
156    /// Unix domain socket (`S_IFSOCK`)
157    Socket,
158}
159
160impl FileType {
161    /// Convert std `FileType` to fuser `FileType`.
162    pub fn from_std(file_type: std::fs::FileType) -> Option<Self> {
163        if file_type.is_file() {
164            Some(FileType::RegularFile)
165        } else if file_type.is_dir() {
166            Some(FileType::Directory)
167        } else if file_type.is_symlink() {
168            Some(FileType::Symlink)
169        } else if file_type.is_fifo() {
170            Some(FileType::NamedPipe)
171        } else if file_type.is_socket() {
172            Some(FileType::Socket)
173        } else if file_type.is_char_device() {
174            Some(FileType::CharDevice)
175        } else if file_type.is_block_device() {
176            Some(FileType::BlockDevice)
177        } else {
178            None
179        }
180    }
181}
182
183/// File attributes
184#[derive(Clone, Copy, Debug, Eq, PartialEq)]
185#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
186pub struct FileAttr {
187    /// Inode number
188    pub ino: INodeNo,
189    /// Size in bytes
190    pub size: u64,
191    /// Allocated size in 512-byte blocks. May be smaller than the actual file size
192    /// if the file is compressed, for example.
193    pub blocks: u64,
194    /// Time of last access
195    pub atime: SystemTime,
196    /// Time of last modification
197    pub mtime: SystemTime,
198    /// Time of last change
199    pub ctime: SystemTime,
200    /// Time of creation (macOS only)
201    pub crtime: SystemTime,
202    /// Kind of file (directory, file, pipe, etc)
203    pub kind: FileType,
204    /// Permissions
205    pub perm: u16,
206    /// Number of hard links
207    pub nlink: u32,
208    /// User id
209    pub uid: u32,
210    /// Group id
211    pub gid: u32,
212    /// Rdev
213    pub rdev: u32,
214    /// Block size to be reported by `stat()`. If unsure, set to 4096.
215    pub blksize: u32,
216    /// Flags (macOS only, see chflags(2))
217    pub flags: u32,
218}
219
220/// Configuration of the fuse kernel module connection
221#[derive(Debug)]
222pub struct KernelConfig {
223    capabilities: InitFlags,
224    requested: InitFlags,
225    max_readahead: u32,
226    max_max_readahead: u32,
227    max_background: u16,
228    congestion_threshold: Option<u16>,
229    max_write: u32,
230    time_gran: Duration,
231    max_stack_depth: u32,
232    kernel_abi: Version,
233}
234
235impl KernelConfig {
236    fn new(capabilities: InitFlags, max_readahead: u32, kernel_abi: Version) -> Self {
237        Self {
238            capabilities,
239            requested: default_init_flags(capabilities),
240            max_readahead,
241            max_max_readahead: max_readahead,
242            max_background: 16,
243            congestion_threshold: None,
244            // use a max write size that fits into the session's buffer
245            max_write: MAX_WRITE_SIZE as u32,
246            // 1ns means nano-second granularity.
247            time_gran: Duration::new(0, 1),
248            max_stack_depth: 0,
249            kernel_abi,
250        }
251    }
252
253    /// Set the maximum stacking depth of the filesystem
254    ///
255    /// This has to be at least 1 to support passthrough to backing files.  Setting this to 0 (the
256    /// default) effectively disables support for passthrough.
257    ///
258    /// With `max_stack_depth` > 1, the backing files can be on a stacked fs (e.g. overlayfs)
259    /// themselves and with `max_stack_depth` == 1, this FUSE filesystem can be stacked as the
260    /// underlying fs of a stacked fs (e.g. overlayfs).
261    ///
262    /// The kernel currently has a hard maximum value of 2.  Anything higher won't work.
263    ///
264    /// On success, returns the previous value.  
265    /// # Errors
266    /// If argument is too large, returns the nearest value which will succeed.
267    pub fn set_max_stack_depth(&mut self, value: u32) -> Result<u32, u32> {
268        // https://lore.kernel.org/linux-fsdevel/CAOYeF9V_n93OEF_uf0Gwtd=+da0ReX8N2aaT6RfEJ9DPvs8O2w@mail.gmail.com/
269        const FILESYSTEM_MAX_STACK_DEPTH: u32 = 2;
270
271        if value > FILESYSTEM_MAX_STACK_DEPTH {
272            return Err(FILESYSTEM_MAX_STACK_DEPTH);
273        }
274
275        let previous = self.max_stack_depth;
276        self.max_stack_depth = value;
277        Ok(previous)
278    }
279
280    /// Set the timestamp granularity
281    ///
282    /// Must be a power of 10 nanoseconds. i.e. 1s, 0.1s, 0.01s, 1ms, 0.1ms...etc
283    ///
284    /// On success returns the previous value.  
285    /// # Errors
286    /// If the argument does not match any valid granularity, returns the nearest value which will succeed.
287    pub fn set_time_granularity(&mut self, value: Duration) -> Result<Duration, Duration> {
288        if value.as_nanos() == 0 {
289            return Err(Duration::new(0, 1));
290        }
291        if value.as_secs() > 1 || (value.as_secs() == 1 && value.subsec_nanos() > 0) {
292            return Err(Duration::new(1, 0));
293        }
294        let mut power_of_10 = 1;
295        while power_of_10 < value.as_nanos() {
296            if value.as_nanos() < power_of_10 * 10 {
297                // value must not be a power of ten, since power_of_10 < value < power_of_10 * 10
298                return Err(Duration::new(0, power_of_10 as u32));
299            }
300            power_of_10 *= 10;
301        }
302        let previous = self.time_gran;
303        self.time_gran = value;
304        Ok(previous)
305    }
306
307    /// Set the maximum write size for a single request
308    ///
309    /// On success returns the previous value.
310    /// # Errors
311    /// If the argument is too large, returns the nearest value which will succeed.
312    pub fn set_max_write(&mut self, value: u32) -> Result<u32, u32> {
313        if value == 0 {
314            return Err(1);
315        }
316        if value > MAX_WRITE_SIZE as u32 {
317            return Err(MAX_WRITE_SIZE as u32);
318        }
319        let previous = self.max_write;
320        self.max_write = value;
321        Ok(previous)
322    }
323
324    /// Set the maximum readahead size
325    ///
326    /// On success returns the previous value.
327    /// # Errors
328    /// If the argument is too large, returns the nearest value which will succeed.
329    pub fn set_max_readahead(&mut self, value: u32) -> Result<u32, u32> {
330        if value == 0 {
331            return Err(1);
332        }
333        if value > self.max_max_readahead {
334            return Err(self.max_max_readahead);
335        }
336        let previous = self.max_readahead;
337        self.max_readahead = value;
338        Ok(previous)
339    }
340
341    /// Query kernel capabilities.
342    pub fn capabilities(&self) -> InitFlags {
343        self.capabilities & !InitFlags::FUSE_INIT_EXT
344    }
345
346    /// Kernel ABI version.
347    pub fn kernel_abi(&self) -> Version {
348        self.kernel_abi
349    }
350
351    /// Add a set of capabilities.
352    ///
353    /// # Errors
354    /// When the argument includes capabilities not supported by the kernel, returns the bits of the capabilities not supported.
355    pub fn add_capabilities(&mut self, capabilities_to_add: InitFlags) -> Result<(), InitFlags> {
356        if !self.capabilities.contains(capabilities_to_add) {
357            let unsupported = capabilities_to_add & !self.capabilities;
358            return Err(unsupported);
359        }
360        self.requested |= capabilities_to_add;
361        Ok(())
362    }
363
364    /// Set the maximum number of pending background requests. Such as readahead requests.
365    ///
366    /// On success returns the previous value.
367    /// # Errors
368    /// If the argument is too small, returns the nearest value which will succeed.
369    pub fn set_max_background(&mut self, value: u16) -> Result<u16, u16> {
370        if value == 0 {
371            return Err(1);
372        }
373        let previous = self.max_background;
374        self.max_background = value;
375        Ok(previous)
376    }
377
378    /// Set the threshold of background requests at which the kernel will consider the filesystem
379    /// request queue congested. (it may then switch to sleeping instead of spin-waiting, for example)
380    ///
381    /// On success returns the previous value.
382    /// # Errors
383    /// If the argument is too small, returns the nearest value which will succeed.
384    pub fn set_congestion_threshold(&mut self, value: u16) -> Result<u16, u16> {
385        if value == 0 {
386            return Err(1);
387        }
388        let previous = self.congestion_threshold();
389        self.congestion_threshold = Some(value);
390        Ok(previous)
391    }
392
393    fn congestion_threshold(&self) -> u16 {
394        match self.congestion_threshold {
395            // Default to a threshold of 3/4 of the max background threads
396            None => (u32::from(self.max_background) * 3 / 4) as u16,
397            Some(value) => min(value, self.max_background),
398        }
399    }
400
401    fn max_pages(&self) -> u16 {
402        ((max(self.max_write, self.max_readahead) - 1) / page_size::get() as u32) as u16 + 1
403    }
404}
405
406/// Filesystem trait.
407///
408/// This trait must be implemented to provide a userspace filesystem via FUSE.
409/// These methods correspond to `fuse_lowlevel_ops` in libfuse. Reasonable default
410/// implementations are provided here to get a mountable filesystem that does
411/// nothing.
412#[allow(clippy::too_many_arguments)]
413pub trait Filesystem: Send + Sync + 'static {
414    /// Initialize filesystem.
415    /// Called before any other filesystem method.
416    /// The kernel module connection can be configured using the `KernelConfig` object
417    fn init(&mut self, _req: &Request, _config: &mut KernelConfig) -> io::Result<()> {
418        Ok(())
419    }
420
421    /// Clean up filesystem.
422    /// Called on filesystem exit.
423    fn destroy(&mut self) {}
424
425    /// Look up a directory entry by name and get its attributes.
426    fn lookup(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEntry) {
427        warn!("[Not Implemented] lookup(parent: {parent:#x?}, name {name:?})");
428        reply.error(Errno::ENOSYS);
429    }
430
431    /// Forget about an inode.
432    /// The nlookup parameter indicates the number of lookups previously performed on
433    /// this inode. If the filesystem implements inode lifetimes, it is recommended that
434    /// inodes acquire a single reference on each lookup, and lose nlookup references on
435    /// each forget. The filesystem may ignore forget calls, if the inodes don't need to
436    /// have a limited lifetime. On unmount it is not guaranteed, that all referenced
437    /// inodes will receive a forget message.
438    fn forget(&self, _req: &Request, _ino: INodeNo, _nlookup: u64) {}
439
440    /// Like [`forget`](Self::forget), but take multiple forget requests at once for performance. The default
441    /// implementation will fallback to `forget`.
442    fn batch_forget(&self, req: &Request, nodes: &[ForgetOne]) {
443        for node in nodes {
444            self.forget(req, node.nodeid(), node.nlookup());
445        }
446    }
447
448    /// Get file attributes.
449    fn getattr(&self, _req: &Request, ino: INodeNo, fh: Option<FileHandle>, reply: ReplyAttr) {
450        warn!("[Not Implemented] getattr(ino: {ino:#x?}, fh: {fh:#x?})");
451        reply.error(Errno::ENOSYS);
452    }
453
454    /// Set file attributes.
455    fn setattr(
456        &self,
457        _req: &Request,
458        ino: INodeNo,
459        mode: Option<u32>,
460        uid: Option<u32>,
461        gid: Option<u32>,
462        size: Option<u64>,
463        _atime: Option<TimeOrNow>,
464        _mtime: Option<TimeOrNow>,
465        _ctime: Option<SystemTime>,
466        fh: Option<FileHandle>,
467        _crtime: Option<SystemTime>,
468        _chgtime: Option<SystemTime>,
469        _bkuptime: Option<SystemTime>,
470        flags: Option<BsdFileFlags>,
471        reply: ReplyAttr,
472    ) {
473        warn!(
474            "[Not Implemented] setattr(ino: {ino:#x?}, mode: {mode:?}, uid: {uid:?}, \
475            gid: {gid:?}, size: {size:?}, fh: {fh:?}, flags: {flags:?})"
476        );
477        reply.error(Errno::ENOSYS);
478    }
479
480    /// Read symbolic link.
481    fn readlink(&self, _req: &Request, ino: INodeNo, reply: ReplyData) {
482        warn!("[Not Implemented] readlink(ino: {ino:#x?})");
483        reply.error(Errno::ENOSYS);
484    }
485
486    /// Create file node.
487    /// Create a regular file, character device, block device, fifo or socket node.
488    fn mknod(
489        &self,
490        _req: &Request,
491        parent: INodeNo,
492        name: &OsStr,
493        mode: u32,
494        umask: u32,
495        rdev: u32,
496        reply: ReplyEntry,
497    ) {
498        warn!(
499            "[Not Implemented] mknod(parent: {parent:#x?}, name: {name:?}, \
500            mode: {mode}, umask: {umask:#x?}, rdev: {rdev})"
501        );
502        reply.error(Errno::ENOSYS);
503    }
504
505    /// Create a directory.
506    fn mkdir(
507        &self,
508        _req: &Request,
509        parent: INodeNo,
510        name: &OsStr,
511        mode: u32,
512        umask: u32,
513        reply: ReplyEntry,
514    ) {
515        warn!(
516            "[Not Implemented] mkdir(parent: {parent:#x?}, name: {name:?}, mode: {mode}, umask: {umask:#x?})"
517        );
518        reply.error(Errno::ENOSYS);
519    }
520
521    /// Remove a file.
522    fn unlink(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) {
523        warn!("[Not Implemented] unlink(parent: {parent:#x?}, name: {name:?})",);
524        reply.error(Errno::ENOSYS);
525    }
526
527    /// Remove a directory.
528    fn rmdir(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) {
529        warn!("[Not Implemented] rmdir(parent: {parent:#x?}, name: {name:?})",);
530        reply.error(Errno::ENOSYS);
531    }
532
533    /// Create a symbolic link.
534    fn symlink(
535        &self,
536        _req: &Request,
537        parent: INodeNo,
538        link_name: &OsStr,
539        target: &Path,
540        reply: ReplyEntry,
541    ) {
542        warn!(
543            "[Not Implemented] symlink(parent: {parent:#x?}, link_name: {link_name:?}, target: {target:?})",
544        );
545        reply.error(Errno::EPERM);
546    }
547
548    /// Rename a file.
549    fn rename(
550        &self,
551        _req: &Request,
552        parent: INodeNo,
553        name: &OsStr,
554        newparent: INodeNo,
555        newname: &OsStr,
556        flags: RenameFlags,
557        reply: ReplyEmpty,
558    ) {
559        warn!(
560            "[Not Implemented] rename(parent: {parent:#x?}, name: {name:?}, \
561            newparent: {newparent:#x?}, newname: {newname:?}, flags: {flags})",
562        );
563        reply.error(Errno::ENOSYS);
564    }
565
566    /// Create a hard link.
567    fn link(
568        &self,
569        _req: &Request,
570        ino: INodeNo,
571        newparent: INodeNo,
572        newname: &OsStr,
573        reply: ReplyEntry,
574    ) {
575        warn!(
576            "[Not Implemented] link(ino: {ino:#x?}, newparent: {newparent:#x?}, newname: {newname:?})"
577        );
578        reply.error(Errno::EPERM);
579    }
580
581    /// Open a file.
582    /// Open flags (with the exception of `O_CREAT`, `O_EXCL`, `O_NOCTTY` and `O_TRUNC`) are
583    /// available in flags. Filesystem may store an arbitrary file handle (pointer, index,
584    /// etc) in fh, and use this in other all other file operations (read, write, flush,
585    /// release, fsync). Filesystem may also implement stateless file I/O and not store
586    /// anything in fh. There are also some flags (`direct_io`, `keep_cache`) which the
587    /// filesystem may set, to change the way the file is opened. See `fuse_file_info`
588    /// structure in <`fuse_common.h`> for more details.
589    fn open(&self, _req: &Request, _ino: INodeNo, _flags: OpenFlags, reply: ReplyOpen) {
590        reply.opened(FileHandle(0), FopenFlags::empty());
591    }
592
593    /// Read data.
594    /// Read should send exactly the number of bytes requested except on EOF or error,
595    /// otherwise the rest of the data will be substituted with zeroes. An exception to
596    /// this is when the file has been opened in `direct_io` mode, in which case the
597    /// return value of the read system call will reflect the return value of this
598    /// operation. fh will contain the value set by the open method, or will be undefined
599    /// if the open method didn't set any value.
600    ///
601    /// flags: these are the file flags, such as `O_SYNC`. Only supported with ABI >= 7.9
602    /// `lock_owner`: only supported with ABI >= 7.9
603    fn read(
604        &self,
605        _req: &Request,
606        ino: INodeNo,
607        fh: FileHandle,
608        offset: u64,
609        size: u32,
610        flags: OpenFlags,
611        lock_owner: Option<LockOwner>,
612        reply: ReplyData,
613    ) {
614        warn!(
615            "[Not Implemented] read(ino: {ino:#x?}, fh: {fh}, offset: {offset}, \
616            size: {size}, flags: {flags:#x?}, lock_owner: {lock_owner:?})"
617        );
618        reply.error(Errno::ENOSYS);
619    }
620
621    /// Write data.
622    /// Write should return exactly the number of bytes requested except on error. An
623    /// exception to this is when the file has been opened in `direct_io` mode, in
624    /// which case the return value of the write system call will reflect the return
625    /// value of this operation. fh will contain the value set by the open method, or
626    /// will be undefined if the open method didn't set any value.
627    ///
628    /// `write_flags`: will contain `FUSE_WRITE_CACHE`, if this write is from the page cache. If set,
629    /// the pid, uid, gid, and fh may not match the value that would have been sent if write cachin
630    /// is disabled
631    /// flags: these are the file flags, such as `O_SYNC`. Only supported with ABI >= 7.9
632    /// `lock_owner`: only supported with ABI >= 7.9
633    fn write(
634        &self,
635        _req: &Request,
636        ino: INodeNo,
637        fh: FileHandle,
638        offset: u64,
639        data: &[u8],
640        write_flags: WriteFlags,
641        flags: OpenFlags,
642        lock_owner: Option<LockOwner>,
643        reply: ReplyWrite,
644    ) {
645        warn!(
646            "[Not Implemented] write(ino: {ino:#x?}, fh: {fh}, offset: {offset}, \
647            data.len(): {}, write_flags: {write_flags:#x?}, flags: {flags:#x?}, \
648            lock_owner: {lock_owner:?})",
649            data.len()
650        );
651        reply.error(Errno::ENOSYS);
652    }
653
654    /// Flush method.
655    /// This is called on each `close()` of the opened file. Since file descriptors can
656    /// be duplicated (dup, dup2, fork), for one open call there may be many flush
657    /// calls. Filesystems shouldn't assume that flush will always be called after some
658    /// writes, or that if will be called at all. fh will contain the value set by the
659    /// open method, or will be undefined if the open method didn't set any value.
660    /// NOTE: the name of the method is misleading, since (unlike fsync) the filesystem
661    /// is not forced to flush pending writes. One reason to flush data, is if the
662    /// filesystem wants to return write errors. If the filesystem supports file locking
663    /// operations (`setlk`, `getlk`) it should remove all locks belonging to `lock_owner`.
664    fn flush(
665        &self,
666        _req: &Request,
667        ino: INodeNo,
668        fh: FileHandle,
669        lock_owner: LockOwner,
670        reply: ReplyEmpty,
671    ) {
672        warn!("[Not Implemented] flush(ino: {ino:#x?}, fh: {fh}, lock_owner: {lock_owner:?})");
673        reply.error(Errno::ENOSYS);
674    }
675
676    /// Release an open file.
677    /// Release is called when there are no more references to an open file: all file
678    /// descriptors are closed and all memory mappings are unmapped. For every open
679    /// call there will be exactly one release call. The filesystem may reply with an
680    /// error, but error values are not returned to `close()` or `munmap()` which triggered
681    /// the release. fh will contain the value set by the open method, or will be undefined
682    /// if the open method didn't set any value. flags will contain the same flags as for
683    /// open.
684    fn release(
685        &self,
686        _req: &Request,
687        _ino: INodeNo,
688        _fh: FileHandle,
689        _flags: OpenFlags,
690        _lock_owner: Option<LockOwner>,
691        _flush: bool,
692        reply: ReplyEmpty,
693    ) {
694        reply.ok();
695    }
696
697    /// Synchronize file contents.
698    /// If the datasync parameter is non-zero, then only the user data should be flushed,
699    /// not the meta data.
700    fn fsync(
701        &self,
702        _req: &Request,
703        ino: INodeNo,
704        fh: FileHandle,
705        datasync: bool,
706        reply: ReplyEmpty,
707    ) {
708        warn!("[Not Implemented] fsync(ino: {ino:#x?}, fh: {fh}, datasync: {datasync})");
709        reply.error(Errno::ENOSYS);
710    }
711
712    /// Open a directory.
713    /// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh, and
714    /// use this in other all other directory stream operations (readdir, releasedir,
715    /// fsyncdir). Filesystem may also implement stateless directory I/O and not store
716    /// anything in fh, though that makes it impossible to implement standard conforming
717    /// directory stream operations in case the contents of the directory can change
718    /// between opendir and releasedir.
719    fn opendir(&self, _req: &Request, _ino: INodeNo, _flags: OpenFlags, reply: ReplyOpen) {
720        reply.opened(FileHandle(0), FopenFlags::empty());
721    }
722
723    /// Read directory.
724    /// Send a buffer filled using `buffer.fill()`, with size not exceeding the
725    /// requested size. Send an empty buffer on end of stream. fh will contain the
726    /// value set by the opendir method, or will be undefined if the opendir method
727    /// didn't set any value.
728    fn readdir(
729        &self,
730        _req: &Request,
731        ino: INodeNo,
732        fh: FileHandle,
733        offset: u64,
734        reply: ReplyDirectory,
735    ) {
736        warn!("[Not Implemented] readdir(ino: {ino:#x?}, fh: {fh}, offset: {offset})");
737        reply.error(Errno::ENOSYS);
738    }
739
740    /// Read directory.
741    /// Send a buffer filled using `buffer.fill()`, with size not exceeding the
742    /// requested size. Send an empty buffer on end of stream. fh will contain the
743    /// value set by the opendir method, or will be undefined if the opendir method
744    /// didn't set any value.
745    fn readdirplus(
746        &self,
747        _req: &Request,
748        ino: INodeNo,
749        fh: FileHandle,
750        offset: u64,
751        reply: ReplyDirectoryPlus,
752    ) {
753        warn!("[Not Implemented] readdirplus(ino: {ino:#x?}, fh: {fh}, offset: {offset})");
754        reply.error(Errno::ENOSYS);
755    }
756
757    /// Release an open directory.
758    /// For every opendir call there will be exactly one releasedir call. fh will
759    /// contain the value set by the opendir method, or will be undefined if the
760    /// opendir method didn't set any value.
761    fn releasedir(
762        &self,
763        _req: &Request,
764        _ino: INodeNo,
765        _fh: FileHandle,
766        _flags: OpenFlags,
767        reply: ReplyEmpty,
768    ) {
769        reply.ok();
770    }
771
772    /// Synchronize directory contents.
773    /// If the datasync parameter is set, then only the directory contents should
774    /// be flushed, not the meta data. fh will contain the value set by the opendir
775    /// method, or will be undefined if the opendir method didn't set any value.
776    fn fsyncdir(
777        &self,
778        _req: &Request,
779        ino: INodeNo,
780        fh: FileHandle,
781        datasync: bool,
782        reply: ReplyEmpty,
783    ) {
784        warn!("[Not Implemented] fsyncdir(ino: {ino:#x?}, fh: {fh}, datasync: {datasync})");
785        reply.error(Errno::ENOSYS);
786    }
787
788    /// Get file system statistics.
789    fn statfs(&self, _req: &Request, _ino: INodeNo, reply: ReplyStatfs) {
790        reply.statfs(0, 0, 0, 0, 0, 512, 255, 0);
791    }
792
793    /// Set an extended attribute.
794    fn setxattr(
795        &self,
796        _req: &Request,
797        ino: INodeNo,
798        name: &OsStr,
799        _value: &[u8],
800        flags: i32,
801        position: u32,
802        reply: ReplyEmpty,
803    ) {
804        warn!(
805            "[Not Implemented] setxattr(ino: {ino:#x?}, name: {name:?}, \
806            flags: {flags:#x?}, position: {position})"
807        );
808        reply.error(Errno::ENOSYS);
809    }
810
811    /// Get an extended attribute.
812    /// If `size` is 0, the size of the value should be sent with `reply.size()`.
813    /// If `size` is not 0, and the value fits, send it with `reply.data()`, or
814    /// `reply.error(ERANGE)` if it doesn't.
815    fn getxattr(&self, _req: &Request, ino: INodeNo, name: &OsStr, size: u32, reply: ReplyXattr) {
816        warn!("[Not Implemented] getxattr(ino: {ino:#x?}, name: {name:?}, size: {size})");
817        reply.error(Errno::ENOSYS);
818    }
819
820    /// List extended attribute names.
821    /// If `size` is 0, the size of the value should be sent with `reply.size()`.
822    /// If `size` is not 0, and the value fits, send it with `reply.data()`, or
823    /// `reply.error(ERANGE)` if it doesn't.
824    fn listxattr(&self, _req: &Request, ino: INodeNo, size: u32, reply: ReplyXattr) {
825        warn!("[Not Implemented] listxattr(ino: {ino:#x?}, size: {size})");
826        reply.error(Errno::ENOSYS);
827    }
828
829    /// Remove an extended attribute.
830    fn removexattr(&self, _req: &Request, ino: INodeNo, name: &OsStr, reply: ReplyEmpty) {
831        warn!("[Not Implemented] removexattr(ino: {ino:#x?}, name: {name:?})");
832        reply.error(Errno::ENOSYS);
833    }
834
835    /// Check file access permissions.
836    /// This will be called for the `access()` system call. If the `default_permissions`
837    /// mount option is given, this method is not called. This method is not called
838    /// under Linux kernel versions 2.4.x
839    fn access(&self, _req: &Request, ino: INodeNo, mask: AccessFlags, reply: ReplyEmpty) {
840        warn!("[Not Implemented] access(ino: {ino:#x?}, mask: {mask})");
841        reply.error(Errno::ENOSYS);
842    }
843
844    /// Create and open a file.
845    /// If the file does not exist, first create it with the specified mode, and then
846    /// open it. You can use any open flags in the flags parameter except `O_NOCTTY`.
847    /// The filesystem can store any type of file handle (such as a pointer or index)
848    /// in fh, which can then be used across all subsequent file operations including
849    /// read, write, flush, release, and fsync. Additionally, the filesystem may set
850    /// certain flags like `direct_io` and `keep_cache` to change the way the file is
851    /// opened. See `fuse_file_info` structure in <`fuse_common.h`> for more details. If
852    /// this method is not implemented or under Linux kernel versions earlier than
853    /// 2.6.15, the `mknod()` and `open()` methods will be called instead.
854    fn create(
855        &self,
856        _req: &Request,
857        parent: INodeNo,
858        name: &OsStr,
859        mode: u32,
860        umask: u32,
861        flags: i32,
862        reply: ReplyCreate,
863    ) {
864        warn!(
865            "[Not Implemented] create(parent: {parent:#x?}, name: {name:?}, mode: {mode}, \
866            umask: {umask:#x?}, flags: {flags:#x?})"
867        );
868        reply.error(Errno::ENOSYS);
869    }
870
871    /// Test for a POSIX file lock.
872    fn getlk(
873        &self,
874        _req: &Request,
875        ino: INodeNo,
876        fh: FileHandle,
877        lock_owner: LockOwner,
878        start: u64,
879        end: u64,
880        typ: i32,
881        pid: u32,
882        reply: ReplyLock,
883    ) {
884        warn!(
885            "[Not Implemented] getlk(ino: {ino:#x?}, fh: {fh}, lock_owner: {lock_owner}, \
886            start: {start}, end: {end}, typ: {typ}, pid: {pid})"
887        );
888        reply.error(Errno::ENOSYS);
889    }
890
891    /// Acquire, modify or release a POSIX file lock.
892    /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but
893    /// otherwise this is not always the case.  For checking lock ownership,
894    /// 'fi->owner' must be used. The `l_pid` field in 'struct flock' should only be
895    /// used to fill in this field in `getlk()`. Note: if the locking methods are not
896    /// implemented, the kernel will still allow file locking to work locally.
897    /// Hence these are only interesting for network filesystems and similar.
898    fn setlk(
899        &self,
900        _req: &Request,
901        ino: INodeNo,
902        fh: FileHandle,
903        lock_owner: LockOwner,
904        start: u64,
905        end: u64,
906        typ: i32,
907        pid: u32,
908        sleep: bool,
909        reply: ReplyEmpty,
910    ) {
911        warn!(
912            "[Not Implemented] setlk(ino: {ino:#x?}, fh: {fh}, lock_owner: {lock_owner}, \
913            start: {start}, end: {end}, typ: {typ}, pid: {pid}, sleep: {sleep})"
914        );
915        reply.error(Errno::ENOSYS);
916    }
917
918    /// Map block index within file to block index within device.
919    /// Note: This makes sense only for block device backed filesystems mounted
920    /// with the 'blkdev' option
921    fn bmap(&self, _req: &Request, ino: INodeNo, blocksize: u32, idx: u64, reply: ReplyBmap) {
922        warn!("[Not Implemented] bmap(ino: {ino:#x?}, blocksize: {blocksize}, idx: {idx})",);
923        reply.error(Errno::ENOSYS);
924    }
925
926    /// control device
927    fn ioctl(
928        &self,
929        _req: &Request,
930        ino: INodeNo,
931        fh: FileHandle,
932        flags: IoctlFlags,
933        cmd: u32,
934        in_data: &[u8],
935        out_size: u32,
936        reply: ReplyIoctl,
937    ) {
938        warn!(
939            "[Not Implemented] ioctl(ino: {ino:#x?}, fh: {fh}, flags: {flags}, \
940            cmd: {cmd}, in_data.len(): {}, out_size: {out_size})",
941            in_data.len()
942        );
943        reply.error(Errno::ENOSYS);
944    }
945
946    /// Poll for events
947    fn poll(
948        &self,
949        _req: &Request,
950        ino: INodeNo,
951        fh: FileHandle,
952        ph: PollNotifier,
953        events: PollEvents,
954        flags: PollFlags,
955        reply: ReplyPoll,
956    ) {
957        warn!(
958            "[Not Implemented] poll(ino: {ino:#x?}, fh: {fh}, \
959            ph: {ph:?}, events: {events}, flags: {flags})"
960        );
961        reply.error(Errno::ENOSYS);
962    }
963
964    /// Preallocate or deallocate space to a file
965    fn fallocate(
966        &self,
967        _req: &Request,
968        ino: INodeNo,
969        fh: FileHandle,
970        offset: u64,
971        length: u64,
972        mode: i32,
973        reply: ReplyEmpty,
974    ) {
975        warn!(
976            "[Not Implemented] fallocate(ino: {ino:#x?}, fh: {fh}, \
977            offset: {offset}, length: {length}, mode: {mode})"
978        );
979        reply.error(Errno::ENOSYS);
980    }
981
982    /// Reposition read/write file offset
983    fn lseek(
984        &self,
985        _req: &Request,
986        ino: INodeNo,
987        fh: FileHandle,
988        offset: i64,
989        whence: i32,
990        reply: ReplyLseek,
991    ) {
992        warn!(
993            "[Not Implemented] lseek(ino: {ino:#x?}, fh: {fh}, \
994            offset: {offset}, whence: {whence})"
995        );
996        reply.error(Errno::ENOSYS);
997    }
998
999    /// Copy the specified range from the source inode to the destination inode
1000    fn copy_file_range(
1001        &self,
1002        _req: &Request,
1003        ino_in: INodeNo,
1004        fh_in: FileHandle,
1005        offset_in: u64,
1006        ino_out: INodeNo,
1007        fh_out: FileHandle,
1008        offset_out: u64,
1009        len: u64,
1010        flags: CopyFileRangeFlags,
1011        reply: ReplyWrite,
1012    ) {
1013        warn!(
1014            "[Not Implemented] copy_file_range(ino_in: {ino_in:#x?}, fh_in: {fh_in}, \
1015            offset_in: {offset_in}, ino_out: {ino_out:#x?}, fh_out: {fh_out}, \
1016            offset_out: {offset_out}, len: {len}, flags: {flags:?})"
1017        );
1018        reply.error(Errno::ENOSYS);
1019    }
1020
1021    /// macOS only: Rename the volume. Set `fuse_init_out.flags` during init to
1022    /// `FUSE_VOL_RENAME` to enable
1023    #[cfg(target_os = "macos")]
1024    fn setvolname(&self, _req: &Request, name: &OsStr, reply: ReplyEmpty) {
1025        warn!("[Not Implemented] setvolname(name: {name:?})");
1026        reply.error(Errno::ENOSYS);
1027    }
1028
1029    /// macOS only (undocumented)
1030    #[cfg(target_os = "macos")]
1031    fn exchange(
1032        &self,
1033        _req: &Request,
1034        parent: INodeNo,
1035        name: &OsStr,
1036        newparent: INodeNo,
1037        newname: &OsStr,
1038        options: u64,
1039        reply: ReplyEmpty,
1040    ) {
1041        warn!(
1042            "[Not Implemented] exchange(parent: {parent:#x?}, name: {name:?}, \
1043            newparent: {newparent:#x?}, newname: {newname:?}, options: {options})"
1044        );
1045        reply.error(Errno::ENOSYS);
1046    }
1047
1048    /// macOS only: Query extended times (`bkuptime` and `crtime`). Set `fuse_init_out.flags`
1049    /// during init to `FUSE_XTIMES` to enable
1050    #[cfg(target_os = "macos")]
1051    fn getxtimes(&self, _req: &Request, ino: INodeNo, reply: ReplyXTimes) {
1052        warn!("[Not Implemented] getxtimes(ino: {ino:#x?})");
1053        reply.error(Errno::ENOSYS);
1054    }
1055}
1056
1057/// Mount the given filesystem to the given mountpoint. This function will
1058/// not return until the filesystem is unmounted.
1059///
1060/// NOTE: This will eventually replace `mount()`, once the API is stable
1061/// # Errors
1062/// Returns an error if the options are incorrect, or if the fuse device can't be mounted,
1063/// and any final error when the session comes to an end.
1064pub fn mount<FS: Filesystem, P: AsRef<Path>>(
1065    filesystem: FS,
1066    mountpoint: P,
1067    options: &Config,
1068) -> io::Result<()> {
1069    Session::new(filesystem, mountpoint.as_ref(), options).and_then(session::Session::run)
1070}
1071
1072/// Mount the given filesystem to the given mountpoint. This function spawns
1073/// a background thread to handle filesystem operations while being mounted
1074/// and therefore returns immediately. The returned handle should be stored
1075/// to reference the mounted filesystem. If it's dropped, the filesystem will
1076/// be unmounted.
1077///
1078/// NOTE: This is the corresponding function to mount2.
1079/// # Errors
1080/// Returns an error if the options are incorrect, or if the fuse device can't be mounted.
1081pub fn spawn_mount<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef<Path>>(
1082    filesystem: FS,
1083    mountpoint: P,
1084    options: &Config,
1085) -> io::Result<BackgroundSession> {
1086    Session::new(filesystem, mountpoint.as_ref(), options).and_then(session::Session::spawn)
1087}