package main // kernel_boot over stand-in roots: a systemd-boot machine with and without the fallback entry, a GRUB machine // with and without the fallback kernel in its menu, and a machine with neither (novox/hq ADR 0508 ยง9, issue 1841). import ( "os" "path/filepath" "testing" ) const selectedVar = "sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" func put(t *testing.T, root, p, content string) { t.Helper() full := filepath.Join(root, p) if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil { t.Fatal(err) } write(t, full, content, 0o644) } func bootOf(t *testing.T, root string) *BootState { t.Helper() got, err := (&Machine{Root: root}).Boot() if err != nil { t.Fatal(err) } return got } func TestKernelBootReadsASystemdBootMachineWithItsFallbackEntry(t *testing.T) { root := t.TempDir() put(t, root, selectedVar, "\x07\x00\x00\x00a\x00r\x00c\x00h\x00.\x00c\x00o\x00n\x00f\x00\x00\x00") put(t, root, "boot/loader/entries/arch.conf", defaultEntry) put(t, root, "boot/loader/entries/mesh-linux-lts.conf", "title Arch Linux (linux-lts, fallback)\nlinux /vmlinuz-linux-lts\n") put(t, root, "boot/vmlinuz-linux", "k") put(t, root, "boot/vmlinuz-linux-lts", "k") got := bootOf(t, root) if got.Loader != "systemd-boot" || got.Booted != "arch.conf" || !got.FallbackPresent { t.Fatalf("%+v", got) } if len(got.Entries) != 2 || got.Entries[0] != "arch.conf" || got.Entries[1] != "mesh-linux-lts.conf" { t.Fatalf("entries: %v", got.Entries) } if len(got.Kernels) != 2 || got.Kernels[0] != "vmlinuz-linux" || got.Kernels[1] != "vmlinuz-linux-lts" { t.Fatalf("kernels: %v", got.Kernels) } if got.BootFreeKiB <= 0 { t.Fatalf("free: %d", got.BootFreeKiB) } } func TestKernelBootSaysASystemdBootMachineHasNoFallbackYet(t *testing.T) { root := t.TempDir() put(t, root, selectedVar, "\x07\x00\x00\x00a\x00r\x00c\x00h\x00.\x00c\x00o\x00n\x00f\x00\x00\x00") put(t, root, "boot/loader/entries/arch.conf", defaultEntry) put(t, root, "boot/vmlinuz-linux", "k") // The entry file alone is not the fallback: the kernel it names must be in /boot. put(t, root, "boot/loader/entries/mesh-linux-lts.conf", "linux /vmlinuz-linux-lts\n") if got := bootOf(t, root); got.FallbackPresent { t.Fatalf("a fallback entry without its kernel: %+v", got) } } func TestKernelBootReadsAGrubMachine(t *testing.T) { root := t.TempDir() put(t, root, "boot/grub/grub.cfg", grubMenu) put(t, root, "boot/vmlinuz-linux", "k") put(t, root, "proc/cmdline", bootedCmdline) got := bootOf(t, root) if got.Loader != "grub" || got.FallbackPresent || got.Booted != "vmlinuz-linux" { t.Fatalf("%+v", got) } if len(got.Entries) != 2 || got.Entries[0] != "Arch Linux" { t.Fatalf("entries: %v", got.Entries) } put(t, root, "boot/vmlinuz-linux-lts", "k") put(t, root, "boot/grub/grub.cfg", grubMenu+"menuentry 'Arch Linux LTS' {\n\tlinux\t/boot/vmlinuz-linux-lts root=x rw\n}\n") if got := bootOf(t, root); !got.FallbackPresent { t.Fatalf("%+v", got) } } func TestKernelBootSaysWhenThereIsNoLoader(t *testing.T) { got := bootOf(t, t.TempDir()) if got.Loader != "none" || got.FallbackPresent || len(got.Entries) != 0 { t.Fatalf("%+v", got) } }