Skip to content

Commit f94c8ec

Browse files
authored
Merge pull request #1405 from pendo324/additional-disk-vz-support
feat: mount additional disks when using vz
2 parents e344191 + eca1613 commit f94c8ec

File tree

6 files changed

+78
-8
lines changed

6 files changed

+78
-8
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ Use `<INSTANCE>:<FILENAME>` to specify a source or target inside an instance.
224224

225225
#### `limactl disk`
226226

227-
`limactl disk create <DISK> --size <SIZE>`: create a new external disk to attach to an instance
227+
`limactl disk create <DISK> --size <SIZE> [--format qcow2]`: create a new external disk to attach to an instance
228228

229229
`limactl disk delete <DISK>`: delete an existing disk
230230

cmd/limactl/disk.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func newDiskCommand() *cobra.Command {
2020
Use: "disk",
2121
Short: "Lima disk management",
2222
Example: ` Create a disk:
23-
$ limactl disk create DISK --size SIZE
23+
$ limactl disk create DISK --size SIZE [--format qcow2]
2424
2525
List existing disks:
2626
$ limactl disk ls
@@ -44,14 +44,15 @@ func newDiskCreateCommand() *cobra.Command {
4444
Use: "create DISK",
4545
Example: `
4646
To create a new disk:
47-
$ limactl disk create DISK --size SIZE
47+
$ limactl disk create DISK --size SIZE [--format qcow2]
4848
`,
4949
Short: "Create a Lima disk",
5050
Args: WrapArgsError(cobra.ExactArgs(1)),
5151
RunE: diskCreateAction,
5252
}
5353
diskCreateCommand.Flags().String("size", "", "configure the disk size")
5454
diskCreateCommand.MarkFlagRequired("size")
55+
diskCreateCommand.Flags().String("format", "qcow2", "specify the disk format")
5556
return diskCreateCommand
5657
}
5758

@@ -61,11 +62,22 @@ func diskCreateAction(cmd *cobra.Command, args []string) error {
6162
return err
6263
}
6364

65+
format, err := cmd.Flags().GetString("format")
66+
if err != nil {
67+
return err
68+
}
69+
6470
diskSize, err := units.RAMInBytes(size)
6571
if err != nil {
6672
return err
6773
}
6874

75+
switch format {
76+
case "qcow2", "raw":
77+
default:
78+
return fmt.Errorf(`disk format %q not supported, use "qcow2" or "raw" instead`, format)
79+
}
80+
6981
// only exactly one arg is allowed
7082
name := args[0]
7183

@@ -78,13 +90,13 @@ func diskCreateAction(cmd *cobra.Command, args []string) error {
7890
return fmt.Errorf("disk %q already exists (%q)", name, diskDir)
7991
}
8092

81-
logrus.Infof("Creating disk %q with size %s", name, units.BytesSize(float64(diskSize)))
93+
logrus.Infof("Creating %s disk %q with size %s", format, name, units.BytesSize(float64(diskSize)))
8294

8395
if err := os.MkdirAll(diskDir, 0700); err != nil {
8496
return err
8597
}
8698

87-
if err := qemu.CreateDataDisk(diskDir, int(diskSize)); err != nil {
99+
if err := qemu.CreateDataDisk(diskDir, format, int(diskSize)); err != nil {
88100
return err
89101
}
90102

docs/internal.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,15 @@ Host agent:
7474
A disk directory contains the following files:
7575

7676
data disk:
77-
- `datadisk`: the qcow2 disk that is attached to an instance
77+
- `datadisk`: the qcow2 or raw disk that is attached to an instance
7878

7979
lock:
8080
- `in_use_by`: symlink to the instance directory that is using the disk
8181

82+
When using `vmType: vz` (Virtualization.framework), on boot, any qcow2 (default) formatted disks that are specified in `additionalDisks` will be converted to RAW since [Virtualization.framework only supports mounting RAW disks](https://developer.apple.com/documentation/virtualization/vzdiskimagestoragedeviceattachment). This conversion enables additional disks to work with both Virtualization.framework and QEMU, but it has some consequences when it comes to interacting with the disks. Most importantly, a regular macOS default `cp` command will copy the _entire_ virtual disk size, instead of just the _used/allocated_ portion. The easiest way to copy only the used data is by adding the `-c` option to cp: `cp -c old_path new_path`. `cp -c` uses clonefile(2) to create a copy-on-write clone of the disk, and should return instantly.
83+
84+
`ls` will also only show the full/virtual size of the disks. To see the allocated space, `du -h disk_path` or `qemu-img info disk_path` can be used instead. See [#1405](https://github.com/lima-vm/lima/pull/1405) for more details.
85+
8286
## Lima cache directory (`~/Library/Caches/lima`)
8387

8488
Currently hard-coded to `~/Library/Caches/lima` on macOS.

pkg/qemu/qemu.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,14 @@ func EnsureDisk(cfg Config) error {
104104
return nil
105105
}
106106

107-
func CreateDataDisk(dir string, size int) error {
107+
func CreateDataDisk(dir, format string, size int) error {
108108
dataDisk := filepath.Join(dir, filenames.DataDisk)
109109
if _, err := os.Stat(dataDisk); err == nil || !errors.Is(err, fs.ErrNotExist) {
110110
// datadisk already exists
111111
return err
112112
}
113113

114-
args := []string{"create", "-f", "qcow2", dataDisk, strconv.Itoa(size)}
114+
args := []string{"create", "-f", format, dataDisk, strconv.Itoa(size)}
115115
cmd := exec.Command("qemu-img", args...)
116116
if out, err := cmd.CombinedOutput(); err != nil {
117117
return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err)

pkg/vz/vm_darwin.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/lima-vm/lima/pkg/localpathutil"
2222
"github.com/lima-vm/lima/pkg/networks"
2323
"github.com/lima-vm/lima/pkg/qemu/imgutil"
24+
"github.com/lima-vm/lima/pkg/store"
2425
"github.com/lima-vm/lima/pkg/store/filenames"
2526
"github.com/sirupsen/logrus"
2627
)
@@ -334,6 +335,58 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura
334335
}
335336
configurations = append(configurations, diffDisk)
336337

338+
for _, diskName := range driver.Yaml.AdditionalDisks {
339+
d, err := store.InspectDisk(diskName)
340+
if err != nil {
341+
return fmt.Errorf("failed to run load disk %q: %q", diskName, err)
342+
}
343+
344+
if d.Instance != "" {
345+
return fmt.Errorf("failed to run attach disk %q, in use by instance %q", diskName, d.Instance)
346+
}
347+
logrus.Infof("Mounting disk %q on %q", diskName, d.MountPoint)
348+
err = d.Lock(driver.Instance.Dir)
349+
if err != nil {
350+
return fmt.Errorf("failed to run lock disk %q: %q", diskName, err)
351+
}
352+
extraDiskPath := filepath.Join(d.Dir, filenames.DataDisk)
353+
354+
extraDiskFormat, err := imgutil.DetectFormat(extraDiskPath)
355+
if err != nil {
356+
return fmt.Errorf("failed to run detect disk format %q: %q", diskName, err)
357+
}
358+
if extraDiskFormat != "raw" {
359+
rawPath := fmt.Sprintf("%s.raw", extraDiskPath)
360+
qcow2Path := fmt.Sprintf("%s.qcow2", extraDiskPath)
361+
if err = imgutil.QCOWToRaw(extraDiskPath, rawPath); err != nil {
362+
return fmt.Errorf("failed to convert qcow2 disk %q to raw for vz driver: %w", diskName, err)
363+
}
364+
if err = os.Rename(extraDiskPath, qcow2Path); err != nil {
365+
return fmt.Errorf("failed to rename additional disk for vz driver: %w", err)
366+
}
367+
if err = os.Rename(rawPath, extraDiskPath); err != nil {
368+
return fmt.Errorf("failed to rename additional disk for vz driver: %w", err)
369+
}
370+
if err = os.Remove(qcow2Path); err != nil {
371+
logrus.Errorf("Failed to delete unused qcow2 additional disk %q."+
372+
"Disk is no longer needed by Lima and it can be removed manually.", qcow2Path)
373+
}
374+
}
375+
376+
if err = validateDiskFormat(extraDiskPath); err != nil {
377+
return fmt.Errorf("failed to validate extra disk %q: %w", extraDiskPath, err)
378+
}
379+
extraDiskPathAttachment, err := vz.NewDiskImageStorageDeviceAttachmentWithCacheAndSync(extraDiskPath, false, vz.DiskImageCachingModeAutomatic, vz.DiskImageSynchronizationModeFsync)
380+
if err != nil {
381+
return fmt.Errorf("failed to create disk attachment for extra disk %q: %w", extraDiskPath, err)
382+
}
383+
extraDisk, err := vz.NewVirtioBlockDeviceConfiguration(extraDiskPathAttachment)
384+
if err != nil {
385+
return fmt.Errorf("failed to create new virtio block device config for extra disk %q: %w", extraDiskPath, err)
386+
}
387+
configurations = append(configurations, extraDisk)
388+
}
389+
337390
if err = validateDiskFormat(ciDataPath); err != nil {
338391
return err
339392
}

pkg/vz/vz_driver_darwin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func (l *LimaVzDriver) Validate() error {
6464
"PropagateProxyEnv",
6565
"CACertificates",
6666
"Rosetta",
67+
"AdditionalDisks",
6768
); len(unknown) > 0 {
6869
logrus.Warnf("Ignoring: vmType %s: %+v", *l.Yaml.VMType, unknown)
6970
}

0 commit comments

Comments
 (0)