Skip to content

Commit

Permalink
disk: add support for custom partition GUIDs
Browse files Browse the repository at this point in the history
This commit takes the blueprint customization added in the last commit,
and makes it actually useful.
  • Loading branch information
ondrejbudai committed Dec 20, 2024
1 parent 58c9b33 commit 7abbefe
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 22 deletions.
21 changes: 21 additions & 0 deletions pkg/disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"io"
"math/rand"
"reflect"
"regexp"
"strings"

"slices"
Expand Down Expand Up @@ -120,6 +121,26 @@ func getPartitionTypeIDfor(ptType PartitionTableType, partTypeName string) (stri
return id, nil
}

// exactly 2 hex digits
var validDosPartitionType = regexp.MustCompile(`^[0-9a-fA-F]{2}$`)

func validatePartitionTypeID(ptType PartitionTableType, partTypeName string) error {
switch ptType {
case PT_DOS:
if !validDosPartitionType.MatchString(partTypeName) {
return fmt.Errorf("invalid dos partition type ID: %s", partTypeName)
}
case PT_GPT:
if _, err := uuid.Parse(partTypeName); err != nil {
return fmt.Errorf("invalid gpt partition type GUID: %s", partTypeName)
}
default:
return fmt.Errorf("unknown or unsupported partition table enum: %d", ptType)
}

return nil
}

// FSType is the filesystem type enum.
//
// There should always be one value for each filesystem type supported by
Expand Down
68 changes: 48 additions & 20 deletions pkg/disk/partition_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -1313,24 +1313,35 @@ func addPlainPartition(pt *PartitionTable, partition blueprint.PartitionCustomiz
return fmt.Errorf("error creating partition with mountpoint %q: %w", partition.Mountpoint, err)
}

// all user-defined partitions are data partitions except boot and swap
var typeName string
switch {
case partition.Mountpoint == "/boot":
typeName = "boot"
case fstype == "swap":
typeName = "swap"
default:
typeName = "data"
}
partType := partition.GUID

partType, err := getPartitionTypeIDfor(pt.Type, typeName)
if err != nil {
return fmt.Errorf("error getting partition type ID for %q: %w", partition.Mountpoint, err)
if partType != "" {
if err := validatePartitionTypeID(pt.Type, partType); err != nil {
return fmt.Errorf("error validating partition type ID for %q: %w", partition.Mountpoint, err)
}
} else {
// if the partition type is not specified, determine it based on the
// mountpoint and the partition type

// all user-defined partitions are data partitions except boot and swap
var typeName string
switch {
case partition.Mountpoint == "/boot":
typeName = "boot"
case fstype == "swap":
typeName = "swap"
default:
typeName = "data"
}

partType, err = getPartitionTypeIDfor(pt.Type, typeName)
if err != nil {
return fmt.Errorf("error getting partition type ID for %q: %w", partition.Mountpoint, err)
}
}

var payload PayloadEntity
switch typeName {
switch fstype {
case "swap":
payload = &Swap{
Label: partition.Label,
Expand Down Expand Up @@ -1408,10 +1419,19 @@ func addLVMPartition(pt *PartitionTable, partition blueprint.PartitionCustomizat
}

// create partition for volume group
partType, err := getPartitionTypeIDfor(pt.Type, "lvm")
if err != nil {
return fmt.Errorf("error creating lvm partition %q: %w", vgname, err)
partType := partition.GUID
if partType != "" {
if err := validatePartitionTypeID(pt.Type, partType); err != nil {
return fmt.Errorf("error validating partition type ID for %q: %w", vgname, err)
}
} else {
var err error
partType, err = getPartitionTypeIDfor(pt.Type, "lvm")
if err != nil {
return fmt.Errorf("error creating lvm partition %q: %w", vgname, err)
}
}

newpart := Partition{
Type: partType,
Size: partition.MinSize,
Expand All @@ -1437,9 +1457,17 @@ func addBtrfsPartition(pt *PartitionTable, partition blueprint.PartitionCustomiz
}

// create partition for btrfs volume
partType, err := getPartitionTypeIDfor(pt.Type, "data")
if err != nil {
return fmt.Errorf("error creating btrfs partition: %w", err)
partType := partition.GUID
if partType != "" {
if err := validatePartitionTypeID(pt.Type, partType); err != nil {
return fmt.Errorf("error validating partition type ID for btrfs: %w", err)
}
} else {
var err error
partType, err = getPartitionTypeIDfor(pt.Type, "data")
if err != nil {
return fmt.Errorf("error creating btrfs partition: %w", err)
}
}
newpart := Partition{
Type: partType,
Expand Down
48 changes: 46 additions & 2 deletions pkg/disk/partition_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,7 @@ func TestNewCustomPartitionTable(t *testing.T) {
Partitions: []blueprint.PartitionCustomization{
{
MinSize: 20 * datasizes.MiB,
GUID: "42", // overrides the inferred type
FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{
Mountpoint: "/data",
Label: "data",
Expand Down Expand Up @@ -1230,7 +1231,7 @@ func TestNewCustomPartitionTable(t *testing.T) {
{
Start: 202 * datasizes.MiB,
Size: 20 * datasizes.MiB,
Type: disk.FilesystemLinuxDOSID,
Type: "42",
Bootable: false,
UUID: "", // partitions on dos PTs don't have UUIDs
Payload: &disk.Filesystem{
Expand Down Expand Up @@ -1267,6 +1268,7 @@ func TestNewCustomPartitionTable(t *testing.T) {
Partitions: []blueprint.PartitionCustomization{
{
MinSize: 20 * datasizes.MiB,
GUID: "01234567-89ab-cdef-0123-456789abcdef", // overrides the inferred type
FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{
Mountpoint: "/data",
Label: "data",
Expand Down Expand Up @@ -1310,7 +1312,7 @@ func TestNewCustomPartitionTable(t *testing.T) {
{
Start: 202 * datasizes.MiB,
Size: 20 * datasizes.MiB,
Type: disk.FilesystemDataGUID,
Type: "01234567-89ab-cdef-0123-456789abcdef",
Bootable: false,
UUID: "a178892e-e285-4ce1-9114-55780875d64e",
Payload: &disk.Filesystem{
Expand Down Expand Up @@ -2640,6 +2642,48 @@ func TestNewCustomPartitionTableErrors(t *testing.T) {
},
errmsg: `error generating partition table: invalid partition table: "dos" partition table type only supports up to 4 partitions: got 5 after creating the partition table with all necessary partitions`,
},
"bad-guid-dos": {
customizations: &blueprint.DiskCustomization{
Type: "dos",
Partitions: []blueprint.PartitionCustomization{
{
MinSize: 20 * datasizes.MiB,
GUID: "01234567-89ab-cdef-0123-456789abcdef", // dos cannot use UUIDs
FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{
Mountpoint: "/data",
Label: "data",
FSType: "ext4",
},
},
},
},
options: &disk.CustomPartitionTableOptions{
DefaultFSType: disk.FS_XFS,
BootMode: platform.BOOT_HYBRID,
},
errmsg: `error generating partition table: error validating partition type ID for "/data": invalid dos partition type ID: 01234567-89ab-cdef-0123-456789abcdef`,
},
"bad-guid-gpt": {
customizations: &blueprint.DiskCustomization{
Type: "gpt",
Partitions: []blueprint.PartitionCustomization{
{
MinSize: 20 * datasizes.MiB,
GUID: "EF", // gpt requires a 36-character GUID
FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{
Mountpoint: "/data",
Label: "data",
FSType: "ext4",
},
},
},
},
options: &disk.CustomPartitionTableOptions{
DefaultFSType: disk.FS_XFS,
BootMode: platform.BOOT_HYBRID,
},
errmsg: `error generating partition table: error validating partition type ID for "/data": invalid gpt partition type GUID: EF`,
},
}

// we don't care about the rng for error tests
Expand Down

0 comments on commit 7abbefe

Please sign in to comment.