This page describes an expanding (Compressed) Parallels image. Everything here is from QEMU’s
docs/interop/parallels.rst, block/parallels.c, block/parallels.h
and block/parallels-ext.c, and OpenVZ’s include/ploop1_image.h (all read on
2026-11-14). Where a fact was only seen in the real images read here, it says so. A Plain image has none of
this: it is the raw disk (see the descriptor page).
The file has three parts: the header, the BAT, and the data area. All numbers are little-endian. A
sector is 512 bytes. A cluster is the number of sectors the header’s
tracks field says; the default is 1 MiB (2,048 sectors), and earlier versions used clusters of
63 sectors, 256 KiB and 252 KiB.
The header
64 bytes at the start of the file. QEMU’s and OpenVZ’s names for the fields differ; both are given.
| Offset | Size | QEMU | ploop | Meaning |
|---|---|---|---|---|
| 0 | 16 | magic | m_Sig | The text WithoutFreeSpace or WithouFreSpacExt (see the two signatures). |
| 16 | 4 | version | m_Type | Must be 2. (In ploop’s header the same field is the disk type, and 2 is PRL_IMAGE_COMPRESSED, “Compressed disk (version 1)”.) |
| 20 | 4 | heads | m_Heads | Disk geometry for the guest. |
| 24 | 4 | cylinders | m_Cylinders | Disk geometry for the guest. (ploop calls it the “tracks count”.) |
| 28 | 4 | tracks | m_Sectors | The cluster size, in sectors. Despite the name it is not geometry. Must not be 0 (QEMU: “Zero sectors per track”). |
| 32 | 4 | nb_bat_entries | m_Size | The disk size in clusters: the number of BAT entries. |
| 36 | 8 | nb_sectors | m_SizeInSectors | The disk size in sectors. WithoutFreeSpace: only the low 4 bytes are used and the high 4 must be 0. WithouFreSpacExt: all 8 bytes. |
| 44 | 4 | in_use | m_DiskInUse | Whether the image is open or closed; see below. |
| 48 | 4 | data_off | m_FirstBlockOffset | Where the data area starts, in sectors from the start of the file; see the data area. |
| 52 | 4 | flags | m_Flags | Flags; see below. |
| 56 | 8 | ext_off | m_FormatExtensionOffset | Where the format extension cluster is, in sectors from the start of the file; 0 when there is none. (QEMU shifts it by 9 bits for both signatures; a comment in ploop’s header says the offset is in bytes.) |
QEMU’s probe accepts a file when the first 16 bytes are one of the two signatures and the version is 2.
Disk size, cluster size and BAT size are tied together: the BAT must cover the disk, that is
nb_bat_entries × tracks ≥ nb_sectors (QEMU: “Catalog size too small for advertised disk
size”). When ploop makes a header it rounds the disk size up to a whole number of clusters, so
nb_bat_entries = nb_sectors ÷ tracks.
The two signatures
| Signature | BAT entries count | Disk size | Data offset | Format extension |
|---|---|---|---|---|
WithoutFreeSpace | sectors: the entry is the sector number of the cluster in the file | 4 bytes (the high 4 bytes are not used) | may be 0: the data area then starts at the end of the BAT, rounded up to a sector | may follow: QEMU reads one whenever ext_off is non-zero, whichever signature the image has |
WithouFreSpacExt | clusters: sector number = entry × tracks | 8 bytes | must be non-zero and a multiple of the cluster size | may follow |
Both are one format with the same header. ploop’s ploop1_version() tells the two apart by the
signature alone (PLOOP_FMT_V1, PLOOP_FMT_V2); QEMU creates new images with the
second.
The in-use word
| Value | Text | Meaning |
|---|---|---|
| 0x746F6E59 | Ynot (bytes 59 6E 6F 74) | The image is open for writing, or was not closed. QEMU treats the image as unclean and checks it; it writes this value when it opens an image read-write and writes 0 when it closes it. (ploop: SIGNATURE_DISK_IN_USE.) |
| 0x312E3276 | v2.1 (bytes 76 32 2E 31) | The image was closed by software that follows version 2.1 of the format, which has the format extension. (ploop: SIGNATURE_DISK_CLOSED_V21.) |
| 0 | – | The image was closed by an older version that does not support the format extension (ploop: SIGNATURE_DISK_CLOSED_V20). |
| anything else | – | QEMU’s page says other values are not allowed, yet its code only compares with 0x746F6E59. Observed: both real Parallels Desktop images read here carry 0x37316470, the text pd17. No source explains it. |
Flags
| Bit | Name | Meaning |
|---|---|---|
| 0 | Empty | The image should be considered clear: no data was written. (ploop: CIF_Empty, “No any data was written”.) |
| 1 | – | QEMU’s page: unused. ploop: CIF_FmtVersionConvert, “Version Convert in progress”. |
| 2–31 | – | Unused. |
The block allocation table (BAT)
The BAT starts at byte 64 and is a contiguous array of nb_bat_entries 32-bit unsigned
entries, 4 bytes each. Entry n belongs to cluster n of the virtual disk, which
holds the virtual bytes n × cluster size up to the next cluster.
- An entry of 0 means the cluster is not allocated and reads as zeros.
- Otherwise the entry says where the cluster is: its sector number in the file is the entry for
WithoutFreeSpace, and the entry ×tracksforWithouFreSpacExt. Its byte offset is that sector number × 512.
To read virtual byte b: c = b ÷ cluster size (rounded down), position p = b − c × cluster size, then read from the file at the byte offset of entry c, plus p.
Rules for the entries
QEMU’s page lists them; its open code and checker apply the first two (a cluster must lie inside the file and may not overlap the header or BAT):
- the value must not be lower than the data offset;
- the cluster must lie inside the file (QEMU: the sector number plus the cluster size must not pass the end of the file);
- the value must be unique among all BAT entries;
- the cluster’s offset minus the data offset must be a multiple of the cluster size.
Implementation limits of QEMU, not of the format: a cluster of more than 4,186,111 sectors
(INT32_MAX ÷ 513) and a BAT of more than INT_MAX ÷ 4 entries are refused.
The data area and the padding
The data area runs from the data offset to the end of the file, as an array of clusters. Most are
allocated by the BAT; some may be allocated by the header’s ext_off and by extensions. The
bytes between the end of the BAT and the data area are padding.
WithoutFreeSpace: ifdata_offis 0 the data area starts at the end of the BAT (64 + 4 × entries bytes) rounded up to a whole sector. If it is not 0 it should be aligned to the sector size, and it is recommended to align it to the cluster size for new images.WithouFreSpacExt:data_offmust be non-zero and aligned to the cluster size.- Either way
data_offmust be at least the end of the BAT in sectors (rounded up to a cluster for the second signature) and not past the end of the file; QEMU checks this and repairs it.
An image’s size must be a multiple of the cluster size. If it is not, ploop assumes the last extension of the file failed in the middle of a transaction, and starts new allocations at the size rounded down to a cluster. Updates to the BAT are made only after the data clusters are safely written, so a crash can leave an unused (lost) cluster but never a BAT entry that points at unwritten data.
The format extension
A cluster that carries optional features, found through the header’s ext_off. It must lie
inside the file and meet the same requirements as a BAT cluster. QEMU drops (ignores) an extension that
lies outside the file or whose magic or MD5 is wrong, and refuses to open the image if the extension is
damaged inside while the image was closed correctly.
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 8 | magic | Must be 0xAB234CEF23DCEA87 (ploop: FORMAT_EXTENSION_MAGIC). |
| 8 | 16 | MD5 checksum | The MD5 of the whole extension cluster except its first 24 bytes (QEMU hashes the rest of the cluster, cluster size − 24 bytes). |
| 24 | … | features | Feature sections, one after another, the last one End of features. |
A feature section
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 8 | magic | The feature’s identifier: 0 is End of features, 0x20385FAE252CB34A is Dirty bitmap. (ploop: EXT_MAGIC_DIRTY_BITMAP.) |
| 8 | 8 | flags | Bit 0, NECESSARY: if the software cannot load the extension (an unknown magic number, or an error), the file must not be changed; if the flag is not set and loading fails, the extension should be dropped. Bit 1, TRANSIT: an unknown extension with this flag must be left as it is. With neither flag the extension is dropped. (QEMU’s code refuses any extension with a non-zero flags word.) |
| 16 | 4 | data_size | The size of the data that follows, in bytes. |
| 20 | 4 | unused | Aligns the header to 8 bytes. |
| 24 | data_size | data | The feature’s data, then padding to the next 8-byte boundary, where the next section starts. |
The last section must be End of features, with every field 0 (ploop’s structure names the three
parts BlockCheck, BlockElementHeader and, for the bitmap,
DirtyBitmapExtension::Raw). A section’s data, rounded up to 8, must fit in what remains of the
cluster, and there must be room for a 24-byte section header.
The dirty bitmap feature
Stores a QEMU-style dirty bitmap in the image: one bit for each granularity sectors of the disk, set when that part changed (used for incremental backup).
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 8 | size | The bitmap size: it should equal the disk size in sectors (QEMU refuses a bitmap whose size differs). |
| 8 | 16 | id | An identifier for backup consistency checking (QEMU uses it as the bitmap’s name, written as a UUID). |
| 24 | 4 | granularity | Sectors for each bit; must be a power of two (QEMU also requires the size in bytes to fit 32 bits). |
| 28 | 4 | l1_size | The number of entries in the L1 table. QEMU requires it to equal the number of clusters the serialised bitmap needs, and the table must fit in the feature’s data. |
| 32 | 8 × l1_size | l1_table | One 64-bit entry for each cluster of the bitmap data. Entry i is for bytes i × cluster size onward of the bitmap. |
| L1 entry | Meaning |
|---|---|
| 0 | All the bits of that part of the bitmap are 0. |
| 1 | All the bits of that part are 1. |
| anything else | The sector number (in the file) of a cluster that holds that part of the bitmap. The byte offset is entry × 512 + (offset in bitmap data) mod cluster size. QEMU requires it to lie at or after the data area and the whole cluster inside the file. |
When an image was left open (in-use word Ynot), QEMU marks the stored
bitmaps inconsistent.
What a valid image obeys
- The signature is
WithoutFreeSpaceorWithouFreSpacExt, and the version is 2. tracksis not 0; the BAT covers the disk (nb_bat_entries × tracks ≥ nb_sectors).- For
WithoutFreeSpacethe high 4 bytes of the size are 0. - The in-use word is 0x312E3276 or 0 for a closed image; 0x746F6E59 says it needs checking.
- The data offset follows the rules for its signature, and is not before the end of the BAT or past the end of the file.
- Every non-zero BAT entry is inside the file, at or after the data area, unique, and cluster-aligned from the data offset.
- The format extension, if there is one, has its magic, a matching MD5, a final End of features, and features that fit inside the cluster.
- Dirty bitmap: size equal to the disk size, a power-of-two granularity, an L1 table that fits, and L1 entries that point into the file.
Example: the start of a real image
The first 0x60 bytes of the expanding image of an Ubuntu 24.04 ARM64 machine made by Parallels Desktop (read directly from the file; the image is a 64 GiB disk in a 56.6 GB file):
00000000: 5769 7468 6f75 7446 7265 6553 7061 6365 WithoutFreeSpace
00000010: 0200 0000 1000 0000 0000 0400 0008 0000 ................
00000020: 0000 0100 0000 0008 0000 0000 7064 3137 ............pd17
00000030: 0008 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0010 0000 0018 0000 0020 0000 0010 ae00 ......... ......
00000050: 00b8 9b00 00b0 9b00 00a8 9b00 00a0 9b00 ................
| Field | Bytes | Value |
|---|---|---|
| magic | “WithoutFreeSpace” | sector-counting image |
| version | 02 00 00 00 | 2 |
| heads | 10 00 00 00 | 16 |
| cylinders | 00 00 04 00 | 262,144 |
| tracks | 00 08 00 00 | 2,048 sectors = 1 MiB clusters |
| nb_bat_entries | 00 00 01 00 | 65,536 |
| nb_sectors | 00 00 00 08, 00 00 00 00 | 134,217,728 sectors = 64 GiB (65,536 × 2,048) |
| in_use | 70 64 31 37 | 0x37316470, “pd17” |
| data_off | 00 08 00 00 | 2,048 sectors: the data area at 1 MiB (the BAT’s 262,144 bytes plus the header, rounded up to a cluster) |
| flags | 00 00 00 00 | none |
| ext_off | 00 00 00 00 00 00 00 00 | no format extension |
| BAT entry 0 | 00 10 00 00 | sector 4,096: the cluster at byte 2 MiB |
| BAT entry 1 | 00 18 00 00 | sector 6,144: the cluster at byte 3 MiB |
Heads × cylinders × 32 sectors per track is the disk size in sectors, and 32 is the Sectors
value of the image’s DiskDescriptor.xml. The header does not
hold the sectors per track.
Sources: QEMU docs/interop/parallels.rst, block/parallels.c,
block/parallels.h, block/parallels-ext.c (master, read 2026-11-14); OpenVZ ploop
include/ploop1_image.h (master, read 2026-11-14); the two real images are from the owner’s Mac.