Creating partitioned virtual disk images
Tagged with: [ cybos ] [ ext2fs ] [ losetup ] [ mount ]
Using a disk image is very easy: download the file, mount it through a so-called “loopback” device and your OS will see the image as it was an real harddisk, CDR or DVD. When I needed to test the IDE-drivers, the partitioning-functionality, the ext2 drivers etc, I wanted to use just such an image so I can quickly make modifications and check how the actual structure looked like, just by reading the disk image file. This is a great help when it comes to debugging.
Our OS debugging takes place in both QEMU and Bochs. These two virtual machines / emulators can use a disk image to emulate a harddisk. This way, I can test my IDE/ATA drivers all the way up to the virtual file system to see if everything works.
- Create an empty image.
- Partition the image through fdisk
- Create the loopbacks for each partition
- Format the partitions
- Mount the partitions and populate
Create an empty image.
There are multiple ways to do this, but one of the easiest methods is to use either dd
or bximage
. The latter one is
a tool from the Bochs Emulator that can create disks.
0xdeadbeef:image joshua$ <strong>bximage</strong>
========================================================================
bximage
Disk Image Creation Tool for Bochs
$Id: bximage.c,v 1.34 2009/04/14 09:45:22 sshwarts Exp $
========================================================================
Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd]
What kind of image should I create?
Please type flat, sparse or growing. [flat]
Enter the hard disk size in megabytes, between 1 and 129023
[10]
I will create a 'flat' hard disk image with
cyl=20
heads=16
sectors per track=63
total sectors=20160
total size=9.84 megabytes
What should I name the image?
[c.img] <strong>hdd.img</strong>
Writing: [] Done.
I wrote 10321920 bytes to hdd.img.
The following line should appear in your bochsrc:
ata0-master: type=disk, path="hdd.img", mode=flat, cylinders=20, heads=16, spt=63
Another way is to use the “dd” utility that comes on most unix/linux systems:
dd if=/dev/zero of=hdd.img bs=1024 count=10240
You will notice a difference in size even though I both entered that I wanted 10MB of space. This is because bximage does it’s calculations on CHS level, while dd just copy bytes. For our purpose, this doesn’t matter.
Partition the image through fdisk
Now that we have a completely blank file (or virtual disk), we need to partition it just like any other disk. The “fdisk” utility it the way to go here, and there is nothing different between creating partitions on a virtual disk than it is on a physical one.
fdisk hdd.img
I’ve added a bunch of partitions (again: I’m testing my OS here, so it needs to be able to handle stuff like extended partitions etc). Just make sure you set the correct settings for your cylinders, heads and sectors-per-track in the fdisk menu. They are located in the “Expert menu” through “x”. I’ve added a big 5MB partition for Linux, a small 2MB partition for dos/fat16, another 1.5MB for linux, and a-little-bit-less-than-1MB partition for Linux, but I’ve put the last one inside an extended partition:
# fdisk -l -u hdd.img -C 20 -H 16 -S 63
Disk hdd1.img: 0 MB, 0 bytes
16 heads, 63 sectors/track, 20 cylinders, total 0 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
Disk identifier: 0x839d4362
Device Boot Start End Blocks Id System
hdd.img1 63 10079 5008+ 83 Linux
hdd.img2 10080 15119 2520 6 FAT16
hdd.img3 15120 18143 1512 83 Linux
hdd.img4 18144 20159 1008 5 Extended
hdd.img5 18207 20159 976+ 83 Linux
Now for the most important part, we NEED to remember the values used in the “Start” and “End” column. As you can see, the first partition does not start at 0 as you might have expected. That is because fdisk leaves room for boot-programs in this first part of the disk. The actual partition starts on sector 63, which is 63 * 512 = position 32256 inside our virtual image. This is important to realize in the next part..
Create the loopbacks for each partition
Get your calculator up and running, because we need a bit of math to do. This is because we don’t mount the whole file as a whole, but we mount each partition separately. So we get 4 mounts: primary partition 1 (linux), primary partition 2 (fat16), primary partition 3 (linux) and extended partition 5 (linux). We don’t need to mount the extended partition, since this is not really more than a placeholder for other partitions.
Before we can mount the partitions, we add them to the loopback driver. This is done with “losetup”.
# losetup -o 32256 --sizelimit 5160448 /dev/loop1 hdd.img
We have 4 arguments: the start (offset) inside the file, the LIMIT of the image, the loopback device we want to add the file to, and the last one is the actual file that we want to create a loopback for.
The offset is a byte-offset and is calculated by
The complete list should look like this:
# losetup -o 32256 --sizelimit 5160448 /dev/loop1 hdd.img
# losetup -o 5160960 --sizelimit 7740928 /dev/loop2 hdd.img
# losetup -o 7741440 --sizelimit 9289216 /dev/loop3 hdd.img
# losetup -o 9321984 --sizelimit 10321408 /dev/loop5 hdd.img
I’ve used the /dev/loopN
to match the actual partition. You could use /dev/loop0
to /dev/loop3
if you want.
Doesn’t really matter.
Format the partitions
At this point you are able to format the partitions. Again, just like any physical partition.
# mkfs.ext2 /dev/loop1
# mkdosfs /dev/loop2
# mkfs.ext2 /dev/loop3
# mkfs.ext2 /dev/loop5
Did I mention the importance of the sizelimit argument during losetup? If you didn’t you will run into trouble when formatting the partitions. In the case of the first partition, the loopback device knows it starts from sector 63 (which is correct), but since you didn’t specify a limit, it thinks it can use the rest of the file. In our case, this would mean it would use a little bit less than 10MB. Same goes for the other partitions as well.
Mount the partitions and populate
Now we can actually mount and populate the partitions.
mkdir /mnt/hd1p1 ; mount /dev/loop1 /mnt/hd1p1
mkdir /mnt/hd1p2 ; mount /dev/loop2 /mnt/hd1p2
mkdir /mnt/hd1p3 ; mount /dev/loop3 /mnt/hd1p3
mkdir /mnt/hd1p5 ; mount /dev/loop5 /mnt/hd1p5
Now you can populate the filesystems any way you like (and in my case, I can actually mount them correctly from CybOS :))
Conclusion
Using partitioned based virtual images isn’t something that is used much, but can come in handy from time to time. One of the biggest issues with them is not to forget the “-sizelimit” argument during losetup. Forgetting this can make your life miserable since you can (and will) overwrite data from other partitions as well without noticing.