Skip to content

Commit 4fb156b

Browse files
Expose directory tell, seek and rewind functionnality
1 parent bc4b454 commit 4fb156b

File tree

6 files changed

+287
-11
lines changed

6 files changed

+287
-11
lines changed

src/fs.rs

+64
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,11 @@ impl ReadDirAllocation {
932932
}
933933
}
934934

935+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
936+
pub struct DirIterationTell {
937+
tell_result: ll::lfs_off_t,
938+
}
939+
935940
pub struct ReadDir<'a, 'b, S: driver::Storage> {
936941
// We must store a raw pointer here since the FFI retains a copy of a pointer
937942
// to the field alloc.state, so we cannot assert unique mutable access.
@@ -940,6 +945,65 @@ pub struct ReadDir<'a, 'b, S: driver::Storage> {
940945
path: &'b Path,
941946
}
942947

948+
impl<S: driver::Storage> ReadDir<'_, '_, S> {
949+
/// Return the position of the directory
950+
///
951+
/// The returned offset is only meant to be consumed by seek and may not make
952+
/// sense, but does indicate the current position in the directory iteration.
953+
///
954+
/// Returns the position of the directory, which can be returned to using [`seek`](Self::seek).
955+
pub fn tell(&self) -> Result<DirIterationTell> {
956+
let value = unsafe {
957+
ll::lfs_dir_tell(
958+
&mut self.fs.alloc.borrow_mut().state,
959+
&mut (**self.alloc.borrow_mut()).state,
960+
)
961+
};
962+
if let Ok(value_positive) = value.try_into() {
963+
Ok(DirIterationTell {
964+
tell_result: value_positive,
965+
})
966+
} else {
967+
Err(result_from((), value as _).unwrap_err())
968+
}
969+
}
970+
971+
/// Change the position of the directory
972+
///
973+
/// The new off must be a value previous returned from [`tell`](Self::tell) and specifies
974+
/// an absolute offset in the directory seek.
975+
pub fn seek(&mut self, state: DirIterationTell) -> Result<()> {
976+
let value = unsafe {
977+
ll::lfs_dir_seek(
978+
&mut self.fs.alloc.borrow_mut().state,
979+
&mut (**self.alloc.borrow_mut()).state,
980+
state.tell_result,
981+
)
982+
};
983+
if value < 0 {
984+
Err(result_from((), value as _).unwrap_err())
985+
} else {
986+
Ok(())
987+
}
988+
}
989+
990+
/// Change the position of the directory to the beginning of the directory
991+
pub fn rewind(&mut self) -> Result<()> {
992+
let res = unsafe {
993+
ll::lfs_dir_rewind(
994+
&mut self.fs.alloc.borrow_mut().state,
995+
&mut (**self.alloc.borrow_mut()).state,
996+
)
997+
};
998+
999+
if res < 0 {
1000+
Err(result_from((), res).unwrap_err())
1001+
} else {
1002+
Ok(())
1003+
}
1004+
}
1005+
}
1006+
9431007
impl<S: driver::Storage> Iterator for ReadDir<'_, '_, S> {
9441008
type Item = Result<DirEntry>;
9451009

src/tests.rs

+38-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use generic_array::typenum::consts;
44
use crate::{
55
fs::{Attribute, File, Filesystem},
66
io::{Error, OpenSeekFrom, Read, Result, SeekFrom},
7-
path, BACKEND_VERSION, DISK_VERSION,
7+
path,
8+
path::PathBuf,
9+
BACKEND_VERSION, DISK_VERSION,
810
};
911

1012
ram_storage!(
@@ -492,31 +494,56 @@ fn test_iter_dirs() {
492494
file.set_len(37)?;
493495
fs.create_file_and_then(path!("/tmp/file.b"), |file| file.set_len(42))
494496
})?;
497+
let mut tells = Vec::new();
495498

496-
fs.read_dir_and_then(path!("/tmp"), |dir| {
499+
fs.read_dir_and_then(path!("/tmp"), |mut dir| {
497500
let mut found_files: usize = 0;
498501
let mut sizes = [0usize; 4];
502+
let mut i = 0;
499503

500-
for (i, entry) in dir.enumerate() {
504+
tells.push(dir.tell()?);
505+
while let Some(entry) = dir.next() {
506+
tells.push(dir.tell()?);
501507
let entry = entry?;
502508

503-
// assert_eq!(entry.file_name(), match i {
504-
// 0 => b".\0",
505-
// 1 => b"..\0",
506-
// 2 => b"file.a\0",
507-
// 3 => b"file.b\0",
508-
// _ => panic!("oh noes"),
509-
// });
509+
let expected_name = match i {
510+
0 => PathBuf::from(path!(".")),
511+
1 => PathBuf::from(path!("..")),
512+
2 => PathBuf::from(path!("file.a")),
513+
3 => PathBuf::from(path!("file.b")),
514+
_ => panic!("oh noes"),
515+
};
516+
517+
assert_eq!(entry.file_name(), &*expected_name);
510518

511519
sizes[i] = entry.metadata().len();
512520
found_files += 1;
521+
i += 1;
513522
}
514523

515524
assert_eq!(sizes, [0, 0, 37, 42]);
516525
assert_eq!(found_files, 4);
517-
526+
assert_eq!(tells.len(), 5);
527+
528+
for (i, tell) in tells.iter().enumerate() {
529+
dir.rewind().unwrap();
530+
let mut found_files: usize = 0;
531+
let mut sizes = Vec::new();
532+
dir.seek(*tell)?;
533+
534+
for entry in &mut dir {
535+
let entry = entry?;
536+
sizes.push(entry.metadata().len());
537+
found_files += 1;
538+
}
539+
540+
assert_eq!(sizes, [0, 0, 37, 42][i..]);
541+
assert_eq!(found_files, 4 - i);
542+
}
518543
Ok(())
519544
})
545+
.unwrap();
546+
Ok(())
520547
})
521548
.unwrap();
522549
}

tests-old-fs/empty.bin

21.9 KB
Binary file not shown.

tests-old-fs/recurse.bin

21.9 KB
Binary file not shown.

tests-old-fs/root.bin

21.9 KB
Binary file not shown.

tests/create_old_fs.rs

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use littlefs2::{
2+
consts,
3+
fs::Filesystem,
4+
path,
5+
path::{Path, PathBuf},
6+
ram_storage,
7+
};
8+
9+
ram_storage!(
10+
name = RamStorage,
11+
backend = Ram,
12+
erase_value = 0xff,
13+
read_size = 20 * 5,
14+
write_size = 20 * 7,
15+
cache_size_ty = consts::U700,
16+
block_size = 20 * 35,
17+
block_count = 32,
18+
lookahead_size_ty = consts::U16,
19+
filename_max_plus_one_ty = consts::U256,
20+
path_max_plus_one_ty = consts::U256,
21+
);
22+
23+
struct FileTest {
24+
name: &'static Path,
25+
content: &'static [u8],
26+
}
27+
28+
struct DirTest {
29+
files: &'static [FileTest],
30+
sub_dirs: &'static [(&'static Path, DirTest)],
31+
}
32+
33+
struct FsTest {
34+
root: DirTest,
35+
name: &'static str,
36+
}
37+
38+
const EMPTY_DIR: FsTest = FsTest {
39+
name: "empty.bin",
40+
root: DirTest {
41+
files: &[],
42+
sub_dirs: &[],
43+
},
44+
};
45+
46+
const ROOT_FULL: FsTest = FsTest {
47+
name: "root.bin",
48+
root: DirTest {
49+
files: &[
50+
FileTest {
51+
name: path!("test_file.txt"),
52+
content: b"Test content - test_file.txt",
53+
},
54+
FileTest {
55+
name: path!("test_file2.txt"),
56+
content: b"Test content - test_file2.txt",
57+
},
58+
FileTest {
59+
name: path!("test_file3.txt"),
60+
content: b"Test content - test_file3.txt",
61+
},
62+
],
63+
sub_dirs: &[],
64+
},
65+
};
66+
67+
const RECURSE: FsTest = FsTest {
68+
name: "recurse.bin",
69+
root: DirTest {
70+
files: ROOT_FULL.root.files,
71+
sub_dirs: &[
72+
(
73+
path!("root1"),
74+
DirTest {
75+
files: &[
76+
FileTest {
77+
name: path!("test_sub_file.txt"),
78+
content: b"Test content - test_sub_file.txt",
79+
},
80+
FileTest {
81+
name: path!("test_sub_file2.txt"),
82+
content: b"Test content - test_sub_file2.txt",
83+
},
84+
],
85+
sub_dirs: &[(
86+
path!("sub-dir"),
87+
DirTest {
88+
files: &[
89+
FileTest {
90+
name: path!("test_sub_sub_file.txt"),
91+
content: b"Test content - test_sub_sub_file.txt",
92+
},
93+
FileTest {
94+
name: path!("test_sub_sub_file2.txt"),
95+
content: b"Test content - test_sub_sub_file2.txt",
96+
},
97+
],
98+
sub_dirs: &[],
99+
},
100+
)],
101+
},
102+
),
103+
(
104+
path!("root2"),
105+
DirTest {
106+
files: &[],
107+
sub_dirs: &[],
108+
},
109+
),
110+
],
111+
},
112+
};
113+
114+
const ALL: &[FsTest] = &[EMPTY_DIR, ROOT_FULL, RECURSE];
115+
116+
fn write_dir(fs: &Filesystem<RamStorage>, dir: &DirTest, current_dir: PathBuf) {
117+
println!("Writing current_dir: {current_dir}");
118+
for f in dir.files {
119+
let mut buf = current_dir.clone();
120+
buf.push(f.name);
121+
println!(
122+
"Writing {}, ({})",
123+
f.name,
124+
std::str::from_utf8(f.content).unwrap()
125+
);
126+
fs.write(&buf, f.content).unwrap();
127+
}
128+
129+
for (name, d) in dir.sub_dirs {
130+
let mut buf = current_dir.clone();
131+
buf.push(name);
132+
fs.create_dir(&buf).unwrap();
133+
write_dir(fs, d, buf);
134+
}
135+
}
136+
137+
fn read_dir(fs: &Filesystem<RamStorage>, dir: &DirTest, current_dir: PathBuf) {
138+
println!("Reading current_dir: {current_dir}");
139+
for f in dir.files {
140+
let mut buf = current_dir.clone();
141+
buf.push(f.name);
142+
dbg!(&buf);
143+
let read = fs.read::<1024>(&buf).unwrap();
144+
assert_eq!(std::str::from_utf8(&read), std::str::from_utf8(f.content));
145+
}
146+
147+
for (name, d) in dir.sub_dirs {
148+
let mut buf = current_dir.clone();
149+
buf.push(name);
150+
read_dir(fs, d, buf);
151+
}
152+
}
153+
154+
#[test]
155+
#[ignore]
156+
fn create() {
157+
for fs_test in ALL {
158+
println!("Got to test: {}", fs_test.name);
159+
let mut backend = Ram::default();
160+
let mut storage = RamStorage::new(&mut backend);
161+
Filesystem::format(&mut storage).unwrap();
162+
Filesystem::mount_and_then(&mut storage, |fs| {
163+
write_dir(fs, &fs_test.root, PathBuf::new());
164+
Ok(())
165+
})
166+
.unwrap();
167+
std::fs::write(format!("tests-old-fs/{}", fs_test.name), backend.buf).unwrap();
168+
}
169+
}
170+
171+
#[test]
172+
fn read() {
173+
for fs_test in ALL {
174+
println!("Got to test: {}", fs_test.name);
175+
let mut backend = Ram::default();
176+
let buf = std::fs::read(format!("tests-old-fs/{}", fs_test.name)).unwrap();
177+
backend.buf.copy_from_slice(&buf);
178+
let mut storage = RamStorage::new(&mut backend);
179+
Filesystem::mount_and_then(&mut storage, |fs| {
180+
read_dir(fs, &fs_test.root, PathBuf::new());
181+
Ok(())
182+
})
183+
.unwrap();
184+
}
185+
}

0 commit comments

Comments
 (0)