From be8ad7ae88a8de9a7711b2466c6ad08b6a25ab63 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Thu, 30 Jan 2025 22:52:10 +0200 Subject: [PATCH] limactl disk: Do not use qemu-img for raw disks Fix `limactl disk create` and `limactl disk resize` to use nativeimgutil for creating and resizing disks using raw format. There are special cases (using direct I/O on certain file systems) when creating a raw image should be done with qemu-img, but these are not relevant to lima on macOS. With this change we have no dependency on qemu when using VZ instance. This change does not fix the issue of default "qcow2" disk format. Users must specify the disk format when creating additional disks for VZ instance: limactl disk create --format raw Unfinished: - Needs tests for nativeimgutil and disk command Fixes: #2853 Signed-off-by: Nir Soffer --- cmd/limactl/disk.go | 19 +++++++++++++++++-- pkg/nativeimgutil/nativeimgutil.go | 27 +++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/cmd/limactl/disk.go b/cmd/limactl/disk.go index dafd601ac63..21fb22729c6 100644 --- a/cmd/limactl/disk.go +++ b/cmd/limactl/disk.go @@ -9,6 +9,7 @@ import ( "text/tabwriter" "github.com/docker/go-units" + "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/qemu" "github.com/lima-vm/lima/pkg/store" "github.com/sirupsen/logrus" @@ -101,7 +102,13 @@ func diskCreateAction(cmd *cobra.Command, args []string) error { return err } - if err := qemu.CreateDataDisk(diskDir, format, int(diskSize)); err != nil { + // qemu may not be available, use it only if needed. + if format == "raw" { + err = nativeimgutil.CreateRawDataDisk(diskDir, int(diskSize)) + } else { + err = qemu.CreateDataDisk(diskDir, format, int(diskSize)) + } + if err != nil { rerr := os.RemoveAll(diskDir) if rerr != nil { err = errors.Join(err, fmt.Errorf("failed to remove a directory %q: %w", diskDir, rerr)) @@ -390,9 +397,17 @@ func diskResizeAction(cmd *cobra.Command, args []string) error { } } } - if err := qemu.ResizeDataDisk(disk.Dir, disk.Format, int(diskSize)); err != nil { + + // qemu may not be available, use it only if needed. + if disk.Format == "raw" { + err = nativeimgutil.ResizeRawDataDisk(disk.Dir, int(diskSize)) + } else { + err = qemu.ResizeDataDisk(disk.Dir, disk.Format, int(diskSize)) + } + if err != nil { return fmt.Errorf("failed to resize disk %q: %w", diskName, err) } + logrus.Infof("Resized disk %q (%q)", diskName, disk.Dir) return nil } diff --git a/pkg/nativeimgutil/nativeimgutil.go b/pkg/nativeimgutil/nativeimgutil.go index 3f2a368ab35..c79d6f47ceb 100644 --- a/pkg/nativeimgutil/nativeimgutil.go +++ b/pkg/nativeimgutil/nativeimgutil.go @@ -2,21 +2,44 @@ package nativeimgutil import ( + "errors" "fmt" "io" + "io/fs" "os" "path/filepath" - "github.com/containerd/continuity/fs" + containerdfs "github.com/containerd/continuity/fs" "github.com/docker/go-units" "github.com/lima-vm/go-qcow2reader" "github.com/lima-vm/go-qcow2reader/convert" "github.com/lima-vm/go-qcow2reader/image/qcow2" "github.com/lima-vm/go-qcow2reader/image/raw" "github.com/lima-vm/lima/pkg/progressbar" + "github.com/lima-vm/lima/pkg/store/filenames" "github.com/sirupsen/logrus" ) +// CreateRawDataDisk creates an empty raw data disk. +func CreateRawDataDisk(dir string, size int) error { + dataDisk := filepath.Join(dir, filenames.DataDisk) + if _, err := os.Stat(dataDisk); err == nil || !errors.Is(err, fs.ErrNotExist) { + return err + } + f, err := os.Create(dataDisk) + if err != nil { + return err + } + defer f.Close() + return f.Truncate(int64(size)) +} + +// ResizeRawDataDisk resizes a raw data disk. +func ResizeRawDataDisk(dir string, size int) error { + dataDisk := filepath.Join(dir, filenames.DataDisk) + return os.Truncate(dataDisk, int64(size)) +} + // ConvertToRaw converts a source disk into a raw disk. // source and dest may be same. // ConvertToRaw is a NOP if source == dest, and no resizing is needed. @@ -106,7 +129,7 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b func convertRawToRaw(source, dest string, size *int64) error { if source != dest { // continuity attempts clonefile - if err := fs.CopyFile(dest, source); err != nil { + if err := containerdfs.CopyFile(dest, source); err != nil { return fmt.Errorf("failed to copy %q into %q: %w", source, dest, err) } }