Skip to content

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.

📝 Report an issue with this page