From 4fb156b1d1a70b504058081e4185eb31aec0c6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 13 Mar 2023 14:36:35 +0100 Subject: [PATCH 1/2] Expose directory `tell`, `seek` and `rewind` functionnality --- src/fs.rs | 64 ++++++++++++++ src/tests.rs | 49 ++++++++--- tests-old-fs/empty.bin | Bin 0 -> 22400 bytes tests-old-fs/recurse.bin | Bin 0 -> 22400 bytes tests-old-fs/root.bin | Bin 0 -> 22400 bytes tests/create_old_fs.rs | 185 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 287 insertions(+), 11 deletions(-) create mode 100644 tests-old-fs/empty.bin create mode 100644 tests-old-fs/recurse.bin create mode 100644 tests-old-fs/root.bin create mode 100644 tests/create_old_fs.rs diff --git a/src/fs.rs b/src/fs.rs index 3f33d9585..548268db7 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -932,6 +932,11 @@ impl ReadDirAllocation { } } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct DirIterationTell { + tell_result: ll::lfs_off_t, +} + pub struct ReadDir<'a, 'b, S: driver::Storage> { // We must store a raw pointer here since the FFI retains a copy of a pointer // to the field alloc.state, so we cannot assert unique mutable access. @@ -940,6 +945,65 @@ pub struct ReadDir<'a, 'b, S: driver::Storage> { path: &'b Path, } +impl ReadDir<'_, '_, S> { + /// Return the position of the directory + /// + /// The returned offset is only meant to be consumed by seek and may not make + /// sense, but does indicate the current position in the directory iteration. + /// + /// Returns the position of the directory, which can be returned to using [`seek`](Self::seek). + pub fn tell(&self) -> Result { + let value = unsafe { + ll::lfs_dir_tell( + &mut self.fs.alloc.borrow_mut().state, + &mut (**self.alloc.borrow_mut()).state, + ) + }; + if let Ok(value_positive) = value.try_into() { + Ok(DirIterationTell { + tell_result: value_positive, + }) + } else { + Err(result_from((), value as _).unwrap_err()) + } + } + + /// Change the position of the directory + /// + /// The new off must be a value previous returned from [`tell`](Self::tell) and specifies + /// an absolute offset in the directory seek. + pub fn seek(&mut self, state: DirIterationTell) -> Result<()> { + let value = unsafe { + ll::lfs_dir_seek( + &mut self.fs.alloc.borrow_mut().state, + &mut (**self.alloc.borrow_mut()).state, + state.tell_result, + ) + }; + if value < 0 { + Err(result_from((), value as _).unwrap_err()) + } else { + Ok(()) + } + } + + /// Change the position of the directory to the beginning of the directory + pub fn rewind(&mut self) -> Result<()> { + let res = unsafe { + ll::lfs_dir_rewind( + &mut self.fs.alloc.borrow_mut().state, + &mut (**self.alloc.borrow_mut()).state, + ) + }; + + if res < 0 { + Err(result_from((), res).unwrap_err()) + } else { + Ok(()) + } + } +} + impl Iterator for ReadDir<'_, '_, S> { type Item = Result; diff --git a/src/tests.rs b/src/tests.rs index 20bc09299..742de3d30 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -4,7 +4,9 @@ use generic_array::typenum::consts; use crate::{ fs::{Attribute, File, Filesystem}, io::{Error, OpenSeekFrom, Read, Result, SeekFrom}, - path, BACKEND_VERSION, DISK_VERSION, + path, + path::PathBuf, + BACKEND_VERSION, DISK_VERSION, }; ram_storage!( @@ -492,31 +494,56 @@ fn test_iter_dirs() { file.set_len(37)?; fs.create_file_and_then(path!("/tmp/file.b"), |file| file.set_len(42)) })?; + let mut tells = Vec::new(); - fs.read_dir_and_then(path!("/tmp"), |dir| { + fs.read_dir_and_then(path!("/tmp"), |mut dir| { let mut found_files: usize = 0; let mut sizes = [0usize; 4]; + let mut i = 0; - for (i, entry) in dir.enumerate() { + tells.push(dir.tell()?); + while let Some(entry) = dir.next() { + tells.push(dir.tell()?); let entry = entry?; - // assert_eq!(entry.file_name(), match i { - // 0 => b".\0", - // 1 => b"..\0", - // 2 => b"file.a\0", - // 3 => b"file.b\0", - // _ => panic!("oh noes"), - // }); + let expected_name = match i { + 0 => PathBuf::from(path!(".")), + 1 => PathBuf::from(path!("..")), + 2 => PathBuf::from(path!("file.a")), + 3 => PathBuf::from(path!("file.b")), + _ => panic!("oh noes"), + }; + + assert_eq!(entry.file_name(), &*expected_name); sizes[i] = entry.metadata().len(); found_files += 1; + i += 1; } assert_eq!(sizes, [0, 0, 37, 42]); assert_eq!(found_files, 4); - + assert_eq!(tells.len(), 5); + + for (i, tell) in tells.iter().enumerate() { + dir.rewind().unwrap(); + let mut found_files: usize = 0; + let mut sizes = Vec::new(); + dir.seek(*tell)?; + + for entry in &mut dir { + let entry = entry?; + sizes.push(entry.metadata().len()); + found_files += 1; + } + + assert_eq!(sizes, [0, 0, 37, 42][i..]); + assert_eq!(found_files, 4 - i); + } Ok(()) }) + .unwrap(); + Ok(()) }) .unwrap(); } diff --git a/tests-old-fs/empty.bin b/tests-old-fs/empty.bin new file mode 100644 index 0000000000000000000000000000000000000000..1eb401e047c27dcfcb44d1e2a4774035c4c74c4e GIT binary patch literal 22400 zcmeIwF%Cgd5CzcdOSJ5OTEil&L_#9@g~X56#w@@xEI~(s=UIZrZ_XrBOs2Uxxq~FP zak;mqcCj04gHM%^G^)>{07pN8esk`gfeA#3W7{~Eft|l6hJ0U<)mAg1NiIn2xV2V1JXh=-lAwX*?^;+9AAsB-p#+k)g z8J(2HsWX$Ke}Dt$Wf8F&8r zy??NMAsx*a>NX=9kQi!DnTnU>h5F3OQH8%m$2lv;8>td@^-<-Iqv?41RjB^_iLlN@#{ z%Z}3LL6*(6r@Y>K{kVbv0tg_000IagfB*srAbh=Cr=&&2$C~k? zxiOvm*9Ml|e4UY2JFUJrmFM%j`tOwN=F<9=H>1ZCQkJ{FUjv`cSTg4B7i0ZK+>xZu z$)dP~ErxUe-DYU8WH+8{)F%ewP7&T;Ry)w86-x%)etAv3k2#XGZc_e_U*GrJFO!Eo zJV*7XcTLowQwPlq2c^zjmB%o?m@iAG7xR+DxGYD;Y$|;&onD^Jf33PP3vl~h6*z%) zH|%*9)E8Z2qPAqiQs^GPnpxXE25edCPPZ4ZGgeT0x28uw#~DfL?wr)!k#?ET0d%Pt zkwTaIw0&+X&J^LjWu-%1bnRWKo401-kHiWH^qfFYci+{FdPjS>(pd86+n)O)1V#V> I1bRf^4_MQHVgLXD literal 0 HcmV?d00001 diff --git a/tests-old-fs/root.bin b/tests-old-fs/root.bin new file mode 100644 index 0000000000000000000000000000000000000000..6b7fad7913a5089b214ddfe59e8722018d567f0c GIT binary patch literal 22400 zcmeIwF=_%q6b9gjZW9nS5o`AXi4eP-AVtdb2Eq!$vI#haoo7n#VLXCex@^Xvc+Xwc5-NF^=fPz86vEUVGYYyZh>&&c!rUo3vc5>*_9T zQ@(KhoD1daTa&6LmAB=u{@~_ZxbG$=mnOOk#pFD)=thR`vt#oyUcTg)SOhwOB4_j8 zXZK>, dir: &DirTest, current_dir: PathBuf) { + println!("Writing current_dir: {current_dir}"); + for f in dir.files { + let mut buf = current_dir.clone(); + buf.push(f.name); + println!( + "Writing {}, ({})", + f.name, + std::str::from_utf8(f.content).unwrap() + ); + fs.write(&buf, f.content).unwrap(); + } + + for (name, d) in dir.sub_dirs { + let mut buf = current_dir.clone(); + buf.push(name); + fs.create_dir(&buf).unwrap(); + write_dir(fs, d, buf); + } +} + +fn read_dir(fs: &Filesystem, dir: &DirTest, current_dir: PathBuf) { + println!("Reading current_dir: {current_dir}"); + for f in dir.files { + let mut buf = current_dir.clone(); + buf.push(f.name); + dbg!(&buf); + let read = fs.read::<1024>(&buf).unwrap(); + assert_eq!(std::str::from_utf8(&read), std::str::from_utf8(f.content)); + } + + for (name, d) in dir.sub_dirs { + let mut buf = current_dir.clone(); + buf.push(name); + read_dir(fs, d, buf); + } +} + +#[test] +#[ignore] +fn create() { + for fs_test in ALL { + println!("Got to test: {}", fs_test.name); + let mut backend = Ram::default(); + let mut storage = RamStorage::new(&mut backend); + Filesystem::format(&mut storage).unwrap(); + Filesystem::mount_and_then(&mut storage, |fs| { + write_dir(fs, &fs_test.root, PathBuf::new()); + Ok(()) + }) + .unwrap(); + std::fs::write(format!("tests-old-fs/{}", fs_test.name), backend.buf).unwrap(); + } +} + +#[test] +fn read() { + for fs_test in ALL { + println!("Got to test: {}", fs_test.name); + let mut backend = Ram::default(); + let buf = std::fs::read(format!("tests-old-fs/{}", fs_test.name)).unwrap(); + backend.buf.copy_from_slice(&buf); + let mut storage = RamStorage::new(&mut backend); + Filesystem::mount_and_then(&mut storage, |fs| { + read_dir(fs, &fs_test.root, PathBuf::new()); + Ok(()) + }) + .unwrap(); + } +} From fac677f423d5c33bd307cf02eae548549818476d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 3 Mar 2025 17:21:57 +0100 Subject: [PATCH 2/2] Add dir tell, seek and rewind to object_safe abstractions --- core/src/lib.rs | 5 ++++- core/src/object_safe.rs | 28 ++++++++++++++++++++++++++-- src/fs.rs | 18 ++++++++---------- src/tests.rs | 1 + 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 439e5f038..7f1c14629 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -13,7 +13,10 @@ mod path; pub use fs::{Attribute, DirEntry, FileOpenFlags, FileType, Metadata}; pub use io::{Error, OpenSeekFrom, Read, Result, Seek, SeekFrom, Write}; -pub use object_safe::{DirEntriesCallback, DynFile, DynFilesystem, FileCallback, Predicate, Vec}; +pub use object_safe::{ + DirEntriesCallback, DirIterationTell, DirIterator, DynFile, DynFilesystem, FileCallback, + Predicate, Vec, +}; pub use path::{Ancestors, Iter, Path, PathBuf, PathError}; /// Creates a path from a string without a trailing null. diff --git a/core/src/object_safe.rs b/core/src/object_safe.rs index d4138d150..1ec779ab8 100644 --- a/core/src/object_safe.rs +++ b/core/src/object_safe.rs @@ -8,8 +8,7 @@ use crate::{ const _: Option<&dyn DynFile> = None; const _: Option<&dyn DynFilesystem> = None; -pub type DirEntriesCallback<'a, R = ()> = - &'a mut dyn FnMut(&mut dyn Iterator>) -> Result; +pub type DirEntriesCallback<'a, R = ()> = &'a mut dyn FnMut(&mut dyn DirIterator) -> Result; pub type FileCallback<'a, R = ()> = &'a mut dyn FnMut(&dyn DynFile) -> Result; pub type Predicate<'a> = &'a dyn Fn(&DirEntry) -> bool; @@ -87,6 +86,31 @@ impl dyn DynFile + '_ { } } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct DirIterationTell { + #[doc(hidden)] + pub tell_result: u32, +} + +pub trait DirIterator: Iterator> { + /// Return the position of the directory + /// + /// The returned offset is only meant to be consumed by seek and may not make + /// sense, but does indicate the current position in the directory iteration. + /// + /// Returns the position of the directory, which can be returned to using [`seek`](Self::seek). + fn tell(&self) -> Result; + + /// Change the position of the directory + /// + /// The new off must be a value previous returned from [`tell`](Self::tell) and specifies + /// an absolute offset in the directory seek. + fn seek(&self, state: DirIterationTell) -> Result<()>; + + /// Change the position of the directory to the beginning of the directory + fn rewind(&self) -> Result<()>; +} + /// Object-safe trait for filesystems. /// /// The following methods cannot support generic return types in the callbacks: diff --git a/src/fs.rs b/src/fs.rs index 548268db7..f2d7accdb 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -8,12 +8,15 @@ use core::{ mem, slice, }; use generic_array::typenum::marker_traits::Unsigned; +use littlefs2_core::DirIterator; use littlefs2_sys as ll; // so far, don't need `heapless-bytes`. pub type Bytes = generic_array::GenericArray; -pub use littlefs2_core::{Attribute, DirEntry, FileOpenFlags, FileType, Metadata}; +pub use littlefs2_core::{ + Attribute, DirEntry, DirIterationTell, FileOpenFlags, FileType, Metadata, +}; use crate::{ driver, @@ -932,11 +935,6 @@ impl ReadDirAllocation { } } -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct DirIterationTell { - tell_result: ll::lfs_off_t, -} - pub struct ReadDir<'a, 'b, S: driver::Storage> { // We must store a raw pointer here since the FFI retains a copy of a pointer // to the field alloc.state, so we cannot assert unique mutable access. @@ -945,14 +943,14 @@ pub struct ReadDir<'a, 'b, S: driver::Storage> { path: &'b Path, } -impl ReadDir<'_, '_, S> { +impl DirIterator for ReadDir<'_, '_, S> { /// Return the position of the directory /// /// The returned offset is only meant to be consumed by seek and may not make /// sense, but does indicate the current position in the directory iteration. /// /// Returns the position of the directory, which can be returned to using [`seek`](Self::seek). - pub fn tell(&self) -> Result { + fn tell(&self) -> Result { let value = unsafe { ll::lfs_dir_tell( &mut self.fs.alloc.borrow_mut().state, @@ -972,7 +970,7 @@ impl ReadDir<'_, '_, S> { /// /// The new off must be a value previous returned from [`tell`](Self::tell) and specifies /// an absolute offset in the directory seek. - pub fn seek(&mut self, state: DirIterationTell) -> Result<()> { + fn seek(&self, state: DirIterationTell) -> Result<()> { let value = unsafe { ll::lfs_dir_seek( &mut self.fs.alloc.borrow_mut().state, @@ -988,7 +986,7 @@ impl ReadDir<'_, '_, S> { } /// Change the position of the directory to the beginning of the directory - pub fn rewind(&mut self) -> Result<()> { + fn rewind(&self) -> Result<()> { let res = unsafe { ll::lfs_dir_rewind( &mut self.fs.alloc.borrow_mut().state, diff --git a/src/tests.rs b/src/tests.rs index 742de3d30..5debe312e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -8,6 +8,7 @@ use crate::{ path::PathBuf, BACKEND_VERSION, DISK_VERSION, }; +use littlefs2_core::DirIterator; ram_storage!( name = OtherRamStorage,