Per-format implementation notes¶
What instar's parsers support for each disk image format, and the deliberate limits. For the qemu-img parity comparison and the coverage matrix, see format-coverage.md; for user-facing reference, see usage.md and the per-operation pages.
Format Support¶
Measurable target formats: raw, qcow2 (qemu-img-parity),
vmdk, vpc (VHD), vhdx (instar-only — qemu-img does not
implement measure for these targets).
Creatable target formats: raw (host-only —
open + ftruncate + posix_fallocate), qcow2 (qemu-img
info-equivalent modulo refcount_bits / compat / zstd
hardcodes), vmdk monolithicSparse + streamOptimized, vpc
dynamic + fixed (modulo CHS virtual_size rounding), vhdx
dynamic (modulo default block_size when unspecified).
Backing-file references supported on qcow2, vmdk, vpc, vhdx
(matches qemu-img's permission set). See
docs/create.md for the user reference and
docs/quirks.md for the documented writer
divergences.
qcow2¶
QEMU Copy-On-Write version 2/3. Supported features:
- Sparse allocation with cluster sizes 512B-2MB (cluster_bits 9-21)
- Compression (zlib, zstd) for clusters up to 2MB
- Backing file chains (automatic flattening)
- Refcount widths: 1, 2, 4, 8, 16, 32, 64 bits
- Extended L2 entries (16-byte with subcluster bitmaps;
full subcluster support — the bitmap is parsed for
per-subcluster data reading: Normal, Zero, and
Unallocated states; the read path narrows I/O for mixed-
subcluster clusters when sector_size ≤ subcluster_size).
Output with --extended-l2 writes 16-byte L2 entries with
incompatible_features bit 4 and per-subcluster sparse
bitmaps (compute_subcluster_bitmap()).
- Incompatible feature bit validation
- External data files (metadata/data separation, chain discovery with allowlist)
- Legacy AES-128-CBC encryption (crypt_method=1) decryption via --qcow2-password
- LUKS-in-QCOW2 encryption (crypt_method=2) decryption via --luks-passphrase
- LUKS-encrypted output (crypt_method=2) via --luks-encrypt-passphrase
(AES-256-XTS with PBKDF2-SHA256 key derivation, LUKS v1 headers)
- Snapshot table parsing, detection, and extraction via --snapshot
qcow2 write infrastructure¶
In-place mutation of an existing qcow2 (used by commit, rebase
safe mode and bench -w) runs on two shared no_std crates: the
crates/qcow2-write planner (pure, I/O-free, address-free —
turns a write into a typed step program; handles the envelope,
classification, allocate-on-write and copy-on-write) and the
crates/qcow2-write-exec executor (the literal step interpreter
plus the byte-range/device layer). Refcount growth is split the same
way across each crate's growth module. The maintainer reference for
this machinery — the step-program ABI, the write envelope, COW, growth
and the crash-ordering contract — is
docs/qcow2/qcow2-write-planner.md.
raw¶
Simple byte-for-byte disk representation. No metadata, just data.
vmdk¶
VMware Virtual Machine Disk. Supported sub-formats for input/output:
- monolithicSparse (input, output, check)
- streamOptimized (input, output with -c, check)
- monolithicFlat (input and output): two-file descriptor + flat extent.
The VMM detects the descriptor prefix on the host, parses the
extent line via vmdk::parse_descriptor_extents, validates
the flat path against the backing-file allowlist, and opens
the flat extent as a second virtio-block device. Guest
operations read content from that device through the same
ChainConfig.data_device_idx redirect used for QCOW2
external data files. Output via --subformat monolithicFlat.
- twoGbMaxExtentFlat (input): multi-extent flat descriptors with
multiple flat extent files. Each extent is opened as a separate
virtio-block device and reads are dispatched to the correct
device based on the extent offset map.
- monolithicFlat with parentFileNameHint= (input): descriptors
referencing a parent are followed as a backing chain, enabling
flat images in overlay hierarchies.
Detected but not yet supported for I/O: - twoGbMaxExtentSparse (multi-extent sparse, detected and rejected gracefully)
The check operation performs full structural validation: grain directory and grain table walk, grain offset bounds checking, compressed grain marker validation (LBA consistency and compressed size bounds), redundant grain directory (RGD) cross-check, overlap detection via 1-bit-per-grain bitmap, streamOptimized footer validation, fragmentation measurement, and multi-extent detection.
vhd¶
Microsoft Virtual Hard Disk. Supported sub-formats: - Fixed (type 2): raw data with 512-byte footer appended - Dynamic (type 3): BAT-based block allocation with 2 MiB blocks (input, output, check)
The check operation performs full structural validation: footer cookie and checksum, format version and feature flag validation, dynamic header cookie/checksum/version, BAT offset and entry bounds checking, overlap detection via 1-bit-per-block bitmap, fragmentation tracking, fixed VHD size validation, and footer copy consistency (start vs end of file).
vhdx¶
Microsoft VHDX Virtual Hard Disk v2 (Hyper-V). Supported: - Dynamic VHDX: BAT-based block allocation with 32 MiB blocks (input, output, check)
VHDX uses CRC-32C (Castagnoli) checksums, GUID-identified metadata, 64-bit BAT entries with interleaved sector bitmap entries, and 1MB-aligned structures. All on-disk fields are little-endian.
The check operation performs full structural validation: file identifier
signature check, dual header CRC-32C validation with active header
selection by sequence number, dirty log detection, region table 1 and 2
CRC-32C validation with cross-consistency check, GUID-based metadata
parsing, BAT entry validation (offset bounds, 1MB alignment, overlap
detection, state validation), and fragmentation tracking. A payload
block whose BAT entry names a byte range inside the image's own
declared regions, or file offset zero, is counted as a corruption
rather than validated as ordinary data — check tests this against
the same region table it parses for itself, independently of the
reads' version of the rule below.
Reads apply the same overlap rule, on every VHDX image rather than
only under check — "reads" meaning the guest chain walker,
block_lookup and sector_bitmap_lookup, which is what convert,
dd, compare, bench and rebase use, and map and measure's
own whole-BAT walks, map_extents and scan_allocation, which apply
the identical predicate rather than a narrower one. A payload block, or
a differencing child's sector-bitmap block, is refused when its byte
range overlaps any entry in the region table — the BAT region, the
metadata region, or anything else the writer declared, recognised by
instar or not. A conforming writer never places payload on top of the
structures the image declares, so this costs a well-formed image
nothing; a malformed one is refused rather than read.
"Any entry" is any of the first eight, which is the same cap the region
scan itself uses; SPEC(VHDX) permits up to 2047 entries, and an image
declaring more has only its first eight checked. That cap is not
somewhere an image can hide a region instar relies on, because the same
scan is what locates the BAT and the metadata region: declare either
past the eighth entry and the image does not open at all. What a longer
table can keep out of the overlap test is an unrecognised region, whose
bytes instar never reads as structure. A BAT entry
naming file offset 0 is refused separately, because the file
identifier that lives there is fixed structure rather than a region
table entry and so overlaps nothing. A region entry whose
offset + length overflows 64 bits counts as overlapping every block,
which makes the whole image unreadable: an image that cannot say where
its own regions end cannot be used to bound anything.
luks¶
LUKS encrypted containers (v1 and v2). The info operation parses: - Version, cipher name, cipher mode, hash algorithm - UUID, payload offset, master key length, active key slots - LUKS v2: JSON metadata area for cipher/hash extraction
With --luks-passphrase, LUKS v1 and v2 containers are decrypted inside
the KVM guest using pure-Rust RustCrypto crates (software AES, no
hardware acceleration needed in bare-metal). Key derivation uses PBKDF2
(v1) or Argon2id (v2, requires --max-guest-memory for the 1GB+ working
memory). The decrypted first block is passed through format detection to
report the inner format and virtual size.
The convert operation supports decrypting native LUKS containers
(--luks-passphrase) and LUKS-in-QCOW2 images (crypt_method=2). Both
use AES-XTS-plain64 for payload decryption. Native LUKS containers
wrapping QCOW2 images are transparently handled: the convert operation
detects the inner QCOW2 format and wraps the CallTable I/O function
pointers to offset and decrypt reads, allowing the qcow2 crate to
process the inner image without modification. LUKS v2 containers
using Argon2id KDF require --max-guest-memory to allocate the
working memory needed for key derivation.
vdi¶
VirtualBox Disk Image. Read-only input for convert / compare / dd /
bench (src/crates/vdi/, the PLAN-format-coverage work), both
dynamic and static images. Key structures: a single header
(validated against qemu's 12 open-time rules) plus a flat block
map, walked with an allocation-order lookup through the standard
sector-cached read path. Discarded and unallocated block-map
entries read as zeros, and reads at or past device capacity
zero-fill rather than error, matching qemu's lack of length
validation. check still refuses VDI (exit 63); map, measure,
and resize are unchanged refusals. See
docs/format-coverage.md and
docs/quirks.md for the full parity and quirks
detail.
parallels¶
Parallels disk images. Read-only input for convert / compare / dd /
bench (src/crates/parallels/, the PLAN-format-coverage work),
both the legacy "WithoutFreeSpace" (v1) and "WithouFreSpacExt"
(v2/ext) magics. Key structures: a header (tracks, catalog/BAT
size, ext_off) plus a per-magic BAT (sector-valued under v1,
cluster-valued under v2), walked through the standard sector-cached
read path. BAT value 0 and offsets beyond BAT coverage read as
zeros; ext_off != 0 is refused at init (a deliberate divergence —
instar does not parse the format extension). check still refuses
Parallels (exit 63); map, measure, and resize are unchanged
refusals. See docs/format-coverage.md
and docs/quirks.md for the full parity and
quirks detail.
qcow1 (qcow)¶
QEMU's original copy-on-write format ("qcow", superseded by qcow2
but not formally deprecated by qemu). Read-only input for convert /
compare / dd / bench (src/crates/qcow1/, the PLAN-format-coverage work), including backing chains and compressed clusters. Key
structures: a header plus two-level (L1/L2) block lookup, with
compressed clusters as raw DEFLATE (no zlib wrapper) — distinct
from qcow2's zlib-first decompression helper. QCOW1 is the first
non-QCOW2 backing format: unallocated clusters fall through to the
next chain device rather than zero-filling. check still refuses
QCOW1 (exit 63) — genuine parity, since qemu's own qcow driver also
refuses checks. map and measure stay refusals (a deliberate
divergence; qemu supports both on qcow1). See
docs/format-coverage.md and
docs/quirks.md for the full parity and quirks
detail.
dmg¶
Apple UDIF disk image. Read-only input for convert / compare / dd /
bench (src/crates/dmg/, the PLAN-format-coverage work). Key
structures: the koly trailer (shared trailer helpers), the
XML-plist or resource-fork chunk table, and per-sector chunk
lookup. Supports zlib, raw, ADC, bzip2, and LZFSE-compressed
chunks. The read-error model inverts every prior phase's posture:
most malformed inputs are refused rather than best-effort parsed.
DMG is supported at any backing-chain position. check still
refuses DMG (exit 63), but the refusal reports format "raw", not
"dmg", matching qemu-img's passthrough divergence. See
docs/format-coverage.md and
docs/quirks.md for the full parity and quirks
detail.