fstool 0.4.13

Build disk images and filesystems (ext2/3/4, MBR, GPT) from a directory tree and TOML spec, in the spirit of genext2fs.
Documentation
[package]
name = "fstool"
version = "0.4.13"
edition = "2024"
# Bumped from 1.85 by the `purecrypto` dependency (encrypted-DMG crypto),
# whose own MSRV is 1.95.
rust-version = "1.95"
description = "Build disk images and filesystems (ext2/3/4, MBR, GPT) from a directory tree and TOML spec, in the spirit of genext2fs."
license = "MIT"
repository = "https://github.com/KarpelesLab/fstool"
readme = "README.md"
keywords = ["filesystem", "ext4", "ext2", "gpt", "image"]
categories = ["filesystem", "command-line-utilities"]

[lib]
name = "fstool"
path = "src/lib.rs"

[[bin]]
name = "fstool"
path = "src/bin/fstool/main.rs"

[features]
# Compression codecs for SquashFS reads and `.tar.<algo>` streaming I/O.
# Enabled by default; disable with `default-features = false` and pick
# a subset if you want to slim the binary or avoid a C-bundled build.
#
# Archive backends (zip / cpio / ar) are always compiled — they add no new
# dependencies (zip's DEFLATE rides the `gzip` feature; cpio/ar need no
# codec). Every other archive format has a read-only reader behind its own
# per-format feature (`cab`, `amiga-lzx`, `lha`, `arc`, `sit`, `sevenz`,
# `rar`); each enables only the compcol codecs it can use today and returns a
# clean `Unsupported` for methods whose codec hasn't landed in compcol yet.
default = ["gzip", "xz", "lzma", "lz4", "zstd", "lzo", "cab", "amiga-lzx", "lha", "arc", "sit", "sevenz", "rar", "dmg-bzip2", "dmg-lzfse", "dmg-encrypted", "readline"]
# Line editing + command history for the interactive `fstool shell` (↑/↓
# history, Ctrl-A/E, etc.) via `rustyline`. On by default; disable it
# (`default-features = false`) to drop the dependency — the shell then falls
# back to a plain line-buffered reader. No effect on piped/non-TTY input.
readline = ["dep:rustyline"]
# Every codec is served by `compcol` (one uniform crate): gzip/zlib/deflate/
# xz/lzma/zstd/lz4/lzo, plus the CAB + Amiga-LZX archive codecs and the DMG
# bzip2/lzfse decoders. The `gzip` feature also covers zip DEFLATE, DMG zlib,
# and HFS+ decmpfs; lz4 uses compcol's raw block (SquashFS) + canonical frame
# (tar); lzo uses compcol's raw LZO1X block; lzma is the `.lzma` alone codec.
gzip = ["dep:compcol", "compcol/gzip", "compcol/zlib", "compcol/deflate"]
xz = ["dep:compcol", "compcol/xz"]
lzma = ["dep:compcol", "compcol/lzma"]
lz4 = ["dep:compcol", "compcol/lz4"]
# Microsoft Cabinet (.cab) reader: Store/MSZIP/LZX/Quantum folders decode
# via compcol. Read-only.
cab = ["dep:compcol", "compcol/deflate", "compcol/lzx", "compcol/quantum"]
# Amiga LZX (.lzx) reader: Store + LZX (compcol amiga_lzx) groups. Read-only.
amiga-lzx = ["dep:compcol", "compcol/amiga_lzx"]
# LHA / LZH (.lzh) reader (read-only): walks level-0/1/2 headers. `-lh0-`
# store decodes today; the lh1/4/5/6/7 LZSS+Huffman methods list but read as
# Unsupported pending an `lha` codec in compcol (will add `compcol/lha`).
lha = []
# SEA ARC (.arc) reader (read-only): walks the flat header chain. Stored
# methods 1/2 decode today; the compressed methods (RLE90 / squeeze / crunch /
# squash) list but read as Unsupported pending ARC codecs in compcol.
arc = []
# StuffIt (.sit) reader (read-only): classic `SIT!` container — data-fork
# method 0 (store) decodes; compressed methods + StuffIt 5 list/detect but
# read as Unsupported pending StuffIt codecs in compcol.
sit = []
# 7-Zip (.7z) reader (read-only): parses the container + single-coder folders
# (Copy / LZMA / BZip2 / Deflate via compcol; solid folders sliced per
# substream). LZMA2 / BCJ filters / PPMd / multi-coder / encryption list but
# read as Unsupported pending raw-LZMA2 + branch-filter codecs in compcol.
sevenz = ["dep:compcol", "compcol/lzma", "compcol/bzip2", "compcol/deflate"]
# RAR reader (read-only): RAR5 store + compressed via compcol's rar5 decoder,
# including solid groups (decoded as one continuous stream; a sequential walk
# such as repack decompresses the group once). (RAR4 would add compcol/rar1+
# rar2+rar3 later.)
rar = ["dep:compcol", "compcol/rar5"]
zstd = ["dep:compcol", "compcol/zstd"]
lzo = ["dep:compcol", "compcol/lzo"]
# DMG chunk codecs beyond the always-available zero / raw / zlib / ADC set,
# decoded via compcol (bzip2 + lzfse decoders). Gated so a slim build can
# drop them. (`lzfse_rust` is a dev-dependency used only to generate the
# lzfse test vector — compcol's LZFSE is decode-only.)
dmg-bzip2 = ["dep:compcol", "compcol/bzip2"]
dmg-lzfse = ["dep:compcol", "compcol/lzfse"]
# Password-protected DMG read support (`encrcdsa` v2). Served by the
# `purecrypto` crate (AES-CBC, 3DES-EDE3-CBC via Cbc64, HMAC-SHA1, SHA-1,
# PBKDF2) — pure-Rust, no foreign code. Gated so a slim build can drop it.
dmg-encrypted = ["dep:purecrypto"]
# FUSE adapter: enables the `fstool mount` subcommand which exposes an
# ext{2,3,4} image as a userspace filesystem via libfuse (Linux) or
# macFUSE (macOS). Off by default so the core build doesn't need a C
# FUSE library on the host; opt in with `--features fuse`.
fuse = ["dep:fuser"]

[dependencies]
thiserror = "2"
log = "0.4"
uuid = { version = "1", features = ["v4"] }
crc32fast = "1"
clap = { version = "4", features = ["derive"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1"
toml = "1.1.2"
crc32c = "0.6.8"
# Unicode NFD + default case folding for the APFS drec hash function.
# Volumes formatted with APFS_INCOMPAT_NORMALIZATION_INSENSITIVE
# (the macOS default for user data volumes) store drec keys with a
# 22-bit CRC32C of the NFD-normalised (optionally case-folded) name;
# our writer computes it via these crates. Both unicode-normalization
# and caseless are maintained under unicode-rs and MIT/Apache.
unicode-normalization = "0.1"
caseless = "0.2"
# CP949 (Korean) ↔ UTF-8 for the GRF backend (Gravity Ragnarok archives
# store filenames in CP949). encoding_rs's `EUC_KR` label is the WHATWG
# alias that covers CP949 — the spec mapping is identical in practice.
encoding_rs = "0.8"
# Used to spool compressed tar streams to a temp file when reading or
# writing `.tar.gz` / `.tar.zst` / etc., so we don't have to hold the
# whole archive in RAM. Cheap dep; already in dev-dependencies.
tempfile = "3"

# Compression codecs — all optional and feature-gated. Pure-rust where
# possible (flate2 via miniz_oxide, lz4_flex, lzma-rs); zstd and lzo
# pull in bundled C source.
# compcol: uniform no_std codec collection serving gzip/zlib/deflate/xz/zstd
# /lz4/lzo plus the CAB codecs. `std` is always on (for the `io` Read/Write
# adapters); per-algorithm features are added by fstool's feature flags.
compcol = { version = "0.6", optional = true, default-features = false, features = ["std"] }

# Line editing + persistent history for the interactive shell. Optional, behind
# the `readline` feature; the bin enables it by default. Pure-Rust line editor.
rustyline = { version = "15", optional = true }

# Encrypted DMG (encrcdsa v2) crypto — `purecrypto`, a pure-Rust no_std
# toolkit (KarpelesLab, MIT). Opt-in via the `dmg-encrypted` feature; we
# pull only the symmetric/hash/kdf modules (`kdf` re-enables `hash` +
# `cipher`), not its TLS/PQC/RSA surface.
purecrypto = { version = "0.4", optional = true, default-features = false, features = ["std", "cipher", "hash", "kdf"] }

# FUSE adapter — Linux uses libfuse, macOS uses macFUSE. Off by default
# (see the `fuse` feature). `fuser` is the maintained successor to
# `fuse-rs` and tracks the libfuse 3.x ABI. We enable the default
# `libfuse` feature so the build links against the system FUSE
# library; running `cargo build --features fuse` therefore requires
# libfuse-dev (Linux) or macFUSE (macOS) installed on the host.
fuser = { version = "0.16", optional = true, features = ["libfuse"] }

# Needed on Unix for the BLKGETSIZE64 / DKIOCGETBLOCKCOUNT ioctls used to
# query the size of a block device, plus the O_EXCL open flag that refuses
# devices with a mounted partition.
[target.'cfg(unix)'.dependencies]
libc = "0.2"

[dev-dependencies]
tempfile = "3"
env_logger = "0.11"
# Test-only: an independent LZFSE *encoder* to generate fixtures that the
# production `compcol` LZFSE decoder must round-trip (compcol's LZFSE is
# decode-only). Not linked into the shipped binary.
lzfse_rust = "0.2"