As the virtualization is being spreaded into many field, learn to how manipulate virtual disk image is sometime you have to deal with.
In Cisco CGR1000 and IR800 routers, there was a need to create a bootable disk image as a reference guest linux image for customer to use. Along the way, I found manipulating virtual disk file is actually very interesting.
There are a few types of disk image, like ISO, raw disk image, vdi(VirtualBox), vmdk (VMWare) qcow2 (QEMU).
ISO image
An ISO image is an archive file of an optical disc. It can be burnt into CD/DVD. Often people will download ISO file like ubuntu distribution in ISO format, and burn into DVD/CD, and use it as an installation media.
You can also using linux tool “dd” to write ISO file into USB pendrive/memory stick, and use it as installation media. Assuing USB pendrive is detected by Linux as /dev/sdc, then following command will work:
weng@weng-u1604:~$ sudo if = ubuntu-16.04-desktop-amd64.iso of = /dev/sdc bs = 10M; sync ; sync ; sync
Additionaly, under linux, you can mount ISO file, inspect what is inside content, and you can make a copy and make modification (since iso is mounted as read only), and create a new ISO file. See example below.
[ root@sg-centos-hv1 scratch]#
[ root@sg-centos-hv1 scratch]# losetup /dev/loop0 ubuntu-1504-mini.iso
[ root@sg-centos-hv1 scratch]# ls /mnt/iso/
[ root@sg-centos-hv1 scratch]# mount /dev/loop0 /mnt/iso
[ root@sg-centos-hv1 scratch]# ls /mnt/iso
adtxt.cfg exithelp.cfg f2.txt f5.txt f8.txt isolinux.bin libcom32.c32 menu.cfg splash.png vesamenu.c32
boot f10.txt f3.txt f6.txt f9.txt isolinux.cfg libutil.c32 prompt.cfg stdmenu.cfg
boot.cat f1.txt f4.txt f7.txt initrd.gz ldlinux.c32 linux rqtxt.cfg txt.cfg
[ root@sg-centos-hv1 scratch]# # the above two steps mount can be done with one step below.
[ root@sg-centos-hv1 scratch]# umount /mnt/iso
[ root@sg-centos-hv1 scratch]# mount -o loop ubuntu-1504-mini.iso /mnt/iso
[ root@sg-centos-hv1 scratch]# ls /mnt/iso
adtxt.cfg exithelp.cfg f2.txt f5.txt f8.txt isolinux.bin libcom32.c32 menu.cfg splash.png vesamenu.c32
boot f10.txt f3.txt f6.txt f9.txt isolinux.cfg libutil.c32 prompt.cfg stdmenu.cfg
boot.cat f1.txt f4.txt f7.txt initrd.gz ldlinux.c32 linux rqtxt.cfg txt.cfg
[ root@sg-centos-hv1 scratch]# # below show how to create a new ISO by adding new files into existing ISO file
[ root@sg-centos-hv1 scratch]# mkdir /scratch/new_iso
[ root@sg-centos-hv1 scratch]# cp -rf /mnt/iso/* /scratch/new_iso
[ root@sg-centos-hv1 scratch]# touch /scratch/new_iso/hello.txt
[ root@sg-centos-hv1 scratch]# ls /scratch/new_iso/
adtxt.cfg exithelp.cfg f2.txt f5.txt f8.txt initrd.gz ldlinux.c32 linux rqtxt.cfg txt.cfg
boot f10.txt f3.txt f6.txt f9.txt isolinux.bin libcom32.c32 menu.cfg splash.png vesamenu.c32
boot.cat f1.txt f4.txt f7.txt hello.txt isolinux.cfg libutil.c32 prompt.cfg stdmenu.cfg
[ root@sg-centos-hv1 scratch]# mkisofs -o /scratch/ubuntu-1504-mini-A.iso /scratch/new_iso/
I: -input-charset not specified, using utf-8 ( detected in locale settings)
Using USBSE000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/usbserial_pl2303.mod ( usbserial_common.mod)
Using PART_000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/part_sunpc.mod ( part_sun.mod)
Using USBSE001.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/usbserial_common.mod ( usbserial_ftdi.mod)
Using GFXTE000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/gfxterm_background.mod ( gfxterm_menu.mod)
Using VIDEO000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/videotest_checksum.mod ( videotest.mod)
Using PASSW000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/password.mod ( password_pbkdf2.mod)
Using MULTI000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/multiboot.mod ( multiboot2.mod)
Using GCRY_000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/gcry_sha1.mod ( gcry_sha256.mod)
Using USBSE002.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/usbserial_ftdi.mod ( usbserial_usbdebug.mod)
Using MDRAI000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/mdraid09.mod ( mdraid09_be.mod)
Using GCRY_001.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/gcry_sha256.mod ( gcry_sha512.mod)
Using XNU_U000.MOD; 1 for /scratch/new_iso/boot/grub/x86_64-efi/xnu_uuid.mod ( xnu_uuid_test.mod)
29.30% done , estimate finish Fri Aug 12 22:45:51 2016
58.63% done , estimate finish Fri Aug 12 22:45:51 2016
87.89% done , estimate finish Fri Aug 12 22:45:51 2016
Total translation table size: 0
Total rockridge attributes bytes: 0
Total directory bytes: 16384
Path table size( bytes) : 50
Max brk space used 43000
17079 extents written ( 33 MB)
[ root@sg-centos-hv1 scratch]# umount /mnt/iso
[ root@sg-centos-hv1 scratch]# mount -o loop /scratch/ubuntu-1504-mini-A.iso /mnt/iso
[ root@sg-centos-hv1 scratch]# ls /mnt/iso//hello.txt
/mnt/iso//hello.txt
[ root@sg-centos-hv1 scratch]# umount /mnt/iso
[ root@sg-centos-hv1 scratch]#
Raw disk image: create a bootable raw disk image from scratch
There are a few steps to make a bootable raw disk image
Using command "dd" to create a disk file.
Setup the disk file as loopback block device.
Create partition on the loopback block device.
Create file system on the partition.
Download and build bootloader.
Build a kernel and root file system. in the example, kernel an root file system are built using yocto project.
Install boot loader in the first partition, which is makred as bootable partition. below example it is grub as bootlader.
Copy kernel and root file system into partition with file system setup.
Test it using qemu
#!/bin/bash
################################################################################
# Description:
# create a reference disk image file of Linux Guest OS
#
# Author: Wenwei Weng
#
# usage examples:
#
# ./mk_bootable_disk_img.sh -k bzImage.sda -r core-image-basic-qemux86.ext4 -b grub-0.97 -d sda -o linux-bootable-disk-sda.img
#
# ./mk_bootable_disk_img.sh -k bzImage.sda -r core-image-basic-qemux86.ext4 -b grub-0.97 -d hda -o linux-bootable-disk-hda.img
#
################################################################################
cleanmount() {
umount loop2_mnt > & /dev/null
umount ext4_mnt > & /dev/null
rmdir loop2_mnt > & /dev/null
rmdir ext4_mnt > & /dev/null
umount /dev/loop2 > & /dev/null
umount /dev/loop1 > & /dev/null
losetup -d /dev/loop2 > & /dev/null
losetup -d /dev/loop1 > & /dev/null
sync
sleep 2
}
# default setting
# ROOTFS built from yocto
ROOTFSEXT4 = "core-image-basic-qemux86-64.ext4"
#Kernel image from yocto
BZIMAGE = "bzImage"
# Grub legacy version
GRUBSRC = "grub-0.97"
# Result image disk file
DISKIMG = "linux-bootable-disk-sda.img"
# default image size 175MB
DISKIMG_SIZE = 175
#default the disk is detected as /dev/hda
DEVDISK = "hda"
#extra for application package
APP_PKG = ""
#############################################################
# parsing parameters
while getopts "k:r:b:o:d:s:a:" options
do
case $options in
k) BZIMAGE = $OPTARG ;;
b) GRUBSRC = $OPTARG ;;
r) ROOTFSEXT4 = $OPTARG ;;
o) DISKIMG = $OPTARG ;;
d) DEVDISK = $OPTARG ;;
s) DISKIMG_SIZE = $OPTARG ;;
a) APP_PKG = $OPTARG ;;
\? ) echo "option: $options , only -k -b -r -o -d -s -a are valid option" 1>&2
exit 1;;
esac
done
echo -e " \n\n BOOTLOADER is $GRUBSRC , KERNEL is $BZIMAGE , ROOT FS is $ROOTFSEXT4 "
echo -e " APP_PKG is $APP_PKG , Result file: $DISKIMG \n "
if [ ! -d ${ GRUBSRC } ]
then
echo "Grub directory doesn't exist !!! "
exit 2
fi
if [ ! -f $BZIMAGE ]
then
echo "kernel image doesn't exist !!! "
exit 3
fi
f [ ! -f $ROOTFSEXT4 ]
then
echo "ROOTFS EXT3 file doesn't exist !!! "
exit 4
fi
# do some clean up
cleanmount
echo -e " \n Creating raw disk image file with size ${ DISKIMG_SIZE } MB..."
dd if = /dev/zero of = $DISKIMG bs = 1M count = ${ DISKIMG_SIZE } > & /dev/null
losetup /dev/loop1 $DISKIMG > & /dev/null || { echo "Failed to set up /dev/loop1 to empty disk image: $DISKIMG " ; exit 5; }
CREATECMD = "n \n p \n 1 \n\n\n a \n 1 \n w \n "
echo -e $CREATECMD | fdisk /dev/loop1 > & /dev/null
let offset = ` fdisk -u -l /dev/loop1 | tail -1 | awk '{print $3}' ` * 512
losetup -o $offset /dev/loop2 /dev/loop1 > & /dev/null || { echo "Failed to set up /dev/loop2 ! " ; cleanmount; exit 6; }
sync
mke2fs -j /dev/loop2 > & /dev/null
sync ; sync ;
sleep 2
[ -d loop2_mnt ] && { rm -rf loop2_mnt; }
mkdir loop2_mnt
mount /dev/loop2 loop2_mnt || { echo "Failed to mount /dev/loop2 loop2_mnt ! " ; cleanmount; exit 7; }
[ -d ext4_mnt ] && { rm -rf ext4_mnt; }
mkdir ext4_mnt
mount $ROOTFSEXT4 ext4_mnt -o loop || { echo "Failed to mount $ROOTFSEXT4 loop2_mnt ! " ; cleanmount; exit 8; }
echo -e " \n copy ROOTFS from ext4_mnt into loop2_mnt..."
cp -rf ext4_mnt/* loop2_mnt/
if [ " ${ APP_PKG } " != "" ]
then
echo -e " \n adding application packages .."
cd loop2_mnt
tar xzvf ../${ APP_PKG } --no-same-owner
cd ..
fi
sync ; sync
echo -e " \n Copy kernel image ..."
rm -f loop2_mnt/boot/bzImage
cp -f $BZIMAGE loop2_mnt/boot/bzImage
sync ; sync
echo -e " \n copy grub binary from $GRUBSRC ..."
mkdir -p loop2_mnt/boot/grub
cp -rf $GRUBSRC /* loop2_mnt/boot/grub/
sync ; sync ; sync
sleep 2
echo -e " \n install grub in disk image..."
#/usr/sbin/grub --batch --device-map=/dev/null <<EOF
./grub --batch --device-map = /dev/null << EOF
device (hd0) ${ DISKIMG }
root (hd0,0)
setup (hd0)
quit
EOF
echo -e " \n set up grub config ..."
rm -f loop2_mnt/boot/grub/device.map
echo "(hd0) /dev/ $DEVDISK " > loop2_mnt/boot/grub/device.map
echo -e " \n device.map file: "
cat loop2_mnt/boot/grub/device.map
rm -f loop2_mnt/boot/grub/grub.conf
touch loop2_mnt/boot/grub/grub.conf
echo "default=0" >> loop2_mnt/boot/grub/grub.conf
echo "timeout=5" >> loop2_mnt/boot/grub/grub.conf
echo "title Linux Guest OS (yocto)" >> loop2_mnt/boot/grub/grub.conf
echo -e " \t root (hd0,0)" >> loop2_mnt/boot/grub/grub.conf
echo -e " \t kernel /boot/bzImage root=/dev/ ${ DEVDISK } 1 console=tty0 console=ttyS0,115200 udev.children-max=2" >> loop2_mnt/boot/grub/grub.conf
echo -e " \n grub.conf file: "
cat loop2_mnt/boot/grub/grub.conf
cp -f loop2_mnt/boot/grub/grub.conf loop2_mnt/boot/grub/menu.lst
sync ; sync
# clean up
cleanmount
Image convertion between different types
Converting images from one format to another is generally straightforward.
The qemu-img convert command can do conversion between multiple formats, including qcow2, qed, raw, vdi, vhd, and vmdk.
$ # convert a raw image file named image.img to a qcow2 image file.
$ qemu-img convert -f raw -O qcow2 image.img image.qcow2
$ # convert a vmdk image file to a raw image file.
$ qemu-img convert -f vmdk -O raw image.vmdk image.img
$ # convert a vmdk image file to a qcow2 image file.
$ qemu-img convert -f vmdk -O qcow2 image.vmdk image.qcow2