Skip to main content
Version: 3.0.0-rc.2

Open FAT inside an MBR or GPT partition

A partition table and the filesystem inside it are separate layers. Read the table, choose a partition, then restrict all filesystem I/O to that partition's block range.

[dependencies]
anyhow = "1"
[dependencies.hadris]
version = "3.0.0-rc.2"
default-features = false
features = ["std", "sync", "detect", "part"]
use anyhow::{Context, Result};
use hadris::fs::sync::Volume;
use hadris::host::FileDevice;
use hadris::part;
use hadris::sync::AnyFs;

fn main() -> Result<()> {
let mut disk = FileDevice::open("disk.img")?;
let table = part::sync::read(&mut disk)?;
let partition = table.partition(0).context("the disk has no partitions")?;

let slice = part::sync::open(&mut disk, &partition)?;
// `MountError` holds the borrowed slice; keep only its error for `anyhow`.
let opened = hadris::sync::open(slice, hadris::host::mount_options())
.map_err(|err| err.into_error())?;
let AnyFs::Fat(fat) = opened else {
anyhow::bail!("the selected partition is not FAT");
};

let vol = Volume::new(fat);
for entry in vol.read_dir("/")? {
println!("{}", entry?.name().to_str().unwrap_or("?"));
}

Ok(())
}

part::sync::open returns a hadris_storage Partition of the disk: block 0 of the partition is the partition's first block, and requests past its end fail before they reach the disk. part::r#async::open does the same for async devices, and the table is read with part::r#async::read there.

Do not seek to the partition offset and then pass the unrestricted disk handle to a filesystem parser. Filesystem offsets are relative to its start, and an unbounded handle can allow corrupt metadata to address neighboring partitions.

The table is read with the disk device's block size, which must be the logical block size the table was written with. The common value is 512 bytes; wrap a 4Kn image in a StreamDevice with 4096-byte blocks.