Skip to content

Commit fedc243

Browse files
committed
disk: absolute start offset
Some partitioning tools (`systemd-repart`) use the convention that the first usable sector is the first LBA. The convention for this is sector 2048 (1 MiB). This is then rounded up to the grain size. `images` computes the start as `AlignUp(header + StartOffset)`. With the previous default grain size of 1 MiB this means that the first partition always started at 1 MiB when no `StartOffset` was provided. When the grain is configured to be smaller (4096 bytes) it instead produces sector 40 (20,480 bytes) which falls inside the reserved range `sfdisk` claims which then makes `sfdisk` reject the partition table because we don't pass `--first-lba` in the stage (and we probably don't want to either). Adding an `AbsoluteStartOffset` toggle allows us to set the offset to 1 MiB exactly; mimicking `systemd-repart`'s behavior. Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
1 parent 13b5118 commit fedc243

2 files changed

Lines changed: 76 additions & 12 deletions

File tree

pkg/disk/partition_table.go

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ type PartitionTable struct {
3232
SectorSize uint64 `json:"sector_size,omitempty" yaml:"sector_size,omitempty"`
3333
// Extra space at the end of the partition table (sectors)
3434
ExtraPadding uint64 `json:"extra_padding,omitempty" yaml:"extra_padding,omitempty"`
35-
// Starting offset of the first partition in the table (in bytes)
35+
// Starting offset of the first partition in the table (in bytes).
36+
// By default this is added to the header size. When
37+
// AbsoluteStartOffset is true, it is treated as a minimum absolute
38+
// position (matching systemd-repart's use of libfdisk's first_lba).
3639
StartOffset Offset `json:"start_offset,omitempty" yaml:"start_offset,omitempty"`
40+
// Treat StartOffset as an absolute minimum start position rather
41+
// than an additive offset on top of the header.
42+
AbsoluteStartOffset bool `json:"absolute_start_offset,omitempty" yaml:"absolute_start_offset,omitempty"`
3743
// Align the GPT footer to the grain size, ensuring the last partition's
3844
// size is also grain-aligned. Matches systemd-repart behavior.
3945
AlignFooter bool `json:"align_footer,omitempty" yaml:"align_footer,omitempty"`
@@ -220,16 +226,17 @@ func (pt *PartitionTable) Clone() Entity {
220226
}
221227

222228
clone := &PartitionTable{
223-
Size: pt.Size,
224-
UUID: pt.UUID,
225-
Type: pt.Type,
226-
Partitions: make([]Partition, len(pt.Partitions)),
227-
SectorSize: pt.SectorSize,
228-
GrainSize: pt.GrainSize,
229-
ExtraPadding: pt.ExtraPadding,
230-
StartOffset: pt.StartOffset,
231-
AlignFooter: pt.AlignFooter,
232-
Policy: pt.Policy,
229+
Size: pt.Size,
230+
UUID: pt.UUID,
231+
Type: pt.Type,
232+
Partitions: make([]Partition, len(pt.Partitions)),
233+
SectorSize: pt.SectorSize,
234+
GrainSize: pt.GrainSize,
235+
ExtraPadding: pt.ExtraPadding,
236+
StartOffset: pt.StartOffset,
237+
AbsoluteStartOffset: pt.AbsoluteStartOffset,
238+
AlignFooter: pt.AlignFooter,
239+
Policy: pt.Policy,
233240
}
234241

235242
for idx, partition := range pt.Partitions {
@@ -525,7 +532,11 @@ func (pt *PartitionTable) relayout(size datasizes.Size) uint64 {
525532
}
526533
}
527534

528-
start := pt.AlignUp(header + pt.StartOffset).Uint64()
535+
startBase := header + pt.StartOffset
536+
if pt.AbsoluteStartOffset && pt.StartOffset > header {
537+
startBase = pt.StartOffset
538+
}
539+
start := pt.AlignUp(startBase).Uint64()
529540

530541
size = pt.AlignUp(size)
531542

pkg/disk/partition_table_internal_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,59 @@ func TestRelayout(t *testing.T) {
836836
},
837837
},
838838
},
839+
"gpt-4k-grain-repart-compat": {
840+
pt: &PartitionTable{
841+
Type: PT_GPT,
842+
Size: 200*MiB + 2*GiB,
843+
GrainSize: 4096,
844+
StartOffset: 1 * MiB,
845+
AbsoluteStartOffset: true,
846+
AlignFooter: true,
847+
Partitions: []Partition{
848+
{
849+
Size: 200 * MiB,
850+
Type: EFISystemPartitionGUID,
851+
Payload: &Filesystem{
852+
Type: "vfat",
853+
Mountpoint: "/boot/efi",
854+
},
855+
},
856+
{
857+
Size: 2 * GiB,
858+
Payload: &Filesystem{
859+
Mountpoint: "/",
860+
},
861+
},
862+
},
863+
},
864+
size: 200*MiB + 2*GiB,
865+
expected: &PartitionTable{
866+
Type: PT_GPT,
867+
Size: 1*MiB + 200*MiB + 2*GiB + 5*4096, // start (1 MiB) + partitions + aligned footer
868+
GrainSize: 4096,
869+
StartOffset: 1 * MiB,
870+
AbsoluteStartOffset: true,
871+
AlignFooter: true,
872+
Partitions: []Partition{
873+
{
874+
Start: 1 * MiB, // StartOffset treated as absolute minimum, already grain-aligned
875+
Size: 200 * MiB,
876+
Type: EFISystemPartitionGUID,
877+
Payload: &Filesystem{
878+
Type: "vfat",
879+
Mountpoint: "/boot/efi",
880+
},
881+
},
882+
{
883+
Start: 1*MiB + 200*MiB,
884+
Size: 2 * GiB,
885+
Payload: &Filesystem{
886+
Mountpoint: "/",
887+
},
888+
},
889+
},
890+
},
891+
},
839892
}
840893

841894
for name := range testCases {

0 commit comments

Comments
 (0)