From aea76a2bec253b9c6c29823538903352094a7039 Mon Sep 17 00:00:00 2001 From: Kai Lueke Date: Fri, 29 Sep 2023 18:06:09 +0200 Subject: [PATCH] sgdisk: Run partx after partition changes The sgdisk tool does not update the kernel partition table with BLKPG in contrast to other similar tools but only uses BLKRRPART which fails as soon as one partition of the disk is mounted. Update the kernel partition table with partx when we know that a partition of the disk is in use. --- docs/release-notes.md | 2 ++ dracut/30ignition/module-setup.sh | 1 + internal/distro/distro.go | 2 ++ internal/exec/stages/disks/partitions.go | 40 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 9e929859bf..941228cf03 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -32,11 +32,13 @@ Starting with this release, ignition-validate binaries are signed with the ### Changes - Require Go 1.19+ +- The Dracut module now installs partx ### Bug fixes - Prevent races with udev after disk editing - Don't fail to wipe partition table if it's corrupted +- Force partition table update after repartitioning, even if one partition is already mounted ## Ignition 2.16.2 (2023-07-12) diff --git a/dracut/30ignition/module-setup.sh b/dracut/30ignition/module-setup.sh index 51bec7e791..d2b33ed3df 100755 --- a/dracut/30ignition/module-setup.sh +++ b/dracut/30ignition/module-setup.sh @@ -40,6 +40,7 @@ install() { mkfs.xfs \ mkswap \ sgdisk \ + partx \ useradd \ userdel \ usermod \ diff --git a/internal/distro/distro.go b/internal/distro/distro.go index 61ca87aed9..15cc1aeafc 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -37,6 +37,7 @@ var ( mdadmCmd = "mdadm" mountCmd = "mount" sgdiskCmd = "sgdisk" + partxCmd = "partx" modprobeCmd = "modprobe" udevadmCmd = "udevadm" usermodCmd = "usermod" @@ -90,6 +91,7 @@ func GroupdelCmd() string { return groupdelCmd } func MdadmCmd() string { return mdadmCmd } func MountCmd() string { return mountCmd } func SgdiskCmd() string { return sgdiskCmd } +func PartxCmd() string { return partxCmd } func ModprobeCmd() string { return modprobeCmd } func UdevadmCmd() string { return udevadmCmd } func UsermodCmd() string { return usermodCmd } diff --git a/internal/exec/stages/disks/partitions.go b/internal/exec/stages/disks/partitions.go index 0e62f21bab..68cc29583c 100644 --- a/internal/exec/stages/disks/partitions.go +++ b/internal/exec/stages/disks/partitions.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "os" + "os/exec" "path/filepath" "regexp" "sort" @@ -31,6 +32,7 @@ import ( cutil "github.com/coreos/ignition/v2/config/util" "github.com/coreos/ignition/v2/config/v3_5_experimental/types" + "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/sgdisk" ) @@ -482,6 +484,10 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { return err } + var partxAdd []uint64 + var partxDelete []uint64 + var partxUpdate []uint64 + for _, part := range resolvedPartitions { shouldExist := partitionShouldExist(part) info, exists := diskInfo.GetPartition(part.Number) @@ -511,11 +517,17 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { case !exists && shouldExist: op.CreatePartition(part) modification = true + if partInUse { + partxAdd = append(partxAdd, uint64(part.Number)) + } case exists && !shouldExist && !wipeEntry: return fmt.Errorf("partition %d exists but is specified as nonexistant and wipePartitionEntry is false", part.Number) case exists && !shouldExist && wipeEntry: op.DeletePartition(part.Number) modification = true + if partInUse { + partxDelete = append(partxDelete, uint64(part.Number)) + } case exists && shouldExist && matches: s.Logger.Info("partition %d found with correct specifications", part.Number) case exists && shouldExist && !wipeEntry && !matches: @@ -529,6 +541,9 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { part.StartSector = &info.StartSector op.CreatePartition(part) modification = true + if partInUse { + partxUpdate = append(partxUpdate, uint64(part.Number)) + } } else { return fmt.Errorf("Partition %d didn't match: %v", part.Number, matchErr) } @@ -537,6 +552,9 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { op.DeletePartition(part.Number) op.CreatePartition(part) modification = true + if partInUse { + partxUpdate = append(partxUpdate, uint64(part.Number)) + } default: // unfortunatey, golang doesn't check that all cases are handled exhaustively return fmt.Errorf("Unreachable code reached when processing partition %d. golang--", part.Number) @@ -551,6 +569,28 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { return fmt.Errorf("commit failure: %v", err) } + // In contrast to similar tools, sgdisk does not trigger the update of the + // kernel partition table with BLKPG but only uses BLKRRPART which fails + // as soon as one partition of the disk is mounted + for _, partNr := range partxDelete { + cmd := exec.Command(distro.PartxCmd(), "--delete", "--nr", strconv.FormatUint(partNr, 10), blockDevResolved) + if _, err := s.Logger.LogCmd(cmd, "triggering partition %d deletion on %q", partNr, devAlias); err != nil { + return fmt.Errorf("deleting partition failed: %v", err) + } + } + for _, partNr := range partxUpdate { + cmd := exec.Command(distro.PartxCmd(), "--update", "--nr", strconv.FormatUint(partNr, 10), blockDevResolved) + if _, err := s.Logger.LogCmd(cmd, "triggering partition %d re-read on %q", partNr, devAlias); err != nil { + return fmt.Errorf("updating partition failed: %v", err) + } + } + for _, partNr := range partxAdd { + cmd := exec.Command(distro.PartxCmd(), "--add", "--nr", strconv.FormatUint(partNr, 10), blockDevResolved) + if _, err := s.Logger.LogCmd(cmd, "triggering partition %d addition on %q", partNr, devAlias); err != nil { + return fmt.Errorf("adding partition failed: %v", err) + } + } + // It's best to wait here for the /dev/ABC entries to be // (re)created, not only for other parts of the initramfs but // also because s.waitOnDevices() can still race with udev's