Oracle has not published a VDI specification; the structures are defined in
VirtualBox’s open source code (src/VBox/Storage/VDICore.h), which this page
follows. A VDI file is a pre-header, a header, a block map and the data blocks. All
numbers are little-endian.
Pre-header
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x00 | 64 | text | szFileInfo | A banner line, padded with zeros. Current VirtualBox source (VDICore.h, VDI_IMAGE_FILE_INFO) writes <<< Oracle VirtualBox Disk Image >>> and a line feed, and calls the field “for eyes only”; files from older versions may carry other product names, which were not checked against a source. The signature after it, not this text, identifies the file. |
0x40 | 4 | uint32 | u32Signature | 0xBEDA107F, stored 7F 10 DA BE. |
0x44 | 4 | uint32 | u32Version | Major version in the high 16 bits, minor in the low: 0x00010001 is 1.1, the current version. Version 0 files have a different header. |
Header, version 1.1
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x48 | 4 | uint32 | cbHeader | Size of this header: 384 (0x180), or 400 (0x190) with the LCHS geometry at the end. |
0x4C | 4 | uint32 | u32Type | See image types. |
0x50 | 4 | uint32 | fFlags | Image flags. VirtualBox turns them into its own image flags by shifting them left 8 bits; the individual values are not defined in VDICore.h. |
0x54 | 256 | text | szComment | A description, zero-terminated. |
0x154 | 4 | uint32 | offBlocks | Where the block map starts; aligned to 1 MiB by current VirtualBox. |
0x158 | 4 | uint32 | offData | Where the data blocks start; aligned to 1 MiB. |
0x15C | 4 | uint32 | Cylinders | Legacy geometry (LegacyGeometry), 16 bytes in all. |
0x160 | 4 | uint32 | Heads | – |
0x164 | 4 | uint32 | Sectors | – |
0x168 | 4 | uint32 | Sector size | 512. |
0x16C | 4 | uint32 | u32Dummy | Unused. |
0x170 | 8 | uint64 | cbDisk | The virtual disk size in bytes. |
0x178 | 4 | uint32 | cbBlock | Bytes of disk per block, a power of two; 1 MiB by default. |
0x17C | 4 | uint32 | cbBlockExtra | Extra bytes stored before each block’s data, a power of two and a multiple of 512; normally 0. |
0x180 | 4 | uint32 | cBlocks | Blocks in the disk, so entries in the block map. |
0x184 | 4 | uint32 | cBlocksAllocated | Blocks stored in the file. |
0x188 | 16 | UUID | uuidCreate | The image’s UUID. |
0x198 | 16 | UUID | uuidModify | Changed each time the image is modified. |
0x1A8 | 16 | UUID | uuidLinkage | The parent image’s UUID, for a differencing image. |
0x1B8 | 16 | UUID | uuidParentModify | The parent’s modification UUID when this image was linked to it. |
0x1C8 | 16 | geometry | LCHSGeometry | Logical cylinders, heads, sectors and sector size, four uint32s. Present only when cbHeader ≥ 400. |
Header, version 0
Early images. The block map follows the header directly, and the data follows the block map.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x48 | 4 | uint32 | u32Type | Image type. |
0x4C | 4 | uint32 | fFlags | Image flags. |
0x50 | 256 | text | szComment | Description. |
0x150 | 16 | geometry | LegacyGeometry | Cylinders, heads, sectors, sector size. |
0x160 | 8 | uint64 | cbDisk | Disk size in bytes. |
0x168 | 4 | uint32 | cbBlock | Block size. |
0x16C | 4 | uint32 | cBlocks | Blocks in the disk. |
0x170 | 4 | uint32 | cBlocksAllocated | Blocks stored. |
0x174 | 16 | UUID | uuidCreate | – |
0x184 | 16 | UUID | uuidModify | – |
0x194 | 16 | UUID | uuidLinkage | Parent UUID. The block map starts right after, at 0x1A4. |
Image types
| Value | Name | Meaning |
|---|---|---|
| 1 | VDI_IMAGE_TYPE_NORMAL | Dynamically growing base image |
| 2 | VDI_IMAGE_TYPE_FIXED | Fixed-size base image, every block allocated |
| 3 | VDI_IMAGE_TYPE_UNDO | Undo image (supports undo and commit) |
| 4 | VDI_IMAGE_TYPE_DIFF | Differencing image, on top of a parent |
Block map
An array of cBlocks uint32 entries at offBlocks, one per
block of the virtual disk. An entry is the block’s index in the data area, or a
marker:
| Value | Name | Meaning |
|---|---|---|
0xFFFFFFFF | VDI_IMAGE_BLOCK_FREE | Not allocated; its contents are undefined (a differencing image reads it from the parent). |
0xFFFFFFFE | VDI_IMAGE_BLOCK_ZERO | Not allocated; reads as zeros. |
| any other | – | The block is stored at offData + entry × (cbBlock + cbBlockExtra), its data starting cbBlockExtra bytes in. |
Blocks are stored in the order they were first written, so the entries are not in order.
Example
The start of a dynamic 8 GiB image: the banner, the signature and version (marked), then the start of the header, and further on the offsets and sizes.
00000000 3C 3C 3C 20 4F 72 61 63 6C 65 20 56 69 72 74 75 <<< Oracle Virtu
00000010 61 6C 42 6F 78 20 44 69 73 6B 20 49 6D 61 67 65 alBox Disk Image
00000020 20 3E 3E 3E 0A 00 00 00 00 00 00 00 00 00 00 00 >>>...........
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040 7F 10 DA BE 01 00 01 00 90 01 00 00 01 00 00 00 ................
…
00000150 00 00 00 00 00 00 10 00 00 00 20 00 00 00 00 00 .......... .....
…
00000170 00 00 00 00 02 00 00 00 00 00 10 00 00 00 00 00 ................
00000180 00 20 00 00 23 01 00 00 … . ..#...
Reading it: signature 0xBEDA107F; version 1.1; header size 400; type 1,
dynamic; block map at 0x100000 and data at 0x200000; disk
size 0x200000000, 8 GiB; block size 1 MiB with no extra bytes; 8,192
blocks, of which 291 (0x123) are stored.