diff --git a/internal/server/storage/drivers/generic_vfs.go b/internal/server/storage/drivers/generic_vfs.go index 3519c281812..373e0d88f3c 100644 --- a/internal/server/storage/drivers/generic_vfs.go +++ b/internal/server/storage/drivers/generic_vfs.go @@ -342,7 +342,7 @@ func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) ( d.Logger().Debug("Receiving block volume started", logger.Ctx{"volName": volName, "path": path}) defer d.Logger().Debug("Receiving block volume stopped", logger.Ctx{"volName": volName, "path": path}) - _, err = io.Copy(to, fromPipe) + _, err = io.Copy(NewSparseFileWrapper(to), fromPipe) if err != nil { return fmt.Errorf("Error copying from migration connection to %q: %w", path, err) } @@ -786,7 +786,7 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str } d.Logger().Debug(logMsg, logger.Ctx{"source": srcFile, "target": targetPath}) - _, err = io.Copy(to, tr) + _, err = io.Copy(NewSparseFileWrapper(to), tr) if err != nil { return err } diff --git a/internal/server/storage/drivers/utils.go b/internal/server/storage/drivers/utils.go index 4dac87f4754..9b4657f32b7 100644 --- a/internal/server/storage/drivers/utils.go +++ b/internal/server/storage/drivers/utils.go @@ -880,3 +880,49 @@ func wipeBlockHeaders(path string) error { func IsContentBlock(contentType ContentType) bool { return contentType == ContentTypeBlock || contentType == ContentTypeISO } + +// NewSparseFileWrapper returns a SparseFileWrapper for the provided io.File. +func NewSparseFileWrapper(w *os.File) *SparseFileWrapper { + return &SparseFileWrapper{w: w} +} + +// SparseFileWrapper wraps os.File to create sparse Files. +type SparseFileWrapper struct { + w *os.File +} + +// Write performs the write but skips null bytes. +func (sfw *SparseFileWrapper) Write(p []byte) (n int, err error) { + originalLength := len(p) + start := 0 + + for start < len(p) { + end := start + if p[start] == 0 { + for end < len(p) && p[end] == 0 { + end++ + } + + _, err := sfw.w.Seek(int64(end-start), io.SeekCurrent) + if err != nil { + return start, err + } + + start = end + } else { + // Write non-zero bytes + for end < len(p) && p[end] != 0 { + end++ + } + + written, err := sfw.w.Write(p[start:end]) + if err != nil { + return start + written, err + } + + start = end + } + } + + return originalLength, nil +}