From e4f693ed3effc986bc5e887e310f462b60155f72 Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Sun, 27 Jul 2025 14:38:17 +0200 Subject: [PATCH] only expose regular DirEntries, long filename one's are internal --- fat-bits/src/dir.rs | 173 +++++++++++++++++++++++++++++++------------ fat-bits/src/lib.rs | 4 +- fat-dump/src/main.rs | 4 +- 3 files changed, 128 insertions(+), 53 deletions(-) diff --git a/fat-bits/src/dir.rs b/fat-bits/src/dir.rs index e2a0acf..98ff94c 100644 --- a/fat-bits/src/dir.rs +++ b/fat-bits/src/dir.rs @@ -45,8 +45,9 @@ impl Display for Attr { } } +/// represents an entry in a diectory #[derive(Debug)] -pub struct RegularDirEntry { +pub struct DirEntry { name: [u8; 11], attr: Attr, @@ -66,7 +67,7 @@ pub struct RegularDirEntry { long_name: Option, } -impl Display for RegularDirEntry { +impl Display for DirEntry { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut name = self.name_string().unwrap_or_else(|| "".to_owned()); @@ -87,8 +88,8 @@ impl Display for RegularDirEntry { } } -impl RegularDirEntry { - pub fn load(bytes: &[u8]) -> anyhow::Result { +impl DirEntry { + pub fn load(bytes: &[u8]) -> anyhow::Result { assert_eq!(bytes.len(), 32); let name = bytes[..11].try_into().unwrap(); @@ -129,7 +130,7 @@ impl RegularDirEntry { ) } - Ok(RegularDirEntry { + Ok(DirEntry { name, attr, create_time_tenths, @@ -273,7 +274,10 @@ impl RegularDirEntry { } } -pub struct LongNameDirEntry { +/// long filename entry in a directory +/// +/// this should not be exposed to end users, only for internal consumption in the DirIter +struct LongNameDirEntry { ordinal: u8, is_last: bool, name: [u16; 13], @@ -330,7 +334,7 @@ impl LongNameDirEntry { self.ordinal } - pub fn is_first(&self) -> bool { + pub fn is_last(&self) -> bool { self.is_last } @@ -343,21 +347,25 @@ impl LongNameDirEntry { } } -pub enum DirEntry { - Regular(RegularDirEntry), +/// wraps both Regular DirEntry and LongNameDirEntry +/// +/// should not be exposed publicly, end users only see DirEntries +/// just for making the bytes -> DirEntry loading a bit easier +enum DirEntryWrapper { + Regular(DirEntry), LongName(LongNameDirEntry), } -impl DirEntry { - pub fn load(bytes: &[u8]) -> anyhow::Result { +impl DirEntryWrapper { + pub fn load(bytes: &[u8]) -> anyhow::Result { assert_eq!(bytes.len(), 32); let attr = Attr::from_bits_truncate(bytes[11]); let dir_entry = if attr == Attr::LongName { - DirEntry::LongName(LongNameDirEntry::load(bytes)?) + DirEntryWrapper::LongName(LongNameDirEntry::load(bytes)?) } else { - DirEntry::Regular(RegularDirEntry::load(bytes)?) + DirEntryWrapper::Regular(DirEntry::load(bytes)?) }; Ok(dir_entry) @@ -379,7 +387,7 @@ impl LongFilenameBuf { } pub fn next(&mut self, dir_entry: LongNameDirEntry) -> anyhow::Result<()> { - if dir_entry.is_last { + if dir_entry.is_last() { // first/lasts entry let mut name = dir_entry.name(); @@ -470,51 +478,53 @@ impl DirIter { long_filename_buf: Default::default(), } } -} -impl Iterator for DirIter { - type Item = RegularDirEntry; - - fn next(&mut self) -> Option { + /// inner function for iterator + fn next_impl(&mut self) -> anyhow::Result> { let mut chunk = [0; 32]; - self.reader.read_exact(&mut chunk).ok()?; + if self.reader.read_exact(&mut chunk).is_err() { + // reading failed; nothing we can do here + return Ok(None); + } // let Ok(dir_entry) = DirEntry::load(&chunk) else { // return self.next(); // }; - let dir_entry = match DirEntry::load(&chunk) { - Ok(dir_entry) => dir_entry, - Err(e) => { - // if loading fails: print error and try next entry - eprintln!("failed to load dir entry: {e}"); - - return self.next(); - } - }; + let dir_entry = DirEntryWrapper::load(&chunk) + .map_err(|e| anyhow::anyhow!("failed to load dir entry: {e}"))?; let mut dir_entry = match dir_entry { - DirEntry::Regular(dir_entry) => dir_entry, - DirEntry::LongName(long_name) => { - if let Err(e) = self.long_filename_buf.next(long_name) { - eprintln!("invalid long filename entry: {}", e); - } + DirEntryWrapper::Regular(dir_entry) => dir_entry, + DirEntryWrapper::LongName(long_name) => { + self.long_filename_buf.next(long_name).map_err(|e| { + self.long_filename_buf.reset(); + anyhow::anyhow!("invalid long filename entry: {e}") + })?; - // simply skip long name entries for now - return self.next(); + return self.next_impl(); } }; if dir_entry.is_sentinel() { - return None; + return Ok(None); } if dir_entry.is_empty() { - return self.next(); + return self.next_impl(); } - match self.long_filename_buf.get_buf(dir_entry.checksum()) { - Ok(Some(iter)) => { + match self + .long_filename_buf + .get_buf(dir_entry.checksum()) + .map_err(|e| { + anyhow::anyhow!( + "failed to get long filename for {}: {}", + dir_entry.name_string().as_deref().unwrap_or(""), + e + ) + })? { + Some(iter) => { // attach long filename to dir_entry let long_filename: String = @@ -522,18 +532,83 @@ impl Iterator for DirIter { dir_entry.set_long_name(long_filename); } - Ok(None) => {} // no long filename -> do nothing - Err(e) => { - eprintln!( - "failed to get long filename for {}: {}", - dir_entry.name_string().as_deref().unwrap_or(""), - e - ); - } + None => {} // no long filename -> do nothing } self.long_filename_buf.reset(); - Some(dir_entry) + Ok(Some(dir_entry)) + } +} + +impl Iterator for DirIter { + type Item = DirEntry; + + fn next(&mut self) -> Option { + match self.next_impl() { + Ok(x) => x, + Err(e) => { + eprintln!("{}", e); + + self.next() + } + } + + // let mut chunk = [0; 32]; + // self.reader.read_exact(&mut chunk).ok()?; + + // let dir_entry = match DirEntryWrapper::load(&chunk) { + // Ok(dir_entry) => dir_entry, + // Err(e) => { + // // if loading fails: print error and try next entry + // eprintln!("failed to load dir entry: {e}"); + + // return self.next(); + // } + // }; + + // let mut dir_entry = match dir_entry { + // DirEntryWrapper::Regular(dir_entry) => dir_entry, + // DirEntryWrapper::LongName(long_name) => { + // if let Err(e) = self.long_filename_buf.next(long_name) { + // self.long_filename_buf.reset(); + // + // eprintln!("invalid long filename entry: {}", e); + // } + + // return self.next(); + // } + // }; + + // if dir_entry.is_sentinel() { + // return None; + // } + + // if dir_entry.is_empty() { + // return self.next(); + // } + + // match self.long_filename_buf.get_buf(dir_entry.checksum()) { + // Ok(Some(iter)) => { + // // attach long filename to dir_entry + + // let long_filename: String = + // char::decode_utf16(iter).filter_map(|x| x.ok()).collect(); + + // dir_entry.set_long_name(long_filename); + // } + // Ok(None) => {} // no long filename -> do nothing + // Err(e) => { + // eprintln!( + // "failed to get long filename for {}: {}", + // dir_entry.name_string().as_deref().unwrap_or(""), + // e + // ); + // } + // } + + // self.long_filename_buf.reset(); + + // Some(dir_entry) } } diff --git a/fat-bits/src/lib.rs b/fat-bits/src/lib.rs index ebeb6ef..5591bd5 100644 --- a/fat-bits/src/lib.rs +++ b/fat-bits/src/lib.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::io::{Read, Seek, SeekFrom, Write}; use std::rc::Rc; -use crate::dir::{DirIter, RegularDirEntry}; +use crate::dir::{DirEntry, DirIter}; use crate::fat::{FatError, Fatty}; use crate::subslice::{SubSlice, SubSliceMut}; @@ -224,7 +224,7 @@ impl FatFs { Ok(data) } - pub fn root_dir_iter(&self) -> Box + '_> { + pub fn root_dir_iter(&self) -> Box + '_> { // TODO: maybe wrap this in another RootDirIter enum, so we don't have to Box if let Some(root_dir_offset) = self.root_dir_offset { diff --git a/fat-dump/src/main.rs b/fat-dump/src/main.rs index a227756..1407e9e 100644 --- a/fat-dump/src/main.rs +++ b/fat-dump/src/main.rs @@ -1,4 +1,4 @@ -use fat_bits::dir::{DirIter, RegularDirEntry}; +use fat_bits::dir::{DirIter, DirEntry}; use fat_bits::fat::Fatty as _; use fat_bits::{FatFs, SliceLike}; @@ -50,7 +50,7 @@ fn tree(fat_fs: &FatFs, show_hidden: bool) { fn tree_impl( fat_fs: &FatFs, - iter: impl Iterator, + iter: impl Iterator, show_hidden: bool, indent: u32, ) {