Skip to main content

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