Skip to content

Commit

Permalink
Merge pull request #325 from cloudfoundry/efi_support
Browse files Browse the repository at this point in the history
Adding support for EFI based stemcells.
  • Loading branch information
jpalermo committed May 11, 2024
2 parents 11f25e8 + 245e0d1 commit 163ffee
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 57 deletions.
1 change: 1 addition & 0 deletions platform/disk/partitioner_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type PartitionType string

const (
PartitionTypeSwap PartitionType = "swap"
PartitionTypeEFI PartitionType = "efi"
PartitionTypeLinux PartitionType = "linux"
PartitionTypeEmpty PartitionType = "empty"
PartitionTypeUnknown PartitionType = "unknown"
Expand Down
38 changes: 24 additions & 14 deletions platform/disk/root_device_partitioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,21 @@ func (p rootDevicePartitioner) Partition(devicePath string, partitions []Partiti
return bosherr.Errorf("Missing first partition on `%s'", devicePath)
}

if p.partitionsMatch(existingPartitions[1:], partitions) {
p.logger.Info(p.logTag, "Partitions already match, skipping partitioning")
return nil
// If EFI based stemcells, there is an extra partition. Matching is accounting for it here.
indexExistingStart := 1
if existingPartitions[0].Type == PartitionTypeEFI {
indexExistingStart = 2
}

if len(existingPartitions) > 1 {
p.logger.Error(p.logTag,
"Failed to create ephemeral partitions on root device `%s'. Expected 1 partition, found %d: %s",
devicePath,
len(existingPartitions),
existingPartitions,
)
return bosherr.Errorf("Found %d unexpected partitions on `%s'", len(existingPartitions)-1, devicePath)
if p.partitionsMatch(existingPartitions[indexExistingStart:], partitions) {
p.logger.Info(p.logTag, "Partitions already match, skipping partitioning")
return nil
}

// To support optimal reads on HDDs and optimal erasure on SSD: use 1MiB partition alignments.
alignmentInBytes := uint64(1048576)

partitionStart := p.roundUp(existingPartitions[0].EndInBytes+1, alignmentInBytes)
partitionStart := p.roundUp(existingPartitions[len(existingPartitions)-1].EndInBytes+1, alignmentInBytes)

for index, partition := range partitions {
partitionEnd := partitionStart + partition.SizeInBytes - 1
Expand Down Expand Up @@ -100,14 +96,18 @@ func (p rootDevicePartitioner) GetDeviceSizeInBytes(devicePath string) (uint64,
return 0, bosherr.Errorf("Getting remaining size of `%s'", devicePath)
}

partitionInfoLines := allLines[1:3]
partitionInfoLines := allLines[1 : len(allLines)-1]
deviceInfo := strings.Split(partitionInfoLines[0], ":")
deviceFullSizeInBytes, err := strconv.ParseUint(strings.TrimRight(deviceInfo[1], "B"), 10, 64)
if err != nil {
return 0, bosherr.WrapErrorf(err, "Getting remaining size of `%s'", devicePath)
}

firstPartitionInfo := strings.Split(partitionInfoLines[1], ":")
// If EFI partition is used, use the second partition as it is root partition
if firstPartitionInfo[4] == "fat16" {
firstPartitionInfo = strings.Split(partitionInfoLines[2], ":")
}
firstPartitionEndInBytes, err := strconv.ParseUint(strings.TrimRight(firstPartitionInfo[2], "B"), 10, 64)
if err != nil {
return 0, bosherr.WrapErrorf(err, "Getting remaining size of `%s'", devicePath)
Expand Down Expand Up @@ -170,17 +170,27 @@ func (p rootDevicePartitioner) GetPartitions(devicePath string) (
return partitions, deviceFullSizeInBytes, bosherr.WrapErrorf(err, "Parsing existing partitions of `%s'", devicePath)
}

// Saving parition type to detect if we're using EFI partition
partitionType := PartitionTypeUnknown
if partitionInfo[4] == "ext4" || partitionInfo[4] == "xfs" {
partitionType = PartitionTypeLinux
} else if partitionInfo[4] == "linux-swap(v1)" {
partitionType = PartitionTypeSwap
} else if partitionInfo[4] == "fat16" {
partitionType = PartitionTypeEFI
}

partitions = append(
partitions,
ExistingPartition{
Index: partitionIndex,
SizeInBytes: uint64(partitionSizeInBytes),
StartInBytes: uint64(partitionStartInBytes),
EndInBytes: uint64(partitionEndInBytes),
Type: partitionType,
},
)
}

return partitions, deviceFullSizeInBytes, nil
}

Expand Down
163 changes: 120 additions & 43 deletions platform/disk/root_device_partitioner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ var _ = Describe("rootDevicePartitioner", func() {
// 3071279104 + 8589934592 - 1 = 11661213695 (desired end)
// 11661213695 - 3071279104 + 1 = 8589934592 (final size)
// swap start=3071279104, end=11661213695, size=8589934592
// (11661213695 + 1) % 1048576 = 0
// (11661213695 + 1) = 11661213696 (aligned start)
// (11661213695 + 1) % 1048576 = 1048575
// (11661213695 + 1) + 1048575 = 11662262271 (aligned start)
// 11661213696 + 9813934079 - 1 = 21475147774 (desired end)
// 21475147774 > 21474836480 = true (bigger than disk)
// 21474836480 - 1 = 21474836479 (end of disk)
Expand Down Expand Up @@ -115,6 +115,98 @@ var _ = Describe("rootDevicePartitioner", func() {
})
})

Context("when the desired partitions do not exist with EFI partition", func() {
BeforeEach(func() {
// 20GiB device, ~50Mib partition 0, ~5GiB partition 1, 18403868671B remaining
fakeCmdRunner.AddCmdResult(
"parted -m /dev/sda unit B print",
fakesys.FakeCmdResult{
Stdout: `BYT;
/dev/vda:21474836480B:virtblk:512:512:msdos:Virtio Block Device;
1:1048576B:51380223B:50331648B:fat16::lba;
2:52428800B:5368709119B:5316280320B:ext4::;
`,
},
)
})

It("creates partitions (aligned to 1MiB) using parted", func() {
partitions := []Partition{
{SizeInBytes: 4294967296}, // swap (4GiB)
{SizeInBytes: 4294967296}, // ephemeral (4GiB)
}

// Calculating "aligned" partition start/end/size
// (5368709119 + 1) % 1048576 = 0
// (5368709119 + 1) = 5368709120 (aligned start)
// 5368709120 + 4294967296 - 1 = 9663676415 (desired end)
// swap start=5369757696, end=9663676415, size=4294967296
// (9663676415 + 1) % 1048576 = 0
// (9663676415 + 1) = 9663676416 (aligned start)
// 9663676416 + 4294967296 - 1 = 13958643711 (desired end)
// 13958643711 > 21474836480 = false (smaller than disk)
// swap start=9663676416, end=13958643711, size=4294967296

err := partitioner.Partition("/dev/sda", partitions)
Expect(err).ToNot(HaveOccurred())

Expect(len(fakeCmdRunner.RunCommands)).To(Equal(3))
Expect(fakeCmdRunner.RunCommands).To(ContainElement([]string{"parted", "-m", "/dev/sda", "unit", "B", "print"}))
Expect(fakeCmdRunner.RunCommands).To(ContainElement([]string{"parted", "-s", "/dev/sda", "unit", "B", "mkpart", "primary", "5368709120", "9663676415"}))
Expect(fakeCmdRunner.RunCommands).To(ContainElement([]string{"parted", "-s", "/dev/sda", "unit", "B", "mkpart", "primary", "9663676416", "13958643711"}))
})

It("truncates the last partition if it is larger than remaining disk space", func() {
partitions := []Partition{
{SizeInBytes: 8589934592}, // swap (8GiB)
{SizeInBytes: 9813934079}, // ephemeral ("remaining" that exceeds disk size when aligned)
}

// Calculating "aligned" partition start/end/size
// (5368709119 + 1) % 1048576 = 0
// (5368709119 + 1) = 5368709120 (aligned start)
// 5368709120 + 8589934592 - 1 = 13958643711 (desired end)
// 11661213695 - 3071279104 + 1 = 8589934592 (final size)
// swap start=5368709120, end=13958643711, size=8589934592
// (13958643711 + 1) % 1048576 = 0
// (13958643711 + 1) = 13958643712 (aligned start)
// 13958643712 + 9813934079 - 1 = 23772577790 (desired end)
// 23772577790 > 21474836480 = true (bigger than disk)
// 21474836480 - 1 = 21474836479 (end of disk)
// 21474836479 - 13958643712 + 1 = 7516192768 (final size from aligned start to end of disk)
// swap start=13958643712, end=21474836479, size=7516192768

err := partitioner.Partition("/dev/sda", partitions)
Expect(err).ToNot(HaveOccurred())

Expect(len(fakeCmdRunner.RunCommands)).To(Equal(3))
Expect(fakeCmdRunner.RunCommands).To(ContainElement([]string{"parted", "-m", "/dev/sda", "unit", "B", "print"}))
Expect(fakeCmdRunner.RunCommands).To(ContainElement([]string{"parted", "-s", "/dev/sda", "unit", "B", "mkpart", "primary", "5368709120", "13958643711"}))
Expect(fakeCmdRunner.RunCommands).To(ContainElement([]string{"parted", "-s", "/dev/sda", "unit", "B", "mkpart", "primary", "13958643712", "21474836479"}))
})

Context("when partitioning fails", func() {
BeforeEach(func() {
fakeCmdRunner.AddCmdResult(
"parted -s /dev/sda unit B mkpart primary 5368709120 13958643711",
fakesys.FakeCmdResult{Error: errors.New("fake-parted-error")},
)
})

It("returns error", func() {
partitions := []Partition{
{SizeInBytes: 8589934592}, // swap (8GiB)
{SizeInBytes: 9813934079}, // ephemeral (remaining)
}

err := partitioner.Partition("/dev/sda", partitions)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("Partitioning disk `/dev/sda'"))
Expect(err.Error()).To(ContainSubstring("fake-parted-error"))
})
})
})

Context("when the desired partitions do not exist and there are 2 existing partitions", func() {
BeforeEach(func() {
// 20GiB device, ~3GiB partition 0, 18403868671B remaining
Expand Down Expand Up @@ -250,26 +342,23 @@ var _ = Describe("rootDevicePartitioner", func() {
})
})

Context("when partitions are within delta", func() {
Context("when partitions already match with EFI partition", func() {
BeforeEach(func() {
fakeCmdRunner.AddCmdResult(
"parted -m /dev/sda unit B print",
fakesys.FakeCmdResult{
Stdout: `BYT;
/dev/sda:128B:virtblk:512:512:msdos:Virtio Block Device;
1:1B:31B:31B:ext4::;
2:32B:64B:33B:ext4::;
3:65B:125B:61B:ext4::;
1:1B:16B:16B:fat16::;
2:17B:80B:64B:ext4::;
3:81B:114B:32B:ext4::;
`,
},
)
})

It("does not partition", func() {
partitions := []Partition{
{SizeInBytes: 32},
{SizeInBytes: 62},
}
partitions := []Partition{{SizeInBytes: 32}}

err := partitioner.Partition("/dev/sda", partitions)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -278,39 +367,6 @@ var _ = Describe("rootDevicePartitioner", func() {
})
})

Context("when partition in the middle does not match", func() {
BeforeEach(func() {
fakeCmdRunner.AddCmdResult(
"parted -m /dev/sda unit B print",
fakesys.FakeCmdResult{
Stdout: `BYT;
/dev/sda:128B:virtblk:512:512:msdos:Virtio Block Device;
1:1B:32B:32B:ext4::;
2:33B:47B:15B:ext4::;
3:48B:79B:32B:ext4::;
4:80B:111B:32B:ext4::;
5:112B:119B:8B:ext4::;
`,
},
)
})

It("returns an error", func() {
partitions := []Partition{
{SizeInBytes: 16},
{SizeInBytes: 16},
{SizeInBytes: 32},
}

err := partitioner.Partition("/dev/sda", partitions)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("Found 4 unexpected partitions on `/dev/sda'"))
Expect(fakeCmdRunner.RunCommands).To(Equal([][]string{
{"parted", "-m", "/dev/sda", "unit", "B", "print"},
}))
})
})

Context("when the first partition is missing", func() {
BeforeEach(func() {
fakeCmdRunner.AddCmdResult(
Expand Down Expand Up @@ -409,6 +465,27 @@ var _ = Describe("rootDevicePartitioner", func() {
})
})

Context("when getting disk partition with EFI boot disk information succeeds", func() {
BeforeEach(func() {
fakeCmdRunner.AddCmdResult(
"parted -m /dev/sda unit B print",
fakesys.FakeCmdResult{
Stdout: `BYT;
/dev/sda:53687091200B:virtblk:512:512:msdos:Virtio Block Device;
1:1048576B:51380223B:50331648B:fat16::lba;
2:52428800B:5368709119B:5316280320B:ext4::;
`,
},
)
})

It("returns the size of the device", func() {
size, err := partitioner.GetDeviceSizeInBytes("/dev/sda")
Expect(err).ToNot(HaveOccurred())
Expect(size).To(Equal(uint64(48318382080)))
})
})

Context("when getting disk partition information fails", func() {
BeforeEach(func() {
fakeCmdRunner.AddCmdResult(
Expand Down

0 comments on commit 163ffee

Please sign in to comment.