Compare commits
5 commits
8b55d8d13c
...
18c45d5141
| Author | SHA1 | Date | |
|---|---|---|---|
| 18c45d5141 | |||
| bdcda26d77 | |||
| 7a4d89a09a | |||
| df20ae6f0c | |||
| f708ab0b50 |
9 changed files with 406 additions and 280 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
|
@ -168,6 +168,16 @@ version = "0.8.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
|
||||
dependencies = [
|
||||
"nix 0.30.1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum_dispatch"
|
||||
version = "0.3.13"
|
||||
|
|
@ -243,6 +253,7 @@ name = "fat-mount"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ctrlc",
|
||||
"env_logger",
|
||||
"fat-fuse",
|
||||
"fuser",
|
||||
|
|
@ -257,7 +268,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"nix 0.29.0",
|
||||
"page_size",
|
||||
"pkg-config",
|
||||
"smallvec",
|
||||
|
|
@ -376,6 +387,18 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
|
|
|
|||
|
|
@ -236,6 +236,7 @@ impl Bpb {
|
|||
if self.fat_type() == FatType::Fat32 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.fat_offset() + self.sector_to_offset(self.num_fats() as u32 * self.fat_size()))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,8 +98,15 @@ pub struct FatFs {
|
|||
fat: fat::Fat,
|
||||
}
|
||||
|
||||
unsafe impl Send for FatFs {}
|
||||
|
||||
impl FatFs {
|
||||
pub fn load(data: Rc<RefCell<dyn SliceLike>>) -> anyhow::Result<FatFs> {
|
||||
pub fn load<S>(data: S) -> anyhow::Result<FatFs>
|
||||
where
|
||||
S: SliceLike + Send + 'static,
|
||||
{
|
||||
let data = Rc::new(RefCell::new(data));
|
||||
|
||||
let mut bpb_bytes = [0; 512];
|
||||
|
||||
data.borrow_mut().read_at_offset(0, &mut bpb_bytes)?;
|
||||
|
|
@ -178,12 +185,28 @@ impl FatFs {
|
|||
}
|
||||
|
||||
pub fn cluster_as_subslice_mut(&mut self, cluster: u32) -> SubSliceMut<'_> {
|
||||
if cluster == 0 {
|
||||
// for cluster 0 simply return empty subslice
|
||||
// this makes things a bit easier, since cluster 0 is used as a marker that a file/dir
|
||||
// is empty
|
||||
|
||||
SubSliceMut::new(self, 0, 0);
|
||||
}
|
||||
|
||||
let offset = self.data_cluster_to_offset(cluster);
|
||||
|
||||
SubSliceMut::new(self, offset, self.bytes_per_cluster)
|
||||
}
|
||||
|
||||
pub fn cluster_as_subslice(&self, cluster: u32) -> SubSlice<'_> {
|
||||
if cluster == 0 {
|
||||
// for cluster 0 simply return empty subslice
|
||||
// this makes things a bit easier, since cluster 0 is used as a marker that a file/dir
|
||||
// is empty
|
||||
|
||||
SubSlice::new(self, 0, 0);
|
||||
}
|
||||
|
||||
let offset = self.data_cluster_to_offset(cluster);
|
||||
|
||||
SubSlice::new(self, offset, self.bytes_per_cluster)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub fn main() -> anyhow::Result<()> {
|
|||
|
||||
// println!("{}", bpb);
|
||||
|
||||
let fat_fs = FatFs::load(Rc::new(RefCell::new(file)))?;
|
||||
let fat_fs = FatFs::load(file)?;
|
||||
|
||||
println!("{}", fat_fs.bpb());
|
||||
println!();
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ use std::ffi::c_int;
|
|||
use std::time::Duration;
|
||||
|
||||
use fat_bits::dir::DirEntry;
|
||||
use fuser::Filesystem;
|
||||
use libc::{EIO, ENOENT, ENOSYS, EPERM};
|
||||
use fuser::{FileType, Filesystem};
|
||||
use libc::{EINVAL, EIO, ENOENT, ENOSYS, ENOTDIR, EPERM};
|
||||
use log::{debug, warn};
|
||||
|
||||
use crate::FatFuse;
|
||||
use crate::inode::Inode;
|
||||
|
||||
const TTL: Duration = Duration::from_secs(1);
|
||||
|
||||
impl Filesystem for FatFuse {
|
||||
fn init(
|
||||
&mut self,
|
||||
|
|
@ -32,14 +34,18 @@ impl Filesystem for FatFuse {
|
|||
|
||||
let Some(name) = name.to_str() else {
|
||||
// TODO: add proper handling of non-utf8 strings
|
||||
debug!("cannot convert OsStr {:?} to str", name);
|
||||
reply.error(ENOSYS);
|
||||
return;
|
||||
};
|
||||
|
||||
debug!("looking up file {} with parent ino {}", name, parent);
|
||||
|
||||
let Some(parent_inode) = self.get_inode(parent) else {
|
||||
// parent inode does not exist
|
||||
// TODO: how can we make sure this does not happed?
|
||||
// TODO: panic?
|
||||
debug!("could not find inode for parent ino {}", parent);
|
||||
reply.error(EIO);
|
||||
|
||||
return;
|
||||
|
|
@ -67,29 +73,33 @@ impl Filesystem for FatFuse {
|
|||
}) {
|
||||
Ok(dir_entry) => dir_entry,
|
||||
Err(err) => {
|
||||
debug!("error: {}", err);
|
||||
reply.error(err);
|
||||
|
||||
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 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 ino = self.next_ino();
|
||||
// let inode = Inode::new(&self.fat_fs, &dir_entry, ino, self.uid, self.gid);
|
||||
|
||||
let inode = Inode::new(&self.fat_fs, dir_entry, ino, self.uid, self.gid);
|
||||
// self.insert_inode(inode)
|
||||
// }
|
||||
// };
|
||||
|
||||
self.insert_inode(inode)
|
||||
}
|
||||
};
|
||||
let inode = self.get_or_make_inode_by_dir_entry(&dir_entry);
|
||||
|
||||
let attr = inode.file_attr();
|
||||
let generation = inode.generation();
|
||||
|
||||
reply.entry(&Duration::from_secs(1), &attr, generation as u64);
|
||||
reply.entry(&TTL, &attr, generation as u64);
|
||||
|
||||
inode.refcount_inc();
|
||||
}
|
||||
|
||||
fn forget(&mut self, _req: &fuser::Request<'_>, ino: u64, nlookup: u64) {
|
||||
|
|
@ -99,18 +109,9 @@ impl Filesystem for FatFuse {
|
|||
return;
|
||||
};
|
||||
|
||||
let ref_count = inode.ref_count_mut();
|
||||
// *ref_count = ref_count.saturating_sub(nlookup);
|
||||
|
||||
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 {
|
||||
if inode.refcount_dec(nlookup) == 0 {
|
||||
// no more references, drop inode
|
||||
self.drop_inode(ino);
|
||||
}
|
||||
|
|
@ -123,8 +124,28 @@ impl Filesystem for FatFuse {
|
|||
fh: Option<u64>,
|
||||
reply: fuser::ReplyAttr,
|
||||
) {
|
||||
warn!("[Not Implemented] getattr(ino: {:#x?}, fh: {:#x?})", ino, fh);
|
||||
reply.error(ENOSYS);
|
||||
// warn!("[Not Implemented] getattr(ino: {:#x?}, fh: {:#x?})", ino, fh);
|
||||
// reply.error(ENOSYS);
|
||||
|
||||
let inode = if let Some(fh) = fh {
|
||||
let Some(inode) = self.get_inode_by_fh(fh) else {
|
||||
reply.error(EIO);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
inode
|
||||
} else if let Some(inode) = self.get_inode(ino) {
|
||||
inode
|
||||
} else {
|
||||
reply.error(EIO);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
let attr = inode.file_attr();
|
||||
|
||||
reply.attr(&TTL, &attr);
|
||||
}
|
||||
|
||||
fn setattr(
|
||||
|
|
@ -214,21 +235,6 @@ impl Filesystem for FatFuse {
|
|||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn symlink(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
parent: u64,
|
||||
link_name: &std::ffi::OsStr,
|
||||
target: &std::path::Path,
|
||||
reply: fuser::ReplyEntry,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] symlink(parent: {:#x?}, link_name: {:?}, target: {:?})",
|
||||
parent, link_name, target,
|
||||
);
|
||||
reply.error(EPERM);
|
||||
}
|
||||
|
||||
fn rename(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
|
|
@ -247,23 +253,19 @@ impl Filesystem for FatFuse {
|
|||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn link(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
newparent: u64,
|
||||
newname: &std::ffi::OsStr,
|
||||
reply: fuser::ReplyEntry,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] link(ino: {:#x?}, newparent: {:#x?}, newname: {:?})",
|
||||
ino, newparent, newname
|
||||
);
|
||||
reply.error(EPERM);
|
||||
fn open(&mut self, _req: &fuser::Request<'_>, ino: u64, _flags: i32, reply: fuser::ReplyOpen) {
|
||||
if !self.inode_table.contains_key(&ino) {
|
||||
reply.error(EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
fn open(&mut self, _req: &fuser::Request<'_>, _ino: u64, _flags: i32, reply: fuser::ReplyOpen) {
|
||||
reply.opened(0, 0);
|
||||
let fh = self.next_fh();
|
||||
|
||||
if let Some(old_ino) = self.ino_by_fh.insert(fh, ino) {
|
||||
debug!("fh {} was associated with ino {}, now with ino {}", fh, old_ino, ino);
|
||||
}
|
||||
|
||||
reply.opened(fh, 0);
|
||||
}
|
||||
|
||||
fn read(
|
||||
|
|
@ -354,11 +356,17 @@ impl Filesystem for FatFuse {
|
|||
fn opendir(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
_ino: u64,
|
||||
ino: u64,
|
||||
_flags: i32,
|
||||
reply: fuser::ReplyOpen,
|
||||
) {
|
||||
reply.opened(0, 0);
|
||||
let fh = self.next_fh();
|
||||
|
||||
if let Some(old_ino) = self.ino_by_fh.insert(fh, ino) {
|
||||
debug!("fh {} was already associated with ino {}, now with ino {}", fh, old_ino, ino);
|
||||
}
|
||||
|
||||
reply.opened(fh, 0);
|
||||
}
|
||||
|
||||
fn readdir(
|
||||
|
|
@ -367,10 +375,79 @@ impl Filesystem for FatFuse {
|
|||
ino: u64,
|
||||
fh: u64,
|
||||
offset: i64,
|
||||
reply: fuser::ReplyDirectory,
|
||||
mut reply: fuser::ReplyDirectory,
|
||||
) {
|
||||
warn!("[Not Implemented] readdir(ino: {:#x?}, fh: {}, offset: {})", ino, fh, offset);
|
||||
reply.error(ENOSYS);
|
||||
let Ok(mut offset): Result<usize, _> = offset.try_into() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(inode) = self.get_inode_by_fh(fh) else {
|
||||
debug!("could not find inode accociated with fh {} (ino: {})", fh, ino);
|
||||
|
||||
reply.error(EINVAL);
|
||||
return;
|
||||
};
|
||||
|
||||
if inode.ino() != ino {
|
||||
debug!(
|
||||
"ino {} of inode associated with fh {} does not match given ino {}",
|
||||
inode.ino(),
|
||||
fh,
|
||||
ino
|
||||
);
|
||||
|
||||
reply.error(EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut _next_idx = 1;
|
||||
let mut next_offset = || {
|
||||
let next_idx = _next_idx;
|
||||
_next_idx += 1;
|
||||
next_idx
|
||||
};
|
||||
|
||||
if inode.is_root() {
|
||||
if offset == 0 {
|
||||
debug!("adding . to root dir");
|
||||
if reply.add(1, next_offset(), FileType::Directory, ".") {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
offset -= 1;
|
||||
}
|
||||
|
||||
if offset == 0 {
|
||||
debug!("adding .. to root dir");
|
||||
if reply.add(1, next_offset(), FileType::Directory, "..") {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
offset -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
let Ok(dir_iter) = inode.dir_iter(&self.fat_fs) else {
|
||||
reply.error(ENOTDIR);
|
||||
return;
|
||||
};
|
||||
|
||||
// need to drop dir_iter here so we can borrow self mut again
|
||||
// also skip over `offset` entries
|
||||
let dirs: Vec<DirEntry> = dir_iter.skip(offset).collect();
|
||||
|
||||
for dir_entry in dirs {
|
||||
let name = dir_entry.name_string().unwrap_or("<invalid>".into());
|
||||
|
||||
let inode: &Inode = self.get_or_make_inode_by_dir_entry(&dir_entry);
|
||||
|
||||
debug!("adding entry {} (ino: {})", name, inode.ino());
|
||||
if reply.add(ino, next_offset(), inode.kind().into(), name) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
reply.ok();
|
||||
}
|
||||
|
||||
fn readdirplus(
|
||||
|
|
@ -391,11 +468,34 @@ impl Filesystem for FatFuse {
|
|||
fn releasedir(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
_ino: u64,
|
||||
_fh: u64,
|
||||
ino: u64,
|
||||
fh: u64,
|
||||
_flags: i32,
|
||||
reply: fuser::ReplyEmpty,
|
||||
) {
|
||||
let Some(ino) = self.ino_by_fh.remove(&fh) else {
|
||||
debug!("can't find inode {} by fh {}", ino, fh);
|
||||
|
||||
reply.error(EIO);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(inode) = self.inode_table.get(&ino) else {
|
||||
debug!("ino {} not associated with an inode", ino);
|
||||
|
||||
reply.ok();
|
||||
return;
|
||||
};
|
||||
|
||||
if inode.ino() != ino {
|
||||
debug!(
|
||||
"inode with ino {}, associated with fh {}, does not have expected ino {}",
|
||||
inode.ino(),
|
||||
fh,
|
||||
ino
|
||||
);
|
||||
}
|
||||
|
||||
reply.ok();
|
||||
}
|
||||
|
||||
|
|
@ -418,62 +518,6 @@ impl Filesystem for FatFuse {
|
|||
reply.statfs(0, 0, 0, 0, 0, 512, 255, 0);
|
||||
}
|
||||
|
||||
fn setxattr(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
name: &std::ffi::OsStr,
|
||||
_value: &[u8],
|
||||
flags: i32,
|
||||
position: u32,
|
||||
reply: fuser::ReplyEmpty,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] setxattr(ino: {:#x?}, name: {:?}, flags: {:#x?}, position: {})",
|
||||
ino, name, flags, position
|
||||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn getxattr(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
name: &std::ffi::OsStr,
|
||||
size: u32,
|
||||
reply: fuser::ReplyXattr,
|
||||
) {
|
||||
debug!("[Not Implemented] getxattr(ino: {:#x?}, name: {:?}, size: {})", ino, name, size);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn listxattr(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
size: u32,
|
||||
reply: fuser::ReplyXattr,
|
||||
) {
|
||||
debug!("[Not Implemented] listxattr(ino: {:#x?}, size: {})", ino, size);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn removexattr(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
name: &std::ffi::OsStr,
|
||||
reply: fuser::ReplyEmpty,
|
||||
) {
|
||||
debug!("[Not Implemented] removexattr(ino: {:#x?}, name: {:?})", ino, name);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn access(&mut self, _req: &fuser::Request<'_>, ino: u64, mask: i32, reply: fuser::ReplyEmpty) {
|
||||
debug!("[Not Implemented] access(ino: {:#x?}, mask: {})", ino, mask);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn create(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
|
|
@ -492,104 +536,6 @@ impl Filesystem for FatFuse {
|
|||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn getlk(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
fh: u64,
|
||||
lock_owner: u64,
|
||||
start: u64,
|
||||
end: u64,
|
||||
typ: i32,
|
||||
pid: u32,
|
||||
reply: fuser::ReplyLock,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] getlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \
|
||||
end: {}, typ: {}, pid: {})",
|
||||
ino, fh, lock_owner, start, end, typ, pid
|
||||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn setlk(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
fh: u64,
|
||||
lock_owner: u64,
|
||||
start: u64,
|
||||
end: u64,
|
||||
typ: i32,
|
||||
pid: u32,
|
||||
sleep: bool,
|
||||
reply: fuser::ReplyEmpty,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] setlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \
|
||||
end: {}, typ: {}, pid: {}, sleep: {})",
|
||||
ino, fh, lock_owner, start, end, typ, pid, sleep
|
||||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn bmap(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
blocksize: u32,
|
||||
idx: u64,
|
||||
reply: fuser::ReplyBmap,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] bmap(ino: {:#x?}, blocksize: {}, idx: {})",
|
||||
ino, blocksize, idx,
|
||||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
fh: u64,
|
||||
flags: u32,
|
||||
cmd: u32,
|
||||
in_data: &[u8],
|
||||
out_size: u32,
|
||||
reply: fuser::ReplyIoctl,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] ioctl(ino: {:#x?}, fh: {}, flags: {}, cmd: {}, \
|
||||
in_data.len(): {}, out_size: {})",
|
||||
ino,
|
||||
fh,
|
||||
flags,
|
||||
cmd,
|
||||
in_data.len(),
|
||||
out_size,
|
||||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn fallocate(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
fh: u64,
|
||||
offset: i64,
|
||||
length: i64,
|
||||
mode: i32,
|
||||
reply: fuser::ReplyEmpty,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] fallocate(ino: {:#x?}, fh: {}, offset: {}, \
|
||||
length: {}, mode: {})",
|
||||
ino, fh, offset, length, mode
|
||||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn lseek(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
|
|
@ -605,26 +551,4 @@ impl Filesystem for FatFuse {
|
|||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
|
||||
fn copy_file_range(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino_in: u64,
|
||||
fh_in: u64,
|
||||
offset_in: i64,
|
||||
ino_out: u64,
|
||||
fh_out: u64,
|
||||
offset_out: i64,
|
||||
len: u64,
|
||||
flags: u32,
|
||||
reply: fuser::ReplyWrite,
|
||||
) {
|
||||
debug!(
|
||||
"[Not Implemented] copy_file_range(ino_in: {:#x?}, fh_in: {}, \
|
||||
offset_in: {}, ino_out: {:#x?}, fh_out: {}, offset_out: {}, \
|
||||
len: {}, flags: {})",
|
||||
ino_in, fh_in, offset_in, ino_out, fh_out, offset_out, len, flags
|
||||
);
|
||||
reply.error(ENOSYS);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use fat_bits::FatFs;
|
|||
use fat_bits::dir::DirEntry;
|
||||
use fuser::FileAttr;
|
||||
use libc::ENOTDIR;
|
||||
use log::debug;
|
||||
use rand::{Rng, SeedableRng as _};
|
||||
|
||||
thread_local! {
|
||||
|
|
@ -91,7 +92,7 @@ impl Inode {
|
|||
((secs as u32) << 16) | rand as u32
|
||||
}
|
||||
|
||||
pub fn new(fat_fs: &FatFs, dir_entry: DirEntry, ino: u64, uid: u32, gid: u32) -> Inode {
|
||||
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 = Self::new_generation();
|
||||
|
|
@ -114,6 +115,12 @@ impl Inode {
|
|||
let mtime = datetime_to_system(dir_entry.write_time());
|
||||
let crtime = datetime_to_system(dir_entry.create_time());
|
||||
|
||||
debug!(
|
||||
"creating new inode: ino: {} name: {}",
|
||||
ino,
|
||||
dir_entry.name_string().unwrap_or("<invalid>".into())
|
||||
);
|
||||
|
||||
Inode {
|
||||
ino,
|
||||
generation,
|
||||
|
|
@ -131,6 +138,28 @@ impl Inode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn root_inode(fat_fs: &FatFs, uid: u32, gid: u32) -> Inode {
|
||||
// let generation = Self::new_generation();
|
||||
|
||||
let root_cluster = fat_fs.bpb().root_cluster().unwrap_or(0);
|
||||
|
||||
Inode {
|
||||
ino: 1,
|
||||
generation: 0,
|
||||
ref_count: 0,
|
||||
size: 0,
|
||||
block_size: fat_fs.bpb().bytes_per_sector() as u32,
|
||||
kind: Kind::Dir,
|
||||
read_only: false,
|
||||
atime: SystemTime::UNIX_EPOCH,
|
||||
mtime: SystemTime::UNIX_EPOCH,
|
||||
crtime: SystemTime::UNIX_EPOCH,
|
||||
uid,
|
||||
gid,
|
||||
first_cluster: root_cluster,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ino(&self) -> u64 {
|
||||
self.ino
|
||||
}
|
||||
|
|
@ -143,14 +172,37 @@ impl Inode {
|
|||
self.ref_count
|
||||
}
|
||||
|
||||
pub fn ref_count_mut(&mut self) -> &mut u64 {
|
||||
&mut self.ref_count
|
||||
pub fn refcount_inc(&mut self) {
|
||||
self.ref_count += 1;
|
||||
}
|
||||
|
||||
pub fn refcount_dec(&mut self, n: u64) -> u64 {
|
||||
if self.ref_count < n {
|
||||
debug!(
|
||||
"inode {}: tried to decrement refcount by {}, but is only {}",
|
||||
self.ino(),
|
||||
n,
|
||||
self.ref_count
|
||||
);
|
||||
}
|
||||
|
||||
self.ref_count = self.ref_count.saturating_sub(n);
|
||||
|
||||
self.ref_count
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> Kind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
pub fn first_cluster(&self) -> u32 {
|
||||
self.first_cluster
|
||||
}
|
||||
|
||||
pub fn is_root(&self) -> bool {
|
||||
self.ino == ROOT_INO
|
||||
}
|
||||
|
||||
pub fn file_attr(&self) -> FileAttr {
|
||||
let perm = if self.read_only { 0o555 } else { 0o777 };
|
||||
|
||||
|
|
@ -180,7 +232,7 @@ impl Inode {
|
|||
return Err(ENOTDIR);
|
||||
}
|
||||
|
||||
if self.ino == ROOT_INO {
|
||||
if self.is_root() {
|
||||
// root dir
|
||||
|
||||
return Ok(fat_fs.root_dir_iter());
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::cell::RefCell;
|
|||
use std::collections::BTreeMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use fat_bits::dir::DirEntry;
|
||||
use fat_bits::{FatFs, SliceLike};
|
||||
use log::debug;
|
||||
|
||||
|
|
@ -18,31 +19,55 @@ pub struct FatFuse {
|
|||
gid: u32,
|
||||
|
||||
next_ino: u64,
|
||||
next_fd: u32,
|
||||
next_fh: u64,
|
||||
|
||||
inode_table: BTreeMap<u64, Inode>,
|
||||
|
||||
ino_by_first_cluster: BTreeMap<u32, u64>,
|
||||
ino_by_fh: BTreeMap<u64, u64>,
|
||||
}
|
||||
|
||||
impl Drop for FatFuse {
|
||||
fn drop(&mut self) {
|
||||
println!("inode_table: {}", self.inode_table.len());
|
||||
|
||||
println!("ino_by_first_cluster: {}", self.ino_by_first_cluster.len());
|
||||
for (&first_cluster, &ino) in self.ino_by_first_cluster.iter() {
|
||||
println!("{} -> {}", first_cluster, ino);
|
||||
}
|
||||
|
||||
println!("ino_by_fh: {}", self.ino_by_fh.len());
|
||||
}
|
||||
}
|
||||
|
||||
impl FatFuse {
|
||||
pub fn new(data: Rc<RefCell<dyn SliceLike>>) -> anyhow::Result<FatFuse> {
|
||||
pub fn new<S>(data: S) -> anyhow::Result<FatFuse>
|
||||
where
|
||||
S: SliceLike + Send + 'static,
|
||||
{
|
||||
let uid = unsafe { libc::getuid() };
|
||||
let gid = unsafe { libc::getgid() };
|
||||
|
||||
let fat_fs = FatFs::load(data)?;
|
||||
|
||||
// TODO: build and insert root dir inode
|
||||
|
||||
Ok(FatFuse {
|
||||
let mut fat_fuse = FatFuse {
|
||||
fat_fs,
|
||||
uid,
|
||||
gid,
|
||||
next_ino: 2, // 0 is reserved and 1 is root
|
||||
next_fd: 0,
|
||||
next_fh: 0,
|
||||
inode_table: BTreeMap::new(),
|
||||
ino_by_first_cluster: BTreeMap::new(),
|
||||
})
|
||||
ino_by_fh: BTreeMap::new(),
|
||||
};
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
fn next_ino(&mut self) -> u64 {
|
||||
|
|
@ -55,6 +80,16 @@ impl FatFuse {
|
|||
ino
|
||||
}
|
||||
|
||||
fn next_fh(&mut self) -> u64 {
|
||||
let fh = self.next_fh;
|
||||
|
||||
assert!(!self.ino_by_fh.contains_key(&fh));
|
||||
|
||||
self.next_fh += 1;
|
||||
|
||||
fh
|
||||
}
|
||||
|
||||
fn insert_inode(&mut self, inode: Inode) -> &mut Inode {
|
||||
let ino = inode.ino();
|
||||
let generation = inode.generation();
|
||||
|
|
@ -78,8 +113,6 @@ impl FatFuse {
|
|||
}
|
||||
};
|
||||
|
||||
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
|
||||
|
|
@ -89,14 +122,18 @@ impl FatFuse {
|
|||
debug!("ejected inode {} {}", old_inode.ino(), old_inode.generation());
|
||||
}
|
||||
|
||||
if let Some(old_ino) = old_ino {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
new_inode
|
||||
}
|
||||
|
||||
fn drop_inode(&mut self, ino: u64) {
|
||||
debug!("dropping ino {}", ino);
|
||||
|
||||
let Some(inode) = self.inode_table.remove(&ino) else {
|
||||
debug!("tried to drop inode with ino {}, but was not in table", ino);
|
||||
|
||||
|
|
@ -105,6 +142,7 @@ impl FatFuse {
|
|||
|
||||
let first_cluster = inode.first_cluster();
|
||||
|
||||
if first_cluster != 0 {
|
||||
let entry = self.ino_by_first_cluster.entry(first_cluster);
|
||||
|
||||
match entry {
|
||||
|
|
@ -127,8 +165,7 @@ impl FatFuse {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_inode(&self, ino: u64) -> Option<&Inode> {
|
||||
|
|
@ -139,7 +176,31 @@ impl FatFuse {
|
|||
self.inode_table.get_mut(&ino)
|
||||
}
|
||||
|
||||
fn get_or_make_inode_by_dir_entry(&mut self, dir_entry: &DirEntry) -> &mut Inode {
|
||||
if self
|
||||
.get_inode_by_first_cluster_mut(dir_entry.first_cluster())
|
||||
.is_some()
|
||||
{
|
||||
return self
|
||||
.get_inode_by_first_cluster_mut(dir_entry.first_cluster())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
pub fn get_inode_by_first_cluster(&self, first_cluster: u32) -> Option<&Inode> {
|
||||
if first_cluster == 0 {
|
||||
debug!("trying to get inode by first cluster 0");
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
let ino = self.ino_by_first_cluster.get(&first_cluster)?;
|
||||
|
||||
if let Some(inode) = self.inode_table.get(ino) {
|
||||
|
|
@ -155,6 +216,12 @@ impl FatFuse {
|
|||
}
|
||||
|
||||
pub fn get_inode_by_first_cluster_mut(&mut self, first_cluster: u32) -> Option<&mut Inode> {
|
||||
if first_cluster == 0 {
|
||||
debug!("trying to get inode by first cluster 0");
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
let ino = self.ino_by_first_cluster.get(&first_cluster)?;
|
||||
|
||||
if let Some(inode) = self.inode_table.get_mut(ino) {
|
||||
|
|
@ -168,4 +235,28 @@ impl FatFuse {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_inode_by_fh(&self, fh: u64) -> Option<&Inode> {
|
||||
let ino = self.ino_by_fh.get(&fh)?;
|
||||
|
||||
if let Some(inode) = self.inode_table.get(ino) {
|
||||
Some(inode)
|
||||
} else {
|
||||
debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_inode_by_fh_mut(&mut self, fh: u64) -> Option<&mut Inode> {
|
||||
let ino = self.ino_by_fh.get(&fh)?;
|
||||
|
||||
if let Some(inode) = self.inode_table.get_mut(ino) {
|
||||
Some(inode)
|
||||
} else {
|
||||
debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.98"
|
||||
ctrlc = "3.4.7"
|
||||
env_logger = "0.11.8"
|
||||
fat-fuse = { version = "0.1.0", path = "../fat-fuse" }
|
||||
fuser = "0.15.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use fat_fuse::FatFuse;
|
||||
use fuser::MountOption;
|
||||
|
|
@ -10,12 +9,13 @@ fn main() -> anyhow::Result<()> {
|
|||
|
||||
let mut args = std::env::args();
|
||||
|
||||
let _prog_name = args.next().unwrap();
|
||||
let path = args.next().ok_or(anyhow::anyhow!("missing fs path"))?;
|
||||
let mountpoint = args.next().ok_or(anyhow::anyhow!("missing mount point"))?;
|
||||
|
||||
let file = File::open(path)?;
|
||||
|
||||
let fat_fuse = FatFuse::new(Rc::new(RefCell::new(file)))?;
|
||||
let fat_fuse = FatFuse::new(file)?;
|
||||
|
||||
let options = vec![
|
||||
MountOption::RO,
|
||||
|
|
@ -23,7 +23,18 @@ fn main() -> anyhow::Result<()> {
|
|||
MountOption::AutoUnmount,
|
||||
];
|
||||
|
||||
fuser::mount2(fat_fuse, mountpoint, &options).unwrap();
|
||||
let (tx, rx) = channel();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
tx.send(()).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let handle = fuser::spawn_mount2(fat_fuse, mountpoint, &options)?;
|
||||
|
||||
rx.recv().unwrap();
|
||||
|
||||
println!("done");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue