Skip to content

Instantly share code, notes, and snippets.

@rarensu
Last active August 2, 2025 12:58
Show Gist options
  • Select an option

  • Save rarensu/fc664da955f9f3a6d57eba3a4632bede to your computer and use it in GitHub Desktop.

Select an option

Save rarensu/fc664da955f9f3a6d57eba3a4632bede to your computer and use it in GitHub Desktop.
Examples for modular fuse components for fuser
#[test]
fn test_encryptfs_read() {
// Prepare ciphertext
let key = vec![1, 2, 3, 4, 5, 6, 7, 8];
let plaintext = b"hello world";
let ciphertext = simple_crypt::encrypt(plaintext, &key).expect("Encryption failed");
// Prepare a read request
let req = RequestMeta {
unique: 1,
uid: 1000,
gid: 1000,
pid: 1000,
};
let fh = 1u64;
let offset = 0i64;
let size = ciphertext.len() as u32;
let flags_i32 = 0i32;
let lock_owner = None;
// Setup AssertFS to expect a read() and return the prepared ciphertext
let mut assert_fs = AssertFS::new();
assert_fs.set_read_in(req, 1, fh, offset, size, flags_i32, lock_owner);
assert_fs.set_read_out(Ok(Bytes::from(ciphertext)));
// Setup EncryptFS with AssertFS as inner filesystem
let mut encrypt_fs = EncryptFS::new(key.clone(), new_fs_box(assert_fs));
// Perform the read operation
let read_result = encrypt_fs.read(req, 1, fh, offset, size, flags_i32, lock_owner);
// Assert that the decrypted data matches the original plaintext
assert!(read_result.is_ok());
let decrypted_data = read_result.unwrap();
assert_eq!(decrypted_data.as_ref(), plaintext);
}
// Example six-layer filesystem by Richard Lawrence 2025/07/30
/*
1 BranchFS
2 ├─ MonitorFS
3 │ └─ CacheFS
4 │ └─ EncryptFS
5 │ └─ BindFS
6 └─ EmptyFS
*/
use std::{path::PathBuf, time::Duration};
use fuser::{MountOption};
use log::info;
use clap::Parser;
use fuse_components::{
BranchFS, BindFS, MonitorFS, CacheFS, EncryptFS, EmptyFS,
new_fs_box, new_fs_layer_box
};
#[derive(Parser, Debug)]
#[command(author = "Richard Lawrence", version = "0.1", about = "Fuse Layered Filesystem", long_about = None)]
struct Args {
/// The path to the mount point
#[arg(short, long = "mountpoint")]
mountpoint: String,
/// The path to the underlying directory for BindFS
#[arg(short, long = "bind-path")]
bind_path: String,
/// Password for the EncryptFS
#[arg(short, long = "password")]
password: String,
}
/// Helper function to build example six-layer filesystem
fn construct_six_layer_filesystem(bind_path: PathBuf, password: Vec<u8>) -> BranchFS {
// Outer filesystem layer is a BranchFS
/* 1 */
let mut outer_fs = BranchFS::default();
// Nested branch containing layers 2,3,4,5
let nested_branch = new_fs_box(
/* 2 */
MonitorFS {
name: "before cache".to_owned(),
inner: new_fs_layer_box(
/* 3 */
CacheFS::new(
Duration::new(10, 0),
128,
128,
128,
new_fs_layer_box(
/* 4 */
EncryptFS::new(
Vec::from(password),
new_fs_box(
/* 5 */
BindFS::new(bind_path)
)
)
)
)
)
}
);
/* 6 */
let empty_branch = new_fs_box(EmptyFS);
// Mount the nested branch at the root of the filesystem (overwrites default)
outer_fs.insert_branch(0, nested_branch, PathBuf::from(""));
// Mount the empty branch under a subdirectory named "empty"
outer_fs.insert_branch(1, empty_branch, PathBuf::from("empty"));
// Return
outer_fs
}
fn main() -> Result<(), i32> {
env_logger::init();
let args = Args::parse();
let mountpoint = PathBuf::from(&args.mountpoint);
let bind_path = PathBuf::from(&args.bind_path);
let password= Vec::from(args.password);
info!("Building filesystem with bind path {:?} (and secret password)", bind_path);
let fs = construct_six_layer_filesystem(bind_path, password);
let options = vec![
MountOption::FSName("fuse example".to_string()),
];
info!("Mounting filesystem at {:?}", mountpoint);
let mut session = fuser::Session::new(fs, &mountpoint, &options).unwrap();
// wait for the session to finish
session.run_with_notifications().unwrap();
info!("Filesystem unmounted");
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment