io_transform! {
use alloc::vec;
use crate::error::Result;
use crate::raw::{
DirEntryAttrFlags, RawBpb,
RawBpbExt16, RawBpbExt32, RawFileEntry, RawFsInfo,
};
use super::super::io::{Read, Seek, SeekFrom, Write};
use super::super::write::FatDateTime;
use super::super::fat_table::FatType;
use super::super::fs::{FSINFO_LEAD_SIG, FSINFO_STRUC_SIG, FSINFO_TRAIL_SIG};
use hadris_common::types::endian::{Endian, LittleEndian};
use hadris_common::types::number::{U16, U32};
use super::calc::FormatParams;
use super::options::FormatOptions;
pub async fn initialize_volume<DATA: Read + Write + Seek>(
data: &mut DATA,
options: &FormatOptions,
params: &FormatParams,
) -> Result<()> {
zero_reserved_area(data, params).await?;
write_boot_sector(data, options, params).await?;
if params.fat_type == FatType::Fat32 {
write_fsinfo(data, params).await?;
write_backup_boot_sector(data, options, params).await?;
}
initialize_fat_tables(data, params).await?;
initialize_root_directory(data, options, params).await?;
Ok(())
}
async fn zero_reserved_area<DATA: Write + Seek>(data: &mut DATA, params: &FormatParams) -> Result<()> {
let reserved_bytes = params.reserved_sectors as usize * params.sector_size;
data.seek(SeekFrom::Start(0)).await?;
let zeros = vec![0u8; params.sector_size.min(4096)];
let mut remaining = reserved_bytes;
while remaining > 0 {
let to_write = remaining.min(zeros.len());
data.write_all(&zeros[..to_write]).await?;
remaining -= to_write;
}
Ok(())
}
async fn write_boot_sector<DATA: Write + Seek>(
data: &mut DATA,
options: &FormatOptions,
params: &FormatParams,
) -> Result<()> {
data.seek(SeekFrom::Start(0)).await?;
let total_sectors_16 = if params.total_sectors < 0x10000 && params.fat_type != FatType::Fat32 {
params.total_sectors as u16
} else {
0
};
let total_sectors_32 = if total_sectors_16 == 0 {
params.total_sectors
} else {
0
};
let bpb = RawBpb {
jump: [0xEB, 0x58, 0x90], oem_name: *options.oem_name.as_bytes(),
bytes_per_sector: U16::<LittleEndian>::new(params.sector_size as u16),
sectors_per_cluster: params.sectors_per_cluster,
reserved_sector_count: U16::<LittleEndian>::new(params.reserved_sectors),
fat_count: params.fat_count,
root_entry_count: params.root_entry_count.to_le_bytes(),
total_sectors_16: total_sectors_16.to_le_bytes(),
media_type: params.media_type,
sectors_per_fat_16: if params.fat_type == FatType::Fat32 {
[0, 0]
} else {
(params.sectors_per_fat as u16).to_le_bytes()
},
sectors_per_track: 63u16.to_le_bytes(), num_heads: 255u16.to_le_bytes(), hidden_sector_count: params.hidden_sectors.to_le_bytes(),
total_sectors_32: total_sectors_32.to_le_bytes(),
};
data.write_all(bytemuck::bytes_of(&bpb)).await?;
let volume_id = options.volume_id.unwrap_or_else(generate_volume_id);
match params.fat_type {
FatType::Fat12 | FatType::Fat16 => {
let fs_type = if params.fat_type == FatType::Fat12 {
*b"FAT12 "
} else {
*b"FAT16 "
};
let bpb_ext16 = RawBpbExt16 {
drive_number: 0x80,
reserved1: 0,
ext_boot_signature: 0x29,
volume_id: volume_id.to_le_bytes(),
volume_label: *options.volume_label.as_bytes(),
fs_type,
padding1: [0; 448],
signature_word: 0xAA55u16.to_le_bytes(),
};
data.write_all(bytemuck::bytes_of(&bpb_ext16)).await?;
}
FatType::Fat32 => {
let bpb_ext32 = RawBpbExt32 {
sectors_per_fat_32: U32::<LittleEndian>::new(params.sectors_per_fat),
ext_flags: [0, 0], version: [0, 0], root_cluster: U32::<LittleEndian>::new(2), fs_info_sector: U16::<LittleEndian>::new(1),
boot_sector: 6u16.to_le_bytes(), reserved: [0; 12],
drive_number: 0x80,
reserved1: 0,
ext_boot_signature: 0x29,
volume_id: volume_id.to_le_bytes(),
volume_label: *options.volume_label.as_bytes(),
fs_type: *b"FAT32 ",
padding1: [0; 420],
signature_word: U16::<LittleEndian>::new(0xAA55),
};
data.write_all(bytemuck::bytes_of(&bpb_ext32)).await?;
}
}
Ok(())
}
async fn write_fsinfo<DATA: Write + Seek>(data: &mut DATA, params: &FormatParams) -> Result<()> {
data.seek(SeekFrom::Start(params.sector_size as u64)).await?;
let free_count = params.cluster_count.saturating_sub(1);
let fsinfo = RawFsInfo {
signature: FSINFO_LEAD_SIG.to_le_bytes(),
reserved1: [0; 480],
structure_signature: FSINFO_STRUC_SIG.to_le_bytes(),
free_count: U32::<LittleEndian>::new(free_count),
next_free: U32::<LittleEndian>::new(3), reserved2: [0; 12],
trail_signature: U32::<LittleEndian>::new(FSINFO_TRAIL_SIG),
};
data.write_all(bytemuck::bytes_of(&fsinfo)).await?;
let written = core::mem::size_of::<RawFsInfo>();
let padding = params.sector_size - written;
if padding > 0 {
data.write_all(&vec![0u8; padding]).await?;
}
Ok(())
}
async fn write_backup_boot_sector<DATA: Read + Write + Seek>(
data: &mut DATA,
_options: &FormatOptions,
params: &FormatParams,
) -> Result<()> {
data.seek(SeekFrom::Start(0)).await?;
let mut boot_sector = vec![0u8; params.sector_size];
data.read_exact(&mut boot_sector).await?;
data.seek(SeekFrom::Start(6 * params.sector_size as u64)).await?;
data.write_all(&boot_sector).await?;
data.seek(SeekFrom::Start(params.sector_size as u64)).await?;
let mut fsinfo_sector = vec![0u8; params.sector_size];
data.read_exact(&mut fsinfo_sector).await?;
data.seek(SeekFrom::Start(7 * params.sector_size as u64)).await?;
data.write_all(&fsinfo_sector).await?;
Ok(())
}
async fn initialize_fat_tables<DATA: Write + Seek>(data: &mut DATA, params: &FormatParams) -> Result<()> {
let fat_start = params.reserved_sectors as u64 * params.sector_size as u64;
let fat_bytes = params.sectors_per_fat as usize * params.sector_size;
for fat_idx in 0..params.fat_count {
let fat_offset = fat_start + (fat_idx as u64 * fat_bytes as u64);
data.seek(SeekFrom::Start(fat_offset)).await?;
let zeros = vec![0u8; fat_bytes];
data.write_all(&zeros).await?;
data.seek(SeekFrom::Start(fat_offset)).await?;
match params.fat_type {
FatType::Fat12 => {
let entry0 = params.media_type as u16 | 0xF00;
let entry1: u16 = 0xFFF;
let bytes = [
entry0 as u8,
((entry0 >> 8) as u8 & 0x0F) | ((entry1 << 4) as u8),
(entry1 >> 4) as u8,
];
data.write_all(&bytes).await?;
}
FatType::Fat16 => {
let entry0 = 0xFF00u16 | params.media_type as u16;
let entry1 = 0xFFFFu16;
data.write_all(&entry0.to_le_bytes()).await?;
data.write_all(&entry1.to_le_bytes()).await?;
}
FatType::Fat32 => {
let entry0 = 0x0FFFFF00u32 | params.media_type as u32;
let entry1 = 0x0FFFFFFFu32;
let entry2 = 0x0FFFFFF8u32; data.write_all(&entry0.to_le_bytes()).await?;
data.write_all(&entry1.to_le_bytes()).await?;
data.write_all(&entry2.to_le_bytes()).await?;
}
}
}
Ok(())
}
async fn initialize_root_directory<DATA: Write + Seek>(
data: &mut DATA,
options: &FormatOptions,
params: &FormatParams,
) -> Result<()> {
let root_dir_offset = match params.fat_type {
FatType::Fat12 | FatType::Fat16 => {
params.reserved_sectors as u64 * params.sector_size as u64
+ params.fat_count as u64
* params.sectors_per_fat as u64
* params.sector_size as u64
}
FatType::Fat32 => {
params.data_start_sector as u64 * params.sector_size as u64
}
};
data.seek(SeekFrom::Start(root_dir_offset)).await?;
let root_dir_size = match params.fat_type {
FatType::Fat12 | FatType::Fat16 => params.root_entry_count as usize * 32,
FatType::Fat32 => params.sectors_per_cluster as usize * params.sector_size,
};
let zeros = vec![0u8; root_dir_size];
data.write_all(&zeros).await?;
if options.volume_label.as_bytes() != b"NO NAME " {
data.seek(SeekFrom::Start(root_dir_offset)).await?;
let now = FatDateTime::now();
let (date, time, _) = now.to_raw();
let label_entry = RawFileEntry {
name: *options.volume_label.as_bytes(),
attributes: DirEntryAttrFlags::VOLUME_ID.bits(),
reserved: 0,
creation_time_tenth: 0,
creation_time: time.to_le_bytes(),
creation_date: date.to_le_bytes(),
last_access_date: date.to_le_bytes(),
first_cluster_high: U16::<LittleEndian>::new(0),
last_write_time: time.to_le_bytes(),
last_write_date: date.to_le_bytes(),
first_cluster_low: U16::<LittleEndian>::new(0),
size: U32::<LittleEndian>::new(0),
};
data.write_all(bytemuck::bytes_of(&label_entry)).await?;
}
Ok(())
}
fn generate_volume_id() -> u32 {
#[cfg(feature = "std")]
{
use std::time::{SystemTime, UNIX_EPOCH};
let duration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default();
let secs = duration.as_secs() as u32;
let nanos = duration.subsec_nanos();
secs ^ nanos
}
#[cfg(not(feature = "std"))]
{
0x12345678
}
}
}