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 Jan 12, 2024
1 parent bc89548 commit 1b80a4c
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ nav_order: 9
### Changes

- Require Go 1.20+
- The Dracut module now installs partx

### Bug fixes

- Force partition table update after repartitioning, even if one partition is already mounted

## Ignition 2.17.0 (2023-11-20)

Starting with this release, ignition-validate binaries are signed with the
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 @@ -39,6 +39,7 @@ install() {
mkfs.fat \
mkfs.xfs \
mkswap \
partx \
sgdisk \
useradd \
userdel \
Expand Down
2 changes: 2 additions & 0 deletions internal/distro/distro.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
groupdelCmd = "groupdel"
mdadmCmd = "mdadm"
mountCmd = "mount"
partxCmd = "partx"
sgdiskCmd = "sgdisk"
modprobeCmd = "modprobe"
udevadmCmd = "udevadm"
Expand Down Expand Up @@ -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 }
Expand Down
33 changes: 33 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,29 @@ 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)
}
}
}

Check failure on line 575 in internal/exec/stages/disks/partitions.go

View workflow job for this annotation

GitHub Actions / Test (1.20.x)

missing return

Check failure on line 575 in internal/exec/stages/disks/partitions.go

View workflow job for this annotation

GitHub Actions / Test (1.21.x)

missing return
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
Expand Down

0 comments on commit 1b80a4c

Please sign in to comment.