Creating a flashable Linux image can be a valuable task when you want to distribute a pre-configured environment or system for various purposes. In this blog post, we’ll walk you through the process of building a flashable Linux image using a set of commands. These commands, when executed, will create a Linux image with an EFI system partition and a Linux root filesystem. We’ll break down each step and explain what it does.
Prerequisites
Before we begin, make sure you have the following prerequisites in place:
- A Linux system (the commands are Linux-specific).
- Sufficient disk space to accommodate the image (at least 5GB in this case).
- Administrative privileges (you may need to use
sudo).
Now, let’s dive into the steps:
1. Set Up Variables
The initial part of the script sets up some variables that will be used later. Here’s what each line does:
set -e CHROOT=/var/chroot/jammy sudo mkdir -p $CHROOT truncate --size 5G base-image.img
set -e: This command ensures that the script exits immediately if any command fails, making it easier to catch errors early.CHROOT=/var/chroot/jammy: This sets theCHROOTvariable to specify the location where we’ll create the chroot environment.sudo mkdir -p $CHROOT: It creates the directory specified byCHROOT, creating parent directories if they don’t exist.truncate --size 5G base-image.img: This command creates an empty image file namedbase-image.imgwith a size of 5GB.
2. Create Partition Layout
This step involves creating a partition layout on the base-image.img file. We use sgdisk for this purpose. Here’s what the command does:
sgdisk --clear \
--new 1:1M:+63M --typecode=1:ef00 --change-name=1:'EFI System' \
--new 2::-0 --typecode=2:8300 --change-name=2:'Linux root filesystem' \
base-image.img -g
sgdisk --clear: Clears any existing partition table on the image.--new 1:1M:+63M: Creates an EFI System partition with a size of 63MB starting at 1MB offset.--typecode=1:ef00: Sets the type of the first partition to EFI System.--change-name=1:'EFI System': Assigns a name to the first partition.--new 2::-0: Creates a second partition using the remaining space.--typecode=2:8300: Sets the type of the second partition to Linux root filesystem.--change-name=2:'Linux root filesystem': Assigns a name to the second partition.base-image.img -g: Specifies the image file to operate on and saves the changes with-g.
3. Format Partitions
Next, we need to format the newly created partitions. Here’s how it’s done:
bash
sudo losetup -P /dev/loop99 base-image.img $CHROOT sudo mkfs.fat -F32 /dev/loop99p1 sudo mkfs.ext4 -F -L "rootfs" /dev/loop99p2
sudo losetup -P /dev/loop99 base-image.img $CHROOT: Attaches the image filebase-image.imgto a loop device (/dev/loop99) and partitions it according to the partition layout.sudo mkfs.fat -F32 /dev/loop99p1: Formats the first partition as a FAT32 filesystem (commonly used for EFI System partitions).sudo mkfs.ext4 -F -L "rootfs" /dev/loop99p2: Formats the second partition as an ext4 filesystem and labels it as “rootfs.”
4. Mount Partitions
Now, it’s time to mount the partitions to prepare for populating them with the Linux distribution. This is crucial for creating a chroot environment:
bash
sudo mount -o rw /dev/loop99p2 $CHROOT sudo mkdir -p $CHROOT/boot/efi sudo mount -o rw /dev/loop99p1 $CHROOT/boot/efi
sudo mount -o rw /dev/loop99p2 $CHROOT: Mounts the second partition to$CHROOT, which is the root filesystem of your Linux image.sudo mkdir -p $CHROOT/boot/efi: Creates a directory for the EFI System partition within the chroot environment.sudo mount -o rw /dev/loop99p1 $CHROOT/boot/efi: Mounts the first partition (EFI System partition) to$CHROOT/boot/efiwithin the chroot environment.
5. Set Up Error Handling
Set up an error handler to ensure proper cleanup in case any subsequent commands fail:
bash
error_handler() {
sudo umount /dev/loop99p1
sudo umount /dev/loop99p2 -l
sudo losetup -d /dev/loop99
}
error_handler(): Defines a function that will be executed in case of an error.sudo umount /dev/loop99p1: Attempts to unmount the EFI System partition.sudo umount /dev/loop99p2 -l: Attempts to unmount the Linux root filesystem partition, forcibly if necessary.sudo losetup -d /dev/loop99: Detaches the loop device from the image.
6. Debootstrap the System
Use debootstrap to bootstrap an Ubuntu system into the chroot environment:
bash
sudo debootstrap --arch=amd64 --include systemd jammy $CHROOT http://archive.ubuntu.com/ubuntu/
sudo debootstrap: Initiates the debootstrap process.--arch=amd64: Specifies the architecture of the system you want to install (amd64 in this case).--include systemd: Includes the systemd init system.jammy: Specifies the release (you can change this to the desired Ubuntu release).$CHROOT: Defines the target directory for the debootstrap process.http://archive.ubuntu.com/ubuntu/: Specifies the repository from which packages should be fetched.
7. Modify the base system
You will need to bind mount some of your running system in to the mounted partitions next. This will enable you to install packages inside the image
sudo mount --bind /dev $CHROOT/dev/
sudo mount --bind /sys $CHROOT/sys/
sudo mount --bind /proc $CHROOT/proc/
sudo mount --bind /dev/pts $CHROOT/dev/pts
sudo mount --bind /run $CHROOT/run
now you can use the chroot command to install packages in the image, you will need grub first to boot the system after flashing:
chroot $CHROOT /bin/bash -x <<'EOF'
cat << ! | debconf-set-selections -v
grub2 grub2/linux_cmdline select
grub2 grub2/linux_cmdline_default select
grub-pc grub-pc/install_devices_empty select yes
grub-pc grub-pc/install_devices select
!
sudo apt update
DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install grub-efi-amd64 parted -y
echo 'set timeout_style=hidden
set timeout=0' | sudo tee /boot/grub/custom.cfg
EOF
8. Unmount all the mount points and loop device
Finally, release all the mounted directories and the loop device:
sudo umount $CHROOT/dev/pts
sudo umount $CHROOT/dev/
sudo umount $CHROOT/sys/
sudo umount $CHROOT/proc/
sudo umount $CHROOT/run
sudo umount /dev/loop99p1
sudo umount /dev/loop99p2 -l
sudo losetup -d /dev/loop99
Conclusion
With these steps, you’ve successfully created a flashable Linux image with an EFI System partition and a Linux root filesystem. This image can be further customized and then flashed onto a bootable device such as a USB drive or an SD card. Building custom Linux images can be a powerful tool for system administrators and developers who need to deploy specific configurations or applications on multiple systems.
