///|
/// Parameters used to create a new in-memory FAT volume.
pub(all) struct FormatOptions {
kind : FatKind
bytes_per_sector : Int
total_sectors : Int
volume_label : String
volume_id : Int
} derive(Debug)
///|
pub fn floppy_1440_options() -> FormatOptions {
{
kind: Fat12,
bytes_per_sector: 512,
total_sectors: 2880,
volume_label: "MOONFAT",
volume_id: 0x4D4F4F4E,
}
}
///|
pub fn small_fat16_options() -> FormatOptions {
{
kind: Fat16,
bytes_per_sector: 512,
total_sectors: 4163,
volume_label: "MOONFAT",
volume_id: 0x4D4F4F4E,
}
}
///|
pub fn min_fat32_options() -> FormatOptions {
{
kind: Fat32,
bytes_per_sector: 512,
total_sectors: 66581,
volume_label: "MOONFAT",
volume_id: 0x4D4F4F4E,
}
}
///|
fn default_reserved(kind : FatKind) -> Int {
match kind {
Fat32 => 32
_ => 1
}
}
///|
fn default_root_entries(kind : FatKind) -> Int {
match kind {
Fat12 => 224
Fat16 => 512
Fat32 => 0
}
}
///|
fn default_media(kind : FatKind, total_sectors : Int) -> Byte {
if kind == Fat12 && total_sectors == 2880 {
b'\xF0'
} else {
b'\xF8'
}
}
///|
fn fat_sectors_for(
kind : FatKind,
clusters : Int,
bytes_per_sector : Int,
) -> Int {
let entries = clusters + 2
let bits = kind.bits()
let bytes = (entries * bits + 7) / 8
(bytes + bytes_per_sector - 1) / bytes_per_sector
}
///|
fn compute_layout(options : FormatOptions) -> Result[Layout, FatError] {
if !power_of_two(options.bytes_per_sector) || options.bytes_per_sector < 512 {
return Err(InvalidBoot("bytes per sector must be a power of two >= 512"))
}
if options.total_sectors <= 0 {
return Err(InvalidBoot("total sector count must be positive"))
}
let reserved = default_reserved(options.kind)
let root_entries = default_root_entries(options.kind)
let fat_count = 2
let mut spc = 1
let mut loops = 0
while loops < 8 {
let root_dir_sectors = (root_entries * 32 + options.bytes_per_sector - 1) /
options.bytes_per_sector
let mut fat_sectors = 1
let mut stable = false
let mut cluster_count = 0
let mut inner = 0
while inner < 16 {
let first_data = reserved + fat_count * fat_sectors + root_dir_sectors
if options.total_sectors <= first_data {
break
}
cluster_count = (options.total_sectors - first_data) / spc
let next_fat = fat_sectors_for(
options.kind,
cluster_count,
options.bytes_per_sector,
)
if next_fat == fat_sectors {
stable = true
break
}
fat_sectors = next_fat
inner = inner + 1
}
if stable && cluster_count > 0 {
let detected = fat_kind_from_clusters(cluster_count)
if detected == options.kind {
let root_dir_sectors = (
root_entries * 32 + options.bytes_per_sector - 1
) /
options.bytes_per_sector
let first_fat_sector = reserved
let first_root_sector = reserved + fat_count * fat_sectors
let first_data_sector = first_root_sector + root_dir_sectors
let label = if options.volume_label.length() == 0 {
"NO NAME"
} else {
options.volume_label
}
return Ok({
kind: options.kind,
bytes_per_sector: options.bytes_per_sector,
sectors_per_cluster: spc,
reserved_sectors: reserved,
fat_count,
fat_sectors,
root_entries,
root_dir_sectors,
first_fat_sector,
first_root_sector,
first_data_sector,
cluster_count,
root_cluster: match options.kind {
Fat32 => 2
_ => 0
},
total_sectors: options.total_sectors,
media: default_media(options.kind, options.total_sectors),
volume_id: options.volume_id,
label,
fsinfo_sector: match options.kind {
Fat32 => 1
_ => 0
},
backup_boot_sector: match options.kind {
Fat32 => 6
_ => 0
},
})
}
}
spc = spc * 2
loops = loops + 1
}
Err(
InvalidBoot(
"cannot build a \{options.kind.name()} layout for \{options.total_sectors} sectors",
),
)
}
///|
pub fn format_volume(options : FormatOptions) -> Result[Volume, FatError] {
let layout = match compute_layout(options) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let bytes = layout.total_sectors * layout.bytes_per_sector
let data = Array::make(bytes, b'\x00')
match write_boot_sector(data, layout) {
Err(err) => return Err(err)
Ok(_) => ()
}
match layout.kind {
Fat32 => {
let backup = layout.backup_boot_sector * layout.bytes_per_sector
for i = 0; i < layout.bytes_per_sector; i = i + 1 {
data[backup + i] = data[i]
}
}
_ => ()
}
match init_fat_tables(data, layout) {
Err(err) => return Err(err)
Ok(_) => ()
}
match layout.kind {
Fat32 => {
let root_sector = match layout.cluster_sector(layout.root_cluster) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
match
fill_bytes(
data,
root_sector * layout.bytes_per_sector,
layout.bytes_per_cluster(),
b'\x00',
) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
match write_fsinfo(data, layout, layout.cluster_count - 1, 3) {
Err(err) => return Err(err)
Ok(_) => ()
}
}
_ =>
match
fill_bytes(
data,
layout.first_root_sector * layout.bytes_per_sector,
layout.root_dir_sectors * layout.bytes_per_sector,
b'\x00',
) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
}
if layout.label.length() > 0 && layout.label != "NO NAME" {
match
create_dir_item(
data,
layout,
root_cluster_of(layout),
layout.label,
ATTR_VOLUME_ID,
0,
0,
) {
Err(err) => return Err(err)
Ok(_) => ()
}
}
Ok({ data, layout })
}
///|
pub fn format_floppy() -> Result[Volume, FatError] {
format_volume(floppy_1440_options())
}
///|
pub fn format_fat16_small() -> Result[Volume, FatError] {
format_volume(small_fat16_options())
}
///|
pub fn format_fat32_min() -> Result[Volume, FatError] {
format_volume(min_fat32_options())
}