mirror of
https://github.com/clearlinux/clr-installer.git
synced 2026-09-05 13:11:28 +00:00
storage: refactor for manual partitioning
Always store the current partition table, with free space, in the disk block device information. Find free space from the already loaded partition table. This information will be used for manual disk partitioning. Use separate mkpart and format flags Signed-off-by: Mark D Horn <mark.d.horn@intel.com>
This commit is contained in:
@@ -211,7 +211,7 @@ func Install(rootDir string, model *model.SystemInstall, options args.Args) erro
|
||||
}
|
||||
|
||||
// Do not overwrite File System content for pre-existing
|
||||
if !ch.IsUserDefined() {
|
||||
if !ch.FormatPartition {
|
||||
msg := fmt.Sprintf("Skipping new file system for %s", ch.Name)
|
||||
log.Debug(msg)
|
||||
continue
|
||||
|
||||
+316
-53
@@ -240,11 +240,33 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
|
||||
var start uint64
|
||||
maxFound := false
|
||||
|
||||
// First remove any user removed partitions
|
||||
log.Debug("WritePartitionTable: remove partitions : %v", bd.removedParts)
|
||||
if len(bd.removedParts) > 0 {
|
||||
rmArgs := []string{
|
||||
"parted",
|
||||
"-a",
|
||||
"optimal",
|
||||
bd.GetDeviceFile(),
|
||||
"unit", "MB",
|
||||
"--script",
|
||||
"--",
|
||||
}
|
||||
for _, curr := range bd.removedParts {
|
||||
rmArgs = append(rmArgs, fmt.Sprintf("rm %d", curr))
|
||||
}
|
||||
err = cmd.RunAndLog(rmArgs...)
|
||||
if err != nil {
|
||||
log.Warning("Failed to remove existing partition: %v (%s)", bd.removedParts, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the partition list before we add new ones
|
||||
currentPartitions := bd.getPartitionList()
|
||||
|
||||
// Make the needed new partitions
|
||||
for _, curr := range bd.Children {
|
||||
log.Debug("WritePartitionTable: processing child: %v", curr)
|
||||
baseArgs := []string{
|
||||
"parted",
|
||||
"-a",
|
||||
@@ -255,7 +277,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
|
||||
"--",
|
||||
}
|
||||
|
||||
if !curr.userDefined {
|
||||
if !curr.MakePartition {
|
||||
log.Debug("WritePartitionTable: skipping partition %s", curr.Name)
|
||||
continue
|
||||
}
|
||||
@@ -276,9 +298,11 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
|
||||
size := uint64(curr.Size)
|
||||
end := start + size
|
||||
if !wholeDisk {
|
||||
start = curr.partStart
|
||||
end = curr.partEnd
|
||||
start, end = bd.getPartitionStartEnd(curr.partition)
|
||||
} else {
|
||||
log.Debug("WritePartitionTable: WholeDisk mode")
|
||||
}
|
||||
log.Debug("WritePartitionTable: start: %d, end: %d", start, end)
|
||||
|
||||
if size < 1 {
|
||||
if maxFound {
|
||||
@@ -314,7 +338,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
|
||||
// Get the new list of partitions
|
||||
newPartitions := bd.getPartitionList()
|
||||
// The current partition is new one added
|
||||
curr.SetPartitionNumber(findNewPartition(currentPartitions, newPartitions).number)
|
||||
curr.SetPartitionNumber(findNewPartition(currentPartitions, newPartitions).Number)
|
||||
log.Debug("WritePartitionTable: Found partition number %d for %s", curr.partition, curr.Name)
|
||||
|
||||
start = end
|
||||
@@ -338,7 +362,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
|
||||
}
|
||||
|
||||
// Only set GUIDs on newly created partitions
|
||||
if !curr.userDefined {
|
||||
if !curr.MakePartition {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -383,7 +407,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
|
||||
// need to set / as boot
|
||||
for _, curr := range bd.Children {
|
||||
// Only check for / in new partitions
|
||||
if !curr.userDefined {
|
||||
if !curr.MakePartition {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -421,8 +445,8 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bd *BlockDevice) getPartitionList() []partedPartition {
|
||||
var partitionList []partedPartition
|
||||
func (bd *BlockDevice) getPartitionList() []*PartedPartition {
|
||||
var partitionList []*PartedPartition
|
||||
var err error
|
||||
|
||||
partTable := bytes.NewBuffer(nil)
|
||||
@@ -450,30 +474,30 @@ func (bd *BlockDevice) getPartitionList() []partedPartition {
|
||||
return partitionList
|
||||
}
|
||||
|
||||
var partition partedPartition
|
||||
|
||||
for _, line := range strings.Split(partTable.String(), ";\n") {
|
||||
partition := &PartedPartition{}
|
||||
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) == 7 {
|
||||
partition.number, err = strconv.ParseUint(fields[0], 10, 64)
|
||||
partition.Number, err = strconv.ParseUint(fields[0], 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("getPartitionList: Failed to parse partition number from: %s", line)
|
||||
}
|
||||
partition.start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
|
||||
partition.Start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("getPartitionList: Failed to parse start position from: %s", line)
|
||||
}
|
||||
partition.end, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
|
||||
partition.End, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("getPartitionList: Failed to parse end position from: %s", line)
|
||||
}
|
||||
partition.size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
|
||||
partition.Size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("getPartitionList: Failed to parse partition size from: %s", line)
|
||||
}
|
||||
partition.fileSystem = fields[4]
|
||||
partition.name = fields[5]
|
||||
partition.flags = fields[6]
|
||||
partition.FileSystem = fields[4]
|
||||
partition.Name = fields[5]
|
||||
partition.Flags = fields[6]
|
||||
|
||||
partitionList = append(partitionList, partition)
|
||||
}
|
||||
@@ -482,8 +506,8 @@ func (bd *BlockDevice) getPartitionList() []partedPartition {
|
||||
return partitionList
|
||||
}
|
||||
|
||||
func findNewPartition(currentPartitions, newPartitions []partedPartition) partedPartition {
|
||||
var newPartition partedPartition
|
||||
func findNewPartition(currentPartitions, newPartitions []*PartedPartition) *PartedPartition {
|
||||
newPartition := &PartedPartition{}
|
||||
if len(newPartitions) <= len(currentPartitions) {
|
||||
log.Warning("findNewPartition: number of new partitions is not greater than the current")
|
||||
return newPartition
|
||||
@@ -496,7 +520,7 @@ func findNewPartition(currentPartitions, newPartitions []partedPartition) parted
|
||||
for _, newPart := range newPartitions {
|
||||
found := true
|
||||
for _, curPart := range currentPartitions {
|
||||
if curPart.number == newPart.number {
|
||||
if curPart.Number == newPart.Number {
|
||||
found = false
|
||||
continue
|
||||
}
|
||||
@@ -542,29 +566,22 @@ func (bd *BlockDevice) getPartitionTable() *bytes.Buffer {
|
||||
return partTable
|
||||
}
|
||||
|
||||
func largestContiguousFreeSpace(partTable *bytes.Buffer, minSize uint64) (uint64, uint64) {
|
||||
var start, end, size uint64
|
||||
size = minSize - 1
|
||||
func (bd *BlockDevice) getPartitionStartEnd(partNumber uint64) (uint64, uint64) {
|
||||
var start, end uint64
|
||||
devFile := bd.GetDeviceFile()
|
||||
|
||||
for _, line := range strings.Split(partTable.String(), ";\n") {
|
||||
log.Debug("largestContiguousFreeSpace() line is %q", line)
|
||||
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
|
||||
log.Warning("getPartitionStartEnd() called on non-disk %q", devFile)
|
||||
return start, end
|
||||
}
|
||||
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) == 5 && fields[4] == "free" {
|
||||
lineSize, err := strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
|
||||
if err == nil {
|
||||
if lineSize > size {
|
||||
lineStart, errStart := strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
|
||||
lineEnd, errEnd := strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
|
||||
if errStart == nil && errEnd == nil {
|
||||
start = lineStart
|
||||
end = lineEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, part := range bd.PartTable {
|
||||
if part.Number == partNumber {
|
||||
return part.Start, part.End
|
||||
}
|
||||
}
|
||||
|
||||
log.Warning("getPartitionStartEnd() did not find partition %s for disk %q", partNumber, devFile)
|
||||
return start, end
|
||||
}
|
||||
|
||||
@@ -572,7 +589,7 @@ func largestContiguousFreeSpace(partTable *bytes.Buffer, minSize uint64) (uint64
|
||||
// space in the partition table for the block device.
|
||||
// If none found, returns {0, 0}
|
||||
func (bd *BlockDevice) LargestContiguousFreeSpace(minSize uint64) (uint64, uint64) {
|
||||
var start, end uint64
|
||||
var start, end, size uint64
|
||||
devFile := bd.GetDeviceFile()
|
||||
|
||||
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
|
||||
@@ -580,14 +597,268 @@ func (bd *BlockDevice) LargestContiguousFreeSpace(minSize uint64) (uint64, uint6
|
||||
return start, end
|
||||
}
|
||||
|
||||
// Read the partition table for the device
|
||||
partTable := bd.getPartitionTable()
|
||||
size = minSize - 1
|
||||
|
||||
start, end = largestContiguousFreeSpace(partTable, minSize)
|
||||
for _, part := range bd.PartTable {
|
||||
if part.Number == 0 && part.FileSystem == "free" {
|
||||
if part.Size > size {
|
||||
start = part.Start
|
||||
end = part.End
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return start, end
|
||||
}
|
||||
|
||||
// AddFromFreePartition reduces the free partition by the size given
|
||||
// User when adding a new partition to a disk from free space
|
||||
func (bd *BlockDevice) AddFromFreePartition(parted *PartedPartition, child *BlockDevice) {
|
||||
var next uint64
|
||||
var partitionList []*PartedPartition
|
||||
devFile := bd.GetDeviceFile()
|
||||
|
||||
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
|
||||
log.Warning("AddFromFreePartition() called on non-disk %q", devFile)
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
maxPartitions = 127
|
||||
)
|
||||
|
||||
found := false
|
||||
next = 1
|
||||
|
||||
for !found && next < maxPartitions {
|
||||
present := false
|
||||
for _, partition := range bd.PartTable {
|
||||
if partition.Number == next {
|
||||
present = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if present {
|
||||
next = next + 1
|
||||
} else {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if next >= maxPartitions {
|
||||
log.Warning("AddFromFreePartition() could not add new partition: %v", child)
|
||||
return
|
||||
}
|
||||
|
||||
for _, partition := range bd.PartTable {
|
||||
// Find the partition to update/remove
|
||||
if partition.Number == parted.Number &&
|
||||
partition.Start == parted.Start {
|
||||
log.Debug("Found the free partition to update: %v", partition)
|
||||
|
||||
addPart := partition.Clone()
|
||||
addPart.Number = next
|
||||
addPart.End = addPart.Start + (child.Size - 1)
|
||||
addPart.Size = child.Size
|
||||
addPart.FileSystem = ""
|
||||
log.Debug("Adding the new partition: %v", addPart)
|
||||
partitionList = append(partitionList, addPart)
|
||||
|
||||
child.SetPartitionNumber(addPart.Number)
|
||||
bd.AddChild(child)
|
||||
log.Debug("Added new child partition: %v", child)
|
||||
|
||||
newSize := partition.Size - addPart.Size
|
||||
newStart := addPart.End + 1
|
||||
|
||||
log.Debug("Free partition newStart: %d, newSize: %d", newStart, newSize)
|
||||
if (int(partition.End) - int(newStart)) <= 0 {
|
||||
log.Debug("No Free space left: %v", partition)
|
||||
continue
|
||||
}
|
||||
|
||||
if newSize > (10 * 1024 * 1024) {
|
||||
newPart := partition.Clone()
|
||||
newPart.Start = newStart
|
||||
newPart.Size = newSize
|
||||
log.Debug("Found enough free to add back: %v", newPart)
|
||||
partitionList = append(partitionList, newPart)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("Not the right partition, adding back: %v", partition)
|
||||
partitionList = append(partitionList, partition)
|
||||
}
|
||||
|
||||
bd.PartTable = partitionList
|
||||
for i, p := range bd.PartTable {
|
||||
log.Debug("Dump of PartTable %d: %v", i, p)
|
||||
}
|
||||
|
||||
// Consolidate neighboring free partitions
|
||||
bd.consolidateFree()
|
||||
|
||||
for i, p := range bd.PartTable {
|
||||
log.Debug("Dump of PartTable post consolidate %d: %v", i, p)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (bd *BlockDevice) consolidateFree() {
|
||||
last := &PartedPartition{}
|
||||
var newPartTable []*PartedPartition
|
||||
|
||||
for _, part := range bd.PartTable {
|
||||
log.Debug("consolidateFree() checking part %v", part)
|
||||
// Found a free partition
|
||||
if part.Number == 0 && part.FileSystem == "free" {
|
||||
log.Debug("consolidateFree() part is free %v", part)
|
||||
// And the last partition was also free, then consolidate
|
||||
if last.Number == 0 && last.FileSystem == "free" {
|
||||
log.Debug("consolidateFree() last is also free %v", last)
|
||||
last.End = part.End
|
||||
last.Size = last.Size + part.Size
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
newPart := part.Clone()
|
||||
newPartTable = append(newPartTable, newPart)
|
||||
last = newPart
|
||||
}
|
||||
|
||||
bd.PartTable = newPartTable
|
||||
}
|
||||
|
||||
// RemovePartition remove a child from the disk and updates
|
||||
// frees the space in the partition table
|
||||
func (bd *BlockDevice) RemovePartition(child *BlockDevice) *PartedPartition {
|
||||
log.Debug("RemovePartition() called")
|
||||
var removedPartition *PartedPartition
|
||||
devFile := bd.GetDeviceFile()
|
||||
|
||||
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
|
||||
log.Warning("RemovePartition() called on non-disk %q", devFile)
|
||||
return removedPartition
|
||||
}
|
||||
|
||||
deleteIndex := -1
|
||||
for idx, curr := range bd.Children {
|
||||
if curr.Name == child.Name {
|
||||
child.Parent = nil
|
||||
deleteIndex = idx
|
||||
break
|
||||
}
|
||||
}
|
||||
if deleteIndex < 0 {
|
||||
log.Warning("RemovePartition() fail to find (and remove) child: %v", child)
|
||||
return removedPartition
|
||||
}
|
||||
log.Debug("RemovePartition() found child partition index to delete %d", deleteIndex)
|
||||
// keep a reference to the child
|
||||
delelteChild := bd.Children[deleteIndex].Clone()
|
||||
// Remove the child for the block devices
|
||||
bd.Children = append(bd.Children[:deleteIndex], bd.Children[deleteIndex+1:]...)
|
||||
|
||||
partString := devNameSuffixExp.FindString(delelteChild.Name)
|
||||
partNumber, err := strconv.ParseUint(partString, 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("RemovePartition() fail to find child partition number: %v", child)
|
||||
return removedPartition
|
||||
}
|
||||
log.Debug("RemovePartition() Need to add partition %d to the remove list", partNumber)
|
||||
|
||||
for _, partition := range bd.PartTable {
|
||||
// Find the partition to free/remove
|
||||
if partition.Number == partNumber {
|
||||
log.Debug("Found the partition to free partition: %v", partition)
|
||||
partition.Number = 0
|
||||
partition.FileSystem = "free"
|
||||
partition.Name = ""
|
||||
partition.Flags = ""
|
||||
removedPartition = partition.Clone()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate neighboring free partitions
|
||||
bd.consolidateFree()
|
||||
|
||||
if !delelteChild.MakePartition {
|
||||
bd.addRemovePartition(partNumber)
|
||||
log.Debug("RemovePartition() Add partition to be removed %d", partNumber)
|
||||
}
|
||||
|
||||
return removedPartition
|
||||
}
|
||||
|
||||
// Populate the current partition table for a disk device
|
||||
func (bd *BlockDevice) setPartitionTable(partTable *bytes.Buffer) {
|
||||
var partitionList []*PartedPartition
|
||||
devFile := bd.GetDeviceFile()
|
||||
|
||||
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
|
||||
log.Warning("setPartitionTable() called on non-disk %q", devFile)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
for _, line := range strings.Split(partTable.String(), ";\n") {
|
||||
partition := &PartedPartition{}
|
||||
|
||||
log.Debug("setPartitionTable() line is %q", line)
|
||||
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) == 7 {
|
||||
partition.Number, err = strconv.ParseUint(fields[0], 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("setPartitionTable: Failed to parse partition number from: %s", line)
|
||||
}
|
||||
partition.Start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("setPartitionTable: Failed to parse start position from: %s", line)
|
||||
}
|
||||
partition.End, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("setPartitionTable: Failed to parse end position from: %s", line)
|
||||
}
|
||||
partition.Size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("setPartitionTable: Failed to parse partition size from: %s", line)
|
||||
}
|
||||
partition.FileSystem = fields[4]
|
||||
partition.Name = fields[5]
|
||||
partition.Flags = fields[6]
|
||||
|
||||
partitionList = append(partitionList, partition)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(fields) == 5 && fields[4] == "free" {
|
||||
partition.Number = 0 // We use 0 to special case as a free partition
|
||||
partition.Start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("setPartitionTable: Failed to parse start position from: %s", line)
|
||||
}
|
||||
partition.End, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("setPartitionTable: Failed to parse end position from: %s", line)
|
||||
}
|
||||
partition.Size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning("setPartitionTable: Failed to parse partition size from: %s", line)
|
||||
}
|
||||
partition.FileSystem = fields[4]
|
||||
|
||||
partitionList = append(partitionList, partition)
|
||||
}
|
||||
}
|
||||
|
||||
bd.PartTable = partitionList
|
||||
}
|
||||
|
||||
// MountMetaFs mounts proc, sysfs and devfs in the target installation directory
|
||||
func MountMetaFs(rootDir string) error {
|
||||
err := mountProcFs(rootDir)
|
||||
@@ -923,22 +1194,14 @@ type InstallTarget struct {
|
||||
WholeDisk bool // Can we use the whole disk?
|
||||
Removable bool // Is this removable/hotswap media?
|
||||
EraseDisk bool // Are we wiping the disk? New partition table
|
||||
DataLoss bool // Are we making changes which will lose data
|
||||
Manual bool // Was this disk manually configured?
|
||||
FreeStart uint64 // Starting position of free space
|
||||
FreeEnd uint64 // Ending position of free space
|
||||
}
|
||||
|
||||
type partedPartition struct {
|
||||
number uint64 // partition number
|
||||
start uint64 // starting byte location
|
||||
end uint64 // ending byte location
|
||||
size uint64 // size in bytes
|
||||
fileSystem string // file system Type
|
||||
name string // partition name
|
||||
flags string // flags for partition
|
||||
}
|
||||
|
||||
const (
|
||||
// MinimumServerInstallSize is the smallest installation size in bytes for a Desktop
|
||||
// MinimumServerInstallSize is the smallest installation size in bytes for a Server
|
||||
MinimumServerInstallSize = 4294967296
|
||||
|
||||
// MinimumDesktopInstallSize is the smallest installation size in bytes for a Desktop
|
||||
|
||||
+208
-108
@@ -22,31 +22,45 @@ import (
|
||||
"github.com/clearlinux/clr-installer/utils"
|
||||
)
|
||||
|
||||
// PartedPartition hold partition information
|
||||
// Number 0 and FileSystem "free" are free spaces
|
||||
type PartedPartition struct {
|
||||
Number uint64 // partition number 0 indicates free space
|
||||
Start uint64 // starting byte location
|
||||
End uint64 // ending byte location
|
||||
Size uint64 // size in bytes
|
||||
FileSystem string // file system Type
|
||||
Name string // partition name
|
||||
Flags string // flags for partition
|
||||
}
|
||||
|
||||
// A BlockDevice describes a block device and its partitions
|
||||
type BlockDevice struct {
|
||||
Name string // device name
|
||||
MappedName string // mapped device name
|
||||
Model string // device model
|
||||
MajorMinor string // major:minor device number
|
||||
PtType string // partition table type
|
||||
FsType string // filesystem type
|
||||
UUID string // filesystem uuid
|
||||
Serial string // device serial number
|
||||
MountPoint string // where the device is mounted
|
||||
Label string // label for the partition; set with mkfs
|
||||
Size uint64 // size of the device
|
||||
Type BlockDeviceType // device type
|
||||
State BlockDeviceState // device state (running, live etc)
|
||||
ReadOnly bool // read-only device
|
||||
RemovableDevice bool // removable device
|
||||
Children []*BlockDevice // children devices/partitions
|
||||
Parent *BlockDevice // Parent block device; nil for disk
|
||||
userDefined bool // was this value set by user?
|
||||
available bool // was it mounted the moment we loaded?
|
||||
partStart uint64 // Start of the partition
|
||||
partEnd uint64 // End of the partition
|
||||
partition uint64 // Assigned partition for media - can't set until after mkpart
|
||||
Options string // arbitrary mkfs.* options
|
||||
Name string // device name
|
||||
MappedName string // mapped device name
|
||||
Model string // device model
|
||||
MajorMinor string // major:minor device number
|
||||
PtType string // partition table type
|
||||
FsType string // filesystem type
|
||||
UUID string // filesystem uuid
|
||||
Serial string // device serial number
|
||||
MountPoint string // where the device is mounted
|
||||
Label string // label for the partition; set with mkfs
|
||||
Size uint64 // size of the device
|
||||
Type BlockDeviceType // device type
|
||||
State BlockDeviceState // device state (running, live etc)
|
||||
ReadOnly bool // read-only device
|
||||
RemovableDevice bool // removable device
|
||||
Children []*BlockDevice // children devices/partitions
|
||||
Parent *BlockDevice // Parent block device; nil for disk
|
||||
UserDefined bool // was this value set by user?
|
||||
MakePartition bool // Do we need to make a new partition?
|
||||
FormatPartition bool // Do we need to format the partition
|
||||
Options string // arbitrary mkfs.* options
|
||||
available bool // was it mounted the moment we loaded?
|
||||
partition uint64 // Assigned partition for media - can't set until after mkpart
|
||||
PartTable []*PartedPartition // Existing Disk partition table from parted
|
||||
removedParts []uint64 // List of manually removed partitions
|
||||
}
|
||||
|
||||
// Version used for reading and writing YAML
|
||||
@@ -116,9 +130,9 @@ var (
|
||||
avBlockDevices []*BlockDevice
|
||||
lsblkBinary = "lsblk"
|
||||
storageExp = regexp.MustCompile(`^([0-9]*(\.)?[0-9]*)([bkmgtp]{1}){0,1}$`)
|
||||
devNameSuffixExp = regexp.MustCompile(`([0-9]*)$`)
|
||||
labelExp = regexp.MustCompile(`^([[:word:]-+_]+)$`)
|
||||
mountExp = regexp.MustCompile(`^(/|(/[[:word:]-+_]+)+)$`)
|
||||
devNameSuffixExp = regexp.MustCompile(`([0-9]*)$`)
|
||||
blockDeviceStateMap = map[BlockDeviceState]string{
|
||||
BlockDeviceStateRunning: "running",
|
||||
BlockDeviceStateLive: "live",
|
||||
@@ -170,14 +184,17 @@ func (bd *BlockDevice) ExpandName(alias map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetPartitionNumber is sed when we add a new partition to a disk
|
||||
// GetNewPartitionName returns the name with the new partition number
|
||||
func (bd *BlockDevice) GetNewPartitionName(partition uint64) string {
|
||||
// Replace the last set of digits with the current partition number
|
||||
return devNameSuffixExp.ReplaceAllString(bd.Name, fmt.Sprintf("%d", partition))
|
||||
}
|
||||
|
||||
// SetPartitionNumber is set when we add a new partition to a disk
|
||||
// which stores the newly allocated partition number, and then corrects
|
||||
// the devices partition name
|
||||
func (bd *BlockDevice) SetPartitionNumber(partition uint64) {
|
||||
bd.partition = partition
|
||||
|
||||
// Replace the last set of digits with the current partition number
|
||||
bd.Name = devNameSuffixExp.ReplaceAllString(bd.Name, fmt.Sprintf("%d", partition))
|
||||
}
|
||||
|
||||
// GetDeviceFile formats the block device's file path
|
||||
@@ -239,6 +256,36 @@ func parseBlockDeviceState(bds string) (BlockDeviceState, error) {
|
||||
return BlockDeviceStateUnknown, errors.Errorf("Unrecognized block device state: %s", bds)
|
||||
}
|
||||
|
||||
func (bd *BlockDevice) findFree(size uint64) *PartedPartition {
|
||||
var freePart *PartedPartition
|
||||
|
||||
for _, part := range bd.PartTable {
|
||||
if part.Number == 0 && part.FileSystem == "free" {
|
||||
if part.Size >= size {
|
||||
freePart = part.Clone()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return freePart
|
||||
}
|
||||
|
||||
// Clone creates a copies a PartedPartition
|
||||
func (part *PartedPartition) Clone() *PartedPartition {
|
||||
clone := &PartedPartition{
|
||||
Number: part.Number,
|
||||
Start: part.Start,
|
||||
End: part.End,
|
||||
Size: part.Size,
|
||||
FileSystem: part.FileSystem,
|
||||
Name: part.Name,
|
||||
Flags: part.Flags,
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// Clone creates a copies a BlockDevice and its children
|
||||
func (bd *BlockDevice) Clone() *BlockDevice {
|
||||
clone := &BlockDevice{
|
||||
@@ -257,11 +304,13 @@ func (bd *BlockDevice) Clone() *BlockDevice {
|
||||
ReadOnly: bd.ReadOnly,
|
||||
RemovableDevice: bd.RemovableDevice,
|
||||
Parent: bd.Parent,
|
||||
userDefined: bd.userDefined,
|
||||
UserDefined: bd.UserDefined,
|
||||
MakePartition: bd.MakePartition,
|
||||
FormatPartition: bd.FormatPartition,
|
||||
available: bd.available,
|
||||
partStart: bd.partStart,
|
||||
partEnd: bd.partEnd,
|
||||
partition: bd.partition,
|
||||
PartTable: bd.PartTable,
|
||||
removedParts: bd.removedParts,
|
||||
}
|
||||
|
||||
clone.Children = []*BlockDevice{}
|
||||
@@ -279,7 +328,7 @@ func (bd *BlockDevice) Clone() *BlockDevice {
|
||||
// IsUserDefined returns true if the configuration was interactively
|
||||
// defined by the user
|
||||
func (bd *BlockDevice) IsUserDefined() bool {
|
||||
return bd.userDefined
|
||||
return bd.UserDefined
|
||||
}
|
||||
|
||||
// IsAvailable returns true if the media is not a installer media, returns false otherwise
|
||||
@@ -404,6 +453,11 @@ func (bd *BlockDevice) RemoveChild(child *BlockDevice) {
|
||||
}
|
||||
}
|
||||
|
||||
// addRemovePartition adds a partition to the list to be removed
|
||||
func (bd *BlockDevice) addRemovePartition(part uint64) {
|
||||
bd.removedParts = append(bd.removedParts, part)
|
||||
}
|
||||
|
||||
// AddChild adds a partition to a disk block device
|
||||
func (bd *BlockDevice) AddChild(child *BlockDevice) {
|
||||
if bd.Children == nil {
|
||||
@@ -422,8 +476,13 @@ func (bd *BlockDevice) AddChild(child *BlockDevice) {
|
||||
}
|
||||
|
||||
if child.Name == "" {
|
||||
child.Name = fmt.Sprintf("%s%s%d", bd.Name, partPrefix, len(bd.Children))
|
||||
if child.partition < 1 {
|
||||
child.Name = fmt.Sprintf("%s%s?", bd.Name, partPrefix)
|
||||
} else {
|
||||
child.Name = fmt.Sprintf("%s%s%d", bd.Name, partPrefix, child.partition)
|
||||
}
|
||||
}
|
||||
log.Debug("AddChild: child.Name is %q", child.Name)
|
||||
}
|
||||
|
||||
// HumanReadableSizeWithUnitAndPrecision converts the size representation in bytes to the
|
||||
@@ -555,6 +614,10 @@ func listBlockDevices(userDefined []*BlockDevice) ([]*BlockDevice, error) {
|
||||
if err = bd.PartProbe(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read the partition table for the device
|
||||
partTable := bd.getPartitionTable()
|
||||
bd.setPartitionTable(partTable)
|
||||
}
|
||||
|
||||
if userDefined == nil || len(userDefined) == 0 {
|
||||
@@ -772,21 +835,6 @@ func getNextBoolToken(dec *json.Decoder, name string) (bool, error) {
|
||||
return false, errors.Errorf("Unknown ro value: %s", str)
|
||||
}
|
||||
|
||||
// MaxParitionSize returns largest size the partition
|
||||
// can be in bytes. Return 0 if there is an error; in theory
|
||||
// the maximum size should at least be the current size.
|
||||
func (bd *BlockDevice) MaxParitionSize() uint64 {
|
||||
|
||||
if bd.Parent != nil {
|
||||
free, err := bd.Parent.FreeSpace()
|
||||
if err == nil {
|
||||
return (bd.Size + free)
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsValidLabel returns empty string if label is valid
|
||||
func IsValidLabel(label string, fstype string) string {
|
||||
if label == "" {
|
||||
@@ -818,7 +866,7 @@ func IsValidMount(str string) string {
|
||||
// -- size is suffixed with B, K, M, G, T, P
|
||||
// -- size is greater than MinimumPartitionSize
|
||||
// -- size is less than (or equal to) current size + free space
|
||||
func (bd *BlockDevice) IsValidSize(str string) string {
|
||||
func (bd *BlockDevice) IsValidSize(str string, maxPartSize uint64) string {
|
||||
str = strings.ToLower(str)
|
||||
|
||||
if !storageExp.MatchString(str) {
|
||||
@@ -832,7 +880,6 @@ func (bd *BlockDevice) IsValidSize(str string) string {
|
||||
return "Size too small"
|
||||
}
|
||||
|
||||
maxPartSize := bd.MaxParitionSize()
|
||||
if maxPartSize == 0 {
|
||||
return "Unknown free space"
|
||||
} else if size > maxPartSize {
|
||||
@@ -879,6 +926,43 @@ func ParseVolumeSize(str string) (uint64, error) {
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// ParseVolumeHumanSize will parse a string formatted (1M, 10G, 2T) size and
|
||||
// return its representation in human bytes; MB, not MiB
|
||||
func ParseVolumeHumanSize(str string) (uint64, error) {
|
||||
var size uint64
|
||||
|
||||
str = strings.ToLower(str)
|
||||
|
||||
if !storageExp.MatchString(str) {
|
||||
return strconv.ParseUint(str, 0, 64)
|
||||
}
|
||||
|
||||
unit := storageExp.ReplaceAllString(str, `$3`)
|
||||
fsize, err := strconv.ParseFloat(storageExp.ReplaceAllString(str, `$1`), 64)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err)
|
||||
}
|
||||
|
||||
switch unit {
|
||||
case "b":
|
||||
fsize = fsize * (1.0)
|
||||
case "k":
|
||||
fsize = fsize * (1.0 * 1000.0)
|
||||
case "m":
|
||||
fsize = fsize * (1.0 * 1000.0 * 1000.0)
|
||||
case "g":
|
||||
fsize = fsize * (1.0 * 1000.0 * 1000.0 * 1000.0)
|
||||
case "t":
|
||||
fsize = fsize * (1.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)
|
||||
case "p":
|
||||
fsize = fsize * (1.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)
|
||||
}
|
||||
|
||||
size = uint64(math.Round(fsize))
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a BlockDevice, targeted to integrate with json
|
||||
// decoding framework
|
||||
func (bd *BlockDevice) UnmarshalJSON(b []byte) error {
|
||||
@@ -1095,7 +1179,8 @@ func (bd *BlockDevice) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
}
|
||||
bd.Type = iType
|
||||
if iType != BlockDeviceTypeDisk {
|
||||
bd.userDefined = true
|
||||
bd.MakePartition = true
|
||||
bd.FormatPartition = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1180,53 +1265,50 @@ func MaxLabelLength(fstype string) int {
|
||||
}
|
||||
|
||||
// AddBootStandardPartition will add to disk a new standard Boot partition
|
||||
func AddBootStandardPartition(disk *BlockDevice, start uint64) uint64 {
|
||||
end := start + bootSize
|
||||
|
||||
disk.AddChild(&BlockDevice{
|
||||
Size: bootSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "vfat",
|
||||
MountPoint: "/boot",
|
||||
Label: "boot",
|
||||
userDefined: true,
|
||||
partStart: start,
|
||||
partEnd: end,
|
||||
func AddBootStandardPartition(disk *BlockDevice) uint64 {
|
||||
freePart := disk.findFree(bootSize)
|
||||
disk.AddFromFreePartition(freePart, &BlockDevice{
|
||||
Size: bootSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "vfat",
|
||||
MountPoint: "/boot",
|
||||
Label: "boot",
|
||||
UserDefined: true,
|
||||
MakePartition: true,
|
||||
FormatPartition: true,
|
||||
})
|
||||
|
||||
return end
|
||||
return bootSize
|
||||
}
|
||||
|
||||
// AddSwapStandardPartition will add to disk a new standard Swap partition
|
||||
func AddSwapStandardPartition(disk *BlockDevice, start uint64) uint64 {
|
||||
end := start + swapSize
|
||||
|
||||
disk.AddChild(&BlockDevice{
|
||||
Size: swapSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "swap",
|
||||
Label: "swap",
|
||||
userDefined: true,
|
||||
partStart: start,
|
||||
partEnd: end,
|
||||
func AddSwapStandardPartition(disk *BlockDevice) uint64 {
|
||||
freePart := disk.findFree(swapSize)
|
||||
disk.AddFromFreePartition(freePart, &BlockDevice{
|
||||
Size: swapSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "swap",
|
||||
Label: "swap",
|
||||
UserDefined: true,
|
||||
MakePartition: true,
|
||||
FormatPartition: true,
|
||||
})
|
||||
|
||||
return end
|
||||
return swapSize
|
||||
}
|
||||
|
||||
// AddRootStandardPartition will add to disk a new standard Root partition
|
||||
func AddRootStandardPartition(disk *BlockDevice, size uint64, start uint64) {
|
||||
end := start + size
|
||||
|
||||
disk.AddChild(&BlockDevice{
|
||||
Size: size,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "ext4",
|
||||
MountPoint: "/",
|
||||
Label: "root",
|
||||
userDefined: true,
|
||||
partStart: start,
|
||||
partEnd: end,
|
||||
func AddRootStandardPartition(disk *BlockDevice, rootSize uint64) {
|
||||
freePart := disk.findFree(rootSize)
|
||||
disk.AddFromFreePartition(freePart, &BlockDevice{
|
||||
Size: rootSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "ext4",
|
||||
MountPoint: "/",
|
||||
Label: "root",
|
||||
UserDefined: true,
|
||||
MakePartition: true,
|
||||
FormatPartition: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1234,33 +1316,51 @@ func AddRootStandardPartition(disk *BlockDevice, size uint64, start uint64) {
|
||||
// default set of partitions required for an installation
|
||||
func NewStandardPartitions(disk *BlockDevice) {
|
||||
disk.Children = nil
|
||||
newFreePart := &PartedPartition{
|
||||
Number: 0,
|
||||
Start: 0,
|
||||
End: disk.Size,
|
||||
Size: disk.Size,
|
||||
FileSystem: "free",
|
||||
}
|
||||
disk.PartTable = nil
|
||||
disk.PartTable = append(disk.PartTable, newFreePart)
|
||||
|
||||
rootSize := uint64(disk.Size - bootSize - swapSize)
|
||||
|
||||
disk.AddChild(&BlockDevice{
|
||||
Size: bootSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "vfat",
|
||||
MountPoint: "/boot",
|
||||
Label: "boot",
|
||||
userDefined: true,
|
||||
freePart := disk.findFree(bootSize)
|
||||
disk.AddFromFreePartition(freePart, &BlockDevice{
|
||||
Size: bootSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "vfat",
|
||||
MountPoint: "/boot",
|
||||
Label: "boot",
|
||||
UserDefined: true,
|
||||
MakePartition: true,
|
||||
FormatPartition: true,
|
||||
})
|
||||
|
||||
disk.AddChild(&BlockDevice{
|
||||
Size: swapSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "swap",
|
||||
Label: "swap",
|
||||
userDefined: true,
|
||||
freePart = disk.findFree(swapSize)
|
||||
disk.AddFromFreePartition(freePart, &BlockDevice{
|
||||
Size: swapSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "swap",
|
||||
Label: "swap",
|
||||
UserDefined: true,
|
||||
MakePartition: true,
|
||||
FormatPartition: true,
|
||||
})
|
||||
|
||||
disk.AddChild(&BlockDevice{
|
||||
Size: rootSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "ext4",
|
||||
MountPoint: "/",
|
||||
Label: "root",
|
||||
userDefined: true,
|
||||
freePart = disk.findFree(rootSize)
|
||||
disk.AddFromFreePartition(freePart, &BlockDevice{
|
||||
Size: rootSize,
|
||||
Type: BlockDeviceTypePart,
|
||||
FsType: "ext4",
|
||||
MountPoint: "/",
|
||||
Label: "root",
|
||||
UserDefined: true,
|
||||
MakePartition: true,
|
||||
FormatPartition: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+21
-13
@@ -921,37 +921,45 @@ BYT;
|
||||
`
|
||||
|
||||
var start, end, twentyGig, fourGig uint64
|
||||
children := make([]*BlockDevice, 0)
|
||||
bd := &BlockDevice{Name: "sda", Children: children}
|
||||
|
||||
twentyGig = 21474836480
|
||||
fourGig = 4294967296
|
||||
t.Logf("getPartAllFreeOutput: twentyGig: %d, fourGig: %d", twentyGig, fourGig)
|
||||
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartAllFreeOutput)), twentyGig)
|
||||
|
||||
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartAllFreeOutput)))
|
||||
start, end = bd.LargestContiguousFreeSpace(twentyGig)
|
||||
if start == 0 && end == 0 {
|
||||
t.Fatalf("Should have found %d free in getPartAllFreeOutput", twentyGig)
|
||||
}
|
||||
t.Logf("getPartAllFreeOutput: start: %d, end: %d", start, end)
|
||||
|
||||
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartSomeFreeOutput)), twentyGig)
|
||||
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartSomeFreeOutput)))
|
||||
start, end = bd.LargestContiguousFreeSpace(twentyGig)
|
||||
if start == 0 && end == 0 {
|
||||
t.Fatalf("Should have found %d free in getPartSomeFreeOutput", twentyGig)
|
||||
}
|
||||
t.Logf("getPartSomeFreeOutput: start: %d, end: %d", start, end)
|
||||
|
||||
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartNotEnoughFreeOutput)), fourGig)
|
||||
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartNotEnoughFreeOutput)))
|
||||
start, end = bd.LargestContiguousFreeSpace(fourGig)
|
||||
if start != 0 || end != 0 {
|
||||
t.Logf("getPartNotEnoughFreeOutput: start: %d, end: %d", start, end)
|
||||
t.Fatalf("Should NOT have found %d free in getPartNotEnoughFreeOutput", twentyGig)
|
||||
}
|
||||
t.Logf("getPartNotEnoughFreeOutput: start: %d, end: %d", start, end)
|
||||
|
||||
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartNotEnoughFree2Output)), twentyGig)
|
||||
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartNotEnoughFree2Output)))
|
||||
start, end = bd.LargestContiguousFreeSpace(twentyGig)
|
||||
if start != 0 || end != 0 {
|
||||
t.Logf("getPartNotEnoughFree2Output: start: %d, end: %d", start, end)
|
||||
t.Fatalf("Should NOT have found %d free in getPartNotEnoughFree2Output", twentyGig)
|
||||
}
|
||||
t.Logf("getPartNotEnoughFree2Output: start: %d, end: %d", start, end)
|
||||
|
||||
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartNotEnoughFree3Output)), twentyGig)
|
||||
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartNotEnoughFree3Output)))
|
||||
start, end = bd.LargestContiguousFreeSpace(twentyGig)
|
||||
if start != 0 || end != 0 {
|
||||
t.Logf("getPartNotEnoughFree3Output: start: %d, end: %d", start, end)
|
||||
t.Fatalf("Should NOT have found %d free in getPartNotEnoughFree3Output", twentyGig)
|
||||
@@ -982,7 +990,7 @@ func TestSwapCheck(t *testing.T) {
|
||||
}
|
||||
|
||||
bd = &BlockDevice{Size: MinimumServerInstallSize}
|
||||
_ = AddBootStandardPartition(bd, 0)
|
||||
_ = AddBootStandardPartition(bd)
|
||||
if bd.DeviceHasSwap() {
|
||||
t.Fatalf("Device should NOT have swap, but does: %v", bd)
|
||||
}
|
||||
@@ -992,15 +1000,15 @@ func TestSwapCheck(t *testing.T) {
|
||||
func TestAddPartititions(t *testing.T) {
|
||||
bd := &BlockDevice{Size: MinimumServerInstallSize}
|
||||
|
||||
end := AddBootStandardPartition(bd, 0)
|
||||
if end != bootSize {
|
||||
t.Fatalf("Boot partition should end at %d, but was %d", bootSize, end)
|
||||
size := AddBootStandardPartition(bd)
|
||||
if size != bootSize {
|
||||
t.Fatalf("Boot partition should be %d, but was %d", bootSize, size)
|
||||
}
|
||||
end = AddSwapStandardPartition(bd, end)
|
||||
if end != (bootSize + swapSize) {
|
||||
t.Fatalf("Swap partition should end at %d, but was %d", (bootSize + swapSize), end)
|
||||
size = AddSwapStandardPartition(bd)
|
||||
if size != swapSize {
|
||||
t.Fatalf("Swap partition should be %d, but was %d", swapSize, size)
|
||||
}
|
||||
|
||||
rootSize := uint64(bd.Size - bootSize - swapSize)
|
||||
AddRootStandardPartition(bd, rootSize, end)
|
||||
AddRootStandardPartition(bd, rootSize)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user