morpheus_network/transfer/disk/fat32.rs
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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
//! FAT32 formatter for post-EBS.
//!
//! Allocation-free FAT32 filesystem formatting using stack buffers.
//! Creates minimal FAT32 filesystem suitable for ISO chunk storage.
use gpt_disk_io::BlockIo;
use gpt_disk_types::{Lba, LbaLe};
use super::types::{DiskError, DiskResult, SECTOR_SIZE};
/// FAT32 filesystem formatter
pub struct Fat32Formatter;
impl Fat32Formatter {
/// Format a partition as FAT32
///
/// Creates minimal FAT32 with:
/// - Boot sector at LBA 0
/// - FSInfo at LBA 1
/// - Backup boot sector at LBA 6
/// - Two FAT tables starting at LBA 32
/// - Root directory at data start
pub fn format<B: BlockIo>(
block_io: &mut B,
partition_start_lba: u64,
partition_sectors: u64,
volume_label: &str,
) -> DiskResult<Fat32Info> {
// Validate partition size (min ~65MB, max ~2TB for FAT32)
if partition_sectors < 133120 {
return Err(DiskError::InvalidSize);
}
let total_sectors = partition_sectors as u32;
let reserved_sectors = 32u16;
let sectors_per_cluster = Self::optimal_cluster_size(total_sectors);
// Calculate FAT size
let fat_size =
Self::calculate_fat_size(total_sectors, reserved_sectors, sectors_per_cluster);
// Calculate cluster count
let fat_sectors = fat_size * 2; // Two FAT copies
let data_sectors = total_sectors - reserved_sectors as u32 - fat_sectors;
let cluster_count = data_sectors / sectors_per_cluster as u32;
// Build and write boot sector
let boot_sector = Self::build_boot_sector(
total_sectors,
fat_size,
sectors_per_cluster,
partition_start_lba as u32,
volume_label,
);
let lba = Lba(partition_start_lba);
block_io
.write_blocks(lba, &boot_sector)
.map_err(|_| DiskError::IoError)?;
// Write FSInfo sector
let fsinfo = Self::build_fsinfo(cluster_count - 1);
let fsinfo_lba = Lba(partition_start_lba + 1);
block_io
.write_blocks(fsinfo_lba, &fsinfo)
.map_err(|_| DiskError::IoError)?;
// Write backup boot sector
let backup_lba = Lba(partition_start_lba + 6);
block_io
.write_blocks(backup_lba, &boot_sector)
.map_err(|_| DiskError::IoError)?;
// Initialize first FAT sector (reserved entries + root cluster)
let mut fat_sector = [0u8; SECTOR_SIZE];
// Entry 0: Media type marker (0x0FFFFFF8)
fat_sector[0] = 0xF8;
fat_sector[1] = 0xFF;
fat_sector[2] = 0xFF;
fat_sector[3] = 0x0F;
// Entry 1: End-of-chain marker
fat_sector[4] = 0xFF;
fat_sector[5] = 0xFF;
fat_sector[6] = 0xFF;
fat_sector[7] = 0xFF;
// Entry 2: Root directory cluster (end-of-chain)
fat_sector[8] = 0xFF;
fat_sector[9] = 0xFF;
fat_sector[10] = 0xFF;
fat_sector[11] = 0x0F;
// Write first FAT sector
let fat1_lba = Lba(partition_start_lba + reserved_sectors as u64);
block_io
.write_blocks(fat1_lba, &fat_sector)
.map_err(|_| DiskError::IoError)?;
// Write second FAT sector
let fat2_lba = Lba(partition_start_lba + reserved_sectors as u64 + fat_size as u64);
block_io
.write_blocks(fat2_lba, &fat_sector)
.map_err(|_| DiskError::IoError)?;
// Zero out root directory (first cluster of data area)
let data_start = partition_start_lba + reserved_sectors as u64 + (fat_size * 2) as u64;
let empty_sector = [0u8; SECTOR_SIZE];
for i in 0..sectors_per_cluster as u64 {
block_io
.write_blocks(Lba(data_start + i), &empty_sector)
.map_err(|_| DiskError::IoError)?;
}
block_io.flush().map_err(|_| DiskError::IoError)?;
Ok(Fat32Info {
reserved_sectors,
sectors_per_cluster,
fat_size,
data_start_lba: data_start,
cluster_count,
})
}
/// Calculate optimal cluster size for partition
fn optimal_cluster_size(total_sectors: u32) -> u8 {
// Based on partition size, choose appropriate cluster size
// to stay within FAT32 limits (max ~268M clusters)
let size_mb = total_sectors / 2048; // Approx MB
match size_mb {
0..=512 => 1, // <=512MB: 512B clusters
513..=8192 => 8, // <=8GB: 4KB clusters
8193..=16384 => 16, // <=16GB: 8KB clusters
16385..=32768 => 32, // <=32GB: 16KB clusters
_ => 64, // >32GB: 32KB clusters
}
}
/// Calculate FAT size in sectors
fn calculate_fat_size(total_sectors: u32, reserved: u16, spc: u8) -> u32 {
// Microsoft formula for FAT32 FAT size calculation
let tmp1 = total_sectors - reserved as u32;
let tmp2 = (256 * spc as u32) + 2;
(tmp1 + tmp2 - 1) / tmp2
}
/// Build FAT32 boot sector
fn build_boot_sector(
total_sectors: u32,
fat_size: u32,
spc: u8,
hidden_sectors: u32,
label: &str,
) -> [u8; SECTOR_SIZE] {
let mut bs = [0u8; SECTOR_SIZE];
// Jump instruction
bs[0] = 0xEB;
bs[1] = 0x58;
bs[2] = 0x90;
// OEM name
bs[3..11].copy_from_slice(b"MORPHEUS");
// BPB (BIOS Parameter Block)
bs[11..13].copy_from_slice(&512u16.to_le_bytes()); // Bytes per sector
bs[13] = spc; // Sectors per cluster
bs[14..16].copy_from_slice(&32u16.to_le_bytes()); // Reserved sectors
bs[16] = 2; // Number of FATs
bs[17..19].copy_from_slice(&0u16.to_le_bytes()); // Root entries (0 for FAT32)
bs[19..21].copy_from_slice(&0u16.to_le_bytes()); // Total sectors 16 (0 for FAT32)
bs[21] = 0xF8; // Media type
bs[22..24].copy_from_slice(&0u16.to_le_bytes()); // FAT size 16 (0 for FAT32)
bs[24..26].copy_from_slice(&63u16.to_le_bytes()); // Sectors per track
bs[26..28].copy_from_slice(&255u16.to_le_bytes()); // Number of heads
bs[28..32].copy_from_slice(&hidden_sectors.to_le_bytes());
bs[32..36].copy_from_slice(&total_sectors.to_le_bytes());
// FAT32 specific
bs[36..40].copy_from_slice(&fat_size.to_le_bytes()); // FAT size 32
bs[40..42].copy_from_slice(&0u16.to_le_bytes()); // Ext flags
bs[42..44].copy_from_slice(&0u16.to_le_bytes()); // FS version
bs[44..48].copy_from_slice(&2u32.to_le_bytes()); // Root cluster
bs[48..50].copy_from_slice(&1u16.to_le_bytes()); // FSInfo sector
bs[50..52].copy_from_slice(&6u16.to_le_bytes()); // Backup boot sector
// Reserved[12] at 52-63 already zero
bs[64] = 0x80; // Drive number
bs[65] = 0; // Reserved
bs[66] = 0x29; // Boot signature
bs[67..71].copy_from_slice(&0x12345678u32.to_le_bytes()); // Volume ID
// Volume label (11 bytes, space-padded)
let label_bytes = label.as_bytes();
let mut label_buf = [b' '; 11];
let copy_len = label_bytes.len().min(11);
label_buf[..copy_len].copy_from_slice(&label_bytes[..copy_len]);
bs[71..82].copy_from_slice(&label_buf);
// FS type
bs[82..90].copy_from_slice(b"FAT32 ");
// Boot sector signature
bs[510] = 0x55;
bs[511] = 0xAA;
bs
}
/// Build FSInfo sector
fn build_fsinfo(free_clusters: u32) -> [u8; SECTOR_SIZE] {
let mut fs = [0u8; SECTOR_SIZE];
// Lead signature
fs[0..4].copy_from_slice(&0x41615252u32.to_le_bytes());
// Structure signature
fs[484..488].copy_from_slice(&0x61417272u32.to_le_bytes());
// Free cluster count
fs[488..492].copy_from_slice(&free_clusters.to_le_bytes());
// Next free cluster
fs[492..496].copy_from_slice(&3u32.to_le_bytes());
// Trail signature
fs[508..512].copy_from_slice(&0xAA550000u32.to_le_bytes());
fs
}
}
/// Information about formatted FAT32 filesystem
#[derive(Debug, Clone, Copy)]
pub struct Fat32Info {
/// Number of reserved sectors
pub reserved_sectors: u16,
/// Sectors per cluster
pub sectors_per_cluster: u8,
/// FAT size in sectors
pub fat_size: u32,
/// First LBA of data area
pub data_start_lba: u64,
/// Total cluster count
pub cluster_count: u32,
}