1pub const RESERVED_ENTRIES: u32 = 2;
7
8#[derive(Debug, Copy, Clone, PartialEq, Eq)]
10pub enum FatType {
11 Fat16,
13 Fat32,
15}
16
17pub(crate) struct BlockCache {
18 block: Block,
19 idx: Option<BlockIdx>,
20}
21impl BlockCache {
22 pub fn empty() -> Self {
23 BlockCache {
24 block: Block::new(),
25 idx: None,
26 }
27 }
28 pub(crate) fn read<D>(
29 &mut self,
30 block_device: &D,
31 block_idx: BlockIdx,
32 reason: &str,
33 ) -> Result<&Block, Error<D::Error>>
34 where
35 D: BlockDevice,
36 {
37 if Some(block_idx) != self.idx {
38 self.idx = Some(block_idx);
39 block_device
40 .read(core::slice::from_mut(&mut self.block), block_idx, reason)
41 .map_err(Error::DeviceError)?;
42 }
43 Ok(&self.block)
44 }
45}
46
47mod bpb;
48mod info;
49mod ondiskdirentry;
50mod volume;
51
52pub use bpb::Bpb;
53pub use info::{Fat16Info, Fat32Info, FatSpecificInfo, InfoSector};
54pub use ondiskdirentry::OnDiskDirEntry;
55pub use volume::{parse_volume, FatVolume, VolumeName};
56
57use crate::{Block, BlockDevice, BlockIdx, Error};
58
59#[cfg(test)]
66mod test {
67
68 use super::*;
69 use crate::{Attributes, BlockIdx, ClusterId, DirEntry, ShortFileName, Timestamp};
70
71 fn parse(input: &str) -> Vec<u8> {
72 let mut output = Vec::new();
73 for line in input.lines() {
74 let line = line.trim();
75 if !line.is_empty() {
76 for index in 0..32 {
78 let start = index * 2;
79 let end = start + 1;
80 let piece = &line[start..=end];
81 let value = u8::from_str_radix(piece, 16).unwrap();
82 output.push(value);
83 }
84 }
85 }
86 output
87 }
88
89 #[test]
116 fn test_dir_entries() {
117 #[derive(Debug)]
118 enum Expected {
119 Lfn(bool, u8, [char; 13]),
120 Short(DirEntry),
121 }
122 let raw_data = r#"
123 626f6f7420202020202020080000699c754775470000699c7547000000000000 boot ...i.uGuG..i.uG......
124 416f007600650072006c000f00476100790073000000ffffffff0000ffffffff Ao.v.e.r.l...Ga.y.s.............
125 4f5645524c4159532020201000001b9f6148614800001b9f6148030000000000 OVERLAYS .....aHaH....aH......
126 422d0070006c00750073000f00792e006400740062000000ffff0000ffffffff B-.p.l.u.s...y..d.t.b...........
127 01620063006d00320037000f0079300038002d0072007000690000002d006200 .b.c.m.2.7...y0.8.-.r.p.i...-.b.
128 42434d3237307e31445442200064119f614861480000119f61480900702b0000 BCM270~1DTB .d..aHaH....aH..p+..
129 4143004f005000590049000f00124e0047002e006c0069006e00000075007800 AC.O.P.Y.I....N.G...l.i.n...u.x.
130 434f5059494e7e314c494e2000000f9f6148614800000f9f6148050005490000 COPYIN~1LIN ....aHaH....aH...I..
131 4263006f006d000000ffff0f0067ffffffffffffffffffffffff0000ffffffff Bc.o.m.......g..................
132 014c004900430045004e000f0067430045002e00620072006f00000061006400 .L.I.C.E.N...gC.E...b.r.o...a.d.
133 4c4943454e437e3142524f200000119f614861480000119f61480800d6050000 LICENC~1BRO ....aHaH....aH......
134 422d0062002e00640074000f001962000000ffffffffffffffff0000ffffffff B-.b...d.t....b.................
135 01620063006d00320037000f0019300039002d0072007000690000002d003200 .b.c.m.2.7....0.9.-.r.p.i...-.2.
136 42434d3237307e34445442200064129f614861480000129f61480f004c2f0000 BCM270~4DTB .d..aHaH....aH..L/..
137 422e0064007400620000000f0059ffffffffffffffffffffffff0000ffffffff B..d.t.b.....Y..................
138 01620063006d00320037000f0059300038002d0072007000690000002d006200 .b.c.m.2.7...Y0.8.-.r.p.i...-.b.
139 "#;
140 let results = [
141 Expected::Short(DirEntry {
142 name: ShortFileName::create_from_str_mixed_case("boot").unwrap(),
143 mtime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
144 ctime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
145 attributes: Attributes::create_from_fat(Attributes::VOLUME),
146 cluster: ClusterId(0),
147 size: 0,
148 entry_block: BlockIdx(0),
149 entry_offset: 0,
150 }),
151 Expected::Lfn(
152 true,
153 1,
154 [
155 'o', 'v', 'e', 'r', 'l', 'a', 'y', 's', '\u{0000}', '\u{ffff}', '\u{ffff}',
156 '\u{ffff}', '\u{ffff}',
157 ],
158 ),
159 Expected::Short(DirEntry {
160 name: ShortFileName::create_from_str("OVERLAYS").unwrap(),
161 mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 54).unwrap(),
162 ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 54).unwrap(),
163 attributes: Attributes::create_from_fat(Attributes::DIRECTORY),
164 cluster: ClusterId(3),
165 size: 0,
166 entry_block: BlockIdx(0),
167 entry_offset: 0,
168 }),
169 Expected::Lfn(
170 true,
171 2,
172 [
173 '-', 'p', 'l', 'u', 's', '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}',
174 '\u{ffff}', '\u{ffff}',
175 ],
176 ),
177 Expected::Lfn(
178 false,
179 1,
180 [
181 'b', 'c', 'm', '2', '7', '0', '8', '-', 'r', 'p', 'i', '-', 'b',
182 ],
183 ),
184 Expected::Short(DirEntry {
185 name: ShortFileName::create_from_str("BCM270~1.DTB").unwrap(),
186 mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
187 ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
188 attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
189 cluster: ClusterId(9),
190 size: 11120,
191 entry_block: BlockIdx(0),
192 entry_offset: 0,
193 }),
194 Expected::Lfn(
195 true,
196 1,
197 [
198 'C', 'O', 'P', 'Y', 'I', 'N', 'G', '.', 'l', 'i', 'n', 'u', 'x',
199 ],
200 ),
201 Expected::Short(DirEntry {
202 name: ShortFileName::create_from_str("COPYIN~1.LIN").unwrap(),
203 mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 30).unwrap(),
204 ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 30).unwrap(),
205 attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
206 cluster: ClusterId(5),
207 size: 18693,
208 entry_block: BlockIdx(0),
209 entry_offset: 0,
210 }),
211 Expected::Lfn(
212 true,
213 2,
214 [
215 'c', 'o', 'm', '\u{0}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
216 '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
217 ],
218 ),
219 Expected::Lfn(
220 false,
221 1,
222 [
223 'L', 'I', 'C', 'E', 'N', 'C', 'E', '.', 'b', 'r', 'o', 'a', 'd',
224 ],
225 ),
226 Expected::Short(DirEntry {
227 name: ShortFileName::create_from_str("LICENC~1.BRO").unwrap(),
228 mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
229 ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
230 attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
231 cluster: ClusterId(8),
232 size: 1494,
233 entry_block: BlockIdx(0),
234 entry_offset: 0,
235 }),
236 Expected::Lfn(
237 true,
238 2,
239 [
240 '-', 'b', '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
241 '\u{ffff}', '\u{ffff}', '\u{ffff}',
242 ],
243 ),
244 Expected::Lfn(
245 false,
246 1,
247 [
248 'b', 'c', 'm', '2', '7', '0', '9', '-', 'r', 'p', 'i', '-', '2',
249 ],
250 ),
251 Expected::Short(DirEntry {
252 name: ShortFileName::create_from_str("BCM270~4.DTB").unwrap(),
253 mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 36).unwrap(),
254 ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 36).unwrap(),
255 attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
256 cluster: ClusterId(15),
257 size: 12108,
258 entry_block: BlockIdx(0),
259 entry_offset: 0,
260 }),
261 Expected::Lfn(
262 true,
263 2,
264 [
265 '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
266 '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
267 ],
268 ),
269 Expected::Lfn(
270 false,
271 1,
272 [
273 'b', 'c', 'm', '2', '7', '0', '8', '-', 'r', 'p', 'i', '-', 'b',
274 ],
275 ),
276 ];
277
278 let data = parse(raw_data);
279 for (part, expected) in data.chunks(OnDiskDirEntry::LEN).zip(results.iter()) {
280 let on_disk_entry = OnDiskDirEntry::new(part);
281 match expected {
282 Expected::Lfn(start, index, contents) if on_disk_entry.is_lfn() => {
283 let (calc_start, calc_index, calc_contents) =
284 on_disk_entry.lfn_contents().unwrap();
285 assert_eq!(*start, calc_start);
286 assert_eq!(*index, calc_index);
287 assert_eq!(*contents, calc_contents);
288 }
289 Expected::Short(expected_entry) if !on_disk_entry.is_lfn() => {
290 let parsed_entry = on_disk_entry.get_entry(FatType::Fat32, BlockIdx(0), 0);
291 assert_eq!(*expected_entry, parsed_entry);
292 }
293 _ => {
294 panic!(
295 "Bad dir entry, expected:\n{:#?}\nhad\n{:#?}",
296 expected, on_disk_entry
297 );
298 }
299 }
300 }
301 }
302
303 #[test]
304 fn test_bpb() {
305 const BPB_EXAMPLE: [u8; 512] = hex!(
307 "EB 3C 90 6D 6B 66 73 2E 66 61 74 00 02 10 01 00
308 02 00 02 00 00 F8 20 00 3F 00 FF 00 00 00 00 00
309 00 E0 01 00 80 01 29 BB B0 71 77 62 6F 6F 74 20
310 20 20 20 20 20 20 46 41 54 31 36 20 20 20 0E 1F
311 BE 5B 7C AC 22 C0 74 0B 56 B4 0E BB 07 00 CD 10
312 5E EB F0 32 E4 CD 16 CD 19 EB FE 54 68 69 73 20
313 69 73 20 6E 6F 74 20 61 20 62 6F 6F 74 61 62 6C
314 65 20 64 69 73 6B 2E 20 20 50 6C 65 61 73 65 20
315 69 6E 73 65 72 74 20 61 20 62 6F 6F 74 61 62 6C
316 65 20 66 6C 6F 70 70 79 20 61 6E 64 0D 0A 70 72
317 65 73 73 20 61 6E 79 20 6B 65 79 20 74 6F 20 74
318 72 79 20 61 67 61 69 6E 20 2E 2E 2E 20 0D 0A 00
319 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
321 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
322 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
323 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
324 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
325 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
326 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
327 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
328 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
329 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
331 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
332 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
333 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
334 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
335 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
336 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
337 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
338 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
339 );
340 let bpb = Bpb::create_from_bytes(&BPB_EXAMPLE).unwrap();
341 assert_eq!(bpb.footer(), Bpb::FOOTER_VALUE);
342 assert_eq!(bpb.oem_name(), b"mkfs.fat");
343 assert_eq!(bpb.bytes_per_block(), 512);
344 assert_eq!(bpb.blocks_per_cluster(), 16);
345 assert_eq!(bpb.reserved_block_count(), 1);
346 assert_eq!(bpb.num_fats(), 2);
347 assert_eq!(bpb.root_entries_count(), 512);
348 assert_eq!(bpb.total_blocks16(), 0);
349 assert_eq!(bpb.fat_size16(), 32);
350 assert_eq!(bpb.total_blocks32(), 122_880);
351 assert_eq!(bpb.footer(), 0xAA55);
352 assert_eq!(bpb.volume_label(), b"boot ");
353 assert_eq!(bpb.fat_size(), 32);
354 assert_eq!(bpb.total_blocks(), 122_880);
355 assert_eq!(bpb.fat_type, FatType::Fat16);
356 }
357}
358
359