Repackaging images with mkosi

Published January 29th, 2024, 4 min read, #archlinux

In a fediverse discussion following my last post about mkosi I got nerd-sniped to try and repackage System Rescue into an USI with mkosi.

In this post we'll explore how to do this.

Prerequisites

We'll need a few packages first:

# pacman -S mkosi squashfs-tools
# pacman -S --asdeps systemd-ukify qemu-base

In addition to mkosi, we install its optional dependencies systemd-ukify (required to build a UKI) and qemu-base (to test with mkosi qemu). We install squashfs-tools to extract the root file system from the system-rescue squashfs image with unsquashfs.

Configure mkosi

We start with a simple mkosi.conf which enables our desired output format:

[Distribution]
Distribution=custom

[Output]
Format=uki
ImageId=system-rescue

Get the root file system

Next we download systemrescue-11.00-amd64.iso from the System Rescue download page. We mount the ISO, copy sysresccd/x86_64/airootfs.sfs to the directory of our mkosi.conf file, and extract it (as root to maintain proper file ownership, etc.)

# unsquashfs airootfs.sfs

The extracted root file system becomes our base tree for the mkosi image:

diff --git i/mkosi.conf w/mkosi.conf
index 2b1ad05..297c99c 100644
--- i/mkosi.conf
+++ w/mkosi.conf
@@ -4,3 +4,6 @@ Distribution=custom
 [Output]
 Format=uki
 ImageId=system-rescue
+
+[Content]
+BaseTree=squashfs-root

Let's try:

$ mkosi
[…]
‣ A kernel must be installed in the image to build a UKI

Looks like there's no kernel in the root file system:

$ ls squashfs-root/lib/modules/6.6.14-1-lts/
kernel/            modules.builtin            modules.builtin.modinfo  modules.devname  modules.symbols
modules.alias      modules.builtin.alias.bin  modules.dep              modules.order    modules.symbols.bin
modules.alias.bin  modules.builtin.bin        modules.dep.bin          modules.softdep  pkgbase

Which makes sense: By the time the root file system gets mounted the kernel is already loaded, so System Rescue doesn't waste any space here.

Get the kernel

So let's copy the kernel from sysresccd/boot/x86_64/vmlinuz on the ISO to the directory of our mkosi file, and add it on top of the base tree:

diff --git i/mkosi.conf w/mkosi.conf
index 65f5e5f..7755a27 100644
--- i/mkosi.conf
+++ w/mkosi.conf
@@ -7,3 +7,4 @@ ImageId=system-rescue

 [Content]
 BaseTrees=squashfs-root
+ExtraTrees=vmlinuz:/usr/lib/modules/6.6.14-1-lts/vmlinuz

Still not there, though:

$ mkosi -f
mkosi -f
‣ Building system-rescue image
Create subvolume '/var/tmp/mkosi-workspacepbun1nmm/root'
‣  Copying in base trees…
‣  Copying in extra file trees…
‣  Installing systemd-boot…
Failed to resolve path /efi: No such file or directory
‣ "bootctl install --root /var/tmp/mkosi-workspacepbun1nmm/root --all-architectures --no-variables" returned non-zero exit code 1.

mkosi tries to install bootloader the disk image, but there's no /efi directory in the image. However, we don't need a bootloader inside a USI; instead a USI gets booted by an external bootloader. So let's disable bootloader installation:

diff --git i/mkosi.conf w/mkosi.conf
index 7755a27..f501fee 100644
--- i/mkosi.conf
+++ w/mkosi.conf
@@ -8,3 +8,5 @@ ImageId=system-rescue
 [Content]
 BaseTrees=squashfs-root
 ExtraTrees=vmlinuz:/usr/lib/modules/6.6.14-1-lts/vmlinuz
+Bootloader=none
+Bootable=false

Now we get an image:

$ mkosi -f
‣  […]/system-rescue.efi size is 909.3M, consumes 909.3M.

It's pretty large, but that's not surprising given that we're combining some very large source files:

$ du -h vmlinuz airootfs.sfs
13M     vmlinuz
768M    airootfs.sfs

However, we'll need more memory for testing this in a VM than the default 2 GiB mkosi uses for its qemu command:

diff --git i/mkosi.conf w/mkosi.conf
index f501fee..9633e9d 100644
--- i/mkosi.conf
+++ w/mkosi.conf
@@ -10,3 +10,6 @@ BaseTrees=squashfs-root
 ExtraTrees=vmlinuz:/usr/lib/modules/6.6.14-1-lts/vmlinuz
 Bootloader=none
 Bootable=false
+
+[Host]
+QemuMem=8G

We're choosing 8 GiB because an initramfs of almost 1 GiB will likely use several gigabytes of memory, and we also need a bit of RAM for the system itself.

Let's see if it boots:

$ mkosi qemu
[…]
 ========= SystemRescue 11.00 (x86_64) ======== ttyS0/6 =========
                    https://www.system-rescue.org/

* Console environment :
   Run setkmap to choose the keyboard layout (also accessible with the arrow up key)
   Run manual to read the documentation of SystemRescue

* Graphical environment :
   Type startx to run the graphical environment
   X.Org comes with the XFCE environment and several graphical tools:
   - Partition manager: .. gparted
   - Web browser: ........ firefox
   - Text editor: ........ featherpad

sysrescue login: root (automatic login)

[root@sysrescue ~]#

🎉

Conclusion

This post illustrates some cool mkosi features to build your own images of arbitrary root file system trees. You could use this to e.g. build a USI from the good old GRML on ESP trick, or repackage any kind of live disk (small enough to live in memory) into a single bootable file.

But for System Rescue specifically this is kind of a futile exercise. System Rescue is based on Arch Linux, after all, which has first-class mkosi support In its gitlab repo we can find its package list as well as its extra file system tree, so instead of repackaging its binary artifacts we could just build a mkosi image right from its sources.

But that's a story for another blog post.