Summary
When Run is invoked against a real block device (/dev/sda, /dev/nvme0n1, …) and the resize plan includes a filesystem shrink, shrinkFilesystems ends up calling resize2fs against the whole-disk path rather than the partition's device path. resize2fs on a GPT-partitioned whole disk has nothing to resize and errors out.
Diagnosis
In shrinkFilesystems (resize.go:177):
p := d.Backend.Path() // e.g. "/dev/sda"
...
if err := resizeFilesystem(p, r.original, delta, fixErrors); err != nil { ... }
d.Backend.Path() is the path passed to OpenFromPath — i.e. the whole disk.
In resizeFilesystem (run_helpers.go):
case disk.DeviceTypeBlockDevice:
return execResize2fs(device, newSizeMB, fixErrors) // "/dev/sda"
So resize2fs /dev/sda and e2fsck /dev/sda are invoked, not resize2fs /dev/sda9 and e2fsck /dev/sda9. e2fsck immediately fails with Superblock has an invalid ext4 signature.
The DeviceTypeFile branch (disk-image files) correctly handles this by copying the partition out to a temp file, running resize2fs against that, and copying back. That branch is the one tested by TestShrinkFilesystem/success. The block-device branch is not currently exercised by any test, so the bug has been latent.
Impact
Any caller running partitionresizer.Run(...) against a real block device with a shrink in the plan hits this. This is the primary deployment shape for partitionresizer (vs. testing against disk images), so it's a real blocker for anyone wanting to use it in production.
Suggested fix
Derive the partition device path from the whole-disk path + partition number via sysfs:
- Read
/sys/class/block/<basename(diskPath)>/ for child directories that contain a partition file.
- For each child, read the
partition file to get its partition number.
- The matching child's directory name is the partition's kernel name. The device node is
/dev/<kernel-name> (e.g. sda9, nvme0n1p9, mmcblk0p9).
This avoids hardcoding the naming convention (sda9 vs nvme0n1p9 vs mmcblk0p9 etc.), which differs by disk type.
partitionresizer already does sysfs traversal in discover.go, so the building blocks exist.
A PR with this approach + a test (using a fake sysfs as in TestFindDisks) will follow.
Summary
When
Runis invoked against a real block device (/dev/sda,/dev/nvme0n1, …) and the resize plan includes a filesystem shrink,shrinkFilesystemsends up callingresize2fsagainst the whole-disk path rather than the partition's device path.resize2fson a GPT-partitioned whole disk has nothing to resize and errors out.Diagnosis
In
shrinkFilesystems(resize.go:177):d.Backend.Path()is the path passed toOpenFromPath— i.e. the whole disk.In
resizeFilesystem(run_helpers.go):So
resize2fs /dev/sdaande2fsck /dev/sdaare invoked, notresize2fs /dev/sda9ande2fsck /dev/sda9.e2fsckimmediately fails withSuperblock has an invalid ext4 signature.The DeviceTypeFile branch (disk-image files) correctly handles this by copying the partition out to a temp file, running
resize2fsagainst that, and copying back. That branch is the one tested byTestShrinkFilesystem/success. The block-device branch is not currently exercised by any test, so the bug has been latent.Impact
Any caller running
partitionresizer.Run(...)against a real block device with a shrink in the plan hits this. This is the primary deployment shape for partitionresizer (vs. testing against disk images), so it's a real blocker for anyone wanting to use it in production.Suggested fix
Derive the partition device path from the whole-disk path + partition number via sysfs:
/sys/class/block/<basename(diskPath)>/for child directories that contain apartitionfile.partitionfile to get its partition number./dev/<kernel-name>(e.g.sda9,nvme0n1p9,mmcblk0p9).This avoids hardcoding the naming convention (sda9 vs nvme0n1p9 vs mmcblk0p9 etc.), which differs by disk type.
partitionresizer already does sysfs traversal in
discover.go, so the building blocks exist.A PR with this approach + a test (using a fake sysfs as in TestFindDisks) will follow.