ahrightos_fs/
disk_format.rs1use super::blkfs::{BlkFS, BLKFS_MIN_BLOCKS};
10use super::block_dev::{BlockDeviceHandle, BLOCK_SIZE};
11use super::file::FileError;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum DiskFormat {
16 Blank,
18 BlkFS,
20 InvalidBlkFS,
22 TooSmall,
24 MasterBootRecord,
26 GuidPartitionTable,
28 FatFamily,
30 ExtFamily,
32 Unknown,
34}
35
36impl DiskFormat {
37 pub const fn description(self) -> &'static str {
38 match self {
39 DiskFormat::Blank => "blank device",
40 DiskFormat::BlkFS => "AhRightOS BlkFS",
41 DiskFormat::InvalidBlkFS => "invalid AhRightOS BlkFS",
42 DiskFormat::TooSmall => "device too small for BlkFS",
43 DiskFormat::MasterBootRecord => "MBR partition table",
44 DiskFormat::GuidPartitionTable => "GPT partition table",
45 DiskFormat::FatFamily => "FAT filesystem",
46 DiskFormat::ExtFamily => "ext-family filesystem",
47 DiskFormat::Unknown => "unknown disk format",
48 }
49 }
50}
51
52const BLKFS_MAGIC_LE: [u8; 4] = 0x424C_4B46u32.to_le_bytes();
53const BLANK_SCAN_MAX_BLOCKS: usize = BLKFS_MIN_BLOCKS * 64;
54
55pub fn detect_disk_format(dev: &BlockDeviceHandle) -> Result<DiskFormat, FileError> {
57 if dev.block_count() < BLKFS_MIN_BLOCKS {
58 return Ok(DiskFormat::TooSmall);
59 }
60
61 let mut block0 = [0u8; BLOCK_SIZE];
62 dev.read_block(0, &mut block0)?;
63
64 if block0[..4] == BLKFS_MAGIC_LE {
65 return if BlkFS::open(dev.clone()).is_ok() {
66 Ok(DiskFormat::BlkFS)
67 } else {
68 Ok(DiskFormat::InvalidBlkFS)
69 };
70 }
71
72 if looks_like_gpt(dev)? {
73 return Ok(DiskFormat::GuidPartitionTable);
74 }
75 if looks_like_ext(dev)? {
76 return Ok(DiskFormat::ExtFamily);
77 }
78 if looks_like_fat(&block0) {
79 return Ok(DiskFormat::FatFamily);
80 }
81 if has_mbr_signature(&block0) {
82 return Ok(DiskFormat::MasterBootRecord);
83 }
84 if let Some(true) = device_is_blank(dev, &block0)? {
85 return Ok(DiskFormat::Blank);
86 }
87
88 Ok(DiskFormat::Unknown)
89}
90
91fn device_is_blank(
92 dev: &BlockDeviceHandle,
93 block0: &[u8; BLOCK_SIZE],
94) -> Result<Option<bool>, FileError> {
95 if !block0.iter().all(|&byte| byte == 0) {
96 return Ok(Some(false));
97 }
98 if dev.block_count() > BLANK_SCAN_MAX_BLOCKS {
99 return Ok(None);
100 }
101
102 let mut block = [0u8; BLOCK_SIZE];
103 for block_id in 1..dev.block_count() {
104 dev.read_block(block_id, &mut block)?;
105 if !block.iter().all(|&byte| byte == 0) {
106 return Ok(Some(false));
107 }
108 }
109 Ok(Some(true))
110}
111
112fn looks_like_gpt(dev: &BlockDeviceHandle) -> Result<bool, FileError> {
113 if dev.block_count() <= 1 {
114 return Ok(false);
115 }
116 let mut block1 = [0u8; BLOCK_SIZE];
117 dev.read_block(1, &mut block1)?;
118 Ok(block1[..8] == *b"EFI PART")
119}
120
121fn looks_like_ext(dev: &BlockDeviceHandle) -> Result<bool, FileError> {
122 if dev.block_count() <= 2 {
123 return Ok(false);
124 }
125 let mut block2 = [0u8; BLOCK_SIZE];
126 dev.read_block(2, &mut block2)?;
127 Ok(block2[56] == 0x53 && block2[57] == 0xEF)
130}
131
132fn looks_like_fat(block0: &[u8; BLOCK_SIZE]) -> bool {
133 let has_boot_signature = has_mbr_signature(block0);
134 let fat12_or_16 = block0[54..59] == *b"FAT12" || block0[54..59] == *b"FAT16";
135 let fat32 = block0[82..87] == *b"FAT32";
136 has_boot_signature && (fat12_or_16 || fat32)
137}
138
139fn has_mbr_signature(block0: &[u8; BLOCK_SIZE]) -> bool {
140 block0[510] == 0x55 && block0[511] == 0xAA
141}