fuser_ng/lib.rs
1//! FuserNG -- A higher-level FUSE (Filesystem in Userspace) interface and wrapper around the
2//! low-level `fuser` library that makes implementing a filesystem a bit easier.
3//!
4//! FuserNG translates inodes to paths and simplifies some details of filesystem implementation,
5//! for example: splitting the `setattr` call
6//! into multiple separate operations, and simplifying the `readdir` call so that filesystems don't
7//! need to deal with pagination.
8//!
9//! To implement a filesystem, implement the `Filesystem` trait. Not all functions in it need to
10//! be implemented -- the default behavior is to return `ENOSYS` ("Function not implemented"). For
11//! example, a read-only filesystem can skip implementing the `write` call and many others.
12
13//
14// Copyright (c) 2016-2022 by William R. Fraser
15//
16
17#![deny(rust_2018_idioms)]
18
19#[macro_use]
20extern crate log;
21
22mod directory_cache;
23mod fuserng;
24mod inode_table;
25mod types;
26
27/// Crate version from Cargo package metadata.
28pub const VERSION: &str = env!("CARGO_PKG_VERSION");
29
30pub use crate::fuserng::*;
31pub use crate::types::*;
32pub use fuser::FileType;
33pub use fuser::KernelConfig;
34pub use fuser::MountOption;
35// Forward to similarly-named fuser functions to work around deprecation for now.
36// When these are removed, we'll have to either reimplement or break reverse compat.
37// Keep the doc comments in sync with those in fuser.
38
39use std::io;
40use std::path::Path;
41
42/// Number of fuser event-loop threads used to serve a mount.
43/// This configures fuser session threading, not an internal worker pool.
44#[derive(Debug, Default)]
45pub enum ThreadCount {
46 /// Use the current machine parallelism as reported by the standard library.
47 #[default]
48 Default,
49 /// Use an explicit number of fuser event-loop threads.
50 NumThreads(usize),
51}
52
53impl ThreadCount {
54 fn value(&self) -> usize {
55 match self {
56 Self::Default => std::thread::available_parallelism().unwrap().into(),
57 Self::NumThreads(num_threads) => *num_threads,
58 }
59 }
60}
61
62impl From<usize> for ThreadCount {
63 fn from(value: usize) -> Self {
64 ThreadCount::NumThreads(value)
65 }
66}
67/// Mount the given filesystem to the given mountpoint. This function will not return until the
68/// filesystem is unmounted.
69#[inline(always)]
70pub fn mount<FS: fuser::Filesystem, P: AsRef<Path>>(
71 fs: FS,
72 mountpoint: P,
73 options: &[MountOption],
74 num_threads: ThreadCount,
75) -> io::Result<()> {
76 let mut config = fuser::Config::default();
77 config.mount_options = options.to_vec();
78 let num_threads = num_threads.value();
79 if num_threads > 0 {
80 config.n_threads = Some(num_threads);
81 }
82 fuser::mount2(fs, mountpoint, &config)
83}
84
85/// Mount the given filesystem to the given mountpoint. This function spawns a background thread to
86/// handle filesystem operations while being mounted and therefore returns immediately. The
87/// returned handle should be stored to reference the mounted filesystem. If it's dropped, the
88/// filesystem will be unmounted.
89#[inline(always)]
90pub fn spawn_mount<FS: fuser::Filesystem + Send + 'static, P: AsRef<Path>>(
91 fs: FS,
92 mountpoint: P,
93 options: &[MountOption],
94 num_threads: ThreadCount,
95) -> io::Result<fuser::BackgroundSession> {
96 let mut config = fuser::Config::default();
97 config.mount_options = options.to_vec();
98 let num_threads = num_threads.value();
99 if num_threads > 0 {
100 config.n_threads = Some(num_threads);
101 }
102 fuser::spawn_mount2(fs, mountpoint, &config)
103}