Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LVM (+ thin-provisioning) support #149

Merged
merged 3 commits into from
Nov 4, 2023
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/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func upgrade(cmd *cobra.Command, args []string) error {

var operation core.ABSystemOperation
if force {
operation = core.FOCE_UPGRADE
operation = core.FORCE_UPGRADE
} else {
operation = core.UPGRADE
}
Expand Down
3 changes: 3 additions & 0 deletions config/abroot.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@
"partLabelBoot": "vos-boot",
"partLabelEfi": "vos-efi",

"thinProvisioning": false,
"thinInitVolume": "",

"libPathStates": "/var/lib/abroot/states"
}
120 changes: 38 additions & 82 deletions core/disk-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,26 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
)

// DiskManager represents a disk
type DiskManager struct{}

// Disk represents a disk
type Disk struct {
Device string
Partitions []Partition
}

// Partition represents a standard partition
// Partition represents either a standard partition or a device-mapper partition.
type Partition struct {
Label string
MountPoint string
MountOptions string
Uuid string
FsType string
Device string // e.g. sda1, nvme0n1p1

// If the partition is LUKS-encrypted, the logical volume opened in /dev/mapper/
// will be a child of the physical partiiton in /dev/.
// If standard partition, Device will be the partition's name (e.g. sda1, nvme0n1p1).
// If LUKS-encrypted or LVM volume, Device will be the name in device-mapper.
Device string

// If the partition is LUKS-encrypted or an LVM volume, the logical volume
// opened in /dev/mapper will be a child of the physical partiiton in /dev.
// Otherwise, the partition will be a direct child of the block device, and
// therefore Parent will be nil.
Parent *Partition
Expand All @@ -66,77 +61,25 @@ func NewDiskManager() *DiskManager {
return &DiskManager{}
}

// GetDisk gets a disk by device
func (d *DiskManager) GetDisk(device string) (Disk, error) {
PrintVerbose("DiskManager.GetDisk: running...")
partitions, err := d.getPartitions(device)
if err != nil {
PrintVerbose("DiskManager.GetDisk:err: %s", err)
return Disk{}, err
}

PrintVerbose("DiskManager.GetDisk: successfully got disk %s", device)

return Disk{
Device: device,
Partitions: partitions,
}, nil
}

// GetDiskByPartition gets a disk by partition
func (d *DiskManager) GetDiskByPartition(partition string) (Disk, error) {
PrintVerbose("DiskManager.GetDiskByPartition: running...")

output, err := exec.Command("lsblk", "-n", "-o", "PKNAME", "/dev/"+partition).Output()
if err != nil {
PrintVerbose("DiskManager.GetDiskByPartition:err: %s", err)
return Disk{}, err
}

device := strings.TrimSpace(string(output))

PrintVerbose("DiskManager.GetDiskByPartition: successfully got disk %s", device)

return d.GetDisk(device)
}

// GetCurrentDisk gets the current disk
func (d *DiskManager) GetCurrentDisk() (Disk, error) {
PrintVerbose("DiskManager.GetCurrentDisk: running...")

// we need to evaluate symlinks to get the real root path
// in case of weird setups
root, err := filepath.EvalSymlinks("/")
if err != nil {
PrintVerbose("DiskManager.GetCurrentDisk:err(2): %s", err)
return Disk{}, err
}
// GetPartitionByLabel finds a partition by searching for its label.
//
// If no partition can be found with the given label, returns error.
func (d *DiskManager) GetPartitionByLabel(label string) (Partition, error) {
PrintVerbose("DiskManager.GetPartitionByLabel: retrieving partitions")

output, err := exec.Command("df", "-P", root).Output()
partitions, err := d.getPartitions("")
if err != nil {
PrintVerbose("DiskManager.GetCurrentDisk:err(3): %s", err)
return Disk{}, err
}

lines := strings.Split(string(output), "\n")
if len(lines) < 2 {
err := errors.New("could not determine device name for " + root)
PrintVerbose("DiskManager.GetCurrentDisk:err(4): %s", err)
return Disk{}, err
return Partition{}, err
}

fields := strings.Fields(lines[1])
if len(fields) < 6 {
err := errors.New("could not determine device name for " + root)
PrintVerbose("DiskManager.GetCurrentDisk:err(5): %s", err)
return Disk{}, err
for _, part := range partitions {
if part.Label == label {
PrintVerbose("DiskManager.GetPartitionByLabel: Partition with UUID %s has label %s", part.Uuid, label)
return part, nil
}
}

device := filepath.Base(fields[0])

PrintVerbose("DiskManager.GetCurrentDisk: successfully got disk %s", device)

return d.GetDiskByPartition(device)
return Partition{}, fmt.Errorf("could not find partition with label %s", label)
}

// iterChildren iterates through the children of a device or partition recursively
Expand Down Expand Up @@ -166,7 +109,7 @@ func iterChildren(childs *[]Children, result *[]Partition) {
}
}

// getPartitions gets a disk's partitions
// getPartitions gets a disk's partitions. If device is an empty string, gets all partitions from all disks.
func (d *DiskManager) getPartitions(device string) ([]Partition, error) {
PrintVerbose("DiskManager.getPartitions: running...")

Expand All @@ -191,7 +134,7 @@ func (d *DiskManager) getPartitions(device string) ([]Partition, error) {

var result []Partition
for _, blockDevice := range partitions.BlockDevices {
if blockDevice.Name != device {
if device != "" && blockDevice.Name != device {
continue
}

Expand All @@ -214,14 +157,20 @@ func (p *Partition) Mount(destination string) error {
}
}

err := syscall.Mount(fmt.Sprintf("/dev/%s", p.Device), destination, p.FsType, 0, "")
devicePath := "/dev/"
if p.IsDevMapper() {
devicePath += "mapper/"
}
devicePath += p.Device

err := syscall.Mount(devicePath, destination, p.FsType, 0, "")
if err != nil {
PrintVerbose("Partition.Mount: error(2): %s", err)
return err
}

p.MountPoint = destination
PrintVerbose("Partition.Mount: successfully mounted partition")
PrintVerbose("Partition.Mount: successfully mounted %s at %s", devicePath, p.MountPoint)
return nil
}

Expand All @@ -236,11 +185,18 @@ func (p *Partition) Unmount() error {

err := syscall.Unmount(p.MountPoint, 0)
if err != nil {
PrintVerbose("Partition.Unmount: failed to unmount %s", p.MountPoint)
PrintVerbose("Partition.Unmount: error(2): %s", err)
return err
}

PrintVerbose("Partition.Unmount: successfully unmounted %s", p.MountPoint)
p.MountPoint = ""
PrintVerbose("Partition.Unmount: successfully unmounted partition")

return nil
}

// Returns whether the partition is a device-mapper virtual partition
func (p *Partition) IsDevMapper() bool {
return p.Parent != nil
}
37 changes: 29 additions & 8 deletions core/grub.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"os"
"path/filepath"
"strings"

"github.com/vanilla-os/abroot/settings"
)

type Grub struct {
Expand All @@ -28,7 +30,7 @@ type Grub struct {

// generateABGrubConf generates a new grub config with the given details
// kernel version is automatically detected
func generateABGrubConf(rootPath string, rootUuid string) error {
func generateABGrubConf(rootPath string, rootUuid string, rootLabel string) error {
PrintVerbose("generateABGrubConf: generating grub config for ABRoot")

kargs, err := KargsRead()
Expand All @@ -37,16 +39,35 @@ func generateABGrubConf(rootPath string, rootUuid string) error {
return err
}

grubPath := filepath.Join(rootPath, "boot", "grub")
var grubPath, bootPrefix, bootPath, systemRoot string
if settings.Cnf.ThinProvisioning {
grubPath = filepath.Join(rootPath, "boot", "init", rootLabel)
bootPrefix = "/" + rootLabel
bootPath = grubPath

diskM := NewDiskManager()
sysRootPart, err := diskM.GetPartitionByLabel(rootLabel)
if err != nil {
PrintVerbose("generateABGrubConf:err(3): %s", err)
return err
}
systemRoot = "/dev/mapper/" + sysRootPart.Device
} else {
grubPath = filepath.Join(rootPath, "boot", "grub")
bootPrefix = "/.system/boot"
bootPath = filepath.Join(rootPath, "boot")
systemRoot = "UUID=" + rootUuid
}

confPath := filepath.Join(grubPath, "abroot.cfg")
template := `insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root %s
linux /.system/boot/vmlinuz-%s root=UUID=%s %s
initrd /.system/boot/initrd.img-%s`
linux %s/vmlinuz-%s root=%s %s
initrd %s/initrd.img-%s`

kernelVersion := getKernelVersion(rootPath)
kernelVersion := getKernelVersion(bootPath)
if kernelVersion == "" {
err := errors.New("could not get kernel version")
PrintVerbose("generateABGrubConf:err: %s", err)
Expand All @@ -61,7 +82,7 @@ initrd /.system/boot/initrd.img-%s`

err = os.WriteFile(
confPath,
[]byte(fmt.Sprintf(template, rootUuid, kernelVersion, rootUuid, kargs, kernelVersion)),
[]byte(fmt.Sprintf(template, rootUuid, bootPrefix, kernelVersion, systemRoot, kargs, bootPrefix, kernelVersion)),
0644,
)
if err != nil {
Expand All @@ -73,10 +94,10 @@ initrd /.system/boot/initrd.img-%s`
}

// getKernelVersion returns the latest kernel version available in the root
func getKernelVersion(rootPath string) string {
func getKernelVersion(bootPath string) string {
PrintVerbose("getKernelVersion: getting kernel version")

kernelDir := filepath.Join(rootPath, "boot", "vmlinuz-*")
kernelDir := filepath.Join(bootPath, "vmlinuz-*")
files, err := filepath.Glob(kernelDir)
if err != nil {
PrintVerbose("getKernelVersion:err: %s", err)
Expand Down
3 changes: 2 additions & 1 deletion core/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)

Expand Down Expand Up @@ -77,7 +78,7 @@ func (a *ABImage) WriteTo(dest string, suffix string) error {
suffix = "-" + suffix
}
imageName := "abimage" + suffix + ".abr"
imagePath := fmt.Sprintf("%s/%s", dest, imageName)
imagePath := filepath.Join(dest, imageName)

abimage, err := json.Marshal(a)
if err != nil {
Expand Down
Loading