Skip to content

Commit 52e0e4f

Browse files
authored
Merge pull request #157 from rust-embedded-community/add-lfn-support
Add LFN support when iterating directories
2 parents 1b9fa5c + 837ae27 commit 52e0e4f

14 files changed

+682
-162
lines changed

CHANGELOG.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,21 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic
88

99
### Changed
1010

11-
- None
11+
- __Breaking Change__: `VolumeManager` now uses interior-mutability (with a `RefCell`) and so most methods are now `&self`. This also makes it easier to open multiple `File`, `Directory` or `Volume` objects at once.
12+
- __Breaking Change__: The `VolumeManager`, `File`, `Directory` and `Volume` no longer implement `Send` or `Sync.
13+
- `VolumeManager` uses an interior block cache of 512 bytes, increasing its size by about 520 bytes but hugely reducing stack space required at run-time.
14+
- __Breaking Change__: The `VolumeManager::device` method now takes a callback rather than giving you a reference to the underlying `BlockDevice`
15+
- __Breaking Change__: `Error:LockError` variant added.
16+
- __Breaking Change__: `SearchId` was renamed to `Handle`
1217

1318
### Added
1419

1520
- `File` now implements the `embedded-io` `Read`, `Write` and `Seek` traits.
21+
- New `iterate_dir_lfn` method on `VolumeManager` and `Directory` - provides decoded Long File Names as `Option<&str>`
1622

1723
### Removed
1824

19-
- None
25+
- __Breaking Change__: Removed the `reason: &str` argument from `BlockDevice`
2026

2127
## [Version 0.8.0] - 2024-07-12
2228

examples/shell.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@
7171
7272
use std::{cell::RefCell, io::prelude::*};
7373

74-
use embedded_sdmmc::{Error as EsError, RawDirectory, RawVolume, ShortFileName, VolumeIdx};
74+
use embedded_sdmmc::{
75+
Error as EsError, LfnBuffer, RawDirectory, RawVolume, ShortFileName, VolumeIdx,
76+
};
7577

7678
type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4>;
7779
type Directory<'a> = embedded_sdmmc::Directory<'a, LinuxBlockDevice, Clock, 8, 8, 4>;
@@ -229,17 +231,24 @@ impl Context {
229231
fn dir(&self, path: &Path) -> Result<(), Error> {
230232
println!("Directory listing of {:?}", path);
231233
let dir = self.resolve_existing_directory(path)?;
232-
dir.iterate_dir(|entry| {
233-
if !entry.attributes.is_volume() && !entry.attributes.is_lfn() {
234-
println!(
235-
"{:12} {:9} {} {} {:08X?} {:?}",
234+
let mut storage = [0u8; 128];
235+
let mut lfn_buffer = LfnBuffer::new(&mut storage);
236+
dir.iterate_dir_lfn(&mut lfn_buffer, |entry, lfn| {
237+
if !entry.attributes.is_volume() {
238+
print!(
239+
"{:12} {:9} {} {} {:08X?} {:5?}",
236240
entry.name,
237241
entry.size,
238242
entry.ctime,
239243
entry.mtime,
240244
entry.cluster,
241-
entry.attributes
245+
entry.attributes,
242246
);
247+
if let Some(lfn) = lfn {
248+
println!(" {:?}", lfn);
249+
} else {
250+
println!();
251+
}
243252
}
244253
})?;
245254
Ok(())

src/fat/mod.rs

+51-18
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ mod test {
8484
fn test_dir_entries() {
8585
#[derive(Debug)]
8686
enum Expected {
87-
Lfn(bool, u8, [char; 13]),
87+
Lfn(bool, u8, u8, [u16; 13]),
8888
Short(DirEntry),
8989
}
9090
let raw_data = r#"
@@ -105,6 +105,7 @@ mod test {
105105
422e0064007400620000000f0059ffffffffffffffffffffffff0000ffffffff B..d.t.b.....Y..................
106106
01620063006d00320037000f0059300038002d0072007000690000002d006200 .b.c.m.2.7...Y0.8.-.r.p.i...-.b.
107107
"#;
108+
108109
let results = [
109110
Expected::Short(DirEntry {
110111
name: unsafe {
@@ -123,9 +124,10 @@ mod test {
123124
Expected::Lfn(
124125
true,
125126
1,
127+
0x47,
126128
[
127-
'o', 'v', 'e', 'r', 'l', 'a', 'y', 's', '\u{0000}', '\u{ffff}', '\u{ffff}',
128-
'\u{ffff}', '\u{ffff}',
129+
'o' as u16, 'v' as u16, 'e' as u16, 'r' as u16, 'l' as u16, 'a' as u16,
130+
'y' as u16, 's' as u16, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
129131
],
130132
),
131133
Expected::Short(DirEntry {
@@ -141,16 +143,20 @@ mod test {
141143
Expected::Lfn(
142144
true,
143145
2,
146+
0x79,
144147
[
145-
'-', 'p', 'l', 'u', 's', '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}',
146-
'\u{ffff}', '\u{ffff}',
148+
'-' as u16, 'p' as u16, 'l' as u16, 'u' as u16, 's' as u16, '.' as u16,
149+
'd' as u16, 't' as u16, 'b' as u16, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF,
147150
],
148151
),
149152
Expected::Lfn(
150153
false,
151154
1,
155+
0x79,
152156
[
153-
'b', 'c', 'm', '2', '7', '0', '8', '-', 'r', 'p', 'i', '-', 'b',
157+
'b' as u16, 'c' as u16, 'm' as u16, '2' as u16, '7' as u16, '0' as u16,
158+
'8' as u16, '-' as u16, 'r' as u16, 'p' as u16, 'i' as u16, '-' as u16,
159+
'b' as u16,
154160
],
155161
),
156162
Expected::Short(DirEntry {
@@ -166,8 +172,11 @@ mod test {
166172
Expected::Lfn(
167173
true,
168174
1,
175+
0x12,
169176
[
170-
'C', 'O', 'P', 'Y', 'I', 'N', 'G', '.', 'l', 'i', 'n', 'u', 'x',
177+
'C' as u16, 'O' as u16, 'P' as u16, 'Y' as u16, 'I' as u16, 'N' as u16,
178+
'G' as u16, '.' as u16, 'l' as u16, 'i' as u16, 'n' as u16, 'u' as u16,
179+
'x' as u16,
171180
],
172181
),
173182
Expected::Short(DirEntry {
@@ -183,16 +192,31 @@ mod test {
183192
Expected::Lfn(
184193
true,
185194
2,
195+
0x67,
186196
[
187-
'c', 'o', 'm', '\u{0}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
188-
'\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
197+
'c' as u16,
198+
'o' as u16,
199+
'm' as u16,
200+
'\u{0}' as u16,
201+
0xFFFF,
202+
0xFFFF,
203+
0xFFFF,
204+
0xFFFF,
205+
0xFFFF,
206+
0xFFFF,
207+
0xFFFF,
208+
0xFFFF,
209+
0xFFFF,
189210
],
190211
),
191212
Expected::Lfn(
192213
false,
193214
1,
215+
0x67,
194216
[
195-
'L', 'I', 'C', 'E', 'N', 'C', 'E', '.', 'b', 'r', 'o', 'a', 'd',
217+
'L' as u16, 'I' as u16, 'C' as u16, 'E' as u16, 'N' as u16, 'C' as u16,
218+
'E' as u16, '.' as u16, 'b' as u16, 'r' as u16, 'o' as u16, 'a' as u16,
219+
'd' as u16,
196220
],
197221
),
198222
Expected::Short(DirEntry {
@@ -208,16 +232,20 @@ mod test {
208232
Expected::Lfn(
209233
true,
210234
2,
235+
0x19,
211236
[
212-
'-', 'b', '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
213-
'\u{ffff}', '\u{ffff}', '\u{ffff}',
237+
'-' as u16, 'b' as u16, '.' as u16, 'd' as u16, 't' as u16, 'b' as u16, 0x0000,
238+
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
214239
],
215240
),
216241
Expected::Lfn(
217242
false,
218243
1,
244+
0x19,
219245
[
220-
'b', 'c', 'm', '2', '7', '0', '9', '-', 'r', 'p', 'i', '-', '2',
246+
'b' as u16, 'c' as u16, 'm' as u16, '2' as u16, '7' as u16, '0' as u16,
247+
'9' as u16, '-' as u16, 'r' as u16, 'p' as u16, 'i' as u16, '-' as u16,
248+
'2' as u16,
221249
],
222250
),
223251
Expected::Short(DirEntry {
@@ -233,16 +261,20 @@ mod test {
233261
Expected::Lfn(
234262
true,
235263
2,
264+
0x59,
236265
[
237-
'.', 'd', 't', 'b', '\u{0000}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
238-
'\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
266+
'.' as u16, 'd' as u16, 't' as u16, 'b' as u16, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF,
267+
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
239268
],
240269
),
241270
Expected::Lfn(
242271
false,
243272
1,
273+
0x59,
244274
[
245-
'b', 'c', 'm', '2', '7', '0', '8', '-', 'r', 'p', 'i', '-', 'b',
275+
'b' as u16, 'c' as u16, 'm' as u16, '2' as u16, '7' as u16, '0' as u16,
276+
'8' as u16, '-' as u16, 'r' as u16, 'p' as u16, 'i' as u16, '-' as u16,
277+
'b' as u16,
246278
],
247279
),
248280
];
@@ -251,12 +283,13 @@ mod test {
251283
for (part, expected) in data.chunks(OnDiskDirEntry::LEN).zip(results.iter()) {
252284
let on_disk_entry = OnDiskDirEntry::new(part);
253285
match expected {
254-
Expected::Lfn(start, index, contents) if on_disk_entry.is_lfn() => {
255-
let (calc_start, calc_index, calc_contents) =
286+
Expected::Lfn(start, index, csum, contents) if on_disk_entry.is_lfn() => {
287+
let (calc_start, calc_index, calc_csum, calc_contents) =
256288
on_disk_entry.lfn_contents().unwrap();
257289
assert_eq!(*start, calc_start);
258290
assert_eq!(*index, calc_index);
259291
assert_eq!(*contents, calc_contents);
292+
assert_eq!(*csum, calc_csum);
260293
}
261294
Expected::Short(expected_entry) if !on_disk_entry.is_lfn() => {
262295
let parsed_entry = on_disk_entry.get_entry(FatType::Fat32, BlockIdx(0), 0);

src/fat/ondiskdirentry.rs

+18-38
Original file line numberDiff line numberDiff line change
@@ -78,47 +78,27 @@ impl<'a> OnDiskDirEntry<'a> {
7878
}
7979

8080
/// If this is an LFN, get the contents so we can re-assemble the filename.
81-
pub fn lfn_contents(&self) -> Option<(bool, u8, [char; 13])> {
81+
pub fn lfn_contents(&self) -> Option<(bool, u8, u8, [u16; 13])> {
8282
if self.is_lfn() {
83-
let mut buffer = [' '; 13];
8483
let is_start = (self.data[0] & 0x40) != 0;
8584
let sequence = self.data[0] & 0x1F;
86-
// LFNs store UCS-2, so we can map from 16-bit char to 32-bit char without problem.
87-
buffer[0] =
88-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[1..=2]))).unwrap();
89-
buffer[1] =
90-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[3..=4]))).unwrap();
91-
buffer[2] =
92-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[5..=6]))).unwrap();
93-
buffer[3] =
94-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[7..=8]))).unwrap();
95-
buffer[4] = core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[9..=10])))
96-
.unwrap();
97-
buffer[5] =
98-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[14..=15])))
99-
.unwrap();
100-
buffer[6] =
101-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[16..=17])))
102-
.unwrap();
103-
buffer[7] =
104-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[18..=19])))
105-
.unwrap();
106-
buffer[8] =
107-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[20..=21])))
108-
.unwrap();
109-
buffer[9] =
110-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[22..=23])))
111-
.unwrap();
112-
buffer[10] =
113-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[24..=25])))
114-
.unwrap();
115-
buffer[11] =
116-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[28..=29])))
117-
.unwrap();
118-
buffer[12] =
119-
core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[30..=31])))
120-
.unwrap();
121-
Some((is_start, sequence, buffer))
85+
let csum = self.data[13];
86+
let buffer = [
87+
LittleEndian::read_u16(&self.data[1..=2]),
88+
LittleEndian::read_u16(&self.data[3..=4]),
89+
LittleEndian::read_u16(&self.data[5..=6]),
90+
LittleEndian::read_u16(&self.data[7..=8]),
91+
LittleEndian::read_u16(&self.data[9..=10]),
92+
LittleEndian::read_u16(&self.data[14..=15]),
93+
LittleEndian::read_u16(&self.data[16..=17]),
94+
LittleEndian::read_u16(&self.data[18..=19]),
95+
LittleEndian::read_u16(&self.data[20..=21]),
96+
LittleEndian::read_u16(&self.data[22..=23]),
97+
LittleEndian::read_u16(&self.data[24..=25]),
98+
LittleEndian::read_u16(&self.data[28..=29]),
99+
LittleEndian::read_u16(&self.data[30..=31]),
100+
];
101+
Some((is_start, sequence, csum, buffer))
122102
} else {
123103
None
124104
}

0 commit comments

Comments
 (0)