debian-forge-composer/internal/osbuild2/disk.go
Christian Kellner 9e5b265a58 osbuild2: lock loopback devices during sfdisk, mkfs
Since udev will probe block devices it is advisable to hold a lock
on the device when modifying its partition table or the superblock
of the filesystem (see [1]). osbuild loopback devices do support
this via the `lock` option. Set this option for all operation that
involve changing block device "metadata" that could potentionally
race with udev, such as sfdisk, mkfs, creating a luks2 container
and creating LVM2 volume groups and logical volumes.
NB: osbuild also has its own device inhibition logic to prevent
udev/lvm2 from auto activating devices and in general to limit the
interaction between the host and devices used by osbuild. See [2]
for more information.
NB: this also locks the loopback device in situation where we the
it is strickly not the right thing to do, e.g. when creating a fs
on a logical voume that is located on a loopback device, since in
this case the device we would need to lock is the logical volume.
Sadly, LVM/DM devices are exempt from block device locking. But,
due to a bug in osbuild < 50, the udev inhibitor does *not* work
for loopback devices and therefore we have to use the actual lock
to preven LVM device auto-activation via `69-dm-lvm-metad.rules`.
The change was implemented by adding a new boolean to `getDevices`
indicating if the loopback device should be locked or not. Once
we depend on osbuild 50 we can change the logic in `getDevices`
to only lock the loopback device if the number of devices is one,
i.e. we are working directly on the loopback device.

[1] https://systemd.io/BLOCK_DEVICE_LOCKING/
[2] /usr/lib/udev/rules.d/10-osbuild-inhibitor.rules
2022-02-28 17:09:30 +01:00

84 lines
2 KiB
Go

package osbuild2
import (
"fmt"
"github.com/osbuild/osbuild-composer/internal/disk"
)
// sfdiskStageOptions creates the options and devices properties for an
// org.osbuild.sfdisk stage based on a partition table description
func sfdiskStageOptions(pt *disk.PartitionTable) *SfdiskStageOptions {
partitions := make([]Partition, len(pt.Partitions))
for idx, p := range pt.Partitions {
partitions[idx] = Partition{
Bootable: p.Bootable,
Start: pt.BytesToSectors(p.Start),
Size: pt.BytesToSectors(p.Size),
Type: p.Type,
UUID: p.UUID,
}
}
stageOptions := &SfdiskStageOptions{
Label: pt.Type,
UUID: pt.UUID,
Partitions: partitions,
}
return stageOptions
}
func GenImagePrepareStages(pt *disk.PartitionTable, filename string) []*Stage {
stages := make([]*Stage, 0)
// create an empty file of the given size via `org.osbuild.truncate`
stage := NewTruncateStage(
&TruncateStageOptions{
Filename: filename,
Size: fmt.Sprintf("%d", pt.Size),
})
stages = append(stages, stage)
// create the partition layout in the empty file
sfOptions := sfdiskStageOptions(pt)
loopback := NewLoopbackDevice(
&LoopbackDeviceOptions{
Filename: filename,
Lock: true,
},
)
sfdisk := NewSfdiskStage(sfOptions, loopback)
stages = append(stages, sfdisk)
// Generate all the needed "devices", like LUKS2 and LVM2
s := GenDeviceCreationStages(pt, filename)
stages = append(stages, s...)
// Generate all the filesystems on partitons and devices
s = GenMkfsStages(pt, loopback)
stages = append(stages, s...)
return stages
}
func GenImageFinishStages(pt *disk.PartitionTable, filename string) []*Stage {
return GenDeviceFinishStages(pt, filename)
}
func GenImageKernelOptions(pt *disk.PartitionTable) []string {
cmdline := make([]string, 0)
genOptions := func(e disk.Entity, path []disk.Entity) error {
switch ent := e.(type) {
case *disk.LUKSContainer:
karg := "luks.uuid=" + ent.UUID
cmdline = append(cmdline, karg)
}
return nil
}
_ = pt.ForEachEntity(genOptions)
return cmdline
}