How to copy VirtualBox VDI contents to a partition and dual boot the OS from it?

I'm a Linux user but I keep a compressed Windows XP ISO with me on a pen drive for the case I absolutely need Windows to do something. This works in VirtualBox most of the time.

But now I want to play some games, so I would like to run the Windows image natively. My computer don't have CD drive so cannot just burn the ISO and make an install normally.

What I trying to do is moving the installed Windows image to a physical NTFS partition on my HDD and set up GRUB to let me dual boot it.

I found many tutorials that deal with making VDI to physical drive. But they assume I want to overwrite my entire drive. Moving the raw disk image with dd to the partition resulted in a corrupt partition.

I also tried the VMDK trick to use that empty partition and install the Windows on it. Although the text mode phase of the installation finishes without problems, the VM won't work, either crashes and keeps rebooting or just immediately freezes (depending on how I created the VMDK, with -rawdisk /dev/sda3 or -rawdisk /dev/sda -partition 3).


I spent all yesterday with research, and finally was able to make Windows XP boot. It should be somewhat similar for other OS-es too. But the operation is everything but not trivial.

Because this a dangerous operation, I recommend backing up your precious data.

Here is the steps:

Preparation

In the case Windows XP you need to make it forget the current disk letter and partition settings, so you need to erase all values from the HKEY_LOCAL_MACHINE/MountedDevices key on the virtual machine before the migration. The kernel will rebuild it on the next reboot. On Linux virtual machines, this mean fixing the fstab after migration.

Mounting the VDI image

You cannot just copy the entire VDI to a partition, because it contains an MBR too. You need to copy only the virtual partition, so first you need to find a way to mount the VDI.

You need the nbd driver and the qemu-nbd command. On Ubuntu it's in the qemu-kvm package.

First load the nbd driver:

# modprobe nbd

This should make some nbd devices in /dev.

Then mount the VDI:

# qemu-nbd -c /dev/nbd0 path_to.vdi

This should make /dev/nbd0p1, /dev/nbd0p2, /dev/nbd0p3 etc for all virtual partitions. These can be mounted like any ordinary devices.

Migrating the partition

Use dd for that, unmount both partitions before the operation:

# dd -if=<nbd_device> -of=<real_partition>

eg.:

# dd -if=/dev/nbd0p3 -of=/dev/sda4

Operation of the dd is silent, this may take several minutes, even a half hour. During the operation you may open a terminal and use fdisk -l to see all is going well.

UPDATE:

Apparently nbd is not a foolproof solution. It may cause I/O error and make dd fail. Moreover next try fails immediately. You can also try making a raw disk VMDK with this command (under linux):

$ VBoxManage internalcommands createrawvmdk -filename physical.vmdk -rawdisk /dev/sda

You need to be in the disk group to make it work. Then add this vmdk to the virtual machine as a secondary hard drive, then use a Live Linux ISO to dd the partition.

Fixing the boot sector of the NTFS partition

This is the hackish part.

We are almost ready, but Windows XP won't boot, because we need to set the number of hidden sectors of the filesystem at the offset 0x1C. This basically a number of sectors before NTFS partition. We can get this number from the fdisk -ul command. The start field of the output states which sector the given partition starts at, which is basically the number of sectors before the partition. So get the number from there, convert it to hexadecimal using gcalctool for example.

Open the partition with hexedit, like this:

# hexedit /dev/sda4

Then write the number of hidden sectors in little endian order at the 1C offset. Little endiean means: 0xABCDEFGH will be GH EF CD AB. If the hexa number is shorter that 8 digits, precede it with zero.

When done, save it and exit (Ctrl+X).

Setting up GRUB

You must tell GRUB to boot directly that partition, for GRUB add the following menu entry:

title       Microsoft Windows XP Professional
root        (hd0,3)
savedefault
makeactive
chainloader +1

Where (hdX, Y) identifies the partition. X is 0 for the default harddisk, 1 for /dev/sda, 2 for /dev/sdb etc. Y is the partition number. 0 for /dev/sda1, 1 for /dev/sda2, etc.

This should be similar for GRUB2 too, but it uses a bit different syntax for menu entries.

Final touches

The new NTFS partition is ready to boot up. But you still need some hacking to reach the Windows XP desktop. Other systems might need different hacks. First the boot.ini needs to be fixed.

[boot loader]
timeout=1
default=multi(0)disk(0)rdisk(0)partition(3)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(3)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect

Set the parition number accordingly. I installed it on the /dev/sda4. So the partition number should be 3.

You may need to fix the MountedDevices entry in the registry if Windows freezes right before the logon screen.

The registry is at <path to windows>/system32/config/system you can view it by chntpw. This can be a problem if your system drive letter is not C. Since chntpw's registry writing capabilities are quite limited, you need to fallback and use a hexeditor, like ghex2 to fix it. You need to find \DosDevices\C: and replace the C with a different drive letter, F in my case.

After these, you should be able to reach the desktop, now you need to hunt for all drivers, but this is out of the scope of this tutorial.

Sources

Moving Windows XP to a different partition

Mount a VDI under linux