instar measure — predict file size for a target format¶
instar measure predicts the file size required to convert a source image
(or a hypothetical empty image of a given size) to a target format. It is
the safe, sandboxed equivalent of qemu-img measure, with byte-for-byte
output parity for the targets qemu-img supports plus instar-specific support
for vmdk, vpc (VHD), and vhdx targets that qemu-img cannot measure.
Synopsis¶
Exactly one of --size SIZE or a positional INPUT file path is required.
Common options:
-O, --target-format <FMT> raw | qcow2 | vmdk | vpc | vhdx
Default: raw
-s, --size <SIZE> Hypothetical image size with K/M/G/T suffix
-f, --format <FMT> Source format override (usually auto-detected)
--output <FORMAT> human (default) | json
-o, --options <KEY=VALUE,...> qemu-img-style options (repeatable)
Per-target option flags (alternatives to -o):
qcow2: --cluster-size, --refcount-bits, --extended-l2,
--lazy-refcounts, --compat, --preallocation, --compress
vmdk: --subformat, --grain-size
vpc: --subformat, --block-size
vhdx: --subformat, --block-size
The full flag surface is reported by instar measure --help.
Target formats¶
| Target | Source-image mode | --size mode | qemu-img parity |
|---|---|---|---|
| raw | Yes | Yes | byte-identical |
| qcow2 | Yes | Yes | byte-identical |
| vmdk | Yes | Yes | instar-only |
| vpc | Yes | Yes | instar-only |
| vhdx | Yes | Yes | instar-only |
For raw and qcow2 targets, instar's --output=human and --output=json
output matches qemu-img measure exactly across every qemu-img version from
6.0.0 through 10.2.0 (verified by the cross-version baseline matrix in
instar-testdata).
Output format¶
Human (default):
JSON (--output=json):
Note the hyphen in fully-allocated. Four-space indent, no trailing comma
after the last field — these match qemu-img exactly.
For qcow2 v3 sources targeting qcow2, instar emits an additional bitmaps
field (count of persistent qcow2 bitmaps the conversion would preserve,
currently always 0). qemu-img has the same rule. The gate uses a 4+4 byte
peek of magic and version; see docs/quirks.md for details.
-o key=value,... reference¶
Repeatable; later -o invocations override earlier ones. Individual flag
values are overridden by matching -o keys when both are specified.
qcow2¶
Honoured:
- cluster_size, compat (0.10|1.1), refcount_bits
- extended_l2 (on|off), lazy_refcounts (on|off)
- compression_type (zlib|zstd), preallocation (off|metadata|falloc|full)
Rejected (future work):
- backing_file, backing_fmt, data_file, data_file_raw, encrypt.*
vmdk¶
Honoured:
- subformat (monolithicSparse|streamOptimized|monolithicFlat), grain_size
- preallocation (off|falloc|full) — accepted for vmdk, vpc and vhdx
alike, and the answer is the one off gives. preallocation=metadata
is refused (measure: preallocation=metadata is not supported for
FORMAT), through -o and through --preallocation. qemu-img refuses
metadata preallocation for these formats when it creates them
(handle_aiocb_truncate() in qemu 7.0.0's block/file-posix.c), and
measure predicts what instar's own writer produces. qemu-img cannot
measure these formats at all, so there is no qemu-img answer to compare
with.
Accepted but no size effect:
- adapter_type, hwversion, toolsversion, zeroed_grain
vpc (VHD)¶
Honoured:
- subformat (dynamic|fixed)
Accepted but no size effect:
- force_size, force_size_calc
vhdx¶
Honoured:
- subformat (dynamic), block_size
The virtual size is at most 64 TiB (70368744177664 bytes), the largest
VHDX qemu-img creates. A larger --size is refused before the guest
runs (measure: virtual size N exceeds the VHDX maximum of 64 TiB
(70368744177664 bytes)). A larger source is read, and then refused with
measure failed: source virtual size exceeds the VHDX maximum of 64 TiB
(70368744177664 bytes) (MeasureResult::ERROR_TOO_LARGE), not as an
unsupported source format. A size that is not a multiple of 512 is
measured rounded up to one, as convert and create record it.
Accepted but no size effect:
- log_size, block_state_zero
Rejected (future work):
- subformat=fixed
raw¶
No options accepted. Any -o key with -O raw is rejected with an error.
Known divergences from qemu-img¶
For raw and qcow2 targets, instar measure matches qemu-img exactly on the cross-version baseline matrix. A handful of source-image cases exhibit small numeric divergences because instar's parser scanners are simpler than qemu-img's:
- Raw sources with on-disk sparse extents: instar over-reports
requiredbecause the raw scanner does not useSEEK_HOLE/SEEK_DATA. qemu-img does. - QCOW2 sources for some real-world images: instar's scanner counts allocated bytes slightly differently. Likely a compressed-cluster or extended-L2 subcluster edge case under investigation.
- QCOW2 sources with backing chains: instar reports the top layer's allocations only. qemu-img consults the chain. Tracked as #642; the same plumbing closes this for a VHD or VHDX chain, whichever format the chain is in.
- VHDX sources: instar treats every BAT block as fully allocated. qemu-img returns the actual block-state distribution.
- VMDK multi-extent source layouts: instar's extent map propagation differs from qemu-img for some non-trivial layouts.
- VHD legacy CHS-only sources: instar's reported virtual_size differs by approximately 2 MiB.
The canonical list of test cases skipped due to these divergences is
KNOWN_SOURCE_SCANNER_DIVERGENCES in tests/test_measure.py. Each entry
names the divergence category.
Malformed block tables¶
measure's VHDX allocation scan refuses a payload block whose BAT entry
names a byte range that is the image's own structure, rather than
counting it as allocated: file offset zero, where the file identifier
and headers live, or a range overlapping any region the image's region
table declares. This is the same test the guest chain walker already
applies when reading such a block for convert, dd, compare,
bench and rebase; measure's own whole-BAT scan did not apply it
until recently, so it could count a block one of those operations
already refused to read:
measure: source block table names a block inside the image's own
structure (at file offset zero, or overlapping a region the image
declares); the format is recognised, the image is malformed
Exit 1, the same as any other refused source.
The format is recognised and the image parsed far enough to walk its BAT, so the message deliberately does not say "unsupported format" — that would send a user hunting a format problem that is not there. A block declared immediately past the last region in the table is unaffected and still measures normally. See docs/format-internals.md § vhdx for the predicate both the readers and this scan share.
Future work¶
- SEEK_HOLE detection in the raw scanner.
- VHDX scanner partial-block-state walk.
- VMDK multi-extent sparse propagation.
- QCOW2 scanner backing-chain composition.
- QCOW2 compressed-cluster / extended-L2 subcluster overcount investigation.
encrypt.format=luksaware sizing.-l SNAPSHOTsnapshot-targeted measurement.--image-optsparsing.-o helplisting.subformat=fixedfor vhdx target.
For per-format size formulas implemented today, see
src/crates/measure/src/lib.rs.
Examples¶
Predict size for a 1 GiB hypothetical qcow2 with the default cluster size:
Same but with a 4 KiB cluster size:
Predict size for converting a qcow2 image to raw:
JSON output for scripting:
Predict an instar-only target (qemu-img cannot do this):
Differencing images¶
A differencing VHD (footer disk type 4) or VHDX (HasParent set) stores
only the sectors that differ from a parent image. measure reads an
image on its own rather than composing a parent chain into it, so it
refuses such a source by name and exits 1 rather than reporting a
required-size estimate that ignores the parent's data:
measure: source is a differencing <VHD|VHDX> image, and measure reads an
image on its own rather than composing a parent into it, so the sectors
it leaves to its parent could not be composed
instar info is the exception — it reports the parent as a backing file
instead of refusing. convert, dd, compare, bench and rebase do
compose a differencing source against its parent; see the "VHD/VHDX
differencing" section of quirks.md for the per-operation
record.
This refusal is deliberate, not merely unimplemented: measure reports
on one image, and a differencing child's own blocks are almost always
intact while the required size they describe only covers part of the
image. Whether that should change anyway is tracked as
#643, open and not
settled either way.