mirror of
https://github.com/kairos-io/immucore.git
synced 2025-04-27 11:12:30 +00:00
The default plugin does not support gpt Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
175 lines
7.0 KiB
Go
175 lines
7.0 KiB
Go
/*
|
|
Copyright © 2022 SUSE LLC
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package mocks
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/jaypipes/ghw/pkg/block"
|
|
"github.com/jaypipes/ghw/pkg/context"
|
|
"github.com/jaypipes/ghw/pkg/linuxpath"
|
|
)
|
|
|
|
// GhwMock is used to construct a fake disk to present to ghw when scanning block devices
|
|
// The way this works is ghw will use the existing files in the system to determine the different disks, partitions and
|
|
// mountpoints. It uses /sys/block, /proc/self/mounts and /run/udev/data to gather everything
|
|
// It also has an entrypoint to overwrite the root dir from which the paths are constructed so that allows us to override
|
|
// it easily and make it read from a different location.
|
|
// This mock is used to construct a fake FS with all its needed files on a different chroot and just add a Disk with its
|
|
// partitions and let the struct do its thing creating files and mountpoints and such
|
|
// You can even just pass no disks to simulate a system in which there is no disk/no cos partitions.
|
|
type GhwMock struct {
|
|
chroot string
|
|
paths *linuxpath.Paths
|
|
disks []block.Disk
|
|
mounts []string
|
|
}
|
|
|
|
// AddDisk adds a disk to GhwMock.
|
|
func (g *GhwMock) AddDisk(disk block.Disk) {
|
|
g.disks = append(g.disks, disk)
|
|
}
|
|
|
|
// AddPartitionToDisk will add a partition to the given disk and call Clean+CreateDevices, so we recreate all files
|
|
// It makes no effort checking if the disk exists.
|
|
func (g *GhwMock) AddPartitionToDisk(diskName string, partition *block.Partition) {
|
|
for _, disk := range g.disks {
|
|
if disk.Name == diskName {
|
|
disk.Partitions = append(disk.Partitions, partition)
|
|
g.Clean()
|
|
g.CreateDevices()
|
|
}
|
|
}
|
|
}
|
|
|
|
// CreateDevices will create a new context and paths for ghw using the Chroot value as base, then set the env var GHW_ROOT so the
|
|
// ghw library picks that up and then iterate over the disks and partitions and create the necessary files.
|
|
func (g *GhwMock) CreateDevices() {
|
|
d, _ := os.MkdirTemp("", "ghwmock")
|
|
g.chroot = d
|
|
ctx := context.New()
|
|
ctx.Chroot = d
|
|
g.paths = linuxpath.New(ctx)
|
|
_ = os.Setenv("GHW_CHROOT", g.chroot)
|
|
// Create the /sys/block dir
|
|
_ = os.MkdirAll(g.paths.SysBlock, 0755)
|
|
// Create the /run/udev/data dir
|
|
_ = os.MkdirAll(g.paths.RunUdevData, 0755)
|
|
// Create only the /proc/self dir, we add the mounts file afterwards
|
|
procDir, _ := filepath.Split(g.paths.ProcMounts)
|
|
_ = os.MkdirAll(procDir, 0755)
|
|
|
|
for indexDisk, disk := range g.disks {
|
|
// For each dir we create the /sys/block/DISK_NAME
|
|
diskPath := filepath.Join(g.paths.SysBlock, disk.Name)
|
|
_ = os.Mkdir(diskPath, 0755)
|
|
for indexPart, partition := range disk.Partitions {
|
|
// For each partition we create the /sys/block/DISK_NAME/PARTITION_NAME
|
|
_ = os.Mkdir(filepath.Join(diskPath, partition.Name), 0755)
|
|
// Create the /sys/block/DISK_NAME/PARTITION_NAME/dev file which contains the major:minor of the partition
|
|
_ = os.WriteFile(filepath.Join(diskPath, partition.Name, "dev"), []byte(fmt.Sprintf("%d:6%d\n", indexDisk, indexPart)), 0644)
|
|
// Create the /run/udev/data/bMAJOR:MINOR file with the data inside to mimic the udev database
|
|
data := []string{fmt.Sprintf("E:ID_PART_ENTRY_NAME=%s\n", partition.FilesystemLabel)}
|
|
data = append(data, fmt.Sprintf("E:ID_FS_LABEL=%s\n", partition.Label))
|
|
if partition.Type != "" {
|
|
data = append(data, fmt.Sprintf("E:ID_FS_TYPE=%s\n", partition.Type))
|
|
}
|
|
_ = os.WriteFile(filepath.Join(g.paths.RunUdevData, fmt.Sprintf("b%d:6%d", indexDisk, indexPart)), []byte(strings.Join(data, "")), 0644)
|
|
// If we got a mountpoint, add it to our fake /proc/self/mounts
|
|
if partition.MountPoint != "" {
|
|
// Check if the partition has a fs, otherwise default to ext4
|
|
if partition.Type == "" {
|
|
partition.Type = "ext4"
|
|
}
|
|
// Prepare the g.mounts with all the mount lines
|
|
g.mounts = append(
|
|
g.mounts,
|
|
fmt.Sprintf("%s %s %s ro,relatime 0 0\n", filepath.Join("/dev", partition.Name), partition.MountPoint, partition.Type))
|
|
}
|
|
}
|
|
}
|
|
// Finally, write all the mounts
|
|
_ = os.WriteFile(g.paths.ProcMounts, []byte(strings.Join(g.mounts, "")), 0644)
|
|
}
|
|
|
|
// RemoveDisk will remove the files for a disk. It makes no effort to check if the disk exists or not.
|
|
func (g *GhwMock) RemoveDisk(disk string) {
|
|
// This could be simpler I think, just removing the /sys/block/DEVICE should make ghw not find anything and not search
|
|
// for partitions, but just in case do it properly
|
|
var newMounts []string
|
|
diskPath := filepath.Join(g.paths.SysBlock, disk)
|
|
_ = os.RemoveAll(diskPath)
|
|
|
|
// Try to find any mounts that match the disk given and remove them from the mounts
|
|
for _, mount := range g.mounts {
|
|
fields := strings.Fields(mount)
|
|
// If first field does not contain the /dev/DEVICE, add it to the newmounts
|
|
if !strings.Contains(fields[0], filepath.Join("/dev", disk)) {
|
|
newMounts = append(newMounts, mount)
|
|
}
|
|
}
|
|
g.mounts = newMounts
|
|
// Write the mounts again
|
|
_ = os.WriteFile(g.paths.ProcMounts, []byte(strings.Join(g.mounts, "")), 0644)
|
|
}
|
|
|
|
// RemovePartitionFromDisk will remove the files for a partition
|
|
// It makes no effort checking if the disk/partition/files exist.
|
|
func (g *GhwMock) RemovePartitionFromDisk(diskName string, partitionName string) {
|
|
var newMounts []string
|
|
diskPath := filepath.Join(g.paths.SysBlock, diskName)
|
|
// Read the dev major:minor
|
|
devName, _ := os.ReadFile(filepath.Join(diskPath, partitionName, "dev"))
|
|
// Remove the MAJOR:MINOR file from the udev database
|
|
_ = os.RemoveAll(filepath.Join(g.paths.RunUdevData, fmt.Sprintf("b%s", devName)))
|
|
// Remove the /sys/block/DISK/PARTITION dir
|
|
_ = os.RemoveAll(filepath.Join(diskPath, partitionName))
|
|
|
|
// Try to find any mounts that match the partition given and remove them from the mounts
|
|
for _, mount := range g.mounts {
|
|
fields := strings.Fields(mount)
|
|
// If first field does not contain the /dev/PARTITION, add it to the newmounts
|
|
if !strings.Contains(fields[0], filepath.Join("/dev", partitionName)) {
|
|
newMounts = append(newMounts, mount)
|
|
}
|
|
}
|
|
g.mounts = newMounts
|
|
// Write the mounts again
|
|
_ = os.WriteFile(g.paths.ProcMounts, []byte(strings.Join(g.mounts, "")), 0644)
|
|
// Remove it from the partitions list
|
|
for index, disk := range g.disks {
|
|
if disk.Name == diskName {
|
|
var newPartitions []*block.Partition
|
|
for _, partition := range disk.Partitions {
|
|
if partition.Name != partitionName {
|
|
newPartitions = append(newPartitions, partition)
|
|
}
|
|
}
|
|
g.disks[index].Partitions = newPartitions
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean will remove the chroot dir and unset the env var.
|
|
func (g *GhwMock) Clean() {
|
|
_ = os.Unsetenv("GHW_CHROOT")
|
|
_ = os.RemoveAll(g.chroot)
|
|
}
|