Understanding Qcow2 image , its header and verifying corruption..

qcow2 is one of the best image formats available for virtual machines running on top of KVM .. Its known as qemu image format version 2. Use it to have smaller images (useful if your filesystem does not supports holes, for example on Windows), optional AES encryption, zlib based compression and support of multiple VM snapshots.

Supported options:

“compat”
Determines the qcow2 version to use. “compat=0.10” uses the traditional image format that can be read by any QEMU since 0.10 (this
is the default). “compat=1.1” enables image format extensions that only QEMU 1.1 and newer understand. Amongst others, this
includes zero clusters, which allow efficient copy-on-read for sparse images.

“backing_file”
File name of a base image (see create subcommand)

“backing_fmt”
Image format of the base image

“encryption”
If this option is set to “on”, the image is encrypted.

Encryption uses the AES format which is very secure (128 bit keys). Use a long password (16 characters) to get maximum protection.

“cluster_size”
Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster sizes can improve the image file size whereas larger
cluster sizes generally provide better performance.
“preallocation”
Preallocation mode (allowed values: off, metadata). An image with preallocated metadata is initially larger but can improve
performance when the image needs to grow.

“lazy_refcounts”
If this option is set to “on”, reference count updates are postponed with the goal of avoiding metadata I/O and improving
performance. This is particularly interesting with cache=writethrough which doesn’t batch metadata updates. The tradeoff is that
after a host crash, the reference count tables must be rebuilt, i.e. on the next open an (automatic) “qemu-img check -r all” is
required, which may take some time.

This option can only be enabled if “compat=1.1” is specified.

Qcow2 header is composed by below byte allocation..

Byte 0 – 3: magic
QCOW magic string (“QFI\xfb”)

4 – 7: version
Version number (valid values are 2 and 3)

8 – 15: backing_file_offset
Offset into the image file at which the backing file name
is stored (NB: The string is not null terminated). 0 if the
image doesn’t have a backing file.

16 – 19: backing_file_size
Length of the backing file name in bytes. Must not be
longer than 1023 bytes. Undefined if the image doesn’t have
a backing file.

20 – 23: cluster_bits
Number of bits that are used for addressing an offset
within a cluster (1 << cluster_bits is the cluster size). Must not be less than 9 (i.e. 512 byte clusters). Note: qemu as of today has an implementation limit of 2 MB as the maximum cluster size and won't be able to open images with larger cluster sizes. 24 - 31: size Virtual disk size in bytes 32 - 35: crypt_method 0 for no encryption 1 for AES encryption 36 - 39: l1_size Number of entries in the active L1 table 40 - 47: l1_table_offset Offset into the image file at which the active L1 table starts. Must be aligned to a cluster boundary. 48 - 55: refcount_table_offset Offset into the image file at which the refcount table starts. Must be aligned to a cluster boundary. 56 - 59: refcount_table_clusters Number of clusters that the refcount table occupies 60 - 63: nb_snapshots Number of snapshots contained in the image 64 - 71: snapshots_offset Offset into the image file at which the snapshot table starts. Must be aligned to a cluster boundary. Now lets verify a qcow image header by following above byte allocation. I have an image whose first 1 MB look like : [humble@localhost source-gits]$ hexdump -C -n $((1024 * 1024)) myimg.qcow 00000000 51 46 49 fb 00 00 00 02 00 00 00 00 00 00 00 00 |QFI.............| 00000010 00 00 00 00 00 00 00 10 00 00 00 00 3e 80 00 00 |............>…|
00000020 00 00 00 00 00 00 00 02 00 00 00 00 00 01 00 00 |…………….|
00000030 00 00 00 00 00 02 00 00 00 00 00 01 00 00 00 00 |…………….|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….|
*
00020000 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 |…………….|
00020010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….|
*
00030000 00 01 00 01 00 01 00 01 00 00 00 00 00 00 00 00 |…………….|
00030010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….|
*
00040000
[humble@localhost source-gits]$

Lets start verifying the image integrity ( well atleast its header) by manually unwinding above data in hexadecimal notation…

The first 4 bytes represents its magic string.. so starting from offset 0x00000000 we can see its “51 46 49 fb” which is equivalanto to “QFI” . luckily atleast these bytes are not corrupted..

lets see next 4 bytes.. 00 00 00 02 , Isnt it saying its version is “2” ?? yes..

Now, lets see what is the size of this image : Bytes from 24-31 tells us the size of the image..

3e 80 00 00 00 00 00 00 which is equivalent to
00111110100000000000000000000000 in binary or 1048576000 in decimal.. 1048576000 == 1 GB ? yes, it is..

Lets see whether this image has any snapshots ? 60-63 says it all..
00 00 00 00 -> so, NO SNAPSHOTS!

If you extract above hexadecimal values you would end up in :

magic 0x514649fb
version 2
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 1048576000
crypt_method 0
l1_size 2
l1_table_offset 0x10000
refcount_table_offset 0x20000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x0
autoclear_features 0x0
refcount_order 4
header_length 72

Hmmmm.. looks like there is no corruption reported at least in this header.. well, is there any binary available for checking integrity of the image itself ? Yes, ‘qemu-img’ check can be used for the same purpose..

lot many operations can be performed against qcow2 image using this binary..

To get some information about this image we can try:

[humble@localhost source-gits]$ qemu-img info myimg.qcow
image: myimg.qcow
file format: qcow2
virtual size: 1.0G (1048576000 bytes)
disk size: 256K
cluster_size: 65536
[humble@localhost source-gits]$

Also its possible to check the integrity of the image by :

[humble@localhost source-gits]$ qemu-img check myimg.qcow
No errors were found on the image
[humble@localhost source-gits]$

Hope this helps!!