Convert a VHD image from a native Windows backup to raw format using qemu-img, and write it directly to a disk or partition with the Linux dd command
I’ve recently been evaluating native Windows Server Backup as an option for bare-metal backup and recovery for our remaining physical servers at work. The utility creates several XML files and a VHD image for each partition it backs up. It seems to work ok for the most part, but I ran into a problem when I came across a system that for some unknown reason had a 38MB boot partition with insufficient space to create a VSS snapshot, thus preventing the tool from properly backing up the partition. I’ve read all the articles about allocating storage on a separate partition to get VSS to behave, but I could never get it to function correctly.
This got me thinking… I have some personal trust issues with the reliability of Microsoft products to begin with and now I’m having these problems which are just reinforcing the fear of something going wrong during the restore process. This lead me to start researching restore options using the VHD files produced by the native Windows Server Backup.
Option 1 is to do a restore using a Windows installation CD and select the restore option. Option 2 is to mount the VHD and manually copy files. This option is really only good for individual file restores. Obviously this is not something you’d want to do for a bare metal restore. Option 3 is to restore the VHD image directly to disk. This is the option I was most interested in and it made sense to me that there would be a straightforward way of doing this as every other bare metal backup solution I’ve used had this sort of option. While searching for a tool to do a VHD to disk image I found “VHD2Disk”. Unfortunately this tool was designed to do just that… write a VHD to DISK. No option for writing to a partition. Feel free to correct me if I’m wrong, but I see no way of ever getting 2 partitions on a single disk with this tool which makes it useless for my purposes.
After finding that there were no tools to do a image to disk write I became curious to find out if there was a way to just use the dd command in linux. After all, I would have instinctively turned to dd if this were something I were doing with linux. dd does block by block copying and a block is a block regardless of which OS you’re using. I quickly learned this is not something that can be done directly with a VHD because they are not in raw format, but the qemu-img supports VHD format and has the ability to convert them to raw image format. Below you will find the detailed instructions of how to convert the VHD, and use dd to write your new image to disk or partition. I’ll also give some details about getting the system to boot if you’re like me in this case and you don’t have a good backup of the boot partition.
The backup directory created by Windows Server Backup will look like this. I’ve highlighted the “interesting files” that I’ll mention throughout the article. The one ending in “Components.xml” has useful information about the disk partition layout that can come in handy when recreating partitions on your new disk. The .vhd file is the actual image data.
First thing we need to do is convert the VHD image to raw format. To do so, you’ll need access to a linux environment and use the qemu-img command. I’d recommend using either a Clonezilla or GParted liveCD as they both have all sorts of utilities pre-installed for disk imaging and partitioning. Boot the CD on the system you will be using for the restore image. When it finishes booting type the following commands to install qemu-img: (You may need to type sudo before each command if you’re not root. Keep this in mind for the remaining command as well.)
apt-get update apt-get install qemu-utils -y
My backups are on a windows file share so I’ll use the following command to mount them to the /mnt directory:
mount -t cifs -o username=YOURUSERNAME,domain=YOURDOMAIN //HOSTNAME/PATH /mnt
You’ll be prompted for your password to mount the share. Be sure to replace YOURUSERNAME, YOURPASSWORD, HOSTNAME, and PATH with the information appropriate for your environment.
Next change into the directory containing the VHD file you need to convert: (the path used in this command may vary greatly depending on your environment)
cd /mnt/WindowsImageBackup/SERVERNAME/Backup\ 2016-08-05\ 133013/
Use the following command to convert the VHD image into raw format:
qemu-img convert -f vpc -O raw c9887432-6c68-11e0-a354-806e6f6e6963.vhd myserver.raw
You’ll need to repeat this command for any additional partitions you need to convert. Be sure to change the .vhd and .raw filenames to those appropriate for your environment. To be clear, the .vhd filename should be the one that exists in this directory like the highlighted file in the screenshot above, and the .raw filename can be whatever you want to name it.
You’ll notice a new file will be created that will reflect the full size of the partition for the data it contains. This is expected considering the nature of the raw image format.
-rwxr-xr-x 1 root root 668 Aug 5 13:51 BackupSpecs.xml -rwxr-xr-x 1 root root 61G Aug 5 13:51 c9887432-6c68-11e0-a354-806e6f6e6963.vhd -rwxr-xr-x 1 root root 149G Aug 5 21:39 myserver.raw
The conversion process can take a long time depending on the size of the partition. You can use the following command to output the status of the process: (I’ve noticed there is often some delay before the command writes to stdout)
root@debian# kill -SIGUSR1 `pidof qemu-img` root@debian# (22.03/100%)
Next you’ll need to create the 100MB boot partition (unless you’re restoring only a single partition and all others are fully intact) and any additional partitions the system originally had. I’ll assume you know how to do this, but you can use the output below for help if necessary. In the event that you don’t know the original partition layout, you can use the raw image size as a hint or the “Components.xml” file generated by Windows Server Backup in the backup directory for the server. With the values BytesPerSector, PartitionOffset, and PartitionLength contained in that file, you can re-create the exact partition table.
BytesPerSector / PartitionOffset = starting sector
(BytesPerSector / PartitionOffset) + (BytesPerSector / PartitionLength) = ending sector
fdisk /dev/sda Welcome to fdisk (util-linux 2.27). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. Command (m for help): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): p Partition number (1-4, default 1): 1 First sector (2048-335544319, default 2048): Last sector, +sectors or +size{K,M,G,T,P} (2048-335544319, default 335544319): +100M Created a new partition 1 of type 'Linux' and of size 100 MiB. Command (m for help): t Selected partition 1 Partition type (type L to list all types): 7 Changed type of partition 'Linux' to 'HPFS/NTFS/exFAT'. Command (m for help): a Selected partition 1 The bootable flag on partition 1 is enabled now. Command (m for help): n Partition type p primary (1 primary, 0 extended, 3 free) e extended (container for logical partitions) Select (default p): p Partition number (2-4, default 2): First sector (206848-335544319, default 206848): Last sector, +sectors or +size{K,M,G,T,P} (206848-335544319, default 335544319): Created a new partition 2 of type 'Linux' and of size 159.9 GiB. Command (m for help): t Partition number (1,2, default 2): 2 Partition type (type L to list all types): 7 Changed type of partition 'Linux' to 'HPFS/NTFS/exFAT'. Command (m for help): p Disk /dev/sda: 160 GiB, 171798691840 bytes, 335544320 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x5dcc5434 Device Boot Start End Sectors Size Id Type /dev/sda1 * 2048 206847 204800 100M 7 HPFS/NTFS/exFAT /dev/sda2 206848 335544319 335337472 159.9G 7 HPFS/NTFS/exFAT Command (m for help): w The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks.
If you created the 100MB boot partition, format it as NTFS and the default “System Reserved” label:
mkfs.ntfs -f /dev/sda1 -L "System Reserved"
The VHDs always store the image as a partition within the image which means we have to get the offset to determine where the data actually begins in the raw image before we write it to disk. Use the following command:
fdisk -l myserver.raw Disk myserver.raw: 148.1 GiB, 158967767040 bytes, 310483920 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x00000000 Device Boot Start End Sectors Size Id Type myserver.raw1 128 310483071 310482944 148.1G 7 HPFS/NTFS/exFAT
The values “512” and “128” are what we need from this output. This tells us that the block size is 512 bytes and the partition starts at sector 128. Now we have all the information we need to give dd to write the image to our physical disk using this command:
dd if=myserver.raw bs=512 skip=128 of=/dev/sda2
You’ll need to repeat this command for any additional partitions you need to restore. You can use the following command to get the status of the dd process:
kill -SIGUSR1 `pidof dd` 369665+0 records in 369665+0 records out 189268480 bytes (189 MB) copied, 12.4497 s, 15.2 MB/s
When the dd command finishes writing the image to /dev/sda2, you should be able to mount the NTFS partition and view the files like so:
mkdir /sda2 mount -t ntfs-3g /dev/sda2 /sda2 ls -l /sda2 total 6267677 lrwxrwxrwx 2 root root 60 Jul 14 2009 Documents and Settings -> /sda2/Users -rwxrwxrwx 1 root root 6327107584 Jul 31 11:03 pagefile.sys drwxrwxrwx 1 root root 4096 Feb 17 2015 Patch Management drwxrwxrwx 1 root root 0 Jul 14 2009 PerfLogs drwxrwxrwx 1 root root 4096 Jun 17 07:44 ProgramData drwxrwxrwx 1 root root 4096 Jul 21 2014 Program Files drwxrwxrwx 1 root root 4096 Mar 22 2013 Program Files (x86) drwxrwxrwx 1 root root 0 Apr 21 2011 Recovery drwxrwxrwx 1 root root 8192 Apr 22 19:10 $Recycle.Bin drwxrwxrwx 1 root root 4096 Aug 6 13:30 System Volume Information drwxrwxrwx 1 root root 4096 May 30 14:16 Users drwxrwxrwx 1 root root 24576 May 30 14:07 Windows
After you’ve restored all of your partitions, its time to reboot. Don’t forget to make sure the boot flag is on for your boot partition. If you didn’t have a copy of the boot partition, you’ll need to use the windows installation CD to repair the MBR. This usually involves a combination of the startup repair option available from the installation CD as well as some of the boot repair utilities that you can use from a command prompt on the windows installation CD. Like these:
bootrec /fixboot bootrec /fixmbr bootrec /rebuildbcd cdromdriveletter:\boot\bootsect\bootrec /nt60 SYS /mbr
UPDATE:
I recently found a cool new way of mounting the VHD image directly and imaging from the virtual block device instead of waiting for the qemu-img conversion and using up all of your precious storage for the VHD image you already have as well as a raw copy of the data. Below are the commands to enable the NBD kernel module with the right arguments, mount the VHD image as a virtual block device, and perform a dd copy to your physical disk. This is assuming you’ve already booted the liveCD, installed the qemu-utils, mounted the media containing your backups, and changed directly to the path containing the VHDs.
rmmod nbd modprobe nbd max_part=16 qemu-nbd -c /dev/nbd0 c9887432-6c68-11e0-a354-806e6f6e6963.vhd dd if=/dev/nbd0p1 of=/dev/sda2
You can see I’m using /dev/nbd0p1 as the source for the dd command. This is because as I mentioned earlier in the article, each VHD image contains a partition. nbd0p1 is referencing the first partition (the only partition) in the nbd0 virtual block device. Previously we had to specify the block size and offset with the dd command to specify where the partition started. Use the following command to remove the virtual block device for the VHD image.
qemu-nbd -d /dev/nbd0
If you have any questions or if you found this post useful, please leave a comment!