fat-rs/fat-fuse/src/lib.rs

300 lines
8.4 KiB
Rust
Raw Normal View History

mod fuse;
mod inode;
2025-08-01 18:09:45 +02:00
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
2025-07-27 14:38:31 +02:00
2025-07-31 01:14:28 +02:00
use fat_bits::dir::DirEntry;
2025-07-27 14:38:31 +02:00
use fat_bits::{FatFs, SliceLike};
use fxhash::FxHashMap;
2025-08-01 18:09:45 +02:00
use log::{debug, error};
2025-07-27 14:38:31 +02:00
2025-08-01 18:09:45 +02:00
use crate::inode::{Inode, InodeRef};
2025-07-27 14:38:31 +02:00
#[allow(dead_code)]
pub struct FatFuse {
fat_fs: FatFs,
2025-07-27 14:38:31 +02:00
uid: u32,
gid: u32,
2025-07-30 21:39:45 +02:00
next_ino: u64,
2025-07-31 01:14:28 +02:00
next_fh: u64,
2025-08-01 18:09:45 +02:00
inode_table: BTreeMap<u64, InodeRef>,
2025-07-30 21:39:45 +02:00
ino_by_first_cluster: BTreeMap<u32, u64>,
2025-07-31 01:14:28 +02:00
ino_by_fh: BTreeMap<u64, u64>,
ino_by_path: FxHashMap<Rc<str>, u64>,
2025-07-31 01:14:28 +02:00
}
/// SAFETY
///
/// do NOT leak Rc<str> from this type
unsafe impl Send for FatFuse {}
impl FatFuse {
pub fn new<S>(data: S) -> anyhow::Result<FatFuse>
where
S: SliceLike + Send + 'static,
{
2025-07-27 14:38:31 +02:00
let uid = unsafe { libc::getuid() };
let gid = unsafe { libc::getgid() };
let fat_fs = FatFs::load(data)?;
2025-07-31 01:14:28 +02:00
let mut fat_fuse = FatFuse {
fat_fs,
uid,
gid,
2025-07-30 21:39:45 +02:00
next_ino: 2, // 0 is reserved and 1 is root
2025-07-31 01:14:28 +02:00
next_fh: 0,
inode_table: BTreeMap::new(),
2025-07-30 21:39:45 +02:00
ino_by_first_cluster: BTreeMap::new(),
2025-07-31 01:14:28 +02:00
ino_by_fh: BTreeMap::new(),
ino_by_path: FxHashMap::default(),
2025-07-31 01:14:28 +02:00
};
// TODO: build and insert root dir inode
let root_inode = Inode::root_inode(&fat_fuse.fat_fs, uid, gid);
fat_fuse.insert_inode(root_inode);
Ok(fat_fuse)
2025-07-27 14:38:31 +02:00
}
2025-07-30 21:39:45 +02:00
fn next_ino(&mut self) -> u64 {
let ino = self.next_ino;
assert!(!self.inode_table.contains_key(&ino));
self.next_ino += 1;
ino
}
2025-07-31 01:14:28 +02:00
fn next_fh(&mut self) -> u64 {
let fh = self.next_fh;
assert!(!self.ino_by_fh.contains_key(&fh));
self.next_fh += 1;
fh
}
2025-08-01 18:09:45 +02:00
fn insert_inode(&mut self, inode: Inode) -> InodeRef {
2025-07-30 21:39:45 +02:00
let ino = inode.ino();
let generation = inode.generation();
let first_cluster = inode.first_cluster();
// let old_inode = self.inode_table.insert(ino, inode);
2025-08-01 18:09:45 +02:00
let inode = Rc::new(RefCell::new(inode));
2025-07-30 21:39:45 +02:00
let entry = self.inode_table.entry(ino);
2025-08-01 18:09:45 +02:00
let (new_inode, old_inode) = match entry {
2025-07-30 21:39:45 +02:00
std::collections::btree_map::Entry::Vacant(vacant_entry) => {
let new_inode = vacant_entry.insert(inode);
2025-08-01 18:09:45 +02:00
(Rc::clone(new_inode), None)
2025-07-30 21:39:45 +02:00
}
std::collections::btree_map::Entry::Occupied(occupied_entry) => {
let entry_ref = occupied_entry.into_mut();
let old_inode = std::mem::replace(entry_ref, inode);
2025-08-01 18:09:45 +02:00
(Rc::clone(entry_ref), Some(old_inode))
2025-07-30 21:39:45 +02:00
}
};
debug!(
"inserted new inode with ino {} and generation {} (first cluster: {})",
ino, generation, first_cluster
);
if let Some(old_inode) = old_inode {
2025-08-01 18:09:45 +02:00
let old_inode = old_inode.borrow();
2025-07-30 21:39:45 +02:00
debug!("ejected inode {} {}", old_inode.ino(), old_inode.generation());
}
2025-07-31 01:14:28 +02:00
if first_cluster != 0 {
if let Some(old_ino) = self.ino_by_first_cluster.insert(first_cluster, ino) {
debug!("ejected old {} -> {} cluster to ino mapping", first_cluster, old_ino);
}
2025-07-30 21:39:45 +02:00
}
2025-08-01 18:09:45 +02:00
let path = new_inode.borrow().path();
if let Some(old_ino) = self.ino_by_path.insert(Rc::clone(&path), ino) {
debug!("ejected old {} -> {} path to ino mapping", path, old_ino);
}
2025-07-30 21:39:45 +02:00
new_inode
}
2025-08-01 18:09:45 +02:00
fn drop_inode(&mut self, inode: InodeRef) {
let inode = inode.borrow();
2025-07-31 01:14:28 +02:00
2025-08-01 18:09:45 +02:00
let ino = inode.ino();
debug!("dropping inode {}", ino);
2025-08-01 22:42:13 +02:00
if self.inode_table.remove(&ino).is_none() {
2025-08-01 18:09:45 +02:00
error!("tried to drop inode with ino {}, but was not in table", ino);
2025-07-30 21:39:45 +02:00
return;
};
let first_cluster = inode.first_cluster();
2025-07-31 01:14:28 +02:00
if first_cluster != 0 {
let entry = self.ino_by_first_cluster.entry(first_cluster);
match entry {
std::collections::btree_map::Entry::Vacant(_) => debug!(
"removed inode with ino {} from table, but it's first cluster did not point to any ino",
ino
),
std::collections::btree_map::Entry::Occupied(occupied_entry) => {
let found_ino = *occupied_entry.get();
if found_ino == ino {
// matches our inode, remove it
occupied_entry.remove();
} else {
// does not match removed inode, leave it as is
debug!(
"removed inode with ino {} from table, but its first cluster pointed to ino {} instead",
2025-07-31 01:14:28 +02:00
ino, found_ino
);
}
2025-07-30 21:39:45 +02:00
}
}
}
{
let entry = self.ino_by_path.entry(inode.path());
match entry {
std::collections::hash_map::Entry::Vacant(_) => debug!(
"removed inode with ino {} from table, but it's path did not point to any ino",
ino
),
std::collections::hash_map::Entry::Occupied(occupied_entry) => {
let found_ino = *occupied_entry.get();
if found_ino == ino {
// matches our inode, remove it
occupied_entry.remove();
} else {
// does not match removed inode, leave it as is
debug!(
"removed inode with ino {} from table, but its path pointed to ino {} instead",
ino, found_ino
);
}
}
}
}
2025-07-30 21:39:45 +02:00
}
2025-08-01 18:09:45 +02:00
fn get_inode(&self, ino: u64) -> Option<&InodeRef> {
self.inode_table.get(&ino)
2025-07-27 14:38:31 +02:00
}
fn get_or_make_inode_by_dir_entry(
&mut self,
dir_entry: &DirEntry,
parent_ino: u64,
parent_path: Rc<str>,
2025-08-01 18:09:45 +02:00
) -> InodeRef {
// try to find inode by first cluster first
if dir_entry.first_cluster() != 0
2025-08-01 18:09:45 +02:00
&& let Some(inode) = self.get_inode_by_first_cluster(dir_entry.first_cluster())
2025-07-31 01:14:28 +02:00
{
2025-08-01 18:09:45 +02:00
return inode;
2025-07-31 01:14:28 +02:00
}
2025-08-01 18:09:45 +02:00
// try to find inode by path
// mostly for empty files/directories which have a first cluster of 0
let path = {
let mut path = parent_path.as_ref().to_owned();
if parent_ino != inode::ROOT_INO {
// root inode already has trailing slash
path.push('/');
}
path += &dir_entry.name_string();
path
};
2025-08-01 18:09:45 +02:00
if let Some(inode) = self.get_inode_by_path(&path) {
return inode;
}
2025-07-31 01:14:28 +02:00
// no inode found, make a new one
let ino = self.next_ino();
2025-08-01 18:09:45 +02:00
let Some(parent_inode) = self.get_inode(parent_ino).cloned() else {
// TODO: what do we do here? should not happen
panic!("parent_ino {} does not lead to inode", parent_ino);
};
2025-08-01 18:09:45 +02:00
let inode =
Inode::new(&self.fat_fs, dir_entry, ino, self.uid, self.gid, path, parent_inode);
2025-07-31 01:14:28 +02:00
self.insert_inode(inode)
}
2025-08-01 18:09:45 +02:00
pub fn get_inode_by_first_cluster(&self, first_cluster: u32) -> Option<InodeRef> {
2025-07-31 01:14:28 +02:00
if first_cluster == 0 {
debug!("trying to get inode by first cluster 0");
return None;
}
2025-07-30 21:39:45 +02:00
let ino = self.ino_by_first_cluster.get(&first_cluster)?;
if let Some(inode) = self.inode_table.get(ino) {
2025-08-01 18:09:45 +02:00
Some(Rc::clone(inode))
2025-07-30 21:39:45 +02:00
} else {
debug!(
"first cluster {} is mapped to ino {}, but inode is not in table",
first_cluster, ino
);
None
}
}
2025-08-01 18:09:45 +02:00
pub fn get_inode_by_fh(&self, fh: u64) -> Option<InodeRef> {
let ino = *self.ino_by_fh.get(&fh)?;
2025-07-31 01:14:28 +02:00
2025-08-01 18:09:45 +02:00
if let Some(inode) = self.get_inode(ino).cloned() {
2025-07-31 01:14:28 +02:00
Some(inode)
} else {
debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino);
None
}
}
2025-08-01 18:09:45 +02:00
pub fn get_inode_by_path(&self, path: &str) -> Option<InodeRef> {
let ino = *self.ino_by_path.get(path)?;
2025-08-01 18:09:45 +02:00
if let Some(inode) = self.get_inode(ino).cloned() {
Some(inode)
} else {
debug!("path {} is mapped to ino {}, but inode is not in table", path, ino);
None
}
}
2025-07-27 14:38:31 +02:00
}