A no_std + alloc, read-only ZFS pool reader for UEFI bootloaders, from
Lamco Development LLC.
lamzfs imports a ZFS pool over a plain byte source, walks to one dataset, and
reads its files: Enough for a bootloader to find a kernel and initramfs without
the OpenZFS kernel module, without std, and without a GPL UEFI driver. The
motivating target is Ubuntu and Debian root-on-ZFS, which keep a simple bpool
boot pool. Read-only by construction: There is no write path and no
BlockWrite.
- Website: LamBoot, the bootloader lamzfs is maintained for
- Documentation: docs.rs/lamzfs and the LamBoot docs
- Repository: github.com/lamco-admin/lamzfs
- Company: Lamco Development LLC
- Topologies: One top-level vdev: A single disk, a mirror, or a single-parity RAID-Z1 (including degraded reads that rebuild a missing or corrupt column from parity). The active uberblock is chosen from all four labels of every member, so a mirror member that missed the last writes can't win.
- Pools as installers create them: Proxmox VE's (every feature enabled),
Ubuntu's
bpooland itsrpoolwithdnodesize=auto,xattr=sa,acltype=posixaclandnormalization=formD. - Compression:
off,zle,lzjb,lz4,gzipandzstd(each a feature). - Checksums: Fletcher-2/4, SHA-256, SHA-512 (OpenZFS's SHA-512/256) and BLAKE3 (keyed with the pool's checksum salt, as OpenZFS does).
- The full read walk: The vdev label, uberblock, MOS, DSL, dataset, ZPL, ZAP
directories (micro and fat), names over 255 bytes (
longname), large dnodes, large blocks, and the indirect block-pointer tree (sparse-file aware).
Everything else fails with a typed error and a stable token
(Error::token),
never a wrong read:
- Active read features it doesn't implement, checked in the label before
the MOS is read and in the MOS
features_for_readset after:raidz_expansion,device_removal,draid,dynamic_gang_header,redacted_datasets,redaction_list_spill, and any feature it doesn't know (feat_unknown_read_incompat). - Several top-level vdevs (
topo_multi_toplevel): A stripe such as Proxmox's RAID0, or a pool with log or special vdevs. - RAID-Z2/3 and dRAID, gang blocks (
gang_block), and encrypted datasets (encryption; an unencrypted/bootbeside them still reads). - Skein and Edon-R checksums, block by block (
csum_skein,csum_edonr). Both are keyed with the pool's salt, and no permissive Rust implementation of either offers a keyed mode (Debian packages neither).
cargo add lamzfsor in Cargo.toml:
[dependencies]
lamzfs = "0.1"| Feature | Default | What it adds |
|---|---|---|
lz4, zstd, gzip, lzjb |
yes | That compression algorithm |
mirror |
yes | Mirror topologies |
sha512 |
yes | SHA-512/256 checksums (sha2) |
blake3 |
yes | BLAKE3 checksums (blake3, portable Rust) |
std |
no | impl std::error::Error for Error |
For x86_64-unknown-uefi, build in release mode: sha2 0.11's x86 backend
hits an LLVM error in unoptimized builds for that target. Release builds, and
every other target, are unaffected.
Implement BlockRead over your device, then import and read:
use lamzfs::{BlockRead, PoolMember, Zfs};
# struct Disk;
# impl BlockRead for Disk {
# type Error = ();
# fn read_at(&mut self, _off: u64, _buf: &mut [u8]) -> Result<(), ()> { Ok(()) }
# }
# fn members() -> Vec<PoolMember<Disk>> { Vec::new() }
let mut zfs = Zfs::import(members())?; // one PoolMember per vdev member
println!("pool {} ({:#x})", zfs.pool_name(), zfs.pool_guid());
// Dataset path = child-directory components under the pool root; the second
// argument is the directory within that dataset (empty = its root).
for entry in zfs.read_dir(&["BOOT", "default"], &[])? {
println!("{:?} {}", entry.kind, entry.name);
}
let kernel = zfs.read(&["BOOT", "default"], &["vmlinuz"])?;
# Ok::<(), lamzfs::Error>(())See examples/zfsdump.rs for a runnable host tool.
The fixtures are real pools that OpenZFS wrote through its kernel module, so the
kernel is the oracle: Every file must hash to what the kernel wrote, and every
refused layout must fail with its named token. tests/fixtures/regen.sh builds
the original compatibility=grub2 pools and tests/fixtures/regen-installer.sh
the installer-shaped and refused ones (both need the ZFS kernel module and
sudo). Three fuzz targets live in fuzz/.
The on-disk decoders are vendored from
rzfs, electing the MIT half of its
GPL-2.0 OR MIT dual license: A clean-room reader, not GPL kernel or userspace
source. The orchestration (pool import, vdev routing, feature gate, dataset
walk, path resolve, file read) is new lamzfs code. The crate is scalar-only
and #![forbid(unsafe_code)]. See NOTICE for attribution.
Issues and pull requests are welcome at
github.com/lamco-admin/lamzfs. A change
to the read path should come with a fixture built by one of the scripts in
tests/fixtures/.
The crate as published is MIT
(LICENSE-MIT). The vendored rzfs decoders are MIT only, by the
election recorded in NOTICE. The code written for lamzfs is
additionally available under the Apache License, Version 2.0
(LICENSE-APACHE), as each of its files' SPDX headers says.
Copyright 2025-2026 Lamco Development LLC.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.