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: ®ion::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: ®ion::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: ®ion::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: ®ion::boot::BootSector = unsafe { mem::transmute(§or[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(§or[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}