1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use core::str::from_utf8;
use bitflags::bitflags;
use thiserror::Error;
use super::{
io::{BLOCK_SIZE, Block},
utils::{read_u16_le, read_u32_le, read_u64_le},
};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Error, Debug, Clone, Copy)]
pub enum Error {
/// if you attempt to read an exfat file system boot sector but you've actually read the
/// master boot record (MBR) or GPT boot record of an SD card then you will typically encounter
/// this error. The MBR typically points to the boot sector of the exFAT file system.
/// Alternatively if you attempt to read another file system you will also encounter this
#[error("invalid jump boot as boot_sector[..2] expected [0xeb, 0x76, 0x90]")]
InvalidJumpBoot,
#[error("invalid file system name at boot_sector[3..11] expected \"EXFAT \"")]
InvalidFileSystemName,
#[error("invalid boot signature at boot_sector[510..512] expected [0x55, 0xaa]")]
InvalidBootSignature,
}
/// boot sector describes the exfat volume structure
#[derive(Debug)]
#[allow(unused)]
pub(crate) struct BootSector {
/// media-relative offset of the partition which hosts this exFAT volume
pub partition_offset: u64,
/// size of exFAT volume in sectors
pub volume_length: u64,
/// volume-relative sector offset of the first FAT
pub fat_offset: u32,
/// fat length in sectors
pub fat_length: u32,
/// volume-relative sector offset of the cluster heap
pub cluster_heap_offset: u32,
/// number of clusters in the cluster heap
pub cluster_count: u32,
/// cluster index of the first cluster of the root directory
pub first_cluster_of_root_dir: u32,
/// autogenerated by combining date and time when formatting the volume
pub volume_serial_number: u32,
/// major (high byte) and minor (low byte) revision number of the exfat structures
pub file_system_revision: u16,
/// status of various file system structures in the exfat volume
pub volume_flags: VolumeFlags,
/// num bytes per sector (512 bytes is typical for an SD card)
pub bytes_per_sector: u16,
/// number of sectors per cluster (64 is normal for an SD card of 8GB - that equates to 32KB per cluster)
pub sectors_per_cluster: u8,
/// number of fats and allocation bitmaps (normally 1 but can be two for TexFAT volumes)
pub number_of_fats: u8,
/// 13h drive number for bootstrapping purposes (128 is standard)
pub drive_select: u8,
/// percentage of clusters in the cluster heap that are allocated
pub percent_in_use: u8,
}
impl BootSector {
pub(crate) fn check_is_valid(value: &Block) -> Result<(), Error> {
// jump boot
// check that this is not MBR or GPT which is usually the first sector of an SD card
if value[0] != 0xeb && value[1] != 0x76 && value[2] != 0x90 {
return Err(Error::InvalidJumpBoot);
}
// file system name
match from_utf8(&value[3..11]) {
Ok(s) => {
if s != "EXFAT " {
return Err(Error::InvalidFileSystemName);
}
}
Err(_e) => return Err(Error::InvalidFileSystemName),
};
// boot signature
if value[510] != 0x55 && value[511] != 0xaa {
return Err(Error::InvalidBootSignature);
}
Ok(())
}
}
impl TryFrom<&[u8; BLOCK_SIZE]> for BootSector {
type Error = Error;
fn try_from(value: &[u8; BLOCK_SIZE]) -> Result<Self, Self::Error> {
Self::check_is_valid(value)?;
let partition_offset = read_u64_le::<64, _>(value);
let volume_length = read_u64_le::<72, _>(value);
let fat_offset = read_u32_le::<80, _>(value);
let fat_length = read_u32_le::<84, _>(value);
let cluster_heap_offset = read_u32_le::<88, _>(value);
let cluster_count = read_u32_le::<92, _>(value);
let first_cluster_of_root_dir = read_u32_le::<96, _>(value);
let volume_serial_number = read_u32_le::<100, _>(value);
let file_system_revision = read_u16_le::<104, _>(value);
let volume_flags = VolumeFlags::from_bits_truncate(read_u16_le::<106, _>(value));
let bytes_per_sector = 1u16 << value[108]; // bytes_per_sector_shift
let sectors_per_cluster = 1u8 << value[109]; // sectors_per_cluster_shift
let number_of_fats = value[110];
let drive_select = value[111];
let percent_in_use = value[112];
Ok(BootSector {
partition_offset,
volume_length,
fat_offset,
fat_length,
cluster_heap_offset,
cluster_count,
first_cluster_of_root_dir,
volume_serial_number,
file_system_revision,
volume_flags,
bytes_per_sector,
sectors_per_cluster,
number_of_fats,
drive_select,
percent_in_use,
})
}
}
bitflags! {
/// Represents a set of volume flags.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct VolumeFlags: u16 {
/// The value `FirstFat`, at bit position `0`.
/// Value 0 for ActiveFat and 1 for TexFAT
const FirstFat = 0b0000_0001;
/// The value `VolumeDirty`, at bit position `1`.
/// Set on mount, restored on unmount
const VolumeDirty = 0b0000_0010;
/// The value `MediaFailure`, at bit position `2`.
/// Set on unrecoverable disk error, cleared after a surface scan.
const MediaFailure = 0b0000_0100;
}
}