@@ -10,17 +10,21 @@ import (
1010 "fmt"
1111 "io"
1212 "io/fs"
13+ "math"
14+ "math/rand/v2"
1315 "os"
1416 "path/filepath"
1517
1618 containerdfs "github.com/containerd/continuity/fs"
1719 "github.com/docker/go-units"
1820 "github.com/lima-vm/go-qcow2reader"
1921 "github.com/lima-vm/go-qcow2reader/convert"
22+ "github.com/lima-vm/go-qcow2reader/image/asif"
2023 "github.com/lima-vm/go-qcow2reader/image/qcow2"
2124 "github.com/lima-vm/go-qcow2reader/image/raw"
2225 "github.com/sirupsen/logrus"
2326
27+ "github.com/lima-vm/lima/v2/pkg/imgutil/nativeimgutil/asifutil"
2428 "github.com/lima-vm/lima/v2/pkg/progressbar"
2529)
2630
@@ -38,10 +42,17 @@ func roundUp(size int64) int64 {
3842 return sectors * sectorSize
3943}
4044
41- // convertToRaw converts a source disk into a raw disk.
45+ type targetImageType string
46+
47+ const (
48+ imageRaw targetImageType = "raw"
49+ imageASIF targetImageType = "ASIF"
50+ )
51+
52+ // convertTo converts a source disk into a raw or ASIF disk.
4253// source and dest may be same.
43- // convertToRaw is a NOP if source == dest, and no resizing is needed.
44- func convertToRaw ( source , dest string , size * int64 , allowSourceWithBackingFile bool ) error {
54+ // 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 {
4556 srcF , err := os .Open (source )
4657 if err != nil {
4758 return err
@@ -54,13 +65,15 @@ func convertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b
5465 if size != nil && * size < srcImg .Size () {
5566 return fmt .Errorf ("specified size %d is smaller than the original image size (%d) of %q" , * size , srcImg .Size (), source )
5667 }
57- logrus .Infof ("Converting %q (%s) to a raw disk %q" , source , srcImg .Type (), dest )
68+ logrus .Infof ("Converting %q (%s) to a %s disk %q" , source , srcImg .Type (), destType , dest )
5869 switch t := srcImg .Type (); t {
5970 case raw .Type :
6071 if err = srcF .Close (); err != nil {
6172 return err
6273 }
63- return convertRawToRaw (source , dest , size )
74+ if destType == imageRaw {
75+ return convertRawToRaw (source , dest , size )
76+ }
6477 case qcow2 .Type :
6578 if ! allowSourceWithBackingFile {
6679 q , ok := srcImg .(* qcow2.Qcow2 )
@@ -71,6 +84,11 @@ func convertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b
7184 return fmt .Errorf ("qcow2 image %q has an unexpected backing file: %q" , source , q .BackingFile )
7285 }
7386 }
87+ case asif .Type :
88+ if destType == imageASIF {
89+ return convertASIFToASIF (source , dest , size )
90+ }
91+ return fmt .Errorf ("conversion from ASIF to %q is not supported" , destType )
7492 default :
7593 logrus .Warnf ("image %q has an unexpected format: %q" , source , t )
7694 }
@@ -79,11 +97,26 @@ func convertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b
7997 }
8098
8199 // Create a tmp file because source and dest can be same.
82- destTmpF , err := os .CreateTemp (filepath .Dir (dest ), filepath .Base (dest )+ ".lima-*.tmp" )
100+ var (
101+ destTmpF * os.File
102+ destTmp string
103+ attachedDevice string
104+ )
105+ switch destType {
106+ case imageRaw :
107+ destTmpF , err = os .CreateTemp (filepath .Dir (dest ), filepath .Base (dest )+ ".lima-*.tmp" )
108+ destTmp = destTmpF .Name ()
109+ case imageASIF :
110+ // destTmp != destTmpF.Name() because destTmpF is mounted ASIF device file.
111+ randomBase := fmt .Sprintf ("%s.lima-%d.tmp.asif" , filepath .Base (dest ), rand .UintN (math .MaxUint ))
112+ destTmp = filepath .Join (filepath .Dir (dest ), randomBase )
113+ attachedDevice , destTmpF , err = asifutil .NewAttachedASIF (destTmp , srcImg .Size ())
114+ default :
115+ return fmt .Errorf ("unsupported target image type: %q" , destType )
116+ }
83117 if err != nil {
84118 return err
85119 }
86- destTmp := destTmpF .Name ()
87120 defer os .RemoveAll (destTmp )
88121 defer destTmpF .Close ()
89122
@@ -116,6 +149,13 @@ func convertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b
116149 if err = destTmpF .Close (); err != nil {
117150 return err
118151 }
152+ // Detach ASIF device
153+ if destType == imageASIF {
154+ err := asifutil .DetachASIF (attachedDevice )
155+ if err != nil {
156+ return fmt .Errorf ("failed to detach ASIF image %q: %w" , attachedDevice , err )
157+ }
158+ }
119159
120160 // Rename destTmp into dest
121161 if err = os .RemoveAll (dest ); err != nil {
@@ -149,6 +189,24 @@ func convertRawToRaw(source, dest string, size *int64) error {
149189 return nil
150190}
151191
192+ func convertASIFToASIF (source , dest string , size * int64 ) error {
193+ if source != dest {
194+ if err := containerdfs .CopyFile (dest , source ); err != nil {
195+ return fmt .Errorf ("failed to copy %q into %q: %w" , source , dest , err )
196+ }
197+ if err := os .Chmod (dest , 0o644 ); err != nil {
198+ return fmt .Errorf ("failed to set permissions on %q: %w" , dest , err )
199+ }
200+ }
201+ if size != nil {
202+ logrus .Infof ("Resizing to %s" , units .BytesSize (float64 (* size )))
203+ if err := asifutil .ResizeASIF (dest , * size ); err != nil {
204+ return fmt .Errorf ("failed to resize ASIF image %q: %w" , dest , err )
205+ }
206+ }
207+ return nil
208+ }
209+
152210func makeSparse (f * os.File , offset int64 ) error {
153211 if _ , err := f .Seek (offset , io .SeekStart ); err != nil {
154212 return err
@@ -172,7 +230,7 @@ func (n *NativeImageUtil) CreateDisk(_ context.Context, disk string, size int64)
172230
173231// ConvertToRaw converts a disk image to raw format.
174232func (n * NativeImageUtil ) ConvertToRaw (_ context.Context , source , dest string , size * int64 , allowSourceWithBackingFile bool ) error {
175- return convertToRaw ( source , dest , size , allowSourceWithBackingFile )
233+ return convertTo ( imageRaw , source , dest , size , allowSourceWithBackingFile )
176234}
177235
178236// ResizeDisk resizes an existing disk image to the specified size.
@@ -185,3 +243,8 @@ func (n *NativeImageUtil) ResizeDisk(_ context.Context, disk string, size int64)
185243func (n * NativeImageUtil ) MakeSparse (_ context.Context , f * os.File , offset int64 ) error {
186244 return makeSparse (f , offset )
187245}
246+
247+ // ConvertToASIF converts a disk image to ASIF format.
248+ func (n * NativeImageUtil ) ConvertToASIF (_ context.Context , source , dest string , size * int64 , allowSourceWithBackingFile bool ) error {
249+ return convertTo (imageASIF , source , dest , size , allowSourceWithBackingFile )
250+ }
0 commit comments