1use crate::bytes::{le_u16, le_u32, u8_at};
6use crate::error::{FatError, Result};
7
8const FAT12_MAX_CLUSTERS: u32 = 4085;
11const FAT16_MAX_CLUSTERS: u32 = 65525;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum FatVariant {
16 Fat12,
18 Fat16,
20 Fat32,
22 ExFat,
24}
25
26#[derive(Debug, Clone)]
29pub struct Geometry {
30 pub variant: FatVariant,
32 pub bytes_per_sector: u32,
34 pub sectors_per_cluster: u32,
36 pub cluster_size: u32,
38 pub reserved_sectors: u32,
40 pub num_fats: u32,
42 pub fat_size_sectors: u32,
44 pub root_entry_count: u32,
46 pub total_sectors: u64,
48 pub root_cluster: u32,
50 pub count_of_clusters: u32,
52 pub fat_start: u64,
54 pub root_dir_start: u64,
56 pub root_dir_bytes: u32,
58 pub data_start: u64,
60}
61
62struct Fields {
64 bytes_per_sector: u32,
65 sectors_per_cluster: u32,
66 reserved_sectors: u32,
67 num_fats: u32,
68 root_entry_count: u32,
69 fat_size_sectors: u32,
70 total_sectors: u64,
71}
72
73impl Geometry {
74 pub fn parse(boot: &[u8]) -> Result<Geometry> {
79 let f = read_fields(boot)?;
80 let bps = u64::from(f.bytes_per_sector);
81
82 let root_dir_sectors = (u64::from(f.root_entry_count) * 32).div_ceil(bps);
84 let meta_sectors = u64::from(f.reserved_sectors)
85 + u64::from(f.num_fats) * u64::from(f.fat_size_sectors)
86 + root_dir_sectors;
87 if meta_sectors > f.total_sectors {
88 return Err(FatError::InvalidBoot(format!(
89 "reserved+FATs+root ({meta_sectors} sectors) exceeds total ({})",
90 f.total_sectors
91 )));
92 }
93
94 let data_sectors = f.total_sectors - meta_sectors;
95 let count_of_clusters =
96 u32::try_from(data_sectors / u64::from(f.sectors_per_cluster)).unwrap_or(u32::MAX);
97 let variant = if count_of_clusters < FAT12_MAX_CLUSTERS {
98 FatVariant::Fat12
99 } else if count_of_clusters < FAT16_MAX_CLUSTERS {
100 FatVariant::Fat16
101 } else {
102 FatVariant::Fat32
103 };
104 let is32 = variant == FatVariant::Fat32;
105
106 let fat_start = u64::from(f.reserved_sectors) * bps;
107 let root_region_start = (u64::from(f.reserved_sectors)
108 + u64::from(f.num_fats) * u64::from(f.fat_size_sectors))
109 * bps;
110 let (root_dir_start, root_dir_bytes) = if is32 {
111 (0, 0)
112 } else {
113 (
114 root_region_start,
115 u32::try_from(root_dir_sectors * bps).unwrap_or(u32::MAX),
116 )
117 };
118
119 Ok(Geometry {
120 variant,
121 bytes_per_sector: f.bytes_per_sector,
122 sectors_per_cluster: f.sectors_per_cluster,
123 cluster_size: f.bytes_per_sector * f.sectors_per_cluster,
124 reserved_sectors: f.reserved_sectors,
125 num_fats: f.num_fats,
126 fat_size_sectors: f.fat_size_sectors,
127 root_entry_count: if is32 { 0 } else { f.root_entry_count },
128 total_sectors: f.total_sectors,
129 root_cluster: if is32 { le_u32(boot, 44) } else { 0 },
130 count_of_clusters,
131 fat_start,
132 root_dir_start,
133 root_dir_bytes,
134 data_start: meta_sectors * bps,
135 })
136 }
137
138 pub fn cluster_offset(&self, cluster: u32) -> Option<u64> {
140 if cluster < 2 {
141 return None;
142 }
143 let rel = u64::from(cluster - 2).checked_mul(u64::from(self.cluster_size))?;
144 self.data_start.checked_add(rel)
145 }
146}
147
148fn read_fields(boot: &[u8]) -> Result<Fields> {
150 let sig = le_u16(boot, 510);
151 if sig != 0xAA55 {
152 return Err(FatError::InvalidBoot(format!(
153 "boot signature at 0x1FE is {sig:#06x}, expected 0x55AA"
154 )));
155 }
156
157 let bytes_per_sector = u32::from(le_u16(boot, 11));
158 if !bytes_per_sector.is_power_of_two() || !(512..=4096).contains(&bytes_per_sector) {
159 return Err(FatError::InvalidBoot(format!(
160 "bytes-per-sector at 0x0B is {bytes_per_sector}, not a power of two in 512..=4096"
161 )));
162 }
163
164 let sectors_per_cluster = u32::from(u8_at(boot, 13));
165 if !sectors_per_cluster.is_power_of_two() || !(1..=128).contains(§ors_per_cluster) {
166 return Err(FatError::InvalidBoot(format!(
167 "sectors-per-cluster at 0x0D is {sectors_per_cluster}, not a power of two in 1..=128"
168 )));
169 }
170
171 let reserved_sectors = u32::from(le_u16(boot, 14));
172 if reserved_sectors == 0 {
173 return Err(FatError::InvalidBoot(
174 "reserved-sector count at 0x0E is 0".into(),
175 ));
176 }
177
178 let num_fats = u32::from(u8_at(boot, 16));
179 if num_fats == 0 {
180 return Err(FatError::InvalidBoot("number of FATs at 0x10 is 0".into()));
181 }
182
183 let fat_size_16 = u32::from(le_u16(boot, 22));
184 let fat_size_sectors = if fat_size_16 != 0 {
185 fat_size_16
186 } else {
187 le_u32(boot, 36)
188 };
189 if fat_size_sectors == 0 {
190 return Err(FatError::InvalidBoot(
191 "FAT size is 0 (both 0x16 and 0x24 are zero)".into(),
192 ));
193 }
194
195 let total_sectors_16 = u32::from(le_u16(boot, 19));
196 let total_sectors = if total_sectors_16 != 0 {
197 u64::from(total_sectors_16)
198 } else {
199 u64::from(le_u32(boot, 32))
200 };
201 if total_sectors == 0 {
202 return Err(FatError::InvalidBoot(
203 "total sector count is 0 (both 0x13 and 0x20 are zero)".into(),
204 ));
205 }
206
207 Ok(Fields {
208 bytes_per_sector,
209 sectors_per_cluster,
210 reserved_sectors,
211 num_fats,
212 root_entry_count: u32::from(le_u16(boot, 17)),
213 fat_size_sectors,
214 total_sectors,
215 })
216}
217
218#[cfg(test)]
219mod tests {
220 use super::{FatVariant, Geometry};
221
222 fn fat12_boot() -> Vec<u8> {
225 let mut b = vec![0u8; 512];
226 b[0] = 0xEB;
227 b[1] = 0x3C;
228 b[2] = 0x90;
229 b[11..13].copy_from_slice(&512u16.to_le_bytes()); b[13] = 1; b[14..16].copy_from_slice(&1u16.to_le_bytes()); b[16] = 2; b[17..19].copy_from_slice(&224u16.to_le_bytes()); b[19..21].copy_from_slice(&2880u16.to_le_bytes()); b[22..24].copy_from_slice(&9u16.to_le_bytes()); b[510] = 0x55;
237 b[511] = 0xAA;
238 b
239 }
240
241 fn fat32_boot() -> Vec<u8> {
244 let mut b = vec![0u8; 512];
245 b[0] = 0xEB;
246 b[2] = 0x90;
247 b[11..13].copy_from_slice(&512u16.to_le_bytes());
248 b[13] = 1;
249 b[14..16].copy_from_slice(&32u16.to_le_bytes());
250 b[16] = 2;
251 b[17..19].copy_from_slice(&0u16.to_le_bytes());
252 b[19..21].copy_from_slice(&0u16.to_le_bytes()); b[22..24].copy_from_slice(&0u16.to_le_bytes()); b[32..36].copy_from_slice(&70000u32.to_le_bytes()); b[36..40].copy_from_slice(&512u32.to_le_bytes()); b[44..48].copy_from_slice(&2u32.to_le_bytes()); b[510] = 0x55;
258 b[511] = 0xAA;
259 b
260 }
261
262 #[test]
263 fn detects_fat12_geometry() {
264 let g = Geometry::parse(&fat12_boot()).unwrap();
265 assert_eq!(g.variant, FatVariant::Fat12);
266 assert_eq!(g.bytes_per_sector, 512);
267 assert_eq!(g.sectors_per_cluster, 1);
268 assert_eq!(g.cluster_size, 512);
269 assert_eq!(g.count_of_clusters, 2847);
271 assert_eq!(g.fat_start, 512);
272 assert_eq!(g.root_dir_start, 19 * 512);
273 assert_eq!(g.root_dir_bytes, 14 * 512);
274 assert_eq!(g.data_start, 33 * 512);
275 }
276
277 #[test]
278 fn detects_fat32_geometry() {
279 let g = Geometry::parse(&fat32_boot()).unwrap();
280 assert_eq!(g.variant, FatVariant::Fat32);
281 assert_eq!(g.root_entry_count, 0);
282 assert_eq!(g.root_cluster, 2);
283 assert_eq!(g.root_dir_bytes, 0);
284 assert_eq!(g.count_of_clusters, 68944);
286 assert_eq!(g.data_start, (32 + 1024) * 512);
287 }
288
289 #[test]
290 fn detects_fat16_boundary() {
291 let mut b = fat12_boot();
293 b[17..19].copy_from_slice(&0u16.to_le_bytes()); b[19..21].copy_from_slice(&0u16.to_le_bytes());
295 b[32..36].copy_from_slice(&5033u32.to_le_bytes()); b[22..24].copy_from_slice(&0u16.to_le_bytes());
297 b[36..40].copy_from_slice(&16u32.to_le_bytes()); let g = Geometry::parse(&b).unwrap();
300 assert_eq!(g.variant, FatVariant::Fat16);
301 assert_eq!(g.count_of_clusters, 5000);
302 }
303
304 #[test]
305 fn cluster_offset_maps_from_data_start() {
306 let g = Geometry::parse(&fat12_boot()).unwrap();
307 assert_eq!(g.cluster_offset(2), Some(g.data_start));
309 assert_eq!(
311 g.cluster_offset(3),
312 Some(g.data_start + u64::from(g.cluster_size))
313 );
314 assert_eq!(g.cluster_offset(0), None);
316 assert_eq!(g.cluster_offset(1), None);
317 }
318
319 #[test]
320 fn rejects_bad_signature() {
321 let mut b = fat12_boot();
322 b[510] = 0x00;
323 let e = Geometry::parse(&b).unwrap_err();
324 assert!(format!("{e}").contains("55")); }
326
327 #[test]
328 fn rejects_non_power_of_two_sector() {
329 let mut b = fat12_boot();
330 b[11..13].copy_from_slice(&513u16.to_le_bytes());
331 assert!(Geometry::parse(&b).is_err());
332 }
333
334 #[test]
335 fn rejects_zero_fats() {
336 let mut b = fat12_boot();
337 b[16] = 0;
338 assert!(Geometry::parse(&b).is_err());
339 }
340
341 #[test]
342 fn rejects_non_power_of_two_cluster() {
343 let mut b = fat12_boot();
344 b[13] = 3; assert!(Geometry::parse(&b).is_err());
346 }
347
348 #[test]
349 fn rejects_zero_reserved() {
350 let mut b = fat12_boot();
351 b[14..16].copy_from_slice(&0u16.to_le_bytes());
352 assert!(Geometry::parse(&b).is_err());
353 }
354
355 #[test]
356 fn rejects_zero_fat_size() {
357 let mut b = fat12_boot();
358 b[22..24].copy_from_slice(&0u16.to_le_bytes()); b[36..40].copy_from_slice(&0u32.to_le_bytes()); assert!(Geometry::parse(&b).is_err());
361 }
362
363 #[test]
364 fn rejects_zero_total_sectors() {
365 let mut b = fat12_boot();
366 b[19..21].copy_from_slice(&0u16.to_le_bytes()); b[32..36].copy_from_slice(&0u32.to_le_bytes()); assert!(Geometry::parse(&b).is_err());
369 }
370
371 #[test]
372 fn rejects_oversize_reserved_region() {
373 let mut b = fat12_boot();
375 b[14..16].copy_from_slice(&9000u16.to_le_bytes());
376 assert!(Geometry::parse(&b).is_err());
377 }
378}