1use alloc::boxed::Box;
2use alloc::sync::Arc;
3use alloc::vec;
4use alloc::vec::Vec;
5
6use crate::{
7 Label,
8 boot_sector::{BootSector, VolumeFlags},
9 disk::ReadOffset,
10 error::RootError,
11 fat::Fat,
12};
13use bytemuck::from_bytes_mut;
14use endify::Endify;
15use entry::{
16 BitmapEntry, ClusterAllocation, DirEntry, UpcaseTableEntry, VOLUME_GUID_ENTRY_TYPE,
17 VolumeGuidEntry, VolumeLabelEntry,
18 fs::{FsElement, directory::Directory, file::File},
19 parsed::ParsedFileEntry,
20};
21use reader::{
22 DirEntryReader,
23 cluster::{ClusterChainOptions, ClusterChainReader},
24};
25
26pub mod entry;
27pub(crate) mod reader;
28
29#[repr(align(8))]
31struct AlignedBootSector([u8; 512]);
32
33pub struct RawRoot {
35 vol_label: DirEntry,
36 vol_guid: DirEntry,
37 bitmap: DirEntry,
38 uptable: DirEntry,
39 items: Vec<DirEntry>,
40}
41
42impl RawRoot {
43 pub(crate) fn new(
44 volume_label: Label,
45 volume_guid: Option<u128>,
46 bitmap_length_bytes: u64,
47 uptable_start_cluster: u32,
48 ) -> RawRoot {
49 let vol_label = DirEntry::VolumeLabel(VolumeLabelEntry::new(volume_label));
51
52 let vol_guid = if let Some(guid) = volume_guid {
54 DirEntry::VolumeGuid(VolumeGuidEntry::new(guid))
55 } else {
56 DirEntry::new_unused(VOLUME_GUID_ENTRY_TYPE)
57 };
58
59 let bitmap = DirEntry::Bitmap(BitmapEntry::new(bitmap_length_bytes));
61
62 let uptable = DirEntry::UpcaseTable(UpcaseTableEntry::new(uptable_start_cluster));
64
65 RawRoot {
66 vol_label,
67 vol_guid,
68 bitmap,
69 uptable,
70 items: Vec::default(),
71 }
72 }
73
74 pub(crate) fn bytes(self) -> Vec<u8> {
75 let mut all_items = vec![self.vol_label, self.vol_guid, self.bitmap, self.uptable];
76 all_items.extend(self.items);
77 all_items
78 .into_iter()
79 .flat_map(|b| b.bytes())
80 .collect::<Vec<u8>>()
81 }
82}
83
84pub struct Root<O: ReadOffset> {
85 volume_label: Option<Label>,
86 items: Vec<FsElement<O>>,
87}
88
89impl<O: ReadOffset> Root<O> {
90 pub fn label(&self) -> Option<&Label> {
91 self.volume_label.as_ref()
92 }
93 pub fn items(&mut self) -> &mut [FsElement<O>] {
94 &mut self.items
95 }
96}
97
98impl<O: ReadOffset> Root<O> {
99 pub fn open(device: O) -> Result<Self, RootError<O>> {
100 let device = Arc::new(device);
101 let mut aligned = Box::new(AlignedBootSector([0u8; 512]));
102 device
103 .read_exact(0, &mut aligned.0[..])
104 .map_err(RootError::Io)?;
105
106 let boot_sector = from_bytes_mut::<BootSector>(&mut aligned.0);
107
108 let boot_sector = Arc::new(Endify::from_le(*boot_sector));
110
111 if boot_sector.filesystem_name != *b"EXFAT " {
113 return Err(RootError::WrongFs);
114 }
115
116 if !(9..=12).contains(&boot_sector.bytes_per_sector_shift) {
118 return Err(RootError::InvalidBytesPerSectorShift(
119 boot_sector.bytes_per_sector_shift,
120 ));
121 }
122
123 if boot_sector.sectors_per_cluster_shift > 25 - boot_sector.bytes_per_sector_shift {
125 return Err(RootError::InvalidSectorsPerClusterShift(
126 boot_sector.sectors_per_cluster_shift,
127 ));
128 }
129
130 let fat_num = if [1, 2].contains(&boot_sector.number_of_fats) {
132 Ok(boot_sector.number_of_fats)
133 } else {
134 Err(RootError::InvalidNumberOfFats(boot_sector.number_of_fats))
135 }?;
136 let volume_flags = VolumeFlags::from_bits_truncate(boot_sector.volume_flags);
137
138 if volume_flags.contains(VolumeFlags::ACTIVE_FAT) && fat_num == 1
140 || !volume_flags.contains(VolumeFlags::ACTIVE_FAT) && fat_num == 2
141 {
142 return Err(RootError::InvalidNumberOfFats(fat_num));
143 }
144
145 let fat = Arc::new(Fat::load(&device, &boot_sector)?);
147
148 let first_cluster = boot_sector.first_cluster_of_root_directory;
149 if first_cluster < 2 || first_cluster > boot_sector.cluster_count + 1 {
151 return Err(RootError::InvalidRootDirectoryClusterIndex(first_cluster));
152 }
153
154 let mut reader = DirEntryReader::from(ClusterChainReader::try_new(
155 Arc::clone(&boot_sector),
156 &fat,
157 first_cluster,
158 ClusterChainOptions::default(),
159 Arc::clone(&device),
160 )?);
161
162 let mut allocation_bitmaps: [Option<BitmapEntry>; 2] = [None, None];
164 let mut upcase_table: Option<UpcaseTableEntry> = None;
165 let mut volume_label: Option<Label> = None;
166 let mut items: Vec<FsElement<O>> = Vec::new();
167
168 loop {
169 let entry = reader.read()?;
170
171 if entry.unused() {
173 continue;
174 }
175
176 if !entry.regular() {
177 break;
178 } else if !entry.primary() {
179 return Err(RootError::RootEntryNotPrimary(entry.entry_type()));
180 }
181
182 match entry {
183 DirEntry::Bitmap(bitmap_entry) => {
184 let index = if allocation_bitmaps[1].is_some() {
185 return Err(RootError::InvalidNumberOfAllocationBitmaps);
186 } else if allocation_bitmaps[0].is_some() {
187 1
188 } else {
189 0
190 };
191 if index != bitmap_entry.index() || !bitmap_entry.valid() {
192 return Err(RootError::InvalidAllocationBitmap);
193 }
194
195 allocation_bitmaps[index as usize] = Some(bitmap_entry);
196 }
197 DirEntry::UpcaseTable(upcase_table_entry) => {
198 if upcase_table.is_some() {
199 return Err(RootError::InvalidNumberOfUpcaseTables);
200 }
201 if !upcase_table_entry.valid() {
202 return Err(RootError::InvalidUpcaseTable);
203 }
204 upcase_table = Some(upcase_table_entry);
205 }
206 DirEntry::VolumeLabel(volume_label_entry) => {
207 if volume_label.is_some() {
208 return Err(RootError::InvalidNumberOfVolumeLabels);
209 }
210 if volume_label_entry.character_count > 11 {
211 return Err(RootError::InvalidVolumeLabel);
212 }
213
214 volume_label = Some(Label(
215 volume_label_entry.volume_label,
216 volume_label_entry.character_count,
217 ));
218 }
219 DirEntry::File(file_entry) => {
220 let parsed = ParsedFileEntry::try_new(&file_entry, &mut reader)?;
221 let item = if file_entry.file_attributes.is_directory() {
222 FsElement::D(Directory::new(
223 Arc::clone(&device),
224 Arc::clone(&boot_sector),
225 Arc::clone(&fat),
226 parsed.name,
227 parsed.stream_extension_entry,
228 parsed.timestamps,
229 ))
230 } else {
231 FsElement::F(File::try_new(
232 &device,
233 &boot_sector,
234 &fat,
235 parsed.name,
236 parsed.stream_extension_entry,
237 parsed.timestamps,
238 )?)
239 };
240
241 items.push(item);
242 }
243 _ => return Err(RootError::UnexpectedRootEntry(entry.entry_type())),
244 }
245 }
246
247 if boot_sector.number_of_fats == 2 && allocation_bitmaps[1].is_none()
249 || allocation_bitmaps[0].is_none()
250 {
251 return Err(RootError::InvalidNumberOfAllocationBitmaps);
252 }
253
254 if upcase_table.is_none() {
256 return Err(RootError::InvalidNumberOfUpcaseTables);
257 }
258 Ok(Root {
259 volume_label,
260 items,
261 })
262 }
263}