Files
markbase/markbase-fuse/src/fuse/filesystem.rs
T
Warren d94cb2df4c Fix code quality: trailing whitespace, unused imports, clippy warnings
- Fix trailing whitespace in kex.rs and s3.rs
- Add missing KexProposal import in kex_complete.rs
- Auto-fix clippy warnings across all crates
- All 153 tests pass
2026-06-19 05:21:38 +08:00

281 lines
8.1 KiB
Rust

use anyhow::Result;
use fuse_backend_rs::abi::fuse_abi::{stat64, statvfs64, FsOptions, OpenOptions};
use fuse_backend_rs::api::filesystem::{Context, DirEntry, Entry, FileSystem, ZeroCopyWriter};
use std::collections::HashMap;
use std::ffi::CStr;
use std::fs::File;
use std::io::Read;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::{Arc, RwLock};
use std::time::{Duration, SystemTime};
use super::cache::ThreadSafeCache;
use super::db::DbManager;
const TTL: Duration = Duration::from_secs(1);
const READ_CHUNK_SIZE: usize = 524288; // 512KB
pub struct MarkBaseFs {
db: Arc<DbManager>,
cache: Arc<ThreadSafeCache>,
inode_map: RwLock<HashMap<u64, String>>,
next_inode: RwLock<u64>,
}
impl MarkBaseFs {
pub fn new(db_path: &str, tree_type: &str) -> Result<Self> {
let db = DbManager::open(db_path, tree_type)?;
let cache = ThreadSafeCache::new();
Ok(Self {
db: Arc::new(db),
cache: Arc::new(cache),
inode_map: RwLock::new(HashMap::new()),
next_inode: RwLock::new(2),
})
}
fn find_node_id_by_inode(&self, ino: u64) -> Option<String> {
self.inode_map.read().unwrap().get(&ino).cloned()
}
fn get_or_create_inode(&self, node_id: &str) -> u64 {
let map = self.inode_map.read().unwrap();
for (ino, id) in &*map {
if id == node_id {
return *ino;
}
}
drop(map);
let mut next = self.next_inode.write().unwrap();
let ino = *next;
*next += 1;
let mut map = self.inode_map.write().unwrap();
map.insert(ino, node_id.to_string());
ino
}
fn make_stat64(node_type: &str, file_size: u64) -> stat64 {
let mut st = unsafe { std::mem::zeroed::<stat64>() };
st.st_ino = 0;
st.st_mode = if node_type == "folder" { 0o755 } else { 0o644 };
st.st_nlink = if node_type == "folder" { 2 } else { 1 };
st.st_uid = 501;
st.st_gid = 20;
st.st_size = file_size as i64;
st.st_blocks = file_size.div_ceil(512) as i64;
st.st_blksize = 4096;
let now = SystemTime::now();
st.st_atime = now
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
st.st_mtime = st.st_atime;
st.st_ctime = st.st_atime;
st
}
}
impl FileSystem for MarkBaseFs {
type Inode = u64;
type Handle = u64;
fn init(&self, capable: FsOptions) -> std::io::Result<FsOptions> {
Ok(capable)
}
fn lookup(&self, _ctx: &Context, parent: Self::Inode, name: &CStr) -> std::io::Result<Entry> {
let name_str = name.to_string_lossy();
let node_id = if parent == 1 {
self.db
.find_node_id(&format!("/{}", name_str))
.ok()
.flatten()
} else {
let parent_id = self.find_node_id_by_inode(parent);
match parent_id {
Some(pid) => self
.db
.find_node_id_by_parent(&pid, &name_str)
.ok()
.flatten(),
None => None,
}
};
match node_id {
Some(id) => {
let info = self.db.get_node_info(&id);
match info {
Ok(Some((node_type, file_size))) => {
let ino = self.get_or_create_inode(&id);
let attr = MarkBaseFs::make_stat64(&node_type, file_size);
Ok(Entry {
inode: ino,
generation: 0,
attr,
attr_flags: 0,
attr_timeout: TTL,
entry_timeout: TTL,
})
}
_ => Err(std::io::Error::from_raw_os_error(libc::ENOENT)),
}
}
None => Err(std::io::Error::from_raw_os_error(libc::ENOENT)),
}
}
fn forget(&self, _ctx: &Context, inode: Self::Inode, _count: u64) {
let mut map = self.inode_map.write().unwrap();
map.remove(&inode);
}
fn getattr(
&self,
_ctx: &Context,
inode: Self::Inode,
_handle: Option<Self::Handle>,
) -> std::io::Result<(stat64, Duration)> {
if inode == 1 {
let attr = MarkBaseFs::make_stat64("folder", 0);
return Ok((attr, TTL));
}
let node_id = self.find_node_id_by_inode(inode);
match node_id {
Some(id) => {
let info = self.db.get_node_info(&id);
match info {
Ok(Some((node_type, file_size))) => {
let attr = MarkBaseFs::make_stat64(&node_type, file_size);
Ok((attr, TTL))
}
_ => Err(std::io::Error::from_raw_os_error(libc::ENOENT)),
}
}
None => Err(std::io::Error::from_raw_os_error(libc::ENOENT)),
}
}
fn open(
&self,
_ctx: &Context,
inode: Self::Inode,
_flags: u32,
_fuse_flags: u32,
) -> std::io::Result<(Option<Self::Handle>, OpenOptions, Option<u32>)> {
Ok((Some(inode), OpenOptions::empty(), None))
}
fn read(
&self,
_ctx: &Context,
inode: Self::Inode,
_handle: Self::Handle,
w: &mut dyn ZeroCopyWriter,
size: u32,
offset: u64,
_lock_owner: Option<u64>,
_flags: u32,
) -> std::io::Result<usize> {
let node_id = self.find_node_id_by_inode(inode);
match node_id {
Some(id) => {
let file_path = self.db.get_file_path(&id);
match file_path {
Ok(Some(fp)) => {
let file = File::open(&fp)?;
let fd = file.as_raw_fd();
let f = unsafe { File::from_raw_fd(fd) };
let mut f = std::mem::ManuallyDrop::new(f);
w.write_from(&mut *f, size as usize, offset)
}
_ => Err(std::io::Error::from_raw_os_error(libc::ENOENT)),
}
}
None => Err(std::io::Error::from_raw_os_error(libc::ENOENT)),
}
}
fn release(
&self,
_ctx: &Context,
_inode: Self::Inode,
_flags: u32,
_handle: Self::Handle,
_flush: bool,
_flock_release: bool,
_lock_owner: Option<u64>,
) -> std::io::Result<()> {
Ok(())
}
fn opendir(
&self,
_ctx: &Context,
inode: Self::Inode,
_flags: u32,
) -> std::io::Result<(Option<Self::Handle>, OpenOptions)> {
Ok((Some(inode), OpenOptions::empty()))
}
fn readdir(
&self,
_ctx: &Context,
inode: Self::Inode,
_handle: Self::Handle,
_size: u32,
offset: u64,
add_entry: &mut dyn FnMut(DirEntry) -> std::io::Result<usize>,
) -> std::io::Result<()> {
if offset == 0 {
add_entry(DirEntry {
name: b".\0",
ino: inode,
offset: 1,
type_: libc::S_IFDIR as u32,
})?;
add_entry(DirEntry {
name: b"..\0",
ino: 1,
offset: 2,
type_: libc::S_IFDIR as u32,
})?;
}
Ok(())
}
fn releasedir(
&self,
_ctx: &Context,
_inode: Self::Inode,
_flags: u32,
_handle: Self::Handle,
) -> std::io::Result<()> {
Ok(())
}
fn statfs(&self, _ctx: &Context, _inode: Self::Inode) -> std::io::Result<statvfs64> {
let mut st = unsafe { std::mem::zeroed::<statvfs64>() };
st.f_bsize = 4096;
st.f_blocks = 1000000;
st.f_bfree = 500000;
st.f_bavail = 500000;
st.f_files = 1000;
st.f_ffree = 500;
st.f_favail = 500;
st.f_namemax = 255;
Ok(st)
}
}