mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-04-28 03:32:27 +00:00
147 lines
4.3 KiB
Go
147 lines
4.3 KiB
Go
package partitioner
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/diskfs/go-diskfs"
|
|
"github.com/diskfs/go-diskfs/disk"
|
|
"github.com/diskfs/go-diskfs/partition"
|
|
"github.com/diskfs/go-diskfs/partition/gpt"
|
|
"github.com/gofrs/uuid"
|
|
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
|
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
|
sdkTypes "github.com/kairos-io/kairos-sdk/types"
|
|
"github.com/sanity-io/litter"
|
|
)
|
|
|
|
type Disk struct {
|
|
*disk.Disk
|
|
logger sdkTypes.KairosLogger
|
|
}
|
|
|
|
func (d *Disk) NewPartitionTable(partType string, parts sdkTypes.PartitionList) error {
|
|
d.logger.Infof("Creating partition table for partition type %s", partType)
|
|
var table partition.Table
|
|
switch partType {
|
|
case v1.GPT:
|
|
table = &gpt.Table{
|
|
ProtectiveMBR: true,
|
|
GUID: cnst.DiskUUID, // Set know predictable UUID
|
|
Partitions: kairosPartsToDiskfsGPTParts(parts, d.Size, d.LogicalBlocksize),
|
|
LogicalSectorSize: int(d.LogicalBlocksize),
|
|
PhysicalSectorSize: int(d.PhysicalBlocksize),
|
|
}
|
|
default:
|
|
return fmt.Errorf("invalid partition type: %s", partType)
|
|
}
|
|
err := d.Partition(table)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.logger.Infof("Created partition table for partition type %s", partType)
|
|
return nil
|
|
}
|
|
|
|
func getSectorEndFromSize(start, size uint64, sectorSize int64) uint64 {
|
|
return (size / uint64(sectorSize)) + start - 1
|
|
}
|
|
|
|
func kairosPartsToDiskfsGPTParts(parts sdkTypes.PartitionList, diskSize int64, sectorSize int64) []*gpt.Partition {
|
|
var partitions []*gpt.Partition
|
|
for index, part := range parts {
|
|
var start uint64
|
|
var end uint64
|
|
var size uint64
|
|
if len(partitions) == 0 {
|
|
// first partition, align to 1Mb
|
|
start = 1024 * 1024 / uint64(sectorSize)
|
|
} else {
|
|
// get latest partition end, sum 1
|
|
start = partitions[len(partitions)-1].End + 1
|
|
}
|
|
|
|
// part.Size 0 means take over whats left on the disk
|
|
if part.Size == 0 {
|
|
// Remember to add the 1Mb alignment to total size
|
|
// This will be on bytes already no need to transform it
|
|
var sizeUsed = uint64(1024 * 1024)
|
|
for _, p := range partitions {
|
|
sizeUsed = sizeUsed + p.Size
|
|
}
|
|
// leave 1Mb at the end for backup GPT header
|
|
size = uint64(diskSize) - sizeUsed - uint64(1024*1024)
|
|
} else {
|
|
// Change it to bytes
|
|
// If its the last partition to do, leave 1 Mb at the end for backup GPT header
|
|
if index == len(parts)-1 {
|
|
size = uint64(part.Size*1024*1024) - uint64(1024*1024)
|
|
} else {
|
|
size = uint64(part.Size * 1024 * 1024)
|
|
}
|
|
|
|
}
|
|
|
|
end = getSectorEndFromSize(start, size, sectorSize)
|
|
|
|
if part.Name == cnst.EfiPartName && part.FS == cnst.EfiFs {
|
|
// EFI boot partition
|
|
partitions = append(partitions, &gpt.Partition{
|
|
Start: start,
|
|
End: end,
|
|
Type: gpt.EFISystemPartition,
|
|
Size: size, // partition size in bytes
|
|
GUID: uuid.NewV5(uuid.NamespaceURL, part.FilesystemLabel).String(), // set know predictable UUID
|
|
Name: part.Name,
|
|
Attributes: 0x1, // system partition flag
|
|
})
|
|
} else if part.Name == cnst.BiosPartName {
|
|
// Non-EFI boot partition
|
|
partitions = append(partitions, &gpt.Partition{
|
|
Start: start,
|
|
End: end,
|
|
Type: gpt.BIOSBoot,
|
|
Size: size, // partition size in bytes
|
|
GUID: uuid.NewV5(uuid.NamespaceURL, part.FilesystemLabel).String(), // set know predictable UUID
|
|
Name: part.Name,
|
|
Attributes: 0x4, // legacy bios bootable flag
|
|
})
|
|
} else {
|
|
// Other partitions
|
|
partitions = append(partitions, &gpt.Partition{
|
|
Start: start,
|
|
End: end,
|
|
Type: gpt.LinuxFilesystem,
|
|
Size: size,
|
|
GUID: uuid.NewV5(uuid.NamespaceURL, part.FilesystemLabel).String(),
|
|
Name: part.Name,
|
|
})
|
|
}
|
|
}
|
|
return partitions
|
|
}
|
|
|
|
type DiskOptions func(d *Disk) error
|
|
|
|
func WithLogger(logger sdkTypes.KairosLogger) func(d *Disk) error {
|
|
return func(d *Disk) error {
|
|
d.logger = logger
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func NewDisk(device string, opts ...DiskOptions) (*Disk, error) {
|
|
d, err := diskfs.Open(device)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dev := &Disk{d, sdkTypes.NewKairosLogger("partitioner", "info", false)}
|
|
|
|
for _, opt := range opts {
|
|
if err := opt(dev); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
dev.logger.Debugf("Initialized new disk from device %s", litter.Sdump(dev))
|
|
return dev, nil
|
|
}
|