The block allocation table (BAT) is a region of 64-bit little-endian entries, found through the region table. It says where each payload block (a block of the virtual disk) is in the file, and, for differencing disks, where each sector bitmap block is.
Entries
| Bits | Name | Meaning |
|---|---|---|
| 0–2 | State | See states. |
| 3–19 | Reserved | 0. |
| 20–63 | FileOffsetMB | Where the block is, in megabytes from the start of the file. |
So the byte offset of a block is the entry with its low 20 bits cleared:
entry & 0xFFFFFFFFFFF00000.
States
| Value | Payload block | Meaning |
|---|---|---|
| 0 | PAYLOAD_BLOCK_NOT_PRESENT | The default for new blocks of dynamic and differencing disks. In a fixed or dynamic disk the contents are undefined (a reader may return arbitrary data, zeros or the block’s earlier contents); in a differencing disk they are in the parent. The file offset is reserved. |
| 1 | PAYLOAD_BLOCK_UNDEFINED | Contents not defined in the file; reads may return arbitrary data, zeros or the earlier contents. The file offset may be kept, zeroed, or point to the earlier contents. |
| 2 | PAYLOAD_BLOCK_ZERO | The block reads as zeros. The file offset is reserved. |
| 3 | PAYLOAD_BLOCK_UNMAPPED | Every sector of the block was unmapped (trimmed); reads return zeros or the block’s contents from before the unmap. |
| 4, 5 | – | Reserved. (QEMU notes that version 0.95 of the specification used 5 for unmapped.) |
| 6 | PAYLOAD_BLOCK_FULLY_PRESENT | The whole block is in the file at the given offset (the default for every block of a fixed disk). A differencing disk’s sector bitmap is not consulted. |
| 7 | PAYLOAD_BLOCK_PARTIALLY_PRESENT | Differencing disks only: the block is at the given offset, but each sector is read from here or from the parent as the sector bitmap says; that bitmap block must be present. |
| Value | Sector bitmap block | Meaning |
|---|---|---|
| 0 | SB_BLOCK_NOT_PRESENT | No bitmap in the file. Every sector bitmap entry of a fixed or dynamic disk is in this state. |
| 6 | SB_BLOCK_PRESENT | The bitmap is in the file at the given offset; required in a differencing disk when any payload block of the chunk is partially present. |
Sector bitmap values 1–5 and 7 are reserved. (Source: [MS-VHDX] revision 8.0, sections 2.4 and 2.5.)
Chunks and sector bitmaps
Payload entries are grouped in chunks, and every chunk is followed by one sector bitmap entry. A sector bitmap block is 1 MiB, one bit per logical sector, so it covers 223 sectors. The chunk ratio is how many payload blocks that is:
chunk ratio = 223 × logical sector size ÷ block size
For 32 MiB blocks and 512-byte sectors the ratio is 128: BAT entries 0 to 127 are payload blocks 0 to 127, entry 128 is the sector bitmap for them, entries 129 to 256 are payload blocks 128 to 255, and so on. The payload block b is entry b + b ÷ ratio (dropping the remainder), and its sector bitmap is entry (b ÷ ratio + 1) × (ratio + 1) − 1.
In a sector bitmap, bit i (least significant bit of each byte first) stands for sector i of the chunk: 1 means the sector is in this file, 0 that it must be read from the parent.
Fixed and dynamic disks have the same interleaved layout, with every sector bitmap entry not present; keeping the entries avoids rewriting the table when converting a disk between dynamic and differencing. In a fixed or dynamic disk the last entry locates the last payload block; in a differencing disk the table must reach the sector bitmap entry of the chunk holding the last payload block. (The specification gives the entry counts as formulas; they follow from this layout.)