exfat_fs/dir/
mod.rs

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/// Buffer used to read the boot sector.
30#[repr(align(8))]
31struct AlignedBootSector([u8; 512]);
32
33/// Root directory entry.
34pub 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        // create volume label entry
50        let vol_label = DirEntry::VolumeLabel(VolumeLabelEntry::new(volume_label));
51
52        // create volume GUID entry
53        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        // create bitmap entry
60        let bitmap = DirEntry::Bitmap(BitmapEntry::new(bitmap_length_bytes));
61
62        // create upcase table entry
63        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        // convert to native endianess
109        let boot_sector = Arc::new(Endify::from_le(*boot_sector));
110
111        // check for fs name
112        if boot_sector.filesystem_name != *b"EXFAT   " {
113            return Err(RootError::WrongFs);
114        }
115
116        // check for bytes per sector shift
117        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        // check for sectors per cluster shift
124        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        // check for number of fats
131        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        // check for correct active fat
139        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        // parse FAT
146        let fat = Arc::new(Fat::load(&device, &boot_sector)?);
147
148        let first_cluster = boot_sector.first_cluster_of_root_directory;
149        // check for correct index of root cluster
150        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        // Load root directory
163        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            // unused entries are ignored
172            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        // check allocation bitmap count
248        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        // check upcase table
255        if upcase_table.is_none() {
256            return Err(RootError::InvalidNumberOfUpcaseTables);
257        }
258        Ok(Root {
259            volume_label,
260            items,
261        })
262    }
263}