Skip to content

Commit

Permalink
sgdisk: Run partx after partition changes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pothos committed Dec 18, 2023
1 parent 85851a8 commit 3a4cab0
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions dracut/30ignition/module-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ install() {
mkfs.xfs \
mkswap \
sgdisk \
partx \
useradd \
userdel \
usermod \
Expand Down
2 changes: 2 additions & 0 deletions internal/distro/distro.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
mdadmCmd = "mdadm"
mountCmd = "mount"
sgdiskCmd = "sgdisk"
partxCmd = "partx"
modprobeCmd = "modprobe"
udevadmCmd = "udevadm"
usermodCmd = "usermod"
Expand Down Expand Up @@ -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 }
Expand Down
34 changes: 34 additions & 0 deletions internal/exec/stages/disks/partitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
Expand All @@ -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"
)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -511,11 +517,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:
Expand All @@ -529,6 +537,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)
}
Expand All @@ -537,6 +546,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)
Expand All @@ -551,6 +561,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 {
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
Expand Down

0 comments on commit 3a4cab0

Please sign in to comment.