Skip to content

Commit

Permalink
Expose directory tell, seek and rewind functionnality
Browse files Browse the repository at this point in the history
  • Loading branch information
sosthene-nitrokey committed Mar 3, 2025
1 parent bc4b454 commit 837c389
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 11 deletions.
64 changes: 64 additions & 0 deletions src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -940,6 +945,65 @@ pub struct ReadDir<'a, 'b, S: driver::Storage> {
path: &'b Path,
}

impl<S: driver::Storage> 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<DirIterationTell> {
let value = unsafe {
ll::lfs_dir_tell(
&mut self.fs.alloc.borrow_mut().state,
&mut (**self.alloc.borrow_mut()).state,
)
};
if value < 0 {
Err(result_from((), value).unwrap_err())
} else {
Ok(DirIterationTell {
tell_result: value as _,
})
}
}

/// 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<S: driver::Storage> Iterator for ReadDir<'_, '_, S> {
type Item = Result<DirEntry>;

Expand Down
49 changes: 38 additions & 11 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down Expand Up @@ -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();
}
Expand Down
Binary file added tests-old-fs/empty.bin
Binary file not shown.
Binary file added tests-old-fs/recurse.bin
Binary file not shown.
Binary file added tests-old-fs/root.bin
Binary file not shown.
185 changes: 185 additions & 0 deletions tests/create_old_fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
use littlefs2::{
consts,
fs::Filesystem,
path,
path::{Path, PathBuf},
ram_storage,
};

ram_storage!(
name = RamStorage,
backend = Ram,
erase_value = 0xff,
read_size = 20 * 5,
write_size = 20 * 7,
cache_size_ty = consts::U700,
block_size = 20 * 35,
block_count = 32,
lookahead_size_ty = consts::U16,
filename_max_plus_one_ty = consts::U256,
path_max_plus_one_ty = consts::U256,
);

struct FileTest {
name: &'static Path,
content: &'static [u8],
}

struct DirTest {
files: &'static [FileTest],
sub_dirs: &'static [(&'static Path, DirTest)],
}

struct FsTest {
root: DirTest,
name: &'static str,
}

const EMPTY_DIR: FsTest = FsTest {
name: "empty.bin",
root: DirTest {
files: &[],
sub_dirs: &[],
},
};

const ROOT_FULL: FsTest = FsTest {
name: "root.bin",
root: DirTest {
files: &[
FileTest {
name: path!("test_file.txt"),
content: b"Test content - test_file.txt",
},
FileTest {
name: path!("test_file2.txt"),
content: b"Test content - test_file2.txt",
},
FileTest {
name: path!("test_file3.txt"),
content: b"Test content - test_file3.txt",
},
],
sub_dirs: &[],
},
};

const RECURSE: FsTest = FsTest {
name: "recurse.bin",
root: DirTest {
files: ROOT_FULL.root.files,
sub_dirs: &[
(
path!("root1"),
DirTest {
files: &[
FileTest {
name: path!("test_sub_file.txt"),
content: b"Test content - test_sub_file.txt",
},
FileTest {
name: path!("test_sub_file2.txt"),
content: b"Test content - test_sub_file2.txt",
},
],
sub_dirs: &[(
path!("sub-dir"),
DirTest {
files: &[
FileTest {
name: path!("test_sub_sub_file.txt"),
content: b"Test content - test_sub_sub_file.txt",
},
FileTest {
name: path!("test_sub_sub_file2.txt"),
content: b"Test content - test_sub_sub_file2.txt",
},
],
sub_dirs: &[],
},
)],
},
),
(
path!("root2"),
DirTest {
files: &[],
sub_dirs: &[],
},
),
],
},
};

const ALL: &[FsTest] = &[EMPTY_DIR, ROOT_FULL, RECURSE];

fn write_dir(fs: &Filesystem<RamStorage>, 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<RamStorage>, 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();
}
}

0 comments on commit 837c389

Please sign in to comment.