From 33f603a7dfab2b8202857837f9c8ab87515a2a04 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 23 Sep 2024 12:54:35 +0400 Subject: [PATCH] fix: correctly process GPT with gaps There were two bugs related to processing partition tables with gaps (that is, partitions which do not exist). Signed-off-by: Andrey Smirnov --- partitioning/gpt/gpt.go | 21 +++++++++++-------- partitioning/gpt/gpt_test.go | 12 ++++++----- partitioning/gpt/testdata/mix-allocate.gdisk | 10 ++++----- partitioning/gpt/testdata/mix-allocate.sfdisk | 8 +++---- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/partitioning/gpt/gpt.go b/partitioning/gpt/gpt.go index 5365b14..6d4dc98 100644 --- a/partitioning/gpt/gpt.go +++ b/partitioning/gpt/gpt.go @@ -294,11 +294,12 @@ func (t *Table) allocatableRanges() []allocatableRange { for { for partitionIdx < len(t.entries) { - if t.entries[partitionIdx] == nil { - partitionIdx++ + if t.entries[partitionIdx] != nil { + break } - break + // skip empty entries + partitionIdx++ } var highLBA uint64 @@ -634,12 +635,14 @@ func (t *Table) syncKernel() error { } } - err = t.dev.KernelPartitionAdd(no, - myEntry.FirstLBA*uint64(t.sectorSize), - (myEntry.LastLBA-myEntry.FirstLBA+1)*uint64(t.sectorSize), - ) - if err != nil { - return fmt.Errorf("failed to add partition %d: %w", no, err) + if myEntry != nil { + err = t.dev.KernelPartitionAdd(no, + myEntry.FirstLBA*uint64(t.sectorSize), + (myEntry.LastLBA-myEntry.FirstLBA+1)*uint64(t.sectorSize), + ) + if err != nil { + return fmt.Errorf("failed to add partition %d: %w", no, err) + } } } diff --git a/partitioning/gpt/gpt_test.go b/partitioning/gpt/gpt_test.go index 222ee4b..fe2b6e9 100644 --- a/partitioning/gpt/gpt_test.go +++ b/partitioning/gpt/gpt_test.go @@ -160,19 +160,21 @@ func TestGPT(t *testing.T) { allocator: func(t *testing.T, table *gpt.Table) { t.Helper() - // allocate 3 1G partitions first, and delete the middle one + // allocate 4 1G partitions first, and delete two in the middle require.NoError(t, allocateError(table.AllocatePartition(1*GiB, "1G1", partType1, gpt.WithUniqueGUID(uuid.MustParse("DA66737E-1ED4-4DDF-B98C-70CEBFE3ADA0")), ))) require.NoError(t, allocateError(table.AllocatePartition(1*GiB, "1G2", partType1))) - require.NoError(t, allocateError(table.AllocatePartition(1*GiB, "1G3", partType2, + require.NoError(t, allocateError(table.AllocatePartition(1*GiB, "1G3", partType1))) + require.NoError(t, allocateError(table.AllocatePartition(1*GiB, "1G4", partType2, gpt.WithUniqueGUID(uuid.MustParse("3D0FE86B-7791-4659-B564-FC49A542866D")), ))) require.NoError(t, table.DeletePartition(1)) + require.NoError(t, table.DeletePartition(2)) - // allocate smaller partitions to fill the gap + // gap is 2 GiB, while the tail available space is < 2 GiB, so small partitions will be appended to the end require.NoError(t, allocateError(table.AllocatePartition(200*MiB, "200M", partType2, gpt.WithUniqueGUID(uuid.MustParse("EE1A711E-DE12-4D9F-98FF-672F7AD638F8")), ))) @@ -180,8 +182,8 @@ func TestGPT(t *testing.T) { gpt.WithUniqueGUID(uuid.MustParse("15E609C8-9775-4E86-AF59-8A87E7C03FAB")), ))) - // partition that doesn't fit the gap will be appended to the end - require.NoError(t, allocateError(table.AllocatePartition(500*MiB, "500M", partType2, + // bigger partition will fill the gap + require.NoError(t, allocateError(table.AllocatePartition(1500*MiB, "1500M", partType2, gpt.WithUniqueGUID(uuid.MustParse("15E609C8-9775-4E86-AF59-8A87E7C03FAC")), ))) }, diff --git a/partitioning/gpt/testdata/mix-allocate.gdisk b/partitioning/gpt/testdata/mix-allocate.gdisk index c22c4f7..3d0b1e4 100644 --- a/partitioning/gpt/testdata/mix-allocate.gdisk +++ b/partitioning/gpt/testdata/mix-allocate.gdisk @@ -12,11 +12,11 @@ Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 2048, last usable sector is 12582878 Partitions will be aligned on 2048-sector boundaries -Total free space is 6133727 sectors (2.9 GiB) +Total free space is 4085727 sectors (1.9 GiB) Number Start (sector) End (sector) Size Code Name 1 2048 2099199 1024.0 MiB EF00 1G1 - 2 2099200 2508799 200.0 MiB 8E00 200M - 3 2508800 3327999 400.0 MiB 8E00 400M - 4 4196352 6293503 1024.0 MiB 8E00 1G3 - 5 6293504 7317503 500.0 MiB 8E00 500M + 3 2099200 5171199 1.5 GiB 8E00 1500M + 4 6293504 8390655 1024.0 MiB 8E00 1G4 + 5 8390656 8800255 200.0 MiB 8E00 200M + 6 8800256 9619455 400.0 MiB 8E00 400M diff --git a/partitioning/gpt/testdata/mix-allocate.sfdisk b/partitioning/gpt/testdata/mix-allocate.sfdisk index 3e710d5..e39d506 100644 --- a/partitioning/gpt/testdata/mix-allocate.sfdisk +++ b/partitioning/gpt/testdata/mix-allocate.sfdisk @@ -6,7 +6,7 @@ last-lba: 12582878 sector-size: 512 start= 2048, size= 2097152, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=DA66737E-1ED4-4DDF-B98C-70CEBFE3ADA0, name="1G1" -start= 2099200, size= 409600, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=EE1A711E-DE12-4D9F-98FF-672F7AD638F8, name="200M" -start= 2508800, size= 819200, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=15E609C8-9775-4E86-AF59-8A87E7C03FAB, name="400M" -start= 4196352, size= 2097152, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=3D0FE86B-7791-4659-B564-FC49A542866D, name="1G3" -start= 6293504, size= 1024000, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=15E609C8-9775-4E86-AF59-8A87E7C03FAC, name="500M" +start= 2099200, size= 3072000, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=15E609C8-9775-4E86-AF59-8A87E7C03FAC, name="1500M" +start= 6293504, size= 2097152, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=3D0FE86B-7791-4659-B564-FC49A542866D, name="1G4" +start= 8390656, size= 409600, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=EE1A711E-DE12-4D9F-98FF-672F7AD638F8, name="200M" +start= 8800256, size= 819200, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=15E609C8-9775-4E86-AF59-8A87E7C03FAB, name="400M"