diff --git a/fat-bits/src/iter.rs b/fat-bits/src/iter.rs index 8a6f775..e5f6eb4 100644 --- a/fat-bits/src/iter.rs +++ b/fat-bits/src/iter.rs @@ -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 { return false; }; @@ -37,12 +37,33 @@ impl<'a> ClusterChainReader<'a> { 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 { if self.sub_slice.is_empty() { - if !self.next_cluster() { + if !self.move_to_next_cluster() { return Ok(0); } } diff --git a/fat-bits/src/lib.rs b/fat-bits/src/lib.rs index bd50960..3c121cd 100644 --- a/fat-bits/src/lib.rs +++ b/fat-bits/src/lib.rs @@ -11,7 +11,7 @@ mod datetime; pub mod dir; pub mod fat; pub mod fs_info; -mod iter; +pub mod iter; mod subslice; mod utils; @@ -240,7 +240,7 @@ impl FatFs { 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) } @@ -274,4 +274,10 @@ impl FatFs { 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) + } } diff --git a/fat-bits/src/subslice.rs b/fat-bits/src/subslice.rs index a710974..3f388f4 100644 --- a/fat-bits/src/subslice.rs +++ b/fat-bits/src/subslice.rs @@ -104,16 +104,21 @@ impl SubSlice<'_> { self.fat_fs } - pub fn fat_fs_mut(&self) -> &FatFs { - self.fat_fs - } - pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn len(&self) -> usize { 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> { diff --git a/fat-fuse/src/fuse.rs b/fat-fuse/src/fuse.rs index adb0cb5..ed48352 100644 --- a/fat-fuse/src/fuse.rs +++ b/fat-fuse/src/fuse.rs @@ -1,11 +1,12 @@ use std::ffi::c_int; +use std::io::Read; use std::rc::Rc; use std::time::Duration; use fat_bits::dir::DirEntry; use fuser::{FileType, Filesystem}; -use libc::{EINVAL, EIO, ENOENT, ENOSYS, ENOTDIR}; -use log::{debug, warn}; +use libc::{EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, ENOTDIR}; +use log::{debug, error}; 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!("opened inode {}: fh {}", ino, fh); + reply.opened(fh, 0); } @@ -291,16 +294,74 @@ impl Filesystem for FatFuse { fh: u64, offset: i64, size: u32, - flags: i32, - lock_owner: Option, + _flags: i32, + _lock_owner: Option, reply: fuser::ReplyData, ) { - warn!( - "[Not Implemented] read(ino: {:#x?}, fh: {}, offset: {}, size: {}, \ - flags: {:#x?}, lock_owner: {:?})", - ino, fh, offset, size, flags, lock_owner - ); - reply.error(ENOSYS); + // warn!( + // "[Not Implemented] read(ino: {:#x?}, fh: {}, offset: {}, size: {}, \ + // flags: {:#x?}, lock_owner: {:?})", + // ino, fh, offset, size, flags, lock_owner + // ); + // 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( @@ -347,13 +408,27 @@ impl Filesystem for FatFuse { fn release( &mut self, _req: &fuser::Request<'_>, - _ino: u64, - _fh: u64, + ino: u64, + fh: u64, _flags: i32, _lock_owner: Option, _flush: bool, 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(); } diff --git a/fat-fuse/src/inode.rs b/fat-fuse/src/inode.rs index 1ad6c23..c8da0b1 100644 --- a/fat-fuse/src/inode.rs +++ b/fat-fuse/src/inode.rs @@ -5,8 +5,9 @@ use std::time::SystemTime; use chrono::{NaiveDateTime, NaiveTime}; use fat_bits::FatFs; use fat_bits::dir::DirEntry; +use fat_bits::iter::ClusterChainReader; use fuser::FileAttr; -use libc::ENOTDIR; +use libc::{EISDIR, ENOTDIR}; use log::debug; use rand::{Rng, SeedableRng as _}; @@ -232,6 +233,14 @@ impl Inode { 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 { self.first_cluster } @@ -281,4 +290,12 @@ impl Inode { Ok(fat_fs.dir_iter(self.first_cluster)) } + + pub fn file_reader<'a>(&'a self, fat_fs: &'a FatFs) -> Result, i32> { + if self.is_dir() { + return Err(EISDIR); + } + + Ok(fat_fs.file_reader(self.first_cluster())) + } } diff --git a/fat-mount/src/main.rs b/fat-mount/src/main.rs index c63f238..c7ad1c9 100644 --- a/fat-mount/src/main.rs +++ b/fat-mount/src/main.rs @@ -30,9 +30,11 @@ fn main() -> anyhow::Result<()> { }) .unwrap(); - let _handle = fuser::spawn_mount2(fat_fuse, mountpoint, &options)?; + let handle = fuser::spawn_mount2(fat_fuse, mountpoint, &options)?; rx.recv().unwrap(); + handle.join(); + Ok(()) }