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 let raw = alloc::string::String::from_utf8_lossy(short.as_padded_bytes());
30 if raw == "." || raw == ".." {
31 return raw.into_owned();
32 }
33
34 let cased = short.with_nt_case(flags);
35 let raw = alloc::string::String::from_utf8_lossy(cased.as_padded_bytes());
36 let (base, ext) = match raw.find('.') {
37 Some(dot) => (raw[..dot].trim_end(), raw[dot + 1..].trim_end()),
38 None => (raw.trim_end(), ""),
39 };
40 let mut name = alloc::string::String::with_capacity(base.len() + 1 + ext.len());
41 name.push_str(base);
42 if !ext.is_empty() {
43 name.push('.');
44 name.push_str(ext);
45 }
46 name
47}
48
49pub struct FatDir<'a, DATA: Read + Seek> {
51 pub(crate) data: &'a FatVolume<DATA>,
52 pub(crate) cluster: Cluster,
54 pub(crate) fixed_root: Option<(usize, usize)>,
56}
57
58impl<'a, DATA: Read + Seek> FatDir<'a, DATA> {
59 #[cfg(feature = "lfn")]
61 pub fn entries(&self) -> FatDirIter<'a, DATA> {
62 FatDirIter {
63 data: self.data,
64 cluster: self.cluster,
65 #[cfg(feature = "write")]
66 dir_start_cluster: self.cluster,
67 offset: 0,
68 fixed_root_remaining: self.fixed_root.map(|(_, size)| size),
69 fixed_root_start: self.fixed_root.map(|(start, _)| start),
70 cluster_steps: 0,
71 lfn_builder: LfnBuilder::new(),
72 #[cfg(feature = "alloc")]
73 cluster_buffer: None,
74 #[cfg(feature = "alloc")]
75 buffer_valid: false,
76 #[cfg(feature = "alloc")]
77 buffer_base: 0,
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 #[cfg(feature = "alloc")]
98 buffer_base: 0,
99 }
100 }
101
102 pub fn open_entry(&self, entry: &FileEntry) -> Result<FatDir<'a, DATA>> {
106 if !entry.is_directory() {
107 return Err(Error::NotADirectory);
108 }
109 Ok(FatDir {
110 data: self.data,
111 cluster: entry.cluster(),
112 fixed_root: None, })
114 }
115
116 pub async fn find(&self, name: &str) -> Result<Option<FileEntry>> {
124 let mut iter = self.entries();
125 loop {
126 match iter.next_entry().await {
127 Some(result) => {
128 let DirectoryEntry::Entry(file_entry) = result?;
129
130 #[cfg(feature = "lfn")]
132 if let Some(lfn) = file_entry.long_name()
133 && lfn.eq_str(name)
134 {
135 return Ok(Some(file_entry));
136 }
137 if file_entry.short_name().matches(name) {
139 return Ok(Some(file_entry));
140 }
141 }
142 None => return Ok(None),
143 }
144 }
145 }
146
147 pub async fn open_dir(&self, name: &str) -> Result<FatDir<'a, DATA>> {
151 let entry = self.find(name).await?.ok_or(Error::EntryNotFound)?;
152
153 if !entry.is_directory() {
154 return Err(Error::NotADirectory);
155 }
156
157 Ok(FatDir {
159 data: self.data,
160 cluster: entry.cluster(),
161 fixed_root: None,
162 })
163 }
164
165 pub async fn open_file(&self, name: &str) -> Result<FileReader<'a, DATA>> {
169 let entry = self.find(name).await?.ok_or(Error::EntryNotFound)?;
170 FileReader::new(self.data, &entry)
171 }
172}
173
174pub struct FatDirIter<'a, DATA: Read + Seek> {
176 data: &'a FatVolume<DATA>,
177 cluster: Cluster,
179 #[cfg(feature = "write")]
181 dir_start_cluster: Cluster,
182 offset: usize,
184 fixed_root_remaining: Option<usize>,
186 fixed_root_start: Option<usize>,
188 cluster_steps: u32,
192 #[cfg(feature = "lfn")]
193 lfn_builder: LfnBuilder,
194 #[cfg(feature = "alloc")]
196 cluster_buffer: Option<alloc::vec::Vec<u8>>,
197 #[cfg(feature = "alloc")]
199 buffer_valid: bool,
200 #[cfg(feature = "alloc")]
206 buffer_base: usize,
207}
208
209impl<DATA: Read + Seek> FatDirIter<'_, DATA> {
210 pub async fn next_entry(&mut self) -> Option<Result<DirectoryEntry>> {
212 let mut data = self.data.data.lock();
213 let entry_size = size_of::<RawDirectoryEntry>();
214 let cluster_size = data.cluster_size;
215
216 loop {
217 if let Some(ref mut remaining) = self.fixed_root_remaining {
219 if *remaining < entry_size {
221 return None; }
223 } else {
224 if self.cluster.0 < 2 {
229 return None;
230 }
231 if self.offset >= cluster_size {
233 self.cluster_steps = self.cluster_steps.saturating_add(1);
234 if self.cluster_steps > self.data.fat.max_cluster() {
235 return Some(Err(Error::ClusterLoop {
236 cluster: self.cluster.0 as u32,
237 }));
238 }
239 drop(data);
243 let next = match self.data.next_cluster_routed(self.cluster.0).await {
244 Ok(n) => n,
245 Err(e) => return Some(Err(e)),
246 };
247 data = self.data.data.lock();
248 match next {
249 Some(cluster) => {
250 self.cluster.0 = cluster as usize;
251 self.offset = 0;
252 #[cfg(feature = "alloc")]
253 {
254 self.buffer_valid = false;
255 }
256 }
257 None => return None, }
259 }
260 }
261
262 #[cfg(feature = "alloc")]
264 let raw_entry = {
265 let needs_refill = !self.buffer_valid
270 || self.cluster_buffer.is_none()
271 || (self.fixed_root_remaining.is_some()
272 && (self.offset < self.buffer_base
273 || self.offset + entry_size
274 > self.buffer_base + self.cluster_buffer.as_ref().unwrap().len()));
275 if needs_refill {
276 let buffer_size = if let Some(remaining) = self.fixed_root_remaining {
277 remaining.min(4096)
279 } else {
280 cluster_size
281 };
282
283 self.buffer_base = if self.fixed_root_remaining.is_some() {
284 self.offset
285 } else {
286 0
287 };
288 let seek_pos = if self.fixed_root_remaining.is_some() {
289 let start = self.fixed_root_start.unwrap();
290 (start + self.buffer_base) as u64
291 } else {
292 self.cluster
293 .to_bytes(self.data.info.data_start, cluster_size)
294 as u64
295 };
296
297 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)).await {
298 return Some(Err(Error::Io(e.erase())));
299 }
300
301 let mut buffer = alloc::vec![0u8; buffer_size];
302 if let Err(e) = data.read_exact(&mut buffer).await {
303 return Some(Err(Error::Io(e.erase())));
304 }
305
306 self.cluster_buffer = Some(buffer);
307 self.buffer_valid = true;
308 }
309
310 let buffer = self.cluster_buffer.as_ref().unwrap();
312 let offset = self.offset - self.buffer_base;
313
314 if offset + entry_size > buffer.len() {
315 if self.fixed_root_remaining.is_some() {
319 return None;
320 }
321 continue;
322 }
323
324 let entry_bytes: [u8; 32] = buffer[offset..offset + entry_size].try_into().unwrap();
325
326 unsafe { core::mem::transmute::<[u8; 32], RawDirectoryEntry>(entry_bytes) }
329 };
330
331 #[cfg(not(feature = "alloc"))]
332 let raw_entry = {
333 let seek_pos = if self.fixed_root_remaining.is_some() {
335 let start = self.fixed_root_start.unwrap();
336 (start + self.offset) as u64
337 } else {
338 self.cluster
339 .to_bytes(self.data.info.data_start, cluster_size)
340 as u64
341 + self.offset as u64
342 };
343
344 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)).await {
345 return Some(Err(Error::Io(e.erase())));
346 }
347
348 match data.read_struct::<RawDirectoryEntry>().await {
350 Ok(e) => e,
351 Err(e) => return Some(Err(Error::Io(e))),
352 }
353 };
354
355 let entry_bytes = unsafe { raw_entry.bytes };
356
357 if entry_bytes[0] == 0 {
359 #[cfg(feature = "lfn")]
360 self.lfn_builder.reset();
361 return None;
362 }
363
364 if entry_bytes[0] == 0xE5 {
366 self.offset += entry_size;
367 if let Some(ref mut remaining) = self.fixed_root_remaining {
368 *remaining = remaining.saturating_sub(entry_size);
369 }
370 #[cfg(feature = "lfn")]
371 self.lfn_builder.reset(); continue;
373 }
374
375 self.offset += entry_size;
376 if let Some(ref mut remaining) = self.fixed_root_remaining {
377 *remaining = remaining.saturating_sub(entry_size);
378 }
379
380 #[cfg(feature = "lfn")]
382 {
383 let entry_attr = unsafe { raw_entry.file }.attributes;
384 if entry_attr == DirEntryAttrFlags::LONG_NAME.bits() {
385 let lfn = unsafe { raw_entry.lfn };
387 let seq = lfn.sequence_number;
388
389 if seq & LfnBuilder::LAST_ENTRY_MASK != 0 {
391 self.lfn_builder.start(seq, lfn.checksum);
392 }
393
394 if self.lfn_builder.building {
395 self.lfn_builder.add_entry(
396 seq,
397 lfn.checksum,
398 &lfn.name1,
399 &lfn.name2,
400 &lfn.name3,
401 );
402 }
403 continue;
404 }
405 }
406
407 let file_entry = unsafe { raw_entry.file };
409
410 let attr = DirEntryAttrFlags::from_bits_retain(file_entry.attributes);
411 if attr.contains(DirEntryAttrFlags::VOLUME_ID) {
416 #[cfg(feature = "lfn")]
417 self.lfn_builder.reset();
418 continue;
419 }
420
421 let mut name_bytes = file_entry.name;
423 if name_bytes[0] == 0x05 {
424 name_bytes[0] = 0xE5;
425 }
426
427 let short_name = match ShortFileName::new(name_bytes) {
428 Ok(n) => n,
429 Err(_) => return Some(Err(Error::InvalidShortFilename)),
430 };
431
432 #[cfg(feature = "lfn")]
434 let long_name = self.lfn_builder.finish(&short_name);
435
436 let created = FatDateTime::from_raw(
439 u16::from_le_bytes(file_entry.creation_date),
440 u16::from_le_bytes(file_entry.creation_time),
441 file_entry.creation_time_tenth,
442 );
443 let modified = FatDateTime::from_raw(
444 u16::from_le_bytes(file_entry.last_write_date),
445 u16::from_le_bytes(file_entry.last_write_time),
446 0,
447 );
448 let last_access_date = u16::from_le_bytes(file_entry.last_access_date);
449
450 return Some(Ok(DirectoryEntry::Entry(FileEntry {
451 short_name,
452 nt_case: NtCaseFlags::from_bits_truncate(file_entry.reserved),
453 #[cfg(feature = "lfn")]
454 long_name,
455 attr,
456 size: file_entry.size.get() as usize,
457 #[cfg(feature = "write")]
458 parent_dir_clus: self.dir_start_cluster,
459 #[cfg(feature = "write")]
460 parent_clus: self.cluster,
461 #[cfg(feature = "write")]
462 offset_within_cluster: self.offset - entry_size,
463 cluster: Cluster::from_parts(
464 file_entry.first_cluster_high.get(),
465 file_entry.first_cluster_low.get(),
466 ),
467 created,
468 last_access_date,
469 modified,
470 })));
471 }
472 }
473}
474
475#[derive(Debug, Clone)]
476pub enum DirectoryEntry {
478 Entry(FileEntry),
480}
481
482impl DirectoryEntry {
483 #[cfg(feature = "alloc")]
488 pub fn name(&self) -> alloc::borrow::Cow<'_, str> {
489 match self {
490 Self::Entry(ent) => ent.name(),
491 }
492 }
493
494 pub fn as_entry(&self) -> Option<&FileEntry> {
496 match self {
497 Self::Entry(ent) => Some(ent),
498 }
499 }
500}
501
502#[derive(Debug, Clone)]
503pub struct ParseInfo<T> {
505 pub data: T,
507 pub warnings: FileSystemWarnings,
509 pub errors: FileSystemErrors,
511}
512
513bitflags::bitflags! {
514 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
516 pub struct FileSystemWarnings: u64 {
517
518 }
519
520 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
522 pub struct FileSystemErrors: u64 {
523
524 }
525}
526
527#[derive(Debug, Clone)]
528pub struct FileEntry {
530 pub(crate) short_name: ShortFileName,
531 pub(crate) nt_case: NtCaseFlags,
534 #[cfg(feature = "lfn")]
535 pub(crate) long_name: Option<LongFileName>,
536 pub(crate) attr: DirEntryAttrFlags,
537 pub(crate) size: usize,
538 #[cfg(feature = "write")]
540 pub(crate) parent_dir_clus: Cluster<usize>,
541 #[cfg(feature = "write")]
543 pub(crate) parent_clus: Cluster<usize>,
544 #[cfg(feature = "write")]
546 pub(crate) offset_within_cluster: usize,
547 pub(crate) cluster: Cluster<usize>,
548 pub(crate) created: FatDateTime,
550 pub(crate) last_access_date: u16,
552 pub(crate) modified: FatDateTime,
554}
555
556impl FileEntry {
557 #[cfg(feature = "alloc")]
568 pub fn name(&self) -> alloc::borrow::Cow<'_, str> {
569 #[cfg(feature = "lfn")]
570 {
571 use alloc::string::ToString;
572 if let Some(ref lfn) = self.long_name {
573 return alloc::borrow::Cow::Owned(lfn.to_string());
574 }
575 }
576 alloc::borrow::Cow::Owned(short_name_display(&self.short_name, self.nt_case))
577 }
578
579 pub fn short_name(&self) -> &ShortFileName {
584 &self.short_name
585 }
586
587 pub fn nt_case(&self) -> NtCaseFlags {
592 self.nt_case
593 }
594
595 #[cfg(feature = "lfn")]
597 pub fn long_name(&self) -> Option<&LongFileName> {
598 self.long_name.as_ref()
599 }
600
601 pub fn attributes(&self) -> DirEntryAttrFlags {
603 self.attr
604 }
605
606 pub fn is_directory(&self) -> bool {
608 self.attr.contains(DirEntryAttrFlags::DIRECTORY)
609 }
610
611 pub fn is_file(&self) -> bool {
613 !self.is_directory()
614 }
615
616 pub fn len(&self) -> u64 {
618 self.size as u64
619 }
620
621 pub fn is_empty(&self) -> bool {
623 self.len() == 0
624 }
625
626 pub fn created(&self) -> FatDateTime {
628 self.created
629 }
630
631 pub fn accessed_date(&self) -> u16 {
634 self.last_access_date
635 }
636
637 pub fn modified(&self) -> FatDateTime {
640 self.modified
641 }
642
643 pub fn cluster(&self) -> Cluster<usize> {
645 self.cluster
646 }
647}
648
649} sync_only! {
652
653impl<DATA: Read + Seek> Iterator for FatDirIter<'_, DATA> {
654 type Item = Result<DirectoryEntry>;
655
656 fn next(&mut self) -> Option<Self::Item> {
657 let mut data = self.data.data.lock();
658 let entry_size = size_of::<RawDirectoryEntry>();
659 let cluster_size = data.cluster_size;
660
661 loop {
662 if let Some(ref mut remaining) = self.fixed_root_remaining {
664 if *remaining < entry_size {
666 return None; }
668 } else {
669 if self.cluster.0 < 2 {
674 return None;
675 }
676 if self.offset >= cluster_size {
678 self.cluster_steps = self.cluster_steps.saturating_add(1);
679 if self.cluster_steps > self.data.fat.max_cluster() {
680 return Some(Err(Error::ClusterLoop {
681 cluster: self.cluster.0 as u32,
682 }));
683 }
684 drop(data);
687 let next = match self.data.next_cluster_routed(self.cluster.0) {
688 Ok(n) => n,
689 Err(e) => return Some(Err(e)),
690 };
691 data = self.data.data.lock();
692 match next {
693 Some(cluster) => {
694 self.cluster.0 = cluster as usize;
695 self.offset = 0;
696 #[cfg(feature = "alloc")]
697 {
698 self.buffer_valid = false;
699 }
700 }
701 None => return None, }
703 }
704 }
705
706 #[cfg(feature = "alloc")]
708 let raw_entry = {
709 let needs_refill = !self.buffer_valid
714 || self.cluster_buffer.is_none()
715 || (self.fixed_root_remaining.is_some()
716 && (self.offset < self.buffer_base
717 || self.offset + entry_size
718 > self.buffer_base + self.cluster_buffer.as_ref().unwrap().len()));
719 if needs_refill {
720 let buffer_size = if let Some(remaining) = self.fixed_root_remaining {
721 remaining.min(4096)
723 } else {
724 cluster_size
725 };
726
727 self.buffer_base = if self.fixed_root_remaining.is_some() {
728 self.offset
729 } else {
730 0
731 };
732 let seek_pos = if self.fixed_root_remaining.is_some() {
733 let start = self.fixed_root_start.unwrap();
734 (start + self.buffer_base) as u64
735 } else {
736 self.cluster
737 .to_bytes(self.data.info.data_start, cluster_size)
738 as u64
739 };
740
741 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)) {
742 return Some(Err(Error::Io(e.erase())));
743 }
744
745 let mut buffer = alloc::vec![0u8; buffer_size];
746 if let Err(e) = data.read_exact(&mut buffer) {
747 return Some(Err(Error::Io(e.erase())));
748 }
749
750 self.cluster_buffer = Some(buffer);
751 self.buffer_valid = true;
752 }
753
754 let buffer = self.cluster_buffer.as_ref().unwrap();
756 let offset = self.offset - self.buffer_base;
757
758 if offset + entry_size > buffer.len() {
759 if self.fixed_root_remaining.is_some() {
763 return None;
764 }
765 continue;
766 }
767
768 let entry_bytes: [u8; 32] = buffer[offset..offset + entry_size].try_into().unwrap();
769
770 unsafe { core::mem::transmute::<[u8; 32], RawDirectoryEntry>(entry_bytes) }
773 };
774
775 #[cfg(not(feature = "alloc"))]
776 let raw_entry = {
777 let seek_pos = if self.fixed_root_remaining.is_some() {
779 let start = self.fixed_root_start.unwrap();
780 (start + self.offset) as u64
781 } else {
782 self.cluster
783 .to_bytes(self.data.info.data_start, cluster_size)
784 as u64
785 + self.offset as u64
786 };
787
788 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)) {
789 return Some(Err(Error::Io(e.erase())));
790 }
791
792 match data.read_struct::<RawDirectoryEntry>() {
794 Ok(e) => e,
795 Err(e) => return Some(Err(Error::Io(e))),
796 }
797 };
798
799 let entry_bytes = unsafe { raw_entry.bytes };
800
801 if entry_bytes[0] == 0 {
803 #[cfg(feature = "lfn")]
804 self.lfn_builder.reset();
805 return None;
806 }
807
808 if entry_bytes[0] == 0xE5 {
810 self.offset += entry_size;
811 if let Some(ref mut remaining) = self.fixed_root_remaining {
812 *remaining = remaining.saturating_sub(entry_size);
813 }
814 #[cfg(feature = "lfn")]
815 self.lfn_builder.reset(); continue;
817 }
818
819 self.offset += entry_size;
820 if let Some(ref mut remaining) = self.fixed_root_remaining {
821 *remaining = remaining.saturating_sub(entry_size);
822 }
823
824 #[cfg(feature = "lfn")]
826 {
827 let entry_attr = unsafe { raw_entry.file }.attributes;
828 if entry_attr == DirEntryAttrFlags::LONG_NAME.bits() {
829 let lfn = unsafe { raw_entry.lfn };
831 let seq = lfn.sequence_number;
832
833 if seq & LfnBuilder::LAST_ENTRY_MASK != 0 {
835 self.lfn_builder.start(seq, lfn.checksum);
836 }
837
838 if self.lfn_builder.building {
839 self.lfn_builder.add_entry(
840 seq,
841 lfn.checksum,
842 &lfn.name1,
843 &lfn.name2,
844 &lfn.name3,
845 );
846 }
847 continue;
848 }
849 }
850
851 let file_entry = unsafe { raw_entry.file };
853
854 let attr = DirEntryAttrFlags::from_bits_retain(file_entry.attributes);
855 if attr.contains(DirEntryAttrFlags::VOLUME_ID) {
860 #[cfg(feature = "lfn")]
861 self.lfn_builder.reset();
862 continue;
863 }
864
865 let mut name_bytes = file_entry.name;
867 if name_bytes[0] == 0x05 {
868 name_bytes[0] = 0xE5;
869 }
870
871 let short_name = match ShortFileName::new(name_bytes) {
872 Ok(n) => n,
873 Err(_) => return Some(Err(Error::InvalidShortFilename)),
874 };
875
876 #[cfg(feature = "lfn")]
878 let long_name = self.lfn_builder.finish(&short_name);
879
880 let created = FatDateTime::from_raw(
883 u16::from_le_bytes(file_entry.creation_date),
884 u16::from_le_bytes(file_entry.creation_time),
885 file_entry.creation_time_tenth,
886 );
887 let modified = FatDateTime::from_raw(
888 u16::from_le_bytes(file_entry.last_write_date),
889 u16::from_le_bytes(file_entry.last_write_time),
890 0,
891 );
892 let last_access_date = u16::from_le_bytes(file_entry.last_access_date);
893
894 return Some(Ok(DirectoryEntry::Entry(FileEntry {
895 short_name,
896 nt_case: NtCaseFlags::from_bits_truncate(file_entry.reserved),
897 #[cfg(feature = "lfn")]
898 long_name,
899 attr,
900 size: file_entry.size.get() as usize,
901 #[cfg(feature = "write")]
902 parent_dir_clus: self.dir_start_cluster,
903 #[cfg(feature = "write")]
904 parent_clus: self.cluster,
905 #[cfg(feature = "write")]
906 offset_within_cluster: self.offset - entry_size,
907 cluster: Cluster::from_parts(
908 file_entry.first_cluster_high.get(),
909 file_entry.first_cluster_low.get(),
910 ),
911 created,
912 last_access_date,
913 modified,
914 })));
915 }
916 }
917}
918
919}