Skip to content

Commit a43cf63

Browse files
authored
Merge pull request #4327 from norio-nomura/vz-support-asif-as-diffdisk
pkg/driver/vz: Support ASIF as diffdisk
2 parents fcffb10 + cbe7241 commit a43cf63

File tree

16 files changed

+246
-48
lines changed

16 files changed

+246
-48
lines changed

pkg/driver/krunkit/krunkit_darwin_arm64.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"strconv"
1515

1616
"github.com/docker/go-units"
17+
"github.com/lima-vm/go-qcow2reader/image/raw"
1718
"github.com/sirupsen/logrus"
1819

1920
"github.com/lima-vm/lima/v2/pkg/driver/vz"
@@ -65,7 +66,7 @@ func Cmdline(inst *limatype.Instance) (*exec.Cmd, error) {
6566
}
6667
extraDiskPath := filepath.Join(disk.Dir, filenames.DataDisk)
6768
logrus.Infof("Mounting disk %q on %q", disk.Name, disk.MountPoint)
68-
if cerr := diskUtil.ConvertToRaw(ctx, extraDiskPath, extraDiskPath, nil, true); cerr != nil {
69+
if cerr := diskUtil.Convert(ctx, raw.Type, extraDiskPath, extraDiskPath, nil, true); cerr != nil {
6970
return nil, fmt.Errorf("failed to convert extra disk %q to raw: %w", extraDiskPath, cerr)
7071
}
7172
args = append(args, "--device", fmt.Sprintf("virtio-blk,path=%s,format=raw", extraDiskPath))

pkg/driver/krunkit/krunkit_driver_darwin_arm64.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"time"
1818

1919
"github.com/coreos/go-semver/semver"
20+
"github.com/lima-vm/go-qcow2reader/image/raw"
2021
"github.com/sirupsen/logrus"
2122

2223
"github.com/lima-vm/lima/v2/pkg/driver"
@@ -60,7 +61,7 @@ func (l *LimaKrunkitDriver) Configure(inst *limatype.Instance) *driver.Configure
6061

6162
func (l *LimaKrunkitDriver) CreateDisk(ctx context.Context) error {
6263
// Krunkit also supports qcow2 disks but raw is faster to create and use.
63-
return driverutil.EnsureDiskRaw(ctx, l.Instance)
64+
return driverutil.EnsureDisk(ctx, l.Instance.Dir, *l.Instance.Config.Disk, raw.Type)
6465
}
6566

6667
func (l *LimaKrunkitDriver) Start(ctx context.Context) (chan error, error) {

pkg/driver/vz/vm_darwin.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os"
1515
"path/filepath"
1616
"runtime"
17+
"slices"
1718
"strconv"
1819
"sync"
1920
"syscall"
@@ -22,6 +23,8 @@ import (
2223
"github.com/coreos/go-semver/semver"
2324
"github.com/docker/go-units"
2425
"github.com/lima-vm/go-qcow2reader"
26+
"github.com/lima-vm/go-qcow2reader/image"
27+
"github.com/lima-vm/go-qcow2reader/image/asif"
2528
"github.com/lima-vm/go-qcow2reader/image/raw"
2629
"github.com/sirupsen/logrus"
2730

@@ -448,8 +451,9 @@ func validateDiskFormat(diskPath string) error {
448451
if err != nil {
449452
return fmt.Errorf("failed to detect the format of %q: %w", diskPath, err)
450453
}
451-
if t := img.Type(); t != raw.Type {
452-
return fmt.Errorf("expected the format of %q to be %q, got %q", diskPath, raw.Type, t)
454+
supportedDiskTypes := []image.Type{raw.Type, asif.Type}
455+
if t := img.Type(); !slices.Contains(supportedDiskTypes, t) {
456+
return fmt.Errorf("expected the format of %q to be one of %v, got %q", diskPath, supportedDiskTypes, t)
453457
}
454458
// TODO: ensure that the disk is formatted with GPT or ISO9660
455459
return nil
@@ -513,7 +517,7 @@ func attachDisks(ctx context.Context, inst *limatype.Instance, vmConfig *vz.Virt
513517
// ConvertToRaw is a NOP if no conversion is needed
514518
logrus.Debugf("Converting extra disk %q to a raw disk (if it is not a raw)", extraDiskPath)
515519

516-
if err = diskUtil.ConvertToRaw(ctx, extraDiskPath, extraDiskPath, nil, true); err != nil {
520+
if err = diskUtil.Convert(ctx, raw.Type, extraDiskPath, extraDiskPath, nil, true); err != nil {
517521
return fmt.Errorf("failed to convert extra disk %q to a raw disk: %w", extraDiskPath, err)
518522
}
519523
extraDiskPathAttachment, err := vz.NewDiskImageStorageDeviceAttachmentWithCacheAndSync(extraDiskPath, false, diskImageCachingMode, vz.DiskImageSynchronizationModeFsync)

pkg/driver/vz/vz_driver_darwin.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import (
1717

1818
"github.com/Code-Hex/vz/v3"
1919
"github.com/coreos/go-semver/semver"
20+
"github.com/lima-vm/go-qcow2reader/image"
21+
"github.com/lima-vm/go-qcow2reader/image/asif"
22+
"github.com/lima-vm/go-qcow2reader/image/raw"
2023
"github.com/sirupsen/logrus"
2124

2225
"github.com/lima-vm/lima/v2/pkg/driver"
@@ -75,11 +78,12 @@ const Enabled = true
7578
type LimaVzDriver struct {
7679
Instance *limatype.Instance
7780

78-
SSHLocalPort int
79-
vSockPort int
80-
virtioPort string
81-
rosettaEnabled bool
82-
rosettaBinFmt bool
81+
SSHLocalPort int
82+
vSockPort int
83+
virtioPort string
84+
rosettaEnabled bool
85+
rosettaBinFmt bool
86+
diskImageFormat image.Type
8387

8488
machine *virtualMachineWrapper
8589
waitSSHLocalPortAccessible <-chan any
@@ -125,6 +129,11 @@ func (l *LimaVzDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriv
125129
if vzOpts.Rosetta.BinFmt != nil {
126130
l.rosettaBinFmt = *vzOpts.Rosetta.BinFmt
127131
}
132+
if vzOpts.DiskImageFormat != nil {
133+
l.diskImageFormat = *vzOpts.DiskImageFormat
134+
} else {
135+
l.diskImageFormat = raw.Type
136+
}
128137

129138
return &driver.ConfiguredDriver{
130139
Driver: l,
@@ -165,6 +174,9 @@ func (l *LimaVzDriver) FillConfig(ctx context.Context, cfg *limatype.LimaYAML, _
165174
if vzOpts.Rosetta.BinFmt == nil {
166175
vzOpts.Rosetta.BinFmt = ptr.Of(false)
167176
}
177+
if vzOpts.DiskImageFormat == nil {
178+
vzOpts.DiskImageFormat = ptr.Of(raw.Type)
179+
}
168180

169181
var opts any
170182
if err := limayaml.Convert(vzOpts, &opts, ""); err != nil {
@@ -290,6 +302,19 @@ func validateConfig(_ context.Context, cfg *limatype.LimaYAML) error {
290302
default:
291303
logrus.Warnf("field `video.display` must be \"vz\", \"default\", or \"none\" for VZ driver , got %q", videoDisplay)
292304
}
305+
var vzOpts limatype.VZOpts
306+
if err := limayaml.Convert(cfg.VMOpts[limatype.VZ], &vzOpts, "vmOpts.vz"); err != nil {
307+
logrus.WithError(err).Warnf("Couldn't convert %q", cfg.VMOpts[limatype.VZ])
308+
}
309+
switch *vzOpts.DiskImageFormat {
310+
case raw.Type:
311+
case asif.Type:
312+
if macOSProductVersion.LessThan(*semver.New("26.0.0")) {
313+
return fmt.Errorf("vmOpts.vz.diskImageFormat=%q requires macOS 26 or higher to run, got %q", asif.Type, macOSProductVersion)
314+
}
315+
default:
316+
return fmt.Errorf("field `vmOpts.vz.diskImageFormat` must be %q or %q, got %q", raw.Type, asif.Type, *vzOpts.DiskImageFormat)
317+
}
293318
return nil
294319
}
295320

@@ -299,7 +324,7 @@ func (l *LimaVzDriver) Create(_ context.Context) error {
299324
}
300325

301326
func (l *LimaVzDriver) CreateDisk(ctx context.Context) error {
302-
return driverutil.EnsureDiskRaw(ctx, l.Instance)
327+
return driverutil.EnsureDisk(ctx, l.Instance.Dir, *l.Instance.Config.Disk, l.diskImageFormat)
303328
}
304329

305330
func (l *LimaVzDriver) Start(ctx context.Context) (chan error, error) {

pkg/driverutil/disk.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,27 @@ import (
1111
"path/filepath"
1212

1313
"github.com/docker/go-units"
14+
"github.com/lima-vm/go-qcow2reader/image"
1415

1516
"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
1617
"github.com/lima-vm/lima/v2/pkg/iso9660util"
17-
"github.com/lima-vm/lima/v2/pkg/limatype"
1818
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
1919
)
2020

21-
func EnsureDiskRaw(ctx context.Context, inst *limatype.Instance) error {
22-
diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk)
21+
// EnsureDisk ensures that the diff disk exists with the specified size and format.
22+
func EnsureDisk(ctx context.Context, instDir, diskSize string, diskImageFormat image.Type) error {
23+
diffDisk := filepath.Join(instDir, filenames.DiffDisk)
2324
if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) {
2425
// disk is already ensured
2526
return err
2627
}
2728

2829
diskUtil := proxyimgutil.NewDiskUtil(ctx)
2930

30-
baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)
31+
baseDisk := filepath.Join(instDir, filenames.BaseDisk)
3132

32-
diskSize, _ := units.RAMInBytes(*inst.Config.Disk)
33-
if diskSize == 0 {
33+
diskSizeInBytes, _ := units.RAMInBytes(diskSize)
34+
if diskSizeInBytes == 0 {
3435
return nil
3536
}
3637
isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk)
@@ -51,8 +52,10 @@ func EnsureDiskRaw(ctx context.Context, inst *limatype.Instance) error {
5152
}
5253
return diffDiskF.Close()
5354
}
54-
if err = diskUtil.ConvertToRaw(ctx, baseDisk, diffDisk, &diskSize, false); err != nil {
55-
return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err)
55+
// Check whether to use ASIF format
56+
57+
if err = diskUtil.Convert(ctx, diskImageFormat, baseDisk, diffDisk, &diskSizeInBytes, false); err != nil {
58+
return fmt.Errorf("failed to convert %q to a disk %q: %w", baseDisk, diffDisk, err)
5659
}
5760
return err
5861
}

pkg/imgutil/manager.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package imgutil
66
import (
77
"context"
88
"os"
9+
10+
"github.com/lima-vm/go-qcow2reader/image"
911
)
1012

1113
// ImageDiskManager defines the common operations for disk image utilities.
@@ -16,8 +18,8 @@ type ImageDiskManager interface {
1618
// ResizeDisk resizes an existing disk image to the specified size.
1719
ResizeDisk(ctx context.Context, disk string, size int64) error
1820

19-
// ConvertToRaw converts a disk image to raw format.
20-
ConvertToRaw(ctx context.Context, source, dest string, size *int64, allowSourceWithBackingFile bool) error
21+
// Convert converts a disk image to the specified format.
22+
Convert(ctx context.Context, imageType image.Type, source, dest string, size *int64, allowSourceWithBackingFile bool) error
2123

2224
// MakeSparse makes a file sparse, starting from the specified offset.
2325
MakeSparse(ctx context.Context, f *os.File, offset int64) error
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package asifutil
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"os"
10+
"os/exec"
11+
"strings"
12+
)
13+
14+
// NewAttachedASIF creates a new ASIF image file at the specified path with the given size
15+
// and attaches it, returning the attached device path and an open file handle.
16+
// The caller is responsible for detaching the ASIF image device when done.
17+
func NewAttachedASIF(path string, size int64) (string, *os.File, error) {
18+
createArgs := []string{"image", "create", "blank", "--fs", "none", "--format", "ASIF", "--size", fmt.Sprintf("%d", size), path}
19+
if err := exec.CommandContext(context.Background(), "diskutil", createArgs...).Run(); err != nil {
20+
return "", nil, fmt.Errorf("failed to create ASIF image %q: %w", path, err)
21+
}
22+
attachArgs := []string{"image", "attach", "--noMount", path}
23+
out, err := exec.CommandContext(context.Background(), "diskutil", attachArgs...).Output()
24+
if err != nil {
25+
return "", nil, fmt.Errorf("failed to attach ASIF image %q: %w", path, err)
26+
}
27+
devicePath := strings.TrimSpace(string(out))
28+
f, err := os.OpenFile(devicePath, os.O_RDWR, 0o644)
29+
if err != nil {
30+
_ = DetachASIF(devicePath)
31+
return "", nil, fmt.Errorf("failed to open ASIF device %q: %w", devicePath, err)
32+
}
33+
return devicePath, f, err
34+
}
35+
36+
// DetachASIF detaches the ASIF image device at the specified path.
37+
func DetachASIF(devicePath string) error {
38+
if output, err := exec.CommandContext(context.Background(), "hdiutil", "detach", devicePath).CombinedOutput(); err != nil {
39+
return fmt.Errorf("failed to detach ASIF image %q: %w: %s", devicePath, err, output)
40+
}
41+
return nil
42+
}
43+
44+
// ResizeASIF resizes the ASIF image at the specified path to the given size.
45+
func ResizeASIF(path string, size int64) error {
46+
resizeArgs := []string{"image", "resize", "--size", fmt.Sprintf("%d", size), path}
47+
if output, err := exec.CommandContext(context.Background(), "diskutil", resizeArgs...).CombinedOutput(); err != nil {
48+
return fmt.Errorf("failed to resize ASIF image %q: %w: %s", path, err, output)
49+
}
50+
return nil
51+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//go:build !darwin
2+
3+
// SPDX-FileCopyrightText: Copyright The Lima Authors
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package asifutil
7+
8+
import (
9+
"errors"
10+
"os"
11+
)
12+
13+
var ErrASIFNotSupported = errors.New("ASIF is only supported on macOS")
14+
15+
func NewAttachedASIF(_ string, _ int64) (string, *os.File, error) {
16+
return "", nil, ErrASIFNotSupported
17+
}
18+
19+
func DetachASIF(_ string) error {
20+
return ErrASIFNotSupported
21+
}
22+
23+
func ResizeASIF(_ string, _ int64) error {
24+
return ErrASIFNotSupported
25+
}

pkg/imgutil/nativeimgutil/fuzz_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"path/filepath"
99
"testing"
1010

11+
"github.com/lima-vm/go-qcow2reader/image/raw"
1112
"gotest.tools/v3/assert"
1213
)
1314

@@ -17,6 +18,6 @@ func FuzzConvertToRaw(f *testing.F) {
1718
destPath := filepath.Join(t.TempDir(), "dest.img")
1819
err := os.WriteFile(srcPath, imgData, 0o600)
1920
assert.NilError(t, err)
20-
_ = convertToRaw(srcPath, destPath, &size, withBacking)
21+
_ = convertTo(raw.Type, srcPath, destPath, &size, withBacking)
2122
})
2223
}

0 commit comments

Comments
 (0)