implemented read
This commit is contained in:
parent
e1d458a384
commit
2b01b9ff0e
6 changed files with 149 additions and 23 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue