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