embedded_fat/fat/
bpb.rs

1//! Boot Parameter Block
2
3use crate::{
4    blockdevice::BlockCount,
5    fat::{FatType, OnDiskDirEntry},
6};
7use byteorder::{ByteOrder, LittleEndian};
8
9/// Represents a Boot Parameter Block. This is the first sector of a FAT
10/// formatted partition, and it describes various properties of the FAT
11/// filesystem.
12pub struct Bpb<'a> {
13    data: &'a [u8; 512],
14    pub(crate) fat_type: FatType,
15    cluster_count: u32,
16}
17
18impl<'a> Bpb<'a> {
19    pub(crate) const FOOTER_VALUE: u16 = 0xAA55;
20
21    /// Attempt to parse a Boot Parameter Block from a 512 byte sector.
22    pub fn create_from_bytes(data: &[u8; 512]) -> Result<Bpb, &'static str> {
23        let mut bpb = Bpb {
24            data,
25            fat_type: FatType::Fat16,
26            cluster_count: 0,
27        };
28        if bpb.footer() != Self::FOOTER_VALUE {
29            return Err("Bad BPB footer");
30        }
31
32        let root_dir_blocks =
33            BlockCount::from_bytes(u32::from(bpb.root_entries_count()) * OnDiskDirEntry::LEN_U32).0;
34        let non_data_blocks = u32::from(bpb.reserved_block_count())
35            + (u32::from(bpb.num_fats()) * bpb.fat_size())
36            + root_dir_blocks;
37        let data_blocks = bpb.total_blocks() - non_data_blocks;
38        bpb.cluster_count = data_blocks / u32::from(bpb.blocks_per_cluster());
39        if bpb.cluster_count < 4085 {
40            return Err("FAT12 is unsupported");
41        } else if bpb.cluster_count < 65525 {
42            bpb.fat_type = FatType::Fat16;
43        } else {
44            bpb.fat_type = FatType::Fat32;
45        }
46
47        match bpb.fat_type {
48            FatType::Fat16 => Ok(bpb),
49            FatType::Fat32 if bpb.fs_ver() == 0 => {
50                // Only support FAT32 version 0.0
51                Ok(bpb)
52            }
53            _ => Err("Invalid FAT format"),
54        }
55    }
56
57    // FAT16/FAT32
58    define_field!(bytes_per_block, u16, 11);
59    define_field!(blocks_per_cluster, u8, 13);
60    define_field!(reserved_block_count, u16, 14);
61    define_field!(num_fats, u8, 16);
62    define_field!(root_entries_count, u16, 17);
63    define_field!(total_blocks16, u16, 19);
64    define_field!(media, u8, 21);
65    define_field!(fat_size16, u16, 22);
66    define_field!(blocks_per_track, u16, 24);
67    define_field!(num_heads, u16, 26);
68    define_field!(hidden_blocks, u32, 28);
69    define_field!(total_blocks32, u32, 32);
70    define_field!(footer, u16, 510);
71
72    // FAT32 only
73    define_field!(fat_size32, u32, 36);
74    define_field!(fs_ver, u16, 42);
75    define_field!(first_root_dir_cluster, u32, 44);
76    define_field!(fs_info, u16, 48);
77    define_field!(backup_boot_block, u16, 50);
78
79    /// Get the OEM name string for this volume
80    pub fn oem_name(&self) -> &[u8] {
81        &self.data[3..11]
82    }
83
84    // FAT16/FAT32 functions
85
86    /// Get the Volume Label string for this volume
87    pub fn volume_label(&self) -> &[u8] {
88        if self.fat_type != FatType::Fat32 {
89            &self.data[43..=53]
90        } else {
91            &self.data[71..=81]
92        }
93    }
94
95    // FAT32 only functions
96
97    /// On a FAT32 volume, return the free block count from the Info Block. On
98    /// a FAT16 volume, returns None.
99    pub fn fs_info_block(&self) -> Option<BlockCount> {
100        if self.fat_type != FatType::Fat32 {
101            None
102        } else {
103            Some(BlockCount(u32::from(self.fs_info())))
104        }
105    }
106
107    // Magic functions that get the right FAT16/FAT32 result
108
109    /// Get the size of the File Allocation Table in blocks.
110    pub fn fat_size(&self) -> u32 {
111        let result = u32::from(self.fat_size16());
112        if result != 0 {
113            result
114        } else {
115            self.fat_size32()
116        }
117    }
118
119    /// Get the total number of blocks in this filesystem.
120    pub fn total_blocks(&self) -> u32 {
121        let result = u32::from(self.total_blocks16());
122        if result != 0 {
123            result
124        } else {
125            self.total_blocks32()
126        }
127    }
128
129    /// Get the total number of clusters in this filesystem.
130    pub fn total_clusters(&self) -> u32 {
131        self.cluster_count
132    }
133}