
Have you ever worked with virtual machines, created virtual disks? If yes, then you probably noticed such convenient features as dynamic increase in disk size (the ability to store only what was recorded) and the ability to create snapshots - snapshots of the disk status. If you are interested in knowing exactly how this capabilities are achieved and how the data is stored in the VHD and VHDX files - welcome under cat.
Fixed drives

A virtual disk is usually a simple file within which everything that a virtual machine writes to a disk device is stored. For fixed disks, a full file is immediately allocated, which does not change in size in the future.
However, here it is necessary to make a reservation, and recall the possibilities of many file systems to create "compressed" files. Usually, compression is achieved by not storing file-filled blocks (for example, NTFS, XFS, and VMFS do so). Even if your file system is free 500GB, you can easily create a fixed 1TB virtual disk and work with it until you run out of space.
')
Fixed disks can be stored in two ways:
- Simple image files . They are RAW, they are flat (* flat.vmdk for example). Here the name is clear. Such files (almost) do not contain any specific metadata. A fixed VHD may contain 512 bytes of its metadata at the end of the file. Otherwise, it can not be distinguished from a copy of the "real" disk to a file.
- Degenerate dynamic disks . Such disks are stored in the same format as dynamic ones, only all the required space is allocated immediately upon creation. As if the dynamic disk immediately after creation was prescribed from beginning to end. You will get this file if you create a fixed VHDX.
Dynamic VHD
File systems write to disk in a chaotic manner. To ensure a gradual increase in the virtual disk file under such conditions, a translation system is necessary. One of the easiest ways to translate is a table, which for each logical block will indicate its location within the file or say that such a block has not yet been allocated.
It is this idea that is in the format of dynamic VHD (and not only). The logical space of a virtual disk (what the OS inside the virtual machine sees as a disk) is divided into blocks of equal size, for example, 2 megabytes each, which are addressed using BAT - Block Allocation table.
When creating snapshots, it may be necessary to give the status “empty-allocated” not a separate block, but a separate sector in the block. Therefore, each block is supplied with a bitmap, which is written before the block. With a logical sector size of 512 bytes and a block size of 2 megabytes, the bitmap for the block will occupy exactly 1 sector (512 * 8 = 2,097,152 / 512). Those. one generalized block will occupy 4097 sectors.
In addition to the BAT and generic blocks, the VHD file also contains the Hard Disk Footer (512B) structure at the very end and a copy at the very beginning. And Dynamic Disk Header (1024 bytes) at the beginning of the file. They store various metadata about a virtual disk: its size, format version, time stamps, block size, BAT offset, number of entries in it, and so on.
To summarize, the contents of the VHD file looks like this (proportions are conditional):
Dynamic VHDX
The format of a dynamic VHDX is a general idea similar to VHD - the logical space is also divided into blocks that are addressed by a special translation table, there are also bitmaps to clarify the status of a particular sector. But there are many differences in the details.
To begin with, in VHDX, the size of one bitmap is fixed - 1 megabyte. And it already covers several blocks. For example, with a logical sector size of 512 bytes (VHDX can also “give up” a sector of 4096 bytes) and a block size of 2 megabytes, one bitmap “covers” 2,048 blocks. This value is also called the
chunk ratio .
The second difference is that the block with bitmap is independently addressed from BAT. First, there are 2048 cells (chunk ratio) that address the corresponding data blocks, then there is a cell addressing the bitmap block, and so on.
The next difference is that the BAT entry now also stores the status of the block. The data block is:
- NOT_PRESENT - absent;
- UNDEFINED - undefined (space is allocated in the file, but there is irrelevant data there);
- ZERO - filled with zeros (space in the file is not allocated);
- UNMAPPED - UNMAP command was executed for all sectors of the block;
- FULLY_PRESENT - the block is fully present:
- PARTIALLY_PRESENT - the block is partially present.
An entry for a bitmap block can have only two statuses:
- NOT_PRESENT - no bitmap block;
- PRESENT - a bitmap block is present.
Bitmaps and partially present data blocks can only be in differential disks, which appear when creating snapshots (I will tell about them below). In other cases, there are no bitmaps, and data blocks have other statuses.
In general, the structure of a VHDX file looks like this:
Briefly about the remaining sections:
- At the very beginning of the disk is the FileIdentifier structure - it contains the signature and comment about the application that created the VHDX file. This is it opened in the HEX editor in the screenshot at the beginning of the article;
- Further there are two versions (for protection against failure) of the header. It shows the offset and size of the log, as well as some other parameters;
- A log is a cyclic buffer through which all operations on metadata, except Headers, are performed. This is done, again, to protect against sudden outages;
- Introduced the concept of regions. The specification lists only 2 types of regions: BAT and Metadata, but it is assumed that there may be more. Regions are addressed using the table of regions, which is stored in two copies (and here is the protection from failures);
- Metadata records are stored in the Metadata region, they are addressed using a metadata table, to which applications can add metadata of their own types. According to the specification, we have to find records about block size, logical sector size, disk size, and others.
Snapshots
Snapshot is a snapshot of the state of a virtual disk at some point in time. Having such a snapshot, we can roll back all changes made after this point.
If we are talking about VHD and VHDX disks, then when creating a snapshot, a new file is created in which all subsequent changes are recorded. This file is called "delta" or "differential disk" (from the English. Differencing).
Earlier we said that the format of dynamic disks allows you to store only recorded data. The same format is great for storing only modified data. Yes, delta files have exactly the same format as dynamic disks. Only the interpretation of free blocks changes. For a delta, a free block means that you should not just return the zeros, but try to read the block from the previous layer — if there is a previous delta, then from it, and if not, then from the basic disk.
If we remove the upper delta, we get the previous fixed state, and if both are, then the earliest.
A look at the data recovery
In terms of data recovery, virtual disks are bad first of all because additional translation levels are introduced between the file and the disk.
Each level is additional data mixing, which increases fragmentation, and a potential point of failure. Any BAD sector now has much more possibilities to make a lot of problems.
In general, to read the data of a file from a virtual disk, you must have
- physical disk file system metadata that describes the location of the virtual disk file;
- metadata of the virtual disk itself (BAT, etc.);
- file system metadata on the virtual disk describing the location of the file.
If you are very lucky, the data recovery task can turn into a puzzle assembly without a sample. Not that I discouraged using virtual disks, but the easier the data is stored, the easier it will be to restore them.
Literature
- Virtual Hard Disk Image Format Specification
- VHDX Format Specification v1.00