simple_fatfs/fat/
fs.rs

1use super::*;
2
3use crate::{error::*, path::*, utils};
4
5use core::{
6    cell::{Ref, RefCell},
7    cmp, iter, num, ops,
8};
9
10#[cfg(not(feature = "std"))]
11use alloc::{boxed::Box, string::ToString, vec, vec::Vec};
12
13use ::time;
14use embedded_io::*;
15use time::PrimitiveDateTime;
16
17/// An enum representing different variants of the FAT filesystem
18///
19/// The logic is essentially the same in all of them, the only thing that
20/// changes is the size in bytes of FAT entries, and thus the maximum volume size
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22// no need for enum variant documentation here
23pub enum FATType {
24    /// One of the earliest versions, originally used all the way back to 1980.
25    /// This probably won't be encountered anywhere outside ancient MS-DOS versions
26    /// or pretty low-size volumes, like microcontrollers
27    ///
28    /// Max volume size: 8 MB
29    FAT12,
30    /// Used in many low-size volumes
31    ///
32    /// Min volume size: 8 MB,
33    /// Max volume size: 16 GB
34    FAT16,
35    /// The most commonly-used variant.
36    ///
37    /// Min volume size: 256 MB,
38    /// Max volume size: 16 TB
39    FAT32,
40    /// An ex-proprietory filesystem that allows for even larger storage sizes
41    /// and its use is currently on the rise
42    ///
43    /// Not currently supported
44    ExFAT,
45}
46
47impl FATType {
48    #[inline]
49    /// How many bits this [`FATType`] uses to address clusters in the disk
50    fn bits_per_entry(&self) -> u8 {
51        match self {
52            FATType::FAT12 => 12,
53            FATType::FAT16 => 16,
54            // the high 4 bits are ignored, but are still part of the entry
55            FATType::FAT32 => 32,
56            FATType::ExFAT => 32,
57        }
58    }
59
60    #[inline]
61    /// How many bytes this [`FATType`] spans across
62    fn entry_size(&self) -> u8 {
63        self.bits_per_entry().next_power_of_two() / 8
64    }
65}
66
67// the first 2 entries are reserved
68const RESERVED_FAT_ENTRIES: FATEntryCount = 2;
69
70#[derive(Debug, Clone, PartialEq)]
71pub(crate) enum FATEntry {
72    /// This cluster is free
73    Free,
74    /// This cluster is allocated and the next cluster is the contained value
75    Allocated(ClusterIndex),
76    /// This cluster is reserved
77    Reserved,
78    /// This is a bad (defective) cluster
79    Bad,
80    /// This cluster is allocated and is the final cluster of the file
81    Eof,
82}
83
84impl From<FATEntry> for FATEntryValue {
85    fn from(value: FATEntry) -> Self {
86        Self::from(&value)
87    }
88}
89
90impl From<&FATEntry> for FATEntryValue {
91    fn from(value: &FATEntry) -> Self {
92        match value {
93            FATEntry::Free => FATEntryValue::MIN,
94            FATEntry::Allocated(cluster) => *cluster,
95            FATEntry::Reserved => 0xFFFFFF6,
96            FATEntry::Bad => 0xFFFFFF7,
97            FATEntry::Eof => FATEntryValue::MAX,
98        }
99    }
100}
101
102/// Properties about the position of a [`FATEntry`] inside the FAT region
103struct FATEntryProps {
104    /// Each `n`th element of the vector points at the corrensponding sector at the (first) active FAT table
105    fat_sector: SectorIndex,
106    sector_offset: usize,
107}
108
109impl FATEntryProps {
110    /// Get the [`FATEntryProps`] of the `n`-th [`FATEntry`] of a [`FileSystem`]
111    pub fn new<S>(n: FATEntryIndex, fs: &FileSystem<S>) -> Self
112    where
113        S: Read + Seek,
114    {
115        let fat_byte_offset: u64 = u64::from(n) * u64::from(fs.fat_type.bits_per_entry()) / 8;
116        let fat_sector = SectorIndex::try_from(
117            u64::from(fs.props.first_fat_sector)
118                + fat_byte_offset / u64::from(fs.props.sector_size),
119        )
120        .expect("this should fit into a u32");
121        let sector_offset: usize =
122            usize::try_from(fat_byte_offset % u64::from(fs.props.sector_size))
123                .expect("this should fit into a usize");
124
125        FATEntryProps {
126            fat_sector,
127            sector_offset,
128        }
129    }
130}
131
132/// the BPB_NumFATs field is 1 byte wide (u8)
133pub(crate) type FATOffset = u8;
134
135/// Properties about the position of a sector within the FAT
136struct FATSectorProps {
137    /// the sector belongs to this FAT copy
138    #[allow(unused)]
139    fat_offset: FATOffset,
140    /// the sector is that many away from the start of the FAT copy
141    sector_offset: SectorIndex,
142}
143
144impl FATSectorProps {
145    /// Returns [`None`] if this sector doesn't belong to a FAT table
146    pub fn new<S>(sector: SectorIndex, fs: &FileSystem<S>) -> Option<Self>
147    where
148        S: Read + Seek,
149    {
150        if !fs.sector_belongs_to_FAT(sector) {
151            return None;
152        }
153
154        let sector_offset_from_first_fat = sector - SectorIndex::from(fs.props.first_fat_sector);
155        let fat_offset =
156            FATOffset::try_from(sector_offset_from_first_fat / fs.props.fat_sector_size)
157                .expect("this should fit in a u89");
158        let sector_offset = sector_offset_from_first_fat % fs.props.fat_sector_size;
159
160        Some(FATSectorProps {
161            fat_offset,
162            sector_offset,
163        })
164    }
165
166    #[allow(non_snake_case)]
167    pub fn get_corresponding_FAT_sectors<S>(&self, fs: &FileSystem<S>) -> Box<[SectorIndex]>
168    where
169        S: Read + Seek,
170    {
171        let mut vec = Vec::with_capacity(fs.props.fat_table_count.into());
172
173        for i in 0..fs.props.fat_table_count {
174            vec.push(
175                SectorIndex::from(fs.props.first_fat_sector)
176                    + SectorCount::from(i) * fs.props.fat_sector_size
177                    + self.sector_offset,
178            )
179        }
180
181        vec.into_boxed_slice()
182    }
183}
184
185#[derive(Debug, Clone)]
186pub(crate) struct DirInfo {
187    pub(crate) path: PathBuf,
188    pub(crate) chain_start: EntryLocationUnit,
189    /// Indicates the [`EntryLocation`] of the last known allocated or removed [`DirEntry`]
190    ///
191    /// [`None`] if it is not known
192    pub(crate) chain_end: Option<EntryLocation>,
193}
194
195impl DirInfo {
196    pub(crate) fn at_root_dir(boot_record: &BootRecord) -> Self {
197        DirInfo {
198            // this is basically the root directory
199            path: PathBuf::from(path_consts::SEPARATOR_STR),
200            chain_start: match boot_record {
201                BootRecord::Fat(boot_record_fat) => match &boot_record_fat.ebr {
202                    // it doesn't really matter what value we put in here, since we won't be using it
203                    Ebr::FAT12_16(_ebr_fat12_16) => EntryLocationUnit::RootDirSector(0),
204                    Ebr::FAT32(ebr_fat32, _) => {
205                        EntryLocationUnit::DataCluster(ebr_fat32.root_cluster)
206                    }
207                },
208                BootRecord::ExFAT(_boot_record_exfat) => todo!(),
209            },
210            chain_end: None,
211        }
212    }
213}
214
215impl<S> iter::FusedIterator for ReadDir<'_, S> where S: Read + Seek {}
216
217pub(crate) trait OffsetConversions {
218    fn sector_size(&self) -> u16;
219    fn cluster_size(&self) -> u32;
220    fn first_data_sector(&self) -> SectorIndex;
221
222    #[inline]
223    fn cluster_to_sector(&self, cluster: ClusterIndex) -> SectorIndex {
224        cluster * ClusterIndex::from(self.sectors_per_cluster())
225    }
226
227    #[inline]
228    fn sectors_per_cluster(&self) -> u8 {
229        (self.cluster_size() / u32::from(self.sector_size()))
230            .try_into()
231            .expect("the SecPerClus field is 1 byte long (u8)")
232    }
233
234    #[inline]
235    fn sector_to_partition_offset(&self, sector: SectorIndex) -> u64 {
236        u64::from(sector) * u64::from(self.sector_size())
237    }
238
239    #[inline]
240    fn data_cluster_to_partition_sector(&self, cluster: ClusterIndex) -> SectorIndex {
241        self.cluster_to_sector(cluster - RESERVED_FAT_ENTRIES) + self.first_data_sector()
242    }
243
244    #[inline]
245    fn partition_sector_to_data_cluster(&self, sector: SectorIndex) -> ClusterIndex {
246        (sector - self.first_data_sector()) / ClusterIndex::from(self.sectors_per_cluster())
247            + RESERVED_FAT_ENTRIES
248    }
249}
250
251impl<S> OffsetConversions for FileSystem<S>
252where
253    S: Read + Seek,
254{
255    #[inline]
256    fn sector_size(&self) -> u16 {
257        self.props.sector_size
258    }
259
260    #[inline]
261    fn cluster_size(&self) -> u32 {
262        self.props.cluster_size
263    }
264
265    #[inline]
266    fn first_data_sector(&self) -> SectorIndex {
267        self.props.first_data_sector
268    }
269
270    #[inline]
271    fn sectors_per_cluster(&self) -> u8 {
272        self.props.sec_per_clus
273    }
274}
275
276/// Some generic properties common across all FAT versions, like a sector's size, are cached here
277#[derive(Debug)]
278pub(crate) struct FSProperties {
279    pub(crate) sector_size: u16,
280    pub(crate) cluster_size: u32,
281    pub(crate) sec_per_clus: u8,
282    pub(crate) total_sectors: SectorCount,
283    pub(crate) total_clusters: ClusterCount,
284    /// sector offset of the FAT
285    pub(crate) fat_table_count: u8,
286    pub(crate) fat_sector_size: u32,
287    pub(crate) first_fat_sector: u16,
288    pub(crate) first_root_dir_sector: SectorIndex,
289    pub(crate) first_data_sector: SectorIndex,
290}
291
292impl From<&BootRecord> for FSProperties {
293    fn from(value: &BootRecord) -> Self {
294        let sector_size = match value {
295            BootRecord::Fat(boot_record_fat) => boot_record_fat.bpb.bytes_per_sector,
296            BootRecord::ExFAT(boot_record_exfat) => 1 << boot_record_exfat.sector_shift,
297        };
298        let cluster_size = match value {
299            BootRecord::Fat(boot_record_fat) => {
300                u32::from(boot_record_fat.bpb.sectors_per_cluster) * u32::from(sector_size)
301            }
302            BootRecord::ExFAT(boot_record_exfat) => {
303                1 << (boot_record_exfat.sector_shift + boot_record_exfat.cluster_shift)
304            }
305        };
306        let sec_per_clus = match value {
307            BootRecord::Fat(boot_record_fat) => boot_record_fat.bpb.sectors_per_cluster,
308            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT is not yet implemented"),
309        };
310        let total_sectors = match value {
311            BootRecord::Fat(boot_record_fat) => boot_record_fat.total_sectors(),
312            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT is not yet implemented"),
313        };
314        let total_clusters = match value {
315            BootRecord::Fat(boot_record_fat) => boot_record_fat.total_clusters(),
316            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT is not yet implemented"),
317        };
318        let fat_table_count = match value {
319            BootRecord::Fat(boot_record_fat) => boot_record_fat.bpb.table_count,
320            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT is not yet implemented"),
321        };
322        let fat_sector_size = match value {
323            BootRecord::Fat(boot_record_fat) => boot_record_fat.fat_sector_size(),
324            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT not yet implemented"),
325        };
326        let first_fat_sector = match value {
327            BootRecord::Fat(boot_record_fat) => boot_record_fat.first_fat_sector(),
328            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT not yet implemented"),
329        };
330        let first_root_dir_sector = match value {
331            BootRecord::Fat(boot_record_fat) => boot_record_fat.first_root_dir_sector(),
332            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT is not yet implemented"),
333        };
334        let first_data_sector = match value {
335            BootRecord::Fat(boot_record_fat) => boot_record_fat.first_data_sector(),
336            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT is not yet implemented"),
337        };
338
339        FSProperties {
340            sector_size,
341            cluster_size,
342            sec_per_clus,
343            fat_table_count,
344            fat_sector_size,
345            first_fat_sector,
346            total_sectors,
347            total_clusters,
348            first_root_dir_sector,
349            first_data_sector,
350        }
351    }
352}
353
354/// Filter (or not) things like hidden files/directories
355/// for FileSystem operations
356#[derive(Debug, Clone)]
357pub(crate) struct FileFilter {
358    show_hidden: bool,
359    show_systen: bool,
360}
361
362impl FileFilter {
363    pub(crate) fn filter(&self, item: &RawProperties) -> bool {
364        let is_hidden = item.attributes.contains(RawAttributes::HIDDEN);
365        let is_system = item.attributes.contains(RawAttributes::SYSTEM);
366        let should_filter = !self.show_hidden && is_hidden || !self.show_systen && is_system;
367
368        !should_filter
369    }
370}
371
372#[allow(clippy::derivable_impls)]
373impl Default for FileFilter {
374    fn default() -> Self {
375        // The FAT spec says to filter everything by default
376        FileFilter {
377            show_hidden: false,
378            show_systen: false,
379        }
380    }
381}
382
383type SyncSectorBufferFn<S> = fn(&FileSystem<S>) -> Result<(), <S as ErrorType>::Error>;
384type UnmountFn<S> = fn(&FileSystem<S>) -> FSResult<(), <S as ErrorType>::Error>;
385
386/// An API to process a FAT filesystem
387#[derive(Debug)]
388pub struct FileSystem<S>
389where
390    S: Read + Seek,
391{
392    /// Any struct that implements the [`Read`], [`Seek`] and optionally [`Write`] traits
393    storage: RefCell<S>,
394
395    /// The length of this will be the sector size of the FS for all FAT types except FAT12, in that case, it will be double that value
396    pub(crate) sector_buffer: RefCell<SectorBuffer>,
397    fsinfo_modified: RefCell<bool>,
398
399    pub(crate) dir_info: RefCell<DirInfo>,
400
401    sync_f: RefCell<Option<SyncSectorBufferFn<S>>>,
402    unmount_f: RefCell<Option<UnmountFn<S>>>,
403
404    pub(crate) options: FSOptions,
405
406    pub(crate) boot_record: RefCell<BootRecord>,
407    // since `self.boot_record.fat_type()` calls like 5 nested functions, we keep this cached and expose it with a public getter function
408    fat_type: FATType,
409    pub(crate) props: FSProperties,
410    // this doesn't mean that this is the first free cluster, it just means
411    // that if we want to figure that out, we should start from this cluster
412    first_free_cluster: RefCell<ClusterIndex>,
413
414    pub(crate) filter: RefCell<FileFilter>,
415}
416
417/// Getter functions
418impl<S> FileSystem<S>
419where
420    S: Read + Seek,
421{
422    /// What is the [`FATType`] of the filesystem
423    pub fn fat_type(&self) -> FATType {
424        self.fat_type
425    }
426}
427
428/// Setter functions
429impl<S> FileSystem<S>
430where
431    S: Read + Seek,
432{
433    /// Whether or not to list hidden files
434    ///
435    /// Off by default
436    #[inline]
437    pub fn show_hidden(&self, show: bool) {
438        self.filter.borrow_mut().show_hidden = show;
439    }
440
441    /// Whether or not to list system files
442    ///
443    /// Off by default
444    #[inline]
445    pub fn show_system(&self, show: bool) {
446        self.filter.borrow_mut().show_systen = show;
447    }
448}
449
450/// Constructors for a [`FileSystem`]
451impl<S> FileSystem<S>
452where
453    S: Read + Seek,
454{
455    /// Create a [`FileSystem`] from a storage object
456    ///
457    /// Fails if the storage is way too small to support a FAT filesystem.
458    /// For most use cases, that shouldn't be an issue, you can just call [`.unwrap()`](Result::unwrap)
459    pub fn new(mut storage: S, options: FSOptions) -> FSResult<Self, S::Error> {
460        use utils::bincode::BINCODE_CONFIG;
461
462        // Begin by reading the boot record
463        // We don't know the sector size yet, so we just go with the biggest possible one for now
464        let mut buffer = [0u8; MAX_SECTOR_SIZE];
465
466        let bytes_read = storage.read(&mut buffer)?;
467        let mut stored_sector = 0;
468
469        if bytes_read < MIN_SECTOR_SIZE {
470            return Err(FSError::InternalFSError(InternalFSError::StorageTooSmall));
471        }
472
473        let bpb: BpbFat = bincode::decode_from_slice(&buffer[..BPBFAT_SIZE], BINCODE_CONFIG)
474            .map(|(v, _)| v)
475            .map_err(utils::bincode::map_err_dec)?;
476
477        let ebr = if bpb.table_size_16 == 0 {
478            let ebr_fat32: EBRFAT32 = bincode::decode_from_slice(
479                &buffer[BPBFAT_SIZE..BPBFAT_SIZE + EBR_SIZE],
480                BINCODE_CONFIG,
481            )
482            .map(|(v, _)| v)
483            .map_err(utils::bincode::map_err_dec)?;
484
485            storage.seek(SeekFrom::Start(
486                u64::from(ebr_fat32.fat_info) * u64::from(bpb.bytes_per_sector),
487            ))?;
488            stored_sector = ebr_fat32.fat_info.into();
489            storage.read_exact(&mut buffer[..usize::from(bpb.bytes_per_sector)])?;
490            let fsinfo: FSInfoFAT32 = bincode::decode_from_slice(
491                &buffer[..usize::from(bpb.bytes_per_sector)],
492                BINCODE_CONFIG,
493            )
494            .map(|(v, _)| v)
495            .map_err(utils::bincode::map_err_dec)?;
496
497            if !fsinfo.verify_signature() {
498                log::error!("FAT32 FSInfo has invalid signature(s)");
499                return Err(FSError::InternalFSError(InternalFSError::InvalidFSInfoSig));
500            }
501
502            Ebr::FAT32(ebr_fat32, fsinfo)
503        } else {
504            Ebr::FAT12_16(
505                bincode::decode_from_slice(
506                    &buffer[BPBFAT_SIZE..BPBFAT_SIZE + EBR_SIZE],
507                    BINCODE_CONFIG,
508                )
509                .map(|(v, _)| v)
510                .map_err(utils::bincode::map_err_dec)?,
511            )
512        };
513
514        // TODO: see how we will handle this for exfat
515        let boot_record = BootRecord::Fat(BootRecordFAT { bpb, ebr });
516
517        // verify boot record signature
518        let fat_type = boot_record.fat_type();
519
520        if fat_type == FATType::ExFAT {
521            log::error!("Filesystem is ExFAT, which is currently unsupported");
522            return Err(FSError::UnsupportedFS);
523        }
524
525        log::info!("The FAT type of the filesystem is {fat_type:?}");
526
527        match &boot_record {
528            BootRecord::Fat(boot_record_fat) => {
529                if boot_record_fat.verify_signature() {
530                    log::error!("FAT boot record has invalid signature(s)");
531                    return Err(FSError::InternalFSError(InternalFSError::InvalidBPBSig));
532                }
533            }
534            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT not yet implemented"),
535        };
536
537        let props = FSProperties::from(&boot_record);
538
539        let fs = Self {
540            storage: storage.into(),
541            sector_buffer: SectorBuffer::new(
542                Box::from(&buffer[..usize::from(props.sector_size)]),
543                stored_sector,
544            )
545            .into(),
546            fsinfo_modified: false.into(),
547            options,
548            dir_info: DirInfo::at_root_dir(&boot_record).into(),
549            sync_f: None.into(),
550            unmount_f: None.into(),
551            boot_record: boot_record.into(),
552            fat_type,
553            props,
554            first_free_cluster: RESERVED_FAT_ENTRIES.into(),
555            filter: FileFilter::default().into(),
556        };
557
558        if !fs.FAT_tables_are_identical()? {
559            return Err(FSError::InternalFSError(
560                InternalFSError::MismatchingFATTables,
561            ));
562        }
563
564        Ok(fs)
565    }
566}
567
568/// Internal [`Read`]-related low-level functions
569impl<S> FileSystem<S>
570where
571    S: Read + Seek,
572{
573    pub(crate) fn process_current_dir<'a>(&'a self) -> ReadDirInt<'a, S> {
574        ReadDirInt::new(self, &self.dir_info.borrow().chain_start)
575    }
576
577    /// Goes to the parent directory.
578    ///
579    /// If this is the root directory, it does nothing
580    fn _go_to_parent_dir(&self) -> FSResult<(), S::Error> {
581        if let Some(parent_path) = self.dir_info.borrow().path.parent() {
582            let parent_pathbuf = parent_path.to_path_buf();
583
584            let mut entries = self.process_current_dir();
585
586            // the PARENT DIR entry is always second on a directory
587            // other than the root directory
588            let parent_entry = entries
589                .nth(NONROOT_MIN_DIRENTRIES - 1)
590                .transpose()?
591                .filter(|entry| entry.is_dir && entry.name == path_consts::PARENT_DIR_STR)
592                .ok_or(FSError::InternalFSError(
593                    InternalFSError::MalformedEntryChain,
594                ))?;
595
596            self.dir_info.borrow_mut().path = parent_pathbuf;
597            self.dir_info.borrow_mut().chain_start =
598                EntryLocationUnit::DataCluster(parent_entry.data_cluster);
599            self.dir_info.borrow_mut().chain_end = None;
600        } else {
601            self._go_to_root_directory();
602        }
603
604        Ok(())
605    }
606
607    /// Goes to the given child directory
608    ///
609    /// If it doesn't exist, the encapsulated [`Option`] will be `None`
610    fn _go_to_child_dir(&self, name: &str) -> FSResult<(), S::Error> {
611        let mut entries = self.process_current_dir();
612
613        let child_entry = loop {
614            let entry = entries.next().ok_or(FSError::NotFound)??;
615
616            if entry.name == name {
617                break entry;
618            }
619        };
620
621        if !child_entry.is_dir {
622            return Err(FSError::NotADirectory);
623        }
624
625        self.dir_info.borrow_mut().path.push(&child_entry.name);
626        self.dir_info.borrow_mut().chain_start =
627            EntryLocationUnit::DataCluster(child_entry.data_cluster);
628        self.dir_info.borrow_mut().chain_end = None;
629
630        Ok(())
631    }
632
633    fn _go_to_root_directory(&self) {
634        *self.dir_info.borrow_mut() = DirInfo::at_root_dir(&self.boot_record.borrow());
635    }
636
637    // This is a helper function for `go_to_dir`
638    fn _go_up_till_target<P>(&self, target: P) -> FSResult<(), S::Error>
639    where
640        P: AsRef<Path>,
641    {
642        let target = target.as_ref();
643
644        while self.dir_info.borrow().path != target {
645            self._go_to_parent_dir()?;
646        }
647
648        Ok(())
649    }
650
651    // This is a helper function for `go_to_dir`
652    fn _go_down_till_target<P>(&self, target: P) -> FSResult<(), S::Error>
653    where
654        P: AsRef<Path>,
655    {
656        let target = target.as_ref();
657
658        let common_path_prefix = find_common_path_prefix(&self.dir_info.borrow().path, target);
659        let common_components = common_path_prefix
660            .normalize()
661            .components()
662            .filter(keep_path_normals)
663            .count();
664
665        for dir_name in target
666            .components()
667            .filter(keep_path_normals)
668            .skip(common_components)
669        {
670            self._go_to_child_dir(dir_name.as_str())?;
671        }
672
673        Ok(())
674    }
675
676    /// Make sure that the sector stored in the sector buffer is the same as
677    /// the first sector of cached directory chain
678    fn _go_to_cached_dir(&self) -> FSResult<(), S::Error> {
679        let dir_chain = self.dir_info.borrow().chain_start;
680        let target_sector = dir_chain.get_entry_sector(self);
681
682        if target_sector != self.sector_buffer.borrow().stored_sector {
683            self.load_nth_sector(target_sector)?;
684        }
685
686        Ok(())
687    }
688
689    // There are many ways this can be achieved. That's how we'll do it:
690    // Firstly, we find the common path prefix of the `current_path` and the `target`
691    // Then, we check whether it is faster to start from the root directory
692    // and get down to the target or whether we should start from where we are
693    // now, go up till we find the common prefix path and then go down to the `target`
694
695    /// Navigates to the `target` [`Path`]
696    pub(crate) fn go_to_dir<P>(&self, target: P) -> FSResult<(), S::Error>
697    where
698        P: AsRef<Path>,
699    {
700        let target = target.as_ref();
701
702        if !target.is_valid() {
703            return Err(FSError::MalformedPath);
704        }
705
706        if self.dir_info.borrow().path == target {
707            // there's a chance that the current loaded sector doesn't belong
708            // to the directory we have cached, so we must also navigate to the correct sector
709            self._go_to_cached_dir()?;
710
711            return Ok(());
712        }
713
714        let common_path_prefix = find_common_path_prefix(&self.dir_info.borrow().path, target);
715
716        // Note: these are the distances to the common prefix, not the target path
717        let distance_from_root = common_path_prefix.ancestors().count() - 1;
718        let distance_from_current_path =
719            (self.dir_info.borrow().path.ancestors().count() - 1) - distance_from_root;
720
721        if distance_from_root <= distance_from_current_path {
722            self._go_to_root_directory();
723
724            self._go_down_till_target(target)?;
725        } else {
726            self._go_up_till_target(common_path_prefix)?;
727
728            self._go_down_till_target(target)?;
729        }
730
731        Ok(())
732    }
733
734    /// Gets the next free cluster. Returns an IO [`Result`]
735    /// If the [`Result`] returns [`Ok`] that contains a [`None`], the drive is full
736    pub(crate) fn next_free_cluster(&self) -> Result<Option<ClusterIndex>, S::Error> {
737        let start_cluster = match *self.boot_record.borrow() {
738            BootRecord::Fat(ref boot_record_fat) => {
739                let mut first_free_cluster = *self.first_free_cluster.borrow();
740
741                if let Ebr::FAT32(_, fsinfo) = &boot_record_fat.ebr {
742                    // a value of u32::MAX denotes unawareness of the first free cluster
743                    // we also do a bit of range checking
744                    // TODO: if this is unknown, figure it out and write it to the FSInfo structure
745                    if fsinfo.first_free_cluster != ClusterIndex::MAX
746                        && fsinfo.first_free_cluster <= self.props.total_sectors
747                    {
748                        first_free_cluster =
749                            cmp::min(first_free_cluster, fsinfo.first_free_cluster);
750                    }
751                }
752
753                first_free_cluster
754            }
755            BootRecord::ExFAT(_) => todo!("ExFAT not yet implemented"),
756        };
757
758        let mut current_cluster = start_cluster;
759
760        while current_cluster < self.props.total_clusters {
761            if self.read_nth_FAT_entry(current_cluster)? == FATEntry::Free {
762                *self.first_free_cluster.borrow_mut() = current_cluster;
763
764                match *self.boot_record.borrow_mut() {
765                    BootRecord::Fat(ref mut boot_record_fat) => {
766                        if let Ebr::FAT32(_, fsinfo) = &mut boot_record_fat.ebr {
767                            fsinfo.first_free_cluster = current_cluster;
768                            *self.fsinfo_modified.borrow_mut() = true;
769                        }
770                    }
771                    BootRecord::ExFAT(_) => todo!("ExFAT not yet implemented"),
772                }
773
774                return Ok(Some(current_cluster));
775            }
776            current_cluster += 1;
777        }
778
779        *self.first_free_cluster.borrow_mut() = self.props.total_clusters - 1;
780        Ok(None)
781    }
782
783    /// Get the next cluster in a cluster chain, otherwise return [`None`]
784    pub(crate) fn get_next_cluster(
785        &self,
786        cluster: ClusterIndex,
787    ) -> Result<Option<ClusterIndex>, S::Error> {
788        Ok(match self.read_nth_FAT_entry(cluster)? {
789            FATEntry::Allocated(next_cluster) => Some(next_cluster),
790            // when a `ROFile` is created, `cluster_chain_is_healthy` is called, if it fails, that ROFile is dropped
791            _ => None,
792        })
793    }
794
795    #[allow(non_snake_case)]
796    /// Check whether or not the all the FAT tables of the storage medium are identical to each other
797    pub(crate) fn FAT_tables_are_identical(&self) -> Result<bool, S::Error> {
798        // we could make it work, but we are only testing regular FAT filesystems (for now)
799        assert_ne!(
800            self.fat_type,
801            FATType::ExFAT,
802            "this function doesn't work with ExFAT"
803        );
804
805        /// How many bytes to probe at max for each FAT per iteration (must be a multiple of [`MAX_SECTOR_SIZE`])
806        const MAX_PROBE_SIZE: u32 = 1 << 20;
807
808        let fat_byte_size = match &*self.boot_record.borrow() {
809            BootRecord::Fat(boot_record_fat) => boot_record_fat.fat_sector_size(),
810            BootRecord::ExFAT(_) => unreachable!(),
811        };
812
813        for nth_iteration in 0..fat_byte_size.div_ceil(MAX_PROBE_SIZE) {
814            let mut tables: Vec<Vec<u8>> = Vec::new();
815
816            for i in 0..self.props.fat_table_count {
817                let fat_start = u32::try_from(
818                    self.sector_to_partition_offset(self.boot_record.borrow().nth_FAT_table_sector(i)),
819                )
820                .expect("there's no way the FAT is more that 4GBs away from the start of the storage medium");
821                let current_offset = fat_start + nth_iteration * MAX_PROBE_SIZE;
822                let bytes_left = fat_byte_size - nth_iteration * MAX_PROBE_SIZE;
823
824                self.storage
825                    .borrow_mut()
826                    .seek(SeekFrom::Start(current_offset.into()))?;
827                let mut buf = vec![
828                    0_u8;
829                    usize::try_from(cmp::min(MAX_PROBE_SIZE, bytes_left))
830                        .unwrap_or(usize::MAX)
831                ];
832                self.storage
833                    .borrow_mut()
834                    .read_exact(buf.as_mut_slice())
835                    .map_err(|e| match e {
836                        ReadExactError::UnexpectedEof => {
837                            panic!("Unexpected EOF while reading FAT table")
838                        }
839                        ReadExactError::Other(e) => e,
840                    })?;
841                tables.push(buf);
842            }
843
844            // we check each table with the first one (except the first one ofc)
845            if !tables.iter().skip(1).all(|buf| buf == &tables[0]) {
846                return Ok(false);
847            }
848        }
849
850        Ok(true)
851    }
852
853    #[allow(non_snake_case)]
854    pub(crate) fn sector_belongs_to_FAT(&self, sector: SectorIndex) -> bool {
855        match &*self.boot_record.borrow() {
856            BootRecord::Fat(boot_record_fat) => (boot_record_fat.first_fat_sector().into()
857                ..boot_record_fat.first_root_dir_sector())
858                .contains(&sector),
859            BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT not yet implemented"),
860        }
861    }
862
863    /// Read the nth sector from the partition's beginning and store it in [`self.sector_buffer`](Self::sector_buffer)
864    ///
865    /// This function also returns an immutable reference to [`self.sector_buffer`](Self::sector_buffer)
866    pub(crate) fn load_nth_sector(&self, n: SectorIndex) -> Result<Ref<'_, [u8]>, S::Error> {
867        // FIXME: don't rely just on the filesystem for the device storage size
868        if n >= self.props.total_sectors {
869            panic!(concat!(
870                "seeked past end of device medium. ",
871                "This is most likely an internal error, please report it: ",
872                "https://github.com/Oakchris1955/simple-fatfs/issues"
873            ));
874        }
875
876        // nothing to do if the sector we wanna read is already cached
877        let stored_sector = self.sector_buffer.borrow().stored_sector;
878        if n != stored_sector {
879            // let's sync the current sector first
880            let sync_sector_option = *self.sync_f.borrow();
881            if let Some(sync_sector_buffer) = sync_sector_option {
882                log::debug!("Syncing sector {stored_sector}");
883
884                sync_sector_buffer(self)?;
885
886                // Now that we have synced the sector buffer, there's no reason
887                // to sync it again if there have been no changes
888                *self.sync_f.borrow_mut() = None;
889            }
890            self.storage
891                .borrow_mut()
892                .seek(SeekFrom::Start(self.sector_to_partition_offset(n)))?;
893            self.storage
894                .borrow_mut()
895                .read_exact(&mut self.sector_buffer.borrow_mut())
896                .map_err(|e| match e {
897                    ReadExactError::UnexpectedEof => {
898                        panic!("Unexpected EOF while reading sector {n}\n\
899                        This is most likely an interal error, please report it: https://github.com/Oakchris1955/simple-fatfs/issues")
900                    }
901                    ReadExactError::Other(e) => e,
902                })?;
903            self.storage
904                .borrow_mut()
905                .seek(SeekFrom::Current(-i64::from(self.props.sector_size)))?;
906
907            self.sector_buffer.borrow_mut().stored_sector = n;
908        }
909
910        Ok(Ref::map(self.sector_buffer.borrow(), |s| &**s))
911    }
912
913    #[allow(non_snake_case)]
914    pub(crate) fn read_nth_FAT_entry(&self, n: FATEntryIndex) -> Result<FATEntry, S::Error> {
915        // the size of an entry rounded up to bytes
916        let entry_size = self.fat_type.entry_size();
917        let entry_props = FATEntryProps::new(n, self);
918
919        self.load_nth_sector(entry_props.fat_sector)?;
920
921        let mut value_bytes = [0_u8; 4];
922        let bytes_to_read: usize = cmp::min(
923            entry_props.sector_offset + usize::from(entry_size),
924            usize::from(self.sector_size()),
925        ) - entry_props.sector_offset;
926        value_bytes[..bytes_to_read].copy_from_slice(
927            &self.sector_buffer.borrow_mut()
928                [entry_props.sector_offset..entry_props.sector_offset + bytes_to_read],
929        ); // this shouldn't panic
930
931        // in FAT12, FAT entries may be split between two different sectors
932        if self.fat_type == FATType::FAT12 && bytes_to_read < usize::from(entry_size) {
933            self.load_nth_sector(entry_props.fat_sector + 1)?;
934
935            value_bytes[bytes_to_read..usize::from(entry_size)].copy_from_slice(
936                &self.sector_buffer.borrow_mut()[..(usize::from(entry_size) - bytes_to_read)],
937            );
938        };
939
940        let mut value = FATEntryValue::from_le_bytes(value_bytes);
941        match self.fat_type {
942            // FAT12 entries are split between different bytes
943            FATType::FAT12 => {
944                if n & 1 != 0 {
945                    value >>= 4
946                } else {
947                    value &= 0xFFF
948                }
949            }
950            // ignore the high 4 bits if this is FAT32
951            FATType::FAT32 => value &= 0x0FFFFFFF,
952            _ => (),
953        }
954
955        /*
956        // pad unused bytes with 1s
957        let padding: u32 = u32::MAX.to_be() << self.fat_type.bits_per_entry();
958        value |= padding.to_le();
959        */
960
961        // TODO: perhaps byte padding can replace some redundant code here?
962        Ok(match self.fat_type {
963            FATType::FAT12 => match value {
964                0x000 => FATEntry::Free,
965                0xFF7 => FATEntry::Bad,
966                #[allow(clippy::manual_range_patterns)]
967                0xFF8..=0xFFE | 0xFFF => FATEntry::Eof,
968                _ => {
969                    if (0x002..(self.props.total_clusters + 1)).contains(&value) {
970                        FATEntry::Allocated(value)
971                    } else {
972                        FATEntry::Reserved
973                    }
974                }
975            },
976            FATType::FAT16 => match value {
977                0x0000 => FATEntry::Free,
978                0xFFF7 => FATEntry::Bad,
979                #[allow(clippy::manual_range_patterns)]
980                0xFFF8..=0xFFFE | 0xFFFF => FATEntry::Eof,
981                _ => {
982                    if (0x0002..(self.props.total_clusters + 1)).contains(&value) {
983                        FATEntry::Allocated(value)
984                    } else {
985                        FATEntry::Reserved
986                    }
987                }
988            },
989            FATType::FAT32 => match value {
990                0x00000000 => FATEntry::Free,
991                0x0FFFFFF7 => FATEntry::Bad,
992                #[allow(clippy::manual_range_patterns)]
993                0x0FFFFFF8..=0xFFFFFFE | 0x0FFFFFFF => FATEntry::Eof,
994                _ => {
995                    if (0x00000002..(self.props.total_clusters + 1)).contains(&value) {
996                        FATEntry::Allocated(value)
997                    } else {
998                        FATEntry::Reserved
999                    }
1000                }
1001            },
1002            FATType::ExFAT => todo!("ExFAT not yet implemented"),
1003        })
1004    }
1005}
1006
1007/// Internal [`Write`]-related low-level functions
1008impl<S> FileSystem<S>
1009where
1010    S: Read + Write + Seek,
1011{
1012    #[allow(non_snake_case)]
1013    pub(crate) fn write_nth_FAT_entry(
1014        &self,
1015        n: FATEntryIndex,
1016        entry: FATEntry,
1017    ) -> Result<(), S::Error> {
1018        // the size of an entry rounded up to bytes
1019        let entry_size = self.fat_type.entry_size();
1020        let entry_props = FATEntryProps::new(n, self);
1021
1022        let mask = utils::bits::setbits_u32_lo(self.fat_type.bits_per_entry());
1023        let mut value: FATEntryValue = FATEntryValue::from(entry.clone()) & mask;
1024
1025        if self.fat_type == FATType::FAT32 {
1026            // in FAT32, the high 4 bits are unused
1027            value &= 0x0FFFFFFF;
1028        }
1029
1030        match self.fat_type {
1031            FATType::FAT12 => {
1032                let should_shift = n & 1 != 0;
1033                if should_shift {
1034                    // FAT12 entries are split between different bytes
1035                    value <<= 4;
1036                }
1037
1038                self.load_nth_sector(entry_props.fat_sector)?;
1039
1040                let value_bytes = value.to_le_bytes();
1041
1042                let mut first_byte = value_bytes[0];
1043
1044                if should_shift {
1045                    let mut old_byte = self.sector_buffer.borrow()[entry_props.sector_offset];
1046                    // ignore the high 4 bytes of the old entry
1047                    old_byte &= 0x0F;
1048                    // OR it with the new value
1049                    first_byte |= old_byte;
1050                }
1051
1052                self.sector_buffer.borrow_mut()[entry_props.sector_offset] = first_byte; // this shouldn't panic
1053                self.set_modified();
1054
1055                let bytes_left_on_sector: usize = cmp::min(
1056                    usize::from(entry_size),
1057                    usize::from(self.sector_size()) - entry_props.sector_offset,
1058                );
1059
1060                if bytes_left_on_sector < entry_size.into() {
1061                    // looks like this FAT12 entry spans multiple sectors, we must also update the other one
1062                    self.load_nth_sector(entry_props.fat_sector + 1)?;
1063                }
1064
1065                let mut second_byte = value_bytes[1];
1066                let second_byte_index =
1067                    (entry_props.sector_offset + 1) % usize::from(self.sector_size());
1068                if !should_shift {
1069                    let mut old_byte = self.sector_buffer.borrow()[second_byte_index];
1070                    // ignore the low 4 bytes of the old entry
1071                    old_byte &= 0xF0;
1072                    // OR it with the new value
1073                    second_byte |= old_byte;
1074                }
1075
1076                self.sector_buffer.borrow_mut()[second_byte_index] = second_byte; // this shouldn't panic
1077                self.set_modified();
1078            }
1079            FATType::FAT16 | FATType::FAT32 => {
1080                self.load_nth_sector(entry_props.fat_sector)?;
1081
1082                let value_bytes = value.to_le_bytes();
1083
1084                self.sector_buffer.borrow_mut()[entry_props.sector_offset
1085                    ..entry_props.sector_offset + usize::from(entry_size)]
1086                    .copy_from_slice(&value_bytes[..usize::from(entry_size)]); // this shouldn't panic
1087                self.set_modified();
1088            }
1089            FATType::ExFAT => todo!("ExFAT not yet implemented"),
1090        };
1091
1092        if entry == FATEntry::Free && n < *self.first_free_cluster.borrow() {
1093            *self.first_free_cluster.borrow_mut() = n;
1094        }
1095
1096        // lastly, update the FSInfoFAT32 structure is it is available
1097        if let BootRecord::Fat(boot_record_fat) = &mut *self.boot_record.borrow_mut() {
1098            if let Ebr::FAT32(_, fsinfo) = &mut boot_record_fat.ebr {
1099                match entry {
1100                    FATEntry::Free => {
1101                        fsinfo.free_cluster_count += 1;
1102                        if n < fsinfo.first_free_cluster {
1103                            fsinfo.first_free_cluster = n;
1104                        }
1105                    }
1106                    _ => fsinfo.free_cluster_count -= 1,
1107                };
1108                *self.fsinfo_modified.borrow_mut() = true;
1109            }
1110        }
1111
1112        Ok(())
1113    }
1114
1115    /// Allocate room for at least `n` contiguous [`FATDirEntries`](FATDirEntry)
1116    /// in the current directory entry chain
1117    ///
1118    /// This may or may not allocate new clusters.
1119    pub(crate) fn allocate_nth_entries(
1120        &self,
1121        n: num::NonZero<EntryCount>,
1122    ) -> FSResult<EntryLocation, S::Error> {
1123        // navigate to the cached directory, if we aren't already there
1124        self._go_to_cached_dir()?;
1125
1126        // we may not even need to allocate new entries.
1127        // let's check if there is a chain of unused entries big enough to be used
1128        let mut first_entry = self.dir_info.borrow().chain_end.unwrap_or_else(|| {
1129            let stored_sector = self.sector_buffer.borrow().stored_sector;
1130            EntryLocation::from_partition_sector(stored_sector, self)
1131        });
1132
1133        let mut last_entry = first_entry;
1134
1135        let mut chain_len = 0;
1136        let mut entry_count: EntryCount = 0;
1137
1138        loop {
1139            let entry_status = last_entry.entry_status(self)?;
1140            entry_count += 1;
1141            match entry_status {
1142                EntryStatus::Unused | EntryStatus::LastUnused => chain_len += 1,
1143                EntryStatus::Used => chain_len = 0,
1144            }
1145
1146            if chain_len >= n.get() {
1147                return Ok(first_entry);
1148            }
1149
1150            if entry_status == EntryStatus::LastUnused {
1151                break;
1152            }
1153
1154            // we also break if we have reached the end of the cluster chain
1155            // or else the MalformedEntryChain below will kick in
1156            if last_entry.unit.get_next_unit(self)?.is_none()
1157                && last_entry.index + 1 >= last_entry.unit.get_max_offset(self)
1158            {
1159                break;
1160            }
1161
1162            // what if for whatever reason the data types changes?
1163            #[allow(clippy::absurd_extreme_comparisons)]
1164            if entry_count + n.get() >= DIRENTRY_LIMIT {
1165                // defragment the cluster chain just in case
1166                // this frees up any space for entries
1167                let new_entry_count = self.defragment_entry_chain()?;
1168
1169                if new_entry_count + n.get() >= DIRENTRY_LIMIT {
1170                    return Err(FSError::DirEntryLimitReached);
1171                }
1172
1173                // we have enough entries now, this call should make us
1174                // enter an infinite recursion
1175                return self.allocate_nth_entries(n);
1176            }
1177
1178            last_entry = last_entry
1179                .next_entry(self)?
1180                .ok_or(FSError::InternalFSError(
1181                    InternalFSError::MalformedEntryChain,
1182                ))?;
1183
1184            if entry_status == EntryStatus::Used {
1185                first_entry = last_entry;
1186            }
1187        }
1188
1189        // let's set the last-known dir entry
1190        self.dir_info.borrow_mut().chain_end = Some(last_entry);
1191
1192        // we have broken out of the loop, that means we reached the end of the chain
1193        // of the already-allocated entries
1194        match last_entry.unit {
1195            EntryLocationUnit::RootDirSector(_) => {
1196                let remaining_sectors: SectorCount = match &*self.boot_record.borrow() {
1197                    BootRecord::Fat(boot_record_fat) => {
1198                        boot_record_fat.first_root_dir_sector()
1199                            + SectorCount::from(boot_record_fat.root_dir_sectors())
1200                            - last_entry.unit.get_entry_sector(self)
1201                    }
1202                    BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT not yet implemented"),
1203                };
1204
1205                let entries_per_sector = self.props.sector_size
1206                    / u16::try_from(DIRENTRY_SIZE).expect("32 can fit in a u16");
1207                let remaining_entries = EntryCount::try_from(
1208                    remaining_sectors * SectorCount::from(entries_per_sector)
1209                        + (SectorCount::from(entries_per_sector)
1210                            - SectorCount::from(last_entry.index)
1211                            + 1),
1212                )
1213                .unwrap();
1214
1215                if remaining_entries < n.get() - chain_len {
1216                    Err(FSError::RootDirectoryFull)
1217                } else {
1218                    Ok(first_entry)
1219                }
1220            }
1221            EntryLocationUnit::DataCluster(cluster) => {
1222                // first, we check how many clusters need to be allocated (if any)
1223                let entries_per_cluster = u16::try_from(
1224                    self.props.cluster_size
1225                        / u32::try_from(DIRENTRY_SIZE).expect("32 can fit into u32"),
1226                )
1227                .expect("a cluster can have a max of ~16k entries");
1228
1229                let entries_left = n.get() - chain_len;
1230                let free_entries_on_current_cluster = entries_per_cluster - (last_entry.index + 1);
1231
1232                // if we do in fact have to allocate some clusters, we allocate them
1233                if free_entries_on_current_cluster < entries_left {
1234                    let clusters_to_allocate = (entries_left - free_entries_on_current_cluster)
1235                        .div_ceil(entries_per_cluster);
1236
1237                    let first_cluster = self.allocate_clusters(
1238                        num::NonZero::new(clusters_to_allocate.into())
1239                            .expect("this should be at least 1"),
1240                        Some(cluster),
1241                    )?;
1242
1243                    // the entry chain begins in the newly allocated clusters
1244                    if chain_len == 0 {
1245                        first_entry.unit = EntryLocationUnit::DataCluster(first_cluster);
1246                        first_entry.index = 0;
1247                    }
1248
1249                    // before we return, we should zero those sectors according to the FAT spec
1250                    for cluster in
1251                        first_cluster..(first_cluster + ClusterCount::from(clusters_to_allocate))
1252                    {
1253                        let first_sector = self.data_cluster_to_partition_sector(cluster);
1254
1255                        for sector in first_sector
1256                            ..(first_sector + SectorCount::from(self.sectors_per_cluster()))
1257                        {
1258                            self.load_nth_sector(sector)?;
1259                            self.sector_buffer.borrow_mut().fill(0);
1260                            self.set_modified();
1261                        }
1262                    }
1263                }
1264
1265                Ok(first_entry)
1266            }
1267        }
1268    }
1269
1270    /// Creates a new cluster chain with the `.` and `..` entries present,
1271    // The datetime parameter is there so that we fully comply with the FAT32 spec:
1272    // ". All date and time fields must be set to the same value as that for
1273    // the containing directory"
1274    pub(crate) fn create_entry_chain(
1275        &self,
1276        parent: EntryLocationUnit,
1277        datetime: PrimitiveDateTime,
1278    ) -> FSResult<u32, S::Error> {
1279        // we need to allocate a cluster
1280        let dir_cluster = self.allocate_clusters(num::NonZero::new(1).unwrap(), None)?;
1281
1282        let entries = Box::from([
1283            MinProperties {
1284                name: Box::from(typed_path::constants::windows::CURRENT_DIR_STR),
1285                sfn: CURRENT_DIR_SFN,
1286                // this needs to be set when creating a file
1287                attributes: RawAttributes::empty() | RawAttributes::DIRECTORY,
1288                created: Some(datetime),
1289                modified: datetime,
1290                accessed: Some(datetime.date()),
1291                file_size: 0,
1292                data_cluster: dir_cluster,
1293            },
1294            MinProperties {
1295                name: Box::from(typed_path::constants::windows::PARENT_DIR_STR),
1296                sfn: PARENT_DIR_SFN,
1297                // this needs to be set when creating a file
1298                attributes: RawAttributes::empty() | RawAttributes::DIRECTORY,
1299                created: Some(datetime),
1300                modified: datetime,
1301                accessed: Some(datetime.date()),
1302                file_size: 0,
1303                data_cluster: match parent {
1304                    EntryLocationUnit::DataCluster(cluster) => cluster,
1305                    EntryLocationUnit::RootDirSector(_) => 0,
1306                },
1307            },
1308        ]);
1309
1310        // this composer will ALWAYS generate 2 entries
1311        let entries_iter = EntryComposer::new(entries, &self.options.codepage);
1312
1313        self.load_nth_sector(self.data_cluster_to_partition_sector(dir_cluster))?;
1314
1315        // we zero the current sector
1316        self.sector_buffer.borrow_mut().fill(0);
1317
1318        let mut entry_location = EntryLocation {
1319            unit: EntryLocationUnit::DataCluster(dir_cluster),
1320            index: 0,
1321        };
1322
1323        for (i, bytes) in entries_iter.enumerate() {
1324            entry_location.set_bytes(self, bytes)?;
1325
1326            if i < NONROOT_MIN_DIRENTRIES {
1327                entry_location = entry_location
1328                    .next_entry(self)?
1329                    .expect("this will only be called once");
1330            }
1331        }
1332
1333        self.set_modified();
1334
1335        // we also zero everything else in the cluster
1336        let stored_sector = self.sector_buffer.borrow().stored_sector;
1337        for sector in
1338            (stored_sector + 1)..(stored_sector + SectorCount::from(self.sectors_per_cluster()))
1339        {
1340            self.load_nth_sector(sector)?;
1341            self.sector_buffer.borrow_mut().fill(0);
1342            self.set_modified();
1343        }
1344
1345        Ok(dir_cluster)
1346    }
1347
1348    /// Insert the provided `entries` to the cluster chain of the current cached directory
1349    ///
1350    /// Returns the corresponding [`DirEntryChain`]
1351    ///
1352    /// Panics if the `entries` array is empty
1353    pub(crate) fn insert_to_entry_chain(
1354        &self,
1355        entries: Box<[MinProperties]>,
1356    ) -> FSResult<DirEntryChain, S::Error> {
1357        let mut entries_needed = 0;
1358
1359        self._go_to_cached_dir()?;
1360
1361        for entry in &entries {
1362            entries_needed += calc_entries_needed(&*entry.name, &self.options.codepage).get();
1363        }
1364
1365        let first_entry = self.allocate_nth_entries(
1366            num::NonZero::new(entries_needed).expect("The entries array shouldn't be empty"),
1367        )?;
1368
1369        let mut entries_iter = EntryComposer::new(entries, &self.options.codepage);
1370
1371        let mut current_entry = first_entry;
1372        let mut entry_bytes = entries_iter
1373            .next()
1374            .expect("this iterator is guaranteed to return at least once");
1375
1376        loop {
1377            current_entry.set_bytes(self, entry_bytes)?;
1378
1379            match entries_iter.next() {
1380                Some(bytes) => entry_bytes = bytes,
1381                None => break,
1382            };
1383
1384            current_entry = current_entry
1385                .next_entry(self)?
1386                .expect("This entry chain should be valid, we just generated it");
1387        }
1388
1389        Ok(DirEntryChain {
1390            len: entries_needed,
1391            location: first_entry,
1392        })
1393    }
1394
1395    /// Defragment the entry chain of the current directory
1396    ///
1397    /// Returns a [`FSResult`] containing the new number of entries
1398    pub(crate) fn defragment_entry_chain(&self) -> FSResult<EntryCount, S::Error> {
1399        let mut current_entry_loc = EntryLocation {
1400            unit: self.dir_info.borrow().chain_start,
1401            index: 0,
1402        };
1403        let mut new_chain_end = current_entry_loc;
1404        let mut entry_count: EntryCount = 0;
1405
1406        loop {
1407            match current_entry_loc.entry_status(self)? {
1408                EntryStatus::Used => {
1409                    // no reason to copy the bytes if both locations are the same
1410                    if current_entry_loc != new_chain_end {
1411                        // copy the bytes where they belong
1412                        let bytes = current_entry_loc.get_bytes(self)?;
1413
1414                        new_chain_end.set_bytes(self, bytes)?;
1415
1416                        // what if for whatever reason the data types changes?
1417                        #[allow(clippy::absurd_extreme_comparisons)]
1418                        if entry_count >= DIRENTRY_LIMIT {
1419                            break;
1420                        }
1421
1422                        // don't forget to free the entry
1423                        current_entry_loc.free_entry(self, false)?;
1424                    }
1425
1426                    entry_count += 1;
1427
1428                    // advance the new entry chain
1429                    new_chain_end = new_chain_end
1430                        .next_entry(self)?
1431                        .expect("we just pushed an entry to this chain")
1432                }
1433                EntryStatus::LastUnused => break,
1434                _ => (),
1435            }
1436
1437            current_entry_loc = match current_entry_loc.next_entry(self)? {
1438                Some(entry) => entry,
1439                None => break,
1440            }
1441        }
1442
1443        // we should also probably mark the entry after the last used one as last and unused
1444        new_chain_end.free_entry(self, true)?;
1445
1446        self.dir_info.borrow_mut().chain_end = Some(new_chain_end);
1447
1448        Ok(entry_count)
1449    }
1450
1451    /// Mark the individual entries of a contiguous FAT entry chain as unused
1452    ///
1453    /// Note: No validation is done to check whether or not the chain is valid
1454    pub(crate) fn remove_entry_chain(&self, chain: &DirEntryChain) -> Result<(), S::Error> {
1455        let mut entries_freed = 0;
1456        let mut current_entry = chain.location;
1457
1458        loop {
1459            current_entry.free_entry(self, false)?;
1460
1461            entries_freed += 1;
1462
1463            if entries_freed >= chain.len {
1464                break;
1465            }
1466
1467            current_entry = match current_entry.next_entry(self)? {
1468                Some(current_entry) => current_entry,
1469                None => unreachable!(
1470                    concat!("It is guaranteed that at least as many entries ",
1471                    "as there are in chain exist, since we counted them when initializing the struct")
1472                ),
1473            };
1474        }
1475
1476        Ok(())
1477    }
1478
1479    /// Frees all the cluster in a cluster chain starting with `first_cluster`
1480    pub(crate) fn free_cluster_chain(&self, first_cluster: u32) -> Result<(), S::Error> {
1481        let mut current_cluster = first_cluster;
1482
1483        loop {
1484            let next_cluster_option = self.get_next_cluster(current_cluster)?;
1485
1486            // free the current cluster
1487            self.write_nth_FAT_entry(current_cluster, FATEntry::Free)?;
1488
1489            // proceed to the next one, otherwise break
1490            match next_cluster_option {
1491                Some(next_cluster) => current_cluster = next_cluster,
1492                None => break,
1493            }
1494        }
1495
1496        Ok(())
1497    }
1498
1499    /// Allocate `n` clusters and return the index of the first one allocated
1500    ///
1501    /// Also has a second [`Option`] argument that if not [`None`] indicates
1502    /// that this cluster should point to the newly allocated cluster chain
1503    pub(crate) fn allocate_clusters(
1504        &self,
1505        n: num::NonZero<ClusterCount>,
1506        first_cluster: Option<ClusterIndex>,
1507    ) -> FSResult<ClusterIndex, S::Error> {
1508        let mut last_cluster_in_chain = first_cluster;
1509        let mut first_allocated_cluster = None;
1510
1511        for i in 0..n.into() {
1512            match self.next_free_cluster()? {
1513                Some(next_free_cluster) => {
1514                    // FIXME: in FAT12 filesystems, this can cause a sector
1515                    // to be updated up to 4 times for seeminly no reason
1516                    // Similar behavour is observed in FAT16/32, with 2 sync operations
1517                    // THis number should be halved for both cases
1518
1519                    if i == 0 {
1520                        first_allocated_cluster = Some(next_free_cluster);
1521                    }
1522
1523                    // we set the last allocated cluster to point to the next free one
1524                    if let Some(last_cluster_in_chain) = last_cluster_in_chain {
1525                        self.write_nth_FAT_entry(
1526                            last_cluster_in_chain,
1527                            FATEntry::Allocated(next_free_cluster),
1528                        )?;
1529                    }
1530                    // we also set the next free cluster to be EOF
1531                    self.write_nth_FAT_entry(next_free_cluster, FATEntry::Eof)?;
1532                    if let Some(last_cluster_in_chain) = last_cluster_in_chain {
1533                        log::trace!(
1534                            "cluster {last_cluster_in_chain} now points to {next_free_cluster}"
1535                        );
1536                    }
1537                    // now the next free cluster i the last allocated one
1538                    last_cluster_in_chain = Some(next_free_cluster);
1539                }
1540                None => {
1541                    log::error!("storage medium full while attempting to allocate more clusters");
1542                    return Err(FSError::StorageFull);
1543                }
1544            }
1545        }
1546
1547        Ok(first_allocated_cluster.expect("This should have Some value by now"))
1548    }
1549
1550    /// Syncs `self.sector_buffer` back to the storage
1551    fn _sync_current_sector(&self) -> Result<(), S::Error> {
1552        self.storage
1553            .borrow_mut()
1554            .write_all(&self.sector_buffer.borrow())?;
1555        self.storage
1556            .borrow_mut()
1557            .seek(SeekFrom::Current(-i64::from(self.props.sector_size)))?;
1558
1559        Ok(())
1560    }
1561
1562    /// Syncs a FAT sector to ALL OTHER FAT COPIES on the device medium
1563    #[allow(non_snake_case)]
1564    fn _sync_FAT_sector(&self, fat_sector_props: &FATSectorProps) -> Result<(), S::Error> {
1565        let current_offset = self.storage.borrow_mut().stream_position()?;
1566
1567        for sector in fat_sector_props.get_corresponding_FAT_sectors(self) {
1568            self.storage.borrow_mut().seek(SeekFrom::Start(u64::from(
1569                sector * u32::from(self.props.sector_size),
1570            )))?;
1571            self.storage
1572                .borrow_mut()
1573                .write_all(&self.sector_buffer.borrow())?;
1574        }
1575
1576        self.storage
1577            .borrow_mut()
1578            .seek(SeekFrom::Start(current_offset))?;
1579
1580        Ok(())
1581    }
1582
1583    /// Marks that a modification has been made to the storage medium, setting the `sync_f` and `unmount_f` fields
1584    pub(crate) fn set_modified(&self) {
1585        *self.sync_f.borrow_mut() = Some(Self::sync_sector_buffer);
1586        *self.unmount_f.borrow_mut() = Some(Self::unmount);
1587    }
1588
1589    pub(crate) fn sync_sector_buffer(&self) -> Result<(), S::Error> {
1590        // If this is called, we assume the sector buffer has been modified
1591        let stored_sector = self.sector_buffer.borrow().stored_sector;
1592        if let Some(fat_sector_props) = FATSectorProps::new(stored_sector, self) {
1593            log::trace!("syncing FAT sector {}", fat_sector_props.sector_offset,);
1594            match &*self.boot_record.borrow() {
1595                BootRecord::Fat(boot_record_fat) => match &boot_record_fat.ebr {
1596                    Ebr::FAT12_16(_) => {
1597                        self._sync_FAT_sector(&fat_sector_props)?;
1598                    }
1599                    Ebr::FAT32(ebr_fat32, _) => {
1600                        if ebr_fat32.extended_flags.mirroring_disabled() {
1601                            self._sync_current_sector()?;
1602                        } else {
1603                            self._sync_FAT_sector(&fat_sector_props)?;
1604                        }
1605                    }
1606                },
1607                BootRecord::ExFAT(_boot_record_exfat) => todo!("ExFAT not yet implemented"),
1608            }
1609        } else {
1610            log::trace!(
1611                "syncing sector {}",
1612                self.sector_buffer.borrow().stored_sector
1613            );
1614            self._sync_current_sector()?;
1615        }
1616
1617        // we don't want to call this again for no reason
1618        *self.sync_f.borrow_mut() = None;
1619
1620        Ok(())
1621    }
1622
1623    /// Sync the [`FSInfoFAT32`] back to the storage medium
1624    /// if this is FAT32
1625    pub(crate) fn sync_fsinfo(&self) -> FSResult<(), S::Error> {
1626        use utils::bincode::BINCODE_CONFIG;
1627
1628        if *self.fsinfo_modified.borrow() {
1629            if let BootRecord::Fat(boot_record_fat) = &*self.boot_record.borrow() {
1630                if let Ebr::FAT32(ebr_fat32, fsinfo) = &boot_record_fat.ebr {
1631                    self.load_nth_sector(ebr_fat32.fat_info.into())?;
1632
1633                    bincode::encode_into_slice(
1634                        fsinfo,
1635                        &mut self.sector_buffer.borrow_mut()[..FSINFO_SIZE],
1636                        BINCODE_CONFIG,
1637                    )
1638                    .map_err(utils::bincode::map_err_enc)?;
1639                }
1640            }
1641
1642            *self.fsinfo_modified.borrow_mut() = false;
1643        }
1644
1645        Ok(())
1646    }
1647
1648    /// Like [`Self::get_rw_file`], but will ignore the read-only flag (if it is present)
1649    ///
1650    /// This is a private function for obvious reasons
1651    fn get_rw_file_unchecked<P: AsRef<Path>>(&self, path: P) -> FSResult<RWFile<'_, S>, S::Error> {
1652        let ro_file = self.get_ro_file(path)?;
1653
1654        Ok(ro_file.into())
1655    }
1656}
1657
1658/// Public [`Read`]-related functions
1659impl<S> FileSystem<S>
1660where
1661    S: Read + Seek,
1662{
1663    /// Read all the entries of a directory ([`Path`]) into [`ReadDir`]
1664    ///
1665    /// Fails if `path` doesn't represent a directory, or if that directory doesn't exist
1666    pub fn read_dir<P: AsRef<Path>>(&self, path: P) -> FSResult<ReadDir<'_, S>, S::Error> {
1667        // normalize the given path
1668        let path = path.as_ref();
1669
1670        if !path.is_valid() {
1671            return Err(FSError::MalformedPath);
1672        }
1673
1674        let path = path.normalize();
1675
1676        self.go_to_dir(&path)?;
1677
1678        Ok(ReadDir::new(
1679            self,
1680            &self.dir_info.borrow().chain_start,
1681            &self.dir_info.borrow().path,
1682        ))
1683    }
1684
1685    /// Get a corresponding [`ROFile`] object from a [`Path`]
1686    ///
1687    /// Fails if `path` doesn't represent a file, or if that file doesn't exist
1688    pub fn get_ro_file<P: AsRef<Path>>(&self, path: P) -> FSResult<ROFile<'_, S>, S::Error> {
1689        let path = path.as_ref();
1690
1691        if !path.is_valid() {
1692            return Err(FSError::MalformedPath);
1693        }
1694
1695        if let Some(file_name) = path.file_name() {
1696            let parent_dir = self.read_dir(
1697                path.parent()
1698                    .expect("we aren't in the root directory, this shouldn't panic"),
1699            )?;
1700
1701            // don't ask, I don't know either (https://doc.rust-lang.org/std/boxed/index.html#editions)
1702            let mut entry = None;
1703
1704            for dir_entry in parent_dir {
1705                let dir_entry = dir_entry?;
1706
1707                if dir_entry
1708                    .path()
1709                    .file_name()
1710                    .is_some_and(|entry_name| entry_name == file_name)
1711                {
1712                    entry = Some(dir_entry.entry);
1713
1714                    break;
1715                }
1716            }
1717
1718            match entry {
1719                Some(entry) => {
1720                    let mut file = ROFile::from_props(
1721                        FileProps {
1722                            offset: 0,
1723                            current_cluster: entry.data_cluster,
1724                            entry,
1725                        },
1726                        self,
1727                    );
1728
1729                    if file.cluster_chain_is_healthy()? {
1730                        Ok(file)
1731                    } else {
1732                        log::error!("The cluster chain of a file is malformed");
1733                        Err(FSError::InternalFSError(
1734                            InternalFSError::MalformedClusterChain,
1735                        ))
1736                    }
1737                }
1738                None => {
1739                    log::error!("ROFile {path} not found");
1740
1741                    Err(FSError::NotFound)
1742                }
1743            }
1744        } else {
1745            log::error!("Is a directory (not a file)");
1746            Err(FSError::IsADirectory)
1747        }
1748    }
1749}
1750
1751/// [`Write`]-related functions
1752impl<S> FileSystem<S>
1753where
1754    S: Read + Write + Seek,
1755{
1756    /// Create a new [`RWFile`] and return its handle
1757    #[inline]
1758    pub fn create_file<P: AsRef<Path>>(&self, path: P) -> FSResult<RWFile<'_, S>, S::Error> {
1759        let path = path.as_ref();
1760
1761        if !path.is_valid() {
1762            return Err(FSError::MalformedPath);
1763        }
1764
1765        let target = path.normalize();
1766
1767        let parent_dir = match target.parent() {
1768            Some(parent) => parent,
1769            // technically, the path provided is a directory, the root directory
1770            None => return Err(FSError::IsADirectory),
1771        };
1772
1773        let file_name = target
1774            .file_name()
1775            .expect("the path is normalized and it isn't the root directory either");
1776
1777        self.go_to_dir(parent_dir)?;
1778
1779        // check if there is already a file or directory with the same name
1780        for entry in self.process_current_dir() {
1781            let entry = entry?;
1782
1783            if entry.name == file_name {
1784                return Err(FSError::AlreadyExists);
1785            }
1786        }
1787
1788        let file_cluster = self.allocate_clusters(num::NonZero::new(1).expect("1 != 0"), None)?;
1789
1790        let sfn = utils::string::gen_sfn(file_name, self, parent_dir)?;
1791
1792        let now = self.options.clock.now();
1793
1794        // we got everything to create our first (and only) RawProperties struct
1795        let raw_properties = MinProperties {
1796            name: file_name.into(),
1797            sfn,
1798            // this needs to be set when creating a file
1799            attributes: RawAttributes::empty() | RawAttributes::ARCHIVE,
1800            created: Some(now),
1801            modified: now,
1802            accessed: Some(now.date()),
1803            file_size: 0,
1804            data_cluster: file_cluster,
1805        };
1806
1807        let entries = [raw_properties.clone()];
1808        let chain = self.insert_to_entry_chain(Box::new(entries))?;
1809
1810        Ok(RWFile::from_props(
1811            FileProps {
1812                current_cluster: raw_properties.data_cluster,
1813                entry: Properties::from_raw(
1814                    RawProperties::from_chain(raw_properties, chain),
1815                    path.into(),
1816                    self.options.codepage,
1817                ),
1818                offset: 0,
1819            },
1820            self,
1821        ))
1822    }
1823
1824    /// Create a new directory
1825    #[inline]
1826    pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> FSResult<(), S::Error> {
1827        let path = path.as_ref();
1828
1829        if !path.is_valid() {
1830            return Err(FSError::MalformedPath);
1831        }
1832
1833        let target = path.normalize();
1834
1835        let parent_dir = match target.parent() {
1836            Some(parent) => parent,
1837            // the path provided is the root directory, which already exists
1838            None => return Err(FSError::AlreadyExists),
1839        };
1840
1841        let file_name = target
1842            .file_name()
1843            .expect("the path is normalized and it isn't the root directory either");
1844
1845        // check if there is already a file or directory with the same name
1846        for entry in self.process_current_dir() {
1847            let entry = entry?;
1848
1849            if entry.name == file_name {
1850                return Err(FSError::AlreadyExists);
1851            }
1852        }
1853
1854        let now = self.options.clock.now();
1855
1856        let dir_cluster = self.create_entry_chain(self.dir_info.borrow().chain_start, now)?;
1857
1858        // The cluster chain of the directory has been created,
1859        // we now need to add it as an entry to the current directory
1860
1861        let sfn = utils::string::gen_sfn(file_name, self, parent_dir)?;
1862
1863        // we got everything to create our first (and only) RawProperties struct
1864        let raw_properties = MinProperties {
1865            name: file_name.into(),
1866            sfn,
1867            attributes: RawAttributes::empty() | RawAttributes::DIRECTORY,
1868            created: Some(now),
1869            modified: now,
1870            accessed: Some(now.date()),
1871            file_size: 0,
1872            data_cluster: dir_cluster,
1873        };
1874
1875        let entries = [raw_properties];
1876
1877        self.go_to_dir(parent_dir)?;
1878
1879        self.insert_to_entry_chain(Box::new(entries))?;
1880
1881        Ok(())
1882    }
1883
1884    /// Rename a file or directory to a new name
1885    pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> FSResult<(), S::Error> {
1886        let from = from.as_ref();
1887        let to = to.as_ref();
1888
1889        if !from.is_valid() || !to.is_valid() {
1890            return Err(FSError::MalformedPath);
1891        }
1892
1893        let from = from.normalize();
1894        let to = to.normalize();
1895
1896        let parent_from = match from.parent() {
1897            Some(parent) => parent,
1898            // we can't rename the root directory
1899            None => return Err(FSError::PermissionDenied),
1900        };
1901        let parent_to = match to.parent() {
1902            Some(parent) => parent,
1903            // we can't rename the root directory
1904            None => return Err(FSError::PermissionDenied),
1905        };
1906
1907        let entry_from = {
1908            let mut entry_from = None;
1909
1910            for entry in self.read_dir(parent_from)? {
1911                let entry = entry?;
1912
1913                if *entry.path() == from {
1914                    entry_from = Some(entry);
1915
1916                    break;
1917                }
1918            }
1919
1920            match entry_from {
1921                Some(entry) => entry,
1922                None => return Err(FSError::NotFound),
1923            }
1924        };
1925
1926        for entry in self.read_dir(parent_to)? {
1927            let entry = entry?;
1928
1929            if *entry.path() == to {
1930                return Err(FSError::AlreadyExists);
1931            }
1932        }
1933
1934        // if the entry is a file, everything is way more simple,
1935        // we just need to remove this entry a create a new one at
1936        // the target directory. This can be accomplished in 2 ways:
1937        // 1. we first remove the old entry and then create the new one, or
1938        // 2. we first create the new entry and then remove the old one
1939        // the first method is easier to implement, but has a higher risk of data loss
1940        // the second method is a bit more difficult and in a worst-case scenario
1941        // the file won't be lost, althought we will be left with 2 hard links
1942        // pointing to the same file. Here we use the second method
1943        self.go_to_dir(parent_to)?;
1944
1945        let now = self.options.clock.now();
1946
1947        if entry_from.is_dir() {
1948            // the process with directories is the same, expect we must modify the ".." entry
1949            // so that it points to the new parent directory
1950            // the ".." entry is always the second entry, so we will do something a bit hacky here
1951            let parent_entry = MinProperties {
1952                name: Box::from(typed_path::constants::windows::PARENT_DIR_STR),
1953                sfn: PARENT_DIR_SFN,
1954                // this needs to be set when creating a file
1955                attributes: RawAttributes::empty() | RawAttributes::DIRECTORY,
1956                created: Some(now),
1957                modified: now,
1958                accessed: Some(now.date()),
1959                file_size: 0,
1960                data_cluster: match self.dir_info.borrow().chain_start {
1961                    EntryLocationUnit::DataCluster(cluster) => cluster,
1962                    EntryLocationUnit::RootDirSector(_) => 0,
1963                },
1964            };
1965
1966            // we are modifying the 2nd entry
1967            let entry_location = EntryLocation {
1968                unit: EntryLocationUnit::DataCluster(entry_from.data_cluster),
1969                index: 1,
1970            };
1971
1972            use utils::bincode::BINCODE_CONFIG;
1973
1974            self._go_to_cached_dir()?;
1975            let mut bytes: [u8; DIRENTRY_SIZE] = [0; DIRENTRY_SIZE];
1976
1977            bincode::encode_into_slice(FATDirEntry::from(parent_entry), &mut bytes, BINCODE_CONFIG)
1978                .map_err(utils::bincode::map_err_enc)?;
1979
1980            entry_location.set_bytes(self, bytes)?;
1981        }
1982
1983        let old_chain = entry_from.chain;
1984        let old_props: MinProperties = entry_from.into();
1985        let to_filename = to.file_name().expect("this path is normalized");
1986        let sfn = utils::string::gen_sfn(to_filename, self, parent_to)?;
1987
1988        let props = MinProperties {
1989            name: Box::from(to_filename),
1990            sfn,
1991            attributes: old_props.attributes,
1992            created: Some(now),
1993            modified: now,
1994            accessed: Some(now.date()),
1995            file_size: old_props.file_size,
1996            data_cluster: old_props.data_cluster,
1997        };
1998        self.insert_to_entry_chain(Box::from([props]))?;
1999
2000        self.remove_entry_chain(&old_chain)?;
2001
2002        Ok(())
2003    }
2004
2005    /// Remove a [`RWFile`] from the filesystem
2006    ///
2007    /// This is an alias to `self.get_rw_file(path)?.remove()?`
2008    #[inline]
2009    pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> FSResult<(), S::Error> {
2010        self.get_rw_file(path)?.remove()?;
2011
2012        Ok(())
2013    }
2014
2015    /// Remove a file from the filesystem, even if it is read-only
2016    ///
2017    /// **USE WITH EXTREME CAUTION!**
2018    #[inline]
2019    pub fn remove_file_unchecked<P: AsRef<Path>>(&self, path: P) -> FSResult<(), S::Error> {
2020        self.get_rw_file_unchecked(path)?.remove()?;
2021
2022        Ok(())
2023    }
2024
2025    /// Remove an empty directory from the filesystem
2026    ///
2027    /// Errors if the path provided points to the root directory
2028    pub fn remove_empty_dir<P: AsRef<Path>>(&self, path: P) -> FSResult<(), S::Error> {
2029        let path = path.as_ref();
2030
2031        if !path.is_valid() {
2032            return Err(FSError::MalformedPath);
2033        }
2034
2035        if path
2036            .components()
2037            .next_back()
2038            .expect("this iterator will always yield at least the root directory")
2039            == WindowsComponent::root()
2040        {
2041            // we are in the root directory, we can't remove it
2042            return Err(FSError::InvalidInput);
2043        }
2044
2045        if self.read_dir(path)?.next().is_some() {
2046            return Err(FSError::DirectoryNotEmpty);
2047        }
2048
2049        let parent_path = path
2050            .parent()
2051            .expect("we aren't in the root directory, this shouldn't panic");
2052
2053        let parent_dir_entries = self.read_dir(parent_path)?;
2054
2055        let entry = {
2056            let mut entry = None;
2057
2058            for ent in parent_dir_entries {
2059                let ent = ent?;
2060
2061                if ent.path() == path {
2062                    entry = Some(ent);
2063
2064                    break;
2065                }
2066            }
2067
2068            entry.ok_or(FSError::NotFound)?
2069        };
2070
2071        // we first clear the corresponding entry chain in the parent directory
2072        self.remove_entry_chain(&entry.chain)?;
2073
2074        // then we remove the allocated cluster chain
2075        self.free_cluster_chain(entry.data_cluster)?;
2076
2077        Ok(())
2078    }
2079
2080    /// Removes a directory at this path, after removing all its contents.
2081    ///
2082    /// Use with caution!
2083    ///
2084    /// This will fail if there is at least 1 (one) read-only file
2085    /// in this directory or in any subdirectory. To avoid this behavior,
2086    /// use [`remove_dir_all_unchecked()`](FileSystem::remove_dir_all_unchecked)
2087    pub fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> FSResult<(), S::Error> {
2088        // before we actually start removing stuff,
2089        // let's make sure there are no read-only files
2090
2091        if self.check_for_readonly_files(&path)? {
2092            log::error!(concat!(
2093                "A read-only file has been found ",
2094                "in a directory pending deletion."
2095            ));
2096            return Err(FSError::ReadOnlyFile);
2097        }
2098
2099        // we have checked everything, this is safe to use
2100        self.remove_dir_all_unchecked(&path)?;
2101
2102        Ok(())
2103    }
2104
2105    /// Like [`remove_dir_all()`](FileSystem::remove_dir_all),
2106    /// but also removes read-only files.
2107    ///
2108    /// **USE WITH EXTREME CAUTION!**
2109    pub fn remove_dir_all_unchecked<P: AsRef<Path>>(&self, path: P) -> FSResult<(), S::Error> {
2110        let path = path.as_ref();
2111
2112        if !path.is_valid() {
2113            return Err(FSError::MalformedPath);
2114        }
2115
2116        let mut read_dir = self.read_dir(path)?;
2117        loop {
2118            let entry = match read_dir.next() {
2119                Some(entry) => entry?,
2120                None => break,
2121            };
2122
2123            if entry.is_dir() {
2124                self.remove_dir_all_unchecked(&entry.path)?;
2125            } else if entry.is_file() {
2126                self.remove_file_unchecked(&entry.path)?;
2127            } else {
2128                unreachable!()
2129            }
2130        }
2131
2132        self.remove_empty_dir(path)?;
2133
2134        Ok(())
2135    }
2136
2137    /// Check `path` recursively to see if there are any read-only files in it
2138    ///
2139    /// If successful, the `bool` returned indicates
2140    /// whether or not at least 1 (one) read-only file has been found
2141    pub fn check_for_readonly_files<P: AsRef<Path>>(&self, path: P) -> FSResult<bool, S::Error> {
2142        let path = path.as_ref();
2143
2144        if !path.is_valid() {
2145            return Err(FSError::MalformedPath);
2146        }
2147
2148        let mut read_dir = self.read_dir(path)?;
2149
2150        loop {
2151            let entry = match read_dir.next() {
2152                Some(entry) => entry?,
2153                None => break,
2154            };
2155
2156            let read_only_found = if entry.is_dir() {
2157                self.check_for_readonly_files(&entry.path)?
2158            } else if entry.is_file() {
2159                entry.attributes.read_only
2160            } else {
2161                unreachable!()
2162            };
2163
2164            if read_only_found {
2165                // we have found at least 1 read-only file,
2166                // no need to search any further
2167                return Ok(true);
2168            }
2169        }
2170
2171        Ok(false)
2172    }
2173
2174    /// Get a corresponding [`RWFile`] object from a [`Path`]
2175    ///
2176    /// Fails if `path` doesn't represent a file, or if that file doesn't exist
2177    pub fn get_rw_file<P: AsRef<Path>>(&self, path: P) -> FSResult<RWFile<'_, S>, S::Error> {
2178        let rw_file = self.get_rw_file_unchecked(path)?;
2179
2180        if rw_file.attributes.read_only {
2181            return Err(FSError::ReadOnlyFile);
2182        }
2183
2184        Ok(rw_file)
2185    }
2186
2187    /// Sync any pending changes back to the storage medium and drop
2188    ///
2189    /// Use this to catch any IO errors that might be rejected silently
2190    /// while [`Drop`]ping
2191    pub fn unmount(&self) -> FSResult<(), S::Error> {
2192        self.sync_fsinfo()?;
2193        let should_sync_buffer = self.sync_f.borrow().is_some();
2194        if should_sync_buffer {
2195            self.sync_sector_buffer()?;
2196        }
2197        self.storage.borrow_mut().flush()?;
2198
2199        Ok(())
2200    }
2201}
2202
2203impl<S> ops::Drop for FileSystem<S>
2204where
2205    S: Read + Seek,
2206{
2207    fn drop(&mut self) {
2208        if let Some(unmount) = *self.unmount_f.borrow() {
2209            // nothing to do if this errors out while dropping
2210            let _ = unmount(self);
2211        }
2212    }
2213}