Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cargo-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- rust_toolchain: stable
deny_warnings: --deny warnings
- rust_toolchain: nightly
deny_warnings:
deny_warnings: true
steps:
- name: Set up FUSE
run: ${{matrix.fuse_install}}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
Cargo.lock
.history
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ readme = "README.md"
edition = "2018"

[dependencies]
fuser = "0.11"
tracing = "0.1"
libc = "0.2"
log = "0.4"
threadpool = "1.0"
threadpool = "1.8.1"

[dependencies.fuser]
version="0.11"
features=[ "abi-7-31", "libfuse" ]

[workspace]
members = [".", "example"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Some random notes on the implementation:
* write
* flush
* fsync
* ioctl
* Other calls run synchronously on the main thread because either it is expected that they will complete quickly and/or they require mutating internal state of the InodeTranslator and I want to avoid needing locking in there.
* The inode/path translation is always done on the main thread.
* It might be a good idea to limit the number of concurrent read and write operations in flight. I'm not sure yet how many outstanding read/write requests FUSE will issue though, so it might be a non-issue.
2 changes: 1 addition & 1 deletion example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
name = "passthrufs"
version = "0.1.0"
authors = ["William R. Fraser <[email protected]>"]
edition = "2018"
workspace = ".."
edition = "2018"

[dependencies]
libc = "0.2"
Expand Down
2 changes: 1 addition & 1 deletion example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn main() {
target: args[1].clone(),
};

let fuse_args = [OsStr::new("-o"), OsStr::new("fsname=passthrufs")];
let fuse_args = [OsStr::new("-o"), OsStr::new("fsname=passthrufs,auto_unmount")];

fuse_mt::mount(fuse_mt::FuseMT::new(filesystem, 1), &args[2], &fuse_args[..]).unwrap();
}
76 changes: 67 additions & 9 deletions src/fusemt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@
// operations to multiple threads.
//
// Copyright (c) 2016-2022 by William R. Fraser
//
// Copyright (C) 2019-2022 Ahmed Masud.


use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::SystemTime;

use fuser::TimeOrNow;

use threadpool::ThreadPool;
use tracing::{debug, error};

use crate:: {
directory_cache::*,
inode_table::*,
types::*
};
use fuser::TimeOrNow;

use crate::directory_cache::*;
use crate::inode_table::*;
use crate::types::*;

trait IntoRequestInfo {
fn info(&self) -> RequestInfo;
Expand All @@ -26,7 +32,7 @@ impl<'a> IntoRequestInfo for fuser::Request<'a> {
unique: self.unique(),
uid: self.uid(),
gid: self.gid(),
pid: self.pid(),
pid: self.pid() as i32,
}
}
}
Expand All @@ -47,6 +53,7 @@ fn fuse_fileattr(attr: FileAttr, ino: u64) -> fuser::FileAttr {
gid: attr.gid,
rdev: attr.rdev,
blksize: 4096, // TODO
// padding: 0,
flags: attr.flags,
}
}
Expand Down Expand Up @@ -89,7 +96,8 @@ impl<T: FilesystemMT + Sync + Send + 'static> FuseMT<T> {
f()
} else {
if self.threads.is_none() {
debug!("initializing threadpool with {} threads", self.num_threads);
debug!("initializing threadpool with {} threads",
self.num_threads);
self.threads = Some(ThreadPool::new(self.num_threads));
}
self.threads.as_ref().unwrap().execute(f);
Expand Down Expand Up @@ -117,7 +125,6 @@ impl<T: FilesystemMT + Sync + Send + 'static> fuser::Filesystem for FuseMT<T> {
debug!("init");
self.target.init(req.info())
}

fn destroy(&mut self) {
debug!("destroy");
self.target.destroy();
Expand Down Expand Up @@ -480,6 +487,37 @@ impl<T: FilesystemMT + Sync + Send + 'static> fuser::Filesystem for FuseMT<T> {
});
}

fn ioctl(
&mut self,
req: &fuser::Request<'_>,
ino: u64,
fh: u64,
flags: u32,
cmd: u32,
in_data: &[u8],
out_size: u32,
reply: fuser::ReplyIoctl
) {
let path = get_path!(self, ino, reply);
debug!("ioctl: {:?} {:#x} {:#x}", path, flags, cmd);
let target = self.target.clone();
let req_info = req.info();
let data_buf = Vec::from(in_data);
self.threadpool_run(move || {
target.ioctl(req_info, &path, fh, flags, cmd, &data_buf,
|result| {
match result {
Ok(data) => reply.ioctl(data.0,
&data.1[..out_size as usize]),
Err(e) => reply.error(e),
}
CallbackResult {
_private: std::marker::PhantomData {},
}
});
});
}

fn flush(
&mut self,
req: &fuser::Request<'_>,
Expand Down Expand Up @@ -826,7 +864,27 @@ impl<T: FilesystemMT + Sync + Send + 'static> fuser::Filesystem for FuseMT<T> {

// bmap

#[cfg(target_os = "macos")]

fn fallocate(
&mut self,
req: &fuser::Request<'_>,
ino: u64,
fh: u64,
offset: i64,
length: i64,
mode: i32,
reply: fuser::ReplyEmpty,
) {
let path = get_path!(self, ino, reply);
debug!("fallocate: {:?}, offset={:?}, length={:?}, mode={:#o}",
path, offset, length, mode);
match self.target.fallocate(req.info(), &path, fh, offset, length, mode) {
Ok(()) => reply.ok(),
Err(e) => reply.error(e),
}
}

#[cfg(target_os = "macos")]
fn setvolname(
&mut self,
req: &fuser::Request<'_>,
Expand Down
1 change: 1 addition & 0 deletions src/inode_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::collections::hash_map::Entry::*;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tracing::{debug, error};

pub type Inode = u64;
pub type Generation = u64;
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
#![deny(rust_2018_idioms)]

#[macro_use]
extern crate log;
extern crate libc;


mod directory_cache;
mod fusemt;
Expand All @@ -26,6 +27,7 @@ mod types;

pub const VERSION: &str = env!("CARGO_PKG_VERSION");


pub use fuser::FileType;
pub use crate::fusemt::*;
pub use crate::types::*;
Expand Down
99 changes: 98 additions & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct RequestInfo {
/// The group ID of the process making the request.
pub gid: u32,
/// The process ID of the process making the request.
pub pid: u32,
pub pid: i32,
}

/// A directory entry.
Expand Down Expand Up @@ -99,6 +99,11 @@ pub enum Xattr {
Data(Vec<u8>),
}

#[derive(Clone, Debug)]
pub struct BmapBlock {
pub block: u64,
}

#[cfg(target_os = "macos")]
#[derive(Clone, Debug)]
pub struct XTimes {
Expand All @@ -116,6 +121,9 @@ pub type ResultWrite = Result<u32, libc::c_int>;
pub type ResultStatfs = Result<Statfs, libc::c_int>;
pub type ResultCreate = Result<CreatedEntry, libc::c_int>;
pub type ResultXattr = Result<Xattr, libc::c_int>;
pub type ResultBmap = Result<u64, libc::c_int>;
pub type ResultLseek = Result<u64, libc::c_int>;
pub type ResultIOCTL<'a> = Result<(i32, &'a [u8]), libc::c_int>;

#[cfg(target_os = "macos")]
pub type ResultXTimes = Result<XTimes, libc::c_int>;
Expand Down Expand Up @@ -471,6 +479,95 @@ pub trait FilesystemMT {

// bmap

/// Test for a POSIX file lock.
#[allow(clippy::too_many_arguments)]
fn getlk(&self, _req: &RequestInfo, _path: &Path, _fh: u64,
_lock_owner: u64, _start: u64, _end: u64, _typ: i32, _pid: u32) -> ResultEmpty {
Err(libc::ENOSYS)
}

/// Acquire, modify or release a POSIX file lock.
/// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but
/// otherwise this is not always the case. For checking lock ownership,
/// 'fi->owner' must be used. The l_pid field in 'struct flock' should only be
/// used to fill in this field in getlk(). Note: if the locking methods are not
/// implemented, the kernel will still allow file locking to work locally.
/// Hence these are only interesting for network filesystems and similar.
#[allow(clippy::too_many_arguments)]
fn setlk(&self,
_req: RequestInfo,
_path: &Path,
_fh: Option<u64>, _lock_owner: u64,
_start: u64, _end: u64,
_typ: i32, _pid: u32,
_sleep: bool)-> ResultEmpty {
Err(libc::ENOSYS)
}

/// Map block index within file to block index within device.
/// Note: This makes sense only for block device backed filesystems mounted
/// with the 'blkdev' option
fn bmap(&self,
_req: RequestInfo,
_path: &Path,
_blocksize: u32,
_idx: u64) -> ResultBmap {
Err(libc::ENOSYS)
}

/// control device
#[allow(clippy::too_many_arguments)]
fn ioctl(
&self,
_req: RequestInfo,
_path: &Path,
_fh: u64, _flags: u32,
_cmd: u32,
_in_data: &[u8],
callback: impl FnOnce(ResultIOCTL<'_>) ->
CallbackResult) -> CallbackResult {
callback(Err(libc::ENOSYS))
}

/// Preallocate or deallocate space to a file
fn fallocate(
&self,
_req: RequestInfo,
_path: &Path,
_fh: u64, _offset: i64,
_length: i64, _mode: i32) -> ResultEmpty {
Err(libc::ENOSYS)
}

/// Reposition read/write file offset
fn lseek(
&self,
_req: RequestInfo,
_fh: u64,
_offset: i64,
_whence: i32
) -> ResultLseek {
Err(libc::ENOSYS)
}

/// Copy the specified range from the source inode to the destination inode
#[allow(clippy::too_many_arguments)]
fn copy_file_range(
&self,
_req: RequestInfo,
_path_in: &Path,
_fh_in: Option<u64>,
_offset_in: i64,
_path_out: &Path,
_fh_out: Option<u64>,
_offset_out: i64,
_len: u64,
_flags: u32,
) -> ResultWrite {
Err(libc::ENOSYS)
}


/// macOS only: Rename the volume.
///
/// * `name`: new name for the volume
Expand Down