1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#![cfg_attr(not(any(test, feature = "std")), no_std)]
extern crate alloc;
#[macro_use]
extern crate hex_literal;
extern crate heapless;
#[macro_use]
extern crate log;
mod cluster_heap;
mod endian;
pub mod error;
mod fat;
pub mod io;
mod region;
pub(crate) mod sync;
pub mod types;
mod upcase_table;
use core::fmt::Debug;
use core::mem;
use memoffset::offset_of;
use cluster_heap::clusters::SectorRef;
pub use cluster_heap::directory::{Directory, FileOrDirectory};
use cluster_heap::root::RootDirectory as RootDir;
use error::Error;
use io::IOWrapper;
pub struct ExFAT<IO> {
io: IOWrapper<IO>,
serial_number: u32,
sector_size_shift: u8,
fat_info: fat::Info,
sector_ref: SectorRef,
}
#[cfg_attr(not(feature = "async"), deasync::deasync)]
impl<E: Debug, IO: io::IO<Error = E>> ExFAT<IO> {
pub async fn new(mut io: IO) -> Result<Self, Error<E>> {
let blocks = io.read(0.into()).await.map_err(|e| Error::IO(e))?;
let boot_sector: ®ion::boot::BootSector = unsafe { mem::transmute(&blocks[0]) };
if !boot_sector.is_exfat() {
return Err(Error::NotExFAT);
}
if boot_sector.number_of_fats > 1 {
return Err(Error::TexFATNotSupported);
}
let sector_size = boot_sector.bytes_per_sector() as usize;
let fat_offset = boot_sector.fat_offset.to_ne();
let fat_length = boot_sector.fat_length.to_ne();
trace!("FAT offset {} length {}", fat_offset, fat_length);
io.set_sector_size(sector_size).map_err(|e| Error::IO(e))?;
let sector_ref = SectorRef {
heap_offset: boot_sector.cluster_heap_offset.to_ne(),
sectors_per_cluster_shift: boot_sector.sectors_per_cluster_shift,
cluster_id: boot_sector.first_cluster_of_root_directory.to_ne().into(),
offset: 0,
};
let sector_size_shift = boot_sector.bytes_per_sector_shift;
let fat_info = fat::Info::new(sector_size_shift, fat_offset, fat_length);
Ok(Self {
io: IOWrapper::new(io),
serial_number: boot_sector.volumn_serial_number.to_ne(),
sector_size_shift,
fat_info,
sector_ref,
})
}
pub async fn is_dirty(&mut self) -> Result<bool, Error<E>> {
let blocks = self.io.read(0.into()).await?;
let boot_sector: ®ion::boot::BootSector = unsafe { mem::transmute(&blocks[0]) };
Ok(boot_sector.volume_flags().volume_dirty() > 0)
}
pub async fn percent_inuse(&mut self) -> Result<u8, Error<E>> {
let blocks = self.io.read(0.into()).await?;
let boot_sector: ®ion::boot::BootSector = unsafe { mem::transmute(&blocks[0]) };
Ok(boot_sector.percent_inuse)
}
pub async fn set_dirty(&mut self, dirty: bool) -> Result<(), Error<E>> {
let sector = self.io.read(0.into()).await?;
let boot_sector: ®ion::boot::BootSector = unsafe { mem::transmute(§or[0]) };
let mut volume_flags = boot_sector.volume_flags();
volume_flags.set_volume_dirty(dirty as u16);
let offset = offset_of!(region::boot::BootSector, volume_flags);
let bytes: [u8; 2] = unsafe { mem::transmute(volume_flags) };
self.io.write(0.into(), offset, &bytes).await?;
self.io.flush().await
}
pub async fn validate_checksum(&mut self) -> Result<(), Error<E>> {
let mut checksum = region::boot::BootChecksum::default();
for i in 0..=10 {
let sector = self.io.read(i.into()).await?;
for block in sector.iter() {
checksum.write(i as usize, block);
}
}
let sector = self.io.read(11.into()).await?;
let array: &[u32; 128] = unsafe { core::mem::transmute(§or[0]) };
if u32::from_le(array[0]) != checksum.sum() {
return Err(Error::Checksum);
}
Ok(())
}
pub fn serial_number(&self) -> u32 {
self.serial_number
}
pub async fn root_directory(&mut self) -> Result<RootDir<E, IO>, Error<E>> {
let io = self.io.clone();
RootDir::new(io, self.fat_info, self.sector_ref, self.sector_size_shift).await
}
}
unsafe impl<IO: Send> Send for ExFAT<IO> {}