moved low-level code into fat-bits subcrate

This commit is contained in:
Moritz Gmeiner 2025-07-27 13:17:04 +02:00
commit 026e56c94e
20 changed files with 186 additions and 46 deletions

3
.gitignore vendored
View file

@ -1,2 +1 @@
/target target/
/tests

10
Cargo.lock generated
View file

@ -42,7 +42,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "fat-rs" name = "fat-bits"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
@ -53,6 +53,14 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "fat-dump"
version = "0.1.0"
dependencies = [
"anyhow",
"fat-bits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"

View file

@ -1,16 +1,3 @@
[package] [workspace]
name = "fat-rs" resolver = "3"
version = "0.1.0" members = ["fat-bits", "fat-dump"]
edition = "2024"
[[bin]]
name = "dump"
path = "src/dump.rs"
[dependencies]
anyhow = "1.0.98"
bitflags = "2.9.1"
chrono = { version = "0.4.41", default-features = false, features = ["alloc", "std"] }
enum_dispatch = "0.3.13"
static_assertions = "1.1.0"
thiserror = "2.0.12"

2
fat-bits/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
/tests

130
fat-bits/Cargo.lock generated Normal file
View file

@ -0,0 +1,130 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"num-traits",
]
[[package]]
name = "enum_dispatch"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fat-rs"
version = "0.1.0"
dependencies = [
"anyhow",
"bitflags",
"chrono",
"enum_dispatch",
"static_assertions",
"thiserror",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"

15
fat-bits/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "fat-bits"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.98"
bitflags = "2.9.1"
chrono = { version = "0.4.41", default-features = false, features = [
"alloc",
"std",
] }
enum_dispatch = "0.3.13"
static_assertions = "1.1.0"
thiserror = "2.0.12"

View file

@ -22,7 +22,7 @@ impl Display for ExtBpb {
pub struct Bpb { pub struct Bpb {
fat_type: FatType, fat_type: FatType,
jmp_boot: [u8; 3], // jmp_boot: [u8; 3],
oem_name: [u8; 8], oem_name: [u8; 8],
bytes_per_sector: u16, bytes_per_sector: u16,
sectors_per_cluster: u8, sectors_per_cluster: u8,
@ -52,11 +52,11 @@ impl Display for Bpb {
writeln!(f, "")?; writeln!(f, "")?;
writeln!( // writeln!(
f, // f,
" jmp_boot: [{:#X}, {:#X}, {:#X}]", // " jmp_boot: [{:#X}, {:#X}, {:#X}]",
self.jmp_boot[0], self.jmp_boot[1], self.jmp_boot[2] // self.jmp_boot[0], self.jmp_boot[1], self.jmp_boot[2]
)?; // )?;
writeln!(f, " oem name: \"{}\"", self.oem_name_str().unwrap_or(""))?; writeln!(f, " oem name: \"{}\"", self.oem_name_str().unwrap_or(""))?;
writeln!(f, " bytes per sector: {}", self.bytes_per_sector())?; writeln!(f, " bytes per sector: {}", self.bytes_per_sector())?;
@ -90,7 +90,8 @@ impl Bpb {
pub fn load(bytes: &[u8]) -> anyhow::Result<Bpb> { pub fn load(bytes: &[u8]) -> anyhow::Result<Bpb> {
anyhow::ensure!(bytes.len() >= 512, "invalid BPB of len {}", bytes.len()); anyhow::ensure!(bytes.len() >= 512, "invalid BPB of len {}", bytes.len());
let jmp_boot = bytes[..3].try_into().unwrap(); // let jmp_boot = bytes[..3].try_into().unwrap();
let oem_name = bytes[3..][..8].try_into().unwrap(); let oem_name = bytes[3..][..8].try_into().unwrap();
let bytes_per_sector = load_u16_le(&bytes[11..][..2]); let bytes_per_sector = load_u16_le(&bytes[11..][..2]);
@ -145,7 +146,7 @@ impl Bpb {
let mut bpb = Bpb { let mut bpb = Bpb {
fat_type, fat_type,
jmp_boot, // jmp_boot,
oem_name, oem_name,
bytes_per_sector, bytes_per_sector,
sectors_per_cluster, sectors_per_cluster,

View file

@ -22,6 +22,7 @@ impl Date {
Ok(date) Ok(date)
} }
#[allow(dead_code)]
pub fn from_day_month_year(day: u8, month: u8, year: u16) -> anyhow::Result<Date> { pub fn from_day_month_year(day: u8, month: u8, year: u16) -> anyhow::Result<Date> {
anyhow::ensure!(day <= 31, "invalid day: {}", day); anyhow::ensure!(day <= 31, "invalid day: {}", day);
anyhow::ensure!(month <= 12, "invalid month: {}", month); anyhow::ensure!(month <= 12, "invalid month: {}", month);
@ -32,6 +33,7 @@ impl Date {
Ok(Date { repr }) Ok(Date { repr })
} }
#[allow(dead_code)]
pub fn from_system_time(time: SystemTime) -> anyhow::Result<Date> { pub fn from_system_time(time: SystemTime) -> anyhow::Result<Date> {
let datetime: DateTime<Utc> = time.into(); let datetime: DateTime<Utc> = time.into();
@ -73,6 +75,7 @@ impl Time {
Ok(time) Ok(time)
} }
#[allow(dead_code)]
pub fn from_seconds_minutes_hours(seconds: u8, minutes: u8, hours: u8) -> anyhow::Result<Time> { pub fn from_seconds_minutes_hours(seconds: u8, minutes: u8, hours: u8) -> anyhow::Result<Time> {
anyhow::ensure!(seconds <= 58 && seconds % 2 == 0, "invalid seconds: {}", seconds); anyhow::ensure!(seconds <= 58 && seconds % 2 == 0, "invalid seconds: {}", seconds);
anyhow::ensure!(minutes <= 59, "invalid minutes: {}", minutes); anyhow::ensure!(minutes <= 59, "invalid minutes: {}", minutes);
@ -83,6 +86,7 @@ impl Time {
Ok(Time { repr }) Ok(Time { repr })
} }
#[allow(dead_code)]
pub fn from_system_time(time: SystemTime) -> anyhow::Result<Time> { pub fn from_system_time(time: SystemTime) -> anyhow::Result<Time> {
let datetime: DateTime<Utc> = time.into(); let datetime: DateTime<Utc> = time.into();

View file

@ -5,7 +5,6 @@ use bitflags::bitflags;
use chrono::{NaiveDate, NaiveDateTime, TimeDelta}; use chrono::{NaiveDate, NaiveDateTime, TimeDelta};
use crate::datetime::{Date, Time}; use crate::datetime::{Date, Time};
use crate::dir;
use crate::utils::{load_u16_le, load_u32_le}; use crate::utils::{load_u16_le, load_u32_le};
bitflags! { bitflags! {

View file

@ -1,5 +1,6 @@
use crate::utils::load_u32_le; use crate::utils::load_u32_le;
#[allow(dead_code)]
pub struct FsInfo { pub struct FsInfo {
free_count: u32, free_count: u32,
next_free: u32, next_free: u32,

View file

@ -38,14 +38,6 @@ pub trait SliceLike {
impl SliceLike for &mut [u8] { impl SliceLike for &mut [u8] {
fn read_at_offset(&mut self, offset: u64, buf: &mut [u8]) -> std::io::Result<()> { fn read_at_offset(&mut self, offset: u64, buf: &mut [u8]) -> std::io::Result<()> {
// anyhow::ensure!(
// offset as usize + buf.len() <= self.len(),
// "reading {} bytes at offset {} is out of bounds for slice of len {}",
// buf.len(),
// offset,
// self.len()
// );
if offset as usize + buf.len() > self.len() { if offset as usize + buf.len() > self.len() {
return Err(std::io::Error::other(anyhow::anyhow!( return Err(std::io::Error::other(anyhow::anyhow!(
"reading {} bytes at offset {} is out of bounds for slice of len {}", "reading {} bytes at offset {} is out of bounds for slice of len {}",

8
fat-dump/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "fat-dump"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.98"
fat-bits = { version = "0.1.0", path = "../fat-bits" }

View file

@ -1,8 +1,6 @@
use std::io::Read; use fat_bits::dir::{DirIter, RegularDirEntry};
use fat_bits::fat::Fatty as _;
use fat_rs::dir::{DirIter, RegularDirEntry}; use fat_bits::{FatFs, SliceLike};
use fat_rs::fat::Fatty as _;
use fat_rs::{FatFs, SliceLike};
pub fn main() -> anyhow::Result<()> { pub fn main() -> anyhow::Result<()> {
let args = std::env::args(); let args = std::env::args();
@ -21,7 +19,7 @@ pub fn main() -> anyhow::Result<()> {
// println!("{}", bpb); // println!("{}", bpb);
let mut fat_fs = FatFs::load(file)?; let fat_fs = FatFs::load(file)?;
println!("{}", fat_fs.bpb()); println!("{}", fat_fs.bpb());
println!(); println!();
@ -35,10 +33,6 @@ pub fn main() -> anyhow::Result<()> {
* fat_fs.bpb().sectors_per_cluster() as usize * fat_fs.bpb().sectors_per_cluster() as usize
); );
for dir_entry in fat_fs.root_dir_iter() {
println!("{}", dir_entry);
}
println!(); println!();
println!(); println!();

BIN
tests/fat12.bin Normal file

Binary file not shown.

BIN
tests/fat16.bin Normal file

Binary file not shown.

BIN
tests/fat32.bin Normal file

Binary file not shown.