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
49#[cfg(feature = "write")]
55#[derive(Clone, Copy)]
56pub(crate) struct DirSlot {
57 pub(crate) parent_clus: Cluster<usize>,
58 pub(crate) offset_within_cluster: usize,
59 pub(crate) short_name: ShortFileName,
60 pub(crate) created: FatDateTime,
63}
64
65#[cfg(feature = "write")]
66impl DirSlot {
67 pub(crate) fn from_entry(entry: &FileEntry) -> Self {
68 Self {
69 parent_clus: entry.parent_clus,
70 offset_within_cluster: entry.offset_within_cluster,
71 short_name: entry.short_name,
72 created: entry.created,
73 }
74 }
75}
76
77pub struct FatDir<'a, DATA: Read + Seek> {
79 pub(crate) data: &'a FatVolume<DATA>,
80 pub(crate) cluster: Cluster,
82 pub(crate) fixed_root: Option<(usize, usize)>,
84 #[cfg(feature = "write")]
87 pub(crate) dir_entry: Option<DirSlot>,
88}
89
90impl<'a, DATA: Read + Seek> FatDir<'a, DATA> {
91 #[cfg(feature = "lfn")]
93 pub fn entries(&self) -> FatDirIter<'a, DATA> {
94 FatDirIter {
95 data: self.data,
96 cluster: self.cluster,
97 #[cfg(feature = "write")]
98 dir_start_cluster: self.cluster,
99 offset: 0,
100 fixed_root_remaining: self.fixed_root.map(|(_, size)| size),
101 fixed_root_start: self.fixed_root.map(|(start, _)| start),
102 cluster_steps: 0,
103 lfn_builder: LfnBuilder::new(),
104 #[cfg(feature = "alloc")]
105 cluster_buffer: None,
106 #[cfg(feature = "alloc")]
107 buffer_valid: false,
108 #[cfg(feature = "alloc")]
109 buffer_base: 0,
110 }
111 }
112
113 #[cfg(not(feature = "lfn"))]
115 pub fn entries(&self) -> FatDirIter<'a, DATA> {
116 FatDirIter {
117 data: self.data,
118 cluster: self.cluster,
119 #[cfg(feature = "write")]
120 dir_start_cluster: self.cluster,
121 offset: 0,
122 fixed_root_remaining: self.fixed_root.map(|(_, size)| size),
123 fixed_root_start: self.fixed_root.map(|(start, _)| start),
124 cluster_steps: 0,
125 #[cfg(feature = "alloc")]
126 cluster_buffer: None,
127 #[cfg(feature = "alloc")]
128 buffer_valid: false,
129 #[cfg(feature = "alloc")]
130 buffer_base: 0,
131 }
132 }
133
134 pub fn open_entry(&self, entry: &FileEntry) -> Result<FatDir<'a, DATA>> {
138 if !entry.is_directory() {
139 return Err(Error::NotADirectory);
140 }
141 Ok(FatDir {
142 data: self.data,
143 cluster: entry.cluster(),
144 fixed_root: None, #[cfg(feature = "write")]
146 dir_entry: Some(DirSlot::from_entry(entry)),
147 })
148 }
149
150 pub async fn find(&self, name: &str) -> Result<Option<FileEntry>> {
158 let mut iter = self.entries();
159 loop {
160 match iter.next_entry().await {
161 Some(result) => {
162 let DirectoryEntry::Entry(file_entry) = result?;
163
164 #[cfg(feature = "lfn")]
166 if let Some(lfn) = file_entry.long_name()
167 && lfn.eq_str(name)
168 {
169 return Ok(Some(file_entry));
170 }
171 if file_entry.short_name().matches(name) {
173 return Ok(Some(file_entry));
174 }
175 }
176 None => return Ok(None),
177 }
178 }
179 }
180
181 pub async fn open_dir(&self, name: &str) -> Result<FatDir<'a, DATA>> {
185 let entry = self.find(name).await?.ok_or(Error::EntryNotFound)?;
186
187 if !entry.is_directory() {
188 return Err(Error::NotADirectory);
189 }
190
191 Ok(FatDir {
193 data: self.data,
194 cluster: entry.cluster(),
195 fixed_root: None,
196 #[cfg(feature = "write")]
197 dir_entry: Some(DirSlot::from_entry(&entry)),
198 })
199 }
200
201 pub async fn open_file(&self, name: &str) -> Result<FileReader<'a, DATA>> {
205 let entry = self.find(name).await?.ok_or(Error::EntryNotFound)?;
206 FileReader::new(self.data, &entry)
207 }
208}
209
210pub struct FatDirIter<'a, DATA: Read + Seek> {
212 data: &'a FatVolume<DATA>,
213 cluster: Cluster,
215 #[cfg(feature = "write")]
217 dir_start_cluster: Cluster,
218 offset: usize,
220 fixed_root_remaining: Option<usize>,
222 fixed_root_start: Option<usize>,
224 cluster_steps: u32,
228 #[cfg(feature = "lfn")]
229 lfn_builder: LfnBuilder,
230 #[cfg(feature = "alloc")]
232 cluster_buffer: Option<alloc::vec::Vec<u8>>,
233 #[cfg(feature = "alloc")]
235 buffer_valid: bool,
236 #[cfg(feature = "alloc")]
242 buffer_base: usize,
243}
244
245impl<DATA: Read + Seek> FatDirIter<'_, DATA> {
246 pub async fn next_entry(&mut self) -> Option<Result<DirectoryEntry>> {
248 let mut data = self.data.data.lock();
249 let entry_size = size_of::<RawDirectoryEntry>();
250 let cluster_size = data.cluster_size;
251
252 loop {
253 if let Some(ref mut remaining) = self.fixed_root_remaining {
255 if *remaining < entry_size {
257 return None; }
259 } else {
260 if self.cluster.0 < 2 {
265 return None;
266 }
267 if self.offset >= cluster_size {
269 self.cluster_steps = self.cluster_steps.saturating_add(1);
270 if self.cluster_steps > self.data.fat.max_cluster() {
271 return Some(Err(Error::ClusterLoop {
272 cluster: self.cluster.0 as u32,
273 }));
274 }
275 drop(data);
279 let next = match self.data.next_cluster_routed(self.cluster.0).await {
280 Ok(n) => n,
281 Err(e) => return Some(Err(e)),
282 };
283 data = self.data.data.lock();
284 match next {
285 Some(cluster) => {
286 self.cluster.0 = cluster as usize;
287 self.offset = 0;
288 #[cfg(feature = "alloc")]
289 {
290 self.buffer_valid = false;
291 }
292 }
293 None => return None, }
295 }
296 }
297
298 #[cfg(feature = "alloc")]
300 let raw_entry = {
301 let needs_refill = !self.buffer_valid
306 || self.cluster_buffer.is_none()
307 || (self.fixed_root_remaining.is_some()
308 && (self.offset < self.buffer_base
309 || self.offset + entry_size
310 > self.buffer_base + self.cluster_buffer.as_ref().unwrap().len()));
311 if needs_refill {
312 let buffer_size = if let Some(remaining) = self.fixed_root_remaining {
313 remaining.min(4096)
315 } else {
316 cluster_size
317 };
318
319 self.buffer_base = if self.fixed_root_remaining.is_some() {
320 self.offset
321 } else {
322 0
323 };
324 let seek_pos = if self.fixed_root_remaining.is_some() {
325 let start = self.fixed_root_start.unwrap();
326 (start + self.buffer_base) as u64
327 } else {
328 self.cluster
329 .to_bytes(self.data.info.data_start, cluster_size)
330 as u64
331 };
332
333 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)).await {
334 return Some(Err(Error::Io(e.erase())));
335 }
336
337 let mut buffer = alloc::vec![0u8; buffer_size];
338 if let Err(e) = data.read_exact(&mut buffer).await {
339 return Some(Err(Error::Io(e.erase())));
340 }
341
342 self.cluster_buffer = Some(buffer);
343 self.buffer_valid = true;
344 }
345
346 let buffer = self.cluster_buffer.as_ref().unwrap();
348 let offset = self.offset - self.buffer_base;
349
350 if offset + entry_size > buffer.len() {
351 if self.fixed_root_remaining.is_some() {
355 return None;
356 }
357 continue;
358 }
359
360 let entry_bytes: [u8; 32] = buffer[offset..offset + entry_size].try_into().unwrap();
361
362 unsafe { core::mem::transmute::<[u8; 32], RawDirectoryEntry>(entry_bytes) }
365 };
366
367 #[cfg(not(feature = "alloc"))]
368 let raw_entry = {
369 let seek_pos = if self.fixed_root_remaining.is_some() {
371 let start = self.fixed_root_start.unwrap();
372 (start + self.offset) as u64
373 } else {
374 self.cluster
375 .to_bytes(self.data.info.data_start, cluster_size)
376 as u64
377 + self.offset as u64
378 };
379
380 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)).await {
381 return Some(Err(Error::Io(e.erase())));
382 }
383
384 match data.read_struct::<RawDirectoryEntry>().await {
386 Ok(e) => e,
387 Err(e) => return Some(Err(Error::Io(e))),
388 }
389 };
390
391 let entry_bytes = unsafe { raw_entry.bytes };
392
393 if entry_bytes[0] == 0 {
395 #[cfg(feature = "lfn")]
396 self.lfn_builder.reset();
397 return None;
398 }
399
400 if entry_bytes[0] == 0xE5 {
402 self.offset += entry_size;
403 if let Some(ref mut remaining) = self.fixed_root_remaining {
404 *remaining = remaining.saturating_sub(entry_size);
405 }
406 #[cfg(feature = "lfn")]
407 self.lfn_builder.reset(); continue;
409 }
410
411 self.offset += entry_size;
412 if let Some(ref mut remaining) = self.fixed_root_remaining {
413 *remaining = remaining.saturating_sub(entry_size);
414 }
415
416 #[cfg(feature = "lfn")]
418 {
419 let entry_attr = unsafe { raw_entry.file }.attributes;
420 if entry_attr == DirEntryAttrFlags::LONG_NAME.bits() {
421 let lfn = unsafe { raw_entry.lfn };
423 let seq = lfn.sequence_number;
424
425 if seq & LfnBuilder::LAST_ENTRY_MASK != 0 {
427 self.lfn_builder.start(seq, lfn.checksum);
428 }
429
430 if self.lfn_builder.building {
431 self.lfn_builder.add_entry(
432 seq,
433 lfn.checksum,
434 &lfn.name1,
435 &lfn.name2,
436 &lfn.name3,
437 );
438 }
439 continue;
440 }
441 }
442
443 let file_entry = unsafe { raw_entry.file };
445
446 let attr = DirEntryAttrFlags::from_bits_retain(file_entry.attributes);
447 if attr.contains(DirEntryAttrFlags::VOLUME_ID) {
452 #[cfg(feature = "lfn")]
453 self.lfn_builder.reset();
454 continue;
455 }
456
457 let mut name_bytes = file_entry.name;
459 if name_bytes[0] == 0x05 {
460 name_bytes[0] = 0xE5;
461 }
462
463 let short_name = match ShortFileName::new(name_bytes) {
464 Ok(n) => n,
465 Err(_) => return Some(Err(Error::InvalidShortFilename)),
466 };
467
468 #[cfg(feature = "lfn")]
470 let long_name = self.lfn_builder.finish(&short_name);
471
472 let created = FatDateTime::from_raw(
475 u16::from_le_bytes(file_entry.creation_date),
476 u16::from_le_bytes(file_entry.creation_time),
477 file_entry.creation_time_tenth,
478 );
479 let modified = FatDateTime::from_raw(
480 u16::from_le_bytes(file_entry.last_write_date),
481 u16::from_le_bytes(file_entry.last_write_time),
482 0,
483 );
484 let last_access_date = u16::from_le_bytes(file_entry.last_access_date);
485
486 return Some(Ok(DirectoryEntry::Entry(FileEntry {
487 short_name,
488 nt_case: NtCaseFlags::from_bits_truncate(file_entry.reserved),
489 #[cfg(feature = "lfn")]
490 long_name,
491 attr,
492 size: file_entry.size.get() as usize,
493 #[cfg(feature = "write")]
494 parent_dir_clus: self.dir_start_cluster,
495 #[cfg(feature = "write")]
496 parent_clus: self.cluster,
497 #[cfg(feature = "write")]
498 offset_within_cluster: self.offset - entry_size,
499 cluster: Cluster::from_parts(
500 file_entry.first_cluster_high.get(),
501 file_entry.first_cluster_low.get(),
502 ),
503 created,
504 last_access_date,
505 modified,
506 })));
507 }
508 }
509}
510
511#[derive(Debug, Clone)]
512pub enum DirectoryEntry {
514 Entry(FileEntry),
516}
517
518impl DirectoryEntry {
519 #[cfg(feature = "alloc")]
524 pub fn name(&self) -> alloc::borrow::Cow<'_, str> {
525 match self {
526 Self::Entry(ent) => ent.name(),
527 }
528 }
529
530 pub fn as_entry(&self) -> Option<&FileEntry> {
532 match self {
533 Self::Entry(ent) => Some(ent),
534 }
535 }
536}
537
538#[derive(Debug, Clone)]
539pub struct ParseInfo<T> {
541 pub data: T,
543 pub warnings: FileSystemWarnings,
545 pub errors: FileSystemErrors,
547}
548
549bitflags::bitflags! {
550 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
552 pub struct FileSystemWarnings: u64 {
553
554 }
555
556 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
558 pub struct FileSystemErrors: u64 {
559
560 }
561}
562
563#[derive(Debug, Clone)]
564pub struct FileEntry {
566 pub(crate) short_name: ShortFileName,
567 pub(crate) nt_case: NtCaseFlags,
570 #[cfg(feature = "lfn")]
571 pub(crate) long_name: Option<LongFileName>,
572 pub(crate) attr: DirEntryAttrFlags,
573 pub(crate) size: usize,
574 #[cfg(feature = "write")]
576 pub(crate) parent_dir_clus: Cluster<usize>,
577 #[cfg(feature = "write")]
579 pub(crate) parent_clus: Cluster<usize>,
580 #[cfg(feature = "write")]
582 pub(crate) offset_within_cluster: usize,
583 pub(crate) cluster: Cluster<usize>,
584 pub(crate) created: FatDateTime,
586 pub(crate) last_access_date: u16,
588 pub(crate) modified: FatDateTime,
590}
591
592impl FileEntry {
593 #[cfg(feature = "alloc")]
604 pub fn name(&self) -> alloc::borrow::Cow<'_, str> {
605 #[cfg(feature = "lfn")]
606 {
607 use alloc::string::ToString;
608 if let Some(ref lfn) = self.long_name {
609 return alloc::borrow::Cow::Owned(lfn.to_string());
610 }
611 }
612 alloc::borrow::Cow::Owned(short_name_display(&self.short_name, self.nt_case))
613 }
614
615 pub fn short_name(&self) -> &ShortFileName {
620 &self.short_name
621 }
622
623 pub fn nt_case(&self) -> NtCaseFlags {
628 self.nt_case
629 }
630
631 #[cfg(feature = "lfn")]
633 pub fn long_name(&self) -> Option<&LongFileName> {
634 self.long_name.as_ref()
635 }
636
637 pub fn attributes(&self) -> DirEntryAttrFlags {
639 self.attr
640 }
641
642 pub fn is_directory(&self) -> bool {
644 self.attr.contains(DirEntryAttrFlags::DIRECTORY)
645 }
646
647 pub fn is_file(&self) -> bool {
649 !self.is_directory()
650 }
651
652 pub fn len(&self) -> u64 {
654 self.size as u64
655 }
656
657 pub fn is_empty(&self) -> bool {
659 self.len() == 0
660 }
661
662 pub fn created(&self) -> FatDateTime {
664 self.created
665 }
666
667 pub fn accessed_date(&self) -> u16 {
670 self.last_access_date
671 }
672
673 pub fn modified(&self) -> FatDateTime {
676 self.modified
677 }
678
679 pub fn cluster(&self) -> Cluster<usize> {
681 self.cluster
682 }
683}
684
685} sync_only! {
688
689impl<DATA: Read + Seek> Iterator for FatDirIter<'_, DATA> {
690 type Item = Result<DirectoryEntry>;
691
692 fn next(&mut self) -> Option<Self::Item> {
693 let mut data = self.data.data.lock();
694 let entry_size = size_of::<RawDirectoryEntry>();
695 let cluster_size = data.cluster_size;
696
697 loop {
698 if let Some(ref mut remaining) = self.fixed_root_remaining {
700 if *remaining < entry_size {
702 return None; }
704 } else {
705 if self.cluster.0 < 2 {
710 return None;
711 }
712 if self.offset >= cluster_size {
714 self.cluster_steps = self.cluster_steps.saturating_add(1);
715 if self.cluster_steps > self.data.fat.max_cluster() {
716 return Some(Err(Error::ClusterLoop {
717 cluster: self.cluster.0 as u32,
718 }));
719 }
720 drop(data);
723 let next = match self.data.next_cluster_routed(self.cluster.0) {
724 Ok(n) => n,
725 Err(e) => return Some(Err(e)),
726 };
727 data = self.data.data.lock();
728 match next {
729 Some(cluster) => {
730 self.cluster.0 = cluster as usize;
731 self.offset = 0;
732 #[cfg(feature = "alloc")]
733 {
734 self.buffer_valid = false;
735 }
736 }
737 None => return None, }
739 }
740 }
741
742 #[cfg(feature = "alloc")]
744 let raw_entry = {
745 let needs_refill = !self.buffer_valid
750 || self.cluster_buffer.is_none()
751 || (self.fixed_root_remaining.is_some()
752 && (self.offset < self.buffer_base
753 || self.offset + entry_size
754 > self.buffer_base + self.cluster_buffer.as_ref().unwrap().len()));
755 if needs_refill {
756 let buffer_size = if let Some(remaining) = self.fixed_root_remaining {
757 remaining.min(4096)
759 } else {
760 cluster_size
761 };
762
763 self.buffer_base = if self.fixed_root_remaining.is_some() {
764 self.offset
765 } else {
766 0
767 };
768 let seek_pos = if self.fixed_root_remaining.is_some() {
769 let start = self.fixed_root_start.unwrap();
770 (start + self.buffer_base) as u64
771 } else {
772 self.cluster
773 .to_bytes(self.data.info.data_start, cluster_size)
774 as u64
775 };
776
777 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)) {
778 return Some(Err(Error::Io(e.erase())));
779 }
780
781 let mut buffer = alloc::vec![0u8; buffer_size];
782 if let Err(e) = data.read_exact(&mut buffer) {
783 return Some(Err(Error::Io(e.erase())));
784 }
785
786 self.cluster_buffer = Some(buffer);
787 self.buffer_valid = true;
788 }
789
790 let buffer = self.cluster_buffer.as_ref().unwrap();
792 let offset = self.offset - self.buffer_base;
793
794 if offset + entry_size > buffer.len() {
795 if self.fixed_root_remaining.is_some() {
799 return None;
800 }
801 continue;
802 }
803
804 let entry_bytes: [u8; 32] = buffer[offset..offset + entry_size].try_into().unwrap();
805
806 unsafe { core::mem::transmute::<[u8; 32], RawDirectoryEntry>(entry_bytes) }
809 };
810
811 #[cfg(not(feature = "alloc"))]
812 let raw_entry = {
813 let seek_pos = if self.fixed_root_remaining.is_some() {
815 let start = self.fixed_root_start.unwrap();
816 (start + self.offset) as u64
817 } else {
818 self.cluster
819 .to_bytes(self.data.info.data_start, cluster_size)
820 as u64
821 + self.offset as u64
822 };
823
824 if let Err(e) = data.seek(SeekFrom::Start(seek_pos)) {
825 return Some(Err(Error::Io(e.erase())));
826 }
827
828 match data.read_struct::<RawDirectoryEntry>() {
830 Ok(e) => e,
831 Err(e) => return Some(Err(Error::Io(e))),
832 }
833 };
834
835 let entry_bytes = unsafe { raw_entry.bytes };
836
837 if entry_bytes[0] == 0 {
839 #[cfg(feature = "lfn")]
840 self.lfn_builder.reset();
841 return None;
842 }
843
844 if entry_bytes[0] == 0xE5 {
846 self.offset += entry_size;
847 if let Some(ref mut remaining) = self.fixed_root_remaining {
848 *remaining = remaining.saturating_sub(entry_size);
849 }
850 #[cfg(feature = "lfn")]
851 self.lfn_builder.reset(); continue;
853 }
854
855 self.offset += entry_size;
856 if let Some(ref mut remaining) = self.fixed_root_remaining {
857 *remaining = remaining.saturating_sub(entry_size);
858 }
859
860 #[cfg(feature = "lfn")]
862 {
863 let entry_attr = unsafe { raw_entry.file }.attributes;
864 if entry_attr == DirEntryAttrFlags::LONG_NAME.bits() {
865 let lfn = unsafe { raw_entry.lfn };
867 let seq = lfn.sequence_number;
868
869 if seq & LfnBuilder::LAST_ENTRY_MASK != 0 {
871 self.lfn_builder.start(seq, lfn.checksum);
872 }
873
874 if self.lfn_builder.building {
875 self.lfn_builder.add_entry(
876 seq,
877 lfn.checksum,
878 &lfn.name1,
879 &lfn.name2,
880 &lfn.name3,
881 );
882 }
883 continue;
884 }
885 }
886
887 let file_entry = unsafe { raw_entry.file };
889
890 let attr = DirEntryAttrFlags::from_bits_retain(file_entry.attributes);
891 if attr.contains(DirEntryAttrFlags::VOLUME_ID) {
896 #[cfg(feature = "lfn")]
897 self.lfn_builder.reset();
898 continue;
899 }
900
901 let mut name_bytes = file_entry.name;
903 if name_bytes[0] == 0x05 {
904 name_bytes[0] = 0xE5;
905 }
906
907 let short_name = match ShortFileName::new(name_bytes) {
908 Ok(n) => n,
909 Err(_) => return Some(Err(Error::InvalidShortFilename)),
910 };
911
912 #[cfg(feature = "lfn")]
914 let long_name = self.lfn_builder.finish(&short_name);
915
916 let created = FatDateTime::from_raw(
919 u16::from_le_bytes(file_entry.creation_date),
920 u16::from_le_bytes(file_entry.creation_time),
921 file_entry.creation_time_tenth,
922 );
923 let modified = FatDateTime::from_raw(
924 u16::from_le_bytes(file_entry.last_write_date),
925 u16::from_le_bytes(file_entry.last_write_time),
926 0,
927 );
928 let last_access_date = u16::from_le_bytes(file_entry.last_access_date);
929
930 return Some(Ok(DirectoryEntry::Entry(FileEntry {
931 short_name,
932 nt_case: NtCaseFlags::from_bits_truncate(file_entry.reserved),
933 #[cfg(feature = "lfn")]
934 long_name,
935 attr,
936 size: file_entry.size.get() as usize,
937 #[cfg(feature = "write")]
938 parent_dir_clus: self.dir_start_cluster,
939 #[cfg(feature = "write")]
940 parent_clus: self.cluster,
941 #[cfg(feature = "write")]
942 offset_within_cluster: self.offset - entry_size,
943 cluster: Cluster::from_parts(
944 file_entry.first_cluster_high.get(),
945 file_entry.first_cluster_low.get(),
946 ),
947 created,
948 last_access_date,
949 modified,
950 })));
951 }
952 }
953}
954
955}