Before a writer changes the BAT or the metadata, it writes the change to the log, so that after a crash the change can be completed (replayed) or ignored, and the file is never left half-updated. Guest data itself is not logged. The log is a circular buffer at the place the header gives, a whole number of megabytes long, made of 4 KiB sectors. All numbers are little-endian.
Log entry header
This page follows [MS-VHDX] revision 8.0, section 2.3. Each log entry is a run of 4 KiB sectors, aligned to 4 KiB: one or more descriptor sectors, then one data sector per data descriptor. The first descriptor sector holds the 64-byte entry header and up to 126 descriptors of 32 bytes; further descriptor sectors hold up to 128.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 4 | text | Signature | loge. |
4 | 4 | uint32 | Checksum | CRC-32C over the whole entry (as long as the entry length says), computed with this field as 0. |
8 | 4 | uint32 | Entry length | Bytes in the entry, a multiple of 4 KiB. |
12 | 4 | uint32 | Tail | Offset from the start of the log of the first entry of the sequence that ends with this entry; a multiple of 4 KiB. It may point to the entry itself. |
16 | 8 | uint64 | Sequence number | Raised by one for each entry; greater than 0. |
24 | 4 | uint32 | Descriptor count | Number of descriptors in the entry; may be 0. |
28 | 4 | – | Reserved | 0. |
32 | 16 | GUID | Log GUID | The header’s log GUID when the entry was written; if it differs from the header’s now, the entry is not valid. |
48 | 8 | uint64 | Flushed file offset | A file size, in bytes, that the file was known to have reached stably when the entry was written; a multiple of 1 MiB. A shorter file means it was truncated. |
56 | 8 | uint64 | Last file offset | A size, in bytes, that all the file’s structures fit in; a multiple of 1 MiB. After replay the file is extended to at least this size. |
Descriptors
32 bytes each, straight after the entry header. A zero descriptor says “write zeros here”; a data descriptor says “write this data sector here”.
| Offset | Size | Zero descriptor | Data descriptor |
|---|---|---|---|
0 | 4 | Signature zero | Signature desc |
4 | 4 | Reserved, 0 | Trailing bytes: the last 4 bytes of the 4 KiB sector to write (bytes 4092–4095) |
8 | 8 | Zero length: bytes to zero, a multiple of 4 KiB | Leading bytes: the first 8 bytes of the sector to write (bytes 0–7) |
16 | 8 | File offset to start zeroing, a multiple of 4 KiB | File offset to write the sector to, a multiple of 4 KiB |
24 | 8 | Sequence number, equal to the entry’s | Sequence number, equal to the entry’s |
Data sectors
One 4 KiB sector per data descriptor, in the same order. Its first 8 and last 4 bytes are replaced by markers, which is why the descriptor keeps the real bytes.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 4 | text | Signature | data. |
4 | 4 | uint32 | Sequence high | The high 32 bits of the entry’s sequence number. |
8 | 4084 | bytes | Data | Bytes 8 to 4091 of the sector to write. |
4092 | 4 | uint32 | Sequence low | The low 32 bits of the sequence number. |
Replaying the log
When the log is not empty (the header’s log GUID is not zero), a reader must replay it before any other access; one that cannot must not open the file. Replay uses the active sequence: the newest valid and complete run of entries. A sequence is valid when every entry checks (signature, checksum, descriptors, log GUID), each entry after the tail has a sequence number one greater than the one before, and the head entry’s tail lies within the sequence. The specification scans the log from offset 0 in 4 KiB steps, building candidate sequences and keeping the one whose head has the highest sequence number. If none is found the file is corrupt; if the file is shorter than the head entry’s flushed file offset, it has been truncated. Otherwise every descriptor of every entry, from the tail entry on, is applied in order (writing zeros or the restored data sector, extending the file if needed), and the file is then extended to at least the head entry’s last file offset.