qemu-img Output Formats¶
This document describes the output formats supported by qemu-img info and how
instar handles version differences across qemu releases.
Overview¶
qemu-img info supports multiple output formats via the --output flag:
| Format | Flag | Description |
|---|---|---|
| Human-readable | --output=human (default) |
Formatted text for terminal display |
| JSON | --output=json |
Machine-parseable JSON structure |
instar supports both formats and can be configured to match the output of specific qemu-img versions.
Output Profiles¶
Analysis of 80+ qemu-img versions (6.0.0 through 10.2.0) revealed that outputs fall into distinct profiles based on structural changes in the output format. All versions within a profile produce byte-identical output for the same input image.
Human-Readable Output Profiles¶
| Profile | qemu-img Versions | Key Difference |
|---|---|---|
profile-6-0-0 |
6.0.0 - 7.2.x | No "Child node '/file'" section |
profile-8-0-0 |
8.0.0 - 10.x+ | Includes "Child node '/file'" section |
The only structural difference between these profiles is the presence of the "Child node '/file'" section, which was added in qemu-img 8.0.0.
Example: qemu-img 7.2.x output (profile-6-0-0)
image: /path/to/image.qcow2
file format: qcow2
virtual size: 10 GiB (10737418240 bytes)
disk size: 196 KiB
cluster_size: 65536
Format specific information:
compat: 1.1
compression type: zlib
lazy refcounts: false
refcount bits: 16
corrupt: false
extended l2: false
Example: qemu-img 8.0+ output (profile-8-0-0)
image: /path/to/image.qcow2
file format: qcow2
virtual size: 10 GiB (10737418240 bytes)
disk size: 196 KiB
cluster_size: 65536
Format specific information:
compat: 1.1
compression type: zlib
lazy refcounts: false
refcount bits: 16
corrupt: false
extended l2: false
Child node '/file':
filename: /path/to/image.qcow2
protocol type: file
file length: 200 KiB (204800 bytes)
disk size: 200 KiB
JSON Output Profiles¶
| Profile | qemu-img Versions | Key Difference |
|---|---|---|
profile-6-0-0 |
6.0.0 - 7.2.x | No children array |
profile-8-0-0 |
8.0.0 - 10.x+ | Includes children array with file node info |
Example: qemu-img 7.2.x JSON output (profile-6-0-0)
{
"virtual-size": 10737418240,
"filename": "/path/to/image.qcow2",
"cluster-size": 65536,
"format": "qcow2",
"actual-size": 200704,
"format-specific": {
"type": "qcow2",
"data": {
"compat": "1.1",
"compression-type": "zlib",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false,
"extended-l2": false
}
},
"dirty-flag": false
}
Example: qemu-img 8.0+ JSON output (profile-8-0-0)
{
"children": [
{
"name": "file",
"info": {
"children": [],
"virtual-size": 204800,
"filename": "/path/to/image.qcow2",
"format": "file",
"actual-size": 204800,
"format-specific": {
"type": "file",
"data": {}
},
"dirty-flag": false
}
}
],
"virtual-size": 10737418240,
"filename": "/path/to/image.qcow2",
"cluster-size": 65536,
"format": "qcow2",
"actual-size": 200704,
"format-specific": {
"type": "qcow2",
"data": {
"compat": "1.1",
"compression-type": "zlib",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false,
"extended-l2": false
}
},
"dirty-flag": false
}
Version Detection¶
instar can detect the installed qemu-img version at runtime and automatically emit output matching that version's format. This ensures true drop-in replacement compatibility.
Automatic Detection¶
By default, instar detects the qemu-img version by running qemu-img --version
and parsing the version string. It then selects the appropriate output profile.
Explicit Version Override¶
The --qemu-version flag allows forcing a specific output format:
# Force qemu-img 7.2 compatible output (no Child node section)
instar info --qemu-version 7.2 image.qcow2
# Force qemu-img 10.0 compatible output (includes Child node section)
instar info --qemu-version 10.0 image.qcow2
Version Mapping¶
| Specified Version | Output Profile |
|---|---|
| 6.x, 7.x | profile-6-0-0 |
| 8.x, 9.x, 10.x+ | profile-8-0-0 |
JSON Output¶
JSON output is enabled with the --output=json flag:
The JSON structure matches qemu-img exactly, including:
- Field names use hyphens (e.g., virtual-size, not virtual_size)
- Nested format-specific object with type and data fields
- Boolean dirty-flag field
- Version-appropriate children array (8.0+ only)
Parsing JSON Output¶
The JSON output is designed for machine parsing:
import json
import subprocess
result = subprocess.run(
['instar', 'info', '--output=json', 'image.qcow2'],
capture_output=True,
text=True
)
info = json.loads(result.stdout)
print(f"Format: {info['format']}")
print(f"Virtual size: {info['virtual-size']} bytes")
print(f"Actual size: {info['actual-size']} bytes")
Text Read From an Image¶
Many values instar prints come out of the image rather than from instar itself: a backing file name, an external data file path, a VMDK create type or extent file name, a snapshot ID or tag, and the paths instar resolves from them. The guest that reports them is parsing an untrusted image, so instar treats every string it reports as attacker controlled.
Human output escapes the characters that could act on a terminal
rather than be displayed by it: the C0 control range (ESC, newline,
carriage return and the rest), DEL, the C1 range, and the Unicode
bidirectional formatting characters (U+061C, U+200E, U+200F,
U+202A-U+202E and U+2066-U+2069). They are written the way Rust's
escape_debug writes them -- \n, \r, \t, and \u{1b} for the
rest -- so a hostile name can neither send escape sequences to your
terminal nor start a forged line of output:
$ instar info overlay.qcow2
...
backing file: p\u{1b}[2J\nNo errors were found on the image. (actual path: /images/p\u{1b}[2J\nNo errors were found on the image.)
Backslashes and quotes are left alone, so a Windows or UNC parent path
prints exactly as it is stored. A name that contains the literal text
\u{1b} therefore reads the same as one that contains ESC; neither
reaches the terminal as a control character. This is a deliberate
divergence from qemu-img, which prints these strings raw. It only shows
for a name that contains one of the characters above, so the output for
every ordinary image is byte-identical to qemu-img's.
The same rule applies to info --chain, snapshot -l, check, the
stderr notices about a backing reference, and every guest message and
debug line printed with --verbose: the logger escapes each record as a
whole, so this holds for lines added later too. Error messages that end a command
are printed with the same escaping by the Rust runtime.
JSON output always carries the exact value, so a script reading
backing-filename gets the bytes the image holds. Strings are escaped
as JSON requires, and DEL, the C1 range and the bidi characters are
also written as \uXXXX, so a JSON document viewed on a terminal is as
safe as the human form. Every string field is escaped, including ones
such as format and create-type that instar used to interpolate
directly; a VMDK create type ending in a backslash used to produce a
document that did not parse.
Testing and Validation¶
The instar-testdata repository contains baseline outputs for multiple qemu-img versions and output formats. This enables:
- Profile detection: Grouping versions that produce identical output
- Regression testing: Ensuring instar matches expected baselines
- Drift detection: CI jobs that compare instar output against stored baselines
Baseline Structure¶
instar-testdata/expected-outputs/
qemu-img-human/
raw/<version>/ # Raw captured outputs per version
profiles/<profile>/ # Deduplicated profile outputs
version-map.json # Version to profile mapping
qemu-img-json/
raw/<version>/
profiles/<profile>/
version-map.json
Each output format is processed independently, allowing for different profile groupings if the formats diverge in future qemu releases.
Implementation Notes¶
Child Node Section¶
The "Child node '/file'" section (and corresponding children array in JSON)
was added in qemu-img 8.0.0 to expose information about the underlying protocol
layer. For file-backed images, this includes:
- The absolute path to the file
- Protocol type (
filefor local files) - File length (raw file size)
- Disk size (block-allocated size)
This information was previously only available by examining the image file directly. The addition improves transparency for debugging and scripting.
Compatibility Considerations¶
When writing scripts that parse qemu-img/instar output:
- Human-readable format: Use regex patterns that handle optional sections
- JSON format: Check for
childrenkey existence before accessing - Version detection: Use
--qemu-versionfor consistent output across systems
Example robust JSON parsing: