Skip to content

Commit 13b5118

Browse files
committed
disk: align footer
Some partitioning tools (`systemd-repart`) round down the usable end of the disk to the grain boundary (made configurable in the previous commit). This ensures that the *end* of the last partition on disk is grain aligned. `images` instead grows the last partition to take up all remaining size on the disk. When the footer is not aligned to sector size that means that the last partitions gets slightly larger than one would expect it to be. To figure out the size for the last partition we subtract the raw non-aligned header size (16,896 bytes) from the total disk size and then grow the last partition to that. This means that the last partition gains 3,584 bytes (with a 4096 byte grain size). Aligning the footer size to the grain size has the end result that the last partition becomes exactly the requested size. This new behavior is gated behind an `AlignFooter` flag in the partition table as it's new behavior and it would change all pre-existing partition tables. Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
1 parent 7e0c0cd commit 13b5118

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

pkg/disk/partition_table.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ type PartitionTable struct {
3434
ExtraPadding uint64 `json:"extra_padding,omitempty" yaml:"extra_padding,omitempty"`
3535
// Starting offset of the first partition in the table (in bytes)
3636
StartOffset Offset `json:"start_offset,omitempty" yaml:"start_offset,omitempty"`
37+
// Align the GPT footer to the grain size, ensuring the last partition's
38+
// size is also grain-aligned. Matches systemd-repart behavior.
39+
AlignFooter bool `json:"align_footer,omitempty" yaml:"align_footer,omitempty"`
3740

3841
// Dictates if certain bits and bobs are required or not; uses the default
3942
// policy if not set.
@@ -225,6 +228,7 @@ func (pt *PartitionTable) Clone() Entity {
225228
GrainSize: pt.GrainSize,
226229
ExtraPadding: pt.ExtraPadding,
227230
StartOffset: pt.StartOffset,
231+
AlignFooter: pt.AlignFooter,
228232
Policy: pt.Policy,
229233
}
230234

@@ -516,6 +520,9 @@ func (pt *PartitionTable) relayout(size datasizes.Size) uint64 {
516520
// The GPT header is also at the end of the partition table
517521
if pt.Type == PT_GPT {
518522
footer = header
523+
if pt.AlignFooter {
524+
footer = pt.AlignUp(footer)
525+
}
519526
}
520527

521528
start := pt.AlignUp(header + pt.StartOffset).Uint64()

pkg/disk/partition_table_internal_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,55 @@ func TestRelayout(t *testing.T) {
787787
},
788788
},
789789
},
790+
"gpt-4k-grain-align-footer": {
791+
pt: &PartitionTable{
792+
Type: PT_GPT,
793+
Size: 200*MiB + 2*GiB,
794+
GrainSize: 4096,
795+
AlignFooter: true,
796+
Partitions: []Partition{
797+
{
798+
Size: 200 * MiB,
799+
Type: EFISystemPartitionGUID,
800+
Payload: &Filesystem{
801+
Type: "vfat",
802+
Mountpoint: "/boot/efi",
803+
},
804+
},
805+
{
806+
Size: 2 * GiB,
807+
Payload: &Filesystem{
808+
Mountpoint: "/",
809+
},
810+
},
811+
},
812+
},
813+
size: 200*MiB + 2*GiB,
814+
expected: &PartitionTable{
815+
Type: PT_GPT,
816+
Size: 200*MiB + 2*GiB + 10*4096, // header (5*4096) + footer (5*4096)
817+
GrainSize: 4096,
818+
AlignFooter: true,
819+
Partitions: []Partition{
820+
{
821+
Start: 5 * 4096, // header (16896) aligned up to 4096 grain
822+
Size: 200 * MiB,
823+
Type: EFISystemPartitionGUID,
824+
Payload: &Filesystem{
825+
Type: "vfat",
826+
Mountpoint: "/boot/efi",
827+
},
828+
},
829+
{
830+
Start: 5*4096 + 200*MiB,
831+
Size: 2 * GiB, // exactly 2 GiB: footer is grain-aligned so root stays aligned
832+
Payload: &Filesystem{
833+
Mountpoint: "/",
834+
},
835+
},
836+
},
837+
},
838+
},
790839
}
791840

792841
for name := range testCases {

0 commit comments

Comments
 (0)