Skip to content

block-device shrink calls resize2fs against the whole-disk path, not the partition device #9

@eriknordmark

Description

@eriknordmark

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:

  1. Read /sys/class/block/<basename(diskPath)>/ for child directories that contain a partition file.
  2. For each child, read the partition file to get its partition number.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions