exfat/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(any(test, feature = "std")), no_std)]
3
4#[cfg(all(feature = "async", feature = "std", not(any(feature = "smol", feature = "tokio"))))]
5compile_error!("Either smol or tokio must be selected");
6
7extern crate alloc;
8
9#[macro_use]
10extern crate hex_literal;
11extern crate heapless;
12#[macro_use]
13extern crate log;
14
15mod cluster_heap;
16mod endian;
17pub mod error;
18mod fat;
19pub mod file;
20pub(crate) mod fs;
21pub mod io;
22mod region;
23pub(crate) mod sync;
24pub mod types;
25mod upcase_table;
26
27use core::fmt::Debug;
28use core::mem;
29
30use memoffset::offset_of;
31
32pub use cluster_heap::directory::{Directory, FileOrDirectory};
33pub use cluster_heap::file::SeekFrom;
34pub use cluster_heap::root::RootDirectory;
35use error::{DataError, Error, ImplementationError};
36use io::IOWrapper;
37pub use region::data::entryset::primary::DateTime;
38use sync::{Shared, shared};
39use types::ClusterID;
40
41pub struct ExFAT<IO> {
42    io: Shared<IOWrapper<IO>>,
43    serial_number: u32,
44    fat_info: fat::Info,
45    fs_info: fs::Info,
46    root: ClusterID,
47}
48
49#[cfg_attr(not(feature = "async"), deasync::deasync)]
50impl<E: Debug, IO: io::IO<Error = E>> ExFAT<IO> {
51    pub async fn new(mut io: IO) -> Result<Self, Error<E>> {
52        let blocks = io.read(0.into()).await.map_err(|e| Error::IO(e))?;
53        let boot_sector: &region::boot::BootSector = unsafe { mem::transmute(&blocks[0]) };
54        if !boot_sector.is_exfat() {
55            return Err(DataError::NotExFAT.into());
56        }
57        if boot_sector.number_of_fats > 1 {
58            return Err(ImplementationError::TexFATNotSupported.into());
59        }
60        let fat_offset = boot_sector.fat_offset.to_ne();
61        let fat_length = boot_sector.fat_length.to_ne();
62        debug!("FAT offset {} length {}", fat_offset, fat_length);
63
64        io.set_sector_size_shift(boot_sector.bytes_per_sector_shift).map_err(|e| Error::IO(e))?;
65        let root = ClusterID::from(boot_sector.first_cluster_of_root_directory.to_ne());
66        debug!("Root directory on cluster {}", root);
67        let sector_size_shift = boot_sector.bytes_per_sector_shift;
68        let fat_info = fat::Info::new(sector_size_shift, fat_offset, fat_length);
69        let fs_info = fs::Info {
70            heap_offset: boot_sector.cluster_heap_offset.to_ne(),
71            sectors_per_cluster_shift: boot_sector.sectors_per_cluster_shift,
72            sector_size_shift,
73        };
74        debug!("Filesystem info: {:?}", fs_info);
75        Ok(Self {
76            io: shared(IOWrapper::new(io)),
77            serial_number: boot_sector.volumn_serial_number.to_ne(),
78            fs_info,
79            fat_info,
80            root,
81        })
82    }
83
84    pub async fn is_dirty(&mut self) -> Result<bool, Error<E>> {
85        let mut io = acquire!(self.io);
86        let blocks = io.read(0.into()).await?;
87        let boot_sector: &region::boot::BootSector = unsafe { mem::transmute(&blocks[0]) };
88        Ok(boot_sector.volume_flags().volume_dirty() > 0)
89    }
90
91    pub async fn percent_inuse(&mut self) -> Result<u8, Error<E>> {
92        let mut io = acquire!(self.io);
93        let blocks = io.read(0.into()).await?;
94        let boot_sector: &region::boot::BootSector = unsafe { mem::transmute(&blocks[0]) };
95        Ok(boot_sector.percent_inuse)
96    }
97
98    pub async fn set_dirty(&mut self, dirty: bool) -> Result<(), Error<E>> {
99        let mut io = acquire!(self.io);
100        let sector = io.read(0.into()).await?;
101        let boot_sector: &region::boot::BootSector = unsafe { mem::transmute(&sector[0]) };
102        let mut volume_flags = boot_sector.volume_flags();
103        volume_flags.set_volume_dirty(dirty as u16);
104        let offset = offset_of!(region::boot::BootSector, volume_flags);
105        let bytes: [u8; 2] = unsafe { mem::transmute(volume_flags) };
106        io.write(0.into(), offset, &bytes).await?;
107        io.flush().await
108    }
109
110    pub async fn validate_checksum(&mut self) -> Result<(), Error<E>> {
111        let mut io = acquire!(self.io);
112        let mut checksum = region::boot::BootChecksum::default();
113        for i in 0..=10 {
114            let sector = io.read(i.into()).await?;
115            for block in sector.iter() {
116                checksum.write(i as usize, block);
117            }
118        }
119        let sector = io.read(11.into()).await?;
120        let array: &[u32; 128] = unsafe { core::mem::transmute(&sector[0]) };
121        if u32::from_le(array[0]) != checksum.sum() {
122            return Err(DataError::BootChecksum.into());
123        }
124        Ok(())
125    }
126
127    pub fn serial_number(&self) -> u32 {
128        self.serial_number
129    }
130
131    pub async fn root_directory(&mut self) -> Result<RootDirectory<E, IO>, Error<E>> {
132        let io = self.io.clone();
133        RootDirectory::new(io, self.fat_info, self.fs_info, self.root).await
134    }
135
136    pub fn try_free(self) -> Result<IO, Self> {
137        let ExFAT { io, serial_number, fat_info, fs_info, root } = self;
138        let io = match () {
139            #[cfg(all(feature = "sync", any(feature = "tokio", feature = "smol")))]
140            () => alloc::sync::Arc::try_unwrap(io).map(|mutex| mutex.into_inner()),
141            #[cfg(all(feature = "sync", feature = "std", not(feature = "async")))]
142            () => alloc::sync::Arc::try_unwrap(io).map(|mutex| mutex.into_inner().unwrap()),
143            #[cfg(not(feature = "sync"))]
144            () => alloc::rc::Rc::try_unwrap(io).map(|cell| cell.into_inner()),
145        };
146        match io {
147            Ok(io) => Ok(io.unwrap()),
148            Err(io) => Err(Self { io, serial_number, fat_info, fs_info, root }),
149        }
150    }
151}