From 0c8f970b0fc90d7e1609444fb7d37d47a0710e38 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 | 3 +++ dracut/30ignition/module-setup.sh | 1 + internal/distro/distro.go | 2 ++ internal/exec/stages/disks/partitions.go | 34 ++++++++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 7dcecbd21c..d7bb4c4c0b 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -11,9 +11,12 @@ nav_order: 9 ### Features - Support Akamai Connected Cloud (Linode) +- Support partitioning disk with mounted partitions ### Changes +- The Dracut module now installs partx + ### Bug fixes ## Ignition 2.18.0 (2024-03-01) diff --git a/dracut/30ignition/module-setup.sh b/dracut/30ignition/module-setup.sh index 51bec7e791..8edec97287 100755 --- a/dracut/30ignition/module-setup.sh +++ b/dracut/30ignition/module-setup.sh @@ -39,6 +39,7 @@ install() { mkfs.fat \ mkfs.xfs \ mkswap \ + partx \ sgdisk \ useradd \ userdel \ diff --git a/internal/distro/distro.go b/internal/distro/distro.go index 61ca87aed9..12855e2b91 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -36,6 +36,7 @@ var ( groupdelCmd = "groupdel" mdadmCmd = "mdadm" mountCmd = "mount" + partxCmd = "partx" sgdiskCmd = "sgdisk" modprobeCmd = "modprobe" udevadmCmd = "udevadm" @@ -89,6 +90,7 @@ func GroupaddCmd() string { return groupaddCmd } func GroupdelCmd() string { return groupdelCmd } func MdadmCmd() string { return mdadmCmd } func MountCmd() string { return mountCmd } +func PartxCmd() string { return partxCmd } func SgdiskCmd() string { return sgdiskCmd } func ModprobeCmd() string { return modprobeCmd } func UdevadmCmd() string { return udevadmCmd } diff --git a/internal/exec/stages/disks/partitions.go b/internal/exec/stages/disks/partitions.go index a01074044a..f4e3ed2ece 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" iutil "github.com/coreos/ignition/v2/internal/util" @@ -484,6 +486,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) @@ -504,11 +510,13 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { case !exists && shouldExist: op.CreatePartition(part) modification = true + 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 + 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: @@ -522,6 +530,7 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { part.StartSector = &info.StartSector op.CreatePartition(part) modification = true + partxUpdate = append(partxUpdate, uint64(part.Number)) } else { return fmt.Errorf("Partition %d didn't match: %v", part.Number, matchErr) } @@ -530,6 +539,7 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error { op.DeletePartition(part.Number) op.CreatePartition(part) modification = true + 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) @@ -544,6 +554,30 @@ 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 + if len(activeParts) > 0 { + runPartxCommand := func(op string, partitions []uint64) error { + for _, partNr := range partitions { + cmd := exec.Command(distro.PartxCmd(), "--"+op, "--nr", strconv.FormatUint(partNr, 10), blockDevResolved) + if _, err := s.Logger.LogCmd(cmd, "triggering partition %d %s on %q", partNr, op, devAlias); err != nil { + return fmt.Errorf("partition %s failed: %v", op, err) + } + } + return nil + } + if err := runPartxCommand("delete", partxDelete); err != nil { + return err + } + if err := runPartxCommand("update", partxUpdate); err != nil { + return err + } + if err := runPartxCommand("add", partxAdd); err != nil { + return 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