///|
/// On-disk FAT kind selected from cluster count, per the Microsoft FAT spec.
pub(all) enum FatKind {
Fat12
Fat16
Fat32
} derive(Eq, Debug)
///|
pub fn FatKind::name(self : FatKind) -> String {
match self {
Fat12 => "FAT12"
Fat16 => "FAT16"
Fat32 => "FAT32"
}
}
///|
pub fn FatKind::bits(self : FatKind) -> Int {
match self {
Fat12 => 12
Fat16 => 16
Fat32 => 32
}
}
///|
pub fn FatKind::eoc_value(self : FatKind) -> Int {
match self {
Fat12 => 0xFFF
Fat16 => 0xFFFF
Fat32 => 0x0FFFFFFF
}
}
///|
pub fn FatKind::bad_value(self : FatKind) -> Int {
match self {
Fat12 => 0xFF7
Fat16 => 0xFFF7
Fat32 => 0x0FFFFFF7
}
}
///|
pub fn FatKind::is_eoc(self : FatKind, value : Int) -> Bool {
match self {
Fat12 => value >= 0xFF8
Fat16 => value >= 0xFFF8
Fat32 => (value & 0x0FFFFFFF) >= 0x0FFFFFF8
}
}
///|
pub fn FatKind::is_bad(self : FatKind, value : Int) -> Bool {
match self {
Fat12 => (value & 0x0FFF) == 0xFF7
Fat16 => (value & 0xFFFF) == 0xFFF7
Fat32 => (value & 0x0FFFFFFF) == 0x0FFFFFF7
}
}
///|
/// Cached geometry of a mounted or freshly formatted volume.
pub(all) struct Layout {
kind : FatKind
bytes_per_sector : Int
sectors_per_cluster : Int
reserved_sectors : Int
fat_count : Int
fat_sectors : Int
root_entries : Int
root_dir_sectors : Int
first_fat_sector : Int
first_root_sector : Int
first_data_sector : Int
cluster_count : Int
root_cluster : Int
total_sectors : Int
media : Byte
volume_id : Int
label : String
fsinfo_sector : Int
backup_boot_sector : Int
} derive(Debug)
///|
pub fn Layout::bytes_per_cluster(self : Layout) -> Int {
self.bytes_per_sector * self.sectors_per_cluster
}
///|
pub fn Layout::fat_bytes(self : Layout) -> Int {
self.fat_sectors * self.bytes_per_sector
}
///|
pub fn Layout::cluster_sector(
self : Layout,
cluster : Int,
) -> Result[Int, FatError] {
if cluster < 2 || cluster > self.cluster_count + 1 {
Err(BadCluster(cluster))
} else {
Ok(self.first_data_sector + (cluster - 2) * self.sectors_per_cluster)
}
}
///|
pub fn Layout::byte_offset(self : Layout, sector : Int) -> Int {
sector * self.bytes_per_sector
}
///|
fn fat_kind_from_clusters(cluster_count : Int) -> FatKind {
if cluster_count < 4085 {
Fat12
} else if cluster_count < 65525 {
Fat16
} else {
Fat32
}
}
///|
fn decode_label11(data : Array[Byte], offset : Int) -> String {
match slice_bytes(data, offset, 11) {
Err(_) => ""
Ok(raw) => ascii_from_padded(raw)
}
}
///|
fn decode_oem(data : Array[Byte]) -> String {
match slice_bytes(data, 3, 8) {
Err(_) => ""
Ok(raw) => ascii_from_padded(raw)
}
}
///|
pub fn parse_layout(data : Array[Byte]) -> Result[Layout, FatError] {
if data.length() < 512 {
return Err(Truncated("boot", 512, data.length()))
}
let sig0 = data[510]
let sig1 = data[511]
if sig0 != b'\x55' || sig1 != b'\xAA' {
return Err(InvalidBoot("missing 0x55AA signature"))
}
let bytes_per_sector = match u16le_at(data, 11) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let sectors_per_cluster = match u8_at(data, 13) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let reserved_sectors = match u16le_at(data, 14) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let fat_count = match u8_at(data, 16) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let root_entries = match u16le_at(data, 17) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let total_sectors_16 = match u16le_at(data, 19) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let media = data[21]
let fat_sectors_16 = match u16le_at(data, 22) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let total_sectors_32 = match u32le_at(data, 32) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
if !power_of_two(bytes_per_sector) ||
bytes_per_sector < 512 ||
bytes_per_sector > 4096 {
return Err(
InvalidBoot("bytes per sector must be 512..4096 and a power of two"),
)
}
if !power_of_two(sectors_per_cluster) || sectors_per_cluster > 128 {
return Err(InvalidBoot("sectors per cluster must be a power of two <= 128"))
}
if reserved_sectors <= 0 {
return Err(InvalidBoot("reserved sector count must be positive"))
}
if fat_count <= 0 {
return Err(InvalidBoot("FAT count must be positive"))
}
let total_sectors = if total_sectors_16 != 0 {
total_sectors_16
} else {
total_sectors_32
}
if total_sectors <= 0 {
return Err(InvalidBoot("total sector count is zero"))
}
let fat_sectors_32 = if fat_sectors_16 == 0 {
match u32le_at(data, 36) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
} else {
0
}
let fat_sectors = if fat_sectors_16 != 0 {
fat_sectors_16
} else {
fat_sectors_32
}
if fat_sectors <= 0 {
return Err(InvalidBoot("FAT size is zero"))
}
let root_dir_sectors = (root_entries * 32 + bytes_per_sector - 1) /
bytes_per_sector
let first_fat_sector = reserved_sectors
let first_root_sector = reserved_sectors + fat_count * fat_sectors
let first_data_sector = first_root_sector + root_dir_sectors
if first_data_sector >= total_sectors {
return Err(InvalidBoot("data region starts past the end of the volume"))
}
let cluster_count = (total_sectors - first_data_sector) / sectors_per_cluster
if cluster_count <= 0 {
return Err(InvalidBoot("no data clusters"))
}
let kind = fat_kind_from_clusters(cluster_count)
let root_cluster = match kind {
Fat32 => {
let value = match u32le_at(data, 44) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
if value < 2 {
2
} else {
value
}
}
_ => 0
}
let fsinfo_sector = match kind {
Fat32 =>
match u16le_at(data, 48) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
_ => 0
}
let backup_boot_sector = match kind {
Fat32 =>
match u16le_at(data, 50) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
_ => 0
}
let ext_offset = match kind {
Fat32 => 66
_ => 38
}
let ext_sig = match u8_at(data, ext_offset) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
let volume_id = if ext_sig == 0x29 {
match u32le_at(data, ext_offset + 1) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
} else {
0
}
let label = if ext_sig == 0x29 {
decode_label11(data, ext_offset + 5)
} else {
""
}
let needed = total_sectors * bytes_per_sector
if data.length() < needed {
return Err(Truncated("volume", needed, data.length()))
}
let _ = decode_oem(data)
Ok({
kind,
bytes_per_sector,
sectors_per_cluster,
reserved_sectors,
fat_count,
fat_sectors,
root_entries,
root_dir_sectors,
first_fat_sector,
first_root_sector,
first_data_sector,
cluster_count,
root_cluster,
total_sectors,
media,
volume_id,
label,
fsinfo_sector,
backup_boot_sector,
})
}
///|
fn write_label11(
data : Array[Byte],
offset : Int,
label : String,
) -> Result[Unit, FatError] {
let padded = match ascii_pad11(label) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
blit_bytes(data, offset, padded)
}
///|
fn write_ascii_field(
data : Array[Byte],
offset : Int,
text : String,
width : Int,
) -> Result[Unit, FatError] {
match fill_bytes(data, offset, width, b' ') {
Err(err) => return Err(err)
Ok(_) => ()
}
let mut i = 0
while i < text.length() && i < width {
let code = text[i].to_int()
if code < 32 || code > 126 {
return Err(InvalidName(text))
}
data[offset + i] = code.to_byte()
i = i + 1
}
Ok(())
}
///|
fn write_boot_sector(
data : Array[Byte],
layout : Layout,
) -> Result[Unit, FatError] {
let bps = layout.bytes_per_sector
if data.length() < bps {
return Err(Truncated("boot", bps, data.length()))
}
match fill_bytes(data, 0, bps, b'\x00') {
Err(err) => return Err(err)
Ok(_) => ()
}
match layout.kind {
Fat32 => {
data[0] = b'\xEB'
data[1] = b'\x58'
data[2] = b'\x90'
}
_ => {
data[0] = b'\xEB'
data[1] = b'\x3C'
data[2] = b'\x90'
}
}
match write_ascii_field(data, 3, "MSDOS5.0", 8) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 11, bps) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 13, layout.sectors_per_cluster) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 14, layout.reserved_sectors) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 16, layout.fat_count) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 17, layout.root_entries) {
Err(err) => return Err(err)
Ok(_) => ()
}
if layout.total_sectors < 0x10000 && layout.kind != Fat32 {
match put_u16le(data, 19, layout.total_sectors) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, 32, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
} else {
match put_u16le(data, 19, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, 32, layout.total_sectors) {
Err(err) => return Err(err)
Ok(_) => ()
}
}
match put_u8(data, 21, layout.media.to_int()) {
Err(err) => return Err(err)
Ok(_) => ()
}
match layout.kind {
Fat32 =>
match put_u16le(data, 22, 0) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
_ =>
match put_u16le(data, 22, layout.fat_sectors) {
Err(err) => return Err(err)
Ok(ok_val) => ok_val
}
}
match put_u16le(data, 24, 63) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 26, 255) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, 28, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
match layout.kind {
Fat32 => {
match put_u32le(data, 36, layout.fat_sectors) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 40, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 42, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, 44, layout.root_cluster) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 48, layout.fsinfo_sector) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u16le(data, 50, layout.backup_boot_sector) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 64, 0x80) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 65, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 66, 0x29) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, 67, layout.volume_id) {
Err(err) => return Err(err)
Ok(_) => ()
}
match write_label11(data, 71, layout.label) {
Err(err) => return Err(err)
Ok(_) => ()
}
match write_ascii_field(data, 82, "FAT32", 8) {
Err(err) => return Err(err)
Ok(_) => ()
}
}
Fat16 => {
match put_u8(data, 36, 0x80) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 37, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 38, 0x29) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, 39, layout.volume_id) {
Err(err) => return Err(err)
Ok(_) => ()
}
match write_label11(data, 43, layout.label) {
Err(err) => return Err(err)
Ok(_) => ()
}
match write_ascii_field(data, 54, "FAT16", 8) {
Err(err) => return Err(err)
Ok(_) => ()
}
}
Fat12 => {
match put_u8(data, 36, 0x00) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 37, 0) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u8(data, 38, 0x29) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, 39, layout.volume_id) {
Err(err) => return Err(err)
Ok(_) => ()
}
match write_label11(data, 43, layout.label) {
Err(err) => return Err(err)
Ok(_) => ()
}
match write_ascii_field(data, 54, "FAT12", 8) {
Err(err) => return Err(err)
Ok(_) => ()
}
}
}
data[bps - 2] = b'\x55'
data[bps - 1] = b'\xAA'
Ok(())
}
///|
fn write_fsinfo(
data : Array[Byte],
layout : Layout,
free_count : Int,
next_free : Int,
) -> Result[Unit, FatError] {
if layout.kind != Fat32 || layout.fsinfo_sector <= 0 {
return Ok(())
}
let offset = layout.fsinfo_sector * layout.bytes_per_sector
match fill_bytes(data, offset, layout.bytes_per_sector, b'\x00') {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, offset, 0x41615252) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, offset + 484, 0x61417272) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, offset + 488, free_count) {
Err(err) => return Err(err)
Ok(_) => ()
}
match put_u32le(data, offset + 492, next_free) {
Err(err) => return Err(err)
Ok(_) => ()
}
if offset + 511 >= data.length() {
return Err(Truncated("fsinfo", offset + 512, data.length()))
}
data[offset + 508] = b'\x00'
data[offset + 509] = b'\x00'
data[offset + 510] = b'\x55'
data[offset + 511] = b'\xAA'
Ok(())
}