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