@@ -68,6 +68,13 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b
6868 defer os .RemoveAll (destTmp )
6969 defer destTmpF .Close ()
7070
71+ // Truncating before copy eliminates the seeks during copy and provide a
72+ // hint to the file system that may minimize allocations and fragmanation
73+ // of the file.
74+ if err := MakeSparse (destTmpF , srcImg .Size ()); err != nil {
75+ return err
76+ }
77+
7178 // Copy
7279 srcImgR := io .NewSectionReader (srcImg , 0 , srcImg .Size ())
7380 bar , err := progressbar .New (srcImg .Size ())
@@ -124,8 +131,8 @@ func convertRawToRaw(source, dest string, size *int64) error {
124131
125132func copySparse (w * os.File , r io.Reader , bufSize int64 ) (int64 , error ) {
126133 var (
127- n int64
128- eof , hasWrites bool
134+ n int64
135+ eof bool
129136 )
130137
131138 zeroBuf := make ([]byte , bufSize )
@@ -139,31 +146,22 @@ func copySparse(w *os.File, r io.Reader, bufSize int64) (int64, error) {
139146 }
140147 }
141148 // TODO: qcow2reader should have a method to notify whether buf is zero
142- if bytes .Equal (buf , zeroBuf ) {
143- if _ , sErr := w .Seek (int64 (rN ), io .SeekCurrent ); sErr != nil {
144- return n , fmt .Errorf ("failed seek: %w" , sErr )
145- }
146- // no need to ftruncate here
149+ if bytes .Equal (buf [:rN ], zeroBuf [:rN ]) {
147150 n += int64 (rN )
148151 } else {
149- hasWrites = true
150- wN , wErr := w .Write (buf )
152+ wN , wErr := w .WriteAt (buf [:rN ], n )
151153 if wN > 0 {
152154 n += int64 (wN )
153155 }
154156 if wErr != nil {
155- return n , fmt .Errorf ("failed to read : %w" , wErr )
157+ return n , fmt .Errorf ("failed to write : %w" , wErr )
156158 }
157159 if wN != rN {
158160 return n , fmt .Errorf ("read %d, but wrote %d bytes" , rN , wN )
159161 }
160162 }
161163 }
162164
163- // Ftruncate must be run if the file contains only zeros
164- if ! hasWrites {
165- return n , MakeSparse (w , n )
166- }
167165 return n , nil
168166}
169167
0 commit comments