embedded_sdmmc/
fat.rs

1//! embedded-sdmmc-rs - FAT16/FAT32 file system implementation
2//!
3//! Implements the File Allocation Table file system. Supports FAT16 and FAT32 volumes.
4
5use crate::blockdevice::BlockCount;
6use crate::{
7    Attributes, Block, BlockDevice, BlockIdx, Cluster, Controller, DirEntry, Directory, Error,
8    ShortFileName, TimeSource, Timestamp, VolumeType,
9};
10use byteorder::{ByteOrder, LittleEndian};
11use core::convert::TryFrom;
12use log::{debug, trace, warn};
13
14pub(crate) const RESERVED_ENTRIES: u32 = 2;
15
16/// Indentifies the supported types of FAT format
17#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18pub(crate) enum FatType {
19    /// FAT16 Format
20    Fat16,
21    /// FAT32 Format
22    Fat32,
23}
24
25/// Indentifies the supported types of FAT format
26#[derive(Debug, Eq, PartialEq)]
27pub(crate) enum FatSpecificInfo {
28    /// Fat16 Format
29    Fat16(Fat16Info),
30    /// Fat32 Format
31    Fat32(Fat32Info),
32}
33
34/// FAT32 specific data
35#[derive(Debug, Eq, PartialEq)]
36pub(crate) struct Fat32Info {
37    /// The root directory does not have a reserved area in FAT32. This is the
38    /// cluster it starts in (nominally 2).
39    pub(crate) first_root_dir_cluster: Cluster,
40    /// Block idx of the info sector
41    pub(crate) info_location: BlockIdx,
42}
43
44/// FAT16 specific data
45#[derive(Debug, Eq, PartialEq)]
46pub(crate) struct Fat16Info {
47    /// The block the root directory starts in. Relative to start of partition (so add `self.lba_offset` before passing to controller)
48    pub(crate) first_root_dir_block: BlockCount,
49    /// Number of entries in root directory (it's reserved and not in the FAT)
50    pub(crate) root_entries_count: u16,
51}
52
53#[derive(PartialEq, Eq)]
54pub(crate) struct VolumeName {
55    pub(crate) data: [u8; 11],
56}
57
58/// Identifies a FAT16 Volume on the disk.
59#[derive(PartialEq, Eq, Debug)]
60pub struct FatVolume {
61    /// The block number of the start of the partition. All other BlockIdx values are relative to this.
62    pub(crate) lba_start: BlockIdx,
63    /// The number of blocks in this volume
64    pub(crate) num_blocks: BlockCount,
65    /// The name of this volume
66    pub(crate) name: VolumeName,
67    /// Number of 512 byte blocks (or Blocks) in a cluster
68    pub(crate) blocks_per_cluster: u8,
69    /// The block the data starts in. Relative to start of partition (so add `self.lba_offset` before passing to controller)
70    pub(crate) first_data_block: BlockCount,
71    /// The block the FAT starts in. Relative to start of partition (so add `self.lba_offset` before passing to controller)
72    pub(crate) fat_start: BlockCount,
73    /// Expected number of free clusters
74    pub(crate) free_clusters_count: Option<u32>,
75    /// Number of the next expected free cluster
76    pub(crate) next_free_cluster: Option<Cluster>,
77    /// Total number of clusters
78    pub(crate) cluster_count: u32,
79    /// Type of FAT
80    pub(crate) fat_specific_info: FatSpecificInfo,
81}
82
83impl core::fmt::Debug for VolumeName {
84    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
85        match core::str::from_utf8(&self.data) {
86            Ok(s) => write!(fmt, "{:?}", s),
87            Err(_e) => write!(fmt, "{:?}", &self.data),
88        }
89    }
90}
91
92struct Bpb<'a> {
93    data: &'a [u8; 512],
94    fat_type: FatType,
95    cluster_count: u32,
96}
97
98impl<'a> Bpb<'a> {
99    const FOOTER_VALUE: u16 = 0xAA55;
100
101    pub fn create_from_bytes(data: &[u8; 512]) -> Result<Bpb, &'static str> {
102        let mut bpb = Bpb {
103            data,
104            fat_type: FatType::Fat16,
105            cluster_count: 0,
106        };
107        if bpb.footer() != Self::FOOTER_VALUE {
108            return Err("Bad BPB footer");
109        }
110
111        let root_dir_blocks = ((u32::from(bpb.root_entries_count()) * OnDiskDirEntry::LEN_U32)
112            + (Block::LEN_U32 - 1))
113            / Block::LEN_U32;
114        let data_blocks = bpb.total_blocks()
115            - (u32::from(bpb.reserved_block_count())
116                + (u32::from(bpb.num_fats()) * bpb.fat_size())
117                + root_dir_blocks);
118        bpb.cluster_count = data_blocks / u32::from(bpb.blocks_per_cluster());
119        if bpb.cluster_count < 4085 {
120            return Err("FAT12 is unsupported");
121        } else if bpb.cluster_count < 65525 {
122            bpb.fat_type = FatType::Fat16;
123        } else {
124            bpb.fat_type = FatType::Fat32;
125        }
126
127        match bpb.fat_type {
128            FatType::Fat16 => Ok(bpb),
129            FatType::Fat32 if bpb.fs_ver() == 0 => {
130                // Only support FAT32 version 0.0
131                Ok(bpb)
132            }
133            _ => Err("Invalid FAT format"),
134        }
135    }
136
137    // FAT16/FAT32
138    define_field!(bytes_per_block, u16, 11);
139    define_field!(blocks_per_cluster, u8, 13);
140    define_field!(reserved_block_count, u16, 14);
141    define_field!(num_fats, u8, 16);
142    define_field!(root_entries_count, u16, 17);
143    define_field!(total_blocks16, u16, 19);
144    define_field!(media, u8, 21);
145    define_field!(fat_size16, u16, 22);
146    define_field!(blocks_per_track, u16, 24);
147    define_field!(num_heads, u16, 26);
148    define_field!(hidden_blocks, u32, 28);
149    define_field!(total_blocks32, u32, 32);
150    define_field!(footer, u16, 510);
151
152    // FAT32 only
153    define_field!(fat_size32, u32, 36);
154    define_field!(fs_ver, u16, 42);
155    define_field!(first_root_dir_cluster, u32, 44);
156    define_field!(fs_info, u16, 48);
157    define_field!(backup_boot_block, u16, 50);
158
159    pub fn oem_name(&self) -> &[u8] {
160        &self.data[3..11]
161    }
162
163    // FAT16/FAT32 functions
164
165    pub fn drive_number(&self) -> u8 {
166        if self.fat_type != FatType::Fat32 {
167            self.data[36]
168        } else {
169            self.data[64]
170        }
171    }
172
173    pub fn boot_signature(&self) -> u8 {
174        if self.fat_type != FatType::Fat32 {
175            self.data[38]
176        } else {
177            self.data[66]
178        }
179    }
180
181    pub fn volume_id(&self) -> u32 {
182        if self.fat_type != FatType::Fat32 {
183            LittleEndian::read_u32(&self.data[39..=42])
184        } else {
185            LittleEndian::read_u32(&self.data[67..=70])
186        }
187    }
188
189    pub fn volume_label(&self) -> &[u8] {
190        if self.fat_type != FatType::Fat32 {
191            &self.data[43..=53]
192        } else {
193            &self.data[71..=81]
194        }
195    }
196
197    pub fn fs_type(&self) -> &[u8] {
198        if self.fat_type != FatType::Fat32 {
199            &self.data[54..=61]
200        } else {
201            &self.data[82..=89]
202        }
203    }
204
205    // FAT32 only functions
206
207    pub fn current_fat(&self) -> u8 {
208        self.data[40] & 0x0F
209    }
210
211    pub fn use_specific_fat(&self) -> bool {
212        (self.data[40] & 0x80) != 0x00
213    }
214
215    pub fn root_cluster(&self) -> Cluster {
216        Cluster(LittleEndian::read_u32(&self.data[44..=47]))
217    }
218
219    pub fn fs_info_block(&self) -> Option<BlockCount> {
220        if self.fat_type != FatType::Fat32 {
221            None
222        } else {
223            Some(BlockCount(u32::from(self.fs_info())))
224        }
225    }
226
227    // Magic functions that get the right FAT16/FAT32 result
228
229    pub fn fat_size(&self) -> u32 {
230        let result = u32::from(self.fat_size16());
231        if result != 0 {
232            result
233        } else {
234            self.fat_size32()
235        }
236    }
237
238    pub fn total_blocks(&self) -> u32 {
239        let result = u32::from(self.total_blocks16());
240        if result != 0 {
241            result
242        } else {
243            self.total_blocks32()
244        }
245    }
246
247    pub fn total_clusters(&self) -> u32 {
248        self.cluster_count
249    }
250}
251
252/// File System Information structure is only present on FAT32 partitions. It may contain a valid
253/// number of free clusters and the number of the next free cluster.
254/// The information contained in the structure must be considered as advisory only.
255/// File system driver implementations are not required to ensure that information within the
256/// structure is kept consistent.
257struct InfoSector<'a> {
258    data: &'a [u8; 512],
259}
260
261impl<'a> InfoSector<'a> {
262    const LEAD_SIG: u32 = 0x4161_5252;
263    const STRUC_SIG: u32 = 0x6141_7272;
264    const TRAIL_SIG: u32 = 0xAA55_0000;
265
266    fn create_from_bytes(data: &[u8; 512]) -> Result<InfoSector, &'static str> {
267        let info = InfoSector { data };
268        if info.lead_sig() != Self::LEAD_SIG {
269            return Err("Bad lead signature on InfoSector");
270        }
271        if info.struc_sig() != Self::STRUC_SIG {
272            return Err("Bad struc signature on InfoSector");
273        }
274        if info.trail_sig() != Self::TRAIL_SIG {
275            return Err("Bad trail signature on InfoSector");
276        }
277        Ok(info)
278    }
279
280    define_field!(lead_sig, u32, 0);
281    define_field!(struc_sig, u32, 484);
282    define_field!(free_count, u32, 488);
283    define_field!(next_free, u32, 492);
284    define_field!(trail_sig, u32, 508);
285
286    pub fn free_clusters_count(&self) -> Option<u32> {
287        match self.free_count() {
288            0xFFFF_FFFF => None,
289            n => Some(n),
290        }
291    }
292
293    pub fn next_free_cluster(&self) -> Option<Cluster> {
294        match self.next_free() {
295            // 0 and 1 are reserved clusters
296            0xFFFF_FFFF | 0 | 1 => None,
297            n => Some(Cluster(n)),
298        }
299    }
300}
301
302pub(crate) struct OnDiskDirEntry<'a> {
303    data: &'a [u8],
304}
305
306impl<'a> core::fmt::Debug for OnDiskDirEntry<'a> {
307    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
308        write!(f, "OnDiskDirEntry<")?;
309        write!(f, "raw_attr = {}", self.raw_attr())?;
310        write!(f, ", create_time = {}", self.create_time())?;
311        write!(f, ", create_date = {}", self.create_date())?;
312        write!(f, ", last_access_data = {}", self.last_access_data())?;
313        write!(f, ", first_cluster_hi = {}", self.first_cluster_hi())?;
314        write!(f, ", write_time = {}", self.write_time())?;
315        write!(f, ", write_date = {}", self.write_date())?;
316        write!(f, ", first_cluster_lo = {}", self.first_cluster_lo())?;
317        write!(f, ", file_size = {}", self.file_size())?;
318        write!(f, ", is_end = {}", self.is_end())?;
319        write!(f, ", is_valid = {}", self.is_valid())?;
320        write!(f, ", is_lfn = {}", self.is_lfn())?;
321        write!(
322            f,
323            ", first_cluster_fat32 = {:?}",
324            self.first_cluster_fat32()
325        )?;
326        write!(
327            f,
328            ", first_cluster_fat16 = {:?}",
329            self.first_cluster_fat16()
330        )?;
331        write!(f, ">")?;
332        Ok(())
333    }
334}
335
336/// Represents the 32 byte directory entry. This is the same for FAT16 and
337/// FAT32 (except FAT16 doesn't use first_cluster_hi).
338impl<'a> OnDiskDirEntry<'a> {
339    pub(crate) const LEN: usize = 32;
340    pub(crate) const LEN_U32: u32 = 32;
341    const LFN_FRAGMENT_LEN: usize = 13;
342
343    define_field!(raw_attr, u8, 11);
344    define_field!(create_time, u16, 14);
345    define_field!(create_date, u16, 16);
346    define_field!(last_access_data, u16, 18);
347    define_field!(first_cluster_hi, u16, 20);
348    define_field!(write_time, u16, 22);
349    define_field!(write_date, u16, 24);
350    define_field!(first_cluster_lo, u16, 26);
351    define_field!(file_size, u32, 28);
352
353    fn new(data: &[u8]) -> OnDiskDirEntry {
354        OnDiskDirEntry { data }
355    }
356
357    fn is_end(&self) -> bool {
358        self.data[0] == 0x00
359    }
360
361    fn is_valid(&self) -> bool {
362        !self.is_end() && (self.data[0] != 0xE5)
363    }
364
365    fn is_lfn(&self) -> bool {
366        let attributes = Attributes::create_from_fat(self.raw_attr());
367        attributes.is_lfn()
368    }
369
370    fn lfn_contents(&self) -> Option<(bool, u8, [char; 13])> {
371        if self.is_lfn() {
372            let mut buffer = [' '; 13];
373            let is_start = (self.data[0] & 0x40) != 0;
374            let sequence = self.data[0] & 0x1F;
375            buffer[0] =
376                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[1..=2]))).unwrap();
377            buffer[1] =
378                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[3..=4]))).unwrap();
379            buffer[2] =
380                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[5..=6]))).unwrap();
381            buffer[3] =
382                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[7..=8]))).unwrap();
383            buffer[4] = core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[9..=10])))
384                .unwrap();
385            buffer[5] =
386                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[14..=15])))
387                    .unwrap();
388            buffer[6] =
389                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[16..=17])))
390                    .unwrap();
391            buffer[7] =
392                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[18..=19])))
393                    .unwrap();
394            buffer[8] =
395                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[20..=21])))
396                    .unwrap();
397            buffer[9] =
398                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[22..=23])))
399                    .unwrap();
400            buffer[10] =
401                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[24..=25])))
402                    .unwrap();
403            buffer[11] =
404                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[28..=29])))
405                    .unwrap();
406            buffer[12] =
407                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[30..=31])))
408                    .unwrap();
409            Some((is_start, sequence, buffer))
410        } else {
411            None
412        }
413    }
414
415    fn matches(&self, sfn: &ShortFileName) -> bool {
416        self.data[0..11] == sfn.contents
417    }
418
419    fn first_cluster_fat32(&self) -> Cluster {
420        let cluster_no =
421            (u32::from(self.first_cluster_hi()) << 16) | u32::from(self.first_cluster_lo());
422        Cluster(cluster_no)
423    }
424
425    fn first_cluster_fat16(&self) -> Cluster {
426        let cluster_no = u32::from(self.first_cluster_lo());
427        Cluster(cluster_no)
428    }
429
430    fn get_entry(&self, fat_type: FatType, entry_block: BlockIdx, entry_offset: u32) -> DirEntry {
431        let mut result = DirEntry {
432            name: ShortFileName {
433                contents: [0u8; 11],
434            },
435            mtime: Timestamp::from_fat(self.write_date(), self.write_time()),
436            ctime: Timestamp::from_fat(self.create_date(), self.create_time()),
437            attributes: Attributes::create_from_fat(self.raw_attr()),
438            cluster: if fat_type == FatType::Fat32 {
439                self.first_cluster_fat32()
440            } else {
441                self.first_cluster_fat16()
442            },
443            size: self.file_size(),
444            entry_block,
445            entry_offset,
446        };
447        result.name.contents.copy_from_slice(&self.data[0..11]);
448        result
449    }
450}
451
452impl FatVolume {
453    /// Write a new entry in the FAT
454    pub fn update_info_sector<D, T>(
455        &mut self,
456        controller: &mut Controller<D, T>,
457    ) -> Result<(), Error<D::Error>>
458    where
459        D: BlockDevice,
460        T: TimeSource,
461    {
462        match &self.fat_specific_info {
463            FatSpecificInfo::Fat16(_) => {}
464            FatSpecificInfo::Fat32(fat32_info) => {
465                if self.free_clusters_count.is_none() && self.next_free_cluster.is_none() {
466                    return Ok(());
467                }
468                let mut blocks = [Block::new()];
469                controller
470                    .block_device
471                    .read(&mut blocks, fat32_info.info_location, "read_info_sector")
472                    .map_err(Error::DeviceError)?;
473                let block = &mut blocks[0];
474                if let Some(count) = self.free_clusters_count {
475                    block[488..492].copy_from_slice(&count.to_le_bytes());
476                }
477                if let Some(next_free_cluster) = self.next_free_cluster {
478                    block[492..496].copy_from_slice(&next_free_cluster.0.to_le_bytes());
479                }
480                controller
481                    .block_device
482                    .write(&blocks, fat32_info.info_location)
483                    .map_err(Error::DeviceError)?;
484            }
485        }
486        Ok(())
487    }
488
489    /// Get the type of FAT this volume is
490    pub(crate) fn get_fat_type(&self) -> FatType {
491        match &self.fat_specific_info {
492            FatSpecificInfo::Fat16(_) => FatType::Fat16,
493            FatSpecificInfo::Fat32(_) => FatType::Fat32,
494        }
495    }
496
497    /// Get an entry from the FAT
498    fn get_fat<D, T>(
499        &self,
500        controller: &mut Controller<D, T>,
501        cluster: Cluster,
502    ) -> Result<Cluster, Error<D::Error>>
503    where
504        D: BlockDevice,
505        T: TimeSource,
506    {
507        let mut blocks = [Block::new()];
508        let entry = match &self.fat_specific_info {
509            FatSpecificInfo::Fat16(_fat16_info) => {
510                let fat_offset = cluster.0 * 2;
511                let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset);
512                let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize;
513                controller
514                    .block_device
515                    .read(&mut blocks, this_fat_block_num, "read_fat")
516                    .map_err(Error::DeviceError)?;
517                u32::from(LittleEndian::read_u16(
518                    &blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 1],
519                ))
520            }
521            FatSpecificInfo::Fat32(_fat32_info) => {
522                // FAT32 => 4 bytes per entry
523                let fat_offset = cluster.0 as u32 * 4;
524                let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset);
525                let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize;
526                controller
527                    .block_device
528                    .read(&mut blocks, this_fat_block_num, "read_fat")
529                    .map_err(Error::DeviceError)?;
530                LittleEndian::read_u32(&blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 3])
531                    & 0x0FFF_FFFF
532            }
533        };
534        Ok(Cluster(entry))
535    }
536
537    /// Write a new entry in the FAT
538    fn update_fat<D, T>(
539        &mut self,
540        controller: &mut Controller<D, T>,
541        cluster: Cluster,
542        new_value: Cluster,
543    ) -> Result<(), Error<D::Error>>
544    where
545        D: BlockDevice,
546        T: TimeSource,
547    {
548        let mut blocks = [Block::new()];
549        let this_fat_block_num;
550        match &self.fat_specific_info {
551            FatSpecificInfo::Fat16(_fat16_info) => {
552                let fat_offset = cluster.0 * 2;
553                this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset);
554                let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize;
555                controller
556                    .block_device
557                    .read(&mut blocks, this_fat_block_num, "read_fat")
558                    .map_err(Error::DeviceError)?;
559                let entry = match new_value {
560                    Cluster::INVALID => 0xFFF6,
561                    Cluster::BAD => 0xFFF7,
562                    Cluster::EMPTY => 0x0000,
563                    Cluster::END_OF_FILE => 0xFFFF,
564                    _ => new_value.0 as u16,
565                };
566                LittleEndian::write_u16(
567                    &mut blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 1],
568                    entry,
569                );
570            }
571            FatSpecificInfo::Fat32(_fat32_info) => {
572                // FAT32 => 4 bytes per entry
573                let fat_offset = cluster.0 as u32 * 4;
574                this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset);
575                let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize;
576                controller
577                    .block_device
578                    .read(&mut blocks, this_fat_block_num, "read_fat")
579                    .map_err(Error::DeviceError)?;
580                let entry = match new_value {
581                    Cluster::INVALID => 0x0FFF_FFF6,
582                    Cluster::BAD => 0x0FFF_FFF7,
583                    Cluster::EMPTY => 0x0000_0000,
584                    _ => new_value.0,
585                };
586                let existing = LittleEndian::read_u32(
587                    &blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 3],
588                );
589                let new = (existing & 0xF000_0000) | (entry & 0x0FFF_FFFF);
590                LittleEndian::write_u32(
591                    &mut blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 3],
592                    new,
593                );
594            }
595        }
596        controller
597            .block_device
598            .write(&blocks, this_fat_block_num)
599            .map_err(Error::DeviceError)?;
600        Ok(())
601    }
602
603    /// Look in the FAT to see which cluster comes next.
604    pub(crate) fn next_cluster<D, T>(
605        &self,
606        controller: &Controller<D, T>,
607        cluster: Cluster,
608    ) -> Result<Cluster, Error<D::Error>>
609    where
610        D: BlockDevice,
611        T: TimeSource,
612    {
613        let mut blocks = [Block::new()];
614        match &self.fat_specific_info {
615            FatSpecificInfo::Fat16(_fat16_info) => {
616                let fat_offset = cluster.0 * 2;
617                let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset);
618                let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize;
619                controller
620                    .block_device
621                    .read(&mut blocks, this_fat_block_num, "next_cluster")
622                    .map_err(Error::DeviceError)?;
623                let fat_entry = LittleEndian::read_u16(
624                    &blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 1],
625                );
626                match fat_entry {
627                    0xFFF7 => {
628                        // Bad cluster
629                        Err(Error::BadCluster)
630                    }
631                    0xFFF8..=0xFFFF => {
632                        // There is no next cluster
633                        Err(Error::EndOfFile)
634                    }
635                    f => {
636                        // Seems legit
637                        Ok(Cluster(u32::from(f)))
638                    }
639                }
640            }
641            FatSpecificInfo::Fat32(_fat32_info) => {
642                let fat_offset = cluster.0 * 4;
643                let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset);
644                let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize;
645                controller
646                    .block_device
647                    .read(&mut blocks, this_fat_block_num, "next_cluster")
648                    .map_err(Error::DeviceError)?;
649                let fat_entry = LittleEndian::read_u32(
650                    &blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 3],
651                ) & 0x0FFF_FFFF;
652                match fat_entry {
653                    0x0000_0000 => {
654                        // Jumped to free space
655                        Err(Error::JumpedFree)
656                    }
657                    0x0FFF_FFF7 => {
658                        // Bad cluster
659                        Err(Error::BadCluster)
660                    }
661                    0x0000_0001 | 0x0FFF_FFF8..=0x0FFF_FFFF => {
662                        // There is no next cluster
663                        Err(Error::EndOfFile)
664                    }
665                    f => {
666                        // Seems legit
667                        Ok(Cluster(f))
668                    }
669                }
670            }
671        }
672    }
673
674    /// Number of bytes in a cluster.
675    pub(crate) fn bytes_per_cluster(&self) -> u32 {
676        u32::from(self.blocks_per_cluster) * Block::LEN_U32
677    }
678
679    /// Converts a cluster number (or `Cluster`) to a block number (or
680    /// `BlockIdx`). Gives an absolute `BlockIdx` you can pass to the
681    /// controller.
682    pub(crate) fn cluster_to_block(&self, cluster: Cluster) -> BlockIdx {
683        match &self.fat_specific_info {
684            FatSpecificInfo::Fat16(fat16_info) => {
685                let block_num = match cluster {
686                    Cluster::ROOT_DIR => fat16_info.first_root_dir_block,
687                    Cluster(c) => {
688                        // FirstSectorofCluster = ((N – 2) * BPB_SecPerClus) + FirstDataSector;
689                        let first_block_of_cluster =
690                            BlockCount((c - 2) * u32::from(self.blocks_per_cluster));
691                        self.first_data_block + first_block_of_cluster
692                    }
693                };
694                self.lba_start + block_num
695            }
696            FatSpecificInfo::Fat32(fat32_info) => {
697                let cluster_num = match cluster {
698                    Cluster::ROOT_DIR => fat32_info.first_root_dir_cluster.0,
699                    c => c.0,
700                };
701                // FirstSectorofCluster = ((N – 2) * BPB_SecPerClus) + FirstDataSector;
702                let first_block_of_cluster =
703                    BlockCount((cluster_num - 2) * u32::from(self.blocks_per_cluster));
704                self.lba_start + self.first_data_block + first_block_of_cluster
705            }
706        }
707    }
708
709    /// Finds a empty entry space and writes the new entry to it, allocates a new cluster if it's
710    /// needed
711    pub(crate) fn write_new_directory_entry<D, T>(
712        &mut self,
713        controller: &mut Controller<D, T>,
714        dir: &Directory,
715        name: ShortFileName,
716        attributes: Attributes,
717    ) -> Result<(DirEntry), Error<D::Error>>
718    where
719        D: BlockDevice,
720        T: TimeSource,
721    {
722        match &self.fat_specific_info {
723            FatSpecificInfo::Fat16(fat16_info) => {
724                let mut first_dir_block_num = match dir.cluster {
725                    Cluster::ROOT_DIR => self.lba_start + fat16_info.first_root_dir_block,
726                    _ => self.cluster_to_block(dir.cluster),
727                };
728                let mut current_cluster = Some(dir.cluster);
729                let mut blocks = [Block::new()];
730
731                let dir_size = match dir.cluster {
732                    Cluster::ROOT_DIR => BlockCount(
733                        ((u32::from(fat16_info.root_entries_count) * 32) + (Block::LEN as u32 - 1))
734                            / Block::LEN as u32,
735                    ),
736                    _ => BlockCount(u32::from(self.blocks_per_cluster)),
737                };
738                while let Some(cluster) = current_cluster {
739                    for block in first_dir_block_num.range(dir_size) {
740                        controller
741                            .block_device
742                            .read(&mut blocks, block, "read_dir")
743                            .map_err(Error::DeviceError)?;
744                        for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
745                            let start = entry * OnDiskDirEntry::LEN;
746                            let end = (entry + 1) * OnDiskDirEntry::LEN;
747                            let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
748                            // 0x00 or 0xE5 represents a free entry
749                            if !dir_entry.is_valid() {
750                                let ctime = controller.timesource.get_timestamp();
751                                let entry = DirEntry::new(
752                                    name,
753                                    attributes,
754                                    Cluster(0),
755                                    ctime,
756                                    block,
757                                    start as u32,
758                                );
759                                blocks[0][start..start + 32]
760                                    .copy_from_slice(&entry.serialize(FatType::Fat16)[..]);
761                                controller
762                                    .block_device
763                                    .write(&blocks, block)
764                                    .map_err(Error::DeviceError)?;
765                                return Ok(entry);
766                            }
767                        }
768                    }
769                    if cluster != Cluster::ROOT_DIR {
770                        current_cluster = match self.next_cluster(controller, cluster) {
771                            Ok(n) => {
772                                first_dir_block_num = self.cluster_to_block(n);
773                                Some(n)
774                            }
775                            Err(Error::EndOfFile) => {
776                                let c = self.alloc_cluster(controller, Some(cluster), true)?;
777                                first_dir_block_num = self.cluster_to_block(c);
778                                Some(c)
779                            }
780                            _ => None,
781                        };
782                    } else {
783                        current_cluster = None;
784                    }
785                }
786                Err(Error::NotEnoughSpace)
787            }
788            FatSpecificInfo::Fat32(fat32_info) => {
789                let mut first_dir_block_num = match dir.cluster {
790                    Cluster::ROOT_DIR => self.cluster_to_block(fat32_info.first_root_dir_cluster),
791                    _ => self.cluster_to_block(dir.cluster),
792                };
793                let mut current_cluster = Some(dir.cluster);
794                let mut blocks = [Block::new()];
795
796                let dir_size = BlockCount(u32::from(self.blocks_per_cluster));
797                while let Some(cluster) = current_cluster {
798                    for block in first_dir_block_num.range(dir_size) {
799                        controller
800                            .block_device
801                            .read(&mut blocks, block, "read_dir")
802                            .map_err(Error::DeviceError)?;
803                        for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
804                            let start = entry * OnDiskDirEntry::LEN;
805                            let end = (entry + 1) * OnDiskDirEntry::LEN;
806                            let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
807                            // 0x00 or 0xE5 represents a free entry
808                            if !dir_entry.is_valid() {
809                                let ctime = controller.timesource.get_timestamp();
810                                let entry = DirEntry::new(
811                                    name,
812                                    attributes,
813                                    Cluster(0),
814                                    ctime,
815                                    block,
816                                    start as u32,
817                                );
818                                blocks[0][start..start + 32]
819                                    .copy_from_slice(&entry.serialize(FatType::Fat32)[..]);
820                                controller
821                                    .block_device
822                                    .write(&blocks, block)
823                                    .map_err(Error::DeviceError)?;
824                                return Ok(entry);
825                            }
826                        }
827                    }
828                    current_cluster = match self.next_cluster(controller, cluster) {
829                        Ok(n) => {
830                            first_dir_block_num = self.cluster_to_block(n);
831                            Some(n)
832                        }
833                        Err(Error::EndOfFile) => {
834                            let c = self.alloc_cluster(controller, Some(cluster), true)?;
835                            first_dir_block_num = self.cluster_to_block(c);
836                            Some(c)
837                        }
838                        _ => None,
839                    };
840                }
841                Err(Error::NotEnoughSpace)
842            }
843        }
844    }
845
846    /// Calls callback `func` with every valid entry in the given directory.
847    /// Useful for performing directory listings.
848    pub(crate) fn iterate_dir<D, T, F>(
849        &self,
850        controller: &Controller<D, T>,
851        dir: &Directory,
852        mut func: F,
853    ) -> Result<(), Error<D::Error>>
854    where
855        F: FnMut(&DirEntry),
856        D: BlockDevice,
857        T: TimeSource,
858    {
859        match &self.fat_specific_info {
860            FatSpecificInfo::Fat16(fat16_info) => {
861                let mut first_dir_block_num = match dir.cluster {
862                    Cluster::ROOT_DIR => self.lba_start + fat16_info.first_root_dir_block,
863                    _ => self.cluster_to_block(dir.cluster),
864                };
865                let mut current_cluster = Some(dir.cluster);
866                let dir_size = match dir.cluster {
867                    Cluster::ROOT_DIR => BlockCount(
868                        ((u32::from(fat16_info.root_entries_count) * 32) + (Block::LEN as u32 - 1))
869                            / Block::LEN as u32,
870                    ),
871                    _ => BlockCount(u32::from(self.blocks_per_cluster)),
872                };
873                let mut blocks = [Block::new()];
874                while let Some(cluster) = current_cluster {
875                    for block in first_dir_block_num.range(dir_size) {
876                        controller
877                            .block_device
878                            .read(&mut blocks, block, "read_dir")
879                            .map_err(Error::DeviceError)?;
880                        for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
881                            let start = entry * OnDiskDirEntry::LEN;
882                            let end = (entry + 1) * OnDiskDirEntry::LEN;
883                            let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
884                            if dir_entry.is_end() {
885                                // Can quit early
886                                return Ok(());
887                            } else if dir_entry.is_valid() && !dir_entry.is_lfn() {
888                                // Safe, since Block::LEN always fits on a u32
889                                let start = u32::try_from(start).unwrap();
890                                let entry = dir_entry.get_entry(FatType::Fat16, block, start);
891                                func(&entry);
892                            }
893                        }
894                    }
895                    if cluster != Cluster::ROOT_DIR {
896                        current_cluster = match self.next_cluster(controller, cluster) {
897                            Ok(n) => {
898                                first_dir_block_num = self.cluster_to_block(n);
899                                Some(n)
900                            }
901                            _ => None,
902                        };
903                    } else {
904                        current_cluster = None;
905                    }
906                }
907                Ok(())
908            }
909            FatSpecificInfo::Fat32(fat32_info) => {
910                let mut current_cluster = match dir.cluster {
911                    Cluster::ROOT_DIR => Some(fat32_info.first_root_dir_cluster),
912                    _ => Some(dir.cluster),
913                };
914                let mut blocks = [Block::new()];
915                while let Some(cluster) = current_cluster {
916                    let block_idx = self.cluster_to_block(cluster);
917                    for block in block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) {
918                        controller
919                            .block_device
920                            .read(&mut blocks, block, "read_dir")
921                            .map_err(Error::DeviceError)?;
922                        for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
923                            let start = entry * OnDiskDirEntry::LEN;
924                            let end = (entry + 1) * OnDiskDirEntry::LEN;
925                            let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
926                            if dir_entry.is_end() {
927                                // Can quit early
928                                return Ok(());
929                            } else if dir_entry.is_valid() && !dir_entry.is_lfn() {
930                                // Safe, since Block::LEN always fits on a u32
931                                let start = u32::try_from(start).unwrap();
932                                let entry = dir_entry.get_entry(FatType::Fat16, block, start);
933                                func(&entry);
934                            }
935                        }
936                    }
937                    current_cluster = match self.next_cluster(controller, cluster) {
938                        Ok(n) => Some(n),
939                        _ => None,
940                    };
941                }
942                Ok(())
943            }
944        }
945    }
946
947    /// Get an entry from the given directory
948    pub(crate) fn find_directory_entry<D, T>(
949        &self,
950        controller: &mut Controller<D, T>,
951        dir: &Directory,
952        name: &str,
953    ) -> Result<DirEntry, Error<D::Error>>
954    where
955        D: BlockDevice,
956        T: TimeSource,
957    {
958        let match_name = ShortFileName::create_from_str(name).map_err(Error::FilenameError)?;
959        let mut blocks = [Block::new()];
960        match &self.fat_specific_info {
961            FatSpecificInfo::Fat16(fat16_info) => {
962                let mut current_cluster = Some(dir.cluster);
963                let mut first_dir_block_num = match dir.cluster {
964                    Cluster::ROOT_DIR => self.lba_start + fat16_info.first_root_dir_block,
965                    _ => self.cluster_to_block(dir.cluster),
966                };
967                let dir_size = match dir.cluster {
968                    Cluster::ROOT_DIR => BlockCount(
969                        ((u32::from(fat16_info.root_entries_count) * 32) + (Block::LEN as u32 - 1))
970                            / Block::LEN as u32,
971                    ),
972                    _ => BlockCount(u32::from(self.blocks_per_cluster)),
973                };
974
975                while let Some(cluster) = current_cluster {
976                    for block in first_dir_block_num.range(dir_size) {
977                        controller
978                            .block_device
979                            .read(&mut blocks, block, "read_dir")
980                            .map_err(Error::DeviceError)?;
981                        for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
982                            let start = entry * OnDiskDirEntry::LEN;
983                            let end = (entry + 1) * OnDiskDirEntry::LEN;
984                            let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
985                            if dir_entry.is_end() {
986                                // Can quit early
987                                return Err(Error::FileNotFound);
988                            } else if dir_entry.matches(&match_name) {
989                                // Found it
990                                // Safe, since Block::LEN always fits on a u32
991                                let start = u32::try_from(start).unwrap();
992                                return Ok(dir_entry.get_entry(FatType::Fat16, block, start));
993                            }
994                        }
995                    }
996                    if cluster != Cluster::ROOT_DIR {
997                        current_cluster = match self.next_cluster(controller, cluster) {
998                            Ok(n) => {
999                                first_dir_block_num = self.cluster_to_block(n);
1000                                Some(n)
1001                            }
1002                            _ => None,
1003                        };
1004                    } else {
1005                        current_cluster = None;
1006                    }
1007                }
1008                Err(Error::FileNotFound)
1009            }
1010            FatSpecificInfo::Fat32(fat32_info) => {
1011                let match_name =
1012                    ShortFileName::create_from_str(name).map_err(Error::FilenameError)?;
1013                let mut current_cluster = match dir.cluster {
1014                    Cluster::ROOT_DIR => Some(fat32_info.first_root_dir_cluster),
1015                    _ => Some(dir.cluster),
1016                };
1017                let mut blocks = [Block::new()];
1018                while let Some(cluster) = current_cluster {
1019                    let block_idx = self.cluster_to_block(cluster);
1020                    for block in block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) {
1021                        controller
1022                            .block_device
1023                            .read(&mut blocks, block, "read_dir")
1024                            .map_err(Error::DeviceError)?;
1025                        for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
1026                            let start = entry * OnDiskDirEntry::LEN;
1027                            let end = (entry + 1) * OnDiskDirEntry::LEN;
1028                            let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
1029                            if dir_entry.is_end() {
1030                                // Can quit early
1031                                return Err(Error::FileNotFound);
1032                            } else if dir_entry.matches(&match_name) {
1033                                // Found it
1034                                // Safe, since Block::LEN always fits on a u32
1035                                let start = u32::try_from(start).unwrap();
1036                                return Ok(dir_entry.get_entry(FatType::Fat16, block, start));
1037                            }
1038                        }
1039                    }
1040                    current_cluster = match self.next_cluster(controller, cluster) {
1041                        Ok(n) => Some(n),
1042                        _ => None,
1043                    }
1044                }
1045                Err(Error::FileNotFound)
1046            }
1047        }
1048    }
1049
1050    /// Finds the next free cluster after the start_cluster and before end_cluster
1051    pub(crate) fn find_next_free_cluster<D, T>(
1052        &self,
1053        controller: &mut Controller<D, T>,
1054        start_cluster: Cluster,
1055        end_cluster: Cluster,
1056    ) -> Result<Cluster, Error<D::Error>>
1057    where
1058        D: BlockDevice,
1059        T: TimeSource,
1060    {
1061        let mut blocks = [Block::new()];
1062        let mut current_cluster = start_cluster;
1063        match &self.fat_specific_info {
1064            FatSpecificInfo::Fat16(_fat16_info) => {
1065                while current_cluster.0 < end_cluster.0 {
1066                    trace!(
1067                        "current_cluster={:?}, end_cluster={:?}",
1068                        current_cluster,
1069                        end_cluster
1070                    );
1071                    let fat_offset = current_cluster.0 * 2;
1072                    trace!("fat_offset = {:?}", fat_offset);
1073                    let this_fat_block_num =
1074                        self.lba_start + self.fat_start.offset_bytes(fat_offset);
1075                    trace!("this_fat_block_num = {:?}", this_fat_block_num);
1076                    let mut this_fat_ent_offset = usize::try_from(fat_offset % Block::LEN_U32)
1077                        .map_err(|_| Error::ConversionError)?;
1078                    trace!("Reading block {:?}", this_fat_block_num);
1079                    controller
1080                        .block_device
1081                        .read(&mut blocks, this_fat_block_num, "next_cluster")
1082                        .map_err(Error::DeviceError)?;
1083
1084                    while this_fat_ent_offset <= Block::LEN - 2 {
1085                        let fat_entry = LittleEndian::read_u16(
1086                            &blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 1],
1087                        );
1088                        if fat_entry == 0 {
1089                            return Ok(current_cluster);
1090                        }
1091                        this_fat_ent_offset += 2;
1092                        current_cluster += 1;
1093                    }
1094                }
1095            }
1096            FatSpecificInfo::Fat32(_fat32_info) => {
1097                while current_cluster.0 < end_cluster.0 {
1098                    trace!(
1099                        "current_cluster={:?}, end_cluster={:?}",
1100                        current_cluster,
1101                        end_cluster
1102                    );
1103                    let fat_offset = current_cluster.0 * 4;
1104                    trace!("fat_offset = {:?}", fat_offset);
1105                    let this_fat_block_num =
1106                        self.lba_start + self.fat_start.offset_bytes(fat_offset);
1107                    trace!("this_fat_block_num = {:?}", this_fat_block_num);
1108                    let mut this_fat_ent_offset = usize::try_from(fat_offset % Block::LEN_U32)
1109                        .map_err(|_| Error::ConversionError)?;
1110                    trace!("Reading block {:?}", this_fat_block_num);
1111                    controller
1112                        .block_device
1113                        .read(&mut blocks, this_fat_block_num, "next_cluster")
1114                        .map_err(Error::DeviceError)?;
1115
1116                    while this_fat_ent_offset <= Block::LEN - 4 {
1117                        let fat_entry = LittleEndian::read_u32(
1118                            &blocks[0][this_fat_ent_offset..=this_fat_ent_offset + 3],
1119                        ) & 0x0FFF_FFFF;
1120                        if fat_entry == 0 {
1121                            return Ok(current_cluster);
1122                        }
1123                        this_fat_ent_offset += 4;
1124                        current_cluster += 1;
1125                    }
1126                }
1127            }
1128        }
1129        warn!("Out of space...");
1130        Err(Error::NotEnoughSpace)
1131    }
1132
1133    /// Tries to allocate a cluster
1134    pub(crate) fn alloc_cluster<D, T>(
1135        &mut self,
1136        controller: &mut Controller<D, T>,
1137        prev_cluster: Option<Cluster>,
1138        zero: bool,
1139    ) -> Result<Cluster, Error<D::Error>>
1140    where
1141        D: BlockDevice,
1142        T: TimeSource,
1143    {
1144        debug!("Allocating new cluster, prev_cluster={:?}", prev_cluster);
1145        let end_cluster = Cluster(self.cluster_count + RESERVED_ENTRIES);
1146        let start_cluster = match self.next_free_cluster {
1147            Some(cluster) if cluster.0 < end_cluster.0 => cluster,
1148            _ => Cluster(RESERVED_ENTRIES),
1149        };
1150        trace!(
1151            "Finding next free between {:?}..={:?}",
1152            start_cluster,
1153            end_cluster
1154        );
1155        let new_cluster = match self.find_next_free_cluster(controller, start_cluster, end_cluster)
1156        {
1157            Ok(cluster) => cluster,
1158            Err(_) if start_cluster.0 > RESERVED_ENTRIES => {
1159                debug!(
1160                    "Retrying, finding next free between {:?}..={:?}",
1161                    Cluster(RESERVED_ENTRIES),
1162                    end_cluster
1163                );
1164                self.find_next_free_cluster(controller, Cluster(RESERVED_ENTRIES), end_cluster)?
1165            }
1166            Err(e) => return Err(e),
1167        };
1168        self.update_fat(controller, new_cluster, Cluster::END_OF_FILE)?;
1169        if let Some(cluster) = prev_cluster {
1170            trace!(
1171                "Updating old cluster {:?} to {:?} in FAT",
1172                cluster,
1173                new_cluster
1174            );
1175            self.update_fat(controller, cluster, new_cluster)?;
1176        }
1177        trace!(
1178            "Finding next free between {:?}..={:?}",
1179            new_cluster,
1180            end_cluster
1181        );
1182        self.next_free_cluster =
1183            match self.find_next_free_cluster(controller, new_cluster, end_cluster) {
1184                Ok(cluster) => Some(cluster),
1185                Err(_) if new_cluster.0 > RESERVED_ENTRIES => {
1186                    match self.find_next_free_cluster(
1187                        controller,
1188                        Cluster(RESERVED_ENTRIES),
1189                        end_cluster,
1190                    ) {
1191                        Ok(cluster) => Some(cluster),
1192                        Err(e) => return Err(e),
1193                    }
1194                }
1195                Err(e) => return Err(e),
1196            };
1197        debug!("Next free cluster is {:?}", self.next_free_cluster);
1198        if let Some(ref mut number_free_cluster) = self.free_clusters_count {
1199            *number_free_cluster -= 1;
1200        };
1201        if zero {
1202            let blocks = [Block::new()];
1203            let first_block = self.cluster_to_block(new_cluster);
1204            let num_blocks = BlockCount(u32::from(self.blocks_per_cluster));
1205            for block in first_block.range(num_blocks) {
1206                controller
1207                    .block_device
1208                    .write(&blocks, block)
1209                    .map_err(Error::DeviceError)?;
1210            }
1211        }
1212        debug!("All done, returning {:?}", new_cluster);
1213        Ok(new_cluster)
1214    }
1215
1216    /// Tries to allocate a chain of clusters
1217    pub(crate) fn alloc_clusters<D, T>(
1218        &mut self,
1219        controller: &mut Controller<D, T>,
1220        mut prev_cluster: Option<Cluster>,
1221        mut clusters_to_alloc: u32,
1222        zero: bool,
1223    ) -> Result<(), Error<D::Error>>
1224    where
1225        D: BlockDevice,
1226        T: TimeSource,
1227    {
1228        while clusters_to_alloc > 0 {
1229            debug!("Allocating clusters, left={:?}", clusters_to_alloc);
1230            let new_cluster = self.alloc_cluster(controller, prev_cluster, zero)?;
1231            prev_cluster = Some(new_cluster);
1232            clusters_to_alloc -= 1;
1233        }
1234        Ok(())
1235    }
1236
1237    /// Marks the input cluster as an EOF and all the subsequent clusters in the chain as free
1238    pub(crate) fn truncate_cluster_chain<D, T>(
1239        &mut self,
1240        controller: &mut Controller<D, T>,
1241        cluster: Cluster,
1242    ) -> Result<(), Error<D::Error>>
1243    where
1244        D: BlockDevice,
1245        T: TimeSource,
1246    {
1247        if cluster.0 < RESERVED_ENTRIES {
1248            // file doesn't have any valid cluster allocated, there is nothing to do
1249            return Ok(());
1250        }
1251        let mut next = match self.next_cluster(controller, cluster) {
1252            Ok(n) => n,
1253            Err(Error::EndOfFile) => return Ok(()),
1254            Err(e) => return Err(e),
1255        };
1256        if let Some(ref mut next_free_cluster) = self.next_free_cluster {
1257            if next_free_cluster.0 > next.0 {
1258                *next_free_cluster = next;
1259            }
1260        } else {
1261            self.next_free_cluster = Some(next);
1262        }
1263        self.update_fat(controller, cluster, Cluster::END_OF_FILE)?;
1264        loop {
1265            match self.next_cluster(controller, next) {
1266                Ok(n) => {
1267                    self.update_fat(controller, next, Cluster::EMPTY)?;
1268                    next = n;
1269                }
1270                Err(Error::EndOfFile) => {
1271                    self.update_fat(controller, next, Cluster::EMPTY)?;
1272                    break;
1273                }
1274                Err(e) => return Err(e),
1275            }
1276            if let Some(ref mut number_free_cluster) = self.free_clusters_count {
1277                *number_free_cluster += 1;
1278            };
1279        }
1280        Ok(())
1281    }
1282}
1283
1284/// Load the boot parameter block from the start of the given partition and
1285/// determine if the partition contains a valid FAT16 or FAT32 file system.
1286pub fn parse_volume<D, T>(
1287    controller: &mut Controller<D, T>,
1288    lba_start: BlockIdx,
1289    num_blocks: BlockCount,
1290) -> Result<VolumeType, Error<D::Error>>
1291where
1292    D: BlockDevice,
1293    T: TimeSource,
1294    D::Error: core::fmt::Debug,
1295{
1296    let mut blocks = [Block::new()];
1297    controller
1298        .block_device
1299        .read(&mut blocks, lba_start, "read_bpb")
1300        .map_err(Error::DeviceError)?;
1301    let block = &blocks[0];
1302    let bpb = Bpb::create_from_bytes(&block).map_err(Error::FormatError)?;
1303    match bpb.fat_type {
1304        FatType::Fat16 => {
1305            // FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors;
1306            let root_dir_blocks = ((u32::from(bpb.root_entries_count()) * OnDiskDirEntry::LEN_U32)
1307                + (Block::LEN_U32 - 1))
1308                / Block::LEN_U32;
1309            let fat_start = BlockCount(u32::from(bpb.reserved_block_count()));
1310            let first_root_dir_block =
1311                fat_start + BlockCount(u32::from(bpb.num_fats()) * bpb.fat_size());
1312            let first_data_block = first_root_dir_block + BlockCount(root_dir_blocks);
1313            let mut volume = FatVolume {
1314                lba_start,
1315                num_blocks,
1316                name: VolumeName { data: [0u8; 11] },
1317                blocks_per_cluster: bpb.blocks_per_cluster(),
1318                first_data_block: (first_data_block),
1319                fat_start: BlockCount(u32::from(bpb.reserved_block_count())),
1320                free_clusters_count: None,
1321                next_free_cluster: None,
1322                cluster_count: bpb.total_clusters(),
1323                fat_specific_info: FatSpecificInfo::Fat16(Fat16Info {
1324                    root_entries_count: bpb.root_entries_count(),
1325                    first_root_dir_block,
1326                }),
1327            };
1328            volume.name.data[..].copy_from_slice(bpb.volume_label());
1329            Ok(VolumeType::Fat(volume))
1330        }
1331        FatType::Fat32 => {
1332            // FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * FATSz);
1333            let first_data_block = u32::from(bpb.reserved_block_count())
1334                + (u32::from(bpb.num_fats()) * bpb.fat_size());
1335
1336            // Safe to unwrap since this is a Fat32 Type
1337            let info_location = bpb.fs_info_block().unwrap();
1338            let mut info_blocks = [Block::new()];
1339            controller
1340                .block_device
1341                .read(
1342                    &mut info_blocks,
1343                    lba_start + info_location,
1344                    "read_info_sector",
1345                )
1346                .map_err(Error::DeviceError)?;
1347            let info_block = &info_blocks[0];
1348            let info_sector =
1349                InfoSector::create_from_bytes(&info_block).map_err(Error::FormatError)?;
1350
1351            let mut volume = FatVolume {
1352                lba_start,
1353                num_blocks,
1354                name: VolumeName { data: [0u8; 11] },
1355                blocks_per_cluster: bpb.blocks_per_cluster(),
1356                first_data_block: BlockCount(first_data_block),
1357                fat_start: BlockCount(u32::from(bpb.reserved_block_count())),
1358                free_clusters_count: info_sector.free_clusters_count(),
1359                next_free_cluster: info_sector.next_free_cluster(),
1360                cluster_count: bpb.total_clusters(),
1361                fat_specific_info: FatSpecificInfo::Fat32(Fat32Info {
1362                    info_location: lba_start + info_location,
1363                    first_root_dir_cluster: Cluster(bpb.first_root_dir_cluster()),
1364                }),
1365            };
1366            volume.name.data[..].copy_from_slice(bpb.volume_label());
1367            Ok(VolumeType::Fat(volume))
1368        }
1369    }
1370}
1371
1372#[cfg(test)]
1373mod test {
1374    use super::*;
1375
1376    fn parse(input: &str) -> Vec<u8> {
1377        let mut output = Vec::new();
1378        for line in input.lines() {
1379            let line = line.trim();
1380            if !line.is_empty() {
1381                // 32 bytes per line
1382                for index in 0..32 {
1383                    let start = index * 2;
1384                    let end = start + 1;
1385                    let piece = &line[start..=end];
1386                    let value = u8::from_str_radix(piece, 16).unwrap();
1387                    output.push(value);
1388                }
1389            }
1390        }
1391        output
1392    }
1393
1394    /// This is the first block of this directory listing.
1395    /// total 19880
1396    /// -rw-r--r-- 1 jonathan jonathan   10841 2016-03-01 19:56:36.000000000 +0000  bcm2708-rpi-b.dtb
1397    /// -rw-r--r-- 1 jonathan jonathan   11120 2016-03-01 19:56:34.000000000 +0000  bcm2708-rpi-b-plus.dtb
1398    /// -rw-r--r-- 1 jonathan jonathan   10871 2016-03-01 19:56:36.000000000 +0000  bcm2708-rpi-cm.dtb
1399    /// -rw-r--r-- 1 jonathan jonathan   12108 2016-03-01 19:56:36.000000000 +0000  bcm2709-rpi-2-b.dtb
1400    /// -rw-r--r-- 1 jonathan jonathan   12575 2016-03-01 19:56:36.000000000 +0000  bcm2710-rpi-3-b.dtb
1401    /// -rw-r--r-- 1 jonathan jonathan   17920 2016-03-01 19:56:38.000000000 +0000  bootcode.bin
1402    /// -rw-r--r-- 1 jonathan jonathan     136 2015-11-21 20:28:30.000000000 +0000  cmdline.txt
1403    /// -rw-r--r-- 1 jonathan jonathan    1635 2015-11-21 20:28:30.000000000 +0000  config.txt
1404    /// -rw-r--r-- 1 jonathan jonathan   18693 2016-03-01 19:56:30.000000000 +0000  COPYING.linux
1405    /// -rw-r--r-- 1 jonathan jonathan    2505 2016-03-01 19:56:38.000000000 +0000  fixup_cd.dat
1406    /// -rw-r--r-- 1 jonathan jonathan    6481 2016-03-01 19:56:38.000000000 +0000  fixup.dat
1407    /// -rw-r--r-- 1 jonathan jonathan    9722 2016-03-01 19:56:38.000000000 +0000  fixup_db.dat
1408    /// -rw-r--r-- 1 jonathan jonathan    9724 2016-03-01 19:56:38.000000000 +0000  fixup_x.dat
1409    /// -rw-r--r-- 1 jonathan jonathan     110 2015-11-21 21:32:06.000000000 +0000  issue.txt
1410    /// -rw-r--r-- 1 jonathan jonathan 4046732 2016-03-01 19:56:40.000000000 +0000  kernel7.img
1411    /// -rw-r--r-- 1 jonathan jonathan 3963140 2016-03-01 19:56:38.000000000 +0000  kernel.img
1412    /// -rw-r--r-- 1 jonathan jonathan    1494 2016-03-01 19:56:34.000000000 +0000  LICENCE.broadcom
1413    /// -rw-r--r-- 1 jonathan jonathan   18974 2015-11-21 21:32:06.000000000 +0000  LICENSE.oracle
1414    /// drwxr-xr-x 2 jonathan jonathan    8192 2016-03-01 19:56:54.000000000 +0000  overlays
1415    /// -rw-r--r-- 1 jonathan jonathan  612472 2016-03-01 19:56:40.000000000 +0000  start_cd.elf
1416    /// -rw-r--r-- 1 jonathan jonathan 4888200 2016-03-01 19:56:42.000000000 +0000  start_db.elf
1417    /// -rw-r--r-- 1 jonathan jonathan 2739672 2016-03-01 19:56:40.000000000 +0000  start.elf
1418    /// -rw-r--r-- 1 jonathan jonathan 3840328 2016-03-01 19:56:44.000000000 +0000  start_x.elf
1419    /// drwxr-xr-x 2 jonathan jonathan    8192 2015-12-05 21:55:06.000000000 +0000 'System Volume Information'
1420    #[test]
1421    fn test_dir_entries() {
1422        #[derive(Debug)]
1423        enum Expected {
1424            Lfn(bool, u8, [char; 13]),
1425            Short(DirEntry),
1426        }
1427        let raw_data = r#"
1428        626f6f7420202020202020080000699c754775470000699c7547000000000000 boot       ...i.uGuG..i.uG......
1429        416f007600650072006c000f00476100790073000000ffffffff0000ffffffff Ao.v.e.r.l...Ga.y.s.............
1430        4f5645524c4159532020201000001b9f6148614800001b9f6148030000000000 OVERLAYS   .....aHaH....aH......
1431        422d0070006c00750073000f00792e006400740062000000ffff0000ffffffff B-.p.l.u.s...y..d.t.b...........
1432        01620063006d00320037000f0079300038002d0072007000690000002d006200 .b.c.m.2.7...y0.8.-.r.p.i...-.b.
1433        42434d3237307e31445442200064119f614861480000119f61480900702b0000 BCM270~1DTB .d..aHaH....aH..p+..
1434        4143004f005000590049000f00124e0047002e006c0069006e00000075007800 AC.O.P.Y.I....N.G...l.i.n...u.x.
1435        434f5059494e7e314c494e2000000f9f6148614800000f9f6148050005490000 COPYIN~1LIN ....aHaH....aH...I..
1436        4263006f006d000000ffff0f0067ffffffffffffffffffffffff0000ffffffff Bc.o.m.......g..................
1437        014c004900430045004e000f0067430045002e00620072006f00000061006400 .L.I.C.E.N...gC.E...b.r.o...a.d.
1438        4c4943454e437e3142524f200000119f614861480000119f61480800d6050000 LICENC~1BRO ....aHaH....aH......
1439        422d0062002e00640074000f001962000000ffffffffffffffff0000ffffffff B-.b...d.t....b.................
1440        01620063006d00320037000f0019300039002d0072007000690000002d003200 .b.c.m.2.7....0.9.-.r.p.i...-.2.
1441        42434d3237307e34445442200064129f614861480000129f61480f004c2f0000 BCM270~4DTB .d..aHaH....aH..L/..
1442        422e0064007400620000000f0059ffffffffffffffffffffffff0000ffffffff B..d.t.b.....Y..................
1443        01620063006d00320037000f0059300038002d0072007000690000002d006200 .b.c.m.2.7...Y0.8.-.r.p.i...-.b.
1444        "#;
1445        let results = [
1446            Expected::Short(DirEntry {
1447                name: ShortFileName::create_from_str_mixed_case("boot").unwrap(),
1448                mtime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
1449                ctime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
1450                attributes: Attributes::create_from_fat(Attributes::VOLUME),
1451                cluster: Cluster(0),
1452                size: 0,
1453                entry_block: BlockIdx(0),
1454                entry_offset: 0,
1455            }),
1456            Expected::Lfn(
1457                true,
1458                1,
1459                [
1460                    'o', 'v', 'e', 'r', 'l', 'a', 'y', 's', '\u{0000}', '\u{ffff}', '\u{ffff}',
1461                    '\u{ffff}', '\u{ffff}',
1462                ],
1463            ),
1464            Expected::Short(DirEntry {
1465                name: ShortFileName::create_from_str("OVERLAYS").unwrap(),
1466                mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 54).unwrap(),
1467                ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 54).unwrap(),
1468                attributes: Attributes::create_from_fat(Attributes::DIRECTORY),
1469                cluster: Cluster(3),
1470                size: 0,
1471                entry_block: BlockIdx(0),
1472                entry_offset: 0,
1473            }),
1474            Expected::Lfn(
1475                true,
1476                2,
1477                [
1478                    '-', 'p', 'l', 'u', 's', '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}',
1479                    '\u{ffff}', '\u{ffff}',
1480                ],
1481            ),
1482            Expected::Lfn(
1483                false,
1484                1,
1485                [
1486                    'b', 'c', 'm', '2', '7', '0', '8', '-', 'r', 'p', 'i', '-', 'b',
1487                ],
1488            ),
1489            Expected::Short(DirEntry {
1490                name: ShortFileName::create_from_str("BCM270~1.DTB").unwrap(),
1491                mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
1492                ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
1493                attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
1494                cluster: Cluster(9),
1495                size: 11120,
1496                entry_block: BlockIdx(0),
1497                entry_offset: 0,
1498            }),
1499            Expected::Lfn(
1500                true,
1501                1,
1502                [
1503                    'C', 'O', 'P', 'Y', 'I', 'N', 'G', '.', 'l', 'i', 'n', 'u', 'x',
1504                ],
1505            ),
1506            Expected::Short(DirEntry {
1507                name: ShortFileName::create_from_str("COPYIN~1.LIN").unwrap(),
1508                mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 30).unwrap(),
1509                ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 30).unwrap(),
1510                attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
1511                cluster: Cluster(5),
1512                size: 18693,
1513                entry_block: BlockIdx(0),
1514                entry_offset: 0,
1515            }),
1516            Expected::Lfn(
1517                true,
1518                2,
1519                [
1520                    'c', 'o', 'm', '\u{0}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
1521                    '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
1522                ],
1523            ),
1524            Expected::Lfn(
1525                false,
1526                1,
1527                [
1528                    'L', 'I', 'C', 'E', 'N', 'C', 'E', '.', 'b', 'r', 'o', 'a', 'd',
1529                ],
1530            ),
1531            Expected::Short(DirEntry {
1532                name: ShortFileName::create_from_str("LICENC~1.BRO").unwrap(),
1533                mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
1534                ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 34).unwrap(),
1535                attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
1536                cluster: Cluster(8),
1537                size: 1494,
1538                entry_block: BlockIdx(0),
1539                entry_offset: 0,
1540            }),
1541            Expected::Lfn(
1542                true,
1543                2,
1544                [
1545                    '-', 'b', '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
1546                    '\u{ffff}', '\u{ffff}', '\u{ffff}',
1547                ],
1548            ),
1549            Expected::Lfn(
1550                false,
1551                1,
1552                [
1553                    'b', 'c', 'm', '2', '7', '0', '9', '-', 'r', 'p', 'i', '-', '2',
1554                ],
1555            ),
1556            Expected::Short(DirEntry {
1557                name: ShortFileName::create_from_str("BCM270~4.DTB").unwrap(),
1558                mtime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 36).unwrap(),
1559                ctime: Timestamp::from_calendar(2016, 3, 1, 19, 56, 36).unwrap(),
1560                attributes: Attributes::create_from_fat(Attributes::ARCHIVE),
1561                cluster: Cluster(15),
1562                size: 12108,
1563                entry_block: BlockIdx(0),
1564                entry_offset: 0,
1565            }),
1566            Expected::Lfn(
1567                true,
1568                2,
1569                [
1570                    '.', 'd', 't', 'b', '\u{0000}', '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
1571                    '\u{ffff}', '\u{ffff}', '\u{ffff}', '\u{ffff}',
1572                ],
1573            ),
1574            Expected::Lfn(
1575                false,
1576                1,
1577                [
1578                    'b', 'c', 'm', '2', '7', '0', '8', '-', 'r', 'p', 'i', '-', 'b',
1579                ],
1580            ),
1581        ];
1582
1583        let data = parse(raw_data);
1584        for (part, expected) in data.chunks(OnDiskDirEntry::LEN).zip(results.iter()) {
1585            let on_disk_entry = OnDiskDirEntry::new(part);
1586            match expected {
1587                Expected::Lfn(start, index, contents) if on_disk_entry.is_lfn() => {
1588                    let (calc_start, calc_index, calc_contents) =
1589                        on_disk_entry.lfn_contents().unwrap();
1590                    assert_eq!(*start, calc_start);
1591                    assert_eq!(*index, calc_index);
1592                    assert_eq!(*contents, calc_contents);
1593                }
1594                Expected::Short(expected_entry) if !on_disk_entry.is_lfn() => {
1595                    let parsed_entry = on_disk_entry.get_entry(FatType::Fat32, BlockIdx(0), 0);
1596                    assert_eq!(*expected_entry, parsed_entry);
1597                }
1598                _ => {
1599                    panic!(
1600                        "Bad dir entry, expected:\n{:#?}\nhad\n{:#?}",
1601                        expected, on_disk_entry
1602                    );
1603                }
1604            }
1605        }
1606    }
1607
1608    #[test]
1609    fn test_bpb() {
1610        // Taken from a Raspberry Pi bootable SD-Card
1611        const BPB_EXAMPLE: [u8; 512] = [
1612            0xeb, 0x3c, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00, 0x02, 0x10,
1613            0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0xf8, 0x20, 0x00, 0x3f, 0x00, 0xff, 0x00,
1614            0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x80, 0x01, 0x29, 0xbb, 0xb0, 0x71,
1615            0x77, 0x62, 0x6f, 0x6f, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41,
1616            0x54, 0x31, 0x36, 0x20, 0x20, 0x20, 0x0e, 0x1f, 0xbe, 0x5b, 0x7c, 0xac, 0x22, 0xc0,
1617            0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10, 0x5e, 0xeb, 0xf0, 0x32,
1618            0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
1619            0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1620            0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73,
1621            0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f,
1622            0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61,
1623            0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20,
1624            0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61,
1625            0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
1626            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1627            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1628            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1629            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1630            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1631            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1632            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1633            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1634            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1635            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1636            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1637            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1638            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1639            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1640            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1641            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1642            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1643            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1644            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1645            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1646            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1647            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1648            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa,
1649        ];
1650        let bpb = Bpb::create_from_bytes(&BPB_EXAMPLE).unwrap();
1651        assert_eq!(bpb.footer(), Bpb::FOOTER_VALUE);
1652        assert_eq!(bpb.oem_name(), b"mkfs.fat");
1653        assert_eq!(bpb.bytes_per_block(), 512);
1654        assert_eq!(bpb.blocks_per_cluster(), 16);
1655        assert_eq!(bpb.reserved_block_count(), 1);
1656        assert_eq!(bpb.num_fats(), 2);
1657        assert_eq!(bpb.root_entries_count(), 512);
1658        assert_eq!(bpb.total_blocks16(), 0);
1659        assert!((bpb.media() & 0xF0) == 0xF0);
1660        assert_eq!(bpb.fat_size16(), 32);
1661        assert_eq!(bpb.blocks_per_track(), 63);
1662        assert_eq!(bpb.num_heads(), 255);
1663        assert_eq!(bpb.hidden_blocks(), 0);
1664        assert_eq!(bpb.total_blocks32(), 122_880);
1665        assert_eq!(bpb.footer(), 0xAA55);
1666        assert_eq!(bpb.drive_number(), 0x80);
1667        assert_eq!(bpb.boot_signature(), 0x29);
1668        assert_eq!(bpb.volume_id(), 0x7771_B0BB);
1669        assert_eq!(bpb.volume_label(), b"boot       ");
1670        assert_eq!(bpb.fs_type(), b"FAT16   ");
1671        assert_eq!(bpb.fat_size(), 32);
1672        assert_eq!(bpb.total_blocks(), 122_880);
1673        assert_eq!(bpb.fat_type, FatType::Fat16);
1674    }
1675}
1676
1677// ****************************************************************************
1678//
1679// End Of File
1680//
1681// ****************************************************************************