# A Linux-Native DriveStage for the ext4 Era > Design rationale and architecture notes for a clean-slate multi-payload > USB toolkit. Inspired by Easy2Boot and RMPrepUSB. Built from scratch > for modern Linux. **Author:** Jeremy Anderson — [dcos.net](https://dcos.net) ## Motivation Easy2Boot and RMPrepUSB by Steve Si defined the multi-payload USB use case. They demonstrated that a single USB drive can host dozens of bootable ISOs and present a selection menu at boot time. That workflow is the correct workflow. The original implementation, however, targets a computing era that predates UEFI, ext4, and modern GRUB2. Easy2Boot originated when: - **Windows XP was current.** The tooling is Windows batch and .NET. - **FAT32 was the only viable filesystem.** The 4 GB file size limit forced ISO splitting, contiguous-file allocation tricks, and partition table patching at boot time. - **BIOS was the only firmware.** MBR ruled. UEFI did not exist in the consumer market. - **grub4dos was the bootloader.** A branch of an earlier GRUB lineage, unmaintained for over a decade. The accumulated design constraints of that era — NTFS driver loading, contiguous file defragmentation, `.mnu` sidecar files, a switch utility that rewrites the partition table on every selection — function in their niche, but they impose fragility and opacity. On Linux, provisioning a USB stick through that pipeline requires WINE or a Windows VM. That is the wrong tool for the job. This project is an independent implementation of the same use case, designed for the assumptions that hold today: Linux as the host, UEFI as the firmware, ext4 as the filesystem, and GRUB2 as the bootloader. ## Project naming DriveStage is the public project name. BlkStage is the internal backend that ships as `scripts/blkstage.sh` and installs on PATH as the `drivestage` CLI command. The GUI binary is `drivestage-gui`. The previous codename carried a trademark conflict; the rename is deliberate and complete across source, docs, install paths, partition labels, and GRUB cfg identifiers. ## Architecture ### Partition layout Three partitions. GPT. Standard-compliant. Deterministic. ``` ├─ Partition 1: ESP (FAT32, 512 MB) ← UEFI requirement ├─ Partition 2: BIOS Boot (unformatted, 1 MB) ← GRUB GPT embedding └─ Partition 3: Payloads (ext4, remainder) ← all payload data ``` The ESP holds only GRUB's `.efi` binaries. The BIOS Boot partition is a 1 MB slot for GRUB's `i386-pc` boot code (a GPT requirement for BIOS booting). Everything else — ISOs, rootfs tarballs, kernel images, configuration files — resides on the ext4 partition. **Ext4 by decision.** GRUB2 ships built-in ext2/ext3/ext4 drivers. UEFI loads GRUB from the FAT32 ESP; GRUB loads its drivers and reads menu entries and kernels directly from the ext4 partition. The FAT32 4 GB file size limit no longer applies. NTFS drivers are unnecessary. Contiguous-file defragmentation is unnecessary. Partition table rewriting is unnecessary. Files reside on the filesystem as files. ### Bootloader GRUB2. Both targets. Same drive. ``` grub-install --target=x86_64-efi --efi-directory=/mnt/.../boot/efi --removable grub-install --target=i386-pc --boot-directory=/mnt/.../boot /dev/sdX ``` Two commands. The drive boots on a 2024 UEFI machine and a 2008 BIOS machine without reconfiguration. Modern GRUB is mature, universally packaged, and handles ext4 + loopback + ISO boot patterns for every major distribution. ### Boot-time auto-discovery The defining feature. Drop an ISO onto the stick and it appears in the boot menu. No host-side menu generation. No config file editing. No `scan` command for ISOs. GRUB's scripting language is more capable than commonly assumed. It supports `for` loops, `if/elif/else`, `regexp`, and user-defined functions. The auto-scan `grub.cfg` performs the following at boot: 1. Locates the payload partition by UUID 2. Iterates over `/payloads/isos/*.iso` 3. For each ISO, runs a `regexp` cascade to identify the distribution from the filename 4. Invokes a function that emits the appropriate `menuentry` with distro-specific kernel arguments 5. Iterates over `/payloads/rootfs/*/` and emits direct-boot entries 6. Iterates over `/payloads/images/*.img` and emits chainload attempts The result: drop an ISO into the payloads directory, eject the drive, boot it, and the entry appears in the menu. A snippet from the auto-scan `grub.cfg`: ```grub function ds_iso_entry { set iso_path="$1" set iso_name="$2" set distro="unknown" if regexp --quiet --ignore-case 'ubuntu' "$iso_name"; then set distro="ubuntu" elif regexp --quiet --ignore-case 'archlinux' "$iso_name"; then set distro="arch" elif regexp --quiet --ignore-case 'debian.*live' "$iso_name"; then set distro="debian-live" # ... 30 additional patterns fi if [ "$distro" = "ubuntu" ]; then menuentry "$iso_name [ubuntu]" "$iso_path" { set iso_path="$1" search --no-floppy --fs-uuid --set=root $ds_payload_uuid loopback loop "$iso_path" linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$iso_path quiet splash --- initrd (loop)/casper/initrd } elif [ "$distro" = "arch" ]; then # Arch-specific kernel arguments fi } for f in /payloads/isos/*.[iI][sS][oO]; do if [ -f "$f" ]; then ds_iso_entry "$f" "$f" fi done ``` The complete configuration is approximately 350 lines. It is written once at setup time to embed the partition UUID and requires no further modification. ### The engine (BlkStage) The engine is a single bash file, approximately 1500 lines, with six subcommands: | Subcommand | Purpose | |---|---| | `setup` | Partition, format, install GRUB, write the auto-scan `grub.cfg` | | `deploy` | Copy a payload file; extract tarballs on copy | | `scan` | Extract tarballs; refresh the UUID in `grub.cfg` | | `list` | Display registered payloads and sizes | | `shell` | Mount partitions and open a helper shell | | `clean` | Wipe the partition table and signatures | The engine refuses to operate on the disk hosting `/` or `/boot`. Destructive operations require typed confirmation. It handles `/dev/sda` versus `/dev/nvme0n1p1` versus `/dev/mmcblk0p1` naming conventions correctly. It waits for udev to materialize partition nodes before proceeding. The design prioritizes defensive reliability over brevity. ### The GUI (drivestage-gui) The engine is the source of truth. The GUI is a wrapper. Rust handles disk discovery (via `lsblk -J` JSON parsing), UI state, and async orchestration. Privileged operations delegate to the bash engine. The GUI presents five tabs: 1. **Setup wizard** — select a drive, configure ESP size and labels, choose a bootloader, type the device name to confirm, click PROVISION 2. **Payload manager** — drag-and-drop ISOs, view sizes, delete, eject 3. **Bootloader switcher** — install GRUB2, systemd-boot, Limine, or rEFInd per-drive 4. **Menu editor** — edit `/boot/grub/custom.cfg` with a monospace editor (custom entries appear after the auto-scan entries) 5. **Diagnostics** — SMART attributes, partition table dump, filesystem UUID and label browser The visual theme is "dark tech utility" — inspired by btop and htop, with monospace accents for paths, UUIDs, and sizes. It targets the sysadmin-tool aesthetic, not the consumer-application aesthetic. ## Design decisions ### What was adopted from Easy2Boot / RMPrepUSB - **The drop-and-boot workflow.** Files placed in a payload directory appear in the boot menu without explicit registration. This is the correct user experience. - **The multi-payload USB use case itself.** One stick, many ISOs, selectable at boot. - **Distro-specific kernel argument injection.** Different distributions require different boot parameters for ISO loopback. Centralizing this knowledge in a pattern-matching dispatcher is the right design. ### What was implemented differently - **Linux-native host.** No WINE, no Windows VM, no cross-platform shims. The host is Linux; the tooling is Linux. - **Ext4 payload partition.** Files are files. No contiguous-file allocation, no defragmentation passes, no 4 GB limit. - **GPT with proper BIOS Boot partition.** Standard-compliant BIOS booting on GPT, no MBR hacks. - **GRUB2 as the bootloader.** Mature, universally packaged, actively maintained. Both UEFI and BIOS targets installed in two commands. - **Boot-time auto-discovery via GRUB scripting.** No host-side menu generation required for ISOs. The `grub.cfg` is a program, not a static list. - **Standard `custom.cfg` mechanism.** Custom menu entries use GRUB's built-in `source` directive. No sidecar `.mnu` format. ### What was not implemented - **Contiguous file allocation.** Unnecessary on ext4. - **`.mnu` sidecar files.** Superseded by `/boot/grub/custom.cfg` — a standard GRUB mechanism. - **Partition table rewriting at boot.** Eliminated. The partition table is written once at setup time. - **Windows support.** Out of scope. This is a Linux-native tool. - **FAT32 payload partition.** Eliminated. The ESP remains FAT32 (UEFI requires it); payloads reside on ext4. ## Outcome The result is a toolkit that is smaller, more reliable, and more transparent than its inspirations. The bash engine is 1500 lines. The Rust GUI is 3200 lines. Together they implement the multi-payload USB workflow for modern Linux without carrying forward constraints from an obsolete computing era. Drop in an ISO. Boot. Done. --- *Read [QUICKSTART.md](QUICKSTART.md) to begin, or examine [scripts/blkstage.sh](scripts/blkstage.sh) for the implementation.*