From c96143421ab78d17bb74a68580ed34e6e90cb4e0 Mon Sep 17 00:00:00 2001 From: eriknordmark Date: Thu, 28 May 2026 11:03:35 +0200 Subject: [PATCH] discover: fix sysfs end-of-partition calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sysfs branch of findDisks computed each partition's last-byte position as `size - start + 1`, which is meaningless arithmetic — the sysfs `size` attribute is the partition's length in sectors, so the inclusive last LBA is `start + size - 1`. The disk-image branch a few dozen lines up uses the correct formula already. partitionData.end flows into calculateResizes (calculate.go:48/53/ 54/63/67/69/106/131/133/134) and run_helpers.go:150 where it participates in every free-space and shrink-target computation, so the wrong end on the sysfs path produced silently wrong layouts whenever partitionresizer is run against a real block device rather than a disk image. Disk-image-only test fixtures hid it from CI. Extend TestFindDisks/all to assert pd.end so the regression is covered. Signed-off-by: eriknordmark Co-Authored-By: Claude Opus 4.7 --- discover.go | 6 +++++- discover_test.go | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/discover.go b/discover.go index 2f8a02f..99fd392 100644 --- a/discover.go +++ b/discover.go @@ -172,7 +172,11 @@ func findDisks(disk, syspath string) (map[string][]partitionData, error) { if err != nil { return nil, err } - end := size - start + 1 + // sysfs reports `size` as the partition's length in sectors + // (not its last LBA), so the inclusive last sector is + // start + size - 1. The disk-image branch above uses the + // same formula. + end := start + size - 1 // read from uevent to get name ueventPath := filepath.Join(sysClassBlockPath, candidate.Name(), name, "uevent") ueventData, err := os.ReadFile(ueventPath) diff --git a/discover_test.go b/discover_test.go index 24fb180..edff615 100644 --- a/discover_test.go +++ b/discover_test.go @@ -133,11 +133,17 @@ func TestFindDisks(t *testing.T) { if pd.label != "foo" { t.Errorf("pd.label = %q, want foo", pd.label) } - // start and size in bytes (blockSize=512) + // start, size, and end in bytes (blockSize=512). End is the + // inclusive last byte of the partition, i.e. + // (start_sector + size_sectors - 1) * blockSize. if pd.start != 2*512 || pd.size != 4*512 { t.Errorf("(start,size) = (%d,%d), want (%d,%d)", pd.start, pd.size, 2*512, 4*512) } + expectedEnd := int64((2+4-1) * 512) + if pd.end != expectedEnd { + t.Errorf("pd.end = %d, want %d", pd.end, expectedEnd) + } }) t.Run("single", func(t *testing.T) { // restrict to explicit disk