From ae948cbde7abcec8ca6a96bec87ccdb4e1923fce Mon Sep 17 00:00:00 2001 From: Praful Khanduri Date: Tue, 1 Jul 2025 21:03:02 +0530 Subject: [PATCH] defined a common interface for nativeimgutil & qemuimgutil Signed-off-by: Praful Khanduri --- cmd/limactl/disk.go | 17 +-- pkg/imgutil/manager.go | 23 +++ pkg/{ => imgutil}/nativeimgutil/fuzz_test.go | 2 +- .../nativeimgutil/nativeimgutil.go | 75 ++++++---- .../nativeimgutil/nativeimgutil_test.go | 14 +- pkg/imgutil/proxyimgutil/proxyimgutil.go | 75 ++++++++++ .../qemuimgutil/qemuimgutil.go} | 137 +++++++++++++----- .../qemuimgutil/qemuimgutil_test.go} | 8 +- pkg/instance/start.go | 11 +- pkg/qemu/qemu.go | 10 +- pkg/vz/disk.go | 12 +- pkg/vz/vm_darwin.go | 7 +- 12 files changed, 280 insertions(+), 111 deletions(-) create mode 100644 pkg/imgutil/manager.go rename pkg/{ => imgutil}/nativeimgutil/fuzz_test.go (89%) rename pkg/{ => imgutil}/nativeimgutil/nativeimgutil.go (72%) rename pkg/{ => imgutil}/nativeimgutil/nativeimgutil_test.go (89%) create mode 100644 pkg/imgutil/proxyimgutil/proxyimgutil.go rename pkg/{qemu/imgutil/imgutil.go => imgutil/qemuimgutil/qemuimgutil.go} (72%) rename pkg/{qemu/imgutil/imgutil_test.go => imgutil/qemuimgutil/qemuimgutil_test.go} (97%) diff --git a/cmd/limactl/disk.go b/cmd/limactl/disk.go index cf95c0162cf..7f19b069c5d 100644 --- a/cmd/limactl/disk.go +++ b/cmd/limactl/disk.go @@ -18,8 +18,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/lima-vm/lima/pkg/nativeimgutil" - "github.com/lima-vm/lima/pkg/qemu/imgutil" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" ) @@ -113,11 +112,8 @@ func diskCreateAction(cmd *cobra.Command, args []string) error { // qemu may not be available, use it only if needed. dataDisk := filepath.Join(diskDir, filenames.DataDisk) - if format == "raw" { - err = nativeimgutil.CreateRawDisk(dataDisk, int(diskSize)) - } else { - err = imgutil.CreateDisk(dataDisk, format, int(diskSize)) - } + diskUtil := proxyimgutil.NewDiskUtil() + err = diskUtil.CreateDisk(dataDisk, diskSize) if err != nil { rerr := os.RemoveAll(diskDir) if rerr != nil { @@ -410,11 +406,8 @@ func diskResizeAction(cmd *cobra.Command, args []string) error { // qemu may not be available, use it only if needed. dataDisk := filepath.Join(disk.Dir, filenames.DataDisk) - if disk.Format == "raw" { - err = nativeimgutil.ResizeRawDisk(dataDisk, int(diskSize)) - } else { - err = imgutil.ResizeDisk(dataDisk, disk.Format, int(diskSize)) - } + diskUtil := proxyimgutil.NewDiskUtil() + err = diskUtil.ResizeDisk(dataDisk, diskSize) if err != nil { return fmt.Errorf("failed to resize disk %q: %w", diskName, err) } diff --git a/pkg/imgutil/manager.go b/pkg/imgutil/manager.go new file mode 100644 index 00000000000..a7f16ad7776 --- /dev/null +++ b/pkg/imgutil/manager.go @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package imgutil + +import ( + "os" +) + +// ImageDiskManager defines the common operations for disk image utilities. +type ImageDiskManager interface { + // CreateDisk creates a new disk image with the specified size. + CreateDisk(disk string, size int64) error + + // ResizeDisk resizes an existing disk image to the specified size. + ResizeDisk(disk string, size int64) error + + // ConvertToRaw converts a disk image to raw format. + ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error + + // MakeSparse makes a file sparse, starting from the specified offset. + MakeSparse(f *os.File, offset int64) error +} diff --git a/pkg/nativeimgutil/fuzz_test.go b/pkg/imgutil/nativeimgutil/fuzz_test.go similarity index 89% rename from pkg/nativeimgutil/fuzz_test.go rename to pkg/imgutil/nativeimgutil/fuzz_test.go index 7a3face5a24..204d5583fdc 100644 --- a/pkg/nativeimgutil/fuzz_test.go +++ b/pkg/imgutil/nativeimgutil/fuzz_test.go @@ -17,6 +17,6 @@ func FuzzConvertToRaw(f *testing.F) { destPath := filepath.Join(t.TempDir(), "dest.img") err := os.WriteFile(srcPath, imgData, 0o600) assert.NilError(t, err) - _ = ConvertToRaw(srcPath, destPath, &size, withBacking) + _ = convertToRaw(srcPath, destPath, &size, withBacking) }) } diff --git a/pkg/nativeimgutil/nativeimgutil.go b/pkg/imgutil/nativeimgutil/nativeimgutil.go similarity index 72% rename from pkg/nativeimgutil/nativeimgutil.go rename to pkg/imgutil/nativeimgutil/nativeimgutil.go index b4ccb0eab0e..910512d9a3d 100644 --- a/pkg/nativeimgutil/nativeimgutil.go +++ b/pkg/imgutil/nativeimgutil/nativeimgutil.go @@ -28,36 +28,19 @@ import ( // aligned to 512 bytes. const sectorSize = 512 -// RoundUp rounds size up to sectorSize. -func RoundUp(size int) int { +// NativeImageUtil is the native implementation of the imgutil.ImageDiskManager. +type NativeImageUtil struct{} + +// roundUp rounds size up to sectorSize. +func roundUp(size int64) int64 { sectors := (size + sectorSize - 1) / sectorSize return sectors * sectorSize } -// CreateRawDisk creates an empty raw data disk. -func CreateRawDisk(disk string, size int) error { - if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { - return err - } - f, err := os.Create(disk) - if err != nil { - return err - } - defer f.Close() - roundedSize := RoundUp(size) - return f.Truncate(int64(roundedSize)) -} - -// ResizeRawDisk resizes a raw data disk. -func ResizeRawDisk(disk string, size int) error { - roundedSize := RoundUp(size) - return os.Truncate(disk, int64(roundedSize)) -} - -// ConvertToRaw converts a source disk into a raw disk. +// 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. -func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { +// convertToRaw is a NOP if source == dest, and no resizing is needed. +func convertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { srcF, err := os.Open(source) if err != nil { return err @@ -106,7 +89,7 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b // Truncating before copy eliminates the seeks during copy and provide a // hint to the file system that may minimize allocations and fragmentation // of the file. - if err := MakeSparse(destTmpF, srcImg.Size()); err != nil { + if err := makeSparse(destTmpF, srcImg.Size()); err != nil { return err } @@ -125,7 +108,7 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b // Resize if size != nil { logrus.Infof("Expanding to %s", units.BytesSize(float64(*size))) - if err = MakeSparse(destTmpF, *size); err != nil { + if err = makeSparse(destTmpF, *size); err != nil { return err } } @@ -153,7 +136,7 @@ func convertRawToRaw(source, dest string, size *int64) error { if err != nil { return err } - if err = MakeSparse(destF, *size); err != nil { + if err = makeSparse(destF, *size); err != nil { _ = destF.Close() return err } @@ -162,9 +145,39 @@ func convertRawToRaw(source, dest string, size *int64) error { return nil } -func MakeSparse(f *os.File, n int64) error { - if _, err := f.Seek(n, io.SeekStart); err != nil { +func makeSparse(f *os.File, offset int64) error { + if _, err := f.Seek(offset, io.SeekStart); err != nil { return err } - return f.Truncate(n) + return f.Truncate(offset) +} + +// CreateDisk creates a new disk image with the specified size. +func (n *NativeImageUtil) CreateDisk(disk string, size int64) error { + if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { + return err + } + f, err := os.Create(disk) + if err != nil { + return err + } + defer f.Close() + roundedSize := roundUp(size) + return f.Truncate(int64(roundedSize)) +} + +// ConvertToRaw converts a disk image to raw format. +func (n *NativeImageUtil) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { + return convertToRaw(source, dest, size, allowSourceWithBackingFile) +} + +// ResizeDisk resizes an existing disk image to the specified size. +func (n *NativeImageUtil) ResizeDisk(disk string, size int64) error { + roundedSize := roundUp(size) + return os.Truncate(disk, roundedSize) +} + +// MakeSparse makes a file sparse, starting from the specified offset. +func (n *NativeImageUtil) MakeSparse(f *os.File, offset int64) error { + return makeSparse(f, offset) } diff --git a/pkg/nativeimgutil/nativeimgutil_test.go b/pkg/imgutil/nativeimgutil/nativeimgutil_test.go similarity index 89% rename from pkg/nativeimgutil/nativeimgutil_test.go rename to pkg/imgutil/nativeimgutil/nativeimgutil_test.go index 943a8a8c92d..466edc626c9 100644 --- a/pkg/nativeimgutil/nativeimgutil_test.go +++ b/pkg/imgutil/nativeimgutil/nativeimgutil_test.go @@ -15,8 +15,8 @@ import ( func TestRoundUp(t *testing.T) { tests := []struct { - Size int - Rounded int + Size int64 + Rounded int64 }{ {0, 0}, {1, 512}, @@ -25,7 +25,7 @@ func TestRoundUp(t *testing.T) { {123456789, 123457024}, } for _, tc := range tests { - if RoundUp(tc.Size) != tc.Rounded { + if roundUp(tc.Size) != tc.Rounded { t.Errorf("expected %d, got %d", tc.Rounded, tc.Size) } } @@ -63,7 +63,7 @@ func TestConvertToRaw(t *testing.T) { t.Run("qcow without backing file", func(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) - err = ConvertToRaw(qcowImage.Name(), resultImage, nil, false) + err = convertToRaw(qcowImage.Name(), resultImage, nil, false) assert.NilError(t, err) assertFileEquals(t, rawImage.Name(), resultImage) }) @@ -71,7 +71,7 @@ func TestConvertToRaw(t *testing.T) { t.Run("qcow with backing file", func(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) - err = ConvertToRaw(qcowImage.Name(), resultImage, nil, true) + err = convertToRaw(qcowImage.Name(), resultImage, nil, true) assert.NilError(t, err) assertFileEquals(t, rawImage.Name(), resultImage) }) @@ -80,7 +80,7 @@ func TestConvertToRaw(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) size := int64(2_097_152) // 2mb - err = ConvertToRaw(qcowImage.Name(), resultImage, &size, false) + err = convertToRaw(qcowImage.Name(), resultImage, &size, false) assert.NilError(t, err) assertFileEquals(t, rawImageExtended.Name(), resultImage) }) @@ -88,7 +88,7 @@ func TestConvertToRaw(t *testing.T) { t.Run("raw", func(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) - err = ConvertToRaw(rawImage.Name(), resultImage, nil, false) + err = convertToRaw(rawImage.Name(), resultImage, nil, false) assert.NilError(t, err) assertFileEquals(t, rawImage.Name(), resultImage) }) diff --git a/pkg/imgutil/proxyimgutil/proxyimgutil.go b/pkg/imgutil/proxyimgutil/proxyimgutil.go new file mode 100644 index 00000000000..7ff88abcef0 --- /dev/null +++ b/pkg/imgutil/proxyimgutil/proxyimgutil.go @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package proxyimgutil + +import ( + "errors" + "os" + "os/exec" + + "github.com/lima-vm/lima/pkg/imgutil" + "github.com/lima-vm/lima/pkg/imgutil/nativeimgutil" + "github.com/lima-vm/lima/pkg/imgutil/qemuimgutil" +) + +// ImageDiskManager is a proxy implementation of imgutil.ImageDiskManager that uses both QEMU and native image utilities. +type ImageDiskManager struct { + qemu imgutil.ImageDiskManager + native imgutil.ImageDiskManager +} + +// NewDiskUtil returns a new instance of ImageDiskManager that uses both QEMU and native image utilities. +func NewDiskUtil() imgutil.ImageDiskManager { + return &ImageDiskManager{ + qemu: &qemuimgutil.QemuImageUtil{DefaultFormat: qemuimgutil.QemuImgFormat}, + native: &nativeimgutil.NativeImageUtil{}, + } +} + +// CreateDisk creates a new disk image with the specified size. +func (p *ImageDiskManager) CreateDisk(disk string, size int64) error { + err := p.qemu.CreateDisk(disk, size) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.CreateDisk(disk, size) + } + return err +} + +// ResizeDisk resizes an existing disk image to the specified size. +func (p *ImageDiskManager) ResizeDisk(disk string, size int64) error { + err := p.qemu.ResizeDisk(disk, size) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.ResizeDisk(disk, size) + } + return err +} + +// ConvertToRaw converts a disk image to raw format. +func (p *ImageDiskManager) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { + err := p.qemu.ConvertToRaw(source, dest, size, allowSourceWithBackingFile) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.ConvertToRaw(source, dest, size, allowSourceWithBackingFile) + } + return err +} + +func (p *ImageDiskManager) MakeSparse(f *os.File, offset int64) error { + err := p.qemu.MakeSparse(f, offset) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.MakeSparse(f, offset) + } + return err +} diff --git a/pkg/qemu/imgutil/imgutil.go b/pkg/imgutil/qemuimgutil/qemuimgutil.go similarity index 72% rename from pkg/qemu/imgutil/imgutil.go rename to pkg/imgutil/qemuimgutil/qemuimgutil.go index 18512bf0bf7..fc065450c59 100644 --- a/pkg/qemu/imgutil/imgutil.go +++ b/pkg/imgutil/qemuimgutil/qemuimgutil.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package imgutil +package qemuimgutil import ( "bytes" @@ -16,6 +16,28 @@ import ( "github.com/sirupsen/logrus" ) +const QemuImgFormat = "qcow2" + +// QemuImageUtil is the QEMU implementation of the imgutil Interface. +type QemuImageUtil struct { + DefaultFormat string // Default disk format, e.g., "qcow2" +} + +// Info corresponds to the output of `qemu-img info --output=json FILE`. +type Info struct { + Filename string `json:"filename,omitempty"` // since QEMU 1.3 + Format string `json:"format,omitempty"` // since QEMU 1.3 + VSize int64 `json:"virtual-size,omitempty"` // since QEMU 1.3 + ActualSize int64 `json:"actual-size,omitempty"` // since QEMU 1.3 + DirtyFlag bool `json:"dirty-flag,omitempty"` // since QEMU 5.2 + ClusterSize int `json:"cluster-size,omitempty"` // since QEMU 1.3 + BackingFilename string `json:"backing-filename,omitempty"` // since QEMU 1.3 + FullBackingFilename string `json:"full-backing-filename,omitempty"` // since QEMU 1.3 + BackingFilenameFormat string `json:"backing-filename-format,omitempty"` // since QEMU 1.3 + FormatSpecific *InfoFormatSpecific `json:"format-specific,omitempty"` // since QEMU 1.7 + Children []InfoChild `json:"children,omitempty"` // since QEMU 8.0 +} + type InfoChild struct { Name string `json:"name,omitempty"` // since QEMU 8.0 Info Info `json:"info,omitempty"` // since QEMU 8.0 @@ -26,22 +48,8 @@ type InfoFormatSpecific struct { Data json.RawMessage `json:"data,omitempty"` // since QEMU 1.7 } -func CreateDisk(disk, format string, size int) error { - if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { - // disk already exists - return err - } - - args := []string{"create", "-f", format, disk, strconv.Itoa(size)} - cmd := exec.Command("qemu-img", args...) - if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err) - } - return nil -} - -func ResizeDisk(disk, format string, size int) error { - args := []string{"resize", "-f", format, disk, strconv.Itoa(size)} +func resizeDisk(disk, format string, size int64) error { + args := []string{"resize", "-f", format, disk, strconv.FormatInt(size, 10)} cmd := exec.Command("qemu-img", args...) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err) @@ -94,22 +102,7 @@ type InfoFormatSpecificDataVmdkExtent struct { ClusterSize int `json:"cluster-size,omitempty"` // since QEMU 1.7 } -// Info corresponds to the output of `qemu-img info --output=json FILE`. -type Info struct { - Filename string `json:"filename,omitempty"` // since QEMU 1.3 - Format string `json:"format,omitempty"` // since QEMU 1.3 - VSize int64 `json:"virtual-size,omitempty"` // since QEMU 1.3 - ActualSize int64 `json:"actual-size,omitempty"` // since QEMU 1.3 - DirtyFlag bool `json:"dirty-flag,omitempty"` // since QEMU 5.2 - ClusterSize int `json:"cluster-size,omitempty"` // since QEMU 1.3 - BackingFilename string `json:"backing-filename,omitempty"` // since QEMU 1.3 - FullBackingFilename string `json:"full-backing-filename,omitempty"` // since QEMU 1.3 - BackingFilenameFormat string `json:"backing-filename-format,omitempty"` // since QEMU 1.3 - FormatSpecific *InfoFormatSpecific `json:"format-specific,omitempty"` // since QEMU 1.7 - Children []InfoChild `json:"children,omitempty"` // since QEMU 8.0 -} - -func ConvertToRaw(source, dest string) error { +func convertToRaw(source, dest string) error { var stdout, stderr bytes.Buffer cmd := exec.Command("qemu-img", "convert", "-O", "raw", source, dest) cmd.Stdout = &stdout @@ -121,7 +114,7 @@ func ConvertToRaw(source, dest string) error { return nil } -func ParseInfo(b []byte) (*Info, error) { +func parseInfo(b []byte) (*Info, error) { var imgInfo Info if err := json.Unmarshal(b, &imgInfo); err != nil { return nil, err @@ -129,7 +122,7 @@ func ParseInfo(b []byte) (*Info, error) { return &imgInfo, nil } -func GetInfo(f string) (*Info, error) { +func getInfo(f string) (*Info, error) { var stdout, stderr bytes.Buffer cmd := exec.Command("qemu-img", "info", "--output=json", "--force-share", f) cmd.Stdout = &stdout @@ -138,10 +131,80 @@ func GetInfo(f string) (*Info, error) { return nil, fmt.Errorf("failed to run %v: stdout=%q, stderr=%q: %w", cmd.Args, stdout.String(), stderr.String(), err) } - return ParseInfo(stdout.Bytes()) + return parseInfo(stdout.Bytes()) +} + +// CreateDisk creates a new disk image with the specified size. +func (q *QemuImageUtil) CreateDisk(disk string, size int64) error { + if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { + // disk already exists + return err + } + + args := []string{"create", "-f", q.DefaultFormat, disk, strconv.FormatInt(size, 10)} + cmd := exec.Command("qemu-img", args...) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err) + } + return nil +} + +// ResizeDisk resizes an existing disk image to the specified size. +func (q *QemuImageUtil) ResizeDisk(disk string, size int64) error { + info, err := getInfo(disk) + if err != nil { + return fmt.Errorf("failed to get info for disk %q: %w", disk, err) + } + return resizeDisk(disk, info.Format, size) +} + +// MakeSparse is a stub implementation as the qemu package doesn't provide this functionality. +func (q *QemuImageUtil) MakeSparse(_ *os.File, _ int64) error { + return nil +} + +// GetInfo retrieves the information of a disk image. +func GetInfo(path string) (*Info, error) { + qemuInfo, err := getInfo(path) + if err != nil { + return nil, err + } + + return qemuInfo, nil +} + +// ConvertToRaw converts a disk image to raw format. +func (q *QemuImageUtil) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { + if !allowSourceWithBackingFile { + info, err := getInfo(source) + if err != nil { + return fmt.Errorf("failed to get info for source disk %q: %w", source, err) + } + if info.BackingFilename != "" || info.FullBackingFilename != "" { + return fmt.Errorf("qcow2 image %q has an unexpected backing file: %q", source, info.BackingFilename) + } + } + + if err := convertToRaw(source, dest); err != nil { + return err + } + + if size != nil { + destInfo, err := getInfo(dest) + if err != nil { + return fmt.Errorf("failed to get info for converted disk %q: %w", dest, err) + } + + if *size > destInfo.VSize { + return resizeDisk(dest, "raw", *size) + } + } + + return nil } -func AcceptableAsBasedisk(info *Info) error { +// AcceptableAsBaseDisk checks if a disk image is acceptable as a base disk. +func AcceptableAsBaseDisk(info *Info) error { switch info.Format { case "qcow2", "raw": // NOP diff --git a/pkg/qemu/imgutil/imgutil_test.go b/pkg/imgutil/qemuimgutil/qemuimgutil_test.go similarity index 97% rename from pkg/qemu/imgutil/imgutil_test.go rename to pkg/imgutil/qemuimgutil/qemuimgutil_test.go index bb630f48162..b0f39d43b41 100644 --- a/pkg/qemu/imgutil/imgutil_test.go +++ b/pkg/imgutil/qemuimgutil/qemuimgutil_test.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package imgutil +package qemuimgutil import ( "testing" @@ -52,7 +52,7 @@ func TestParseInfo(t *testing.T) { "dirty-flag": false }` - info, err := ParseInfo([]byte(s)) + info, err := parseInfo([]byte(s)) assert.NilError(t, err) assert.Equal(t, 1, len(info.Children)) assert.Check(t, info.FormatSpecific != nil) @@ -104,7 +104,7 @@ func TestParseInfo(t *testing.T) { "backing-filename": "foo.qcow2", "dirty-flag": false }` - info, err := ParseInfo([]byte(s)) + info, err := parseInfo([]byte(s)) assert.NilError(t, err) assert.Equal(t, 1, len(info.Children)) assert.Equal(t, "foo.qcow2", info.BackingFilename) @@ -202,7 +202,7 @@ func TestParseInfo(t *testing.T) { }, "dirty-flag": false }` - info, err := ParseInfo([]byte(s)) + info, err := parseInfo([]byte(s)) assert.NilError(t, err) assert.Equal(t, 3, len(info.Children)) assert.Equal(t, "foo.vmdk", info.Filename) diff --git a/pkg/instance/start.go b/pkg/instance/start.go index 1df00c785a3..acd3741e0bd 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -27,10 +27,9 @@ import ( "github.com/lima-vm/lima/pkg/executil" "github.com/lima-vm/lima/pkg/fileutils" hostagentevents "github.com/lima-vm/lima/pkg/hostagent/events" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/osutil" - "github.com/lima-vm/lima/pkg/qemu/imgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/usrlocalsharelima" @@ -414,7 +413,6 @@ func prepareDiffDisk(inst *store.Instance) error { } diskSize := img.Size() - format := string(img.Type()) if inst.Disk == diskSize { return nil @@ -427,12 +425,9 @@ func prepareDiffDisk(inst *store.Instance) error { return errors.New("diffDisk: Shrinking is currently unavailable") } - if format == "raw" { - err = nativeimgutil.ResizeRawDisk(diffDisk, int(inst.Disk)) - } else { - err = imgutil.ResizeDisk(diffDisk, format, int(inst.Disk)) - } + diskUtil := proxyimgutil.NewDiskUtil() + err = diskUtil.ResizeDisk(diffDisk, inst.Disk) if err != nil { return err } diff --git a/pkg/qemu/qemu.go b/pkg/qemu/qemu.go index 0ba01b616d5..8a54ba8a587 100644 --- a/pkg/qemu/qemu.go +++ b/pkg/qemu/qemu.go @@ -28,12 +28,12 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/fileutils" + "github.com/lima-vm/lima/pkg/imgutil/qemuimgutil" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/lima-vm/lima/pkg/osutil" - "github.com/lima-vm/lima/pkg/qemu/imgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" ) @@ -137,11 +137,11 @@ func EnsureDisk(ctx context.Context, cfg Config) error { if err != nil { return err } - baseDiskInfo, err := imgutil.GetInfo(baseDisk) + baseDiskInfo, err := qemuimgutil.GetInfo(baseDisk) if err != nil { return fmt.Errorf("failed to get the information of base disk %q: %w", baseDisk, err) } - if err = imgutil.AcceptableAsBasedisk(baseDiskInfo); err != nil { + if err = qemuimgutil.AcceptableAsBaseDisk(baseDiskInfo); err != nil { return fmt.Errorf("file %q is not acceptable as the base disk: %w", baseDisk, err) } if baseDiskInfo.Format == "" { @@ -691,11 +691,11 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er if diskSize, _ := units.RAMInBytes(*cfg.LimaYAML.Disk); diskSize > 0 { args = append(args, "-drive", fmt.Sprintf("file=%s,if=virtio,discard=on", diffDisk)) } else if !isBaseDiskCDROM { - baseDiskInfo, err := imgutil.GetInfo(baseDisk) + baseDiskInfo, err := qemuimgutil.GetInfo(baseDisk) if err != nil { return "", nil, fmt.Errorf("failed to get the information of %q: %w", baseDisk, err) } - if err = imgutil.AcceptableAsBasedisk(baseDiskInfo); err != nil { + if err = qemuimgutil.AcceptableAsBaseDisk(baseDiskInfo); err != nil { return "", nil, fmt.Errorf("file %q is not acceptable as the base disk: %w", baseDisk, err) } if baseDiskInfo.Format == "" { diff --git a/pkg/vz/disk.go b/pkg/vz/disk.go index 3fff17c48fd..06194faf5d2 100644 --- a/pkg/vz/disk.go +++ b/pkg/vz/disk.go @@ -14,8 +14,8 @@ import ( "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/fileutils" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/iso9660util" - "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/store/filenames" ) @@ -26,6 +26,8 @@ func EnsureDisk(ctx context.Context, driver *driver.BaseDriver) error { return err } + diskUtil := proxyimgutil.NewDiskUtil() + baseDisk := filepath.Join(driver.Instance.Dir, filenames.BaseDisk) kernel := filepath.Join(driver.Instance.Dir, filenames.Kernel) kernelCmdline := filepath.Join(driver.Instance.Dir, filenames.KernelCmdline) @@ -78,13 +80,15 @@ func EnsureDisk(ctx context.Context, driver *driver.BaseDriver) error { if err != nil { return err } - if err = nativeimgutil.MakeSparse(diffDiskF, diskSize); err != nil { + + err = diskUtil.MakeSparse(diffDiskF, 0) + if err != nil { diffDiskF.Close() - return err + return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err) } return diffDiskF.Close() } - if err = nativeimgutil.ConvertToRaw(baseDisk, diffDisk, &diskSize, false); err != nil { + if err = diskUtil.ConvertToRaw(baseDisk, diffDisk, &diskSize, false); err != nil { return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err) } return err diff --git a/pkg/vz/vm_darwin.go b/pkg/vz/vm_darwin.go index 1f21cf448b0..9ea33a8642b 100644 --- a/pkg/vz/vm_darwin.go +++ b/pkg/vz/vm_darwin.go @@ -25,9 +25,9 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/lima-vm/lima/pkg/osutil" @@ -471,6 +471,8 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura } configurations = append(configurations, diffDisk) + diskUtil := proxyimgutil.NewDiskUtil() + for _, d := range driver.Instance.Config.AdditionalDisks { diskName := d.Name disk, err := store.InspectDisk(diskName) @@ -489,7 +491,8 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura extraDiskPath := filepath.Join(disk.Dir, filenames.DataDisk) // ConvertToRaw is a NOP if no conversion is needed logrus.Debugf("Converting extra disk %q to a raw disk (if it is not a raw)", extraDiskPath) - if err = nativeimgutil.ConvertToRaw(extraDiskPath, extraDiskPath, nil, true); err != nil { + + if err = diskUtil.ConvertToRaw(extraDiskPath, extraDiskPath, nil, true); err != nil { return fmt.Errorf("failed to convert extra disk %q to a raw disk: %w", extraDiskPath, err) } extraDiskPathAttachment, err := vz.NewDiskImageStorageDeviceAttachmentWithCacheAndSync(extraDiskPath, false, diskImageCachingMode, vz.DiskImageSynchronizationModeFsync)