implemented FUSE lookup function
This commit is contained in:
parent
372aa34022
commit
833fb71108
7 changed files with 369 additions and 66 deletions
|
|
@ -1,11 +1,13 @@
|
|||
use std::ffi::c_int;
|
||||
use std::time::Duration;
|
||||
|
||||
use fat_bits::dir::DirEntry;
|
||||
use fuser::Filesystem;
|
||||
use libc::{EIO, ENOENT, ENOSYS, ENOTDIR, EPERM};
|
||||
use libc::{EIO, ENOENT, ENOSYS, EPERM};
|
||||
use log::{debug, warn};
|
||||
|
||||
use crate::FatFuse;
|
||||
use crate::inode::Inode;
|
||||
|
||||
impl Filesystem for FatFuse {
|
||||
fn init(
|
||||
|
|
@ -28,35 +30,91 @@ impl Filesystem for FatFuse {
|
|||
// warn!("[Not Implemented] lookup(parent: {:#x?}, name {:?})", parent, name);
|
||||
// reply.error(ENOSYS);
|
||||
|
||||
let parent_inode = if let Some(inode) = self.get_inode(parent) {
|
||||
inode
|
||||
} else {
|
||||
let Some(name) = name.to_str() else {
|
||||
// TODO: add proper handling of non-utf8 strings
|
||||
reply.error(ENOSYS);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(parent_inode) = self.get_inode(parent) else {
|
||||
// parent inode does not exist
|
||||
// TODO: how can we make sure this does not exist?
|
||||
// panic?
|
||||
// TODO: how can we make sure this does not happed?
|
||||
// TODO: panic?
|
||||
reply.error(EIO);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(dir_iter) = parent_inode.dir_iter(&self.fat_fs) else {
|
||||
reply.error(ENOTDIR);
|
||||
return;
|
||||
// let Ok(mut dir_iter) = parent_inode.dir_iter(&self.fat_fs) else {
|
||||
// reply.error(ENOTDIR);
|
||||
// return;
|
||||
// };
|
||||
|
||||
// let Some(dir_entry) =
|
||||
// dir_iter.find(|dir_entry| dir_entry.name_string().as_deref() == Some(name))
|
||||
// else {
|
||||
// reply.error(ENOENT);
|
||||
// return;
|
||||
// };
|
||||
|
||||
let dir_entry: DirEntry = match parent_inode
|
||||
.dir_iter(&self.fat_fs)
|
||||
// .map_err(|_| ENOTDIR)
|
||||
.and_then(|mut dir_iter| {
|
||||
dir_iter
|
||||
.find(|dir_entry| dir_entry.name_string().as_deref() == Some(name))
|
||||
.ok_or(ENOENT)
|
||||
}) {
|
||||
Ok(dir_entry) => dir_entry,
|
||||
Err(err) => {
|
||||
reply.error(err);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(dir_entry) =
|
||||
dir_iter.find(|dir_entry| dir_entry.name_str().as_deref() == Some(""))
|
||||
else {
|
||||
reply.error(ENOENT);
|
||||
return;
|
||||
let inode = match self.get_inode_by_first_cluster(dir_entry.first_cluster()) {
|
||||
Some(inode) => inode,
|
||||
None => {
|
||||
// no inode found, make a new one
|
||||
|
||||
let ino = self.next_ino();
|
||||
|
||||
let inode = Inode::new(&self.fat_fs, dir_entry, ino, self.uid, self.gid);
|
||||
|
||||
self.insert_inode(inode)
|
||||
}
|
||||
};
|
||||
|
||||
reply.entry(&Duration::from_secs(1), attr, generation);
|
||||
let attr = inode.file_attr();
|
||||
let generation = inode.generation();
|
||||
|
||||
todo!();
|
||||
reply.entry(&Duration::from_secs(1), &attr, generation as u64);
|
||||
}
|
||||
|
||||
fn forget(&mut self, _req: &fuser::Request<'_>, _ino: u64, _nlookup: u64) {}
|
||||
fn forget(&mut self, _req: &fuser::Request<'_>, ino: u64, nlookup: u64) {
|
||||
let Some(inode) = self.get_inode_mut(ino) else {
|
||||
debug!("tried to forget {} refs of inode {}, but was not found", ino, nlookup);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
let ref_count = inode.ref_count_mut();
|
||||
|
||||
if *ref_count < nlookup {
|
||||
debug!(
|
||||
"tried to forget {} refs of inode {}, but ref_count is only {}",
|
||||
nlookup, ino, *ref_count
|
||||
);
|
||||
}
|
||||
|
||||
*ref_count = ref_count.saturating_sub(nlookup);
|
||||
|
||||
if *ref_count == 0 {
|
||||
// no more references, drop inode
|
||||
self.drop_inode(ino);
|
||||
}
|
||||
}
|
||||
|
||||
fn getattr(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ use std::time::SystemTime;
|
|||
|
||||
use chrono::{NaiveDateTime, NaiveTime};
|
||||
use fat_bits::FatFs;
|
||||
use fat_bits::dir::{DirEntry, DirIter};
|
||||
use fat_bits::dir::DirEntry;
|
||||
use fuser::FileAttr;
|
||||
use libc::ENOTDIR;
|
||||
use rand::{Rng, SeedableRng as _};
|
||||
|
||||
thread_local! {
|
||||
|
|
@ -19,7 +20,10 @@ thread_local! {
|
|||
static RNG: LazyCell<RefCell<rand::rngs::SmallRng>> = LazyCell::new(|| RefCell::new(rand::rngs::SmallRng::from_os_rng()));
|
||||
}
|
||||
|
||||
fn get_random_u32() -> u32 {
|
||||
fn get_random<T>() -> T
|
||||
where
|
||||
rand::distr::StandardUniform: rand::distr::Distribution<T>,
|
||||
{
|
||||
// RNG.with(|x| unsafe {
|
||||
// let rng = &mut (*x.get());
|
||||
|
||||
|
|
@ -50,8 +54,12 @@ const ROOT_INO: u64 = 1;
|
|||
#[allow(dead_code)]
|
||||
pub struct Inode {
|
||||
ino: u64,
|
||||
// FUSE uses a u64 for generation, but the Linux kernel only handles u32s anyway, truncating
|
||||
// the high bits, so using more is pretty pointless and possibly even detrimental
|
||||
generation: u32,
|
||||
|
||||
ref_count: u64,
|
||||
|
||||
size: u64,
|
||||
block_size: u32,
|
||||
|
||||
|
|
@ -72,10 +80,21 @@ pub struct Inode {
|
|||
|
||||
#[allow(dead_code)]
|
||||
impl Inode {
|
||||
pub fn new(fat_fs: &FatFs, dir_entry: DirEntry, uid: u32, gid: u32) -> Inode {
|
||||
fn new_generation() -> u32 {
|
||||
let rand: u16 = get_random();
|
||||
|
||||
let secs = SystemTime::UNIX_EPOCH
|
||||
.elapsed()
|
||||
.map(|dur| dur.as_secs() as u16)
|
||||
.unwrap_or(0);
|
||||
|
||||
((secs as u32) << 16) | rand as u32
|
||||
}
|
||||
|
||||
pub fn new(fat_fs: &FatFs, dir_entry: DirEntry, ino: u64, uid: u32, gid: u32) -> Inode {
|
||||
assert!(dir_entry.is_file() || dir_entry.is_dir());
|
||||
|
||||
let generation = get_random_u32();
|
||||
let generation = Self::new_generation();
|
||||
|
||||
let kind = if dir_entry.is_dir() {
|
||||
Kind::Dir
|
||||
|
|
@ -96,8 +115,9 @@ impl Inode {
|
|||
let crtime = datetime_to_system(dir_entry.create_time());
|
||||
|
||||
Inode {
|
||||
ino: dir_entry.first_cluster() as u64,
|
||||
ino,
|
||||
generation,
|
||||
ref_count: 0,
|
||||
size: dir_entry.file_size() as u64,
|
||||
block_size: fat_fs.bpb().bytes_per_sector() as u32,
|
||||
kind,
|
||||
|
|
@ -111,6 +131,26 @@ impl Inode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ino(&self) -> u64 {
|
||||
self.ino
|
||||
}
|
||||
|
||||
pub fn generation(&self) -> u32 {
|
||||
self.generation
|
||||
}
|
||||
|
||||
pub fn ref_count(&self) -> u64 {
|
||||
self.ref_count
|
||||
}
|
||||
|
||||
pub fn ref_count_mut(&mut self) -> &mut u64 {
|
||||
&mut self.ref_count
|
||||
}
|
||||
|
||||
pub fn first_cluster(&self) -> u32 {
|
||||
self.first_cluster
|
||||
}
|
||||
|
||||
pub fn file_attr(&self) -> FileAttr {
|
||||
let perm = if self.read_only { 0o555 } else { 0o777 };
|
||||
|
||||
|
|
@ -133,11 +173,12 @@ impl Inode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn dir_iter(&self, fat_fs: &FatFs) -> anyhow::Result<impl Iterator<Item = DirEntry>> {
|
||||
anyhow::ensure!(self.kind == Kind::Dir, "cannot dir_iter on a file");
|
||||
pub fn dir_iter(&self, fat_fs: &FatFs) -> Result<impl Iterator<Item = DirEntry>, i32> {
|
||||
// anyhow::ensure!(self.kind == Kind::Dir, "cannot dir_iter on a file");
|
||||
|
||||
// TODO: the boxing here is not particularly pretty, but neccessary, since the DirIter for
|
||||
// the root holds a
|
||||
if self.kind != Kind::Dir {
|
||||
return Err(ENOTDIR);
|
||||
}
|
||||
|
||||
if self.ino == ROOT_INO {
|
||||
// root dir
|
||||
|
|
@ -145,9 +186,6 @@ impl Inode {
|
|||
return Ok(fat_fs.root_dir_iter());
|
||||
}
|
||||
|
||||
let chain_reader = fat_fs.chain_reader(self.first_cluster);
|
||||
|
||||
// TODO: get rid of this Box if the boxing is removed from root_dir_iter
|
||||
Ok(DirIter::new(Box::new(chain_reader)))
|
||||
Ok(fat_fs.dir_iter(self.first_cluster))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use std::collections::BTreeMap;
|
|||
use std::rc::Rc;
|
||||
|
||||
use fat_bits::{FatFs, SliceLike};
|
||||
use log::debug;
|
||||
|
||||
use crate::inode::Inode;
|
||||
|
||||
|
|
@ -16,9 +17,12 @@ pub struct FatFuse {
|
|||
uid: u32,
|
||||
gid: u32,
|
||||
|
||||
next_ino: u64,
|
||||
next_fd: u32,
|
||||
|
||||
inode_table: BTreeMap<u64, Inode>,
|
||||
|
||||
ino_by_first_cluster: BTreeMap<u32, u64>,
|
||||
}
|
||||
|
||||
impl FatFuse {
|
||||
|
|
@ -28,15 +32,105 @@ impl FatFuse {
|
|||
|
||||
let fat_fs = FatFs::load(data)?;
|
||||
|
||||
// TODO: build and insert root dir inode
|
||||
|
||||
Ok(FatFuse {
|
||||
fat_fs,
|
||||
uid,
|
||||
gid,
|
||||
next_ino: 2, // 0 is reserved and 1 is root
|
||||
next_fd: 0,
|
||||
inode_table: BTreeMap::new(),
|
||||
ino_by_first_cluster: BTreeMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn next_ino(&mut self) -> u64 {
|
||||
let ino = self.next_ino;
|
||||
|
||||
assert!(!self.inode_table.contains_key(&ino));
|
||||
|
||||
self.next_ino += 1;
|
||||
|
||||
ino
|
||||
}
|
||||
|
||||
fn insert_inode(&mut self, inode: Inode) -> &mut Inode {
|
||||
let ino = inode.ino();
|
||||
let generation = inode.generation();
|
||||
let first_cluster = inode.first_cluster();
|
||||
|
||||
// let old_inode = self.inode_table.insert(ino, inode);
|
||||
|
||||
let entry = self.inode_table.entry(ino);
|
||||
|
||||
let (new_inode, old_inode): (&mut Inode, Option<Inode>) = match entry {
|
||||
std::collections::btree_map::Entry::Vacant(vacant_entry) => {
|
||||
let new_inode = vacant_entry.insert(inode);
|
||||
(new_inode, None)
|
||||
}
|
||||
std::collections::btree_map::Entry::Occupied(occupied_entry) => {
|
||||
let entry_ref = occupied_entry.into_mut();
|
||||
|
||||
let old_inode = std::mem::replace(entry_ref, inode);
|
||||
|
||||
(entry_ref, Some(old_inode))
|
||||
}
|
||||
};
|
||||
|
||||
let old_ino = self.ino_by_first_cluster.insert(first_cluster, ino);
|
||||
|
||||
debug!(
|
||||
"inserted new inode with ino {} and generation {} (first cluster: {})",
|
||||
ino, generation, first_cluster
|
||||
);
|
||||
|
||||
if let Some(old_inode) = old_inode {
|
||||
debug!("ejected inode {} {}", old_inode.ino(), old_inode.generation());
|
||||
}
|
||||
|
||||
if let Some(old_ino) = old_ino {
|
||||
debug!("ejected old {} -> {} cluster to ino mapping", first_cluster, old_ino);
|
||||
}
|
||||
|
||||
new_inode
|
||||
}
|
||||
|
||||
fn drop_inode(&mut self, ino: u64) {
|
||||
let Some(inode) = self.inode_table.remove(&ino) else {
|
||||
debug!("tried to drop inode with ino {}, but was not in table", ino);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
let first_cluster = inode.first_cluster();
|
||||
|
||||
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 it's first cluster pointer to ino {} instead",
|
||||
ino, found_ino
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_inode(&self, ino: u64) -> Option<&Inode> {
|
||||
self.inode_table.get(&ino)
|
||||
}
|
||||
|
|
@ -44,4 +138,34 @@ impl FatFuse {
|
|||
fn get_inode_mut(&mut self, ino: u64) -> Option<&mut Inode> {
|
||||
self.inode_table.get_mut(&ino)
|
||||
}
|
||||
|
||||
pub fn get_inode_by_first_cluster(&self, first_cluster: u32) -> Option<&Inode> {
|
||||
let ino = self.ino_by_first_cluster.get(&first_cluster)?;
|
||||
|
||||
if let Some(inode) = self.inode_table.get(ino) {
|
||||
Some(inode)
|
||||
} else {
|
||||
debug!(
|
||||
"first cluster {} is mapped to ino {}, but inode is not in table",
|
||||
first_cluster, ino
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_inode_by_first_cluster_mut(&mut self, first_cluster: u32) -> Option<&mut Inode> {
|
||||
let ino = self.ino_by_first_cluster.get(&first_cluster)?;
|
||||
|
||||
if let Some(inode) = self.inode_table.get_mut(ino) {
|
||||
Some(inode)
|
||||
} else {
|
||||
debug!(
|
||||
"first cluster {} is mapped to ino {}, but inode is not in table",
|
||||
first_cluster, ino
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue