Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/nornir-build-thing/src/iso_writer.rs

//! **Pure-Rust ISO-9660 + El-Torito writer** — masters a bootable `.iso` with
//! ZERO external process. This is the law-compliant replacement for the deleted
//! `xorriso`/`mkisofs`/`genisoimage` shell-out path: nothing here spawns a
//! subprocess, links a C library, or calls out to any ISO tool. The on-disk
//! ISO-9660 format is fully specified (ECM-119) and the El-Torito boot record is
//! a small, well-documented structure, so the whole image is emitted byte-for-byte
//! from Rust + `std`.
//!
//! # What it produces
//!
//! * A 2048-byte-sector ISO-9660 volume: a 16-sector system area, a **Primary
//!   Volume Descriptor** (`CD001` at sector 16), an **El-Torito Boot Record**
//!   volume descriptor, a Volume Descriptor Set Terminator, both path tables
//!   (type-L little-endian + type-M big-endian), a recursive directory tree, and
//!   the file data.
//! * An **El-Torito boot catalog**: a validation entry, a default/initial entry,
//!   and — for a UEFI image — an EFI **section header + section entry**
//!   (platform `0xEF`) pointing at the `efiboot.img` EFI System Partition. This is
//!   what OVMF/UEFI firmware boots off a `-cdrom`, matching the `-e efiboot.img
//!   -no-emul-boot` the old `xorriso` argv rendered.
//!
//!   **Only when a boot image really resolved.** An image with nothing bootable
//!   is mastered as a plain ISO-9660 data volume with no Boot Record VD and no
//!   catalog — see [`BootImages`] for the catalog that used to be fabricated over
//!   LBA 0 instead.
//!
//! # Layout
//!
//! ```text
//!   BOOTABLE                            NOT BOOTABLE
//!   sector 0..16  system area (zero)    sector 0..16  system area (zero)
//!   sector 16     Primary VD "CD001"    sector 16     Primary VD "CD001"
//!   sector 17     Boot Record VD        sector 17     VD Set Terminator (255)
//!   sector 18     VD Set Terminator     sector 18     type-L path table (LE)
//!   sector 19     type-L path table     sector 19     type-M path table (BE)
//!   sector 20     type-M path table     sector 20..   directory extents
//!   sector 21     El-Torito catalog     sector ..     file data
//!   sector 22..   directory extents
//!   sector ..     file data
//! ```
//!
//! The volume-descriptor sequence has to be contiguous and terminated, so the
//! Boot Record VD cannot merely be left zeroed when there is nothing to boot —
//! everything after it moves up by one sector instead.
//!
//! Everything is content-deterministic (fixed timestamps), so the same input tree
//! masters byte-identically — the property a content-addressed build relies on.

use std::collections::BTreeMap;
use std::io::Read;
use std::path::{Path, PathBuf};

use anyhow::{bail, Context, Result};

/// ISO-9660 logical sector size (bytes).
pub const SECTOR: usize = 2048;

/// El-Torito platform id for a **BIOS** (x86) boot entry.
const PLATFORM_X86: u8 = 0x00;
/// El-Torito platform id for a **UEFI/EFI** boot entry.
const PLATFORM_EFI: u8 = 0xEF;

/// Round `n` up to the next multiple of [`SECTOR`].
fn round_sectors(n: u64) -> u64 {
    n.div_ceil(SECTOR as u64)
}

/// A both-endian 16-bit field (ISO-9660 stores LE then BE).
fn both16(v: u16) -> [u8; 4] {
    let le = v.to_le_bytes();
    let be = v.to_be_bytes();
    [le[0], le[1], be[0], be[1]]
}

/// A both-endian 32-bit field (ISO-9660 stores LE then BE).
fn both32(v: u32) -> [u8; 8] {
    let le = v.to_le_bytes();
    let be = v.to_be_bytes();
    [le[0], le[1], le[2], le[3], be[0], be[1], be[2], be[3]]
}

/// The fixed 7-byte directory-record recording timestamp (deterministic:
/// 2024-01-01T00:00:00, GMT). Year is stored as years-since-1900.
const DIR_DATE: [u8; 7] = [124, 1, 1, 0, 0, 0, 0];

/// The fixed 17-byte volume-descriptor timestamp
/// (`YYYYMMDDHHMMSShh` + 1-byte GMT offset).
const VOL_DATE: &[u8; 17] = b"2024010100000000\0";

/// One node in the in-memory ISO directory tree.
struct DirNode {
    /// Directory name component (empty for the root).
    name: String,
    /// Parent node index (root points at itself, index 0).
    parent: usize,
    /// Child directories: name → node index.
    dirs: BTreeMap<String, usize>,
    /// Files in this directory: name → where its bytes come from.
    files: BTreeMap<String, FileSource>,
    // ── assigned during layout ──
    /// This directory's extent LBA.
    lba: u32,
    /// This directory's extent length in bytes.
    len: u32,
    /// 1-based path-table number.
    path_no: u16,
}

/// A pure-Rust ISO-9660 + El-Torito image writer.
///
/// Build a tree with [`add_file`](Self::add_file) /
/// [`add_dir_recursive`](Self::add_dir_recursive), mark the El-Torito boot
/// image(s), then [`write`](Self::write) the `.iso`. No subprocess is ever
/// spawned.
pub struct IsoWriter {
    volume_id: String,
    nodes: Vec<DirNode>,
    /// ISO-relative path (uppercased, `/`-joined) of the BIOS El-Torito boot image.
    bios_boot: Option<String>,
    /// ISO-relative path of the EFI El-Torito boot image (`efiboot.img`).
    efi_boot: Option<String>,
    /// Number of 512-byte sectors the BIOS boot entry loads.
    bios_sectors: u16,
    /// A real legacy-BIOS stage whose patch table the writer fills at build
    /// time — see [`Self::set_bios_stage`]. When set, `bios_boot` names its
    /// image and `bios_sectors` is derived from the image length.
    bios_stage: Option<BiosStage>,
    /// Whether (and when) the medium also carries a protective MBR + GPT whose
    /// EFI System partition spans the staged `efiboot.img` — see [`GptHybrid`].
    gpt_hybrid: GptHybrid,
    /// The 440 bytes of boot code a HYBRID medium carries at LBA 0 — see
    /// [`Self::set_hybrid_mbr`]. `None` leaves LBA 0 as ISO-9660 leaves it:
    /// all zeros, no `55 AA`, unbootable off any hard-disk device.
    hybrid_mbr: Option<Vec<u8>>,
    /// The shape the LAST [`build_image`](Self::build_image) produced — the
    /// applied layout a caller puts on its receipt.
    layout: Option<MediumLayout>,
}

/// **When the medium is a GPT hybrid** — an `.iso` that is ALSO a valid
/// GPT-partitioned disk image, the way `xorriso -isohybrid-gpt-basdat` and
/// systemd's `mkosi` shape theirs.
///
/// A plain ISO-9660/El-Torito medium describes its EFI System Partition only in
/// the boot catalog, whose sector count is a **u16 of 512-byte sectors**
/// ([`MAX_BOOT_IMAGE_BYTES`] = 33 553 920 B). A hybrid additionally lays a
/// protective MBR at byte 0, a primary GPT header + partition array in the
/// ISO's unused 32 KiB system area, and a backup array + header in a tail
/// appended after the last file extent. Its one partition is an EFI System
/// partition (`C12A7328-F81F-11D2-BA4B-00A0C93EC93B`) whose byte range is
/// EXACTLY the staged ESP image, so firmware reading the medium as a 512-byte-
/// sector disk (a USB stick, a raw `-drive`) finds the whole FAT through the
/// GPT — with no 32 MiB ceiling at all.
///
/// In a hybrid the El-Torito EFI entry is still emitted (firmware booting the
/// medium as optical media reads 2048-byte blocks, where the GPT at byte 512 is
/// invisible, and goes through the catalog), but an ESP past the u16 ceiling
/// has its catalog sector count **clamped to 65 535**, and the writer RECORDS
/// that in [`MediumLayout::efi`] rather than hiding it. What a clamped count
/// buys from optical media is firmware-defined: EDK2/OVMF's `ElTorito.c` sizes a
/// no-emulation child device as `SectorCount × 2048` bytes (it multiplies by
/// the MEDIA block size, not El-Torito's 512), so it exposes up to
/// [`EDK2_OPTICAL_ESP_REACH`] = 128 MiB. MEASURED 2026-08-22 on oden (QEMU /
/// OVMF_CODE_4M, `korp-installer/tests/hybrid_medium_boot.rs`): a 46 889 984 B
/// ESP whose `BOOTX64.EFI` sits behind a 40 MiB filler booted from `-cdrom`
/// through the clamped entry AND from a raw virtio disk through the GPT, the
/// chainloaded stage echoing the run's nonce both times. Firmware that honours
/// the nominal count stops at 32 MiB on optical media — and boots the same
/// medium off USB/disk through the GPT.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum GptHybrid {
    /// A plain ISO-9660 + El-Torito medium. An ESP over the El-Torito ceiling
    /// is a hard error naming the numbers (the 2026-08-03 silent-clamp lesson).
    #[default]
    Off,
    /// Plain while the ESP fits El-Torito; a GPT hybrid (with the clamped,
    /// recorded catalog entry) once it does not. A medium under the ceiling is
    /// **byte-identical** to [`GptHybrid::Off`] — gunnar's and holger's media
    /// do not change shape because korp's outgrew the catalog.
    WhenEspExceedsElTorito,
    /// Always lay the GPT, whatever the ESP's size — a medium meant for USB
    /// sticks as much as for optical drives.
    Always,
}

/// How far EDK2-derived firmware (OVMF, and the AMI/Insyde firmwares built on
/// it) reads a no-emulation El-Torito image whose catalog count is clamped to
/// 65 535: `ElTorito.c` computes the child device as `SectorCount × media block
/// size`, and optical media have 2048-byte blocks, so 65 535 × 2048 =
/// **134 215 680 B**. Beyond this even OVMF's optical path truncates the ESP;
/// the GPT path (USB/disk) has no such reach limit.
pub const EDK2_OPTICAL_ESP_REACH: u64 = u16::MAX as u64 * SECTOR as u64;

/// **The ISO-9660 SYSTEM AREA** — the first 16 sectors (32 KiB) of every
/// ISO-9660 volume, which the standard reserves and this writer leaves zero.
/// It is where a hybrid medium's boot sector, its GPT and the disk-flavoured
/// copy of the BIOS stage live, which is why making a medium hybrid costs it
/// **no bytes at all**.
pub const SYSTEM_AREA_BYTES: usize = 16 * SECTOR;

/// **Where the disk-flavoured copy of the BIOS stage sits**, in 512-byte LBAs
/// — inside [`SYSTEM_AREA_BYTES`], past LBA 0 (the boot sector) and past the
/// GPT's primary header + array (LBAs 1..=33), so a hybrid MBR and a
/// [`GptHybrid`] medium can be laid over the same bytes.
///
/// It is a CONSTANT and not a placement because the boot sector's 440 bytes of
/// code are assembled by the caller (`korp_boot_bios::emit_mbr`, which this
/// crate cannot depend on — the loader depends on THIS crate for
/// [`crate::bios_patch`], not the other way round). One number both sides
/// spell from here, never twice.
pub const HYBRID_STAGE_LBA: u64 = 34;

/// Where the hybrid medium's second boot path landed — what a receipt says
/// when an `.iso` is ALSO a bootable disk.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HybridMbrPlacement {
    /// 512-byte LBA of the disk-flavoured stage copy ([`HYBRID_STAGE_LBA`]).
    pub stage_lba: u64,
    /// Its length in 512-byte sectors — what the MBR code reads.
    pub stage_sectors: u16,
    /// The kernel's LBA as patched into THAT copy's table (the El-Torito
    /// copy's LBA × 4: the same bytes, counted in 512-byte sectors).
    pub kernel_lba: u32,
    /// The initramfs' LBA as patched into that copy (0 = none).
    pub initrd_lba: u32,
}

/// Where the El-Torito catalog put the EFI boot image, and how it described it.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EfiBootPlacement {
    /// The image's load RBA (2048-byte ISO sector).
    pub lba: u32,
    /// The image's byte length on the medium.
    pub len: u64,
    /// The sector count the catalog STATES (512-byte virtual sectors).
    pub catalog_sectors: u16,
    /// `true` when `len` needed more than `u16::MAX` sectors and the count was
    /// clamped — only ever possible in a [`GptHybrid`] medium; the plain
    /// writer refuses instead.
    pub clamped: bool,
}

/// The GPT an [`IsoWriter`] laid over a hybrid medium — its one EFI System
/// partition, in 512-byte LBAs of the medium read as a disk.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GptEspPartition {
    /// First LBA of the ESP partition (inclusive).
    pub first_lba: u64,
    /// Last LBA of the ESP partition (inclusive).
    pub last_lba: u64,
    /// The medium's last LBA (where the backup GPT header sits).
    pub disk_last_lba: u64,
    /// The disk GUID (on-disk mixed-endian order), derived from content so the
    /// mastering stays deterministic.
    pub disk_guid: [u8; 16],
    /// The ESP's unique partition GUID (on-disk order).
    pub partition_guid: [u8; 16],
}

impl GptEspPartition {
    /// Byte offset of the ESP partition on the medium.
    pub fn offset_bytes(&self) -> u64 {
        self.first_lba * GPT_LBA
    }
    /// Byte length of the ESP partition.
    pub fn len_bytes(&self) -> u64 {
        (self.last_lba - self.first_lba + 1) * GPT_LBA
    }
}

/// **The shape a mastered medium has** — what [`IsoWriter::layout`] hands back
/// after a build, so the composer's receipt states what was laid rather than
/// what was intended.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MediumLayout {
    /// Total image bytes (a whole number of 2048-byte sectors, tail included).
    pub total_bytes: u64,
    /// The El-Torito EFI entry, when an EFI boot image was marked.
    pub efi: Option<EfiBootPlacement>,
    /// The GPT's ESP partition, when the medium is a hybrid.
    pub gpt: Option<GptEspPartition>,
    /// `true` when the medium is UEFI-only and the writer laid the BIOS
    /// refusal stub ([`bios_refusal_sector`]) as the El-Torito default entry
    /// (and, on a hybrid, as the protective MBR's boot code).
    pub bios_refusal: bool,
    /// The real legacy-BIOS stage, when one was declared through
    /// [`IsoWriter::set_bios_stage`]: where it landed, what the catalog says,
    /// and what its patch table was filled with.
    pub bios: Option<BiosStagePlacement>,
    /// The HYBRID boot sector, when one was declared through
    /// [`IsoWriter::set_hybrid_mbr`] — the medium is then bootable as a DISK
    /// as well as as a cdrom.
    pub hybrid_mbr: Option<HybridMbrPlacement>,
}

/// **A legacy-BIOS first stage the writer patches** — the loader image, and
/// the payloads whose placement it must be told about. See
/// [`crate::bios_patch`] for the table format and
/// [`IsoWriter::set_bios_stage`] for what the writer does with it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BiosStage {
    /// ISO path of the staged stage image (e.g. `bios/stage.bin`); it becomes
    /// the El-Torito default (80x86, no-emulation) entry.
    pub image: String,
    /// ISO path of the bzImage the stage loads (e.g. `vmlinuz`).
    pub kernel: String,
    /// ISO path of the initramfs the stage loads, if any.
    pub initrd: Option<String>,
    /// The kernel command line the stage hands to the kernel.
    pub cmdline: String,
}

/// Where a declared [`BiosStage`] landed and what was patched into it.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BiosStagePlacement {
    /// The stage image's load RBA (2048-byte ISO sector).
    pub lba: u32,
    /// The stage image's byte length on the medium.
    pub len: u32,
    /// The sector count the catalog states (512-byte virtual sectors) — the
    /// whole image, rounded up.
    pub catalog_sectors: u16,
    /// The kernel's LBA as patched into the table.
    pub kernel_lba: u32,
    /// The kernel's byte length as patched.
    pub kernel_len: u32,
    /// The initramfs' LBA as patched (0 = none).
    pub initrd_lba: u32,
    /// The initramfs' byte length as patched (0 = none).
    pub initrd_len: u32,
    /// The command line's byte length as patched.
    pub cmdline_len: u32,
}

impl MediumLayout {
    /// One receipt line saying what this medium is — plain or hybrid, and for a
    /// hybrid exactly what each boot path gets.
    pub fn describe(&self) -> String {
        let base = self.describe_boot_paths();
        let hybrid = match self.hybrid_mbr {
            None => String::new(),
            Some(h) => format!(
                "; HYBRID: 55 AA boot sector at LBA 0 chainloading a disk-flavoured copy of the stage \
                 at 512-byte LBA {} ({} sectors, sector_shift 9, kernel LBA {}, initrd LBA {}) — this \
                 .iso boots as a DISK too, so a cloud that imports a disk image can run the installer",
                h.stage_lba, h.stage_sectors, h.kernel_lba, h.initrd_lba
            ),
        };
        if let Some(b) = self.bios {
            format!(
                "{base}; legacy BIOS: real stage at LBA {} ({} B, catalog {} sectors) patched with \
                 kernel LBA {} ({} B), initrd LBA {} ({} B), cmdline {} B{hybrid}",
                b.lba, b.len, b.catalog_sectors, b.kernel_lba, b.kernel_len, b.initrd_lba, b.initrd_len, b.cmdline_len
            )
        } else if self.bios_refusal {
            format!(
                "{base}; legacy BIOS/CSM: UEFI-only REFUSAL stub ({BIOS_REFUSAL_FILE}) as the El-Torito \
                 default entry — SeaBIOS prints a sentence and halts instead of running FAT bytes"
            )
        } else {
            base
        }
    }

    fn describe_boot_paths(&self) -> String {
        match (self.efi, self.gpt) {
            (Some(efi), Some(gpt)) => {
                let optical = if efi.clamped {
                    format!(
                        "El-Torito entry CLAMPED to {} sectors (spec-nominal {} B; EDK2/OVMF \
                         optical reach {} B{})",
                        efi.catalog_sectors,
                        MAX_BOOT_IMAGE_BYTES,
                        EDK2_OPTICAL_ESP_REACH,
                        if efi.len > EDK2_OPTICAL_ESP_REACH {
                            " — EXCEEDED: optical boot of this medium is unsupported, boot it as a disk"
                        } else {
                            ""
                        }
                    )
                } else {
                    format!("El-Torito entry {} sectors (exact)", efi.catalog_sectors)
                };
                format!(
                    "GPT HYBRID medium, {} B: ESP {} B at ISO LBA {} = GPT ESP partition LBA {}..={} \
                     (disk/USB boot reads the whole FAT through the GPT); {optical}",
                    self.total_bytes, efi.len, efi.lba, gpt.first_lba, gpt.last_lba,
                )
            }
            (Some(efi), None) => format!(
                "plain ISO-9660/El-Torito medium, {} B: ESP {} B at ISO LBA {}, catalog {} sectors",
                self.total_bytes, efi.len, efi.lba, efi.catalog_sectors
            ),
            (None, _) => format!("ISO-9660 medium, {} B, no EFI boot image", self.total_bytes),
        }
    }
}

/// Where a staged file's bytes come from: a host path (everything a caller
/// stages) or bytes the writer itself synthesised (the BIOS refusal stub —
/// see [`bios_refusal_sector`]). One enum rather than a temp file, so the
/// writer never touches the host filesystem to master something it made up.
#[derive(Clone)]
enum FileSource {
    Host(PathBuf),
    Bytes(Vec<u8>),
}

impl FileSource {
    fn len(&self) -> Result<u64> {
        match self {
            FileSource::Host(p) => Ok(std::fs::metadata(p)
                .with_context(|| format!("iso writer: stat {}", p.display()))?
                .len()),
            FileSource::Bytes(b) => Ok(b.len() as u64),
        }
    }
    fn read(&self, cap: usize) -> Result<Vec<u8>> {
        match self {
            FileSource::Host(p) => {
                let mut f = std::fs::File::open(p)
                    .with_context(|| format!("iso writer: open {}", p.display()))?;
                let mut buf = Vec::with_capacity(cap);
                f.read_to_end(&mut buf)?;
                Ok(buf)
            }
            FileSource::Bytes(b) => Ok(b.clone()),
        }
    }
}

/// A file's assigned data location, filled during layout.
struct FilePlacement {
    lba: u32,
    len: u32,
    src: FileSource,
}

/// The ISO-root name of the BIOS refusal stub a UEFI-only medium carries (8.3,
/// what [`iso_file_name`] would make of it, minus the `;1`).
pub const BIOS_REFUSAL_FILE: &str = "BIOSREF.BIN";

/// The text the BIOS refusal stub prints — on the VGA text console (INT 10h
/// teletype) AND byte-for-byte on COM1 (port `0x3F8`), so a headless box with
/// a serial console reads the same sentence a KVM graphics console shows.
pub const BIOS_REFUSAL_TEXT: &str =
    "This medium boots UEFI only. Legacy BIOS (SeaBIOS/CSM) cannot start it: switch the machine to UEFI firmware.";

/// **The BIOS El-Torito image a UEFI-only medium carries: an honest refusal.**
///
/// # Why a medium with no BIOS payload needs a BIOS entry at all
///
/// El-Torito's *default/initial* entry is platform `0x00` (80x86) and every
/// BIOS reads it. Until 2026-08-28 a UEFI-only medium pointed that entry at the
/// FAT `efiboot.img` (OVMF ignores the default and takes the `0xEF` section),
/// so SeaBIOS loaded the first 512-byte sectors of a FAT filesystem and
/// **executed them as code**. MEASURED twice: gunnar 2026-08-12 ("garbled
/// COLOUR CHARS on the console" — the confetti was FAT bytes running as
/// instructions) and gunnar-installer-…-nowg.iso on UpCloud 2026-08-27
/// (UpCloud boots SeaBIOS only: *"noll utdata, installeraren startar aldrig"*).
/// Both times the operator had NOTHING to read — no sentence, no refusal, no
/// hint that the medium was fine and the firmware was the mismatch.
///
/// This is not BIOS support (a BIOS boot path for a bzImage + initramfs is a
/// real loader and a product decision); it is the one honest sentence the
/// firmware can be made to print. The catalog's default entry now loads THIS
/// sector at `0000:7C00`; it prints [`BIOS_REFUSAL_TEXT`] and halts. The EFI
/// section is untouched — OVMF/UEFI boots exactly as before.
///
/// # The code, hand-assembled (real mode, 16-bit, loaded at `0x7C00`)
///
/// ```text
///   7C00  FA              cli
///   7C01  31 C0           xor  ax, ax
///   7C03  8E D8           mov  ds, ax
///   7C05  8E D0           mov  ss, ax
///   7C07  BC 00 7C        mov  sp, 0x7C00
///   7C0A  FB              sti
///   7C0B  BE 24 7C        mov  si, 0x7C24        ; the message below
///   7C0E  AC          L:  lodsb
///   7C0F  84 C0           test al, al
///   7C11  74 0D           jz   H
///   7C13  BA F8 03        mov  dx, 0x3F8         ; COM1
///   7C16  EE              out  dx, al
///   7C17  B4 0E           mov  ah, 0x0E          ; INT 10h teletype
///   7C19  BB 07 00        mov  bx, 0x0007        ; page 0, light grey
///   7C1C  CD 10           int  0x10
///   7C1E  EB EE           jmp  L
///   7C20  FA          H:  cli
///   7C21  F4              hlt
///   7C22  EB FC           jmp  H
///   7C24  <text> 0D 0A 00
/// ```
///
/// Pure bytes, no assembler in the build: the offsets are asserted by a test
/// below (`the_bios_refusal_sector_is_a_valid_boot_sector`), and the sector
/// ends in the `55 AA` signature so the same bytes serve as the boot code of
/// the hybrid medium's protective MBR ([`lay_hybrid_gpt`]) — a UEFI-only
/// medium dd'd to a USB stick and booted by a BIOS says the same sentence.
pub fn bios_refusal_sector() -> [u8; 512] {
    let mut s = [0u8; 512];
    let code: [u8; 36] = [
        0xFA, // cli
        0x31, 0xC0, // xor ax,ax
        0x8E, 0xD8, // mov ds,ax
        0x8E, 0xD0, // mov ss,ax
        0xBC, 0x00, 0x7C, // mov sp,0x7C00
        0xFB, // sti
        0xBE, 0x24, 0x7C, // mov si,0x7C24
        0xAC, // L: lodsb
        0x84, 0xC0, // test al,al
        0x74, 0x0D, // jz H
        0xBA, 0xF8, 0x03, // mov dx,0x3F8
        0xEE, // out dx,al
        0xB4, 0x0E, // mov ah,0x0E
        0xBB, 0x07, 0x00, // mov bx,0x0007
        0xCD, 0x10, // int 0x10
        0xEB, 0xEE, // jmp L
        0xFA, // H: cli
        0xF4, // hlt
        0xEB, 0xFC, // jmp H
    ];
    s[..code.len()].copy_from_slice(&code);
    let mut off = code.len(); // == 0x24
    for b in BIOS_REFUSAL_TEXT.bytes().chain(*b"\r\n") {
        s[off] = b;
        off += 1;
    }
    s[off] = 0; // NUL terminator the loop stops on
    s[510] = 0x55;
    s[511] = 0xAA;
    s
}

impl IsoWriter {
    /// A fresh writer for a volume with the given id (uppercased + trimmed to the
    /// 32-char ISO d-character volume-id field on write).
    pub fn new(volume_id: impl Into<String>) -> Self {
        IsoWriter {
            volume_id: volume_id.into(),
            nodes: vec![DirNode {
                name: String::new(),
                parent: 0,
                dirs: BTreeMap::new(),
                files: BTreeMap::new(),
                lba: 0,
                len: 0,
                path_no: 1,
            }],
            bios_boot: None,
            efi_boot: None,
            bios_sectors: 4,
            bios_stage: None,
            gpt_hybrid: GptHybrid::Off,
            hybrid_mbr: None,
            layout: None,
        }
    }

    /// Choose whether the medium is laid as a [`GptHybrid`] (default:
    /// [`GptHybrid::Off`] — the plain medium, and a hard error past the
    /// El-Torito ceiling).
    pub fn set_gpt_hybrid(&mut self, mode: GptHybrid) {
        self.gpt_hybrid = mode;
    }

    /// **Make the medium HYBRID** — an `.iso` that is also a bootable DISK.
    ///
    /// `mbr_code` is the boot sector's first [`crate::bios_patch::MBR_CODE_LEN`]
    /// bytes, assembled by the caller from
    /// `korp_boot_bios::emit_mbr(`[`HYBRID_STAGE_LBA`]`, stage_sectors)`: it
    /// relocates itself, reads the stage from that LBA with INT 13h AH=42h and
    /// jumps to it. The writer lays the rest — a partition table, the `55 AA`
    /// signature, and a SECOND copy of the declared [`BiosStage`] at
    /// [`HYBRID_STAGE_LBA`] whose patch table counts in 512-byte sectors
    /// ([`crate::bios_patch::SECTOR_SHIFT_DISK`]) instead of 2048-byte ones.
    ///
    /// # Why a second copy and not one stage that guesses
    ///
    /// The stage's patch table holds LBAs, and an LBA is only a number until
    /// you say how big a sector is. Off a cdrom the firmware's INT 13h counts
    /// 2048-byte sectors; off a disk it counts 512-byte ones. The same file
    /// therefore needs two tables, and a stage that asked the firmware
    /// (INT 13h AH=48h) would be a THIRD opinion about a fact the writer
    /// already knows with certainty. Two patched copies of one image, from one
    /// emitter, is the same trick [`set_bios_stage`](Self::set_bios_stage)
    /// already plays — and both copies live in the ISO's reserved 32 KiB
    /// system area ([`SYSTEM_AREA_BYTES`]), so the medium does not grow by a
    /// byte.
    ///
    /// Refused at build time when no real BIOS stage is declared: a boot
    /// sector that chainloads nothing is worse than no boot sector, because
    /// firmware stops looking once it finds `55 AA`.
    pub fn set_hybrid_mbr(&mut self, mbr_code: Vec<u8>) {
        self.hybrid_mbr = Some(mbr_code);
    }

    /// The shape the last [`build_image`](Self::build_image) / [`write`](Self::write)
    /// produced; `None` before any build.
    pub fn layout(&self) -> Option<&MediumLayout> {
        self.layout.as_ref()
    }

    /// Place `host` on the ISO at the `/`-separated ISO path `iso_path`
    /// (e.g. `EFI/BOOT/BOOTX64.EFI`). Intermediate directories are created.
    pub fn add_file(&mut self, iso_path: &str, host: impl Into<PathBuf>) -> Result<()> {
        let host = host.into();
        let comps: Vec<&str> = iso_path
            .split('/')
            .filter(|c| !c.is_empty() && *c != ".")
            .collect();
        if comps.is_empty() {
            bail!("iso writer: empty iso path for {}", host.display());
        }
        let (dirs, file) = comps.split_at(comps.len() - 1);
        let mut cur = 0usize;
        for d in dirs {
            let key = iso_dir_name(d);
            if let Some(&idx) = self.nodes[cur].dirs.get(&key) {
                cur = idx;
            } else {
                let idx = self.nodes.len();
                self.nodes.push(DirNode {
                    name: key.clone(),
                    parent: cur,
                    dirs: BTreeMap::new(),
                    files: BTreeMap::new(),
                    lba: 0,
                    len: 0,
                    path_no: 0,
                });
                self.nodes[cur].dirs.insert(key, idx);
                cur = idx;
            }
        }
        let fname = iso_file_name(file[0]);
        self.nodes[cur].files.insert(fname, FileSource::Host(host));
        Ok(())
    }

    /// Recursively stage a host directory `host` under the ISO path prefix
    /// `iso_prefix` (empty for the volume root). Symlinks are followed as files.
    pub fn add_dir_recursive(&mut self, iso_prefix: &str, host: &Path) -> Result<()> {
        for entry in std::fs::read_dir(host)
            .with_context(|| format!("iso writer: read staging dir {}", host.display()))?
        {
            let entry = entry?;
            let name = entry.file_name();
            let name = name.to_string_lossy();
            let child_iso = if iso_prefix.is_empty() {
                name.to_string()
            } else {
                format!("{iso_prefix}/{name}")
            };
            let ft = entry.file_type()?;
            if ft.is_dir() {
                self.add_dir_recursive(&child_iso, &entry.path())?;
            } else {
                self.add_file(&child_iso, entry.path())?;
            }
        }
        Ok(())
    }

    /// Mark an already-added file as the BIOS El-Torito boot image (no-emulation).
    pub fn set_bios_boot(&mut self, iso_path: &str, load_sectors: u16) {
        self.bios_boot = Some(normalize_iso_path(iso_path));
        self.bios_sectors = load_sectors.max(1);
    }

    /// **Declare a REAL legacy-BIOS stage** (already added to the tree) as the
    /// El-Torito default (80x86, no-emulation) entry, and have the writer fill
    /// its [`crate::bios_patch`] table at build time with where `stage.kernel`
    /// and `stage.initrd` landed on the volume plus `stage.cmdline`.
    ///
    /// This is the isolinux boot-info-table trick generalised: the writer is
    /// the ONE party that knows every file's LBA, so the loader parses no
    /// ISO-9660 at all. The catalog's sector count is the whole stage image,
    /// rounded up to 512-byte sectors. A medium with a declared stage never
    /// gets the refusal stub ([`bios_refusal_sector`]) — it has a real BIOS
    /// path — and the EFI section is untouched, so UEFI firmware boots exactly
    /// as before: ONE medium, two El-Torito entries.
    pub fn set_bios_stage(&mut self, stage: BiosStage) {
        self.bios_boot = Some(normalize_iso_path(&stage.image));
        self.bios_stage = Some(stage);
    }

    /// Mark an already-added file as the EFI (`0xEF`) El-Torito boot image — the
    /// `efiboot.img` EFI System Partition OVMF boots.
    pub fn set_efi_boot(&mut self, iso_path: &str) {
        self.efi_boot = Some(normalize_iso_path(iso_path));
    }

    /// Master the ISO to `out`, returning the total image size in bytes. Pure
    /// Rust: builds the whole byte image in memory and writes it once.
    pub fn write(&mut self, out: &Path) -> Result<u64> {
        let image = self.build_image()?;
        std::fs::write(out, &image)
            .with_context(|| format!("iso writer: write {}", out.display()))?;
        Ok(image.len() as u64)
    }

    /// Build the complete ISO byte image (the testable core of [`write`]).
    pub fn build_image(&mut self) -> Result<Vec<u8>> {
        // ── layout ──────────────────────────────────────────────────────────
        // A BOOTABLE image: PVD 16, Boot Record VD 17, terminator 18, path
        // tables 19+20, boot catalog 21, then directory extents and file data.
        //
        // A NON-bootable image drops the Boot Record VD and the catalog, and
        // everything after moves up: PVD 16, terminator 17, path tables 18+19,
        // then the extents. The volume-descriptor sequence has to be contiguous
        // and terminated, so the boot record cannot simply be left as a zeroed
        // sector — a reader would take type 0 with no `CD001` as a malformed
        // descriptor. See `BootImages` for why an ISO with nothing bootable no
        // longer gets these structures at all.
        let bootable = self.bios_boot.is_some() || self.efi_boot.is_some();
        // A UEFI-only medium gets an honest BIOS default entry — the refusal
        // stub — instead of the FAT ESP's first sectors running as x86 code
        // under SeaBIOS. See `bios_refusal_sector` for the two measurements.
        // Idempotent: a second `build_image` re-inserts the same key.
        let bios_refusal = self.bios_boot.is_none() && self.efi_boot.is_some();
        if bios_refusal {
            self.nodes[0].files.insert(
                iso_file_name(BIOS_REFUSAL_FILE),
                FileSource::Bytes(bios_refusal_sector().to_vec()),
            );
        }
        const PVD_LBA: u32 = 16;
        let boot_record_lba: u32 = 17;
        let terminator_lba: u32 = if bootable { 18 } else { 17 };
        let path_l_lba: u32 = terminator_lba + 1;
        let path_m_lba: u32 = path_l_lba + 1;
        let boot_catalog_lba: u32 = path_m_lba + 1;
        let mut next_lba: u32 = if bootable { boot_catalog_lba + 1 } else { path_m_lba + 1 };

        // BFS order over directories → path-table numbering + extent placement.
        let order = self.bfs_order();
        // Compute each directory's extent length.
        for &ni in &order {
            let len = self.dir_extent_len(ni);
            self.nodes[ni].len = len as u32;
        }
        // Assign path-table numbers (1-based, root == 1) and directory LBAs.
        for (pos, &ni) in order.iter().enumerate() {
            self.nodes[ni].path_no = (pos + 1) as u16;
        }
        for &ni in &order {
            self.nodes[ni].lba = next_lba;
            next_lba += round_sectors(self.nodes[ni].len as u64) as u32;
        }

        // Place every file's data. Deterministic order: directory BFS, files sorted.
        let mut placements: BTreeMap<String, FilePlacement> = BTreeMap::new();
        for &ni in &order {
            let files: Vec<(String, FileSource)> = self.nodes[ni]
                .files
                .iter()
                .map(|(k, v)| (k.clone(), v.clone()))
                .collect();
            let iso_dir = self.node_iso_path(ni);
            for (fname, src) in files {
                let len = src.len()?;
                let lba = next_lba;
                next_lba += round_sectors(len.max(1)) as u32;
                let key = if iso_dir.is_empty() {
                    strip_version(&fname)
                } else {
                    format!("{iso_dir}/{}", strip_version(&fname))
                };
                placements.insert(
                    key,
                    FilePlacement { lba, len: len as u32, src },
                );
            }
        }
        // ── the real BIOS stage: fill its patch table NOW, after placement ──
        // The writer is the one party that knows where the kernel and the
        // initramfs landed; the loader reads the numbers off its own image and
        // parses no ISO-9660. The patched bytes replace the host source so the
        // serialization below writes what the stage will run with.
        // The disk-flavoured twin of the stage, when the medium is hybrid: the
        // SAME image bytes with a table that counts 512-byte sectors. Produced
        // in the one place that knows where the payloads landed.
        let mut hybrid_stage: Option<(Vec<u8>, HybridMbrPlacement)> = None;
        let bios_placement: Option<BiosStagePlacement> = match self.bios_stage.clone() {
            None => None,
            Some(stage) => {
                let kernel = lookup_placement(&placements, &normalize_iso_path(&stage.kernel))?;
                let (kernel_lba, kernel_len) = (kernel.lba, kernel.len);
                let (initrd_lba, initrd_len) = match stage.initrd.as_deref() {
                    Some(p) => {
                        let i = lookup_placement(&placements, &normalize_iso_path(p))?;
                        (i.lba, i.len)
                    }
                    None => (0, 0),
                };
                let key = normalize_iso_path(&stage.image);
                let pl = placements.get_mut(&key).with_context(|| {
                    format!("iso writer: BIOS stage '{key}' was not staged on the ISO")
                })?;
                let mut bytes = pl.src.read(pl.len as usize)?;
                let off = crate::bios_patch::find_table(&bytes)
                    .with_context(|| format!("iso writer: BIOS stage '{key}'"))?;
                let table = crate::bios_patch::BiosPatchTable {
                    kernel_lba,
                    kernel_len,
                    initrd_lba,
                    initrd_len,
                    cmdline: stage.cmdline.clone(),
                    sector_shift: crate::bios_patch::SECTOR_SHIFT_CD,
                };
                table
                    .write_into(&mut bytes, off)
                    .with_context(|| format!("iso writer: patching BIOS stage '{key}'"))?;
                let catalog_sectors = u16::try_from((bytes.len() as u64).div_ceil(512)).map_err(|_| {
                    anyhow::anyhow!("iso writer: BIOS stage '{key}' is {} B, over the El-Torito ceiling", bytes.len())
                })?;
                // A stage must fit below the conventional-memory hole the BIOS
                // loads it into (0x7C00 upward, under 0xA0000) with room for its
                // own bounce buffers; 64 KiB is far beyond any real stage.
                if bytes.len() > 64 * 1024 {
                    bail!("iso writer: BIOS stage '{key}' is {} B; a real-mode stage must stay under 64 KiB", bytes.len());
                }
                let placement = BiosStagePlacement {
                    lba: pl.lba,
                    len: bytes.len() as u32,
                    catalog_sectors,
                    kernel_lba,
                    kernel_len,
                    initrd_lba,
                    initrd_len,
                    cmdline_len: stage.cmdline.len() as u32,
                };
                // ── the hybrid twin ──────────────────────────────────────
                // Off a disk, INT 13h counts 512-byte sectors, so every LBA in
                // the table is the El-Torito one × 4 and `sector_shift` says
                // 9. Nothing else about the image changes.
                if self.hybrid_mbr.is_some() {
                    const PER_ISO_SECTOR: u32 = (SECTOR / GPT_LBA as usize) as u32;
                    let mut twin = pl.src.read(pl.len as usize)?;
                    let disk = crate::bios_patch::BiosPatchTable {
                        kernel_lba: kernel_lba * PER_ISO_SECTOR,
                        kernel_len,
                        initrd_lba: if initrd_lba == 0 { 0 } else { initrd_lba * PER_ISO_SECTOR },
                        initrd_len,
                        cmdline: stage.cmdline.clone(),
                        sector_shift: crate::bios_patch::SECTOR_SHIFT_DISK,
                    };
                    let toff = crate::bios_patch::find_table(&twin)
                        .with_context(|| format!("iso writer: hybrid twin of BIOS stage '{key}'"))?;
                    disk.write_into(&mut twin, toff)
                        .with_context(|| format!("iso writer: patching the hybrid twin of '{key}'"))?;
                    // div_ceil, never truncating: an MBR that reads floor(len/512)
                    // sectors leaves the stage's tail — its STRINGS, and on a
                    // shorter image its table — unloaded, and the loader then
                    // prints garbage or nothing. `emit_stage` already pads to a
                    // whole sector; this is what makes that a property rather
                    // than a coincidence.
                    let stage_sectors = u16::try_from(twin.len().div_ceil(GPT_LBA as usize).max(1))
                        .map_err(|_| anyhow::anyhow!("iso writer: the hybrid stage copy is too long to chainload"))?;
                    let end = HYBRID_STAGE_LBA as usize * GPT_LBA as usize
                        + stage_sectors as usize * GPT_LBA as usize;
                    if end > SYSTEM_AREA_BYTES {
                        bail!(
                            "iso writer: the hybrid stage copy ({} B at LBA {HYBRID_STAGE_LBA}) ends at byte                              {end}, past the ISO system area's {SYSTEM_AREA_BYTES} B — it would overwrite the                              volume descriptors",
                            twin.len()
                        );
                    }
                    let placement = HybridMbrPlacement {
                        stage_lba: HYBRID_STAGE_LBA,
                        stage_sectors,
                        kernel_lba: disk.kernel_lba,
                        initrd_lba: disk.initrd_lba,
                    };
                    hybrid_stage = Some((twin, placement));
                }
                pl.src = FileSource::Bytes(bytes);
                self.bios_sectors = catalog_sectors.max(1);
                Some(placement)
            }
        };

        // ── resolve El-Torito boot-image placements ──────────────────────────
        // `(placement, 512-byte sector count)`: a caller's BIOS image loads
        // `bios_sectors`; the refusal stub is ONE sector by construction.
        let bios: Option<(&FilePlacement, u16)> = match self.bios_boot.as_ref() {
            Some(p) => Some((lookup_placement(&placements, p)?, self.bios_sectors)),
            None if bios_refusal => Some((lookup_placement(&placements, BIOS_REFUSAL_FILE)?, 1)),
            None => None,
        };
        let efi = self
            .efi_boot
            .as_ref()
            .map(|p| lookup_placement(&placements, p))
            .transpose()?;

        // ── the El-Torito EFI entry: exact, or (hybrid only) clamped + recorded ──
        // The ONLY conversions from a byte length to a catalog sector count are
        // `el_torito_sectors` (refuses past the ceiling) and
        // `el_torito_sectors_clamped` (clamps and SAYS so) — and the second is
        // reachable only when a GPT is also laid, so no medium ever carries a
        // clamped count as its sole description of the ESP.
        let hybrid = match (self.gpt_hybrid, efi.as_ref()) {
            (GptHybrid::Off, _) | (_, None) => false,
            (GptHybrid::Always, Some(_)) => true,
            (GptHybrid::WhenEspExceedsElTorito, Some(p)) => p.len as u64 > MAX_BOOT_IMAGE_BYTES,
        };
        let efi_entry: Option<EfiBootPlacement> = match efi.as_ref() {
            None => None,
            Some(p) if hybrid => {
                let (catalog_sectors, clamped) = el_torito_sectors_clamped(p.len as u64);
                Some(EfiBootPlacement { lba: p.lba, len: p.len as u64, catalog_sectors, clamped })
            }
            Some(p) => {
                let catalog_sectors =
                    el_torito_sectors(p.len as u64, "EFI (efiboot.img)").with_context(|| {
                        format!(
                            "iso writer: building the El-Torito boot catalog for {}",
                            self.efi_boot.as_deref().unwrap_or("(no EFI boot image)")
                        )
                    })?;
                Some(EfiBootPlacement { lba: p.lba, len: p.len as u64, catalog_sectors, clamped: false })
            }
        };

        // A hybrid carries its backup GPT (array + header) in a tail after the
        // last extent; the PVD's volume space covers the tail so the ISO-9660
        // volume and the disk image end on the same byte.
        let total_sectors = if hybrid { next_lba + GPT_TAIL_ISO_SECTORS } else { next_lba };

        // ── serialize ────────────────────────────────────────────────────────
        let mut img = vec![0u8; total_sectors as usize * SECTOR];

        // Primary Volume Descriptor.
        let root = &self.nodes[0];
        let pvd = self.primary_volume_descriptor(
            total_sectors,
            root.lba,
            root.len,
            path_l_lba,
            path_m_lba,
            self.path_table_len(&order),
        );
        write_sector(&mut img, PVD_LBA, &pvd);

        // El-Torito Boot Record volume descriptor + boot catalog — emitted ONLY
        // when a boot image really resolved. An image with nothing bootable is a
        // plain data volume, not a volume that claims a boot catalog it has no
        // image for.
        //
        // Fallible: an oversized boot image stops the build here, with its size
        // and the ceiling in the message, instead of being silently truncated
        // into a catalog that firmware reads as "no bootable device".
        if let Some(images) = BootImages::new(
            bios.map(|(p, sectors)| (p.lba, sectors)),
            efi_entry.map(|e| (e.lba, e.catalog_sectors)),
        ) {
            let catalog = boot_catalog(&images)?;
            write_sector(&mut img, boot_record_lba, &boot_record_vd(boot_catalog_lba));
            write_sector(&mut img, boot_catalog_lba, &catalog);
        }

        // Volume Descriptor Set Terminator.
        let mut term = vec![0u8; SECTOR];
        term[0] = 255;
        term[1..6].copy_from_slice(b"CD001");
        term[6] = 1;
        write_sector(&mut img, terminator_lba, &term);

        // Path tables.
        let (l_table, m_table) = self.path_tables(&order);
        write_at(&mut img, path_l_lba, &l_table);
        write_at(&mut img, path_m_lba, &m_table);

        // Directory extents.
        for &ni in &order {
            let extent = self.dir_extent(ni, &placements);
            write_at(&mut img, self.nodes[ni].lba, &extent);
        }

        // File data.
        for pl in placements.values() {
            let buf = pl.src.read(pl.len as usize)?;
            let off = pl.lba as usize * SECTOR;
            img[off..off + buf.len()].copy_from_slice(&buf);
        }

        // ── the GPT hybrid: protective MBR + primary GPT in the system area,
        //    backup GPT in the tail, ONE EFI System partition == the ESP bytes ──
        let gpt = match efi_entry {
            Some(e) if hybrid => Some(lay_hybrid_gpt(&mut img, &self.volume_id, e, bios_refusal)?),
            _ => None,
        };

        // ── the HYBRID boot sector: the same .iso, bootable as a DISK ────────
        // Laid LAST, because the GPT above owns the same 512 bytes and this is
        // the boot code both shapes carry: a protective GPT MBR and a plain
        // hybrid MBR differ in their PARTITION TABLE, never in whether the
        // firmware is given something to run.
        let hybrid_mbr = match (self.hybrid_mbr.clone(), hybrid_stage) {
            (None, _) => None,
            (Some(_), None) => bail!(
                "iso writer: a hybrid MBR was declared but this medium has no real legacy-BIOS stage                  (`set_bios_stage`) — a boot sector that chainloads nothing is WORSE than none,                  because firmware stops looking the moment it finds 55 AA"
            ),
            (Some(code), Some((twin, placement))) => {
                if code.len() > crate::bios_patch::MBR_CODE_LEN {
                    bail!(
                        "iso writer: {} bytes of MBR boot code do not fit in the {} the partition table                          leaves",
                        code.len(),
                        crate::bios_patch::MBR_CODE_LEN
                    );
                }
                write_lba(&mut img, placement.stage_lba, &twin);
                let total_lbas = (img.len() as u64 / GPT_LBA).min(u32::MAX as u64) as u32;
                let mbr = &mut img[0..GPT_LBA as usize];
                mbr[..code.len()].copy_from_slice(&code);
                if gpt.is_none() {
                    // ONE partition spanning the whole medium, starting at LBA 0
                    // — what `isohybrid` writes, and what every firmware and
                    // partition reader has accepted from an ISO for fifteen
                    // years. With a GPT the 0xEE protective entry
                    // `lay_hybrid_gpt` already laid stays: two partition tables
                    // claiming the same bytes is what a GPT hybrid must not be.
                    let p = 446;
                    mbr[p] = 0x80; // bootable
                    mbr[p + 1] = 0x00; // CHS first: head 0, sector 1, cylinder 0
                    mbr[p + 2] = 0x01;
                    mbr[p + 3] = 0x00;
                    mbr[p + 4] = 0x83; // Linux
                    mbr[p + 5] = 0xFF; // CHS last: max (the LBA fields are the truth)
                    mbr[p + 6] = 0xFF;
                    mbr[p + 7] = 0xFF;
                    mbr[p + 8..p + 12].copy_from_slice(&0u32.to_le_bytes());
                    mbr[p + 12..p + 16].copy_from_slice(&total_lbas.to_le_bytes());
                }
                let mbr = &mut img[0..GPT_LBA as usize];
                mbr[510] = 0x55;
                mbr[511] = 0xAA;
                Some(placement)
            }
        };

        self.layout = Some(MediumLayout {
            total_bytes: img.len() as u64,
            efi: efi_entry,
            gpt,
            bios_refusal,
            bios: bios_placement,
            hybrid_mbr,
        });
        Ok(img)
    }

    /// BFS order of directory node indices (root first).
    fn bfs_order(&self) -> Vec<usize> {
        let mut order = Vec::with_capacity(self.nodes.len());
        let mut queue = std::collections::VecDeque::new();
        queue.push_back(0usize);
        while let Some(ni) = queue.pop_front() {
            order.push(ni);
            for &child in self.nodes[ni].dirs.values() {
                queue.push_back(child);
            }
        }
        order
    }

    /// The `/`-joined ISO path of a directory node ("" for the root).
    fn node_iso_path(&self, ni: usize) -> String {
        if ni == 0 {
            return String::new();
        }
        let mut parts = Vec::new();
        let mut cur = ni;
        while cur != 0 {
            parts.push(self.nodes[cur].name.clone());
            cur = self.nodes[cur].parent;
        }
        parts.reverse();
        parts.join("/")
    }

    /// Length in bytes of a directory's extent (`.` + `..` + children, unpadded).
    fn dir_extent_len(&self, ni: usize) -> usize {
        let mut len = dir_record_len(1) * 2; // "." and ".."
        let node = &self.nodes[ni];
        for name in node.dirs.keys() {
            len += dir_record_len(name.len());
        }
        for name in node.files.keys() {
            len += dir_record_len(name.len());
        }
        len
    }

    /// Serialize a directory's extent (padded to a whole sector), reading each
    /// file record's (lba, len) from the assigned placement map.
    fn dir_extent(&self, ni: usize, places: &BTreeMap<String, FilePlacement>) -> Vec<u8> {
        let node = &self.nodes[ni];
        let parent = &self.nodes[node.parent];
        let iso_dir = self.node_iso_path(ni);
        let mut buf = Vec::new();
        // "." → this directory.
        buf.extend_from_slice(&dir_record(&[0x00], node.lba, node.len, true));
        // ".." → parent directory.
        buf.extend_from_slice(&dir_record(&[0x01], parent.lba, parent.len, true));
        // Child directories (sorted by BTreeMap).
        for (name, &idx) in &node.dirs {
            let c = &self.nodes[idx];
            buf.extend_from_slice(&dir_record(name.as_bytes(), c.lba, c.len, true));
        }
        // Files (sorted) — look up the LBA/len assigned in build_image.
        for name in node.files.keys() {
            let key = if iso_dir.is_empty() {
                strip_version(name)
            } else {
                format!("{iso_dir}/{}", strip_version(name))
            };
            let (lba, len) = places
                .get(&key)
                .map(|p| (p.lba, p.len))
                .unwrap_or((0, 0));
            buf.extend_from_slice(&dir_record(name.as_bytes(), lba, len, false));
        }
        // Pad to a whole sector.
        pad_to_sector(&mut buf);
        buf
    }

    /// Total (unpadded) length of the type-L / type-M path table.
    fn path_table_len(&self, order: &[usize]) -> u32 {
        order
            .iter()
            .map(|&ni| path_record_len(self.nodes[ni].name.len().max(1)) as u32)
            .sum()
    }

    /// Build the type-L (LE) and type-M (BE) path tables, each padded to a sector.
    fn path_tables(&self, order: &[usize]) -> (Vec<u8>, Vec<u8>) {
        let mut l = Vec::new();
        let mut m = Vec::new();
        for &ni in order {
            let node = &self.nodes[ni];
            let parent_no = self.nodes[node.parent].path_no;
            let (name_bytes, name_len): (&[u8], usize) = if ni == 0 {
                (&[0x00], 1)
            } else {
                (node.name.as_bytes(), node.name.len())
            };
            l.extend_from_slice(&path_record(name_bytes, name_len, node.lba, parent_no, false));
            m.extend_from_slice(&path_record(name_bytes, name_len, node.lba, parent_no, true));
        }
        pad_to_sector(&mut l);
        pad_to_sector(&mut m);
        (l, m)
    }

    /// The Primary Volume Descriptor sector.
    #[allow(clippy::too_many_arguments)]
    fn primary_volume_descriptor(
        &self,
        total_sectors: u32,
        root_lba: u32,
        root_len: u32,
        path_l_lba: u32,
        path_m_lba: u32,
        path_table_len: u32,
    ) -> Vec<u8> {
        let mut s = vec![0u8; SECTOR];
        s[0] = 1; // PVD type
        s[1..6].copy_from_slice(b"CD001");
        s[6] = 1; // version
                  // system id (8..40) + volume id (40..72): A/d-chars, space-padded.
        fill_dchars(&mut s[8..40], "");
        fill_dchars(&mut s[40..72], &self.volume_id);
        s[80..88].copy_from_slice(&both32(total_sectors)); // volume space size
        s[120..124].copy_from_slice(&both16(1)); // volume set size
        s[124..128].copy_from_slice(&both16(1)); // volume sequence number
        s[128..132].copy_from_slice(&both16(SECTOR as u16)); // logical block size
        s[132..140].copy_from_slice(&both32(path_table_len)); // path table size
        s[140..144].copy_from_slice(&path_l_lba.to_le_bytes()); // type-L path table
        s[148..152].copy_from_slice(&path_m_lba.to_be_bytes()); // type-M path table
                                                                // root directory record (34 bytes) at 156..190.
        let root_rec = dir_record(&[0x00], root_lba, root_len, true);
        s[156..156 + root_rec.len()].copy_from_slice(&root_rec);
        // text id fields: spaces.
        for r in [190..318, 318..446, 446..574, 574..702] {
            for b in &mut s[r] {
                *b = b' ';
            }
        }
        for r in [702..739, 739..776, 776..813] {
            for b in &mut s[r] {
                *b = b' ';
            }
        }
        s[813..830].copy_from_slice(VOL_DATE); // creation
        s[830..847].copy_from_slice(VOL_DATE); // modification
        s[847..864].copy_from_slice(&[b'0'; 17]); // expiration = unset
        s[847 + 16] = 0;
        s[864..881].copy_from_slice(&[b'0'; 17]); // effective = unset
        s[864 + 16] = 0;
        s[881] = 1; // file structure version
        s
    }
}

// ── free helpers ────────────────────────────────────────────────────────────

/// Bytes of a directory record for a name of `name_len` bytes (even-padded).
fn dir_record_len(name_len: usize) -> usize {
    let base = 33 + name_len;
    base + (base & 1) // pad to even
}

/// Serialize one directory record.
fn dir_record(name: &[u8], extent_lba: u32, data_len: u32, is_dir: bool) -> Vec<u8> {
    let base = 33 + name.len();
    let total = base + (base & 1);
    let mut r = vec![0u8; total];
    r[0] = total as u8;
    r[1] = 0; // ext attr length
    r[2..10].copy_from_slice(&both32(extent_lba));
    r[10..18].copy_from_slice(&both32(data_len));
    r[18..25].copy_from_slice(&DIR_DATE);
    r[25] = if is_dir { 0x02 } else { 0x00 };
    r[26] = 0; // file unit size
    r[27] = 0; // interleave gap
    r[28..32].copy_from_slice(&both16(1)); // volume sequence number
    r[32] = name.len() as u8;
    r[33..33 + name.len()].copy_from_slice(name);
    r
}

/// Bytes of a path-table record for a name of `name_len` bytes (even-padded).
fn path_record_len(name_len: usize) -> usize {
    let base = 8 + name_len;
    base + (base & 1)
}

/// Serialize one path-table record (`big_endian` selects type-M vs type-L).
fn path_record(name: &[u8], name_len: usize, extent_lba: u32, parent_no: u16, big_endian: bool) -> Vec<u8> {
    let base = 8 + name_len;
    let total = base + (base & 1);
    let mut r = vec![0u8; total];
    r[0] = name_len as u8;
    r[1] = 0; // ext attr length
    if big_endian {
        r[2..6].copy_from_slice(&extent_lba.to_be_bytes());
        r[6..8].copy_from_slice(&parent_no.to_be_bytes());
    } else {
        r[2..6].copy_from_slice(&extent_lba.to_le_bytes());
        r[6..8].copy_from_slice(&parent_no.to_le_bytes());
    }
    r[8..8 + name_len].copy_from_slice(&name[..name_len]);
    r
}

/// The El-Torito Boot Record volume descriptor (sector 17).
fn boot_record_vd(catalog_lba: u32) -> Vec<u8> {
    let mut s = vec![0u8; SECTOR];
    s[0] = 0; // boot record type
    s[1..6].copy_from_slice(b"CD001");
    s[6] = 1; // version
    let id = b"EL TORITO SPECIFICATION";
    s[7..7 + id.len()].copy_from_slice(id); // boot system id (32 bytes, zero-padded)
    s[71..75].copy_from_slice(&catalog_lba.to_le_bytes()); // boot catalog pointer (LE)
    s
}

/// One El-Torito *virtual* sector. The catalog's sector counts are in these,
/// **not** in 2048-byte ISO sectors.
pub const EL_TORITO_VIRTUAL_SECTOR: u64 = 512;

/// The largest boot image El-Torito can describe: the sector-count field is 16
/// bits of 512-byte virtual sectors, so `65535 × 512` = **33 553 920 bytes**.
///
/// This is a hard limit of the on-disc format, not a policy of ours. There is
/// nothing to raise it to.
pub const MAX_BOOT_IMAGE_BYTES: u64 = u16::MAX as u64 * EL_TORITO_VIRTUAL_SECTOR;

/// A boot image's length as an El-Torito 512-byte sector count, or an error
/// naming every number needed to act on it.
///
/// # Why this is an error and not a clamp
///
/// It used to be `.min(u16::MAX)`. An oversized boot image was therefore
/// **silently truncated**: the catalog described the first 33 553 920 bytes of
/// an ESP that was larger, the ISO mastered without a word, and the firmware —
/// handed a block device that stops in the middle of the filesystem — reported
/// `no bootable device`. No diagnostic, no log line, nothing in the build
/// output to search for. For an appliance that is the worst failure there is:
/// it is discovered on real hardware, by whoever is standing in front of it,
/// and it looks identical to a dead USB stick.
///
/// It was also not hypothetical. Measured 2026-08-03: gunnar's appliance ESP is
/// **33 471 488 B**, which is 82 KiB — 0.25 % — under the ceiling. One kernel
/// bump crosses it.
///
/// So the size is checked at **build** time, where the numbers are known and a
/// human is watching, and the message carries the actual size, the ceiling and
/// the overflow so nobody has to go and work out which of the three they need.
fn el_torito_sectors(len: u64, what: &str) -> Result<u16> {
    if len > MAX_BOOT_IMAGE_BYTES {
        bail!(
            "the {what} boot image is {len} bytes, which is {over} bytes ({over_kib} KiB) over \
             El-Torito's hard ceiling of {MAX_BOOT_IMAGE_BYTES} bytes.\n\
             The boot catalog's sector count is 16 bits of {EL_TORITO_VIRTUAL_SECTOR}-byte \
             virtual sectors ({max_sectors} × {EL_TORITO_VIRTUAL_SECTOR}), so a larger image \
             cannot be described at all — it is a limit of the ISO-9660/El-Torito format and \
             there is no field to widen.\n\
             This used to be truncated silently and the ISO mastered anyway; the firmware then \
             said `no bootable device` and nothing else. Shrink the boot image instead: \
             compress the initramfs, or drop what the appliance does not boot with.",
            over = len - MAX_BOOT_IMAGE_BYTES,
            over_kib = (len - MAX_BOOT_IMAGE_BYTES).div_ceil(1024),
            max_sectors = u16::MAX,
        );
    }
    // `max(1)`: a zero-length image would describe zero sectors, and firmware
    // reads that as "no image" rather than as an error.
    Ok(len.div_ceil(EL_TORITO_VIRTUAL_SECTOR).max(1) as u16)
}

/// The hybrid's conversion: the exact count while it fits, `u16::MAX` and
/// `clamped = true` when it does not. Reachable ONLY from the GPT-hybrid arm of
/// [`IsoWriter::build_image`], where the GPT carries the full ESP and the clamp
/// is recorded on the [`MediumLayout`] — never as a medium's sole description.
fn el_torito_sectors_clamped(len: u64) -> (u16, bool) {
    let sectors = len.div_ceil(EL_TORITO_VIRTUAL_SECTOR).max(1);
    if sectors > u16::MAX as u64 {
        (u16::MAX, true)
    } else {
        (sectors as u16, false)
    }
}

// ── the GPT hybrid ──────────────────────────────────────────────────────────
//
// The on-disk GPT (UEFI 2.x §5.3) laid over the `.iso`, in 512-byte LBAs:
//
//   LBA 0              protective MBR (one 0xEE partition spanning the medium)
//   LBA 1              primary GPT header
//   LBA 2..=33         primary partition array (128 × 128 B)      ← all inside the
//                                                                    ISO's 32 KiB
//                                                                    system area
//   …                  the ISO-9660 volume (descriptors, tables, extents) — the
//                      ESP's extent is the ONE partition, at ISO LBA × 4
//   last-32..=last-1   backup partition array          ← the appended tail
//   last               backup GPT header
//
// korp-installer's `gpt.rs` lays the same structures on a TARGET DISK (three
// partitions, a disk the installer writes); this lays them over a medium the
// writer is mastering. They cannot share code: korp-installer's PID-1 builds
// must not link this crate (see its Cargo.toml), and this crate is what
// korp-installer's forge consumes — so the ~40 lines of header/entry/MBR
// serialization below are the sibling of gpt.rs's, and
// `the_hybrid_gpt_parses_back_and_spans_exactly_the_esp` proves these bytes
// against an independent reader rather than trusting the twin.

/// A GPT logical block: the medium read as a 512-byte-sector disk.
pub const GPT_LBA: u64 = 512;
/// Partition entries in the array (the UEFI-standard 128).
const GPT_N_ENTRIES: u32 = 128;
/// One partition entry (the UEFI-standard 128 bytes).
const GPT_ENTRY_SIZE: u32 = 128;
/// LBAs the 128 × 128 B array spans.
const GPT_ARRAY_LBAS: u64 = (GPT_N_ENTRIES as u64 * GPT_ENTRY_SIZE as u64) / GPT_LBA;
/// First usable LBA: past the MBR (0), the primary header (1) and the array (2..=33).
const GPT_FIRST_USABLE_LBA: u64 = 2 + GPT_ARRAY_LBAS;
/// The tail that carries the backup array (32 LBAs) + backup header (1 LBA),
/// rounded up to whole ISO sectors: 33 × 512 = 16 896 B → 9 × 2048 = 18 432 B.
const GPT_TAIL_ISO_SECTORS: u32 = ((GPT_ARRAY_LBAS + 1) * GPT_LBA).div_ceil(SECTOR as u64) as u32;
/// The EFI System Partition type GUID (`C12A7328-F81F-11D2-BA4B-00A0C93EC93B`),
/// in on-disk mixed-endian byte order.
pub const GPT_ESP_TYPE_GUID: [u8; 16] = [
    0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B,
];

/// Lay the protective MBR + primary + backup GPT over an already-serialized
/// image (which must already carry the [`GPT_TAIL_ISO_SECTORS`] tail), with one
/// EFI System partition spanning exactly the EFI boot image's bytes.
fn lay_hybrid_gpt(
    img: &mut [u8],
    volume_id: &str,
    efi: EfiBootPlacement,
    bios_refusal: bool,
) -> Result<GptEspPartition> {
    let total = img.len() as u64;
    if total % GPT_LBA != 0 {
        bail!("iso writer: image of {total} bytes is not a whole number of {GPT_LBA}-byte LBAs");
    }
    let disk_lbas = total / GPT_LBA;
    let disk_last_lba = disk_lbas - 1;
    let backup_array_lba = disk_last_lba - GPT_ARRAY_LBAS;
    let last_usable_lba = backup_array_lba - 1;

    // The ESP: ISO sector × 4 is its first LBA; its length is the FILE's length
    // rounded up to whole LBAs (a FAT image is always a whole number of 512-byte
    // sectors, so this is exact for every ESP the composer produces).
    let first_lba = efi.lba as u64 * (SECTOR as u64 / GPT_LBA);
    let last_lba = first_lba + efi.len.div_ceil(GPT_LBA).max(1) - 1;
    if first_lba < GPT_FIRST_USABLE_LBA || last_lba > last_usable_lba {
        bail!(
            "iso writer: the ESP at LBA {first_lba}..={last_lba} does not sit inside the GPT's usable \
             range {GPT_FIRST_USABLE_LBA}..={last_usable_lba} — the layout is wrong, not the payload"
        );
    }

    // Deterministic GUIDs from the content the medium is made of — the same tree
    // masters byte-identically, hybrid or not.
    let (disk_guid, partition_guid) = hybrid_guids(volume_id, first_lba, last_lba);

    // Partition entry array (primary + backup share the bytes).
    let mut entries = vec![0u8; (GPT_N_ENTRIES * GPT_ENTRY_SIZE) as usize];
    {
        let slot = &mut entries[0..GPT_ENTRY_SIZE as usize];
        slot[0..16].copy_from_slice(&GPT_ESP_TYPE_GUID);
        slot[16..32].copy_from_slice(&partition_guid);
        slot[32..40].copy_from_slice(&first_lba.to_le_bytes());
        slot[40..48].copy_from_slice(&last_lba.to_le_bytes());
        // attributes (48..56) = 0; name (56..128) UTF-16LE.
        let mut off = 56;
        for u in "EFI System".encode_utf16() {
            slot[off..off + 2].copy_from_slice(&u.to_le_bytes());
            off += 2;
        }
    }
    let array_crc = crc32(&entries);

    // Protective MBR at LBA 0.
    let mbr = &mut img[0..GPT_LBA as usize];
    // Boot code (bytes 0..440): the same UEFI-only refusal the El-Torito default
    // entry carries, so a hybrid medium dd'd to a stick and booted by a BIOS says
    // the sentence instead of "no bootable device" or silence. The partition
    // table below and the 55 AA signature are what make it a protective MBR.
    if bios_refusal {
        mbr[..440].copy_from_slice(&bios_refusal_sector()[..440]);
    }
    let p = 446;
    mbr[p] = 0x00; // not bootable
    mbr[p + 1] = 0x00; // CHS first: head 0, sector 2, cylinder 0
    mbr[p + 2] = 0x02;
    mbr[p + 3] = 0x00;
    mbr[p + 4] = 0xEE; // GPT protective
    mbr[p + 5] = 0xFF; // CHS last: max
    mbr[p + 6] = 0xFF;
    mbr[p + 7] = 0xFF;
    mbr[p + 8..p + 12].copy_from_slice(&1u32.to_le_bytes());
    mbr[p + 12..p + 16].copy_from_slice(&((disk_lbas - 1).min(u32::MAX as u64) as u32).to_le_bytes());
    mbr[510] = 0x55;
    mbr[511] = 0xAA;

    // Primary header (LBA 1) + array (LBA 2..).
    let primary = gpt_header(1, disk_last_lba, last_usable_lba, &disk_guid, 2, array_crc);
    write_lba(img, 1, &primary);
    write_lba(img, 2, &entries);
    // Backup array + header in the tail.
    let backup = gpt_header(disk_last_lba, 1, last_usable_lba, &disk_guid, backup_array_lba, array_crc);
    write_lba(img, backup_array_lba, &entries);
    write_lba(img, disk_last_lba, &backup);

    Ok(GptEspPartition { first_lba, last_lba, disk_last_lba, disk_guid, partition_guid })
}

/// A 512-byte GPT header with both CRC32s computed; `my_lba`/`alt_lba` swap
/// between the primary and the backup.
fn gpt_header(
    my_lba: u64,
    alt_lba: u64,
    last_usable: u64,
    disk_guid: &[u8; 16],
    entries_lba: u64,
    array_crc: u32,
) -> Vec<u8> {
    let mut h = vec![0u8; GPT_LBA as usize];
    h[0..8].copy_from_slice(b"EFI PART");
    h[8..12].copy_from_slice(&0x0001_0000u32.to_le_bytes()); // revision 1.0
    h[12..16].copy_from_slice(&92u32.to_le_bytes()); // header size
    h[24..32].copy_from_slice(&my_lba.to_le_bytes());
    h[32..40].copy_from_slice(&alt_lba.to_le_bytes());
    h[40..48].copy_from_slice(&GPT_FIRST_USABLE_LBA.to_le_bytes());
    h[48..56].copy_from_slice(&last_usable.to_le_bytes());
    h[56..72].copy_from_slice(disk_guid);
    h[72..80].copy_from_slice(&entries_lba.to_le_bytes());
    h[80..84].copy_from_slice(&GPT_N_ENTRIES.to_le_bytes());
    h[84..88].copy_from_slice(&GPT_ENTRY_SIZE.to_le_bytes());
    h[88..92].copy_from_slice(&array_crc.to_le_bytes());
    let hdr_crc = crc32(&h[0..92]);
    h[16..20].copy_from_slice(&hdr_crc.to_le_bytes());
    h
}

/// Disk + partition GUIDs derived from the medium's identity and the ESP's
/// span (SHA-256, then v4 version + RFC-4122 variant bits, in on-disk
/// mixed-endian order) — unique per distinct medium, identical for a byte-
/// identical re-master.
fn hybrid_guids(volume_id: &str, first_lba: u64, last_lba: u64) -> ([u8; 16], [u8; 16]) {
    use sha2::Digest;
    let mut h = sha2::Sha256::new();
    h.update(b"nornir-build-thing iso hybrid gpt\0");
    h.update(volume_id.as_bytes());
    h.update(first_lba.to_le_bytes());
    h.update(last_lba.to_le_bytes());
    let d = h.finalize();
    let stamp = |bytes: &[u8]| -> [u8; 16] {
        let mut g = [0u8; 16];
        g.copy_from_slice(bytes);
        g[7] = (g[7] & 0x0F) | 0x40; // version 4
        g[8] = (g[8] & 0x3F) | 0x80; // RFC-4122 variant
        g
    };
    (stamp(&d[0..16]), stamp(&d[16..32]))
}

/// CRC-32/ISO-HDLC (the zlib CRC the GPT spec mandates).
fn crc32(bytes: &[u8]) -> u32 {
    let mut h = crc32fast::Hasher::new();
    h.update(bytes);
    h.finalize()
}

/// Write `data` at 512-byte LBA `lba` of the image.
fn write_lba(img: &mut [u8], lba: u64, data: &[u8]) {
    let off = (lba * GPT_LBA) as usize;
    img[off..off + data.len()].copy_from_slice(data);
}

/// The boot image(s) an El-Torito catalog describes: a BIOS entry `(lba,
/// 512-byte sector count)`, an EFI entry `(lba, byte length)`, or both.
///
/// # Why this is a type and not two `Option` arguments
///
/// [`boot_catalog`] used to take the two options directly and carry a third arm:
///
/// ```text
/// (None, None) => (0, 4),   // no boot image at all
/// ```
///
/// followed unconditionally by `c[32] = 0x88` (bootable) and `c[33] = 0x00`
/// (no emulation). An ISO carrying nothing bootable therefore got a fully
/// well-formed, checksummed El-Torito catalog whose default entry told firmware
/// to load four 512-byte sectors from **LBA 0** — the ISO's own zeroed system
/// area. Verified on a real probe image. Nothing said a word at build time and
/// the structures were the ones firmware trusts most.
///
/// `(None, None)` is now unrepresentable: [`BootImages::new`] returns `None`,
/// and [`IsoWriter::build_image`] then emits a plain ISO-9660 data volume with
/// no Boot Record volume descriptor and no catalog — which is also exactly what
/// `mkisofs` produces from an argv carrying neither `-b` nor `-e`, i.e. what
/// [`IsoBuild::master_args`](crate::iso::IsoBuild::master_args) renders for such
/// a descriptor.
struct BootImages {
    /// BIOS (platform `0x00`) boot image: `(load RBA, 512-byte sector count)`.
    bios: Option<(u32, u16)>,
    /// EFI (platform `0xEF`) boot image: `(load RBA, 512-byte sector count)` —
    /// the count already converted by [`el_torito_sectors`] (exact, or refused)
    /// or [`el_torito_sectors_clamped`] (hybrid only, recorded), so the catalog
    /// serializer cannot do a conversion of its own.
    efi: Option<(u32, u16)>,
}

impl BootImages {
    /// `Some` only when at least one boot image is present — the constructor that
    /// makes "a bootable catalog for nothing" impossible to build.
    fn new(bios: Option<(u32, u16)>, efi: Option<(u32, u16)>) -> Option<Self> {
        if bios.is_none() && efi.is_none() {
            return None;
        }
        Some(BootImages { bios, efi })
    }
}

/// The El-Torito boot catalog: validation entry, default/initial entry, and —
/// when an EFI image is present — an EFI section header + entry.
///
/// Reached only when there is something to boot; see [`BootImages`] for the arm
/// that used to fabricate a bootable entry over LBA 0.
///
/// Takes sector COUNTS, never byte lengths: the only conversions from a byte
/// length to a sector count live in [`el_torito_sectors`] (refuses past the
/// ceiling — see it for what the silence used to cost) and
/// [`el_torito_sectors_clamped`] (the GPT-hybrid arm, which records the clamp),
/// both called by [`IsoWriter::build_image`] before the catalog is serialized.
fn boot_catalog(images: &BootImages) -> Result<Vec<u8>> {
    let (bios, efi) = (images.bios, images.efi);
    let mut c = vec![0u8; SECTOR];

    // ── Validation entry (bytes 0..32) ──
    c[0] = 0x01; // header id
    c[1] = PLATFORM_X86; // platform id for the initial/default entry
                         // id string (bytes 4..28) left zero.
    c[30] = 0x55;
    c[31] = 0xAA;
    // 16-bit checksum so the whole 32-byte entry sums to 0 (mod 2^16).
    let sum = validation_checksum(&c[0..32]);
    c[28..30].copy_from_slice(&sum.to_le_bytes());

    // ── Default / initial entry (bytes 32..64) ──
    // A UEFI-only image still needs a valid default entry; point it at whichever
    // boot image exists (OVMF ignores the BIOS default and uses the EFI section).
    // El-Torito sector counts are in 512-byte VIRTUAL sectors (not 2048-byte ISO
    // sectors), so the firmware's block device spans the whole boot image.
    let (def_lba, def_sectors) = match (bios, efi) {
        (Some(b), _) => b,
        (None, Some((lba, sectors))) => (lba, sectors),
        // Unreachable by construction: `BootImages` cannot be built empty, and
        // `efi_sectors` is `Some` exactly when `efi` is. If this ever fires the
        // constructor's invariant broke, and a catalog full of zeros pointed at
        // LBA 0 is precisely the failure this whole type exists to prevent — so
        // it stops the build instead of shipping.
        (None, None) => bail!(
            "iso writer: an El-Torito boot catalog was requested with no boot image. A catalog \
             with no image marks LBA 0 bootable and firmware then loads the ISO's own system \
             area; refusing to write one."
        ),
    };
    c[32] = 0x88; // bootable
    c[33] = 0x00; // no emulation
    c[34..36].copy_from_slice(&0u16.to_le_bytes()); // load segment (default)
    c[36] = 0x00; // system type
    c[38..40].copy_from_slice(&def_sectors.to_le_bytes()); // sector count
    c[40..44].copy_from_slice(&def_lba.to_le_bytes()); // load RBA (2048-sector)

    // ── EFI section (bytes 64..96 + 96..128) ──
    if let Some((lba, efi_sectors)) = efi {
        // Section header entry.
        c[64] = 0x91; // final section header
        c[65] = PLATFORM_EFI; // 0xEF
        c[66..68].copy_from_slice(&1u16.to_le_bytes()); // one section entry
                                                        // Section entry.
        c[96] = 0x88; // bootable
        c[97] = 0x00; // no emulation
        c[98..100].copy_from_slice(&0u16.to_le_bytes()); // load segment
        c[100] = 0x00; // system type
        c[102..104].copy_from_slice(&efi_sectors.to_le_bytes()); // sector count (512B units)
        c[104..108].copy_from_slice(&lba.to_le_bytes()); // load RBA
    }
    Ok(c)
}

/// El-Torito validation-entry checksum: the 16-bit word sum of the 32-byte entry
/// (with the checksum field zero) must make the total zero mod 2^16.
fn validation_checksum(entry: &[u8]) -> u16 {
    let mut sum: u16 = 0;
    for pair in entry.chunks_exact(2) {
        sum = sum.wrapping_add(u16::from_le_bytes([pair[0], pair[1]]));
    }
    0u16.wrapping_sub(sum)
}

/// Fill a fixed-width field with space-padded ISO d-characters (uppercase).
fn fill_dchars(field: &mut [u8], text: &str) {
    for b in field.iter_mut() {
        *b = b' ';
    }
    let up = text.to_ascii_uppercase();
    for (i, ch) in up.bytes().enumerate() {
        if i >= field.len() {
            break;
        }
        field[i] = if is_dchar(ch) { ch } else { b'_' };
    }
}

/// ISO-9660 d-characters: `A-Z`, `0-9`, `_`.
fn is_dchar(b: u8) -> bool {
    b.is_ascii_uppercase() || b.is_ascii_digit() || b == b'_'
}

/// Normalize a filename to an ISO-9660 file identifier: uppercase d-chars, a
/// single `.` separator preserved, `;1` version suffix appended.
fn iso_file_name(name: &str) -> String {
    let (stem, ext) = match name.rsplit_once('.') {
        Some((s, e)) if !s.is_empty() => (s, Some(e)),
        _ => (name, None),
    };
    let clean = |s: &str| -> String {
        s.to_ascii_uppercase()
            .bytes()
            .map(|b| if is_dchar(b) { b as char } else { '_' })
            .collect()
    };
    match ext {
        Some(e) => format!("{}.{};1", clean(stem), clean(e)),
        None => format!("{};1", clean(stem)),
    }
}

/// Normalize a directory name to an ISO-9660 directory identifier (no version).
fn iso_dir_name(name: &str) -> String {
    name.to_ascii_uppercase()
        .bytes()
        .map(|b| if is_dchar(b) || b == b'.' { b as char } else { '_' })
        .collect()
}

/// Drop the trailing `;1` version from an ISO file identifier (for placement keys).
fn strip_version(name: &str) -> String {
    name.rsplit_once(';').map(|(s, _)| s.to_string()).unwrap_or_else(|| name.to_string())
}

/// Normalize a caller-supplied ISO path (as used to mark a boot image) to the
/// uppercased, version-stripped, `/`-joined key form the placement map uses.
fn normalize_iso_path(p: &str) -> String {
    p.split('/')
        .filter(|c| !c.is_empty() && *c != ".")
        .enumerate()
        .map(|(_, c)| strip_version(&iso_file_name(c)))
        .collect::<Vec<_>>()
        .join("/")
}

/// Resolve a boot-image ISO path to its placement, erroring clearly if absent.
fn lookup_placement<'a>(
    places: &'a BTreeMap<String, FilePlacement>,
    iso_path: &str,
) -> Result<&'a FilePlacement> {
    places.get(iso_path).with_context(|| {
        format!(
            "iso writer: boot image '{iso_path}' was not staged on the ISO (known: {:?})",
            places.keys().collect::<Vec<_>>()
        )
    })
}

/// Write `data` starting at sector `lba` (data may span multiple sectors).
fn write_at(img: &mut [u8], lba: u32, data: &[u8]) {
    let off = lba as usize * SECTOR;
    img[off..off + data.len()].copy_from_slice(data);
}

/// Write a single ≤-sector blob at sector `lba`.
fn write_sector(img: &mut [u8], lba: u32, data: &[u8]) {
    debug_assert!(data.len() <= SECTOR);
    write_at(img, lba, data);
}

/// Pad a buffer up to a whole [`SECTOR`].
fn pad_to_sector(buf: &mut Vec<u8>) {
    let pad = round_sectors(buf.len() as u64) as usize * SECTOR - buf.len();
    buf.extend(std::iter::repeat_n(0u8, pad));
}

/// Read a little-endian u32 from `img` at absolute byte offset `off`.
#[cfg(test)]
fn read_u32_le(img: &[u8], off: usize) -> u32 {
    u32::from_le_bytes([img[off], img[off + 1], img[off + 2], img[off + 3]])
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;

    /// The hand-assembled refusal stub IS what its listing says: every branch
    /// target lands on the instruction the listing names, the message sits
    /// exactly where `mov si` points, and the sector carries the `55 AA`
    /// signature the hybrid MBR needs. Offsets asserted here so an edit to the
    /// byte table cannot silently turn a `jz H` into a jump into the message.
    #[test]
    fn the_bios_refusal_sector_is_a_valid_boot_sector() {
        let s = bios_refusal_sector();
        assert_eq!(s.len(), 512);
        assert_eq!(s[0], 0xFA, "cli first");
        // mov si, imm16 → the message address, 0x7C00-relative.
        assert_eq!(s[0x0B], 0xBE);
        let msg_at = u16::from_le_bytes([s[0x0C], s[0x0D]]) as usize - 0x7C00;
        assert_eq!(msg_at, 0x24, "message right after the code");
        assert!(s[msg_at..].starts_with(BIOS_REFUSAL_TEXT.as_bytes()));
        let end = msg_at + BIOS_REFUSAL_TEXT.len();
        assert_eq!(&s[end..end + 3], b"\r\n\0", "CRLF + NUL terminator");
        // jz H: from 0x13, rel 0x0D → 0x20, which must be `cli; hlt`.
        assert_eq!(s[0x11], 0x74);
        let h = 0x13 + s[0x12] as usize;
        assert_eq!((s[h], s[h + 1]), (0xFA, 0xF4), "H: cli; hlt");
        // jmp L: from 0x20, rel 0xEE (−18) → 0x0E, which must be `lodsb`.
        assert_eq!(s[0x1E], 0xEB);
        let l = (0x20i32 + (s[0x1F] as i8) as i32) as usize;
        assert_eq!(s[l], 0xAC, "L: lodsb");
        // jmp H: from 0x24, rel 0xFC (−4) → 0x20.
        assert_eq!(s[0x22], 0xEB);
        assert_eq!((0x24i32 + (s[0x23] as i8) as i32) as usize, h);
        // the serial mirror writes to COM1 before the INT 10h teletype call.
        assert_eq!(&s[0x13..0x17], &[0xBA, 0xF8, 0x03, 0xEE], "mov dx,0x3F8; out dx,al");
        assert_eq!(&s[0x1C..0x1E], &[0xCD, 0x10], "int 0x10");
        assert_eq!((s[510], s[511]), (0x55, 0xAA));
    }

    /// A UEFI-only medium's El-Torito DEFAULT (80x86) entry no longer points at
    /// the FAT ESP: it loads the one-sector refusal stub, while the EFI section
    /// still points at the ESP. RED before 2026-08-28: the default entry's RBA
    /// equalled the EFI section's, and SeaBIOS executed FAT bytes.
    #[test]
    fn a_uefi_only_medium_carries_the_bios_refusal_as_its_default_entry() {
        let dir = tempfile::tempdir().unwrap();
        let efi = dir.path().join("efiboot.img");
        std::fs::write(&efi, vec![0xEFu8; 8192]).unwrap();
        let mut w = IsoWriter::new("UEFI_ONLY");
        w.add_file("efiboot.img", &efi).unwrap();
        w.set_efi_boot("efiboot.img");
        let img = w.build_image().unwrap();

        let cat = 21 * SECTOR;
        let def_lba = read_u32_le(&img, cat + 40) as usize;
        let def_sectors = u16::from_le_bytes([img[cat + 38], img[cat + 39]]);
        let efi_lba = read_u32_le(&img, cat + 104) as usize;
        assert_ne!(def_lba, efi_lba, "the default entry must NOT be the ESP");
        assert_eq!(def_sectors, 1, "one 512-byte sector: the stub");
        let stub = &img[def_lba * SECTOR..def_lba * SECTOR + 512];
        assert_eq!(stub, &bios_refusal_sector()[..], "the default entry loads the refusal stub");
        assert_eq!(&img[efi_lba * SECTOR..efi_lba * SECTOR + 4], &[0xEF; 4], "EFI section still the ESP");
        // It is a real file on the volume too, so `isoinfo -l` shows what the
        // BIOS would run, and the receipt names it.
        let layout = w.layout().unwrap();
        assert!(layout.bios_refusal);
        assert!(layout.describe().contains(BIOS_REFUSAL_FILE), "{}", layout.describe());
    }

    /// A medium that DECLARES a BIOS boot image keeps it: the refusal stub is
    /// only ever laid where nothing else would answer a BIOS.
    #[test]
    fn a_declared_bios_image_is_not_replaced_by_the_refusal() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("bzImage"), vec![0xB2u8; 4096]).unwrap();
        std::fs::write(dir.path().join("efiboot.img"), vec![0xEFu8; 4096]).unwrap();
        let mut w = IsoWriter::new("BOTH");
        w.add_file("bzImage", dir.path().join("bzImage")).unwrap();
        w.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
        w.set_bios_boot("bzImage", 4);
        w.set_efi_boot("efiboot.img");
        let img = w.build_image().unwrap();
        let cat = 21 * SECTOR;
        let def_lba = read_u32_le(&img, cat + 40) as usize;
        assert_eq!(&img[def_lba * SECTOR..def_lba * SECTOR + 4], &[0xB2; 4], "the declared kernel");
        assert!(!w.layout().unwrap().bios_refusal);
        assert!(!w.layout().unwrap().describe().contains(BIOS_REFUSAL_FILE));
    }

    /// **A declared BIOS stage is the default entry, sized to the whole image,
    /// and its patch table comes back off the mastered medium naming the
    /// kernel's and the initramfs' real LBAs** — and no refusal stub is laid
    /// beside it (a medium with a real BIOS path has nothing to refuse).
    #[test]
    fn a_bios_stage_is_patched_with_the_payload_placements_and_is_the_default_entry() {
        let dir = tempfile::tempdir().unwrap();
        // A "stage": 0x400 bytes of code-shaped filler, the blank table, a tail.
        let mut stage = vec![0x90u8; 0x400];
        stage.extend_from_slice(&crate::bios_patch::blank_table());
        stage.extend_from_slice(b"korp-boot-bios strings\0");
        std::fs::write(dir.path().join("stage.bin"), &stage).unwrap();
        std::fs::write(dir.path().join("vmlinuz"), vec![0x4Bu8; 5000]).unwrap();
        std::fs::write(dir.path().join("initrd"), vec![0x49u8; 3000]).unwrap();
        std::fs::write(dir.path().join("efiboot.img"), vec![0xEFu8; 4096]).unwrap();
        let mut w = IsoWriter::new("BIOSUEFI");
        w.add_file("bios/stage.bin", dir.path().join("stage.bin")).unwrap();
        w.add_file("bios/initrd", dir.path().join("initrd")).unwrap();
        w.add_file("vmlinuz", dir.path().join("vmlinuz")).unwrap();
        w.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
        w.set_efi_boot("efiboot.img");
        w.set_bios_stage(BiosStage {
            image: "bios/stage.bin".into(),
            kernel: "vmlinuz".into(),
            initrd: Some("bios/initrd".into()),
            cmdline: "console=ttyS0 korp.entry=gunnar".into(),
        });
        let img = w.build_image().unwrap();
        let layout = *w.layout().unwrap();
        let b = layout.bios.expect("a BIOS stage placement is recorded");
        assert!(!layout.bios_refusal, "a real stage means no refusal stub");
        assert!(!layout.describe().contains(BIOS_REFUSAL_FILE));
        assert!(layout.describe().contains("legacy BIOS: real stage"));
        // The catalog's default entry: the stage's LBA and the WHOLE image in
        // 512-byte sectors (stage is 0x400 + 0x420 + 23 = 2107 B → 5 sectors).
        let cat = 21 * SECTOR;
        assert_eq!(read_u32_le(&img, cat + 40), b.lba);
        assert_eq!(u16::from_le_bytes([img[cat + 38], img[cat + 39]]), b.catalog_sectors);
        assert_eq!(b.catalog_sectors, (stage.len() as u16).div_ceil(512));
        // The EFI section still points at the ESP — the UEFI path is untouched.
        let efi = layout.efi.unwrap();
        assert_eq!(read_u32_le(&img, cat + 104), efi.lba);
        assert_eq!(&img[efi.lba as usize * SECTOR..efi.lba as usize * SECTOR + 4], &[0xEF; 4]);
        // The patch table on the MEDIUM names where the payloads really are.
        let on_medium = &img[b.lba as usize * SECTOR..b.lba as usize * SECTOR + stage.len()];
        let off = crate::bios_patch::find_table(on_medium).unwrap();
        let t = crate::bios_patch::BiosPatchTable::read_from(on_medium, off).unwrap();
        assert_eq!(t.cmdline, "console=ttyS0 korp.entry=gunnar");
        assert_eq!((t.kernel_lba, t.kernel_len), (b.kernel_lba, 5000));
        assert_eq!((t.initrd_lba, t.initrd_len), (b.initrd_lba, 3000));
        assert_eq!(&img[t.kernel_lba as usize * SECTOR..t.kernel_lba as usize * SECTOR + 4], &[0x4B; 4]);
        assert_eq!(&img[t.initrd_lba as usize * SECTOR..t.initrd_lba as usize * SECTOR + 4], &[0x49; 4]);
        // The refusal file is not on the volume at all.
        assert!(!w.nodes[0].files.contains_key(&iso_file_name(BIOS_REFUSAL_FILE)));
        // Deterministic: a second build is byte-identical (the patch is idempotent).
        assert_eq!(w.build_image().unwrap(), img);
    }

    /// **The hybrid boot sector, and the two things it must be true about.**
    ///
    /// The RED this replaces was MEASURED on gunnar's real 38 680 576 B medium
    /// (2026-08-30, lane AQ): `LBA 0[510..512] = 00 00`, the whole first sector
    /// zero, so SeaBIOS booting the identical bytes off a hard-disk device
    /// found no boot record and parked — `--target ovh-public-cloud` left ZERO
    /// bytes of serial for 420 s. This asserts APPLIED OUTPUT on both halves:
    /// the boot sector a firmware reads, and the disk-flavoured table the code
    /// it runs will read.
    ///
    /// The first assertion is the RED itself and it stays: the SAME tree
    /// without the word still has an all-zero LBA 0. A hybrid that could not be
    /// told from a plain medium would be no hybrid.
    #[test]
    fn a_hybrid_medium_has_a_boot_sector_and_a_disk_flavoured_stage_and_costs_no_bytes() {
        let dir = tempfile::tempdir().unwrap();
        let mut stage = vec![0x90u8; 0x400];
        stage.extend_from_slice(&crate::bios_patch::blank_table());
        stage.extend_from_slice(b"korp-boot-bios strings\0");
        std::fs::write(dir.path().join("stage.bin"), &stage).unwrap();
        std::fs::write(dir.path().join("vmlinuz"), vec![0x4Bu8; 5000]).unwrap();
        std::fs::write(dir.path().join("initrd"), vec![0x49u8; 3000]).unwrap();
        std::fs::write(dir.path().join("efiboot.img"), vec![0xEFu8; 4096]).unwrap();
        let tree = |w: &mut IsoWriter| {
            w.add_file("bios/stage.bin", dir.path().join("stage.bin")).unwrap();
            w.add_file("bios/initrd", dir.path().join("initrd")).unwrap();
            w.add_file("vmlinuz", dir.path().join("vmlinuz")).unwrap();
            w.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
            w.set_efi_boot("efiboot.img");
            w.set_bios_stage(BiosStage {
                image: "bios/stage.bin".into(),
                kernel: "vmlinuz".into(),
                initrd: Some("bios/initrd".into()),
                cmdline: "console=ttyS0 korp.entry=gunnar".into(),
            });
        };

        // ── RED: the same tree with no hybrid word ──────────────────────────
        let mut plain = IsoWriter::new("HYBRIDT");
        tree(&mut plain);
        let plain_img = plain.build_image().unwrap();
        assert_eq!(&plain_img[510..512], &[0x00, 0x00], "a plain medium has NO boot sector");
        assert!(plain_img[..512].iter().all(|b| *b == 0), "its whole LBA 0 is zero");
        assert!(plain.layout().unwrap().hybrid_mbr.is_none());

        // ── GREEN: the word, and nothing else, changes ──────────────────────
        // The code is a stand-in for `korp_boot_bios::emit_mbr`'s (this crate
        // cannot depend on the assembler); what is asserted is that the writer
        // lays exactly the bytes it was handed, at LBA 0, with a partition
        // table and the signature after them.
        let code: Vec<u8> = (0..200u32).map(|i| (i % 251) as u8).collect();
        let mut w = IsoWriter::new("HYBRIDT");
        tree(&mut w);
        w.set_hybrid_mbr(code.clone());
        let img = w.build_image().unwrap();
        let layout = *w.layout().unwrap();
        let h = layout.hybrid_mbr.expect("a hybrid placement is recorded");

        // 1. THE BOOT SECTOR a firmware reads off a hard-disk device.
        assert_eq!(&img[..code.len()], &code[..], "the code the caller assembled, at LBA 0");
        assert_eq!((img[510], img[511]), (0x55, 0xAA), "the signature that was 00 00");
        assert_eq!(img[446], 0x80, "one bootable partition");
        assert_eq!(img[446 + 4], 0x83);
        assert_eq!(read_u32_le(&img, 446 + 8), 0, "spanning the medium from LBA 0");
        assert_eq!(read_u32_le(&img, 446 + 12), (img.len() / 512) as u32);

        // 2. THE STAGE the code chainloads: the same image, a 512-byte table.
        assert_eq!(h.stage_lba, HYBRID_STAGE_LBA);
        assert_eq!(h.stage_sectors as usize, stage.len().div_ceil(512), "rounded UP: a truncated read leaves the stage's tail unloaded");
        let at = HYBRID_STAGE_LBA as usize * 512;
        let twin = &img[at..at + stage.len()];
        assert_eq!(&twin[..0x400], &stage[..0x400], "the SAME stage image, not a second loader");
        let off = crate::bios_patch::find_table(twin).unwrap();
        let t = crate::bios_patch::BiosPatchTable::read_from(twin, off).unwrap();
        assert_eq!(t.sector_shift, crate::bios_patch::SECTOR_SHIFT_DISK, "512-byte sectors");
        assert_eq!(t.cmdline, "console=ttyS0 korp.entry=gunnar");
        let cd = layout.bios.unwrap();
        assert_eq!(t.kernel_lba, cd.kernel_lba * 4, "the SAME bytes, counted in 512s");
        assert_eq!(t.initrd_lba, cd.initrd_lba * 4);
        // and those LBAs really land on the payloads (the whole point).
        assert_eq!(&img[t.kernel_lba as usize * 512..t.kernel_lba as usize * 512 + 4], &[0x4B; 4]);
        assert_eq!(&img[t.initrd_lba as usize * 512..t.initrd_lba as usize * 512 + 4], &[0x49; 4]);
        // The El-Torito copy is UNTOUCHED: the cdrom path still reads 2048s.
        let cd_at = cd.lba as usize * SECTOR;
        let cdt = crate::bios_patch::BiosPatchTable::read_from(
            &img[cd_at..cd_at + stage.len()],
            crate::bios_patch::find_table(&img[cd_at..cd_at + stage.len()]).unwrap(),
        )
        .unwrap();
        assert_eq!(cdt.sector_shift, crate::bios_patch::SECTOR_SHIFT_CD);

        // 3. IT COSTS NO BYTES. Both copies and the boot sector live in the
        //    ISO's reserved system area, so the medium is the same size and
        //    every byte from the PVD on is identical to the plain one.
        assert_eq!(img.len(), plain_img.len(), "a hybrid medium is not one byte bigger");
        assert_eq!(&img[SYSTEM_AREA_BYTES..], &plain_img[SYSTEM_AREA_BYTES..], "only the system area moved");
        assert!(layout.describe().contains("HYBRID"), "{}", layout.describe());
        assert_eq!(w.build_image().unwrap(), img, "deterministic");
    }

    /// A `55 AA` that chainloads nothing is WORSE than no boot sector —
    /// firmware stops looking the moment it finds one — so a hybrid medium
    /// with no real BIOS stage is refused by name rather than mastered.
    #[test]
    fn a_hybrid_medium_without_a_bios_stage_is_refused_by_name() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("efiboot.img"), vec![0xEFu8; 4096]).unwrap();
        let mut w = IsoWriter::new("NOSTAGE");
        w.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
        w.set_efi_boot("efiboot.img");
        w.set_hybrid_mbr(vec![0x90; 8]);
        let e = w.build_image().unwrap_err().to_string();
        assert!(e.contains("no real legacy-BIOS stage"), "{e}");
        assert!(e.contains("55 AA"), "{e}");
    }

    /// A stage without a patch table, or a stage whose kernel is not on the
    /// volume, stops the build by name — never a medium that boots into LBA 0.
    #[test]
    fn a_bios_stage_without_a_table_or_kernel_is_refused() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("stage.bin"), vec![0x90u8; 1024]).unwrap();
        std::fs::write(dir.path().join("efiboot.img"), vec![0xEFu8; 4096]).unwrap();
        let mut w = IsoWriter::new("BAD");
        w.add_file("bios/stage.bin", dir.path().join("stage.bin")).unwrap();
        w.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
        w.set_efi_boot("efiboot.img");
        w.set_bios_stage(BiosStage {
            image: "bios/stage.bin".into(),
            kernel: "vmlinuz".into(),
            initrd: None,
            cmdline: "x".into(),
        });
        let err = w.build_image().unwrap_err().to_string();
        assert!(err.contains("vmlinuz") || err.contains("VMLINUZ"), "{err}");
        std::fs::write(dir.path().join("vmlinuz"), vec![0x4Bu8; 5000]).unwrap();
        w.add_file("vmlinuz", dir.path().join("vmlinuz")).unwrap();
        let err = format!("{:#}", w.build_image().unwrap_err());
        assert!(err.contains("no patch table"), "{err}");
    }

    /// Build a tiny ISO in memory and assert the ISO-9660 on-disk structures the
    /// spec mandates: the `CD001` PVD magic at sector 16, the El-Torito boot
    /// record + validation entry, and that a staged payload file is placed and
    /// readable back at its recorded extent.
    #[test]
    fn masters_iso9660_structures_and_reads_payload_back() {
        let dir = tempfile::tempdir().unwrap();
        // Two payload files, one nested — proving the directory tree + placement.
        let kernel = dir.path().join("bzImage");
        std::fs::write(&kernel, b"KERNEL-PAYLOAD-BYTES").unwrap();
        let efi = dir.path().join("efiboot.img");
        let efi_bytes = vec![0xEFu8; 4096];
        std::fs::write(&efi, &efi_bytes).unwrap();
        let nested = dir.path().join("readme.txt");
        std::fs::write(&nested, b"hello from the iso").unwrap();

        let mut w = IsoWriter::new("KORP_DEMO");
        w.add_file("bzImage", &kernel).unwrap();
        w.add_file("efiboot.img", &efi).unwrap();
        w.add_file("docs/readme.txt", &nested).unwrap();
        w.set_bios_boot("bzImage", 4);
        w.set_efi_boot("efiboot.img");
        let img = w.build_image().unwrap();

        // ── PVD: "CD001" magic at sector 16, type 1 ──
        assert_eq!(&img[16 * SECTOR + 1..16 * SECTOR + 6], b"CD001", "PVD magic");
        assert_eq!(img[16 * SECTOR], 1, "PVD type == 1");
        // volume id (uppercased, space-padded).
        let volid = &img[16 * SECTOR + 40..16 * SECTOR + 72];
        assert!(volid.starts_with(b"KORP_DEMO"), "volume id: {:?}", volid);
        // logical block size == 2048 (both-endian LE half).
        assert_eq!(read_u32_le(&img, 16 * SECTOR + 128) & 0xFFFF, 2048, "logical block size");

        // ── El-Torito boot record VD at sector 17 ──
        assert_eq!(img[17 * SECTOR], 0, "boot record type == 0");
        assert_eq!(&img[17 * SECTOR + 1..17 * SECTOR + 6], b"CD001");
        assert!(
            img[17 * SECTOR + 7..17 * SECTOR + 30].starts_with(b"EL TORITO SPECIFICATION"),
            "El-Torito boot-system id"
        );
        let cat_lba = read_u32_le(&img, 17 * SECTOR + 71);
        assert_eq!(cat_lba, 21, "boot-catalog pointer");

        // ── Volume Descriptor Set Terminator at sector 18 ──
        assert_eq!(img[18 * SECTOR], 255, "terminator type == 255");
        assert_eq!(&img[18 * SECTOR + 1..18 * SECTOR + 6], b"CD001");

        // ── Boot catalog: validation entry + EFI section ──
        let cat = 21 * SECTOR;
        assert_eq!(img[cat], 0x01, "validation entry header id");
        assert_eq!(img[cat + 30], 0x55, "validation key 0x55");
        assert_eq!(img[cat + 31], 0xAA, "validation key 0xAA");
        // the 16-bit words of the validation entry sum to zero.
        let mut sum: u16 = 0;
        for pair in img[cat..cat + 32].chunks_exact(2) {
            sum = sum.wrapping_add(u16::from_le_bytes([pair[0], pair[1]]));
        }
        assert_eq!(sum, 0, "validation entry checksum");
        // default/initial entry bootable.
        assert_eq!(img[cat + 32], 0x88, "default entry bootable");
        // EFI section header (platform 0xEF).
        assert_eq!(img[cat + 64], 0x91, "final section header");
        assert_eq!(img[cat + 65], PLATFORM_EFI, "EFI platform id");
        assert_eq!(img[cat + 96], 0x88, "EFI section entry bootable");
        let efi_lba = read_u32_le(&img, cat + 104);

        // ── the EFI boot image is placed at the catalog's recorded extent ──
        let efi_off = efi_lba as usize * SECTOR;
        assert_eq!(&img[efi_off..efi_off + efi_bytes.len()], &efi_bytes[..], "efiboot.img readable back at its El-Torito extent");

        // ── the total image is sector-aligned ──
        assert_eq!(img.len() % SECTOR, 0, "image is a whole number of sectors");
        assert!(img.len() >= 22 * SECTOR, "past the fixed descriptors");
    }

    /// Mastering the same tree twice yields byte-identical images (deterministic,
    /// the property content-addressing needs).
    #[test]
    fn mastering_is_deterministic() {
        let dir = tempfile::tempdir().unwrap();
        let mut f = std::fs::File::create(dir.path().join("payload.bin")).unwrap();
        f.write_all(&[0xABu8; 3000]).unwrap();
        drop(f);

        let build = || {
            let mut w = IsoWriter::new("DET_TEST");
            w.add_file("payload.bin", dir.path().join("payload.bin")).unwrap();
            w.build_image().unwrap()
        };
        assert_eq!(build(), build(), "same input tree → byte-identical ISO");
    }

    /// **The silent El-Torito clamp.** A boot image one byte over the format's
    /// 16-bit sector count must stop the build, naming the numbers — not be cut
    /// down to fit.
    ///
    /// Asserted on the **catalog bytes**, which are the applied output. The old
    /// code's `.min(u16::MAX)` produced a perfectly well-formed catalog whose
    /// sector count said `65535` for an image that was larger, so every
    /// structural assertion in this file passed and the ISO mastered clean. The
    /// failure surfaced on real firmware as `no bootable device`, with nothing
    /// in the build output to search for.
    ///
    /// Both sides of the boundary, because a check that only ever sees the
    /// oversized case cannot tell a working limit from a rejection of
    /// everything.
    #[test]
    fn an_oversized_boot_image_is_an_error_naming_the_numbers_not_a_silent_clamp() {
        // ── exactly at the ceiling: described, and described EXACTLY ──
        let at_ceiling_sectors = el_torito_sectors(MAX_BOOT_IMAGE_BYTES, "EFI (efiboot.img)")
            .expect("an image of exactly 33 553 920 B is representable");
        let images = BootImages::new(None, Some((40, at_ceiling_sectors)))
            .expect("an EFI image is a boot image");
        let at_ceiling = boot_catalog(&images)
            .expect("an image of exactly 33 553 920 B is representable and must be accepted");
        assert_eq!(
            u16::from_le_bytes([at_ceiling[102], at_ceiling[103]]),
            u16::MAX,
            "an image at the ceiling must fill the sector count exactly; anything less means the \
             boundary is off by one and real images are being described short"
        );
        assert_eq!(
            el_torito_sectors(MAX_BOOT_IMAGE_BYTES, "efi").unwrap(),
            65535
        );
        assert_eq!(el_torito_sectors(0, "efi").unwrap(), 1, "zero reads as `no image` to firmware");
        assert_eq!(el_torito_sectors(1, "efi").unwrap(), 1, "rounds UP: a partial sector still ships");
        assert_eq!(el_torito_sectors(513, "efi").unwrap(), 2);

        // ── one byte over: refused, loudly ──
        let over = MAX_BOOT_IMAGE_BYTES + 1;
        let err = el_torito_sectors(over, "EFI (efiboot.img)")
            .expect_err("one byte past a 16-bit field cannot be described and must not be clamped");
        let msg = format!("{err:#}");
        for needle in [
            &over.to_string(),                    // the actual size
            &MAX_BOOT_IMAGE_BYTES.to_string(),    // the ceiling
            "1 bytes",                            // the overflow
            "EFI (efiboot.img)",                  // which image
        ] {
            assert!(
                msg.contains(needle),
                "the error must name {needle:?} — an operator who is told only `too large` has to \
                 go and measure the file, the ceiling and the difference themselves. Got: {msg}"
            );
        }

        // ── and the real gunnar-shaped margin, so the numbers in the doc are
        // the numbers the code enforces ──
        const GUNNAR_ESP_2026_08_03: u64 = 33_471_488;
        assert!(
            el_torito_sectors(GUNNAR_ESP_2026_08_03, "efi").is_ok(),
            "gunnar's shipping ESP must still master"
        );
        assert_eq!(
            MAX_BOOT_IMAGE_BYTES - GUNNAR_ESP_2026_08_03,
            82_432,
            "the margin the ceiling leaves gunnar's appliance ESP is 82 432 B (~82 KiB). If this \
             assertion is what broke, the ceiling arithmetic moved, not the ESP"
        );
    }

    /// The same refusal through the **real** mastering path, not just the
    /// catalog helper, so the error cannot be one that nothing propagates.
    #[test]
    fn build_image_refuses_an_oversized_efi_boot_image() {
        let dir = tempfile::tempdir().unwrap();
        let big = dir.path().join("efiboot.img");
        // One virtual sector past what the 16-bit count can address.
        let len = MAX_BOOT_IMAGE_BYTES + EL_TORITO_VIRTUAL_SECTOR;
        let f = std::fs::File::create(&big).unwrap();
        f.set_len(len).unwrap();
        drop(f);

        let mut w = IsoWriter::new("TOO_BIG");
        w.add_file("efiboot.img", &big).unwrap();
        w.set_efi_boot("efiboot.img");
        let err = w
            // `map(len)` before `expect_err`: on a red the Ok side is the whole
            // 33 MB image, and `expect_err` would print every byte of it. The
            // first run of this test emitted 96 MB of decimal zeros, which is a
            // failure report nobody can read.
            .build_image()
            .map(|img| img.len())
            .expect_err("an oversized ESP must fail the BUILD, where a human is watching, rather \
                         than the boot, where nobody is");
        let msg = format!("{err:#}");
        assert!(
            msg.contains(&len.to_string()) && msg.contains(&MAX_BOOT_IMAGE_BYTES.to_string()),
            "the propagated error lost the numbers: {msg}"
        );
        assert!(
            msg.contains("efiboot.img"),
            "the propagated error must name the image: {msg}"
        );

        // …and an ordinary ISO still masters, so this is a limit and not a
        // blanket refusal.
        let small = dir.path().join("small.img");
        std::fs::write(&small, vec![0xEFu8; 4096]).unwrap();
        let mut ok = IsoWriter::new("FINE");
        ok.add_file("efiboot.img", &small).unwrap();
        ok.set_efi_boot("efiboot.img");
        let img = ok.build_image().expect("a normal ESP still masters");
        assert_eq!(
            u16::from_le_bytes([img[21 * SECTOR + 102], img[21 * SECTOR + 103]]),
            8,
            "4096 B is 8 virtual sectors of 512 B"
        );
    }

    /// **An ISO with no boot image is not presented as bootable.**
    ///
    /// The catalog builder used to have a third arm:
    ///
    /// ```text
    /// (None, None) => (0, 4),   // no boot image at all
    /// c[32] = 0x88;             // bootable
    /// c[33] = 0x00;             // no emulation
    /// ```
    ///
    /// so an image carrying nothing bootable still got an El-Torito Boot Record
    /// volume descriptor, a checksummed validation entry, and a default entry
    /// marked **bootable, no-emulation, load RBA 0** — firmware pointed at the
    /// ISO's own system area. That is a lie told in the exact format firmware
    /// trusts.
    ///
    /// It also made every structural assertion in this file's bootable tests
    /// unfalsifiable: `CD001`, the boot-record type, the El-Torito id, the
    /// validation header and the `0x55AA` key were emitted unconditionally, so
    /// they passed for an image with no boot image and would pass for one whose
    /// boot image never resolved.
    ///
    /// The writer now emits a plain ISO-9660 data volume when nothing is
    /// bootable — which is also what `mkisofs` does when the argv carries no
    /// `-b` and no `-e`, and what `IsoBuild::master_args` renders in that case.
    #[test]
    fn an_iso_with_no_boot_image_is_not_presented_as_bootable() {
        let dir = tempfile::tempdir().unwrap();
        let data = dir.path().join("basemap.bin");
        std::fs::write(&data, vec![0x5Au8; 5000]).unwrap();

        let mut w = IsoWriter::new("KORP_DATA");
        w.add_file("basemap.bin", &data).unwrap();
        // …and NOTHING is marked bootable.
        let img = w.build_image().expect("a data-only ISO is legitimate and must still master");

        // It is a real ISO-9660 volume: the PVD is still there.
        assert_eq!(&img[16 * SECTOR + 1..16 * SECTOR + 6], b"CD001", "PVD magic");
        assert_eq!(img[16 * SECTOR], 1, "PVD type");
        // …and the volume descriptor sequence goes straight to the terminator,
        // with no Boot Record VD wedged in front of it.
        assert_eq!(
            img[17 * SECTOR], 255,
            "with nothing bootable, sector 17 must be the Volume Descriptor Set Terminator, not \
             an El-Torito Boot Record announcing a catalog for boot images that do not exist"
        );
        assert_eq!(&img[17 * SECTOR + 1..17 * SECTOR + 6], b"CD001", "terminator standard id");

        // Nowhere in the image does it claim El-Torito.
        assert!(
            !img.windows(23).any(|w| w == b"EL TORITO SPECIFICATION"),
            "a non-bootable ISO must not carry an El-Torito boot-system id anywhere"
        );
        // …and no 32-byte entry anywhere is a validation entry with the 0x55AA key
        // followed by a bootable default entry.
        for s in 0..img.len() / SECTOR {
            let c = s * SECTOR;
            assert!(
                !(img[c] == 0x01 && img[c + 30] == 0x55 && img[c + 31] == 0xAA),
                "sector {s} holds an El-Torito validation entry in an ISO with no boot image"
            );
        }

        // The data itself is still on the volume, so this is a non-bootable ISO
        // and not an empty one.
        assert!(
            img.windows(16).any(|w| w == [0x5Au8; 16]),
            "the payload must still be mastered onto the data volume"
        );

        // ── GREEN control ──────────────────────────────────────────────────
        // The SAME tree with one file marked bootable does get every structure
        // above, so the assertions are about the absence of a boot image and not
        // about the writer having stopped emitting El-Torito altogether.
        let esp = dir.path().join("efiboot.img");
        std::fs::write(&esp, vec![0xEFu8; 8192]).unwrap();
        let mut w = IsoWriter::new("KORP_DATA");
        w.add_file("basemap.bin", &data).unwrap();
        w.add_file("efiboot.img", &esp).unwrap();
        w.set_efi_boot("efiboot.img");
        let img = w.build_image().expect("a bootable ISO still masters");
        assert_eq!(img[17 * SECTOR], 0, "boot record VD is back");
        assert!(
            img[17 * SECTOR + 7..17 * SECTOR + 30].starts_with(b"EL TORITO SPECIFICATION"),
            "…with the El-Torito id"
        );
        let cat = read_u32_le(&img, 17 * SECTOR + 71) as usize * SECTOR;
        assert_eq!(img[cat], 0x01);
        assert_eq!(img[cat + 30], 0x55);
        assert_eq!(img[cat + 31], 0xAA);
        assert_eq!(img[cat + 32], 0x88, "default entry bootable");
    }

    // ── the GPT hybrid ─────────────────────────────────────────────────────

    /// An independent GPT reader for the tests: header magic + both CRC32s
    /// checked, the first entry parsed. Deliberately NOT the writer's own
    /// serializers read backwards — a reader that shares the writer's arithmetic
    /// agrees with it by construction and proves nothing.
    struct ParsedGpt {
        my_lba: u64,
        alt_lba: u64,
        first_usable: u64,
        last_usable: u64,
        entries_lba: u64,
        disk_guid: [u8; 16],
        type_guid: [u8; 16],
        part_guid: [u8; 16],
        part_first: u64,
        part_last: u64,
        part_name: String,
    }

    fn parse_gpt_at(img: &[u8], header_lba: u64) -> ParsedGpt {
        let h = &img[(header_lba * GPT_LBA) as usize..][..92];
        assert_eq!(&h[0..8], b"EFI PART", "GPT signature at LBA {header_lba}");
        assert_eq!(u32::from_le_bytes(h[12..16].try_into().unwrap()), 92, "header size");
        let recorded_crc = u32::from_le_bytes(h[16..20].try_into().unwrap());
        let mut zeroed = h.to_vec();
        zeroed[16..20].fill(0);
        assert_eq!(crc32fast::hash(&zeroed), recorded_crc, "header CRC32 at LBA {header_lba}");
        let u64_at = |o: usize| u64::from_le_bytes(h[o..o + 8].try_into().unwrap());
        let entries_lba = u64_at(72);
        let n = u32::from_le_bytes(h[80..84].try_into().unwrap()) as usize;
        let sz = u32::from_le_bytes(h[84..88].try_into().unwrap()) as usize;
        let array = &img[(entries_lba * GPT_LBA) as usize..][..n * sz];
        assert_eq!(
            crc32fast::hash(array),
            u32::from_le_bytes(h[88..92].try_into().unwrap()),
            "partition array CRC32 (header at LBA {header_lba})"
        );
        let e = &array[..sz];
        let mut disk_guid = [0u8; 16];
        disk_guid.copy_from_slice(&h[56..72]);
        let mut type_guid = [0u8; 16];
        type_guid.copy_from_slice(&e[0..16]);
        let mut part_guid = [0u8; 16];
        part_guid.copy_from_slice(&e[16..32]);
        let name_units: Vec<u16> = e[56..128]
            .chunks_exact(2)
            .map(|c| u16::from_le_bytes([c[0], c[1]]))
            .take_while(|&u| u != 0)
            .collect();
        // Every OTHER entry is empty — one partition, nothing else claimed.
        for (i, other) in array.chunks_exact(sz).enumerate().skip(1) {
            assert!(other.iter().all(|&b| b == 0), "entry {i} must be unused");
        }
        ParsedGpt {
            my_lba: u64_at(24),
            alt_lba: u64_at(32),
            first_usable: u64_at(40),
            last_usable: u64_at(48),
            entries_lba,
            disk_guid,
            type_guid,
            part_guid,
            part_first: u64::from_le_bytes(e[32..40].try_into().unwrap()),
            part_last: u64::from_le_bytes(e[40..48].try_into().unwrap()),
            part_name: String::from_utf16(&name_units).unwrap(),
        }
    }

    /// A real FAT image of `bytes` carrying one marker file, formatted with the
    /// same crate the composers format the ESP with.
    fn fat_esp(bytes: u64, marker: &[u8]) -> Vec<u8> {
        let mut buf = vec![0u8; bytes as usize];
        {
            let mut cur = std::io::Cursor::new(&mut buf[..]);
            fatfs::format_volume(&mut cur, fatfs::FormatVolumeOptions::new()).unwrap();
            let fs = fatfs::FileSystem::new(&mut cur, fatfs::FsOptions::new()).unwrap();
            let mut f = fs.root_dir().create_file("MARKER.BIN").unwrap();
            f.write_all(marker).unwrap();
        }
        buf
    }

    /// **An ESP past El-Torito's ceiling masters as a GPT hybrid whose ESP
    /// partition IS the FAT image** — parsed back with an independent reader,
    /// the partition's byte range compared to the staged bytes, and the FAT
    /// mounted through that range to read the marker file; the El-Torito entry
    /// is clamped AND the layout says so.
    ///
    /// RED first: with `GptHybrid::Off` the same tree is refused (the 2026-08-03
    /// clamp lesson still holds for a plain medium), so the hybrid arm is what
    /// makes this master, not a writer that stopped checking.
    #[test]
    fn the_hybrid_gpt_parses_back_and_spans_exactly_the_esp() {
        let dir = tempfile::tempdir().unwrap();
        // 34 MiB: 1 029 632 B past the ceiling — big enough to need the clamp,
        // small enough for a unit test.
        let esp_len = MAX_BOOT_IMAGE_BYTES + 1_029_632;
        assert_eq!(esp_len % GPT_LBA, 0, "a FAT image is whole 512-byte sectors");
        let marker = b"the-whole-fat-rides-the-gpt-partition";
        let esp = fat_esp(esp_len, marker);
        std::fs::write(dir.path().join("efiboot.img"), &esp).unwrap();
        std::fs::write(dir.path().join("payload.bin"), vec![0x5Au8; 3000]).unwrap();

        // ── RED: the plain writer refuses this ESP ──
        let mut plain = IsoWriter::new("KORP_APPLIANCE");
        plain.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
        plain.set_efi_boot("efiboot.img");
        let err = plain.build_image().map(|i| i.len()).expect_err("a plain medium must refuse");
        assert!(format!("{err:#}").contains(&MAX_BOOT_IMAGE_BYTES.to_string()));

        // ── GREEN: the hybrid arm ──
        let mut w = IsoWriter::new("KORP_APPLIANCE");
        w.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
        w.add_file("payload.bin", dir.path().join("payload.bin")).unwrap();
        w.set_efi_boot("efiboot.img");
        w.set_gpt_hybrid(GptHybrid::WhenEspExceedsElTorito);
        let img = w.build_image().expect("an oversized ESP masters as a hybrid");
        let layout = *w.layout().expect("a build records its layout");
        let efi = layout.efi.expect("an EFI entry");
        let gpt = layout.gpt.expect("a GPT was laid");

        // The image is whole ISO sectors AND whole GPT LBAs; the PVD's volume
        // space covers all of it, tail included.
        assert_eq!(img.len() % SECTOR, 0);
        assert_eq!(img.len() as u64, layout.total_bytes);
        assert_eq!(read_u32_le(&img, 16 * SECTOR + 80) as usize, img.len() / SECTOR, "PVD volume space");

        // ── protective MBR ──
        assert_eq!(&img[510..512], &[0x55, 0xAA], "MBR signature");
        assert_eq!(img[446 + 4], 0xEE, "one protective (0xEE) partition");
        assert_eq!(u32::from_le_bytes(img[454..458].try_into().unwrap()), 1, "protective starts at LBA 1");
        assert_eq!(
            u32::from_le_bytes(img[458..462].try_into().unwrap()) as u64,
            img.len() as u64 / GPT_LBA - 1,
            "protective spans the medium"
        );

        // ── primary + backup GPT, read back independently ──
        let disk_last = img.len() as u64 / GPT_LBA - 1;
        let primary = parse_gpt_at(&img, 1);
        let backup = parse_gpt_at(&img, disk_last);
        assert_eq!((primary.my_lba, primary.alt_lba), (1, disk_last));
        assert_eq!((backup.my_lba, backup.alt_lba), (disk_last, 1));
        assert_eq!(primary.entries_lba, 2);
        assert_eq!(backup.entries_lba, disk_last - 32, "backup array sits right under the backup header");
        assert_eq!(primary.first_usable, 34);
        assert_eq!(primary.last_usable, disk_last - 33);
        assert_eq!(primary.disk_guid, backup.disk_guid);
        assert_eq!(primary.disk_guid, gpt.disk_guid);
        assert_eq!(primary.type_guid, GPT_ESP_TYPE_GUID, "EFI System partition type");
        assert_eq!(primary.part_guid, gpt.partition_guid);
        assert_eq!(primary.part_name, "EFI System");
        assert_eq!((primary.part_first, primary.part_last), (gpt.first_lba, gpt.last_lba));
        assert_eq!((backup.part_first, backup.part_last), (gpt.first_lba, gpt.last_lba));
        // v4 / RFC-4122 bits, so any GUID-aware tool accepts them.
        assert_eq!(primary.disk_guid[7] & 0xF0, 0x40);
        assert_eq!(primary.disk_guid[8] & 0xC0, 0x80);

        // ── the partition's byte range IS the ESP image ──
        assert_eq!(gpt.first_lba, efi.lba as u64 * 4, "ESP partition starts where the ISO extent starts");
        assert_eq!(gpt.len_bytes(), esp_len, "the partition is exactly as long as the FAT image");
        let span = &img[gpt.offset_bytes() as usize..][..esp_len as usize];
        assert!(span == &esp[..], "the GPT partition's bytes are the staged ESP, byte for byte");
        // …and a FAT reader mounted on that span reads the marker file out of it.
        let mut cur = std::io::Cursor::new(span.to_vec());
        let fs = fatfs::FileSystem::new(&mut cur, fatfs::FsOptions::new()).unwrap();
        let mut got = Vec::new();
        fs.root_dir().open_file("MARKER.BIN").unwrap().read_to_end(&mut got).unwrap();
        assert_eq!(got, marker);

        // ── the El-Torito entry: clamped, and the layout SAYS clamped ──
        assert!(efi.clamped, "a 34 MiB ESP needs more than u16::MAX sectors");
        assert_eq!(efi.catalog_sectors, u16::MAX);
        assert_eq!(efi.len, esp_len);
        let cat = read_u32_le(&img, 17 * SECTOR + 71) as usize * SECTOR;
        assert_eq!(img[cat + 65], PLATFORM_EFI);
        assert_eq!(u16::from_le_bytes([img[cat + 102], img[cat + 103]]), u16::MAX, "catalog count clamped");
        assert_eq!(read_u32_le(&img, cat + 104), efi.lba, "catalog RBA is the ESP extent");
        let line = layout.describe();
        assert!(line.contains("GPT HYBRID") && line.contains("CLAMPED"), "receipt line: {line}");

        // ── deterministic: the same tree masters byte-identically, GUIDs included ──
        let mut again = IsoWriter::new("KORP_APPLIANCE");
        again.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
        again.add_file("payload.bin", dir.path().join("payload.bin")).unwrap();
        again.set_efi_boot("efiboot.img");
        again.set_gpt_hybrid(GptHybrid::WhenEspExceedsElTorito);
        assert!(again.build_image().unwrap() == img, "hybrid mastering is deterministic");
    }

    /// **A medium under the ceiling does not change shape.** With
    /// `WhenEspExceedsElTorito` — the policy the masterer applies to every UEFI
    /// ISO — a small ESP masters BYTE-IDENTICALLY to `Off`: same El-Torito fields,
    /// no MBR, no GPT, no tail. gunnar's and holger's media are the ones this
    /// protects. `Always` is the control: the same tree DOES gain the GPT, so
    /// the identity above is the policy at work and not a hybrid that never lays.
    #[test]
    fn a_medium_under_the_ceiling_is_byte_identical_with_the_hybrid_policy() {
        let dir = tempfile::tempdir().unwrap();
        let esp = fat_esp(4 * 1024 * 1024, b"small");
        std::fs::write(dir.path().join("efiboot.img"), &esp).unwrap();
        let build = |mode: GptHybrid| {
            let mut w = IsoWriter::new("GUNNAR_INSTALLER");
            w.add_file("efiboot.img", dir.path().join("efiboot.img")).unwrap();
            w.set_efi_boot("efiboot.img");
            w.set_gpt_hybrid(mode);
            let img = w.build_image().unwrap();
            (img, *w.layout().unwrap())
        };
        let (off, off_layout) = build(GptHybrid::Off);
        let (when, when_layout) = build(GptHybrid::WhenEspExceedsElTorito);
        assert!(off == when, "under the ceiling the hybrid policy must not change a single byte");
        assert_eq!(off_layout, when_layout);
        assert!(when_layout.gpt.is_none(), "no GPT was laid");
        let efi = when_layout.efi.unwrap();
        assert!(!efi.clamped);
        assert_eq!(efi.catalog_sectors, 8192, "4 MiB = 8192 virtual sectors, exact");
        assert_ne!(&off[0..8], b"EFI PART");
        assert!(&off[510..512] != &[0x55, 0xAA], "no MBR signature on a plain medium");
        assert_eq!(off.len(), when.len(), "no tail appended");

        // Control: `Always` on the same tree lays the GPT and grows by the tail.
        let (always, always_layout) = build(GptHybrid::Always);
        assert!(always != off);
        assert_eq!(always.len(), off.len() + GPT_TAIL_ISO_SECTORS as usize * SECTOR);
        let gpt = always_layout.gpt.expect("Always lays a GPT even under the ceiling");
        let parsed = parse_gpt_at(&always, 1);
        assert_eq!((parsed.part_first, parsed.part_last), (gpt.first_lba, gpt.last_lba));
        assert_eq!(gpt.len_bytes(), 4 * 1024 * 1024);
        let efi = always_layout.efi.unwrap();
        assert!(!efi.clamped, "under the ceiling the catalog count stays exact even in a hybrid");
        assert_eq!(efi.catalog_sectors, 8192);
        // …and the El-Torito fields are the ones the plain medium has.
        let cat = read_u32_le(&always, 17 * SECTOR + 71) as usize * SECTOR;
        let cat_off = read_u32_le(&off, 17 * SECTOR + 71) as usize * SECTOR;
        assert_eq!(&always[cat..cat + 128], &off[cat_off..cat_off + 128], "identical catalog");
    }

    /// The tail arithmetic and the EDK2 reach constant, spelled out so a change
    /// to either is a test failure and not a surprise on a serial console.
    #[test]
    fn hybrid_constants_are_the_documented_numbers() {
        assert_eq!(GPT_TAIL_ISO_SECTORS, 9, "33 LBAs of backup GPT → 9 ISO sectors");
        assert_eq!(GPT_FIRST_USABLE_LBA, 34);
        assert_eq!(EDK2_OPTICAL_ESP_REACH, 134_215_680, "65 535 × 2048");
        assert_eq!(el_torito_sectors_clamped(MAX_BOOT_IMAGE_BYTES), (u16::MAX, false));
        assert_eq!(el_torito_sectors_clamped(MAX_BOOT_IMAGE_BYTES + 1), (u16::MAX, true));
        assert_eq!(el_torito_sectors_clamped(0), (1, false));
        // korp's measured ESP (oden 2026-08-22): 88 385 024 B — clamped, and
        // within EDK2's optical reach.
        assert_eq!(el_torito_sectors_clamped(88_385_024), (u16::MAX, true));
        assert!(88_385_024 < EDK2_OPTICAL_ESP_REACH);
    }

    /// A boot image that was never staged is a hard error, not a silent zero LBA.
    #[test]
    fn unstaged_boot_image_errors() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("a.bin"), b"x").unwrap();
        let mut w = IsoWriter::new("ERR");
        w.add_file("a.bin", dir.path().join("a.bin")).unwrap();
        w.set_efi_boot("efiboot.img"); // never added
        assert!(w.build_image().is_err(), "unstaged EFI boot image is rejected");
    }
}