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 {