Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional partition types. #54

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/imagecustomizer/api/configuration/partition.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,33 @@ Supported options:
This flag is only supported on GPT formatted disks.

For further details, see: https://en.wikipedia.org/wiki/BIOS_boot_partition

- `home`: A `/home` partition.

- `linux-generic`: A generic Linux partition.

This is the default value.

- `root`: The `/` partition.

- `root-verity`: The verity hash partition for `/`.

- `srv`: The `/srv` partition.

- `swap`: A swap partition.

- `tmp`: The `/var/tmp` partition.

- `usr`: The `/usr` partition.

- `usr-verity`: The verity hash partition for `/usr`.
cwize1 marked this conversation as resolved.
Show resolved Hide resolved

Note: Image Customizer does not yet support `/usr` verity partitions.

- `var`: The `/var` partition.

- `xbootldr`: The `/boot` partition.

- A UUID string.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "A partition UUID string" -- just think it would make it more clear


For example: `c12a7328-f81f-11d2-ba4b-00a0c93ec93b`
5 changes: 5 additions & 0 deletions toolkit/tools/imagecustomizerapi/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ import (

var (
workingDir string

logMessagesHook *logger.MemoryLogHook
)

func TestMain(m *testing.M) {
var err error

logger.InitStderrLog()

logMessagesHook = logger.NewMemoryLogHook()
logger.Log.Hooks.Add(logMessagesHook)

workingDir, err = os.Getwd()
if err != nil {
logger.Log.Panicf("Failed to get working directory, error: %s", err)
Expand Down
2 changes: 1 addition & 1 deletion toolkit/tools/imagecustomizerapi/partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Partition struct {
End *DiskSize `yaml:"end" json:"end,omitempty"`
// Size is the size of the partition.
Size PartitionSize `yaml:"size" json:"size,omitempty"`
// Type specifies the type of partition the partition is.
// Type specifies the type of the partition.
Type PartitionType `yaml:"type" json:"type,omitempty"`
}

Expand Down
26 changes: 25 additions & 1 deletion toolkit/tools/imagecustomizerapi/partition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,29 @@ func TestPartitionIsValidBadType(t *testing.T) {

err := partition.IsValid()
assert.Error(t, err)
assert.ErrorContains(t, err, "unknown partition type")
assert.ErrorContains(t, err, "partition type is unknown and is not a UUID (a)")
}

func TestPartitionIsValidTypeUuid(t *testing.T) {
partition := Partition{
Id: "a",
Start: ptrutils.PtrTo(DiskSize(0)),
End: nil,
Type: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b",
}

err := partition.IsValid()
assert.NoError(t, err)
}

func TestPartitionIsValidTypeUuidInvalid(t *testing.T) {
partition := Partition{
Id: "a",
Start: ptrutils.PtrTo(DiskSize(0)),
End: nil,
Type: "c12a7328-f81f-11d2-ba4b-00a0c93ec93",
}

err := partition.IsValid()
assert.ErrorContains(t, err, "partition type is unknown and is not a UUID (c12a7328-f81f-11d2-ba4b-00a0c93ec93)")
}
90 changes: 87 additions & 3 deletions toolkit/tools/imagecustomizerapi/partitiontype.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,99 @@ const (
//
// See, https://en.wikipedia.org/wiki/BIOS_boot_partition
PartitionTypeBiosGrub PartitionType = "bios-grub"

PartitionTypeHome = "home"
PartitionTypeLinuxGeneric = "linux-generic"
PartitionTypeRoot = "root"
PartitionTypeRootVerity = "root-verity"
PartitionTypeSrv = "srv"
PartitionTypeSwap = "swap"
PartitionTypeTmp = "tmp"
PartitionTypeUsr = "usr"
PartitionTypeUsrVerity = "usr-verity"
PartitionTypeVar = "var"
PartitionTypeXbootldr = "xbootldr"
)

var (
// UUIDs come from:
// - https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
// - https://uapi-group.org/specifications/specs/discoverable_partitions_specification/
partitionTypeToUuidArchIndependent = map[PartitionType]string{
PartitionTypeESP: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b",
PartitionTypeBiosGrub: "21686148-6449-6E6F-744E-656564454649",
PartitionTypeHome: "933ac7e1-2eb4-4f13-b844-0e14e2aef915",
PartitionTypeSrv: "3b8f8425-20e0-4f3b-907f-1a25a76f98e8",
PartitionTypeSwap: "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f",
PartitionTypeTmp: "7ec6f557-3bc5-4aca-b293-16ef5df639d1",
PartitionTypeVar: "4d21b016-b534-45c2-a9fb-5c16e091fd2d",
PartitionTypeXbootldr: "bc13c2ff-59e6-4262-a352-b275fd6f7172",
}

cwize1 marked this conversation as resolved.
Show resolved Hide resolved
PartitionTypeToUuid map[PartitionType]string

// List of supported mount paths for each partition type.
// No entry means there are no associated mount paths for the partition type.
// Empty list means the partition type should not be mounted.
PartitionTypeSupportedMountPaths = map[PartitionType][]string{
PartitionTypeESP: {
"/boot/efi",
},
PartitionTypeBiosGrub: {},
PartitionTypeHome: {
"/home",
},
PartitionTypeRoot: {
"/",
},
PartitionTypeRootVerity: {},
PartitionTypeSrv: {
"/srv",
},
PartitionTypeSwap: {},
PartitionTypeTmp: {
"/var/tmp",
},
PartitionTypeUsr: {
"/usr",
},
PartitionTypeUsrVerity: {},
PartitionTypeVar: {
"/var",
},
PartitionTypeXbootldr: {
"/boot",
},
}
)

func init() {
PartitionTypeToUuid = make(map[PartitionType]string)

for k, v := range partitionTypeToUuidArchIndependent {
PartitionTypeToUuid[k] = v
}

for k, v := range partitionTypeToUuidArchDependent {
amritakohli marked this conversation as resolved.
Show resolved Hide resolved
PartitionTypeToUuid[k] = v
}
}

func (p PartitionType) IsValid() (err error) {
switch p {
case PartitionTypeDefault, PartitionTypeESP, PartitionTypeBiosGrub:
// All good.
case PartitionTypeDefault, PartitionTypeESP, PartitionTypeBiosGrub, PartitionTypeHome, PartitionTypeLinuxGeneric,
PartitionTypeRoot, PartitionTypeRootVerity, PartitionTypeSrv, PartitionTypeSwap, PartitionTypeTmp,
PartitionTypeUsr, PartitionTypeUsrVerity, PartitionTypeVar, PartitionTypeXbootldr:
// String is a well known name.
return nil

default:
return fmt.Errorf("unknown partition type (%s)", p)
isUuid := uuidRegex.MatchString(string(p))
if isUuid {
// String is a UUID.
return nil
}

return fmt.Errorf("partition type is unknown and is not a UUID (%s)", p)
}
}
16 changes: 16 additions & 0 deletions toolkit/tools/imagecustomizerapi/partitiontype_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package imagecustomizerapi

var (
// UUIDs come from:
// - https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
// - https://uapi-group.org/specifications/specs/discoverable_partitions_specification/
partitionTypeToUuidArchDependent = map[PartitionType]string{
PartitionTypeRoot: "4f68bce3-e8cd-4db1-96e7-fbcaf984b709",
PartitionTypeRootVerity: "2c7357ed-ebd2-46d9-aec1-23d437ec2bf5",
PartitionTypeUsr: "8484680c-9521-48c6-9c11-b0720656f69e",
PartitionTypeUsrVerity: "77ff5f63-e7b6-4633-acf4-1565b864c0e6",
}
)
16 changes: 16 additions & 0 deletions toolkit/tools/imagecustomizerapi/partitiontype_arm64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package imagecustomizerapi

var (
// UUIDs come from:
// - https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
// - https://uapi-group.org/specifications/specs/discoverable_partitions_specification/
partitionTypeToUuidArchDependent = map[PartitionType]string{
PartitionTypeRoot: "b921b045-1df0-41c3-af44-4c6f280d3fae",
PartitionTypeRootVerity: "df3300ce-d69f-4c92-978c-9bfb0f38d820",
PartitionTypeUsr: "b0e01050-ee5f-4390-949a-9101b17104e9",
PartitionTypeUsrVerity: "6e11a4e7-fbca-4ded-b9e9-e1a512bb664e",
}
)
18 changes: 18 additions & 0 deletions toolkit/tools/imagecustomizerapi/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"strings"

"github.com/microsoft/azurelinux/toolkit/tools/internal/logger"
"github.com/microsoft/azurelinux/toolkit/tools/internal/sliceutils"
)

Expand Down Expand Up @@ -112,6 +113,10 @@ func (s *Storage) IsValid() error {
return fmt.Errorf("ESP partition (%s) must have 'fat32' or 'vfat' filesystem type", partition.Id)
}

if fileSystem.MountPoint == nil || fileSystem.MountPoint.Path != "/boot/efi" {
return fmt.Errorf("ESP partition (%s) must be mounted at /boot/efi", partition.Id)
}

case PartitionTypeBiosGrub:
biosBootPartitionExists = true

Expand All @@ -125,6 +130,19 @@ func (s *Storage) IsValid() error {
return fmt.Errorf("BIOS boot partition (%s) must not have a 'mountPoint'", partition.Id)
}
}

default:
if hasFileSystem && fileSystem.MountPoint != nil {
expectedMountPaths, hasExpectedMountPaths := PartitionTypeSupportedMountPaths[partition.Type]
if hasExpectedMountPaths {
supportedPath := sliceutils.ContainsValue(expectedMountPaths, fileSystem.MountPoint.Path)
if !supportedPath {
logger.Log.Infof(
"Unexpected mount path (%s) for partition (%s) with type (%s). Expected paths: %v",
fileSystem.MountPoint.Path, partition.Id, partition.Type, expectedMountPaths)
}
}
}
}
}
}
Expand Down
103 changes: 96 additions & 7 deletions toolkit/tools/imagecustomizerapi/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"testing"

"github.com/microsoft/azurelinux/toolkit/tools/imagegen/diskutils"
"github.com/microsoft/azurelinux/toolkit/tools/internal/logger"
"github.com/microsoft/azurelinux/toolkit/tools/internal/ptrutils"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -240,12 +242,6 @@ func TestStorageIsValidBadBiosBootStart(t *testing.T) {
},
}},
BootType: BootTypeLegacy,
FileSystems: []FileSystem{
{
DeviceId: "bios",
Type: "fat32",
},
},
}

err := storage.IsValid()
Expand Down Expand Up @@ -386,7 +382,7 @@ func TestStorageIsValidUniqueLabel(t *testing.T) {
Type: FileSystemTypeFat32,
MountPoint: &MountPoint{
IdType: MountIdentifierTypePartLabel,
Path: "/",
Path: "/boot/efi",
},
},
{
Expand Down Expand Up @@ -1317,3 +1313,96 @@ func TestStorageIsValidVerityMissingReadonly(t *testing.T) {
err := value.IsValid()
assert.ErrorContains(t, err, "verity device's (rootverity) filesystem must include the 'ro' mount option")
}

func TestStorageIsValidExpectedMountPath(t *testing.T) {
value := Storage{
Disks: []Disk{{
PartitionTableType: "gpt",
MaxSize: ptrutils.PtrTo(DiskSize(4 * diskutils.GiB)),
Partitions: []Partition{
{
Id: "esp",
Start: ptrutils.PtrTo(DiskSize(1 * diskutils.MiB)),
End: ptrutils.PtrTo(DiskSize(9 * diskutils.MiB)),
Type: PartitionTypeESP,
},
{
Id: "rootfs",
Type: PartitionTypeVar,
Start: ptrutils.PtrTo(DiskSize(9 * diskutils.MiB)),
},
},
}},
BootType: "efi",
FileSystems: []FileSystem{
{
DeviceId: "esp",
Type: "vfat",
MountPoint: &MountPoint{
Path: "/boot/efi",
},
},
{
DeviceId: "rootfs",
Type: "ext4",
MountPoint: &MountPoint{
Path: "/",
},
},
},
}

logMessagesHook := logMessagesHook.AddSubHook()
defer logMessagesHook.Close()

err := value.IsValid()

logMessages := logMessagesHook.ConsumeMessages()

assert.NoError(t, err)
assert.Contains(t, logMessages, logger.MemoryLogMessage{
Message: "Unexpected mount path (/) for partition (rootfs) with type (var). Expected paths: [/var]",
Level: logrus.InfoLevel,
})
cwize1 marked this conversation as resolved.
Show resolved Hide resolved
}

func TestStorageIsValidBadEspPath(t *testing.T) {
value := Storage{
Disks: []Disk{{
PartitionTableType: "gpt",
MaxSize: ptrutils.PtrTo(DiskSize(4 * diskutils.GiB)),
Partitions: []Partition{
{
Id: "esp",
Start: ptrutils.PtrTo(DiskSize(1 * diskutils.MiB)),
End: ptrutils.PtrTo(DiskSize(9 * diskutils.MiB)),
Type: PartitionTypeESP,
},
{
Id: "rootfs",
Start: ptrutils.PtrTo(DiskSize(9 * diskutils.MiB)),
},
},
}},
BootType: "efi",
FileSystems: []FileSystem{
{
DeviceId: "esp",
Type: "vfat",
MountPoint: &MountPoint{
Path: "/boot/efj",
},
},
{
DeviceId: "rootfs",
Type: "ext4",
MountPoint: &MountPoint{
Path: "/",
},
},
},
}

err := value.IsValid()
assert.ErrorContains(t, err, "ESP partition (esp) must be mounted at /boot/efi")
}
Loading
Loading