Skip to content

Commit caec7af

Browse files
committed
Add vmType.vz.diskImageFormat that accepts "raw" or "asif"
- Add `vmType.vz.diskImageFormat` that accepts "raw" or "asif" ```yaml vmOpts: vz: diskImageFormat: null # Specify the disk image format: "raw" or "asif". # Currently only applies to the primary disk image. # "asif" requires macOS 26+, and does not support converting back to "raw". # 🟢 Builtin default: "raw" ``` - Dropped `LIMA_VZ_ASIF` environment variable Signed-off-by: Norio Nomura <[email protected]>
1 parent 27ffe60 commit caec7af

File tree

10 files changed

+70
-80
lines changed

10 files changed

+70
-80
lines changed

pkg/driver/vz/disk.go

Lines changed: 11 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,30 @@ import (
99
"fmt"
1010
"os"
1111
"path/filepath"
12-
"strconv"
1312

14-
"github.com/coreos/go-semver/semver"
1513
"github.com/docker/go-units"
16-
"github.com/sirupsen/logrus"
14+
"github.com/lima-vm/go-qcow2reader/image"
15+
"github.com/lima-vm/go-qcow2reader/image/asif"
1716

1817
"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
1918
"github.com/lima-vm/lima/v2/pkg/iso9660util"
20-
"github.com/lima-vm/lima/v2/pkg/limatype"
2119
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
22-
"github.com/lima-vm/lima/v2/pkg/osutil"
2320
)
2421

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

3230
diskUtil := proxyimgutil.NewDiskUtil(ctx)
3331

34-
baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)
32+
baseDisk := filepath.Join(instDir, filenames.BaseDisk)
3533

36-
diskSize, _ := units.RAMInBytes(*inst.Config.Disk)
37-
if diskSize == 0 {
34+
diskSizeInBytes, _ := units.RAMInBytes(diskSize)
35+
if diskSizeInBytes == 0 {
3836
return nil
3937
}
4038
isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk)
@@ -56,44 +54,13 @@ func EnsureDisk(ctx context.Context, inst *limatype.Instance) error {
5654
return diffDiskF.Close()
5755
}
5856
// Check whether to use ASIF format
57+
5958
converter := diskUtil.ConvertToASIF
60-
if !determineUseASIF() {
59+
if diskImageFormat != asif.Type {
6160
converter = diskUtil.ConvertToRaw
6261
}
63-
if err = converter(ctx, baseDisk, diffDisk, &diskSize, false); err != nil {
62+
if err = converter(ctx, baseDisk, diffDisk, &diskSizeInBytes, false); err != nil {
6463
return fmt.Errorf("failed to convert %q to a disk %q: %w", baseDisk, diffDisk, err)
6564
}
6665
return err
6766
}
68-
69-
func determineUseASIF() bool {
70-
var useASIF bool
71-
if macOSProductVersion, err := osutil.ProductVersion(); err != nil {
72-
logrus.WithError(err).Warn("Failed to get macOS product version; using raw format instead of ASIF")
73-
} else if macOSProductVersion.LessThan(*semver.New("26.0.0")) {
74-
logrus.Infof("macOS version %q does not support ASIF format; using raw format instead", macOSProductVersion)
75-
} else {
76-
// TODO: change default to true,
77-
// if the conversion from ASIF to raw while preserving sparsity is implemented,
78-
// or if enough testing is done to confirm that interoperability issues won't happen with ASIF.
79-
useASIF = false
80-
// allow overriding via LIMA_VZ_ASIF environment variable
81-
if envVar := os.Getenv("LIMA_VZ_ASIF"); envVar != "" {
82-
if b, err := strconv.ParseBool(envVar); err != nil {
83-
logrus.WithError(err).Warnf("invalid LIMA_VZ_ASIF value %q", envVar)
84-
} else {
85-
useASIF = b
86-
uses := "ASIF"
87-
if !useASIF {
88-
uses = "raw"
89-
}
90-
logrus.Infof("LIMA_VZ_ASIF=%s; using %s format to diff disk", envVar, uses)
91-
}
92-
} else if useASIF {
93-
logrus.Info("using ASIF format for the disk image")
94-
} else {
95-
logrus.Info("using raw format for the disk image")
96-
}
97-
}
98-
return useASIF
99-
}

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"
@@ -74,11 +77,12 @@ const Enabled = true
7477
type LimaVzDriver struct {
7578
Instance *limatype.Instance
7679

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

8387
machine *virtualMachineWrapper
8488
waitSSHLocalPortAccessible <-chan any
@@ -124,6 +128,11 @@ func (l *LimaVzDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriv
124128
if vzOpts.Rosetta.BinFmt != nil {
125129
l.rosettaBinFmt = *vzOpts.Rosetta.BinFmt
126130
}
131+
if vzOpts.DiskImageFormat != nil {
132+
l.diskImageFormat = *vzOpts.DiskImageFormat
133+
} else {
134+
l.diskImageFormat = raw.Type
135+
}
127136

128137
return &driver.ConfiguredDriver{
129138
Driver: l,
@@ -160,6 +169,9 @@ func (l *LimaVzDriver) FillConfig(ctx context.Context, cfg *limatype.LimaYAML, _
160169
if vzOpts.Rosetta.BinFmt == nil {
161170
vzOpts.Rosetta.BinFmt = ptr.Of(false)
162171
}
172+
if vzOpts.DiskImageFormat == nil {
173+
vzOpts.DiskImageFormat = ptr.Of(raw.Type)
174+
}
163175

164176
var opts any
165177
if err := limayaml.Convert(vzOpts, &opts, ""); err != nil {
@@ -285,6 +297,19 @@ func validateConfig(_ context.Context, cfg *limatype.LimaYAML) error {
285297
default:
286298
logrus.Warnf("field `video.display` must be \"vz\", \"default\", or \"none\" for VZ driver , got %q", videoDisplay)
287299
}
300+
var vzOpts limatype.VZOpts
301+
if err := limayaml.Convert(cfg.VMOpts[limatype.VZ], &vzOpts, "vmOpts.vz"); err != nil {
302+
logrus.WithError(err).Warnf("Couldn't convert %q", cfg.VMOpts[limatype.VZ])
303+
}
304+
switch *vzOpts.DiskImageFormat {
305+
case raw.Type:
306+
case asif.Type:
307+
if macOSProductVersion.LessThan(*semver.New("26.0.0")) {
308+
return fmt.Errorf("vmOpts.vz.diskImageFormat=%q requires macOS 26 or higher to run, got %q", asif.Type, macOSProductVersion)
309+
}
310+
default:
311+
return fmt.Errorf("field `vmOpts.vz.diskImageFormat` must be %q or %q, got %q", raw.Type, asif.Type, *vzOpts.DiskImageFormat)
312+
}
288313
return nil
289314
}
290315

@@ -294,7 +319,7 @@ func (l *LimaVzDriver) Create(_ context.Context) error {
294319
}
295320

296321
func (l *LimaVzDriver) CreateDisk(ctx context.Context) error {
297-
return EnsureDisk(ctx, l.Instance)
322+
return EnsureDisk(ctx, l.Instance.Dir, *l.Instance.Config.Disk, l.diskImageFormat)
298323
}
299324

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

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-
_ = convertTo(imageRaw, srcPath, destPath, &size, withBacking)
21+
_ = convertTo(raw.Type, srcPath, destPath, &size, withBacking)
2122
})
2223
}

pkg/imgutil/nativeimgutil/nativeimgutil.go

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/docker/go-units"
2020
"github.com/lima-vm/go-qcow2reader"
2121
"github.com/lima-vm/go-qcow2reader/convert"
22+
"github.com/lima-vm/go-qcow2reader/image"
2223
"github.com/lima-vm/go-qcow2reader/image/asif"
2324
"github.com/lima-vm/go-qcow2reader/image/qcow2"
2425
"github.com/lima-vm/go-qcow2reader/image/raw"
@@ -42,17 +43,10 @@ func roundUp(size int64) int64 {
4243
return sectors * sectorSize
4344
}
4445

45-
type targetImageType string
46-
47-
const (
48-
imageRaw targetImageType = "raw"
49-
imageASIF targetImageType = "ASIF"
50-
)
51-
5246
// convertTo converts a source disk into a raw or ASIF disk.
5347
// source and dest may be same.
5448
// convertTo is a NOP if source == dest, and no resizing is needed.
55-
func convertTo(destType targetImageType, source, dest string, size *int64, allowSourceWithBackingFile bool) error {
49+
func convertTo(destType image.Type, source, dest string, size *int64, allowSourceWithBackingFile bool) error {
5650
srcF, err := os.Open(source)
5751
if err != nil {
5852
return err
@@ -71,7 +65,7 @@ func convertTo(destType targetImageType, source, dest string, size *int64, allow
7165
if err = srcF.Close(); err != nil {
7266
return err
7367
}
74-
if destType == imageRaw {
68+
if destType == raw.Type {
7569
return convertRawToRaw(source, dest, size)
7670
}
7771
case qcow2.Type:
@@ -85,7 +79,7 @@ func convertTo(destType targetImageType, source, dest string, size *int64, allow
8579
}
8680
}
8781
case asif.Type:
88-
if destType == imageASIF {
82+
if destType == asif.Type {
8983
return convertASIFToASIF(source, dest, size)
9084
}
9185
return fmt.Errorf("conversion from ASIF to %q is not supported", destType)
@@ -103,10 +97,10 @@ func convertTo(destType targetImageType, source, dest string, size *int64, allow
10397
attachedDevice string
10498
)
10599
switch destType {
106-
case imageRaw:
100+
case raw.Type:
107101
destTmpF, err = os.CreateTemp(filepath.Dir(dest), filepath.Base(dest)+".lima-*.tmp")
108102
destTmp = destTmpF.Name()
109-
case imageASIF:
103+
case asif.Type:
110104
// destTmp != destTmpF.Name() because destTmpF is mounted ASIF device file.
111105
randomBase := fmt.Sprintf("%s.lima-%d.tmp.asif", filepath.Base(dest), rand.UintN(math.MaxUint))
112106
destTmp = filepath.Join(filepath.Dir(dest), randomBase)
@@ -150,7 +144,7 @@ func convertTo(destType targetImageType, source, dest string, size *int64, allow
150144
return err
151145
}
152146
// Detach ASIF device
153-
if destType == imageASIF {
147+
if destType == asif.Type {
154148
err := asifutil.DetachASIF(attachedDevice)
155149
if err != nil {
156150
return fmt.Errorf("failed to detach ASIF image %q: %w", attachedDevice, err)
@@ -230,7 +224,7 @@ func (n *NativeImageUtil) CreateDisk(_ context.Context, disk string, size int64)
230224

231225
// ConvertToRaw converts a disk image to raw format.
232226
func (n *NativeImageUtil) ConvertToRaw(_ context.Context, source, dest string, size *int64, allowSourceWithBackingFile bool) error {
233-
return convertTo(imageRaw, source, dest, size, allowSourceWithBackingFile)
227+
return convertTo(raw.Type, source, dest, size, allowSourceWithBackingFile)
234228
}
235229

236230
// ResizeDisk resizes an existing disk image to the specified size.
@@ -246,5 +240,5 @@ func (n *NativeImageUtil) MakeSparse(_ context.Context, f *os.File, offset int64
246240

247241
// ConvertToASIF converts a disk image to ASIF format.
248242
func (n *NativeImageUtil) ConvertToASIF(_ context.Context, source, dest string, size *int64, allowSourceWithBackingFile bool) error {
249-
return convertTo(imageASIF, source, dest, size, allowSourceWithBackingFile)
243+
return convertTo(asif.Type, source, dest, size, allowSourceWithBackingFile)
250244
}

pkg/imgutil/nativeimgutil/nativeimgutil_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"testing"
1313

14+
"github.com/lima-vm/go-qcow2reader/image/raw"
1415
"gotest.tools/v3/assert"
1516
)
1617

@@ -65,15 +66,15 @@ func TestConvertToRaw(t *testing.T) {
6566
t.Run("qcow without backing file", func(t *testing.T) {
6667
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
6768

68-
err = convertTo(imageRaw, qcowImage.Name(), resultImage, nil, false)
69+
err = convertTo(raw.Type, qcowImage.Name(), resultImage, nil, false)
6970
assert.NilError(t, err)
7071
assertFileEquals(t, rawImage.Name(), resultImage)
7172
})
7273

7374
t.Run("qcow with backing file", func(t *testing.T) {
7475
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
7576

76-
err = convertTo(imageRaw, qcowImage.Name(), resultImage, nil, true)
77+
err = convertTo(raw.Type, qcowImage.Name(), resultImage, nil, true)
7778
assert.NilError(t, err)
7879
assertFileEquals(t, rawImage.Name(), resultImage)
7980
})
@@ -82,15 +83,15 @@ func TestConvertToRaw(t *testing.T) {
8283
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
8384

8485
size := int64(2_097_152) // 2mb
85-
err = convertTo(imageRaw, qcowImage.Name(), resultImage, &size, false)
86+
err = convertTo(raw.Type, qcowImage.Name(), resultImage, &size, false)
8687
assert.NilError(t, err)
8788
assertFileEquals(t, rawImageExtended.Name(), resultImage)
8889
})
8990

9091
t.Run("raw", func(t *testing.T) {
9192
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
9293

93-
err = convertTo(imageRaw, rawImage.Name(), resultImage, nil, false)
94+
err = convertTo(raw.Type, rawImage.Name(), resultImage, nil, false)
9495
assert.NilError(t, err)
9596
assertFileEquals(t, rawImage.Name(), resultImage)
9697
})

pkg/limatype/lima_yaml.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"runtime"
99

10+
"github.com/lima-vm/go-qcow2reader/image"
1011
"github.com/opencontainers/go-digest"
1112
"github.com/sirupsen/logrus"
1213
"golang.org/x/sys/cpu"
@@ -118,7 +119,8 @@ type QEMUOpts struct {
118119
}
119120

120121
type VZOpts struct {
121-
Rosetta Rosetta `yaml:"rosetta,omitempty" json:"rosetta,omitempty"`
122+
Rosetta Rosetta `yaml:"rosetta,omitempty" json:"rosetta,omitempty"`
123+
DiskImageFormat *image.Type `yaml:"diskImageFormat,omitempty" json:"diskImageFormat,omitempty" jsonschema:"nullable"`
122124
}
123125

124126
type Rosetta struct {

pkg/limayaml/marshal_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ func TestVZOpts(t *testing.T) {
108108
vmType: "vz"
109109
vmOpts:
110110
vz:
111+
diskImageFormat: null
111112
rosetta:
112113
enabled: null
113114
binfmt: null
@@ -148,6 +149,7 @@ func TestVZOptsRosettaMessage(t *testing.T) {
148149
vmType: "vz"
149150
vmOpts:
150151
vz:
152+
diskImageFormat: "raw"
151153
rosetta:
152154
enabled: true
153155
binfmt: false
@@ -160,6 +162,7 @@ message: |
160162
want := `vmType: vz
161163
vmOpts:
162164
vz:
165+
diskImageFormat: raw
163166
rosetta:
164167
binfmt: false
165168
enabled: true

pkg/qemuimgutil/qemuimgutil.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,5 @@ func AcceptableAsBaseDisk(info *Info) error {
272272

273273
func (q *QemuImageUtil) ConvertToASIF(_ context.Context, _, _ string, _ *int64, _ bool) error {
274274
// Should never be called because ASIF is not supported by qemu-img.
275-
return nil
275+
return errors.New("unimplemented")
276276
}

templates/default.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,11 @@ vmOpts:
370370
# riscv64: "max" # (or "host" when running on riscv64 host)
371371
# x86_64: "max" # (or "host" when running on x86_64 host; additional options are appended on Intel Mac)
372372
vz:
373+
diskImageFormat: null
374+
# Specify the disk image format: "raw" or "asif".
375+
# Currently only applies to the primary disk image.
376+
# "asif" requires macOS 26+, and does not support converting back to "raw".
377+
# 🟢 Builtin default: "raw"
373378
rosetta:
374379
# Enable Rosetta inside the VM; needs `vmType: vz`
375380
# Hint: try `softwareupdate --install-rosetta` if Lima gets stuck at `Installing rosetta...`

website/content/en/docs/config/environment-variables.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,6 @@ This page documents the environment variables used in Lima.
139139
```sh
140140
export LIMA_USERNET_RESOLVE_IP_ADDRESS_TIMEOUT=5
141141
```
142-
### `LIMA_VZ_ASIF`
143-
144-
- **Description**: Specifies whether to use ASIF disk image format for VZ driver on macOS 26.0 or later.
145-
- **Default**: `false`
146-
- **Usage**:
147-
```sh
148-
export LIMA_VZ_ASIF=true
149-
```
150142

151143
### `_LIMA_QEMU_UEFI_IN_BIOS`
152144

0 commit comments

Comments
 (0)