Skip to content

Commit

Permalink
fix: sync kernel partition table incrementally
Browse files Browse the repository at this point in the history
Previous strategy was to drop every kernel partition and replace it with
the new partitions, but this fails when Talos tries to resize ephemeral
partition on boot: other partitions are already mounted, so they can't
be removed from the kernel state.

Signed-off-by: Andrey Smirnov <[email protected]>
  • Loading branch information
smira authored and talos-bot committed Oct 27, 2020
1 parent 2cb9516 commit ceae64e
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 17 deletions.
12 changes: 12 additions & 0 deletions blockdevice/blkpg/blkpg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package blkpg

// KernelPartition represents kernel partition info.
type KernelPartition struct {
No int
Start int64
Length int64
}
5 changes: 5 additions & 0 deletions blockdevice/blkpg/blkpg_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ func InformKernelOfResize(f *os.File, partition table.Partition) error {
func InformKernelOfDelete(f *os.File, partition table.Partition) error {
return fmt.Errorf("not implemented")
}

// GetKernelPartitions returns kernel state of partitions.
func GetKernelPartitions(f *os.File, devPath string) ([]KernelPartition, error) {
return nil, fmt.Errorf("not implemented")
}
52 changes: 52 additions & 0 deletions blockdevice/blkpg/blkpg_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ package blkpg

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"unsafe"
Expand All @@ -16,6 +20,7 @@ import (

"github.com/talos-systems/go-blockdevice/blockdevice/lba"
"github.com/talos-systems/go-blockdevice/blockdevice/table"
"github.com/talos-systems/go-blockdevice/blockdevice/util"
)

// InformKernelOfAdd invokes the BLKPG_ADD_PARTITION ioctl.
Expand Down Expand Up @@ -98,3 +103,50 @@ func inform(f *os.File, partition table.Partition, op int32) (err error) {

return nil
}

// GetKernelPartitions returns kernel partition table state.
func GetKernelPartitions(f *os.File, devPath string) ([]KernelPartition, error) {
result := []KernelPartition{}

for i := 1; i <= 256; i++ {
partName := util.PartName(devPath, i)
partPath := filepath.Join("/sys/block", filepath.Base(devPath), partName)

_, err := os.Stat(partPath)
if err != nil {
if os.IsNotExist(err) {
continue
}

return nil, err
}

startS, err := ioutil.ReadFile(filepath.Join(partPath, "start"))
if err != nil {
return nil, err
}

sizeS, err := ioutil.ReadFile(filepath.Join(partPath, "size"))
if err != nil {
return nil, err
}

start, err := strconv.ParseInt(strings.TrimSpace(string(startS)), 10, 64)
if err != nil {
return nil, err
}

size, err := strconv.ParseInt(strings.TrimSpace(string(sizeS)), 10, 64)
if err != nil {
return nil, err
}

result = append(result, KernelPartition{
No: i,
Start: start,
Length: size,
})
}

return result, nil
}
76 changes: 59 additions & 17 deletions blockdevice/table/gpt/gpt.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"encoding/binary"
"fmt"
"os"
"path/filepath"

"github.com/google/uuid"

Expand All @@ -18,7 +17,6 @@ import (
"github.com/talos-systems/go-blockdevice/blockdevice/table"
"github.com/talos-systems/go-blockdevice/blockdevice/table/gpt/header"
"github.com/talos-systems/go-blockdevice/blockdevice/table/gpt/partition"
"github.com/talos-systems/go-blockdevice/blockdevice/util"
)

// GPT represents the GUID partition table.
Expand Down Expand Up @@ -102,44 +100,88 @@ func (gpt *GPT) Write() error {
return err
}

if err := gpt.writePrimary(partitions); err != nil {
if err = gpt.writePrimary(partitions); err != nil {
return fmt.Errorf("failed to write primary table: %w", err)
}

if err := gpt.writeSecondary(partitions); err != nil {
if err = gpt.writeSecondary(partitions); err != nil {
return fmt.Errorf("failed to write secondary table: %w", err)
}

if err := gpt.f.Sync(); err != nil {
if err = gpt.f.Sync(); err != nil {
return err
}

for i := 1; ; i++ {
partName := util.PartName(gpt.devname, i)
if err = gpt.syncKernelPartitions(); err != nil {
return fmt.Errorf("failed to sync kernel partitions: %w", err)
}

return gpt.Read()
}

func (gpt *GPT) syncKernelPartitions() error {
kernelPartitions, err := blkpg.GetKernelPartitions(gpt.f, gpt.devname)
if err != nil {
return err
}

// filter out nil partitions
newPartitions := make([]table.Partition, 0, len(gpt.partitions))

for _, part := range gpt.partitions {
if part == nil {
continue
}

newPartitions = append(newPartitions, part)
}

var i int

_, err := os.Stat(filepath.Join("/sys/block", filepath.Base(gpt.devname), partName))
if err != nil {
// find partitions matching exactly or partitions which can be simply resized
for i = 0; i < len(kernelPartitions) && i < len(newPartitions); i++ {
kernelPart := kernelPartitions[i]
newPart := newPartitions[i]

// non-contiguous kernel partition table, stop
if kernelPart.No != i+1 {
break
}

// skip partitions without any changes
if kernelPart.Start == newPart.Start() && kernelPart.Length == newPart.Length() {
continue
}

// resizing a partition which is the last one in the kernel list (no overlaps)
if kernelPart.Start == newPart.Start() && i == len(kernelPartitions)-1 {
if err := blkpg.InformKernelOfResize(gpt.f, newPart); err != nil {
return err
}

continue
}

// partitions don't match, stop
break
}

// process remaining partitions: delete all the kernel partitions left, add new partitions from in-memory set
for j := i; j < len(kernelPartitions); j++ {
if err := blkpg.InformKernelOfDelete(gpt.f, &partition.Partition{
Number: int32(i),
Number: int32(kernelPartitions[j].No),
}); err != nil {
return err
}
}

for _, part := range gpt.partitions {
if part == nil {
continue
}

if err := blkpg.InformKernelOfAdd(gpt.f, part); err != nil {
for j := i; j < len(newPartitions); j++ {
if err := blkpg.InformKernelOfAdd(gpt.f, newPartitions[j]); err != nil {
return err
}
}

return gpt.Read()
return nil
}

// New creates a new partition table and writes it to disk.
Expand Down

0 comments on commit ceae64e

Please sign in to comment.