enter the Rc-RefCell madness
This commit is contained in:
parent
2b01b9ff0e
commit
ea3e2a76c4
12 changed files with 619 additions and 353 deletions
|
|
@ -46,6 +46,10 @@ impl Date {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn repr(&self) -> u16 {
|
||||||
|
self.repr
|
||||||
|
}
|
||||||
|
|
||||||
pub fn day(&self) -> u8 {
|
pub fn day(&self) -> u8 {
|
||||||
(self.repr & 0x1F) as u8
|
(self.repr & 0x1F) as u8
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +101,10 @@ impl Time {
|
||||||
Time::from_seconds_minutes_hours(seconds, time.minute() as u8, time.hour() as u8)
|
Time::from_seconds_minutes_hours(seconds, time.minute() as u8, time.hour() as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn repr(&self) -> u16 {
|
||||||
|
self.repr
|
||||||
|
}
|
||||||
|
|
||||||
pub fn second(&self) -> u8 {
|
pub fn second(&self) -> u8 {
|
||||||
2 * (self.repr & 0x1F) as u8
|
2 * (self.repr & 0x1F) as u8
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::io::Read;
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use chrono::{NaiveDate, NaiveDateTime, TimeDelta};
|
use chrono::{NaiveDate, NaiveDateTime, TimeDelta};
|
||||||
|
|
@ -199,6 +199,58 @@ impl DirEntry {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, mut writer: impl Write) -> std::io::Result<()> {
|
||||||
|
let mut buf = [0; 32];
|
||||||
|
|
||||||
|
let mut name = self.name();
|
||||||
|
|
||||||
|
if name[0] == b'.' && self.is_hidden() {
|
||||||
|
name = &name[1..];
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((idx, _)) = name
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.find(|&(_n, u)| u == b'.')
|
||||||
|
{
|
||||||
|
let (stem, ext) = name.split_at(idx);
|
||||||
|
|
||||||
|
buf[..8][..stem.len()].copy_from_slice(stem);
|
||||||
|
buf[8..][..ext.len()].copy_from_slice(ext);
|
||||||
|
|
||||||
|
// (stem, Some(ext))
|
||||||
|
} else {
|
||||||
|
// all stem, no ext
|
||||||
|
buf[..8][..name.len()].copy_from_slice(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[11] = self.attr().bits();
|
||||||
|
|
||||||
|
buf[12] = 0;
|
||||||
|
|
||||||
|
buf[13] = self.create_time_tenths;
|
||||||
|
buf[13..15].copy_from_slice(&self.create_time.repr().to_le_bytes());
|
||||||
|
|
||||||
|
buf[16..18].copy_from_slice(&self.create_date.repr().to_le_bytes());
|
||||||
|
|
||||||
|
buf[18..20].copy_from_slice(&self.last_access_date.repr().to_le_bytes());
|
||||||
|
|
||||||
|
buf[20..22].copy_from_slice(&((self.first_cluster() >> 16) as u16).to_le_bytes());
|
||||||
|
|
||||||
|
buf[22..24].copy_from_slice(&self.write_time.repr().to_le_bytes());
|
||||||
|
buf[24..26].copy_from_slice(&self.write_date.repr().to_le_bytes());
|
||||||
|
|
||||||
|
buf[26..28].copy_from_slice(&(self.first_cluster as u16).to_le_bytes());
|
||||||
|
|
||||||
|
buf[28..].copy_from_slice(&self.file_size.to_le_bytes());
|
||||||
|
|
||||||
|
writer.write_all(&buf)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// indicates this DirEntry is empty
|
/// indicates this DirEntry is empty
|
||||||
///
|
///
|
||||||
/// can be either simply empty (0xe5) or the sentinel (0x00) that indicates that all following
|
/// can be either simply empty (0xe5) or the sentinel (0x00) that indicates that all following
|
||||||
|
|
@ -247,7 +299,13 @@ impl DirEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &[u8] {
|
pub fn name(&self) -> &[u8] {
|
||||||
&self.name
|
let mut name: &[u8] = &self.name;
|
||||||
|
|
||||||
|
while let Some(&0) = name.last() {
|
||||||
|
name = &name[..name.len() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stem(&self) -> &[u8] {
|
pub fn stem(&self) -> &[u8] {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
use std::io::Write as _;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
use crate::FatType;
|
use crate::FatType;
|
||||||
|
use crate::subslice::SubSliceMut;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum FatError {
|
pub enum FatError {
|
||||||
|
|
@ -19,26 +21,29 @@ pub enum FatError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait Fatty {
|
pub trait FatOps {
|
||||||
// get the next cluster
|
// get the next cluster
|
||||||
// assumes the cluster is valid, i.e. allocated
|
// assumes the cluster is valid, i.e. allocated
|
||||||
fn get_entry(&self, cluster: u32) -> u32;
|
fn get_entry(&self, cluster: u32) -> u32;
|
||||||
|
fn set_entry(&mut self, cluster: u32, entry: u32);
|
||||||
|
|
||||||
fn get_valid_clusters(&self) -> RangeInclusive<u32>;
|
fn valid_clusters(&self) -> RangeInclusive<u32>;
|
||||||
fn get_reserved_clusters(&self) -> RangeInclusive<u32>;
|
fn reserved_clusters(&self) -> RangeInclusive<u32>;
|
||||||
fn get_defective_cluster(&self) -> u32;
|
fn defective_cluster(&self) -> u32;
|
||||||
fn get_reserved_eof_clusters(&self) -> RangeInclusive<u32>;
|
fn reserved_eof_clusters(&self) -> RangeInclusive<u32>;
|
||||||
fn get_eof_cluster(&self) -> u32;
|
fn eof_cluster(&self) -> u32;
|
||||||
|
|
||||||
fn count_free_clusters(&self) -> usize {
|
fn count_free_clusters(&self) -> usize {
|
||||||
self.get_valid_clusters()
|
self.valid_clusters()
|
||||||
.map(|cluster| self.get_entry(cluster))
|
.map(|cluster| self.get_entry(cluster))
|
||||||
.filter(|&entry| entry == 0)
|
.filter(|&entry| entry == 0)
|
||||||
.count()
|
.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_to_disk(&self, sub_slice: SubSliceMut) -> std::io::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(Fatty)]
|
#[enum_dispatch(FatOps)]
|
||||||
pub enum Fat {
|
pub enum Fat {
|
||||||
Fat12(Fat12),
|
Fat12(Fat12),
|
||||||
Fat16(Fat16),
|
Fat16(Fat16),
|
||||||
|
|
@ -70,18 +75,18 @@ impl Fat {
|
||||||
return Err(FatError::FreeCluster);
|
return Err(FatError::FreeCluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.get_reserved_clusters().contains(&cluster) {
|
if self.reserved_clusters().contains(&cluster) {
|
||||||
// can't get next cluster for reserved cluster
|
// can't get next cluster for reserved cluster
|
||||||
return Err(FatError::ReservedCluster(cluster));
|
return Err(FatError::ReservedCluster(cluster));
|
||||||
}
|
}
|
||||||
|
|
||||||
// defective cluster
|
// defective cluster
|
||||||
if cluster == self.get_defective_cluster() {
|
if cluster == self.defective_cluster() {
|
||||||
// can't get next cluster for defective cluster
|
// can't get next cluster for defective cluster
|
||||||
return Err(FatError::DefectiveCluster);
|
return Err(FatError::DefectiveCluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.get_reserved_eof_clusters().contains(&cluster) {
|
if self.reserved_eof_clusters().contains(&cluster) {
|
||||||
// Reserved and should not be used. May be interpreted as an allocated cluster and the
|
// Reserved and should not be used. May be interpreted as an allocated cluster and the
|
||||||
// final cluster in the file (indicating end-of-file condition).
|
// final cluster in the file (indicating end-of-file condition).
|
||||||
//
|
//
|
||||||
|
|
@ -94,12 +99,12 @@ impl Fat {
|
||||||
let entry = self.get_entry(cluster);
|
let entry = self.get_entry(cluster);
|
||||||
|
|
||||||
// interpret second reserved block as EOF here
|
// interpret second reserved block as EOF here
|
||||||
if entry == self.get_eof_cluster() || self.get_reserved_eof_clusters().contains(&entry) {
|
if entry == self.eof_cluster() || self.reserved_eof_clusters().contains(&entry) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// entry should be in the valid cluster range here; otherwise something went wrong
|
// entry should be in the valid cluster range here; otherwise something went wrong
|
||||||
if !self.get_valid_clusters().contains(&entry) {
|
if !self.valid_clusters().contains(&entry) {
|
||||||
return Err(FatError::InvalidEntry(entry));
|
return Err(FatError::InvalidEntry(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,7 +183,7 @@ impl Fat12 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fatty for Fat12 {
|
impl FatOps for Fat12 {
|
||||||
fn get_entry(&self, cluster: u32) -> u32 {
|
fn get_entry(&self, cluster: u32) -> u32 {
|
||||||
let cluster = cluster as usize;
|
let cluster = cluster as usize;
|
||||||
assert!(cluster < self.next_sectors.len());
|
assert!(cluster < self.next_sectors.len());
|
||||||
|
|
@ -186,25 +191,68 @@ impl Fatty for Fat12 {
|
||||||
self.next_sectors[cluster] as u32
|
self.next_sectors[cluster] as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_valid_clusters(&self) -> RangeInclusive<u32> {
|
fn set_entry(&mut self, cluster: u32, entry: u32) {
|
||||||
|
self.next_sectors[cluster as usize] = entry as u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_clusters(&self) -> RangeInclusive<u32> {
|
||||||
2..=self.max
|
2..=self.max
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_reserved_clusters(&self) -> RangeInclusive<u32> {
|
fn reserved_clusters(&self) -> RangeInclusive<u32> {
|
||||||
(self.max as u32 + 1)..=0xFF6
|
(self.max as u32 + 1)..=0xFF6
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_defective_cluster(&self) -> u32 {
|
fn defective_cluster(&self) -> u32 {
|
||||||
0xFF7
|
0xFF7
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_reserved_eof_clusters(&self) -> RangeInclusive<u32> {
|
fn reserved_eof_clusters(&self) -> RangeInclusive<u32> {
|
||||||
0xFF8..=0xFFE
|
0xFF8..=0xFFE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_eof_cluster(&self) -> u32 {
|
fn eof_cluster(&self) -> u32 {
|
||||||
0xFFF
|
0xFFF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_to_disk(&self, mut sub_slice: SubSliceMut) -> std::io::Result<()> {
|
||||||
|
// TODO: currently assumed FAT has even number of entries
|
||||||
|
|
||||||
|
assert_eq!(3 * sub_slice.len(), self.next_sectors.len());
|
||||||
|
|
||||||
|
let mut iter = self.next_sectors.chunks_exact(3);
|
||||||
|
|
||||||
|
let mut buf: [u8; 3];
|
||||||
|
|
||||||
|
for chunk in &mut iter {
|
||||||
|
// first (even) entry gets truncated
|
||||||
|
// let first = u16::from_le_bytes(triple[..2].try_into().unwrap()) & 0xFFF;
|
||||||
|
// second (odd) entry gets shifted
|
||||||
|
// let second = u16::from_le_bytes(triple[1..].try_into().unwrap()) >> 4;
|
||||||
|
|
||||||
|
// assert!(idx + 1 < next_sectors.len());
|
||||||
|
|
||||||
|
// next_sectors[2 * idx] = first;
|
||||||
|
// next_sectors[2 * idx + 1] = second;
|
||||||
|
|
||||||
|
// sub_slice.write_all(&entry.to_le_bytes())?;
|
||||||
|
|
||||||
|
let first = chunk[0];
|
||||||
|
let second = chunk[1];
|
||||||
|
|
||||||
|
buf = [0; 3];
|
||||||
|
|
||||||
|
// buf[..2] |= &first.to_le_bytes();
|
||||||
|
buf[0] = first.to_le_bytes()[0];
|
||||||
|
buf[1] = first.to_le_bytes()[1] | (second << 4).to_le_bytes()[0];
|
||||||
|
buf[2] = (second << 4).to_le_bytes()[1];
|
||||||
|
sub_slice.write_all(&buf)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(iter.remainder().len(), 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Fat16 {
|
pub struct Fat16 {
|
||||||
|
|
@ -262,7 +310,7 @@ impl Fat16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fatty for Fat16 {
|
impl FatOps for Fat16 {
|
||||||
fn get_entry(&self, cluster: u32) -> u32 {
|
fn get_entry(&self, cluster: u32) -> u32 {
|
||||||
let cluster = cluster as usize;
|
let cluster = cluster as usize;
|
||||||
assert!(cluster < self.next_sectors.len());
|
assert!(cluster < self.next_sectors.len());
|
||||||
|
|
@ -270,25 +318,39 @@ impl Fatty for Fat16 {
|
||||||
self.next_sectors[cluster] as u32
|
self.next_sectors[cluster] as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_valid_clusters(&self) -> RangeInclusive<u32> {
|
fn set_entry(&mut self, cluster: u32, entry: u32) {
|
||||||
|
self.next_sectors[cluster as usize] = entry as u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_clusters(&self) -> RangeInclusive<u32> {
|
||||||
2..=self.max
|
2..=self.max
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_reserved_clusters(&self) -> RangeInclusive<u32> {
|
fn reserved_clusters(&self) -> RangeInclusive<u32> {
|
||||||
(self.max as u32 + 1)..=0xFFF6
|
(self.max as u32 + 1)..=0xFFF6
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_defective_cluster(&self) -> u32 {
|
fn defective_cluster(&self) -> u32 {
|
||||||
0xFFF7
|
0xFFF7
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_reserved_eof_clusters(&self) -> RangeInclusive<u32> {
|
fn reserved_eof_clusters(&self) -> RangeInclusive<u32> {
|
||||||
0xFFF8..=0xFFFE
|
0xFFF8..=0xFFFE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_eof_cluster(&self) -> u32 {
|
fn eof_cluster(&self) -> u32 {
|
||||||
0xFFFF
|
0xFFFF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_to_disk(&self, mut sub_slice: SubSliceMut) -> std::io::Result<()> {
|
||||||
|
assert_eq!(2 * sub_slice.len(), self.next_sectors.len());
|
||||||
|
|
||||||
|
for &entry in self.next_sectors.iter() {
|
||||||
|
sub_slice.write_all(&entry.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Fat32 {
|
pub struct Fat32 {
|
||||||
|
|
@ -346,7 +408,7 @@ impl Fat32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fatty for Fat32 {
|
impl FatOps for Fat32 {
|
||||||
fn get_entry(&self, cluster: u32) -> u32 {
|
fn get_entry(&self, cluster: u32) -> u32 {
|
||||||
let cluster = cluster as usize;
|
let cluster = cluster as usize;
|
||||||
assert!(cluster < self.next_sectors.len());
|
assert!(cluster < self.next_sectors.len());
|
||||||
|
|
@ -354,23 +416,37 @@ impl Fatty for Fat32 {
|
||||||
self.next_sectors[cluster] as u32
|
self.next_sectors[cluster] as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_valid_clusters(&self) -> RangeInclusive<u32> {
|
fn set_entry(&mut self, cluster: u32, entry: u32) {
|
||||||
|
self.next_sectors[cluster as usize] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_clusters(&self) -> RangeInclusive<u32> {
|
||||||
2..=self.max
|
2..=self.max
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_reserved_clusters(&self) -> RangeInclusive<u32> {
|
fn reserved_clusters(&self) -> RangeInclusive<u32> {
|
||||||
(self.max + 1)..=0xFFFFFFF6
|
(self.max + 1)..=0xFFFFFFF6
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_defective_cluster(&self) -> u32 {
|
fn defective_cluster(&self) -> u32 {
|
||||||
0xFFFFFFF7
|
0xFFFFFFF7
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_reserved_eof_clusters(&self) -> RangeInclusive<u32> {
|
fn reserved_eof_clusters(&self) -> RangeInclusive<u32> {
|
||||||
0xFFFFFFF8..=0xFFFFFFFE
|
0xFFFFFFF8..=0xFFFFFFFE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_eof_cluster(&self) -> u32 {
|
fn eof_cluster(&self) -> u32 {
|
||||||
0xFFFFFFFF
|
0xFFFFFFFF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_to_disk(&self, mut sub_slice: SubSliceMut) -> std::io::Result<()> {
|
||||||
|
assert_eq!(4 * sub_slice.len(), self.next_sectors.len());
|
||||||
|
|
||||||
|
for &entry in self.next_sectors.iter() {
|
||||||
|
sub_slice.write_all(&entry.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use std::io::Read;
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use crate::FatFs;
|
use crate::FatFs;
|
||||||
use crate::subslice::SubSlice;
|
use crate::subslice::{SubSlice, SubSliceMut};
|
||||||
use crate::utils::replace;
|
|
||||||
|
|
||||||
pub struct ClusterChainReader<'a> {
|
pub struct ClusterChainReader<'a> {
|
||||||
sub_slice: SubSlice<'a>,
|
fat_fs: &'a FatFs,
|
||||||
|
|
||||||
|
sub_slice: SubSlice,
|
||||||
|
|
||||||
next_cluster: Option<u32>,
|
next_cluster: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
@ -17,6 +18,7 @@ impl<'a> ClusterChainReader<'a> {
|
||||||
let sub_slice = fat_fs.cluster_as_subslice(first_cluster);
|
let sub_slice = fat_fs.cluster_as_subslice(first_cluster);
|
||||||
|
|
||||||
ClusterChainReader {
|
ClusterChainReader {
|
||||||
|
fat_fs,
|
||||||
sub_slice,
|
sub_slice,
|
||||||
next_cluster,
|
next_cluster,
|
||||||
}
|
}
|
||||||
|
|
@ -27,13 +29,8 @@ impl<'a> ClusterChainReader<'a> {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
replace(&mut self.sub_slice, |sub_slice| {
|
self.next_cluster = self.fat_fs.next_cluster(next_cluster).unwrap_or(None);
|
||||||
let fat_fs = sub_slice.release();
|
self.sub_slice = self.fat_fs.cluster_as_subslice(next_cluster);
|
||||||
|
|
||||||
self.next_cluster = fat_fs.next_cluster(next_cluster).unwrap_or(None);
|
|
||||||
|
|
||||||
fat_fs.cluster_as_subslice(next_cluster)
|
|
||||||
});
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -71,3 +68,74 @@ impl Read for ClusterChainReader<'_> {
|
||||||
self.sub_slice.read(buf)
|
self.sub_slice.read(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ClusterChainWriter<'a> {
|
||||||
|
fat_fs: &'a FatFs,
|
||||||
|
|
||||||
|
sub_slice: SubSliceMut,
|
||||||
|
|
||||||
|
next_cluster: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ClusterChainWriter<'a> {
|
||||||
|
pub fn new(fat_fs: &'a FatFs, first_cluster: u32) -> ClusterChainWriter<'a> {
|
||||||
|
let next_cluster = fat_fs.next_cluster(first_cluster).unwrap_or(None);
|
||||||
|
|
||||||
|
let sub_slice = fat_fs.cluster_as_subslice_mut(first_cluster);
|
||||||
|
|
||||||
|
ClusterChainWriter {
|
||||||
|
fat_fs,
|
||||||
|
sub_slice,
|
||||||
|
next_cluster,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_to_next_cluster(&mut self) -> bool {
|
||||||
|
// TODO: should allocate a new cluster here!
|
||||||
|
let Some(next_cluster) = self.next_cluster else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next_cluster = self.fat_fs.next_cluster(next_cluster).unwrap_or(None);
|
||||||
|
self.fat_fs.cluster_as_subslice_mut(next_cluster);
|
||||||
|
|
||||||
|
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 Write for ClusterChainWriter<'_> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
if self.sub_slice.is_empty() {
|
||||||
|
if !(self.move_to_next_cluster()) {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sub_slice.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::dir::DirIter;
|
use crate::dir::DirIter;
|
||||||
use crate::fat::{FatError, Fatty};
|
use crate::fat::{FatError, FatOps};
|
||||||
use crate::subslice::{SubSlice, SubSliceMut};
|
use crate::subslice::{SubSlice, SubSliceMut};
|
||||||
|
|
||||||
pub mod bpb;
|
pub mod bpb;
|
||||||
|
|
@ -98,6 +99,16 @@ pub struct FatFs {
|
||||||
fat: fat::Fat,
|
fat: fat::Fat,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for FatFs {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "{}", self.bpb)?;
|
||||||
|
writeln!(f, "")?;
|
||||||
|
writeln!(f, "{}", self.fat)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Send for FatFs {}
|
unsafe impl Send for FatFs {}
|
||||||
|
|
||||||
impl FatFs {
|
impl FatFs {
|
||||||
|
|
@ -160,90 +171,74 @@ impl FatFs {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bpb(&self) -> &bpb::Bpb {
|
|
||||||
&self.bpb
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fat(&self) -> &fat::Fat {
|
|
||||||
&self.fat
|
|
||||||
}
|
|
||||||
|
|
||||||
/// byte offset of data cluster
|
/// byte offset of data cluster
|
||||||
pub fn data_cluster_to_offset(&self, cluster: u32) -> u64 {
|
fn data_cluster_to_offset(&self, cluster: u32) -> u64 {
|
||||||
// assert!(cluster >= 2);
|
// assert!(cluster >= 2);
|
||||||
|
|
||||||
assert!(self.fat().get_valid_clusters().contains(&cluster));
|
assert!(self.fat.valid_clusters().contains(&cluster));
|
||||||
|
|
||||||
self.data_offset + (cluster - 2) as u64 * self.bytes_per_cluster as u64
|
self.data_offset + (cluster - 2) as u64 * self.bytes_per_cluster as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn free_clusters(&self) -> usize {
|
||||||
|
self.fat.count_free_clusters()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_per_sector(&self) -> u16 {
|
||||||
|
self.bpb.bytes_per_sector()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sectors_per_cluster(&self) -> u8 {
|
||||||
|
self.bpb.sectors_per_cluster()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn root_cluster(&self) -> Option<u32> {
|
||||||
|
self.bpb.root_cluster()
|
||||||
|
}
|
||||||
|
|
||||||
/// next data cluster or None is cluster is EOF
|
/// next data cluster or None is cluster is EOF
|
||||||
///
|
///
|
||||||
/// giving an invalid cluster (free, reserved, or defective) returns an appropriate error
|
/// giving an invalid cluster (free, reserved, or defective) returns an appropriate error
|
||||||
pub fn next_cluster(&self, cluster: u32) -> Result<Option<u32>, FatError> {
|
pub fn next_cluster(&self, cluster: u32) -> Result<Option<u32>, FatError> {
|
||||||
self.fat().get_next_cluster(cluster)
|
self.fat.get_next_cluster(cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cluster_as_subslice_mut(&mut self, cluster: u32) -> SubSliceMut<'_> {
|
pub fn cluster_as_subslice_mut(&self, cluster: u32) -> SubSliceMut {
|
||||||
if cluster == 0 {
|
if cluster == 0 {
|
||||||
// for cluster 0 simply return empty subslice
|
// 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
|
// this makes things a bit easier, since cluster 0 is used as a marker that a file/dir
|
||||||
// is empty
|
// is empty
|
||||||
|
|
||||||
SubSliceMut::new(self, 0, 0);
|
SubSliceMut::new(self.inner.clone(), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.data_cluster_to_offset(cluster);
|
let offset = self.data_cluster_to_offset(cluster);
|
||||||
|
|
||||||
SubSliceMut::new(self, offset, self.bytes_per_cluster)
|
SubSliceMut::new(self.inner.clone(), offset, self.bytes_per_cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cluster_as_subslice(&self, cluster: u32) -> SubSlice<'_> {
|
pub fn cluster_as_subslice(&self, cluster: u32) -> SubSlice {
|
||||||
if cluster == 0 {
|
if cluster == 0 {
|
||||||
// for cluster 0 simply return empty subslice
|
// 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
|
// this makes things a bit easier, since cluster 0 is used as a marker that a file/dir
|
||||||
// is empty
|
// is empty
|
||||||
|
|
||||||
SubSlice::new(self, 0, 0);
|
SubSlice::new(self.inner.clone(), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.data_cluster_to_offset(cluster);
|
let offset = self.data_cluster_to_offset(cluster);
|
||||||
|
|
||||||
SubSlice::new(self, offset, self.bytes_per_cluster)
|
SubSlice::new(self.inner.clone(), offset, self.bytes_per_cluster)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn root_dir_bytes(&mut self) -> std::io::Result<Vec<u8>> {
|
|
||||||
if let Some(root_dir_offset) = self.root_dir_offset {
|
|
||||||
let mut data = Vec::new();
|
|
||||||
|
|
||||||
let mut subslice = SubSliceMut::new(self, root_dir_offset, self.root_dir_size);
|
|
||||||
|
|
||||||
subslice.read_to_end(&mut data)?;
|
|
||||||
|
|
||||||
return Ok(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cluster = self.bpb().root_cluster().unwrap();
|
|
||||||
|
|
||||||
let mut data = vec![0; self.bytes_per_cluster];
|
|
||||||
|
|
||||||
let mut inner = self.inner.borrow_mut();
|
|
||||||
|
|
||||||
inner.read_at_offset(self.data_cluster_to_offset(cluster), &mut data)?;
|
|
||||||
|
|
||||||
while let Ok(Some(next_cluster)) = self.next_cluster(cluster) {
|
|
||||||
cluster = next_cluster;
|
|
||||||
|
|
||||||
inner.read_at_offset(self.data_cluster_to_offset(cluster), &mut data)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chain_reader(&'_ self, first_cluster: u32) -> iter::ClusterChainReader<'_> {
|
fn chain_reader(&'_ self, first_cluster: u32) -> iter::ClusterChainReader<'_> {
|
||||||
iter::ClusterChainReader::new(self, first_cluster)
|
iter::ClusterChainReader::new(self, first_cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn chain_writer(&'_ self, first_cluster: u32) -> iter::ClusterChainWriter<'_> {
|
||||||
|
iter::ClusterChainWriter::new(self, first_cluster)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root_dir_iter<'a>(&'a self) -> DirIter<Box<dyn Read + 'a>> {
|
pub fn root_dir_iter<'a>(&'a self) -> DirIter<Box<dyn Read + 'a>> {
|
||||||
// Box<dyn Iterator<Item = DirEntry> + '_>
|
// Box<dyn Iterator<Item = DirEntry> + '_>
|
||||||
// TODO: maybe wrap this in another RootDirIter enum, so we don't have to Box<dyn>
|
// TODO: maybe wrap this in another RootDirIter enum, so we don't have to Box<dyn>
|
||||||
|
|
@ -251,7 +246,7 @@ impl FatFs {
|
||||||
if let Some(root_dir_offset) = self.root_dir_offset {
|
if let Some(root_dir_offset) = self.root_dir_offset {
|
||||||
// FAT12/FAT16
|
// FAT12/FAT16
|
||||||
|
|
||||||
let sub_slice = SubSlice::new(self, root_dir_offset, self.root_dir_size);
|
let sub_slice = SubSlice::new(self.inner.clone(), root_dir_offset, self.root_dir_size);
|
||||||
|
|
||||||
return DirIter::new(Box::new(sub_slice));
|
return DirIter::new(Box::new(sub_slice));
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +254,7 @@ impl FatFs {
|
||||||
// FAT32
|
// FAT32
|
||||||
|
|
||||||
// can't fail; we're in the FAT32 case
|
// can't fail; we're in the FAT32 case
|
||||||
let root_cluster = self.bpb().root_cluster().unwrap();
|
let root_cluster = self.bpb.root_cluster().unwrap();
|
||||||
|
|
||||||
let cluster_iter = iter::ClusterChainReader::new(self, root_cluster);
|
let cluster_iter = iter::ClusterChainReader::new(self, root_cluster);
|
||||||
|
|
||||||
|
|
@ -276,8 +271,16 @@ impl FatFs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_reader(&self, first_cluster: u32) -> iter::ClusterChainReader<'_> {
|
pub fn file_reader(&self, first_cluster: u32) -> iter::ClusterChainReader<'_> {
|
||||||
|
// TODO: needs to take file size into account
|
||||||
assert!(first_cluster >= 2);
|
assert!(first_cluster >= 2);
|
||||||
|
|
||||||
self.chain_reader(first_cluster)
|
self.chain_reader(first_cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_writer(&self, first_cluster: u32) -> iter::ClusterChainWriter<'_> {
|
||||||
|
// TODO: needs to take file size into account
|
||||||
|
assert!(first_cluster >= 2);
|
||||||
|
|
||||||
|
self.chain_writer(first_cluster)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::FatFs;
|
use crate::SliceLike;
|
||||||
|
|
||||||
pub struct SubSliceMut<'a> {
|
pub struct SubSliceMut {
|
||||||
fat_fs: &'a mut FatFs,
|
// fat_fs: &'a FatFs,
|
||||||
|
data: Rc<RefCell<dyn SliceLike>>,
|
||||||
|
|
||||||
offset: u64,
|
offset: u64,
|
||||||
len: usize,
|
len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for SubSliceMut<'_> {
|
impl Debug for SubSliceMut {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("SubSliceMut")
|
f.debug_struct("SubSliceMut")
|
||||||
.field("offset", &self.offset)
|
.field("offset", &self.offset)
|
||||||
|
|
@ -19,17 +22,13 @@ impl Debug for SubSliceMut<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubSliceMut<'_> {
|
impl SubSliceMut {
|
||||||
pub fn new(fat_fs: &mut FatFs, offset: u64, len: usize) -> SubSliceMut<'_> {
|
pub fn new(data: Rc<RefCell<dyn SliceLike>>, offset: u64, len: usize) -> SubSliceMut {
|
||||||
SubSliceMut {
|
SubSliceMut { data, offset, len }
|
||||||
fat_fs,
|
|
||||||
offset,
|
|
||||||
len,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubSliceMut<'_> {
|
impl<'a> SubSliceMut {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
@ -37,14 +36,22 @@ impl SubSliceMut<'_> {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn skip(&mut self, n: usize) -> usize {
|
||||||
|
let n = n.min(self.len());
|
||||||
|
|
||||||
|
self.offset += n as u64;
|
||||||
|
self.len -= n;
|
||||||
|
|
||||||
|
n
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for SubSliceMut<'_> {
|
impl Read for SubSliceMut {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
let bytes_to_read = self.len.min(buf.len());
|
let bytes_to_read = self.len.min(buf.len());
|
||||||
|
|
||||||
self.fat_fs
|
self.data
|
||||||
.inner
|
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.read_at_offset(self.offset, &mut buf[..bytes_to_read])?;
|
.read_at_offset(self.offset, &mut buf[..bytes_to_read])?;
|
||||||
|
|
||||||
|
|
@ -55,12 +62,11 @@ impl Read for SubSliceMut<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for SubSliceMut<'_> {
|
impl Write for SubSliceMut {
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
let bytes_to_write = self.len.min(buf.len());
|
let bytes_to_write = self.len.min(buf.len());
|
||||||
|
|
||||||
self.fat_fs
|
self.data
|
||||||
.inner
|
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.write_at_offset(self.offset, &buf[..bytes_to_write])?;
|
.write_at_offset(self.offset, &buf[..bytes_to_write])?;
|
||||||
|
|
||||||
|
|
@ -75,14 +81,14 @@ impl Write for SubSliceMut<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubSlice<'a> {
|
pub struct SubSlice {
|
||||||
fat_fs: &'a FatFs,
|
data: Rc<RefCell<dyn SliceLike>>,
|
||||||
|
|
||||||
offset: u64,
|
offset: u64,
|
||||||
len: usize,
|
len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for SubSlice<'_> {
|
impl Debug for SubSlice {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("SubSliceMut")
|
f.debug_struct("SubSliceMut")
|
||||||
.field("offset", &self.offset)
|
.field("offset", &self.offset)
|
||||||
|
|
@ -91,17 +97,9 @@ impl Debug for SubSlice<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubSlice<'_> {
|
impl<'a> SubSlice {
|
||||||
pub fn new(fat_fs: &FatFs, offset: u64, len: usize) -> SubSlice<'_> {
|
pub fn new(data: Rc<RefCell<dyn SliceLike>>, offset: u64, len: usize) -> SubSlice {
|
||||||
SubSlice {
|
SubSlice { data, offset, len }
|
||||||
fat_fs,
|
|
||||||
offset,
|
|
||||||
len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fat_fs(&self) -> &FatFs {
|
|
||||||
self.fat_fs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
|
|
@ -121,19 +119,11 @@ impl SubSlice<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SubSlice<'a> {
|
impl Read for SubSlice {
|
||||||
/// releases the inner &FatFs, consuming self in the process
|
|
||||||
pub fn release(self) -> &'a FatFs {
|
|
||||||
self.fat_fs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for SubSlice<'_> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
let bytes_to_read = self.len.min(buf.len());
|
let bytes_to_read = self.len.min(buf.len());
|
||||||
|
|
||||||
self.fat_fs
|
self.data
|
||||||
.inner
|
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.read_at_offset(self.offset, &mut buf[..bytes_to_read])?;
|
.read_at_offset(self.offset, &mut buf[..bytes_to_read])?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,9 @@ pub fn load_u16_le(bytes: &[u8]) -> u16 {
|
||||||
|
|
||||||
u16::from_le_bytes(bytes.try_into().unwrap())
|
u16::from_le_bytes(bytes.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_u32_le(bytes: &[u8]) -> u32 {
|
pub fn load_u32_le(bytes: &[u8]) -> u32 {
|
||||||
assert_eq!(bytes.len(), 4);
|
assert_eq!(bytes.len(), 4);
|
||||||
|
|
||||||
u32::from_le_bytes(bytes.try_into().unwrap())
|
u32::from_le_bytes(bytes.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// replace the value at x with f(x)
|
|
||||||
///
|
|
||||||
/// SAFETY:
|
|
||||||
/// should be safe, I guess? MIRI didn't complain about it
|
|
||||||
pub fn replace<T>(x: &mut T, f: impl FnOnce(T) -> T) {
|
|
||||||
unsafe {
|
|
||||||
let x_ptr = x as *mut T;
|
|
||||||
|
|
||||||
let old_x = std::ptr::read(x_ptr);
|
|
||||||
|
|
||||||
let new_x = f(old_x);
|
|
||||||
|
|
||||||
std::ptr::write(x_ptr, new_x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use fat_bits::FatFs;
|
use fat_bits::FatFs;
|
||||||
use fat_bits::dir::DirEntry;
|
use fat_bits::dir::DirEntry;
|
||||||
use fat_bits::fat::Fatty as _;
|
|
||||||
|
|
||||||
pub fn main() -> anyhow::Result<()> {
|
pub fn main() -> anyhow::Result<()> {
|
||||||
let args = std::env::args();
|
let args = std::env::args();
|
||||||
|
|
@ -21,16 +20,18 @@ pub fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let fat_fs = FatFs::load(file)?;
|
let fat_fs = FatFs::load(file)?;
|
||||||
|
|
||||||
println!("{}", fat_fs.bpb());
|
// println!("{}", fat_fs.bpb());
|
||||||
println!();
|
// println!();
|
||||||
println!("{}", fat_fs.fat());
|
// println!("{}", fat_fs.fat());
|
||||||
|
|
||||||
|
println!("{}", fat_fs);
|
||||||
println!();
|
println!();
|
||||||
println!(
|
println!(
|
||||||
"free clusters: {} ({} bytes)",
|
"free clusters: {} ({} bytes)",
|
||||||
fat_fs.fat().count_free_clusters(),
|
fat_fs.free_clusters(),
|
||||||
fat_fs.fat().count_free_clusters()
|
fat_fs.free_clusters()
|
||||||
* fat_fs.bpb().bytes_per_sector() as usize
|
* fat_fs.bytes_per_sector() as usize
|
||||||
* fat_fs.bpb().sectors_per_cluster() as usize
|
* fat_fs.sectors_per_cluster() as usize
|
||||||
);
|
);
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use std::ffi::c_int;
|
use std::ffi::c_int;
|
||||||
use std::io::Read;
|
use std::io::{Read, Write};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ use fuser::{FileType, Filesystem};
|
||||||
use libc::{EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, ENOTDIR};
|
use libc::{EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, ENOTDIR};
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
|
||||||
use crate::{FatFuse, Inode};
|
use crate::FatFuse;
|
||||||
|
|
||||||
const TTL: Duration = Duration::from_secs(1);
|
const TTL: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
|
|
@ -50,56 +50,34 @@ impl Filesystem for FatFuse {
|
||||||
|
|
||||||
debug!("looking up file {} with parent ino {}", name, parent);
|
debug!("looking up file {} with parent ino {}", name, parent);
|
||||||
|
|
||||||
let Some(parent_inode) = self.get_inode(parent) else {
|
let Some(parent_inode) = self.get_inode(parent).cloned() else {
|
||||||
// parent inode does not exist
|
// parent inode does not exist
|
||||||
// TODO: how can we make sure this does not happed?
|
// TODO: how can we make sure this does not happed?
|
||||||
// TODO: panic?
|
// TODO: panic?
|
||||||
debug!("could not find inode for parent ino {}", parent);
|
debug!("could not find inode for parent ino {}", parent);
|
||||||
reply.error(EIO);
|
|
||||||
|
|
||||||
|
reply.error(ENOENT);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// let Ok(mut dir_iter) = parent_inode.dir_iter(&self.fat_fs) else {
|
let parent_inode = parent_inode.borrow();
|
||||||
// reply.error(ENOTDIR);
|
|
||||||
// return;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let Some(dir_entry) =
|
let dir_entry: DirEntry =
|
||||||
// dir_iter.find(|dir_entry| dir_entry.name_string().as_deref() == Some(name))
|
match parent_inode
|
||||||
// else {
|
.dir_iter(&self.fat_fs)
|
||||||
// reply.error(ENOENT);
|
.and_then(|mut dir_iter| {
|
||||||
// return;
|
dir_iter
|
||||||
// };
|
.find(|dir_entry| &dir_entry.name_string() == name)
|
||||||
|
.ok_or(ENOENT)
|
||||||
|
}) {
|
||||||
|
Ok(dir_entry) => dir_entry,
|
||||||
|
Err(err) => {
|
||||||
|
debug!("error: {}", err);
|
||||||
|
reply.error(err);
|
||||||
|
|
||||||
let dir_entry: DirEntry = match parent_inode
|
return;
|
||||||
.dir_iter(&self.fat_fs)
|
}
|
||||||
// .map_err(|_| ENOTDIR)
|
};
|
||||||
.and_then(|mut dir_iter| {
|
|
||||||
dir_iter
|
|
||||||
.find(|dir_entry| &dir_entry.name_string() == name)
|
|
||||||
.ok_or(ENOENT)
|
|
||||||
}) {
|
|
||||||
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 ino = self.next_ino();
|
|
||||||
|
|
||||||
// let inode = Inode::new(&self.fat_fs, &dir_entry, ino, self.uid, self.gid);
|
|
||||||
|
|
||||||
// self.insert_inode(inode)
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
let inode = self.get_or_make_inode_by_dir_entry(
|
let inode = self.get_or_make_inode_by_dir_entry(
|
||||||
&dir_entry,
|
&dir_entry,
|
||||||
|
|
@ -107,6 +85,8 @@ impl Filesystem for FatFuse {
|
||||||
parent_inode.path(),
|
parent_inode.path(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut inode = inode.borrow_mut();
|
||||||
|
|
||||||
let attr = inode.file_attr();
|
let attr = inode.file_attr();
|
||||||
let generation = inode.generation();
|
let generation = inode.generation();
|
||||||
|
|
||||||
|
|
@ -118,19 +98,21 @@ impl Filesystem for FatFuse {
|
||||||
fn forget(&mut self, _req: &fuser::Request<'_>, ino: u64, nlookup: u64) {
|
fn forget(&mut self, _req: &fuser::Request<'_>, ino: u64, nlookup: u64) {
|
||||||
debug!("forgetting ino {} ({} times)", ino, nlookup);
|
debug!("forgetting ino {} ({} times)", ino, nlookup);
|
||||||
|
|
||||||
let Some(inode) = self.get_inode_mut(ino) else {
|
let Some(inode) = self.get_inode(ino).cloned() else {
|
||||||
debug!("tried to forget {} refs of inode {}, but was not found", ino, nlookup);
|
debug!("tried to forget {} refs of inode {}, but was not found", ino, nlookup);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// *ref_count = ref_count.saturating_sub(nlookup);
|
let mut inode_ref = inode.borrow_mut();
|
||||||
|
|
||||||
if inode.dec_ref_count(nlookup) == 0 {
|
if inode_ref.dec_ref_count(nlookup) == 0 {
|
||||||
debug!("dropping inode with ino {}", inode.ino());
|
debug!("dropping inode {}", inode_ref.ino());
|
||||||
|
|
||||||
|
drop(inode_ref);
|
||||||
|
|
||||||
// no more references, drop inode
|
// no more references, drop inode
|
||||||
self.drop_inode(ino);
|
self.drop_inode(inode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,20 +128,20 @@ impl Filesystem for FatFuse {
|
||||||
|
|
||||||
let inode = if let Some(fh) = fh {
|
let inode = if let Some(fh) = fh {
|
||||||
let Some(inode) = self.get_inode_by_fh(fh) else {
|
let Some(inode) = self.get_inode_by_fh(fh) else {
|
||||||
reply.error(EIO);
|
reply.error(EBADF);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
inode
|
inode
|
||||||
} else if let Some(inode) = self.get_inode(ino) {
|
} else if let Some(inode) = self.get_inode(ino).cloned() {
|
||||||
inode
|
inode
|
||||||
} else {
|
} else {
|
||||||
reply.error(EIO);
|
reply.error(ENOENT);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let inode = inode.borrow();
|
||||||
|
|
||||||
let attr = inode.file_attr();
|
let attr = inode.file_attr();
|
||||||
|
|
||||||
reply.attr(&TTL, &attr);
|
reply.attr(&TTL, &attr);
|
||||||
|
|
@ -189,6 +171,17 @@ impl Filesystem for FatFuse {
|
||||||
ino, mode, uid, gid, size, fh, flags
|
ino, mode, uid, gid, size, fh, flags
|
||||||
);
|
);
|
||||||
reply.error(ENOSYS);
|
reply.error(ENOSYS);
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: implement this properly
|
||||||
|
// let Some(inode) = self.get_inode(ino) else {
|
||||||
|
// debug!("tried to get inode {ino}, but not found");
|
||||||
|
//
|
||||||
|
// reply.error(ENOENT);
|
||||||
|
// return;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// reply.attr(&TTL, &inode.file_attr());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readlink(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyData) {
|
fn readlink(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyData) {
|
||||||
|
|
@ -272,7 +265,7 @@ impl Filesystem for FatFuse {
|
||||||
|
|
||||||
fn open(&mut self, _req: &fuser::Request<'_>, ino: u64, _flags: i32, reply: fuser::ReplyOpen) {
|
fn open(&mut self, _req: &fuser::Request<'_>, ino: u64, _flags: i32, reply: fuser::ReplyOpen) {
|
||||||
if !self.inode_table.contains_key(&ino) {
|
if !self.inode_table.contains_key(&ino) {
|
||||||
reply.error(EINVAL);
|
reply.error(ENOENT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,10 +314,12 @@ impl Filesystem for FatFuse {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let inode = inode.borrow();
|
||||||
|
|
||||||
if inode.ino() != ino {
|
if inode.ino() != ino {
|
||||||
debug!("fh {fh} is associated with inode {} instead of {ino}", inode.ino());
|
debug!("fh {fh} is associated with inode {} instead of {ino}", inode.ino());
|
||||||
|
|
||||||
reply.error(EIO);
|
reply.error(EINVAL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,6 +356,8 @@ impl Filesystem for FatFuse {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("read {n} bytes");
|
||||||
|
|
||||||
reply.data(&buf[..n]);
|
reply.data(&buf[..n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,23 +368,102 @@ impl Filesystem for FatFuse {
|
||||||
fh: u64,
|
fh: u64,
|
||||||
offset: i64,
|
offset: i64,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
write_flags: u32,
|
_write_flags: u32,
|
||||||
flags: i32,
|
_flags: i32,
|
||||||
lock_owner: Option<u64>,
|
_lock_owner: Option<u64>,
|
||||||
reply: fuser::ReplyWrite,
|
reply: fuser::ReplyWrite,
|
||||||
) {
|
) {
|
||||||
debug!(
|
debug!("new write request: ino={ino} fh={fh} offset={offset} data={data:?}");
|
||||||
"[Not Implemented] write(ino: {:#x?}, fh: {}, offset: {}, data.len(): {}, \
|
|
||||||
write_flags: {:#x?}, flags: {:#x?}, lock_owner: {:?})",
|
if offset < 0 {
|
||||||
ino,
|
debug!("tried to write with negative offset {offset}");
|
||||||
fh,
|
|
||||||
offset,
|
reply.error(EINVAL);
|
||||||
data.len(),
|
return;
|
||||||
write_flags,
|
}
|
||||||
flags,
|
|
||||||
lock_owner
|
let offset = offset as u64;
|
||||||
);
|
|
||||||
reply.error(ENOSYS);
|
let Some(inode) = self.get_inode_by_fh(fh) else {
|
||||||
|
debug!("no inode associated with fh {fh} (given ino: {ino}");
|
||||||
|
|
||||||
|
reply.error(EBADF);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let inode = inode.borrow();
|
||||||
|
|
||||||
|
if inode.is_read_only() {
|
||||||
|
reply.error(EBADF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if inode.ino() != ino {
|
||||||
|
debug!("fh {fh} points to ino {}, but ino {ino} was given", inode.ino());
|
||||||
|
|
||||||
|
reply.error(EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !inode.is_file() {
|
||||||
|
debug!("tried to use read on directory {ino}");
|
||||||
|
|
||||||
|
reply.error(EISDIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut writer = match inode.file_writer(&self.fat_fs) {
|
||||||
|
Ok(writer) => writer,
|
||||||
|
Err(err) => {
|
||||||
|
reply.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// if writer.skip(offset) != offset {
|
||||||
|
// // writer is at EOF, bail
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
let cur_offset = writer.skip(offset);
|
||||||
|
|
||||||
|
// can't seek more than we requested
|
||||||
|
assert!(cur_offset <= offset);
|
||||||
|
|
||||||
|
let mut bytes_written = 0;
|
||||||
|
|
||||||
|
if cur_offset < offset {
|
||||||
|
// tried to set offset beyond EOF
|
||||||
|
// fill with zeros
|
||||||
|
let zeros = vec![0; (offset - cur_offset) as usize];
|
||||||
|
|
||||||
|
debug!("writing {} zeros", zeros.len());
|
||||||
|
|
||||||
|
if let Err(err) = writer.write_all(&zeros) {
|
||||||
|
debug!("writing zeros returned error: {err}");
|
||||||
|
|
||||||
|
reply.error(EIO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_written += zeros.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = writer.write_all(&data) {
|
||||||
|
debug!("writing data returned error: {err}");
|
||||||
|
|
||||||
|
reply.error(EIO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_written += data.len();
|
||||||
|
|
||||||
|
reply.written(bytes_written as u32);
|
||||||
|
|
||||||
|
// TODO: update file size
|
||||||
|
if offset + bytes_written as u64 > inode.size() {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(
|
fn flush(
|
||||||
|
|
@ -395,14 +471,34 @@ impl Filesystem for FatFuse {
|
||||||
_req: &fuser::Request<'_>,
|
_req: &fuser::Request<'_>,
|
||||||
ino: u64,
|
ino: u64,
|
||||||
fh: u64,
|
fh: u64,
|
||||||
lock_owner: u64,
|
_lock_owner: u64,
|
||||||
reply: fuser::ReplyEmpty,
|
reply: fuser::ReplyEmpty,
|
||||||
) {
|
) {
|
||||||
debug!(
|
// debug!(
|
||||||
"[Not Implemented] flush(ino: {:#x?}, fh: {}, lock_owner: {:?})",
|
// "[Not Implemented] flush(ino: {:#x?}, fh: {}, lock_owner: {:?})",
|
||||||
ino, fh, lock_owner
|
// ino, fh, lock_owner
|
||||||
);
|
// );
|
||||||
reply.error(ENOSYS);
|
// reply.error(ENOSYS);
|
||||||
|
|
||||||
|
debug!("flushing ino={ino} fh={fh}");
|
||||||
|
|
||||||
|
let Some(&found_ino) = self.ino_by_fh.get(&fh) else {
|
||||||
|
debug!("expected fh {fh} to be mapped to ino {ino}, but not found instead");
|
||||||
|
|
||||||
|
reply.error(EBADF);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if found_ino != ino {
|
||||||
|
debug!(
|
||||||
|
"expected fh {fh} to be mapped to ino {ino}, but was mapped to {found_ino} instead"
|
||||||
|
);
|
||||||
|
|
||||||
|
reply.error(EBADF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release(
|
fn release(
|
||||||
|
|
@ -418,14 +514,14 @@ impl Filesystem for FatFuse {
|
||||||
let Some(found_ino) = self.ino_by_fh.remove(&fh) else {
|
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");
|
debug!("tried to release fh {fh} with ino {ino}, but no ino was found in mapping");
|
||||||
|
|
||||||
reply.error(EINVAL);
|
reply.error(EBADF);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if found_ino != ino {
|
if found_ino != ino {
|
||||||
debug!("tried to release fh {fh} with ino {ino}, but found ino is {found_ino} instead");
|
debug!("tried to release fh {fh} with ino {ino}, but found ino is {found_ino} instead");
|
||||||
|
|
||||||
reply.error(EIO);
|
reply.error(EINVAL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -475,10 +571,12 @@ impl Filesystem for FatFuse {
|
||||||
let Some(dir_inode) = self.get_inode_by_fh(fh) else {
|
let Some(dir_inode) = self.get_inode_by_fh(fh) else {
|
||||||
debug!("could not find inode accociated with fh {} (ino: {})", fh, ino);
|
debug!("could not find inode accociated with fh {} (ino: {})", fh, ino);
|
||||||
|
|
||||||
reply.error(EINVAL);
|
reply.error(EBADF);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let dir_inode = dir_inode.borrow();
|
||||||
|
|
||||||
if dir_inode.ino() != ino {
|
if dir_inode.ino() != ino {
|
||||||
debug!(
|
debug!(
|
||||||
"ino {} of inode associated with fh {} does not match given ino {}",
|
"ino {} of inode associated with fh {} does not match given ino {}",
|
||||||
|
|
@ -533,9 +631,11 @@ impl Filesystem for FatFuse {
|
||||||
for dir_entry in dirs {
|
for dir_entry in dirs {
|
||||||
let name = dir_entry.name_string();
|
let name = dir_entry.name_string();
|
||||||
|
|
||||||
let inode: &Inode =
|
let inode =
|
||||||
self.get_or_make_inode_by_dir_entry(&dir_entry, dir_ino, Rc::clone(&dir_path));
|
self.get_or_make_inode_by_dir_entry(&dir_entry, dir_ino, Rc::clone(&dir_path));
|
||||||
|
|
||||||
|
let inode = inode.borrow();
|
||||||
|
|
||||||
debug!("adding entry {} (ino: {})", name, inode.ino());
|
debug!("adding entry {} (ino: {})", name, inode.ino());
|
||||||
if reply.add(ino, next_offset(), inode.kind().into(), name) {
|
if reply.add(ino, next_offset(), inode.kind().into(), name) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -571,17 +671,19 @@ impl Filesystem for FatFuse {
|
||||||
let Some(ino) = self.ino_by_fh.remove(&fh) else {
|
let Some(ino) = self.ino_by_fh.remove(&fh) else {
|
||||||
debug!("can't find inode {} by fh {}", ino, fh);
|
debug!("can't find inode {} by fh {}", ino, fh);
|
||||||
|
|
||||||
reply.error(EIO);
|
reply.error(EBADF);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(inode) = self.inode_table.get(&ino) else {
|
let Some(inode) = self.get_inode(ino) else {
|
||||||
debug!("ino {} not associated with an inode", ino);
|
debug!("ino {} not associated with an inode", ino);
|
||||||
|
|
||||||
reply.ok();
|
reply.ok();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let inode = inode.borrow();
|
||||||
|
|
||||||
if inode.ino() != ino {
|
if inode.ino() != ino {
|
||||||
debug!(
|
debug!(
|
||||||
"inode with ino {}, associated with fh {}, does not have expected ino {}",
|
"inode with ino {}, associated with fh {}, does not have expected ino {}",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::cell::{LazyCell, RefCell};
|
use std::cell::{LazyCell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
use std::time::SystemTime;
|
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 fat_bits::iter::{ClusterChainReader, ClusterChainWriter};
|
||||||
use fuser::FileAttr;
|
use fuser::FileAttr;
|
||||||
use libc::{EISDIR, ENOTDIR};
|
use libc::{EISDIR, ENOTDIR};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
@ -27,12 +27,6 @@ fn get_random<T>() -> T
|
||||||
where
|
where
|
||||||
rand::distr::StandardUniform: rand::distr::Distribution<T>,
|
rand::distr::StandardUniform: rand::distr::Distribution<T>,
|
||||||
{
|
{
|
||||||
// RNG.with(|x| unsafe {
|
|
||||||
// let rng = &mut (*x.get());
|
|
||||||
|
|
||||||
// rng.random::<u32>()
|
|
||||||
// })
|
|
||||||
|
|
||||||
RNG.with(|rng| rng.borrow_mut().random())
|
RNG.with(|rng| rng.borrow_mut().random())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +47,9 @@ impl From<Kind> for fuser::FileType {
|
||||||
|
|
||||||
pub const ROOT_INO: u64 = 1;
|
pub const ROOT_INO: u64 = 1;
|
||||||
|
|
||||||
|
pub type InodeRef = Rc<RefCell<Inode>>;
|
||||||
|
pub type InodeWeak = Weak<RefCell<Inode>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Inode {
|
pub struct Inode {
|
||||||
|
|
@ -63,7 +60,7 @@ pub struct Inode {
|
||||||
|
|
||||||
ref_count: u64,
|
ref_count: u64,
|
||||||
|
|
||||||
parent_ino: u64,
|
parent: Option<InodeRef>,
|
||||||
|
|
||||||
size: u64,
|
size: u64,
|
||||||
block_size: u32,
|
block_size: u32,
|
||||||
|
|
@ -105,7 +102,7 @@ impl Inode {
|
||||||
uid: u32,
|
uid: u32,
|
||||||
gid: u32,
|
gid: u32,
|
||||||
path: impl Into<Rc<str>>,
|
path: impl Into<Rc<str>>,
|
||||||
parent_ino: u64,
|
parent: InodeRef,
|
||||||
) -> Inode {
|
) -> Inode {
|
||||||
assert!(dir_entry.is_file() || dir_entry.is_dir());
|
assert!(dir_entry.is_file() || dir_entry.is_dir());
|
||||||
|
|
||||||
|
|
@ -142,9 +139,9 @@ impl Inode {
|
||||||
ino,
|
ino,
|
||||||
generation,
|
generation,
|
||||||
ref_count: 0,
|
ref_count: 0,
|
||||||
parent_ino,
|
parent: Some(parent),
|
||||||
size: dir_entry.file_size() as u64,
|
size: dir_entry.file_size() as u64,
|
||||||
block_size: fat_fs.bpb().bytes_per_sector() as u32,
|
block_size: fat_fs.bytes_per_sector() as u32,
|
||||||
kind,
|
kind,
|
||||||
read_only: dir_entry.is_readonly(),
|
read_only: dir_entry.is_readonly(),
|
||||||
atime,
|
atime,
|
||||||
|
|
@ -158,17 +155,15 @@ impl Inode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_inode(fat_fs: &FatFs, uid: u32, gid: u32) -> Inode {
|
pub fn root_inode(fat_fs: &FatFs, uid: u32, gid: u32) -> Inode {
|
||||||
// let generation = Self::new_generation();
|
let root_cluster = fat_fs.root_cluster().unwrap_or(0);
|
||||||
|
|
||||||
let root_cluster = fat_fs.bpb().root_cluster().unwrap_or(0);
|
|
||||||
|
|
||||||
Inode {
|
Inode {
|
||||||
ino: 1,
|
ino: ROOT_INO,
|
||||||
generation: 0,
|
generation: 0, // root cluster always has constant generation of 0
|
||||||
ref_count: 0,
|
ref_count: 0,
|
||||||
parent_ino: ROOT_INO, // parent is self
|
parent: None, // parent is self
|
||||||
size: 0,
|
size: 0,
|
||||||
block_size: fat_fs.bpb().bytes_per_sector() as u32,
|
block_size: fat_fs.bytes_per_sector() as u32,
|
||||||
kind: Kind::Dir,
|
kind: Kind::Dir,
|
||||||
read_only: false,
|
read_only: false,
|
||||||
atime: SystemTime::UNIX_EPOCH,
|
atime: SystemTime::UNIX_EPOCH,
|
||||||
|
|
@ -225,8 +220,18 @@ impl Inode {
|
||||||
self.ref_count
|
self.ref_count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent_ino(&self) -> u64 {
|
pub fn parent(&self) -> Option<InodeRef> {
|
||||||
self.parent_ino
|
self.parent.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> u64 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_size(&mut self, new_size: u64) {
|
||||||
|
self.size = new_size;
|
||||||
|
|
||||||
|
todo!("update dir entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> Kind {
|
||||||
|
|
@ -241,6 +246,10 @@ impl Inode {
|
||||||
self.kind == Kind::Dir
|
self.kind == Kind::Dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_read_only(&self) -> bool {
|
||||||
|
self.read_only
|
||||||
|
}
|
||||||
|
|
||||||
pub fn first_cluster(&self) -> u32 {
|
pub fn first_cluster(&self) -> u32 {
|
||||||
self.first_cluster
|
self.first_cluster
|
||||||
}
|
}
|
||||||
|
|
@ -298,4 +307,12 @@ impl Inode {
|
||||||
|
|
||||||
Ok(fat_fs.file_reader(self.first_cluster()))
|
Ok(fat_fs.file_reader(self.first_cluster()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_writer<'a>(&'a self, fat_fs: &'a FatFs) -> Result<ClusterChainWriter<'a>, i32> {
|
||||||
|
if self.is_dir() {
|
||||||
|
return Err(EISDIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(fat_fs.file_writer(self.first_cluster()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
mod fuse;
|
mod fuse;
|
||||||
mod inode;
|
mod inode;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use fat_bits::dir::DirEntry;
|
use fat_bits::dir::DirEntry;
|
||||||
use fat_bits::{FatFs, SliceLike};
|
use fat_bits::{FatFs, SliceLike};
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use log::debug;
|
use log::{debug, error};
|
||||||
|
|
||||||
use crate::inode::Inode;
|
use crate::inode::{Inode, InodeRef};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct FatFuse {
|
pub struct FatFuse {
|
||||||
|
|
@ -21,7 +22,7 @@ pub struct FatFuse {
|
||||||
next_ino: u64,
|
next_ino: u64,
|
||||||
next_fh: u64,
|
next_fh: u64,
|
||||||
|
|
||||||
inode_table: BTreeMap<u64, Inode>,
|
inode_table: BTreeMap<u64, InodeRef>,
|
||||||
|
|
||||||
ino_by_first_cluster: BTreeMap<u32, u64>,
|
ino_by_first_cluster: BTreeMap<u32, u64>,
|
||||||
ino_by_fh: BTreeMap<u64, u64>,
|
ino_by_fh: BTreeMap<u64, u64>,
|
||||||
|
|
@ -84,26 +85,28 @@ impl FatFuse {
|
||||||
fh
|
fh
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_inode(&mut self, inode: Inode) -> &mut Inode {
|
fn insert_inode(&mut self, inode: Inode) -> InodeRef {
|
||||||
let ino = inode.ino();
|
let ino = inode.ino();
|
||||||
let generation = inode.generation();
|
let generation = inode.generation();
|
||||||
let first_cluster = inode.first_cluster();
|
let first_cluster = inode.first_cluster();
|
||||||
|
|
||||||
// let old_inode = self.inode_table.insert(ino, inode);
|
// let old_inode = self.inode_table.insert(ino, inode);
|
||||||
|
|
||||||
|
let inode = Rc::new(RefCell::new(inode));
|
||||||
|
|
||||||
let entry = self.inode_table.entry(ino);
|
let entry = self.inode_table.entry(ino);
|
||||||
|
|
||||||
let (new_inode, old_inode): (&mut Inode, Option<Inode>) = match entry {
|
let (new_inode, old_inode) = match entry {
|
||||||
std::collections::btree_map::Entry::Vacant(vacant_entry) => {
|
std::collections::btree_map::Entry::Vacant(vacant_entry) => {
|
||||||
let new_inode = vacant_entry.insert(inode);
|
let new_inode = vacant_entry.insert(inode);
|
||||||
(new_inode, None)
|
(Rc::clone(new_inode), None)
|
||||||
}
|
}
|
||||||
std::collections::btree_map::Entry::Occupied(occupied_entry) => {
|
std::collections::btree_map::Entry::Occupied(occupied_entry) => {
|
||||||
let entry_ref = occupied_entry.into_mut();
|
let entry_ref = occupied_entry.into_mut();
|
||||||
|
|
||||||
let old_inode = std::mem::replace(entry_ref, inode);
|
let old_inode = std::mem::replace(entry_ref, inode);
|
||||||
|
|
||||||
(entry_ref, Some(old_inode))
|
(Rc::clone(entry_ref), Some(old_inode))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -113,6 +116,8 @@ impl FatFuse {
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(old_inode) = old_inode {
|
if let Some(old_inode) = old_inode {
|
||||||
|
let old_inode = old_inode.borrow();
|
||||||
|
|
||||||
debug!("ejected inode {} {}", old_inode.ino(), old_inode.generation());
|
debug!("ejected inode {} {}", old_inode.ino(), old_inode.generation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,22 +127,32 @@ impl FatFuse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(old_ino) = self.ino_by_path.insert(new_inode.path(), ino) {
|
let path = new_inode.borrow().path();
|
||||||
debug!("ejected old {} -> {} path to ino mapping", new_inode.path(), old_ino);
|
|
||||||
|
if let Some(old_ino) = self.ino_by_path.insert(Rc::clone(&path), ino) {
|
||||||
|
debug!("ejected old {} -> {} path to ino mapping", path, old_ino);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_inode
|
new_inode
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_inode(&mut self, ino: u64) {
|
fn drop_inode(&mut self, inode: InodeRef) {
|
||||||
debug!("dropping ino {}", ino);
|
let inode = inode.borrow();
|
||||||
|
|
||||||
let Some(inode) = self.inode_table.remove(&ino) else {
|
let ino = inode.ino();
|
||||||
debug!("tried to drop inode with ino {}, but was not in table", ino);
|
|
||||||
|
debug!("dropping inode {}", ino);
|
||||||
|
|
||||||
|
let Some(removed_inode) = self.inode_table.remove(&ino) else {
|
||||||
|
error!("tried to drop inode with ino {}, but was not in table", ino);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if removed_inode.borrow().ino() != ino {
|
||||||
|
error!("removed inode was not expected inode");
|
||||||
|
}
|
||||||
|
|
||||||
let first_cluster = inode.first_cluster();
|
let first_cluster = inode.first_cluster();
|
||||||
|
|
||||||
if first_cluster != 0 {
|
if first_cluster != 0 {
|
||||||
|
|
@ -189,39 +204,28 @@ impl FatFuse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(parent_inode) = self.get_inode_mut(inode.parent_ino()) else {
|
|
||||||
panic!("parent inode {} does not exists anymore", inode.parent_ino());
|
|
||||||
};
|
|
||||||
|
|
||||||
// dec refcount
|
|
||||||
parent_inode.dec_ref_count(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inode(&self, ino: u64) -> Option<&Inode> {
|
fn get_inode(&self, ino: u64) -> Option<&InodeRef> {
|
||||||
self.inode_table.get(&ino)
|
self.inode_table.get(&ino)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inode_mut(&mut self, ino: u64) -> Option<&mut Inode> {
|
|
||||||
self.inode_table.get_mut(&ino)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_or_make_inode_by_dir_entry(
|
fn get_or_make_inode_by_dir_entry(
|
||||||
&mut self,
|
&mut self,
|
||||||
dir_entry: &DirEntry,
|
dir_entry: &DirEntry,
|
||||||
parent_ino: u64,
|
parent_ino: u64,
|
||||||
parent_path: Rc<str>,
|
parent_path: Rc<str>,
|
||||||
) -> &mut Inode {
|
) -> InodeRef {
|
||||||
|
// try to find inode by first cluster first
|
||||||
if dir_entry.first_cluster() != 0
|
if dir_entry.first_cluster() != 0
|
||||||
&& self
|
&& let Some(inode) = self.get_inode_by_first_cluster(dir_entry.first_cluster())
|
||||||
.get_inode_by_first_cluster_mut(dir_entry.first_cluster())
|
|
||||||
.is_some()
|
|
||||||
{
|
{
|
||||||
return self
|
return inode;
|
||||||
.get_inode_by_first_cluster_mut(dir_entry.first_cluster())
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try to find inode by path
|
||||||
|
// mostly for empty files/directories which have a first cluster of 0
|
||||||
|
|
||||||
let path = {
|
let path = {
|
||||||
let mut path = parent_path.as_ref().to_owned();
|
let mut path = parent_path.as_ref().to_owned();
|
||||||
|
|
||||||
|
|
@ -235,27 +239,25 @@ impl FatFuse {
|
||||||
path
|
path
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.get_inode_by_path_mut(&path).is_some() {
|
if let Some(inode) = self.get_inode_by_path(&path) {
|
||||||
return self.get_inode_by_path_mut(&path).unwrap();
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no inode found, make a new one
|
// no inode found, make a new one
|
||||||
let ino = self.next_ino();
|
let ino = self.next_ino();
|
||||||
|
|
||||||
let Some(parent_inode) = self.get_inode_mut(parent_ino) else {
|
let Some(parent_inode) = self.get_inode(parent_ino).cloned() else {
|
||||||
// TODO: what do we do here? should not happen
|
// TODO: what do we do here? should not happen
|
||||||
panic!("parent_ino {} does not lead to inode", parent_ino);
|
panic!("parent_ino {} does not lead to inode", parent_ino);
|
||||||
};
|
};
|
||||||
|
|
||||||
// inc ref of parent
|
let inode =
|
||||||
parent_inode.inc_ref_count();
|
Inode::new(&self.fat_fs, dir_entry, ino, self.uid, self.gid, path, parent_inode);
|
||||||
|
|
||||||
let inode = Inode::new(&self.fat_fs, dir_entry, ino, self.uid, self.gid, path, parent_ino);
|
|
||||||
|
|
||||||
self.insert_inode(inode)
|
self.insert_inode(inode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inode_by_first_cluster(&self, first_cluster: u32) -> Option<&Inode> {
|
pub fn get_inode_by_first_cluster(&self, first_cluster: u32) -> Option<InodeRef> {
|
||||||
if first_cluster == 0 {
|
if first_cluster == 0 {
|
||||||
debug!("trying to get inode by first cluster 0");
|
debug!("trying to get inode by first cluster 0");
|
||||||
|
|
||||||
|
|
@ -265,7 +267,7 @@ impl FatFuse {
|
||||||
let ino = self.ino_by_first_cluster.get(&first_cluster)?;
|
let ino = self.ino_by_first_cluster.get(&first_cluster)?;
|
||||||
|
|
||||||
if let Some(inode) = self.inode_table.get(ino) {
|
if let Some(inode) = self.inode_table.get(ino) {
|
||||||
Some(inode)
|
Some(Rc::clone(inode))
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"first cluster {} is mapped to ino {}, but inode is not in table",
|
"first cluster {} is mapped to ino {}, but inode is not in table",
|
||||||
|
|
@ -276,31 +278,10 @@ impl FatFuse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inode_by_first_cluster_mut(&mut self, first_cluster: u32) -> Option<&mut Inode> {
|
pub fn get_inode_by_fh(&self, fh: u64) -> Option<InodeRef> {
|
||||||
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) {
|
|
||||||
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_fh(&self, fh: u64) -> Option<&Inode> {
|
|
||||||
let ino = *self.ino_by_fh.get(&fh)?;
|
let ino = *self.ino_by_fh.get(&fh)?;
|
||||||
|
|
||||||
if let Some(inode) = self.get_inode(ino) {
|
if let Some(inode) = self.get_inode(ino).cloned() {
|
||||||
Some(inode)
|
Some(inode)
|
||||||
} else {
|
} else {
|
||||||
debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino);
|
debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino);
|
||||||
|
|
@ -309,34 +290,10 @@ impl FatFuse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inode_by_fh_mut(&mut self, fh: u64) -> Option<&mut Inode> {
|
pub fn get_inode_by_path(&self, path: &str) -> Option<InodeRef> {
|
||||||
let ino = *self.ino_by_fh.get(&fh)?;
|
|
||||||
|
|
||||||
if let Some(inode) = self.get_inode_mut(ino) {
|
|
||||||
Some(inode)
|
|
||||||
} else {
|
|
||||||
debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino);
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_inode_by_path(&self, path: &str) -> Option<&Inode> {
|
|
||||||
let ino = *self.ino_by_path.get(path)?;
|
let ino = *self.ino_by_path.get(path)?;
|
||||||
|
|
||||||
if let Some(inode) = self.get_inode(ino) {
|
if let Some(inode) = self.get_inode(ino).cloned() {
|
||||||
Some(inode)
|
|
||||||
} else {
|
|
||||||
debug!("path {} is mapped to ino {}, but inode is not in table", path, ino);
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_inode_by_path_mut(&mut self, path: &str) -> Option<&mut Inode> {
|
|
||||||
let ino = *self.ino_by_path.get(path)?;
|
|
||||||
|
|
||||||
if let Some(inode) = self.get_inode_mut(ino) {
|
|
||||||
Some(inode)
|
Some(inode)
|
||||||
} else {
|
} else {
|
||||||
debug!("path {} is mapped to ino {}, but inode is not in table", path, ino);
|
debug!("path {} is mapped to ino {}, but inode is not in table", path, ino);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::fs::File;
|
use std::fs::OpenOptions;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
use fat_fuse::FatFuse;
|
use fat_fuse::FatFuse;
|
||||||
|
|
@ -13,12 +13,13 @@ fn main() -> anyhow::Result<()> {
|
||||||
let path = args.next().ok_or(anyhow::anyhow!("missing fs path"))?;
|
let path = args.next().ok_or(anyhow::anyhow!("missing fs path"))?;
|
||||||
let mountpoint = args.next().ok_or(anyhow::anyhow!("missing mount point"))?;
|
let mountpoint = args.next().ok_or(anyhow::anyhow!("missing mount point"))?;
|
||||||
|
|
||||||
let file = File::open(path)?;
|
// let file = File::open(path)?;
|
||||||
|
let file = OpenOptions::new().read(true).write(true).open(path)?;
|
||||||
|
|
||||||
let fat_fuse = FatFuse::new(file)?;
|
let fat_fuse = FatFuse::new(file)?;
|
||||||
|
|
||||||
let options = vec![
|
let options = vec![
|
||||||
MountOption::RO,
|
// MountOption::RO,
|
||||||
MountOption::FSName("fat-fuse".to_owned()),
|
MountOption::FSName("fat-fuse".to_owned()),
|
||||||
MountOption::AutoUnmount,
|
MountOption::AutoUnmount,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue