#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Scheme {
Mbr,
Gpt,
Apm,
}
pub const MBR_BOOT_SIGNATURE: [u8; 2] = [0x55, 0xAA];
pub const MBR_BOOT_SIGNATURE_OFFSET: usize = 510;
pub const GPT_HEADER_MAGIC: &[u8; 8] = b"EFI PART";
pub const APM_DDR_MAGIC: &[u8; 2] = b"ER";
#[must_use]
pub fn detect_scheme(boot_area: &[u8]) -> Option<Scheme> {
if boot_area.len() >= MBR_BOOT_SIGNATURE_OFFSET + 2
&& boot_area[MBR_BOOT_SIGNATURE_OFFSET] == MBR_BOOT_SIGNATURE[0]
&& boot_area[MBR_BOOT_SIGNATURE_OFFSET + 1] == MBR_BOOT_SIGNATURE[1]
{
if boot_area.len() >= 520 && &boot_area[512..520] == GPT_HEADER_MAGIC {
return Some(Scheme::Gpt);
}
if looks_like_vbr(boot_area) {
return None;
}
return Some(Scheme::Mbr);
}
if boot_area.len() >= 2 && &boot_area[0..2] == APM_DDR_MAGIC {
return Some(Scheme::Apm);
}
None
}
#[must_use]
pub fn looks_like_vbr(sector0: &[u8]) -> bool {
if sector0.len() < 512 || sector0[510] != 0x55 || sector0[511] != 0xAA {
return false;
}
if sector0[0] != 0xEB && sector0[0] != 0xE9 {
return false;
}
let oem = §or0[3..11];
if oem == b"NTFS " || oem == b"EXFAT " {
return true;
}
if §or0[54..62] == b"FAT12 " || §or0[54..62] == b"FAT16 " {
return true;
}
§or0[82..90] == b"FAT32 "
}
#[cfg(test)]
mod tests {
#[test]
fn detect_scheme_covers_gpt_mbr_apm_vbr_and_none() {
let mut gpt = vec![0u8; 520];
gpt[MBR_BOOT_SIGNATURE_OFFSET] = MBR_BOOT_SIGNATURE[0];
gpt[MBR_BOOT_SIGNATURE_OFFSET + 1] = MBR_BOOT_SIGNATURE[1];
gpt[512..520].copy_from_slice(GPT_HEADER_MAGIC);
assert_eq!(detect_scheme(&gpt), Some(Scheme::Gpt));
let mut mbr = vec![0u8; 512];
mbr[MBR_BOOT_SIGNATURE_OFFSET] = 0x55;
mbr[MBR_BOOT_SIGNATURE_OFFSET + 1] = 0xAA;
assert_eq!(detect_scheme(&mbr), Some(Scheme::Mbr));
let mut vbr = vec![0u8; 512];
vbr[0] = 0xEB;
vbr[3..11].copy_from_slice(b"NTFS ");
vbr[510] = 0x55;
vbr[511] = 0xAA;
assert_eq!(detect_scheme(&vbr), None);
let mut apm = vec![0u8; 8];
apm[0..2].copy_from_slice(APM_DDR_MAGIC);
assert_eq!(detect_scheme(&apm), Some(Scheme::Apm));
assert_eq!(detect_scheme(&[0u8; 4]), None);
assert_eq!(detect_scheme(&[]), None);
}
#[test]
fn looks_like_vbr_accepts_ntfs_exfat_fat_and_rejects_others() {
let mut ntfs = vec![0u8; 512];
ntfs[0] = 0xEB;
ntfs[3..11].copy_from_slice(b"NTFS ");
ntfs[510] = 0x55;
ntfs[511] = 0xAA;
assert!(looks_like_vbr(&ntfs));
let mut exfat = vec![0u8; 512];
exfat[0] = 0xE9;
exfat[3..11].copy_from_slice(b"EXFAT ");
exfat[510] = 0x55;
exfat[511] = 0xAA;
assert!(looks_like_vbr(&exfat));
let mut fat16 = vec![0u8; 512];
fat16[0] = 0xEB;
fat16[54..62].copy_from_slice(b"FAT16 ");
fat16[510] = 0x55;
fat16[511] = 0xAA;
assert!(looks_like_vbr(&fat16));
let mut fat32 = vec![0u8; 512];
fat32[0] = 0xEB;
fat32[82..90].copy_from_slice(b"FAT32 ");
fat32[510] = 0x55;
fat32[511] = 0xAA;
assert!(looks_like_vbr(&fat32));
assert!(!looks_like_vbr(&[0u8; 512]));
let mut nojump = vec![0u8; 512];
nojump[510] = 0x55;
nojump[511] = 0xAA;
assert!(!looks_like_vbr(&nojump));
assert!(!looks_like_vbr(&[0x55, 0xAA]));
}
use super::*;
#[test]
fn magics_have_expected_values() {
assert_eq!(MBR_BOOT_SIGNATURE, [0x55, 0xAA]);
assert_eq!(GPT_HEADER_MAGIC, b"EFI PART");
assert_eq!(APM_DDR_MAGIC, b"ER");
}
}