Skip to main content

fatfs/
fs.rs

1#[cfg(all(not(feature = "std"), feature = "alloc"))]
2use alloc::string::String;
3use core::borrow::BorrowMut;
4use core::cell::{Cell, RefCell};
5use core::convert::TryFrom;
6use core::fmt::Debug;
7use core::marker::PhantomData;
8
9use crate::boot_sector::{format_boot_sector, BiosParameterBlock, BootSector};
10use crate::dir::{Dir, DirRawStream};
11use crate::dir_entry::{DirFileEntryData, FileAttributes, SFN_PADDING, SFN_SIZE};
12use crate::error::Error;
13use crate::file::File;
14use crate::io::{self, IoBase, Read, ReadLeExt, Seek, SeekFrom, Write, WriteLeExt};
15use crate::table::{
16    alloc_cluster, count_free_clusters, format_fat, read_fat_flags, ClusterIterator, RESERVED_FAT_ENTRIES,
17};
18use crate::time::{DefaultTimeProvider, TimeProvider};
19
20// FAT implementation based on:
21//   http://wiki.osdev.org/FAT
22//   https://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html
23
24/// A type of FAT filesystem.
25///
26/// `FatType` values are based on the size of File Allocation Table entry.
27#[derive(Copy, Clone, Eq, PartialEq, Debug)]
28pub enum FatType {
29    /// 12 bits per FAT entry
30    Fat12,
31    /// 16 bits per FAT entry
32    Fat16,
33    /// 32 bits per FAT entry
34    Fat32,
35}
36
37impl FatType {
38    const FAT16_MIN_CLUSTERS: u32 = 4085;
39    const FAT32_MIN_CLUSTERS: u32 = 65525;
40    const FAT32_MAX_CLUSTERS: u32 = 0x0FFF_FFF4;
41
42    pub(crate) fn from_clusters(total_clusters: u32) -> Self {
43        if total_clusters < Self::FAT16_MIN_CLUSTERS {
44            FatType::Fat12
45        } else if total_clusters < Self::FAT32_MIN_CLUSTERS {
46            FatType::Fat16
47        } else {
48            FatType::Fat32
49        }
50    }
51
52    pub(crate) fn bits_per_fat_entry(self) -> u32 {
53        match self {
54            FatType::Fat12 => 12,
55            FatType::Fat16 => 16,
56            FatType::Fat32 => 32,
57        }
58    }
59
60    pub(crate) fn min_clusters(self) -> u32 {
61        match self {
62            FatType::Fat12 => 0,
63            FatType::Fat16 => Self::FAT16_MIN_CLUSTERS,
64            FatType::Fat32 => Self::FAT32_MIN_CLUSTERS,
65        }
66    }
67
68    pub(crate) fn max_clusters(self) -> u32 {
69        match self {
70            FatType::Fat12 => Self::FAT16_MIN_CLUSTERS - 1,
71            FatType::Fat16 => Self::FAT32_MIN_CLUSTERS - 1,
72            FatType::Fat32 => Self::FAT32_MAX_CLUSTERS,
73        }
74    }
75}
76
77/// A FAT volume status flags retrived from the Boot Sector and the allocation table second entry.
78#[derive(Copy, Clone, Eq, PartialEq, Debug)]
79pub struct FsStatusFlags {
80    pub(crate) dirty: bool,
81    pub(crate) io_error: bool,
82}
83
84impl FsStatusFlags {
85    /// Checks if the volume is marked as dirty.
86    ///
87    /// Dirty flag means volume has been suddenly ejected from filesystem without unmounting.
88    #[must_use]
89    pub fn dirty(&self) -> bool {
90        self.dirty
91    }
92
93    /// Checks if the volume has the IO Error flag active.
94    #[must_use]
95    pub fn io_error(&self) -> bool {
96        self.io_error
97    }
98
99    fn encode(self) -> u8 {
100        let mut res = 0_u8;
101        if self.dirty {
102            res |= 1;
103        }
104        if self.io_error {
105            res |= 2;
106        }
107        res
108    }
109
110    pub(crate) fn decode(flags: u8) -> Self {
111        Self {
112            dirty: flags & 1 != 0,
113            io_error: flags & 2 != 0,
114        }
115    }
116}
117
118/// A sum of `Read` and `Seek` traits.
119pub trait ReadSeek: Read + Seek {}
120impl<T: Read + Seek> ReadSeek for T {}
121
122/// A sum of `Read`, `Write` and `Seek` traits.
123pub trait ReadWriteSeek: Read + Write + Seek {}
124impl<T: Read + Write + Seek> ReadWriteSeek for T {}
125
126#[derive(Clone, Default, Debug)]
127struct FsInfoSector {
128    free_cluster_count: Option<u32>,
129    next_free_cluster: Option<u32>,
130    dirty: bool,
131}
132
133impl FsInfoSector {
134    const LEAD_SIG: u32 = 0x4161_5252;
135    const STRUC_SIG: u32 = 0x6141_7272;
136    const TRAIL_SIG: u32 = 0xAA55_0000;
137
138    fn deserialize<R: Read>(rdr: &mut R) -> Result<Self, Error<R::Error>> {
139        let lead_sig = rdr.read_u32_le()?;
140        if lead_sig != Self::LEAD_SIG {
141            error!("invalid lead_sig in FsInfo sector: {}", lead_sig);
142            return Err(Error::CorruptedFileSystem);
143        }
144        let mut reserved = [0_u8; 480];
145        rdr.read_exact(&mut reserved)?;
146        let struc_sig = rdr.read_u32_le()?;
147        if struc_sig != Self::STRUC_SIG {
148            error!("invalid struc_sig in FsInfo sector: {}", struc_sig);
149            return Err(Error::CorruptedFileSystem);
150        }
151        let free_cluster_count = match rdr.read_u32_le()? {
152            0xFFFF_FFFF => None,
153            // Note: value is validated in FileSystem::new function using values from BPB
154            n => Some(n),
155        };
156        let next_free_cluster = match rdr.read_u32_le()? {
157            0xFFFF_FFFF => None,
158            0 | 1 => {
159                warn!("invalid next_free_cluster in FsInfo sector (values 0 and 1 are reserved)");
160                None
161            }
162            // Note: other values are validated in FileSystem::new function using values from BPB
163            n => Some(n),
164        };
165        let mut reserved2 = [0_u8; 12];
166        rdr.read_exact(&mut reserved2)?;
167        let trail_sig = rdr.read_u32_le()?;
168        if trail_sig != Self::TRAIL_SIG {
169            error!("invalid trail_sig in FsInfo sector: {}", trail_sig);
170            return Err(Error::CorruptedFileSystem);
171        }
172        Ok(Self {
173            free_cluster_count,
174            next_free_cluster,
175            dirty: false,
176        })
177    }
178
179    fn serialize<W: Write>(&self, wrt: &mut W) -> Result<(), Error<W::Error>> {
180        wrt.write_u32_le(Self::LEAD_SIG)?;
181        let reserved = [0_u8; 480];
182        wrt.write_all(&reserved)?;
183        wrt.write_u32_le(Self::STRUC_SIG)?;
184        wrt.write_u32_le(self.free_cluster_count.unwrap_or(0xFFFF_FFFF))?;
185        wrt.write_u32_le(self.next_free_cluster.unwrap_or(0xFFFF_FFFF))?;
186        let reserved2 = [0_u8; 12];
187        wrt.write_all(&reserved2)?;
188        wrt.write_u32_le(Self::TRAIL_SIG)?;
189        Ok(())
190    }
191
192    fn validate_and_fix(&mut self, total_clusters: u32) {
193        let max_valid_cluster_number = total_clusters + RESERVED_FAT_ENTRIES;
194        if let Some(n) = self.free_cluster_count {
195            if n > total_clusters {
196                warn!(
197                    "invalid free_cluster_count ({}) in fs_info exceeds total cluster count ({})",
198                    n, total_clusters
199                );
200                self.free_cluster_count = None;
201            }
202        }
203        if let Some(n) = self.next_free_cluster {
204            if n > max_valid_cluster_number {
205                warn!(
206                    "invalid free_cluster_count ({}) in fs_info exceeds maximum cluster number ({})",
207                    n, max_valid_cluster_number
208                );
209                self.next_free_cluster = None;
210            }
211        }
212    }
213
214    fn map_free_clusters(&mut self, map_fn: impl Fn(u32) -> u32) {
215        if let Some(n) = self.free_cluster_count {
216            self.free_cluster_count = Some(map_fn(n));
217            self.dirty = true;
218        }
219    }
220
221    fn set_next_free_cluster(&mut self, cluster: u32) {
222        self.next_free_cluster = Some(cluster);
223        self.dirty = true;
224    }
225
226    fn set_free_cluster_count(&mut self, free_cluster_count: u32) {
227        self.free_cluster_count = Some(free_cluster_count);
228        self.dirty = true;
229    }
230}
231
232/// A FAT filesystem mount options.
233///
234/// Options are specified as an argument for `FileSystem::new` method.
235#[derive(Copy, Clone, Debug, Default)]
236pub struct FsOptions<TP, OCC> {
237    pub(crate) update_accessed_date: bool,
238    pub(crate) oem_cp_converter: OCC,
239    pub(crate) time_provider: TP,
240    pub(crate) strict: bool,
241}
242
243impl FsOptions<DefaultTimeProvider, LossyOemCpConverter> {
244    /// Creates a `FsOptions` struct with default options.
245    #[must_use]
246    pub fn new() -> Self {
247        Self {
248            update_accessed_date: false,
249            oem_cp_converter: LossyOemCpConverter::new(),
250            time_provider: DefaultTimeProvider::new(),
251            strict: true,
252        }
253    }
254}
255
256impl<TP: TimeProvider, OCC: OemCpConverter> FsOptions<TP, OCC> {
257    /// If enabled accessed date field in directory entry is updated when reading or writing a file.
258    #[must_use]
259    pub fn update_accessed_date(mut self, enabled: bool) -> Self {
260        self.update_accessed_date = enabled;
261        self
262    }
263
264    /// Changes default OEM code page encoder-decoder.
265    pub fn oem_cp_converter<OCC2: OemCpConverter>(self, oem_cp_converter: OCC2) -> FsOptions<TP, OCC2> {
266        FsOptions::<TP, OCC2> {
267            update_accessed_date: self.update_accessed_date,
268            oem_cp_converter,
269            time_provider: self.time_provider,
270            strict: self.strict,
271        }
272    }
273
274    /// Changes default time provider.
275    pub fn time_provider<TP2: TimeProvider>(self, time_provider: TP2) -> FsOptions<TP2, OCC> {
276        FsOptions::<TP2, OCC> {
277            update_accessed_date: self.update_accessed_date,
278            oem_cp_converter: self.oem_cp_converter,
279            time_provider,
280            strict: self.strict,
281        }
282    }
283
284    /// If enabled more validations are performed to check if file-system is conforming to specification.
285    pub fn strict(self, strict: bool) -> Self {
286        Self {
287            update_accessed_date: self.update_accessed_date,
288            oem_cp_converter: self.oem_cp_converter,
289            time_provider: self.time_provider,
290            strict,
291        }
292    }
293}
294
295/// A FAT volume statistics.
296#[derive(Copy, Clone, Eq, PartialEq, Debug)]
297pub struct FileSystemStats {
298    cluster_size: u32,
299    total_clusters: u32,
300    free_clusters: u32,
301}
302
303impl FileSystemStats {
304    /// Cluster size in bytes
305    #[must_use]
306    pub fn cluster_size(&self) -> u32 {
307        self.cluster_size
308    }
309
310    /// Number of total clusters in filesystem usable for file allocation
311    #[must_use]
312    pub fn total_clusters(&self) -> u32 {
313        self.total_clusters
314    }
315
316    /// Number of free clusters
317    #[must_use]
318    pub fn free_clusters(&self) -> u32 {
319        self.free_clusters
320    }
321}
322
323/// A FAT filesystem object.
324///
325/// `FileSystem` struct is representing a state of a mounted FAT volume.
326pub struct FileSystem<IO: ReadWriteSeek, TP = DefaultTimeProvider, OCC = LossyOemCpConverter> {
327    pub(crate) disk: RefCell<IO>,
328    pub(crate) options: FsOptions<TP, OCC>,
329    fat_type: FatType,
330    bpb: BiosParameterBlock,
331    first_data_sector: u32,
332    root_dir_sectors: u32,
333    total_clusters: u32,
334    fs_info: RefCell<FsInfoSector>,
335    current_status_flags: Cell<FsStatusFlags>,
336}
337
338pub trait IntoStorage<T: Read + Write + Seek> {
339    fn into_storage(self) -> T;
340}
341
342impl<T: Read + Write + Seek> IntoStorage<T> for T {
343    fn into_storage(self) -> Self {
344        self
345    }
346}
347
348#[cfg(feature = "std")]
349impl<T: std::io::Read + std::io::Write + std::io::Seek> IntoStorage<io::StdIoWrapper<T>> for T {
350    fn into_storage(self) -> io::StdIoWrapper<Self> {
351        io::StdIoWrapper::new(self)
352    }
353}
354
355impl<IO: Read + Write + Seek, TP, OCC> FileSystem<IO, TP, OCC> {
356    /// Creates a new filesystem object instance.
357    ///
358    /// Supplied `storage` parameter cannot be seeked. If there is a need to read a fragment of disk
359    /// image (e.g. partition) library user should wrap the file struct in a struct limiting
360    /// access to partition bytes only e.g. `fscommon::StreamSlice`.
361    ///
362    /// Note: creating multiple filesystem objects with a single underlying storage can
363    /// cause a filesystem corruption.
364    ///
365    /// # Errors
366    ///
367    /// Errors that can be returned:
368    ///
369    /// * `Error::CorruptedFileSystem` will be returned if the boot sector and/or the file system information sector
370    ///   contains invalid values.
371    /// * `Error::Io` will be returned if the provided storage object returned an I/O error.
372    ///
373    /// # Panics
374    ///
375    /// Panics in non-optimized build if `storage` position returned by `seek` is not zero.
376    pub fn new<T: IntoStorage<IO>>(storage: T, options: FsOptions<TP, OCC>) -> Result<Self, Error<IO::Error>> {
377        // Make sure given image is not seeked
378        let mut disk = storage.into_storage();
379        trace!("FileSystem::new");
380        debug_assert!(disk.seek(SeekFrom::Current(0))? == 0);
381
382        // read boot sector
383        let bpb = {
384            let boot = BootSector::deserialize(&mut disk)?;
385            boot.validate(options.strict)?;
386            boot.bpb
387        };
388
389        let root_dir_sectors = bpb.root_dir_sectors();
390        let first_data_sector = bpb.first_data_sector();
391        let total_clusters = bpb.total_clusters();
392        let fat_type = FatType::from_clusters(total_clusters);
393
394        // read FSInfo sector if this is FAT32
395        let mut fs_info = if fat_type == FatType::Fat32 {
396            disk.seek(SeekFrom::Start(bpb.bytes_from_sectors(bpb.fs_info_sector())))?;
397            FsInfoSector::deserialize(&mut disk)?
398        } else {
399            FsInfoSector::default()
400        };
401
402        // if dirty flag is set completly ignore free_cluster_count in FSInfo
403        if bpb.status_flags().dirty {
404            fs_info.free_cluster_count = None;
405        }
406
407        // Validate the numbers stored in the free_cluster_count and next_free_cluster are within bounds for volume
408        fs_info.validate_and_fix(total_clusters);
409
410        // return FileSystem struct
411        let status_flags = bpb.status_flags();
412        trace!("FileSystem::new end");
413        Ok(Self {
414            disk: RefCell::new(disk),
415            options,
416            fat_type,
417            bpb,
418            first_data_sector,
419            root_dir_sectors,
420            total_clusters,
421            fs_info: RefCell::new(fs_info),
422            current_status_flags: Cell::new(status_flags),
423        })
424    }
425
426    /// Returns a type of File Allocation Table (FAT) used by this filesystem.
427    pub fn fat_type(&self) -> FatType {
428        self.fat_type
429    }
430
431    /// Returns a volume identifier read from BPB in the Boot Sector.
432    pub fn volume_id(&self) -> u32 {
433        self.bpb.volume_id
434    }
435
436    /// Returns the bytes per sector.
437    pub fn bytes_per_sector(&self) -> u16 {
438        self.bpb.bytes_per_sector
439    }
440
441    /// Returns a volume label from BPB in the Boot Sector as byte array slice.
442    ///
443    /// Label is encoded in the OEM codepage.
444    /// Note: This function returns label stored in the BPB block. Use `read_volume_label_from_root_dir_as_bytes` to
445    /// read label from the root directory.
446    pub fn volume_label_as_bytes(&self) -> &[u8] {
447        let full_label_slice = &self.bpb.volume_label;
448        let len = full_label_slice
449            .iter()
450            .rposition(|b| *b != SFN_PADDING)
451            .map_or(0, |p| p + 1);
452        &full_label_slice[..len]
453    }
454
455    fn offset_from_sector(&self, sector: u32) -> u64 {
456        self.bpb.bytes_from_sectors(sector)
457    }
458
459    fn sector_from_cluster(&self, cluster: u32) -> u32 {
460        self.first_data_sector + self.bpb.sectors_from_clusters(cluster - RESERVED_FAT_ENTRIES)
461    }
462
463    pub fn cluster_size(&self) -> u32 {
464        self.bpb.cluster_size()
465    }
466
467    pub(crate) fn offset_from_cluster(&self, cluster: u32) -> u64 {
468        self.offset_from_sector(self.sector_from_cluster(cluster))
469    }
470
471    pub(crate) fn bytes_from_clusters(&self, clusters: u32) -> u64 {
472        self.bpb.bytes_from_sectors(self.bpb.sectors_from_clusters(clusters))
473    }
474
475    pub(crate) fn clusters_from_bytes(&self, bytes: u64) -> u32 {
476        self.bpb.clusters_from_bytes(bytes)
477    }
478
479    fn fat_slice(&self) -> impl ReadWriteSeek<Error = Error<IO::Error>> + '_ {
480        let io = FsIoAdapter { fs: self };
481        fat_slice(io, &self.bpb)
482    }
483
484    pub(crate) fn cluster_iter(
485        &self,
486        cluster: u32,
487    ) -> ClusterIterator<impl ReadWriteSeek<Error = Error<IO::Error>> + '_, IO::Error> {
488        let disk_slice = self.fat_slice();
489        ClusterIterator::new(disk_slice, self.fat_type, cluster)
490    }
491
492    pub(crate) fn truncate_cluster_chain(&self, cluster: u32) -> Result<(), Error<IO::Error>> {
493        let mut iter = self.cluster_iter(cluster);
494        let num_free = iter.truncate()?;
495        let mut fs_info = self.fs_info.borrow_mut();
496        fs_info.map_free_clusters(|n| n + num_free);
497        Ok(())
498    }
499
500    pub(crate) fn free_cluster_chain(&self, cluster: u32) -> Result<(), Error<IO::Error>> {
501        let mut iter = self.cluster_iter(cluster);
502        let num_free = iter.free()?;
503        let mut fs_info = self.fs_info.borrow_mut();
504        fs_info.map_free_clusters(|n| n + num_free);
505        Ok(())
506    }
507
508    pub(crate) fn alloc_cluster(&self, prev_cluster: Option<u32>, zero: bool) -> Result<u32, Error<IO::Error>> {
509        trace!("alloc_cluster");
510        let hint = self.fs_info.borrow().next_free_cluster;
511        let cluster = {
512            let mut fat = self.fat_slice();
513            alloc_cluster(&mut fat, self.fat_type, prev_cluster, hint, self.total_clusters)?
514        };
515        if zero {
516            let mut disk = self.disk.borrow_mut();
517            disk.seek(SeekFrom::Start(self.offset_from_cluster(cluster)))?;
518            write_zeros(&mut *disk, u64::from(self.cluster_size()))?;
519        }
520        let mut fs_info = self.fs_info.borrow_mut();
521        fs_info.set_next_free_cluster(cluster + 1);
522        fs_info.map_free_clusters(|n| n - 1);
523        Ok(cluster)
524    }
525
526    /// Returns status flags for this volume.
527    ///
528    /// # Errors
529    ///
530    /// `Error::Io` will be returned if the underlying storage object returned an I/O error.
531    pub fn read_status_flags(&self) -> Result<FsStatusFlags, Error<IO::Error>> {
532        let bpb_status = self.bpb.status_flags();
533        let fat_status = read_fat_flags(&mut self.fat_slice(), self.fat_type)?;
534        Ok(FsStatusFlags {
535            dirty: bpb_status.dirty || fat_status.dirty,
536            io_error: bpb_status.io_error || fat_status.io_error,
537        })
538    }
539
540    /// Returns filesystem statistics like number of total and free clusters.
541    ///
542    /// For FAT32 volumes number of free clusters from the FS Information Sector is returned (may be incorrect).
543    /// For other FAT variants number is computed on the first call to this method and cached for later use.
544    ///
545    /// # Errors
546    ///
547    /// `Error::Io` will be returned if the underlying storage object returned an I/O error.
548    pub fn stats(&self) -> Result<FileSystemStats, Error<IO::Error>> {
549        let free_clusters_option = self.fs_info.borrow().free_cluster_count;
550        let free_clusters = if let Some(n) = free_clusters_option {
551            n
552        } else {
553            self.recalc_free_clusters()?
554        };
555        Ok(FileSystemStats {
556            cluster_size: self.cluster_size(),
557            total_clusters: self.total_clusters,
558            free_clusters,
559        })
560    }
561
562    /// Forces free clusters recalculation.
563    fn recalc_free_clusters(&self) -> Result<u32, Error<IO::Error>> {
564        let mut fat = self.fat_slice();
565        let free_cluster_count = count_free_clusters(&mut fat, self.fat_type, self.total_clusters)?;
566        self.fs_info.borrow_mut().set_free_cluster_count(free_cluster_count);
567        Ok(free_cluster_count)
568    }
569
570    /// Unmounts the filesystem.
571    ///
572    /// Updates the FS Information Sector if needed.
573    ///
574    /// # Errors
575    ///
576    /// `Error::Io` will be returned if the underlying storage object returned an I/O error.
577    pub fn unmount(self) -> Result<(), Error<IO::Error>> {
578        self.unmount_internal()
579    }
580
581    fn unmount_internal(&self) -> Result<(), Error<IO::Error>> {
582        self.flush_fs_info()?;
583        self.set_dirty_flag(false)?;
584        Ok(())
585    }
586
587    fn flush_fs_info(&self) -> Result<(), Error<IO::Error>> {
588        let mut fs_info = self.fs_info.borrow_mut();
589        if self.fat_type == FatType::Fat32 && fs_info.dirty {
590            let mut disk = self.disk.borrow_mut();
591            let fs_info_sector_offset = self.offset_from_sector(u32::from(self.bpb.fs_info_sector));
592            disk.seek(SeekFrom::Start(fs_info_sector_offset))?;
593            fs_info.serialize(&mut *disk)?;
594            fs_info.dirty = false;
595        }
596        Ok(())
597    }
598
599    pub(crate) fn set_dirty_flag(&self, dirty: bool) -> Result<(), IO::Error> {
600        // Do not overwrite flags read from BPB on mount
601        let mut flags = self.bpb.status_flags();
602        flags.dirty |= dirty;
603        // Check if flags has changed
604        let current_flags = self.current_status_flags.get();
605        if flags == current_flags {
606            // Nothing to do
607            return Ok(());
608        }
609        let encoded = flags.encode();
610        // Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous
611        // Compute reserver_1 field offset and write new flags
612        let offset = if self.fat_type() == FatType::Fat32 {
613            0x041
614        } else {
615            0x025
616        };
617        let mut disk = self.disk.borrow_mut();
618        disk.seek(io::SeekFrom::Start(offset))?;
619        disk.write_u8(encoded)?;
620        self.current_status_flags.set(flags);
621        Ok(())
622    }
623
624    /// Returns a root directory object allowing for futher penetration of a filesystem structure.
625    pub fn root_dir(&self) -> Dir<'_, IO, TP, OCC> {
626        trace!("root_dir");
627        let root_rdr = {
628            match self.fat_type {
629                FatType::Fat12 | FatType::Fat16 => DirRawStream::Root(DiskSlice::from_sectors(
630                    self.first_data_sector - self.root_dir_sectors,
631                    self.root_dir_sectors,
632                    1,
633                    &self.bpb,
634                    FsIoAdapter { fs: self },
635                )),
636                FatType::Fat32 => DirRawStream::File(File::new(Some(self.bpb.root_dir_first_cluster), None, self)),
637            }
638        };
639        Dir::new(root_rdr, self)
640    }
641}
642
643impl<IO: ReadWriteSeek, TP, OCC: OemCpConverter> FileSystem<IO, TP, OCC> {
644    /// Returns a volume label from BPB in the Boot Sector as `String`.
645    ///
646    /// Non-ASCII characters are replaced by the replacement character (U+FFFD).
647    /// Note: This function returns label stored in the BPB block. Use `read_volume_label_from_root_dir` to read label
648    /// from the root directory.
649    #[cfg(feature = "alloc")]
650    pub fn volume_label(&self) -> String {
651        // Decode volume label from OEM codepage
652        let volume_label_iter = self.volume_label_as_bytes().iter().copied();
653        let char_iter = volume_label_iter.map(|c| self.options.oem_cp_converter.decode(c));
654        // Build string from character iterator
655        char_iter.collect()
656    }
657}
658
659impl<IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> FileSystem<IO, TP, OCC> {
660    /// Returns a volume label from root directory as `String`.
661    ///
662    /// It finds file with `VOLUME_ID` attribute and returns its short name.
663    ///
664    /// # Errors
665    ///
666    /// `Error::Io` will be returned if the underlying storage object returned an I/O error.
667    #[cfg(feature = "alloc")]
668    pub fn read_volume_label_from_root_dir(&self) -> Result<Option<String>, Error<IO::Error>> {
669        // Note: DirEntry::file_short_name() cannot be used because it interprets name as 8.3
670        // (adds dot before an extension)
671        let volume_label_opt = self.read_volume_label_from_root_dir_as_bytes()?;
672        volume_label_opt.map_or(Ok(None), |volume_label| {
673            // Strip label padding
674            let len = volume_label
675                .iter()
676                .rposition(|b| *b != SFN_PADDING)
677                .map_or(0, |p| p + 1);
678            let label_slice = &volume_label[..len];
679            // Decode volume label from OEM codepage
680            let volume_label_iter = label_slice.iter().copied();
681            let char_iter = volume_label_iter.map(|c| self.options.oem_cp_converter.decode(c));
682            // Build string from character iterator
683            Ok(Some(char_iter.collect::<String>()))
684        })
685    }
686
687    /// Returns a volume label from root directory as byte array.
688    ///
689    /// Label is encoded in the OEM codepage.
690    /// It finds file with `VOLUME_ID` attribute and returns its short name.
691    ///
692    /// # Errors
693    ///
694    /// `Error::Io` will be returned if the underlying storage object returned an I/O error.
695    pub fn read_volume_label_from_root_dir_as_bytes(&self) -> Result<Option<[u8; SFN_SIZE]>, Error<IO::Error>> {
696        let entry_opt = self.root_dir().find_volume_entry()?;
697        Ok(entry_opt.map(|e| *e.raw_short_name()))
698    }
699}
700
701/// `Drop` implementation tries to unmount the filesystem when dropping.
702impl<IO: ReadWriteSeek, TP, OCC> Drop for FileSystem<IO, TP, OCC> {
703    fn drop(&mut self) {
704        if let Err(err) = self.unmount_internal() {
705            error!("unmount failed {:?}", err);
706        }
707    }
708}
709
710pub(crate) struct FsIoAdapter<'a, IO: ReadWriteSeek, TP, OCC> {
711    fs: &'a FileSystem<IO, TP, OCC>,
712}
713
714impl<IO: ReadWriteSeek, TP, OCC> IoBase for FsIoAdapter<'_, IO, TP, OCC> {
715    type Error = IO::Error;
716}
717
718impl<IO: ReadWriteSeek, TP, OCC> Read for FsIoAdapter<'_, IO, TP, OCC> {
719    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
720        self.fs.disk.borrow_mut().read(buf)
721    }
722}
723
724impl<IO: ReadWriteSeek, TP, OCC> Write for FsIoAdapter<'_, IO, TP, OCC> {
725    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
726        let size = self.fs.disk.borrow_mut().write(buf)?;
727        if size > 0 {
728            self.fs.set_dirty_flag(true)?;
729        }
730        Ok(size)
731    }
732
733    fn flush(&mut self) -> Result<(), Self::Error> {
734        self.fs.disk.borrow_mut().flush()
735    }
736}
737
738impl<IO: ReadWriteSeek, TP, OCC> Seek for FsIoAdapter<'_, IO, TP, OCC> {
739    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
740        self.fs.disk.borrow_mut().seek(pos)
741    }
742}
743
744// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925
745impl<IO: ReadWriteSeek, TP, OCC> Clone for FsIoAdapter<'_, IO, TP, OCC> {
746    fn clone(&self) -> Self {
747        FsIoAdapter { fs: self.fs }
748    }
749}
750
751fn fat_slice<S: ReadWriteSeek, B: BorrowMut<S>>(
752    io: B,
753    bpb: &BiosParameterBlock,
754) -> impl ReadWriteSeek<Error = Error<S::Error>> {
755    let sectors_per_fat = bpb.sectors_per_fat();
756    let mirroring_enabled = bpb.mirroring_enabled();
757    let (fat_first_sector, mirrors) = if mirroring_enabled {
758        (bpb.reserved_sectors(), bpb.fats)
759    } else {
760        let active_fat = u32::from(bpb.active_fat());
761        let fat_first_sector = (bpb.reserved_sectors()) + active_fat * sectors_per_fat;
762        (fat_first_sector, 1)
763    };
764    DiskSlice::from_sectors(fat_first_sector, sectors_per_fat, mirrors, bpb, io)
765}
766
767pub(crate) struct DiskSlice<B, S = B> {
768    begin: u64,
769    size: u64,
770    offset: u64,
771    mirrors: u8,
772    inner: B,
773    phantom: PhantomData<S>,
774}
775
776impl<B: BorrowMut<S>, S: ReadWriteSeek> DiskSlice<B, S> {
777    pub(crate) fn new(begin: u64, size: u64, mirrors: u8, inner: B) -> Self {
778        Self {
779            begin,
780            size,
781            mirrors,
782            inner,
783            offset: 0,
784            phantom: PhantomData,
785        }
786    }
787
788    fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, bpb: &BiosParameterBlock, inner: B) -> Self {
789        Self::new(
790            bpb.bytes_from_sectors(first_sector),
791            bpb.bytes_from_sectors(sector_count),
792            mirrors,
793            inner,
794        )
795    }
796
797    pub(crate) fn abs_pos(&self) -> u64 {
798        self.begin + self.offset
799    }
800}
801
802// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925
803impl<B: Clone, S> Clone for DiskSlice<B, S> {
804    fn clone(&self) -> Self {
805        Self {
806            begin: self.begin,
807            size: self.size,
808            offset: self.offset,
809            mirrors: self.mirrors,
810            inner: self.inner.clone(),
811            // phantom is needed to add type bounds on the storage type
812            phantom: PhantomData,
813        }
814    }
815}
816
817impl<B, S: IoBase> IoBase for DiskSlice<B, S> {
818    type Error = Error<S::Error>;
819}
820
821impl<B: BorrowMut<S>, S: Read + Seek> Read for DiskSlice<B, S> {
822    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
823        let offset = self.begin + self.offset;
824        let read_size = (buf.len() as u64).min(self.size - self.offset) as usize;
825        self.inner.borrow_mut().seek(SeekFrom::Start(offset))?;
826        let size = self.inner.borrow_mut().read(&mut buf[..read_size])?;
827        self.offset += size as u64;
828        Ok(size)
829    }
830}
831
832impl<B: BorrowMut<S>, S: Write + Seek> Write for DiskSlice<B, S> {
833    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
834        let offset = self.begin + self.offset;
835        let write_size = (buf.len() as u64).min(self.size - self.offset) as usize;
836        if write_size == 0 {
837            return Ok(0);
838        }
839        // Write data
840        let storage = self.inner.borrow_mut();
841        for i in 0..self.mirrors {
842            storage.seek(SeekFrom::Start(offset + u64::from(i) * self.size))?;
843            storage.write_all(&buf[..write_size])?;
844        }
845        self.offset += write_size as u64;
846        Ok(write_size)
847    }
848
849    fn flush(&mut self) -> Result<(), Self::Error> {
850        Ok(self.inner.borrow_mut().flush()?)
851    }
852}
853
854impl<B, S: IoBase> Seek for DiskSlice<B, S> {
855    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
856        let new_offset_opt: Option<u64> = match pos {
857            SeekFrom::Current(x) => i64::try_from(self.offset)
858                .ok()
859                .and_then(|n| n.checked_add(x))
860                .and_then(|n| u64::try_from(n).ok()),
861            SeekFrom::Start(x) => Some(x),
862            SeekFrom::End(o) => i64::try_from(self.size)
863                .ok()
864                .and_then(|size| size.checked_add(o))
865                .and_then(|n| u64::try_from(n).ok()),
866        };
867        if let Some(new_offset) = new_offset_opt {
868            if new_offset > self.size {
869                error!("Seek beyond the end of the file");
870                Err(Error::InvalidInput)
871            } else {
872                self.offset = new_offset;
873                Ok(self.offset)
874            }
875        } else {
876            error!("Invalid seek offset");
877            Err(Error::InvalidInput)
878        }
879    }
880}
881
882/// An OEM code page encoder/decoder.
883///
884/// Provides a custom implementation for a short name encoding/decoding.
885/// `OemCpConverter` is specified by the `oem_cp_converter` property in `FsOptions` struct.
886pub trait OemCpConverter: Debug {
887    fn decode(&self, oem_char: u8) -> char;
888    fn encode(&self, uni_char: char) -> Option<u8>;
889}
890
891/// Default implementation of `OemCpConverter` that changes all non-ASCII characters to the replacement character (U+FFFD).
892#[derive(Debug, Clone, Copy, Default)]
893pub struct LossyOemCpConverter {
894    _dummy: (),
895}
896
897impl LossyOemCpConverter {
898    #[must_use]
899    pub fn new() -> Self {
900        Self { _dummy: () }
901    }
902}
903
904impl OemCpConverter for LossyOemCpConverter {
905    fn decode(&self, oem_char: u8) -> char {
906        if oem_char <= 0x7F {
907            char::from(oem_char)
908        } else {
909            '\u{FFFD}'
910        }
911    }
912    fn encode(&self, uni_char: char) -> Option<u8> {
913        if uni_char <= '\x7F' {
914            Some(uni_char as u8) // safe cast: value is in range [0, 0x7F]
915        } else {
916            None
917        }
918    }
919}
920
921pub(crate) fn write_zeros<IO: ReadWriteSeek>(disk: &mut IO, mut len: u64) -> Result<(), IO::Error> {
922    const ZEROS: [u8; 512] = [0_u8; 512];
923    while len > 0 {
924        let write_size = len.min(ZEROS.len() as u64) as usize;
925        disk.write_all(&ZEROS[..write_size])?;
926        len -= write_size as u64;
927    }
928    Ok(())
929}
930
931fn write_zeros_until_end_of_sector<IO: ReadWriteSeek>(disk: &mut IO, bytes_per_sector: u16) -> Result<(), IO::Error> {
932    let pos = disk.seek(SeekFrom::Current(0))?;
933    let total_bytes_to_write = u64::from(bytes_per_sector) - (pos % u64::from(bytes_per_sector));
934    if total_bytes_to_write != u64::from(bytes_per_sector) {
935        write_zeros(disk, total_bytes_to_write)?;
936    }
937    Ok(())
938}
939
940/// A FAT filesystem formatting options
941///
942/// This struct implements a builder pattern.
943/// Options are specified as an argument for `format_volume` function.
944#[derive(Debug, Clone)]
945pub struct FormatVolumeOptions {
946    pub(crate) bytes_per_sector: u16,
947    pub(crate) total_sectors: Option<u32>,
948    pub(crate) bytes_per_cluster: Option<u32>,
949    pub(crate) fat_type: Option<FatType>,
950    pub(crate) max_root_dir_entries: u16,
951    pub(crate) fats: u8,
952    pub(crate) media: u8,
953    pub(crate) sectors_per_track: u16,
954    pub(crate) heads: u16,
955    pub(crate) drive_num: Option<u8>,
956    pub(crate) volume_id: u32,
957    pub(crate) volume_label: Option<[u8; SFN_SIZE]>,
958}
959
960impl Default for FormatVolumeOptions {
961    fn default() -> Self {
962        Self {
963            bytes_per_sector: 512,
964            total_sectors: None,
965            bytes_per_cluster: None,
966            fat_type: None,
967            max_root_dir_entries: 512,
968            fats: 2,
969            media: 0xF8,
970            sectors_per_track: 0x20,
971            heads: 0x40,
972            drive_num: None,
973            volume_id: 0x1234_5678,
974            volume_label: None,
975        }
976    }
977}
978
979impl FormatVolumeOptions {
980    /// Create options struct for `format_volume` function
981    ///
982    /// Allows to overwrite many filesystem parameters.
983    /// In normal use-case defaults should suffice.
984    #[must_use]
985    pub fn new() -> Self {
986        Self::default()
987    }
988
989    /// Set size of cluster in bytes (must be dividable by sector size)
990    ///
991    /// Cluster size must be a power of two and be greater or equal to sector size.
992    /// If option is not specified optimal cluster size is selected based on partition size and
993    /// optionally FAT type override (if specified using `fat_type` method).
994    ///
995    /// # Panics
996    ///
997    /// Panics if `bytes_per_cluster` is not a power of two or is lower than `512`.
998    #[must_use]
999    pub fn bytes_per_cluster(mut self, bytes_per_cluster: u32) -> Self {
1000        assert!(
1001            bytes_per_cluster.is_power_of_two() && bytes_per_cluster >= 512,
1002            "Invalid bytes_per_cluster"
1003        );
1004        self.bytes_per_cluster = Some(bytes_per_cluster);
1005        self
1006    }
1007
1008    /// Set File Allocation Table type
1009    ///
1010    /// Option allows to override File Allocation Table (FAT) entry size.
1011    /// It is unrecommended to set this option unless you know what you are doing.
1012    /// Note: FAT type is determined from total number of clusters. Changing this option can cause formatting to fail
1013    /// if the volume cannot be divided into proper number of clusters for selected FAT type.
1014    #[must_use]
1015    pub fn fat_type(mut self, fat_type: FatType) -> Self {
1016        self.fat_type = Some(fat_type);
1017        self
1018    }
1019
1020    /// Set sector size in bytes
1021    ///
1022    /// Sector size must be a power of two and be in range 512 - 4096.
1023    /// Default is `512`.
1024    ///
1025    /// # Panics
1026    ///
1027    /// Panics if `bytes_per_sector` is not a power of two or is lower than `512`.
1028    #[must_use]
1029    pub fn bytes_per_sector(mut self, bytes_per_sector: u16) -> Self {
1030        assert!(
1031            bytes_per_sector.is_power_of_two() && bytes_per_sector >= 512,
1032            "Invalid bytes_per_sector"
1033        );
1034        self.bytes_per_sector = bytes_per_sector;
1035        self
1036    }
1037
1038    /// Set total number of sectors
1039    ///
1040    /// If option is not specified total number of sectors is calculated as storage device size divided by sector size.
1041    #[must_use]
1042    pub fn total_sectors(mut self, total_sectors: u32) -> Self {
1043        self.total_sectors = Some(total_sectors);
1044        self
1045    }
1046
1047    /// Set maximal numer of entries in root directory for FAT12/FAT16 volumes
1048    ///
1049    /// Total root directory size should be dividable by sectors size so keep it a multiple of 16 (for default sector
1050    /// size).
1051    /// Note: this limit is not used on FAT32 volumes.
1052    /// Default is `512`.
1053    #[must_use]
1054    pub fn max_root_dir_entries(mut self, max_root_dir_entries: u16) -> Self {
1055        self.max_root_dir_entries = max_root_dir_entries;
1056        self
1057    }
1058
1059    /// Set number of File Allocation Tables
1060    ///
1061    /// The only allowed values are `1` and `2`. If value `2` is used the FAT is mirrored.
1062    /// Default is `2`.
1063    ///
1064    /// # Panics
1065    ///
1066    /// Panics if `fats` is outside of the range [1, 2].
1067    #[must_use]
1068    pub fn fats(mut self, fats: u8) -> Self {
1069        assert!((1..=2).contains(&fats), "Invalid number of FATs");
1070        self.fats = fats;
1071        self
1072    }
1073
1074    /// Set media field for Bios Parameters Block
1075    ///
1076    /// Default is `0xF8`.
1077    #[must_use]
1078    pub fn media(mut self, media: u8) -> Self {
1079        self.media = media;
1080        self
1081    }
1082
1083    /// Set number of physical sectors per track for Bios Parameters Block (INT 13h CHS geometry)
1084    ///
1085    /// Default is `0x20`.
1086    #[must_use]
1087    pub fn sectors_per_track(mut self, sectors_per_track: u16) -> Self {
1088        self.sectors_per_track = sectors_per_track;
1089        self
1090    }
1091
1092    /// Set number of heads for Bios Parameters Block (INT 13h CHS geometry)
1093    ///
1094    /// Default is `0x40`.
1095    #[must_use]
1096    pub fn heads(mut self, heads: u16) -> Self {
1097        self.heads = heads;
1098        self
1099    }
1100
1101    /// Set drive number for Bios Parameters Block
1102    ///
1103    /// Default is `0` for FAT12, `0x80` for FAT16/FAT32.
1104    #[must_use]
1105    pub fn drive_num(mut self, drive_num: u8) -> Self {
1106        self.drive_num = Some(drive_num);
1107        self
1108    }
1109
1110    /// Set volume ID for Bios Parameters Block
1111    ///
1112    /// Default is `0x12345678`.
1113    #[must_use]
1114    pub fn volume_id(mut self, volume_id: u32) -> Self {
1115        self.volume_id = volume_id;
1116        self
1117    }
1118
1119    /// Set volume label
1120    ///
1121    /// Default is empty label.
1122    #[must_use]
1123    pub fn volume_label(mut self, volume_label: [u8; SFN_SIZE]) -> Self {
1124        self.volume_label = Some(volume_label);
1125        self
1126    }
1127}
1128
1129/// Create FAT filesystem on a disk or partition (format a volume)
1130///
1131/// Warning: this function overrides internal FAT filesystem structures and causes a loss of all data on provided
1132/// partition. Please use it with caution.
1133/// Only quick formatting is supported. To achieve a full format zero entire partition before calling this function.
1134/// Supplied `storage` parameter cannot be seeked (internal pointer must be on position 0).
1135/// To format a fragment of a disk image (e.g. partition) library user should wrap the file struct in a struct
1136/// limiting access to partition bytes only e.g. `fscommon::StreamSlice`.
1137///
1138/// # Errors
1139///
1140/// Errors that can be returned:
1141///
1142/// * `Error::InvalidInput` will be returned if `options` describes an invalid file system that cannot be created.
1143///   Possible reason can be requesting a fat type that is not compatible with the total number of clusters or
1144///   formatting a too big storage. If sectors/clusters related options in `options` structure were left set to
1145///   defaults this error is very unlikely to happen.
1146/// * `Error::Io` will be returned if the provided storage object returned an I/O error.
1147///
1148/// # Panics
1149///
1150/// Panics in non-optimized build if `storage` position returned by `seek` is not zero.
1151#[allow(clippy::needless_pass_by_value)]
1152pub fn format_volume<S: ReadWriteSeek>(storage: &mut S, options: FormatVolumeOptions) -> Result<(), Error<S::Error>> {
1153    trace!("format_volume");
1154    debug_assert!(storage.seek(SeekFrom::Current(0))? == 0);
1155
1156    let total_sectors = if let Some(total_sectors) = options.total_sectors {
1157        total_sectors
1158    } else {
1159        let total_bytes: u64 = storage.seek(SeekFrom::End(0))?;
1160        let total_sectors_64 = total_bytes / u64::from(options.bytes_per_sector);
1161        storage.seek(SeekFrom::Start(0))?;
1162        if total_sectors_64 > u64::from(u32::MAX) {
1163            error!("Volume has too many sectors: {}", total_sectors_64);
1164            return Err(Error::InvalidInput);
1165        }
1166        total_sectors_64 as u32 // safe case: possible overflow is handled above
1167    };
1168
1169    // Create boot sector, validate and write to storage device
1170    let (boot, fat_type) = format_boot_sector(&options, total_sectors)?;
1171    if boot.validate::<S::Error>(true).is_err() {
1172        return Err(Error::InvalidInput);
1173    }
1174    boot.serialize(storage)?;
1175    // Make sure entire logical sector is updated (serialize method always writes 512 bytes)
1176    let bytes_per_sector = boot.bpb.bytes_per_sector;
1177    write_zeros_until_end_of_sector(storage, bytes_per_sector)?;
1178
1179    let bpb = &boot.bpb;
1180    if bpb.is_fat32() {
1181        // FSInfo sector
1182        let fs_info_sector = FsInfoSector {
1183            free_cluster_count: None,
1184            next_free_cluster: None,
1185            dirty: false,
1186        };
1187        storage.seek(SeekFrom::Start(bpb.bytes_from_sectors(bpb.fs_info_sector())))?;
1188        fs_info_sector.serialize(storage)?;
1189        write_zeros_until_end_of_sector(storage, bytes_per_sector)?;
1190
1191        // backup boot sector
1192        storage.seek(SeekFrom::Start(bpb.bytes_from_sectors(bpb.backup_boot_sector())))?;
1193        boot.serialize(storage)?;
1194        write_zeros_until_end_of_sector(storage, bytes_per_sector)?;
1195    }
1196
1197    // format File Allocation Table
1198    let reserved_sectors = bpb.reserved_sectors();
1199    let fat_pos = bpb.bytes_from_sectors(reserved_sectors);
1200    let sectors_per_all_fats = bpb.sectors_per_all_fats();
1201    storage.seek(SeekFrom::Start(fat_pos))?;
1202    write_zeros(storage, bpb.bytes_from_sectors(sectors_per_all_fats))?;
1203    {
1204        let mut fat_slice = fat_slice::<S, &mut S>(storage, bpb);
1205        let sectors_per_fat = bpb.sectors_per_fat();
1206        let bytes_per_fat = bpb.bytes_from_sectors(sectors_per_fat);
1207        format_fat(&mut fat_slice, fat_type, bpb.media, bytes_per_fat, bpb.total_clusters())?;
1208    }
1209
1210    // init root directory - zero root directory region for FAT12/16 and alloc first root directory cluster for FAT32
1211    let root_dir_first_sector = reserved_sectors + sectors_per_all_fats;
1212    let root_dir_sectors = bpb.root_dir_sectors();
1213    let root_dir_pos = bpb.bytes_from_sectors(root_dir_first_sector);
1214    storage.seek(SeekFrom::Start(root_dir_pos))?;
1215    write_zeros(storage, bpb.bytes_from_sectors(root_dir_sectors))?;
1216    if fat_type == FatType::Fat32 {
1217        let root_dir_first_cluster = {
1218            let mut fat_slice = fat_slice::<S, &mut S>(storage, bpb);
1219            alloc_cluster(&mut fat_slice, fat_type, None, None, 1)?
1220        };
1221        assert!(root_dir_first_cluster == bpb.root_dir_first_cluster);
1222        let first_data_sector = reserved_sectors + sectors_per_all_fats + root_dir_sectors;
1223        let data_sectors_before_root_dir = bpb.sectors_from_clusters(root_dir_first_cluster - RESERVED_FAT_ENTRIES);
1224        let fat32_root_dir_first_sector = first_data_sector + data_sectors_before_root_dir;
1225        let fat32_root_dir_pos = bpb.bytes_from_sectors(fat32_root_dir_first_sector);
1226        storage.seek(SeekFrom::Start(fat32_root_dir_pos))?;
1227        write_zeros(storage, u64::from(bpb.cluster_size()))?;
1228    }
1229
1230    // Create volume label directory entry if volume label is specified in options
1231    if let Some(volume_label) = options.volume_label {
1232        storage.seek(SeekFrom::Start(root_dir_pos))?;
1233        let volume_entry = DirFileEntryData::new(volume_label, FileAttributes::VOLUME_ID);
1234        volume_entry.serialize(storage)?;
1235    }
1236
1237    storage.seek(SeekFrom::Start(0))?;
1238    trace!("format_volume end");
1239    Ok(())
1240}