Creating a bootable image disk from an iso file

Goal

Projects that offer bootable ISOs to download and burn don't always provide a bootable disk image. The goal of this documentation is to see which technologies can be used to transform a bootable iso into a bootable image disk.

We will here use a Linutop demo CD (linutopos.iso) as source and genereate a linutopos.img file (downloadable on linutop.com). You will need to adapt a few things (mainly the files to move/edit) for other projects.

We assume that you already know what a boot loader is. We also assume that you know a few things about Linux :)

Tools

Some tools are needed:

  • a Linux box to generate the image
  • qemu-img (or kvm-img)
  • dosfstools to format the disk image
  • syslinux to make it bootable

Roadmap

The goal is basically to switch from the isolinux boot loader to syslinux.

The iso file contains the following architecture (reduced to the essentials):

/
  some_unimportant_stuff/
  packages/
    base.squashfs
  isolinux/
    vmlinuz
    initrd.gz
    isolinux.cfg
    vesamenu.c32
    splash.jpg

The base.squashfs file contains the Linux system. The name and location of the file is usually different depending on the distribution. The important thing is in fact the isolinux/ directory.

It contains 3 essential things:

  • the kernel: vmlinuz
  • the initrd (which is essential to boot the whole system): initrd.gz
  • the boot loader config: isolinux.cfg

vesamenu.c32 and splash.jpg are used to make the boot menu nicer than a boot> prompt.

So what we will have to do:

  • generate a disk image on which we will be able to copy data
  • copy data...
  • setup the boot loader (config and boot binary)

And that's it.

Step by step

  1. Create and mount the disk image

    # qemu-img create linutopos.img 700M
    

    We now need to make the system believe that this file is actually a block device. We will use the loop kernel module for this. We can then format and mount the device:

    # losetup /dev/loop5 linutopos.img
    # mkfs.vfat -F 32 /dev/loop5
    # mkdir target
    # mount /dev/loop5 target/
    
  2. Copy the data

    Now that we can write on the disk, we copy the data from the iso:

    # mkdir source
    # mount -o loop linutopos.iso source
    # cp -a source/* target/
    # umount source/
    # rmdir source/
    
  3. Setup the syslinux boot loader

    Syslinux and isolinux are developed as part of the same project. This means that the configuration syntax is exactly the same for both projects. The big differences are the name of the config files, and their location on the media. Let's adapt things:

    # cd target/
    # mv isolinux/isolinux.cfg syslinux.cfg
    # mv isolinux/splash.jpg .
    # mv isolinux/vesamenu.c32 .
    

    Now if you try to boot this system, you'll see that the boot loader can't find the kernel nor the initrd. We just need to tell syslinux where the files really are:

    # sed -i 's#vmlinuz#isolinux/vmlinuz#;s#initrd.gz#isolinux/initrd.gz#' \
        syslinux.cfg
    

    We could just move vmlinuz and initrd.gz to the root of the disk, but some boot system (this is the case for Linutop) need to have these files in predefined location to start correctly.

    Now that syslinux is configured, let's install the boot loader binary:

    # cd ../
    # umount target
    # rmdir target/
    # syslinux /dev/loop5
    # losetup -d /dev/loop5
    

    That's it! linutopos.img is a bootable disk image.

  4. Use the disk image

    The last step is to install the image on a physical device. On Linux this can be done with the dd utility:

    dd if=linutopos.img of=/dev/sdX
    

    You can find similar tools for Windows (for instance Image Writer).