From b8e1adb7f0faa44ecb835100c8c027da18001890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Fri, 20 Dec 2024 15:13:11 +0100 Subject: [PATCH] disk: enable customizing ESP partitions This commits allow people to customize their ESP partition. Previously, we always added an ESP partition ourselves, regardless of the given customizations, so the customization errored out if there was an ESP because of a conflict. This commit alters the behaviour, so ESP is only added automatically if it's not in the customizations. --- pkg/disk/partition_table.go | 42 +++++++++--- pkg/disk/partition_table_test.go | 112 +++++++++++++++++++++++++++++-- 2 files changed, 140 insertions(+), 14 deletions(-) diff --git a/pkg/disk/partition_table.go b/pkg/disk/partition_table.go index e7dd33a87..c3286c292 100644 --- a/pkg/disk/partition_table.go +++ b/pkg/disk/partition_table.go @@ -1079,7 +1079,7 @@ func addBootPartition(pt *PartitionTable, bootFsType FSType) error { // The function will append the new partitions to the end of the existing // partition table therefore it is best to call this function early to put them // near the front (as is conventional). -func addPartitionsForBootMode(pt *PartitionTable, bootMode platform.BootMode) error { +func addPartitionsForBootMode(pt *PartitionTable, disk *blueprint.DiskCustomization, bootMode platform.BootMode) error { switch bootMode { case platform.BOOT_LEGACY: // add BIOS boot partition @@ -1090,12 +1090,10 @@ func addPartitionsForBootMode(pt *PartitionTable, bootMode platform.BootMode) er pt.Partitions = append(pt.Partitions, part) return nil case platform.BOOT_UEFI: - // add ESP - part, err := mkESP(200*datasizes.MiB, pt.Type) - if err != nil { + // maybe add ESP + if err := maybeAddESP(pt, disk); err != nil { return err } - pt.Partitions = append(pt.Partitions, part) return nil case platform.BOOT_HYBRID: // add both @@ -1103,11 +1101,10 @@ func addPartitionsForBootMode(pt *PartitionTable, bootMode platform.BootMode) er if err != nil { return err } - esp, err := mkESP(200*datasizes.MiB, pt.Type) - if err != nil { + pt.Partitions = append(pt.Partitions, bios) + if err := maybeAddESP(pt, disk); err != nil { return err } - pt.Partitions = append(pt.Partitions, bios, esp) return nil case platform.BOOT_NONE: return nil @@ -1214,6 +1211,18 @@ func maybeAddBootPartition(pt *PartitionTable, disk *blueprint.DiskCustomization return nil } +func maybeAddESP(pt *PartitionTable, disk *blueprint.DiskCustomization) error { + if needsESP(disk) { + // we need an ESP to boot UEFI, create ESP if it does not already exist + part, err := mkESP(200*datasizes.MiB, pt.Type) + if err != nil { + return err + } + pt.Partitions = append(pt.Partitions, part) + } + return nil +} + // NewCustomPartitionTable creates a partition table based almost entirely on the disk customizations from a blueprint. func NewCustomPartitionTable(customizations *blueprint.DiskCustomization, options *CustomPartitionTableOptions, rng *rand.Rand) (*PartitionTable, error) { if options == nil { @@ -1256,7 +1265,7 @@ func NewCustomPartitionTable(customizations *blueprint.DiskCustomization, option // if needed // // TODO: switch to ensure ESP in case customizations already include it - if err := addPartitionsForBootMode(pt, options.BootMode); err != nil { + if err := addPartitionsForBootMode(pt, customizations, options.BootMode); err != nil { return nil, fmt.Errorf("%s %w", errPrefix, err) } // add the /boot partition (if it is needed) @@ -1524,3 +1533,18 @@ func needsBoot(disk *blueprint.DiskCustomization) bool { } return foundBtrfsOrLVM } + +// determine whether an ESP is needed based on the customizations. An ESP is +// needed if there is no /boot/efi partition defined in the customization. +func needsESP(disk *blueprint.DiskCustomization) bool { + if disk == nil { + return true + } + + for _, part := range disk.Partitions { + if part.Mountpoint == "/boot/efi" { + return false + } + } + return true +} diff --git a/pkg/disk/partition_table_test.go b/pkg/disk/partition_table_test.go index 66f547aa9..164d4757d 100644 --- a/pkg/disk/partition_table_test.go +++ b/pkg/disk/partition_table_test.go @@ -927,10 +927,11 @@ func TestAddBootPartition(t *testing.T) { func TestAddPartitionsForBootMode(t *testing.T) { type testCase struct { - pt disk.PartitionTable - bootMode platform.BootMode - expected disk.PartitionTable - errmsg string + pt disk.PartitionTable + bootMode platform.BootMode + expected disk.PartitionTable + diskCustomization *blueprint.DiskCustomization + errmsg string } testCases := map[string]testCase{ @@ -1084,6 +1085,44 @@ func TestAddPartitionsForBootMode(t *testing.T) { }, }, }, + "esp-present-uefi": { + pt: disk.PartitionTable{Type: disk.PT_GPT}, + bootMode: platform.BOOT_UEFI, + diskCustomization: &blueprint.DiskCustomization{ + Partitions: []blueprint.PartitionCustomization{ + { + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/boot/efi", + }, + }, + }, + }, + expected: disk.PartitionTable{Type: disk.PT_GPT}, + }, + "esp-present-hybrid": { + pt: disk.PartitionTable{Type: disk.PT_GPT}, + bootMode: platform.BOOT_HYBRID, + diskCustomization: &blueprint.DiskCustomization{ + Partitions: []blueprint.PartitionCustomization{ + { + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/boot/efi", + }, + }, + }, + }, + expected: disk.PartitionTable{ + Type: disk.PT_GPT, + Partitions: []disk.Partition{ + { + Size: 1 * datasizes.MiB, + Bootable: true, + Type: disk.BIOSBootPartitionGUID, + UUID: disk.BIOSBootPartitionUUID, + }, + }, + }, + }, "bad-pttype-bios": { pt: disk.PartitionTable{Type: disk.PartitionTableType(911)}, bootMode: platform.BOOT_LEGACY, @@ -1111,7 +1150,7 @@ func TestAddPartitionsForBootMode(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) pt := tc.pt - err := disk.AddPartitionsForBootMode(&pt, tc.bootMode) + err := disk.AddPartitionsForBootMode(&pt, tc.diskCustomization, tc.bootMode) if tc.errmsg == "" { assert.NoError(err) assert.Equal(tc.expected, pt) @@ -2517,6 +2556,69 @@ func TestNewCustomPartitionTable(t *testing.T) { }, }, }, + "custom-esp": { + customizations: &blueprint.DiskCustomization{ + Type: "dos", + Partitions: []blueprint.PartitionCustomization{ + { + MinSize: 1 * datasizes.GiB, + GUID: "06", + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/boot/efi", + FSType: "vfat", + }, + }, + }, + }, + options: &disk.CustomPartitionTableOptions{ + DefaultFSType: disk.FS_EXT4, + BootMode: platform.BOOT_HYBRID, + PartitionTableType: disk.PT_GPT, + }, + expected: &disk.PartitionTable{ + Type: disk.PT_DOS, + Size: 1*datasizes.GiB + 2*datasizes.MiB, + UUID: "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8", + Partitions: []disk.Partition{ + { + Start: 1 * datasizes.MiB, // header + Size: 1 * datasizes.MiB, + Bootable: true, + Type: disk.BIOSBootPartitionDOSID, + UUID: disk.BIOSBootPartitionUUID, + }, + { + Start: 2 * datasizes.MiB, + Size: 1 * datasizes.GiB, + Type: "06", + Payload: &disk.Filesystem{ + Type: "vfat", + UUID: "6e4ff95f", + Mountpoint: "/boot/efi", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, + }, + { + Start: 1*datasizes.GiB + 2*datasizes.MiB, + Size: 0, + Type: disk.FilesystemLinuxDOSID, + UUID: "", // partitions on dos PTs don't have UUIDs + Bootable: false, + Payload: &disk.Filesystem{ + Type: "ext4", + Label: "root", + Mountpoint: "/", + UUID: "f662a5ee-e82a-4df4-8a2d-0b75fb180daf", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, + }, + }, + }, + }, } for name := range testCases {