Skip to content

Commit 4a24c29

Browse files
committed
Fix nativeimgutil.ConvertToRaw()
- Truncate before copying the data to eliminate the seeks during the copy. This also provides a hint to the file system that can minimize allocations and fragmentation of the file. - Avoid unneeded seeks during copy using WriteAt. This does not improve performance since practically all time is spent on reading from the source image. - Fix zero detection to handle short reads. Previously we would compare the entire buffer which can contain non-zero bytes from the previous read. - Fix write to handle short reads. Previously we would write the entire buffer including data from previous read, corrupting the image. - Fix error message for failed write. Looks like it was copied from the read branch. Signed-off-by: Nir Soffer <[email protected]>
1 parent 713e1cd commit 4a24c29

File tree

1 file changed

+12
-14
lines changed

1 file changed

+12
-14
lines changed

pkg/nativeimgutil/nativeimgutil.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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

125132
func 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

Comments
 (0)