1io_transform! {
2
3use core::mem::size_of;
4
5use hadris_common::types::endian::Endian;
6
7use crate::error::{Error, Result};
8use crate::file::ShortFileName;
9#[cfg(feature = "lfn")]
10use crate::file::{LfnBuilder, LongFileName};
11use crate::raw::{DirEntryAttrFlags, NtCaseFlags, RawDirectoryEntry};
12use crate::time::FatDateTime;
13use super::fs::FatVolume;
14#[cfg(not(feature = "alloc"))]
15use super::io::ReadExt;
16use super::io::{Cluster, ClusterLike, Read, Seek, SeekFrom};
17use super::read::FileReader;
18
19#[cfg(feature = "alloc")]
26fn short_name_display(short: &ShortFileName, flags: NtCaseFlags) -> alloc::string::String {
27 use alloc::string::ToString;
28
29 let raw = short.as_str();
32 if raw == "." || raw == ".." {
33 return raw.to_string();
34 }
35
36 let cased = short.with_nt_case(flags);
37 let raw = cased.as_str();
38 let (base, ext) = match raw.find('.') {
39 Some(dot) => (raw[..dot].trim_end(), raw[dot + 1..].trim_end()),
40 None => (raw.trim_end(), ""),
41 };
42 let mut name = alloc::string::String::with_capacity(base.len() + 1 + ext.len());
43 name.push_str(base);
44 if !ext.is_empty() {
45 name.push('.');
46 name.push_str(ext);
47 }
48 name
49}
50
51pub struct FatDir<'a, DATA: Read + Seek> {
53 pub(crate) data: &'a FatVolume<DATA>,
54 pub(crate) cluster: Cluster,
56 pub(crate) fixed_root: Option<(usize, usize)>,
58}
59
60impl<'a, DATA: Read + Seek> FatDir<'a, DATA> {
61 #[cfg(feature = "lfn")]
63 pub fn entries(&self) -> FatDirIter<'a, DATA> {
64 FatDirIter {
65 data: self.data,
66 cluster: self.cluster,
67 #[cfg(feature = "write")]
68 dir_start_cluster: self.cluster,
69 offset: 0,
70 fixed_root_remaining: self.fixed_root.map(|(_, size)| size),
71 fixed_root_start: self.fixed_root.map(|(start, _)| start),
72 cluster_steps: 0,
73 lfn_builder: LfnBuilder::new(),
74 #[cfg(feature = "alloc")]
75 cluster_buffer: None,
76 #[cfg(feature = "alloc")]
77 buffer_valid: false,
78 }
79 }
80
81 #[cfg(not(feature = "lfn"))]
83 pub fn entries(&self) -> FatDirIter<'a, DATA> {
84 FatDirIter {
85 data: self.data,
86 cluster: self.cluster,
87 #[cfg(feature = "write")]
88 dir_start_cluster: self.cluster,
89 offset: 0,
90 fixed_root_remaining: self.fixed_root.map(|(_, size)| size),
91 fixed_root_start: self.fixed_root.map(|(start, _)| start),
92 cluster_steps: 0,
93 #[cfg(feature = "alloc")]
94 cluster_buffer: None,
95 #[cfg(feature = "alloc")]
96 buffer_valid: false,
97 }
98 }
99
100 pub fn open_entry(&self, entry: &FileEntry) -> Result<FatDir<'a, DATA>> {
104 if !entry.is_directory() {
105 return Err(Error::NotADirectory);
106 }
107 Ok(FatDir {
108 data: self.data,
109 cluster: entry.cluster(),
110 fixed_root: None, })
112 }
113
114 pub async fn find(&self, name: &str) -> Result<Option<FileEntry>> {
122 let mut iter = self.entries();
123 loop {
124 match iter.next_entry().await {
125 Some(result) => {
126 let DirectoryEntry::Entry(file_entry) = result?;
127
128 #[cfg(feature = "lfn")]
130 if let Some(lfn) = file_entry.long_name()
131 && lfn.eq_str(name)
132 {
133 return Ok(Some(file_entry));
134 }
135 if file_entry.short_name().matches(name) {
137 return Ok(Some(file_entry));
138 }
139 }
140 None => return Ok(None),
141 }
142 }
143 }
144
145 pub async fn open_dir(&self, name: &str) -> Result<FatDir<'a, DATA>> {
149 let entry = self.find(name).await?.ok_or(Error::EntryNotFound)?;
150
151 if !entry.is_directory() {
152 return Err(Error::NotADirectory);
153 }
154
155 Ok(FatDir {
157 data: self.data,
158 cluster: entry.cluster(),
159 fixed_root: None,
160 })
161 }
162
163 pub async fn open_file(&self, name: &str) -> Result<FileReader<'a, DATA>> {
167 let entry = self.find(name).await?.ok_or(Error::EntryNotFound)?;
168 FileReader::new(self.data, &entry)
169 }
170}
171
172pub struct FatDirIter<'a, DATA: Read + Seek> {
174 data: &'a FatVolume<DATA>,
175 cluster: Cluster,
177 #[cfg(feature = "write")]
179 dir_start_cluster: Cluster,
180 offset: usize,
182 fixed_root_remaining: Option<usize>,
184 fixed_root_start: Option<usize>,
186 cluster_steps: u32,
190 #[cfg(feature = "lfn")]
191 lfn_builder: LfnBuilder,
192 #[cfg(feature = "alloc")]
194 cluster_buffer: Option<alloc::vec::Vec<u8>>,
195 #[cfg(feature = "alloc")]
197 buffer_valid: bool,
198}
199
200impl<DATA: Read + Seek> FatDirIter<'_, DATA> {
201 pub async fn next_entry(&mut self) -> Option<Result<DirectoryEntry>> {
203 let mut data = self.data.data.lock();
204 let entry_size = size_of::<RawDirectoryEntry>();
205 let cluster_size = data.cluster_size;
206
207 loop {
208 if let Some(ref mut remaining) = self.fixed_root_remaining {
210 if *remaining < entry_size {
212 return None; }
214 } else {
215 if self.offset >= cluster_size {
218 self.cluster_steps = self.cluster_steps.saturating_add(1);
219 if self.cluster_steps > self.data.fat.max_cluster() {
220 return Some(Err(Error::ClusterLoop {
221 cluster: self.cluster.0 as u32,
222 }));
223 }
224 drop(data);
228 let next = match self.data.next_cluster_routed(self.cluster.0).await {
229 Ok(n) => n,
230 Err(e) => return Some(Err(e)),
231 };
232 data = self.data.data.lock();
233 match next {
234 Some(cluster) => {
235 self.cluster.0 = cluster as usize;
236 self.offset = 0;
237 #[cfg(feature = "alloc")]
238 {
239 self.buffer_valid = false;
240 }
241 }
242 None => return None, }
244 }
245 }
246
247 #[cfg(feature = "alloc")]
249 let raw_entry = {
250 if !self.buffer_valid || self.cluster_buffer.is_none() {
252 let buffer_size = if let Some(remaining) = self.fixed_root_remaining {
253 remaining.min(4096)
255 } else {
256 cluster_size
257 };
258
259 let seek_pos = if self.fixed_root_remaining.is_some() {
260 let start = self.fixed_root_start.unwrap();
261 start as u64
262 } else {
263 self.cluster
264 .to_bytes(self.data.info.data_start, cluster_size)
265 as u64
266 };
267
268 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)).await {
269 return Some(Err(Error::Io(e.erase())));
270 }
271
272 let mut buffer = alloc::vec![0u8; buffer_size];
273 if let Err(e) = data.read_exact(&mut buffer).await {
274 return Some(Err(Error::Io(e.erase())));
275 }
276
277 self.cluster_buffer = Some(buffer);
278 self.buffer_valid = true;
279 }
280
281 let buffer = self.cluster_buffer.as_ref().unwrap();
283 let offset = self.offset;
284
285 if offset + entry_size > buffer.len() {
286 if self.fixed_root_remaining.is_some() {
290 return None;
291 }
292 continue;
293 }
294
295 let entry_bytes: [u8; 32] = buffer[offset..offset + entry_size].try_into().unwrap();
296
297 unsafe { core::mem::transmute::<[u8; 32], RawDirectoryEntry>(entry_bytes) }
300 };
301
302 #[cfg(not(feature = "alloc"))]
303 let raw_entry = {
304 let seek_pos = if self.fixed_root_remaining.is_some() {
306 let start = self.fixed_root_start.unwrap();
307 (start + self.offset) as u64
308 } else {
309 self.cluster
310 .to_bytes(self.data.info.data_start, cluster_size)
311 as u64
312 + self.offset as u64
313 };
314
315 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)).await {
316 return Some(Err(Error::Io(e.erase())));
317 }
318
319 match data.read_struct::<RawDirectoryEntry>().await {
321 Ok(e) => e,
322 Err(e) => return Some(Err(Error::Io(e))),
323 }
324 };
325
326 let entry_bytes = unsafe { raw_entry.bytes };
327
328 if entry_bytes[0] == 0 {
330 #[cfg(feature = "lfn")]
331 self.lfn_builder.reset();
332 return None;
333 }
334
335 if entry_bytes[0] == 0xE5 {
337 self.offset += entry_size;
338 if let Some(ref mut remaining) = self.fixed_root_remaining {
339 *remaining = remaining.saturating_sub(entry_size);
340 }
341 #[cfg(feature = "lfn")]
342 self.lfn_builder.reset(); continue;
344 }
345
346 self.offset += entry_size;
347 if let Some(ref mut remaining) = self.fixed_root_remaining {
348 *remaining = remaining.saturating_sub(entry_size);
349 }
350
351 #[cfg(feature = "lfn")]
353 {
354 let entry_attr = unsafe { raw_entry.file }.attributes;
355 if entry_attr == DirEntryAttrFlags::LONG_NAME.bits() {
356 let lfn = unsafe { raw_entry.lfn };
358 let seq = lfn.sequence_number;
359
360 if seq & LfnBuilder::LAST_ENTRY_MASK != 0 {
362 self.lfn_builder.start(seq, lfn.checksum);
363 }
364
365 if self.lfn_builder.building {
366 self.lfn_builder.add_entry(
367 seq,
368 lfn.checksum,
369 &lfn.name1,
370 &lfn.name2,
371 &lfn.name3,
372 );
373 }
374 continue;
375 }
376 }
377
378 let file_entry = unsafe { raw_entry.file };
380
381 let attr = DirEntryAttrFlags::from_bits_retain(file_entry.attributes);
382 if attr.is_volume_label_entry() {
383 #[cfg(feature = "lfn")]
384 self.lfn_builder.reset();
385 continue;
386 }
387
388 let mut name_bytes = file_entry.name;
390 if name_bytes[0] == 0x05 {
391 name_bytes[0] = 0xE5;
392 }
393
394 let short_name = match ShortFileName::new(name_bytes) {
395 Ok(n) => n,
396 Err(_) => return Some(Err(Error::InvalidShortFilename)),
397 };
398
399 #[cfg(feature = "lfn")]
401 let long_name = self.lfn_builder.finish(&short_name);
402
403 let created = FatDateTime::from_raw(
406 u16::from_le_bytes(file_entry.creation_date),
407 u16::from_le_bytes(file_entry.creation_time),
408 file_entry.creation_time_tenth,
409 );
410 let modified = FatDateTime::from_raw(
411 u16::from_le_bytes(file_entry.last_write_date),
412 u16::from_le_bytes(file_entry.last_write_time),
413 0,
414 );
415 let last_access_date = u16::from_le_bytes(file_entry.last_access_date);
416
417 return Some(Ok(DirectoryEntry::Entry(FileEntry {
418 short_name,
419 nt_case: NtCaseFlags::from_bits_truncate(file_entry.reserved),
420 #[cfg(feature = "lfn")]
421 long_name,
422 attr,
423 size: file_entry.size.get() as usize,
424 #[cfg(feature = "write")]
425 parent_dir_clus: self.dir_start_cluster,
426 #[cfg(feature = "write")]
427 parent_clus: self.cluster,
428 #[cfg(feature = "write")]
429 offset_within_cluster: self.offset - entry_size,
430 cluster: Cluster::from_parts(
431 file_entry.first_cluster_high.get(),
432 file_entry.first_cluster_low.get(),
433 ),
434 created,
435 last_access_date,
436 modified,
437 })));
438 }
439 }
440}
441
442#[derive(Debug)]
443pub enum DirectoryEntry {
445 Entry(FileEntry),
447}
448
449impl DirectoryEntry {
450 #[cfg(feature = "alloc")]
455 pub fn name(&self) -> alloc::borrow::Cow<'_, str> {
456 match self {
457 Self::Entry(ent) => ent.name(),
458 }
459 }
460
461 pub fn as_entry(&self) -> Option<&FileEntry> {
463 match self {
464 Self::Entry(ent) => Some(ent),
465 }
466 }
467}
468
469#[derive(Debug)]
470pub struct ParseInfo<T> {
472 pub data: T,
474 pub warnings: FileSystemWarnings,
476 pub errors: FileSystemErrors,
478}
479
480bitflags::bitflags! {
481 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
483 pub struct FileSystemWarnings: u64 {
484
485 }
486
487 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
489 pub struct FileSystemErrors: u64 {
490
491 }
492}
493
494#[derive(Debug)]
495pub struct FileEntry {
497 pub(crate) short_name: ShortFileName,
498 pub(crate) nt_case: NtCaseFlags,
501 #[cfg(feature = "lfn")]
502 pub(crate) long_name: Option<LongFileName>,
503 pub(crate) attr: DirEntryAttrFlags,
504 pub(crate) size: usize,
505 #[cfg(feature = "write")]
507 pub(crate) parent_dir_clus: Cluster<usize>,
508 #[cfg(feature = "write")]
510 pub(crate) parent_clus: Cluster<usize>,
511 #[cfg(feature = "write")]
513 pub(crate) offset_within_cluster: usize,
514 pub(crate) cluster: Cluster<usize>,
515 pub(crate) created: FatDateTime,
517 pub(crate) last_access_date: u16,
519 pub(crate) modified: FatDateTime,
521}
522
523impl FileEntry {
524 #[cfg(feature = "alloc")]
535 pub fn name(&self) -> alloc::borrow::Cow<'_, str> {
536 #[cfg(feature = "lfn")]
537 {
538 use alloc::string::ToString;
539 if let Some(ref lfn) = self.long_name {
540 return alloc::borrow::Cow::Owned(lfn.to_string());
541 }
542 }
543 alloc::borrow::Cow::Owned(short_name_display(&self.short_name, self.nt_case))
544 }
545
546 pub fn short_name(&self) -> &ShortFileName {
551 &self.short_name
552 }
553
554 pub fn nt_case(&self) -> NtCaseFlags {
559 self.nt_case
560 }
561
562 #[cfg(feature = "lfn")]
564 pub fn long_name(&self) -> Option<&LongFileName> {
565 self.long_name.as_ref()
566 }
567
568 pub fn attributes(&self) -> DirEntryAttrFlags {
570 self.attr
571 }
572
573 pub fn is_directory(&self) -> bool {
575 self.attr.contains(DirEntryAttrFlags::DIRECTORY)
576 }
577
578 pub fn is_file(&self) -> bool {
580 !self.is_directory()
581 }
582
583 pub fn len(&self) -> u64 {
585 self.size as u64
586 }
587
588 pub fn is_empty(&self) -> bool {
590 self.len() == 0
591 }
592
593 pub fn created(&self) -> FatDateTime {
595 self.created
596 }
597
598 pub fn accessed_date(&self) -> u16 {
601 self.last_access_date
602 }
603
604 pub fn modified(&self) -> FatDateTime {
607 self.modified
608 }
609
610 pub fn cluster(&self) -> Cluster<usize> {
612 self.cluster
613 }
614}
615
616} sync_only! {
619
620impl<DATA: Read + Seek> Iterator for FatDirIter<'_, DATA> {
621 type Item = Result<DirectoryEntry>;
622
623 fn next(&mut self) -> Option<Self::Item> {
624 let mut data = self.data.data.lock();
625 let entry_size = size_of::<RawDirectoryEntry>();
626 let cluster_size = data.cluster_size;
627
628 loop {
629 if let Some(ref mut remaining) = self.fixed_root_remaining {
631 if *remaining < entry_size {
633 return None; }
635 } else {
636 if self.offset >= cluster_size {
639 self.cluster_steps = self.cluster_steps.saturating_add(1);
640 if self.cluster_steps > self.data.fat.max_cluster() {
641 return Some(Err(Error::ClusterLoop {
642 cluster: self.cluster.0 as u32,
643 }));
644 }
645 drop(data);
648 let next = match self.data.next_cluster_routed(self.cluster.0) {
649 Ok(n) => n,
650 Err(e) => return Some(Err(e)),
651 };
652 data = self.data.data.lock();
653 match next {
654 Some(cluster) => {
655 self.cluster.0 = cluster as usize;
656 self.offset = 0;
657 #[cfg(feature = "alloc")]
658 {
659 self.buffer_valid = false;
660 }
661 }
662 None => return None, }
664 }
665 }
666
667 #[cfg(feature = "alloc")]
669 let raw_entry = {
670 if !self.buffer_valid || self.cluster_buffer.is_none() {
672 let buffer_size = if let Some(remaining) = self.fixed_root_remaining {
673 remaining.min(4096)
675 } else {
676 cluster_size
677 };
678
679 let seek_pos = if self.fixed_root_remaining.is_some() {
680 let start = self.fixed_root_start.unwrap();
681 start as u64
682 } else {
683 self.cluster
684 .to_bytes(self.data.info.data_start, cluster_size)
685 as u64
686 };
687
688 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)) {
689 return Some(Err(Error::Io(e.erase())));
690 }
691
692 let mut buffer = alloc::vec![0u8; buffer_size];
693 if let Err(e) = data.read_exact(&mut buffer) {
694 return Some(Err(Error::Io(e.erase())));
695 }
696
697 self.cluster_buffer = Some(buffer);
698 self.buffer_valid = true;
699 }
700
701 let buffer = self.cluster_buffer.as_ref().unwrap();
703 let offset = self.offset;
704
705 if offset + entry_size > buffer.len() {
706 if self.fixed_root_remaining.is_some() {
710 return None;
711 }
712 continue;
713 }
714
715 let entry_bytes: [u8; 32] = buffer[offset..offset + entry_size].try_into().unwrap();
716
717 unsafe { core::mem::transmute::<[u8; 32], RawDirectoryEntry>(entry_bytes) }
720 };
721
722 #[cfg(not(feature = "alloc"))]
723 let raw_entry = {
724 let seek_pos = if self.fixed_root_remaining.is_some() {
726 let start = self.fixed_root_start.unwrap();
727 (start + self.offset) as u64
728 } else {
729 self.cluster
730 .to_bytes(self.data.info.data_start, cluster_size)
731 as u64
732 + self.offset as u64
733 };
734
735 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)) {
736 return Some(Err(Error::Io(e.erase())));
737 }
738
739 match data.read_struct::<RawDirectoryEntry>() {
741 Ok(e) => e,
742 Err(e) => return Some(Err(Error::Io(e))),
743 }
744 };
745
746 let entry_bytes = unsafe { raw_entry.bytes };
747
748 if entry_bytes[0] == 0 {
750 #[cfg(feature = "lfn")]
751 self.lfn_builder.reset();
752 return None;
753 }
754
755 if entry_bytes[0] == 0xE5 {
757 self.offset += entry_size;
758 if let Some(ref mut remaining) = self.fixed_root_remaining {
759 *remaining = remaining.saturating_sub(entry_size);
760 }
761 #[cfg(feature = "lfn")]
762 self.lfn_builder.reset(); continue;
764 }
765
766 self.offset += entry_size;
767 if let Some(ref mut remaining) = self.fixed_root_remaining {
768 *remaining = remaining.saturating_sub(entry_size);
769 }
770
771 #[cfg(feature = "lfn")]
773 {
774 let entry_attr = unsafe { raw_entry.file }.attributes;
775 if entry_attr == DirEntryAttrFlags::LONG_NAME.bits() {
776 let lfn = unsafe { raw_entry.lfn };
778 let seq = lfn.sequence_number;
779
780 if seq & LfnBuilder::LAST_ENTRY_MASK != 0 {
782 self.lfn_builder.start(seq, lfn.checksum);
783 }
784
785 if self.lfn_builder.building {
786 self.lfn_builder.add_entry(
787 seq,
788 lfn.checksum,
789 &lfn.name1,
790 &lfn.name2,
791 &lfn.name3,
792 );
793 }
794 continue;
795 }
796 }
797
798 let file_entry = unsafe { raw_entry.file };
800
801 let attr = DirEntryAttrFlags::from_bits_retain(file_entry.attributes);
802 if attr.is_volume_label_entry() {
803 #[cfg(feature = "lfn")]
804 self.lfn_builder.reset();
805 continue;
806 }
807
808 let mut name_bytes = file_entry.name;
810 if name_bytes[0] == 0x05 {
811 name_bytes[0] = 0xE5;
812 }
813
814 let short_name = match ShortFileName::new(name_bytes) {
815 Ok(n) => n,
816 Err(_) => return Some(Err(Error::InvalidShortFilename)),
817 };
818
819 #[cfg(feature = "lfn")]
821 let long_name = self.lfn_builder.finish(&short_name);
822
823 let created = FatDateTime::from_raw(
826 u16::from_le_bytes(file_entry.creation_date),
827 u16::from_le_bytes(file_entry.creation_time),
828 file_entry.creation_time_tenth,
829 );
830 let modified = FatDateTime::from_raw(
831 u16::from_le_bytes(file_entry.last_write_date),
832 u16::from_le_bytes(file_entry.last_write_time),
833 0,
834 );
835 let last_access_date = u16::from_le_bytes(file_entry.last_access_date);
836
837 return Some(Ok(DirectoryEntry::Entry(FileEntry {
838 short_name,
839 nt_case: NtCaseFlags::from_bits_truncate(file_entry.reserved),
840 #[cfg(feature = "lfn")]
841 long_name,
842 attr,
843 size: file_entry.size.get() as usize,
844 #[cfg(feature = "write")]
845 parent_dir_clus: self.dir_start_cluster,
846 #[cfg(feature = "write")]
847 parent_clus: self.cluster,
848 #[cfg(feature = "write")]
849 offset_within_cluster: self.offset - entry_size,
850 cluster: Cluster::from_parts(
851 file_entry.first_cluster_high.get(),
852 file_entry.first_cluster_low.get(),
853 ),
854 created,
855 last_access_date,
856 modified,
857 })));
858 }
859 }
860}
861
862}