implemented read

This commit is contained in:
Moritz Gmeiner 2025-08-01 01:08:48 +02:00
commit 2b01b9ff0e
6 changed files with 149 additions and 23 deletions

View file

@ -22,7 +22,7 @@ impl<'a> ClusterChainReader<'a> {
} }
} }
fn next_cluster(&mut self) -> bool { fn move_to_next_cluster(&mut self) -> bool {
let Some(next_cluster) = self.next_cluster else { let Some(next_cluster) = self.next_cluster else {
return false; return false;
}; };
@ -37,12 +37,33 @@ impl<'a> ClusterChainReader<'a> {
true true
} }
pub fn skip(&mut self, n: u64) -> u64 {
let mut bytes_to_skip = n;
while bytes_to_skip > self.sub_slice.len() as u64 {
bytes_to_skip -= self.sub_slice.len() as u64;
if !self.move_to_next_cluster() {
// ran out of bytes to seek
return n - bytes_to_skip;
}
}
if bytes_to_skip != 0 {
bytes_to_skip -= self.sub_slice.skip(bytes_to_skip as usize) as u64;
}
// n should absolutely be zero here
assert_eq!(bytes_to_skip, 0);
n
}
} }
impl<'a> Read for ClusterChainReader<'a> { impl Read for ClusterChainReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.sub_slice.is_empty() { if self.sub_slice.is_empty() {
if !self.next_cluster() { if !self.move_to_next_cluster() {
return Ok(0); return Ok(0);
} }
} }

View file

@ -11,7 +11,7 @@ mod datetime;
pub mod dir; pub mod dir;
pub mod fat; pub mod fat;
pub mod fs_info; pub mod fs_info;
mod iter; pub mod iter;
mod subslice; mod subslice;
mod utils; mod utils;
@ -240,7 +240,7 @@ impl FatFs {
Ok(data) Ok(data)
} }
fn chain_reader(&self, first_cluster: u32) -> impl Read { fn chain_reader(&'_ self, first_cluster: u32) -> iter::ClusterChainReader<'_> {
iter::ClusterChainReader::new(self, first_cluster) iter::ClusterChainReader::new(self, first_cluster)
} }
@ -274,4 +274,10 @@ impl FatFs {
DirIter::new(Box::new(cluster_iter)) DirIter::new(Box::new(cluster_iter))
} }
pub fn file_reader(&self, first_cluster: u32) -> iter::ClusterChainReader<'_> {
assert!(first_cluster >= 2);
self.chain_reader(first_cluster)
}
} }

View file

@ -104,16 +104,21 @@ impl SubSlice<'_> {
self.fat_fs self.fat_fs
} }
pub fn fat_fs_mut(&self) -> &FatFs {
self.fat_fs
}
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.len self.len
} }
pub fn skip(&mut self, n: usize) -> usize {
let n = n.min(self.len());
self.offset += n as u64;
self.len -= n;
n
}
} }
impl<'a> SubSlice<'a> { impl<'a> SubSlice<'a> {

View file

@ -1,11 +1,12 @@
use std::ffi::c_int; use std::ffi::c_int;
use std::io::Read;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use fat_bits::dir::DirEntry; use fat_bits::dir::DirEntry;
use fuser::{FileType, Filesystem}; use fuser::{FileType, Filesystem};
use libc::{EINVAL, EIO, ENOENT, ENOSYS, ENOTDIR}; use libc::{EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, ENOTDIR};
use log::{debug, warn}; use log::{debug, error};
use crate::{FatFuse, Inode}; use crate::{FatFuse, Inode};
@ -281,6 +282,8 @@ impl Filesystem for FatFuse {
debug!("fh {} was associated with ino {}, now with ino {}", fh, old_ino, ino); debug!("fh {} was associated with ino {}, now with ino {}", fh, old_ino, ino);
} }
debug!("opened inode {}: fh {}", ino, fh);
reply.opened(fh, 0); reply.opened(fh, 0);
} }
@ -291,16 +294,74 @@ impl Filesystem for FatFuse {
fh: u64, fh: u64,
offset: i64, offset: i64,
size: u32, size: u32,
flags: i32, _flags: i32,
lock_owner: Option<u64>, _lock_owner: Option<u64>,
reply: fuser::ReplyData, reply: fuser::ReplyData,
) { ) {
warn!( // warn!(
"[Not Implemented] read(ino: {:#x?}, fh: {}, offset: {}, size: {}, \ // "[Not Implemented] read(ino: {:#x?}, fh: {}, offset: {}, size: {}, \
flags: {:#x?}, lock_owner: {:?})", // flags: {:#x?}, lock_owner: {:?})",
ino, fh, offset, size, flags, lock_owner // ino, fh, offset, size, flags, lock_owner
); // );
reply.error(ENOSYS); // reply.error(ENOSYS);
if offset < 0 {
debug!("tried to read with negative offset {offset}");
reply.error(EINVAL);
return;
}
let offset = offset as u64;
let Some(inode) = self.get_inode_by_fh(fh) else {
debug!("fh {fh} is not associated by any inode");
reply.error(EBADF);
return;
};
if inode.ino() != ino {
debug!("fh {fh} is associated with inode {} instead of {ino}", inode.ino());
reply.error(EIO);
return;
}
if !inode.is_file() {
debug!("tried to use read on directory {ino}");
reply.error(EISDIR);
return;
}
let mut reader = match inode.file_reader(&self.fat_fs) {
Ok(reader) => reader,
Err(err) => {
reply.error(err);
return;
}
};
if reader.skip(offset) != offset {
// reader is exhausted, bail
reply.data(&[]);
return;
}
let mut buf = vec![0; size as usize];
let n = match reader.read(&mut buf) {
Ok(n) => n,
Err(err) => {
error!("error while reading: {err}");
reply.error(EIO);
return;
}
};
reply.data(&buf[..n]);
} }
fn write( fn write(
@ -347,13 +408,27 @@ impl Filesystem for FatFuse {
fn release( fn release(
&mut self, &mut self,
_req: &fuser::Request<'_>, _req: &fuser::Request<'_>,
_ino: u64, ino: u64,
_fh: u64, fh: u64,
_flags: i32, _flags: i32,
_lock_owner: Option<u64>, _lock_owner: Option<u64>,
_flush: bool, _flush: bool,
reply: fuser::ReplyEmpty, reply: fuser::ReplyEmpty,
) { ) {
let Some(found_ino) = self.ino_by_fh.remove(&fh) else {
debug!("tried to release fh {fh} with ino {ino}, but no ino was found in mapping");
reply.error(EINVAL);
return;
};
if found_ino != ino {
debug!("tried to release fh {fh} with ino {ino}, but found ino is {found_ino} instead");
reply.error(EIO);
return;
}
reply.ok(); reply.ok();
} }

View file

@ -5,8 +5,9 @@ use std::time::SystemTime;
use chrono::{NaiveDateTime, NaiveTime}; use chrono::{NaiveDateTime, NaiveTime};
use fat_bits::FatFs; use fat_bits::FatFs;
use fat_bits::dir::DirEntry; use fat_bits::dir::DirEntry;
use fat_bits::iter::ClusterChainReader;
use fuser::FileAttr; use fuser::FileAttr;
use libc::ENOTDIR; use libc::{EISDIR, ENOTDIR};
use log::debug; use log::debug;
use rand::{Rng, SeedableRng as _}; use rand::{Rng, SeedableRng as _};
@ -232,6 +233,14 @@ impl Inode {
self.kind self.kind
} }
pub fn is_file(&self) -> bool {
self.kind == Kind::File
}
pub fn is_dir(&self) -> bool {
self.kind == Kind::Dir
}
pub fn first_cluster(&self) -> u32 { pub fn first_cluster(&self) -> u32 {
self.first_cluster self.first_cluster
} }
@ -281,4 +290,12 @@ impl Inode {
Ok(fat_fs.dir_iter(self.first_cluster)) Ok(fat_fs.dir_iter(self.first_cluster))
} }
pub fn file_reader<'a>(&'a self, fat_fs: &'a FatFs) -> Result<ClusterChainReader<'a>, i32> {
if self.is_dir() {
return Err(EISDIR);
}
Ok(fat_fs.file_reader(self.first_cluster()))
}
} }

View file

@ -30,9 +30,11 @@ fn main() -> anyhow::Result<()> {
}) })
.unwrap(); .unwrap();
let _handle = fuser::spawn_mount2(fat_fuse, mountpoint, &options)?; let handle = fuser::spawn_mount2(fat_fuse, mountpoint, &options)?;
rx.recv().unwrap(); rx.recv().unwrap();
handle.join();
Ok(()) Ok(())
} }