d94cb2df4c
- 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
281 lines
8.1 KiB
Rust
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)
|
|
}
|
|
}
|