Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/osbuild-playground/my-image.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (img *MyImage) InstantiateManifest(m *manifest.Manifest,
}

// TODO: add helper
pt, err := disk.NewPartitionTable(&basePT, nil, 0, partition.RawPartitioningMode, platform.GetArch(), nil, rng)
pt, err := disk.NewPartitionTable(&basePT, nil, 0, partition.RawPartitioningMode, platform.GetArch(), nil, "", rng)
if err != nil {
panic(err)
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/disk/btrfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type Btrfs struct {
Subvolumes []BtrfsSubvolume `json:"subvolumes,omitempty" yaml:"subvolumes,omitempty"`
}

var _ = MountpointCreator(&Btrfs{})

func init() {
payloadEntityMap["btrfs"] = reflect.TypeOf(Btrfs{})
}
Expand Down Expand Up @@ -58,7 +60,11 @@ func (b *Btrfs) GetItemCount() uint {
func (b *Btrfs) GetChild(n uint) Entity {
return &b.Subvolumes[n]
}
func (b *Btrfs) CreateMountpoint(mountpoint string, size uint64) (Entity, error) {
func (b *Btrfs) CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error) {
if defaultFs != "btrfs" {
return nil, fmt.Errorf("only btrfs mountpoints are supported with btrfs subvolumes not %q", defaultFs)
}

name := mountpoint
if name == "/" {
name = "root"
Expand Down
6 changes: 4 additions & 2 deletions pkg/disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,11 @@ type FSTabEntity interface {
// A MountpointCreator is a container that is able to create new volumes.
//
// CreateMountpoint creates a new mountpoint with the given size and
// returns the entity that represents the new mountpoint.
// default filesystem and returns the entity that represents the new
// mountpoint. The defaultFs is only a hint and can be ignored by
// e.g. btrfs subvolumes.
type MountpointCreator interface {
CreateMountpoint(mountpoint string, size uint64) (Entity, error)
CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error)

// AlignUp will align the given bytes according to the
// requirements of the container type.
Expand Down
47 changes: 33 additions & 14 deletions pkg/disk/disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func TestDynamicallyResizePartitionTable(t *testing.T) {
// math/rand is good enough in this case
/* #nosec G404 */
rng := rand.New(rand.NewSource(0))
newpt, err := disk.NewPartitionTable(&pt, mountpoints, 1024, partition.RawPartitioningMode, arch.ARCH_AARCH64, nil, rng)
newpt, err := disk.NewPartitionTable(&pt, mountpoints, 1024, partition.RawPartitioningMode, arch.ARCH_AARCH64, nil, "", rng)
assert.NoError(t, err)
assert.GreaterOrEqual(t, newpt.Size, expectedSize)
}
Expand Down Expand Up @@ -194,7 +194,10 @@ func TestCreatePartitionTable(t *testing.T) {
if ptName == "luks+lvm" {
ptMode = partition.AutoLVMPartitioningMode
}
mpt, err := disk.NewPartitionTable(&pt, bp, uint64(13*MiB), ptMode, arch.ARCH_PPC64LE, nil, rng)
if ptName == "btrfs" {
ptMode = partition.BtrfsPartitioningMode
}
mpt, err := disk.NewPartitionTable(&pt, bp, uint64(13*MiB), ptMode, arch.ARCH_PPC64LE, nil, "", rng)
require.NoError(t, err, "Partition table generation failed: PT %q BP %q (%s)", ptName, bpName, err)
assert.NotNil(mpt, "Partition table generation failed: PT %q BP %q (nil partition table)", ptName, bpName)
assert.Greater(mpt.GetSize(), sumSizes(bp))
Expand All @@ -218,12 +221,12 @@ func TestCreatePartitionTableLVMify(t *testing.T) {
for bpName, tbp := range testBlueprints {
for ptName, pt := range testdisk.TestPartitionTables() {
if tbp != nil && (ptName == "btrfs" || ptName == "luks") {
_, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng)
_, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng)
assert.Error(err, "PT %q BP %q: should return an error with LVMPartitioningMode", ptName, bpName)
continue
}

mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng)
mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng)
assert.NoError(err, "PT %q BP %q: Partition table generation failed: (%s)", ptName, bpName, err)

rootPath := disk.EntityPath(mpt, "/")
Expand Down Expand Up @@ -254,12 +257,12 @@ func TestCreatePartitionTableBtrfsify(t *testing.T) {
for bpName, tbp := range testBlueprints {
for ptName, pt := range testdisk.TestPartitionTables() {
if ptName == "auto-lvm" || ptName == "luks" || ptName == "luks+lvm" {
_, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, rng)
_, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, "", rng)
assert.Error(err, "PT %q BP %q: should return an error with BtrfsPartitioningMode", ptName, bpName)
continue
}

mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, rng)
mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, "", rng)
assert.NoError(err, "PT %q BP %q: Partition table generation failed: (%s)", ptName, bpName, err)

rootPath := disk.EntityPath(mpt, "/")
Expand Down Expand Up @@ -290,12 +293,12 @@ func TestCreatePartitionTableLVMOnly(t *testing.T) {
for bpName, tbp := range testBlueprints {
for ptName, pt := range testdisk.TestPartitionTables() {
if ptName == "btrfs" || ptName == "luks" {
_, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.LVMPartitioningMode, arch.ARCH_S390X, nil, rng)
_, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.LVMPartitioningMode, arch.ARCH_S390X, nil, "", rng)
assert.Error(err, "PT %q BP %q: should return an error with LVMPartitioningMode", ptName, bpName)
continue
}

mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.LVMPartitioningMode, arch.ARCH_S390X, nil, rng)
mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), partition.LVMPartitioningMode, arch.ARCH_S390X, nil, "", rng)
require.NoError(t, err, "PT %q BP %q: Partition table generation failed: (%s)", ptName, bpName, err)

rootPath := disk.EntityPath(mpt, "/")
Expand Down Expand Up @@ -447,7 +450,7 @@ func TestMinimumSizes(t *testing.T) {

for idx, tc := range testCases {
{ // without LVM
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.RawPartitioningMode, arch.ARCH_X86_64, nil, rng)
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.RawPartitioningMode, arch.ARCH_X86_64, nil, "", rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := disk.EntityPath(mpt, mnt)
Expand All @@ -461,7 +464,7 @@ func TestMinimumSizes(t *testing.T) {
}

{ // with LVM
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng)
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := disk.EntityPath(mpt, mnt)
Expand Down Expand Up @@ -548,7 +551,7 @@ func TestLVMExtentAlignment(t *testing.T) {
}

for idx, tc := range testCases {
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng)
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng)
assert.NoError(err)
for mnt, expSize := range tc.ExpectedSizes {
path := disk.EntityPath(mpt, mnt)
Expand Down Expand Up @@ -577,7 +580,7 @@ func TestNewBootWithSizeLVMify(t *testing.T) {
},
}

mpt, err := disk.NewPartitionTable(&pt, custom, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_AARCH64, nil, rng)
mpt, err := disk.NewPartitionTable(&pt, custom, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_AARCH64, nil, "", rng)
assert.NoError(err)

for idx, c := range custom {
Expand Down Expand Up @@ -920,7 +923,7 @@ func TestMinimumSizesWithRequiredSizes(t *testing.T) {

for idx, tc := range testCases {
{ // without LVM
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.RawPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, rng)
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.RawPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, "", rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := disk.EntityPath(mpt, mnt)
Expand All @@ -934,7 +937,7 @@ func TestMinimumSizesWithRequiredSizes(t *testing.T) {
}

{ // with LVM
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, rng)
mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), partition.AutoLVMPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, "", rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := disk.EntityPath(mpt, mnt)
Expand Down Expand Up @@ -1056,3 +1059,19 @@ func TestForEachMountable(t *testing.T) {
})
}
}

func TestCreatePartitionTableDefaultFs(t *testing.T) {
// math/rand is good enough in this case
/* #nosec G404 */
rng := rand.New(rand.NewSource(13))

pt := testdisk.TestPartitionTables()["plain"]
fsCust := []blueprint.FilesystemCustomization{
{Mountpoint: "/var/stuff", MinSize: 10 * datasizes.GiB},
}

mpt, err := disk.NewPartitionTable(&pt, fsCust, 0, partition.RawPartitioningMode, arch.ARCH_X86_64, nil, "ext4", rng)
assert.NoError(t, err)
assert.Equal(t, "/var/stuff", mpt.Partitions[4].Payload.(*disk.Filesystem).Mountpoint)
assert.Equal(t, "ext4", mpt.Partitions[4].Payload.(*disk.Filesystem).Type)
}
9 changes: 7 additions & 2 deletions pkg/disk/lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type LVMVolumeGroup struct {
LogicalVolumes []LVMLogicalVolume `json:"logical_volumes,omitempty" yaml:"logical_volumes,omitempty"`
}

var _ = MountpointCreator(&LVMVolumeGroup{})

func init() {
payloadEntityMap["lvm"] = reflect.TypeOf(LVMVolumeGroup{})
}
Expand Down Expand Up @@ -73,10 +75,13 @@ func (vg *LVMVolumeGroup) GetChild(n uint) Entity {
return &vg.LogicalVolumes[n]
}

func (vg *LVMVolumeGroup) CreateMountpoint(mountpoint string, size uint64) (Entity, error) {
func (vg *LVMVolumeGroup) CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error) {
if defaultFs == "btrfs" {
return nil, fmt.Errorf("btrfs under lvm is not supported")
}

filesystem := Filesystem{
Type: "xfs",
Type: defaultFs,
Mountpoint: mountpoint,
FSTabOptions: "defaults",
FSTabFreq: 0,
Expand Down
10 changes: 5 additions & 5 deletions pkg/disk/lvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,27 @@ func TestLVMVCreateMountpoint(t *testing.T) {
Description: "root volume group",
}

entity, err := vg.CreateMountpoint("/", 0)
entity, err := vg.CreateMountpoint("/", "", 0)
assert.NoError(err)
rootlv := entity.(*LVMLogicalVolume)
assert.Equal("rootlv", rootlv.Name)

_, err = vg.CreateMountpoint("/home_test", 0)
_, err = vg.CreateMountpoint("/home_test", "", 0)
assert.NoError(err)

entity, err = vg.CreateMountpoint("/home/test", 0)
entity, err = vg.CreateMountpoint("/home/test", "", 0)
assert.NoError(err)

dedup := entity.(*LVMLogicalVolume)
assert.Equal("home_testlv00", dedup.Name)

// Lets collide it
for i := 0; i < 99; i++ {
_, err = vg.CreateMountpoint("/home/test", 0)
_, err = vg.CreateMountpoint("/home/test", "", 0)
assert.NoError(err)
}

_, err = vg.CreateMountpoint("/home/test", 0)
_, err = vg.CreateMountpoint("/home/test", "", 0)
assert.Error(err)
}

Expand Down
38 changes: 23 additions & 15 deletions pkg/disk/partition_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type PartitionTable struct {
StartOffset uint64 `json:"start_offset,omitempty" yaml:"start_offset,omitempty"`
}

var _ = MountpointCreator(&PartitionTable{})

// DefaultBootPartitionSize is the default size of the /boot partition if it
// needs to be auto-created. This happens if the custom partitioning don't
// specify one, but the image requires one to boot (/ is on btrfs, or an LV).
Expand Down Expand Up @@ -90,15 +92,20 @@ const DefaultBootPartitionSize = 1 * datasizes.GiB
// containing the root filesystem is grown to fill any left over space on the
// partition table. Logical Volumes are not grown to fill the space in the
// Volume Group since they are trivial to grow on a live system.
func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, mode partition.PartitioningMode, architecture arch.Arch, requiredSizes map[string]uint64, rng *rand.Rand) (*PartitionTable, error) {
func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, mode partition.PartitioningMode, architecture arch.Arch, requiredSizes map[string]uint64, defaultFs string, rng *rand.Rand) (*PartitionTable, error) {
newPT := basePT.Clone().(*PartitionTable)

if basePT.features().LVM && (mode == partition.RawPartitioningMode || mode == partition.BtrfsPartitioningMode) {
return nil, fmt.Errorf("%s partitioning mode set for a base partition table with LVM, this is unsupported", mode)
}

// compatibility with previous versions of images, if no
// default filesystem is given we pick "xfs"
if defaultFs == "" {
defaultFs = "xfs"
}
// first pass: enlarge existing mountpoints and collect new ones
newMountpoints, _ := newPT.applyCustomization(mountpoints, false)
newMountpoints, _ := newPT.applyCustomization(mountpoints, defaultFs, false)

var ensureLVM, ensureBtrfs bool
switch mode {
Expand All @@ -110,24 +117,25 @@ func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.Filesyste
ensureLVM = len(newMountpoints) > 0
case partition.BtrfsPartitioningMode:
ensureBtrfs = true
defaultFs = "btrfs"
default:
return nil, fmt.Errorf("unsupported partitioning mode %q", mode)
}
if ensureLVM {
err := newPT.ensureLVM()
err := newPT.ensureLVM(defaultFs)
if err != nil {
return nil, err
}
} else if ensureBtrfs {
err := newPT.ensureBtrfs(architecture)
err := newPT.ensureBtrfs(defaultFs, architecture)
if err != nil {
return nil, err
}
}

// second pass: deal with new mountpoints and newly created ones, after switching to
// the LVM layout, if requested, which might introduce new mount points, i.e. `/boot`
_, err := newPT.applyCustomization(newMountpoints, true)
_, err := newPT.applyCustomization(newMountpoints, defaultFs, true)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -336,9 +344,9 @@ func (pt *PartitionTable) EnsureDirectorySizes(dirSizeMap map[string]uint64) {
}
}

func (pt *PartitionTable) CreateMountpoint(mountpoint string, size uint64) (Entity, error) {
func (pt *PartitionTable) CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error) {
filesystem := Filesystem{
Type: "xfs",
Type: defaultFs,
Mountpoint: mountpoint,
FSTabOptions: "defaults",
FSTabFreq: 0,
Expand Down Expand Up @@ -437,7 +445,7 @@ func (pt *PartitionTable) HeaderSize() uint64 {
// return a list of left-over mountpoints (i.e. mountpoints in the input that
// were not created). An error can only occur if create is set.
// Does not relayout the table, i.e. a call to relayout might be needed.
func (pt *PartitionTable) applyCustomization(mountpoints []blueprint.FilesystemCustomization, create bool) ([]blueprint.FilesystemCustomization, error) {
func (pt *PartitionTable) applyCustomization(mountpoints []blueprint.FilesystemCustomization, defaultFs string, create bool) ([]blueprint.FilesystemCustomization, error) {

newMountpoints := []blueprint.FilesystemCustomization{}

Expand All @@ -449,7 +457,7 @@ func (pt *PartitionTable) applyCustomization(mountpoints []blueprint.FilesystemC
} else {
if !create {
newMountpoints = append(newMountpoints, mnt)
} else if err := pt.createFilesystem(mnt.Mountpoint, size); err != nil {
} else if err := pt.createFilesystem(mnt.Mountpoint, defaultFs, size); err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -523,7 +531,7 @@ func (pt *PartitionTable) relayout(size uint64) uint64 {
return start
}

func (pt *PartitionTable) createFilesystem(mountpoint string, size uint64) error {
func (pt *PartitionTable) createFilesystem(mountpoint, defaultFs string, size uint64) error {
rootPath := entityPath(pt, "/")
if rootPath == nil {
panic("no root mountpoint for PartitionTable")
Expand All @@ -543,7 +551,7 @@ func (pt *PartitionTable) createFilesystem(mountpoint string, size uint64) error
panic("could not find root volume container")
}

newVol, err := vc.CreateMountpoint(mountpoint, 0)
newVol, err := vc.CreateMountpoint(mountpoint, defaultFs, 0)
if err != nil {
return fmt.Errorf("failed creating volume: %w", err)
}
Expand Down Expand Up @@ -722,7 +730,7 @@ func (pt *PartitionTable) GenUUID(rng *rand.Rand) {

// ensureLVM will ensure that the root partition is on an LVM volume, i.e. if
// it currently is not, it will wrap it in one
func (pt *PartitionTable) ensureLVM() error {
func (pt *PartitionTable) ensureLVM(defaultFS string) error {

rootPath := entityPath(pt, "/")
if rootPath == nil {
Expand All @@ -732,7 +740,7 @@ func (pt *PartitionTable) ensureLVM() error {
// we need a /boot partition to boot LVM, ensure one exists
bootPath := entityPath(pt, "/boot")
if bootPath == nil {
_, err := pt.CreateMountpoint("/boot", DefaultBootPartitionSize)
_, err := pt.CreateMountpoint("/boot", defaultFS, DefaultBootPartitionSize)

if err != nil {
return err
Expand Down Expand Up @@ -781,7 +789,7 @@ func (pt *PartitionTable) ensureLVM() error {

// ensureBtrfs will ensure that the root partition is on a btrfs subvolume, i.e. if
// it currently is not, it will wrap it in one
func (pt *PartitionTable) ensureBtrfs(architecture arch.Arch) error {
func (pt *PartitionTable) ensureBtrfs(defaultFS string, architecture arch.Arch) error {

rootPath := entityPath(pt, "/")
if rootPath == nil {
Expand All @@ -791,7 +799,7 @@ func (pt *PartitionTable) ensureBtrfs(architecture arch.Arch) error {
// we need a /boot partition to boot btrfs, ensure one exists
bootPath := entityPath(pt, "/boot")
if bootPath == nil {
_, err := pt.CreateMountpoint("/boot", DefaultBootPartitionSize)
_, err := pt.CreateMountpoint("/boot", defaultFS, DefaultBootPartitionSize)
if err != nil {
return fmt.Errorf("failed to create /boot partition when ensuring btrfs: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/distro/bootc/partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (t *BootcImageType) genPartitionTableFsCust(basept *disk.PartitionTable, fs
}
fsCustomizations := updateFilesystemSizes(fsCust, rootfsMinSize)

pt, err := disk.NewPartitionTable(basept, fsCustomizations, DEFAULT_SIZE, partitioningMode, t.arch.arch, nil, rng)
pt, err := disk.NewPartitionTable(basept, fsCustomizations, DEFAULT_SIZE, partitioningMode, t.arch.arch, nil, t.arch.distro.defaultFs, rng)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading