From 5d6ce59a8d34debbfd552cd01d7ecf96fc31443f Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 20 Jul 2024 12:23:12 +0200 Subject: [PATCH] restorer: also truncate files if their content is already uptodate Files for which no blobs have to be restored, still have to be truncated to the correct size. Take a file with content "foobar" that should be replaced by restore with content "foo". The first three bytes are already uptodate, such that no data has to be written. As file truncation normally happens when writing data, a special case is necessary. This no blobs written special case is unified with the empty file special case. --- internal/restorer/filerestorer.go | 49 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/internal/restorer/filerestorer.go b/internal/restorer/filerestorer.go index 648633361..1ea82cabb 100644 --- a/internal/restorer/filerestorer.go +++ b/internal/restorer/filerestorer.go @@ -120,26 +120,21 @@ func (r *fileRestorer) restoreFiles(ctx context.Context) error { // create packInfo from fileInfo for _, file := range r.files { fileBlobs := file.blobs.(restic.IDs) - if len(fileBlobs) == 0 { - err := r.restoreEmptyFileAt(file.location) - if errFile := r.sanitizeError(file, err); errFile != nil { - return errFile - } - } - largeFile := len(fileBlobs) > largeFileBlobCount var packsMap map[restic.ID][]fileBlobInfo if largeFile { packsMap = make(map[restic.ID][]fileBlobInfo) } fileOffset := int64(0) + restoredBlobs := false err := r.forEachBlob(fileBlobs, func(packID restic.ID, blob restic.Blob, idx int) { - if largeFile { - if !file.state.HasMatchingBlob(idx) { + if !file.state.HasMatchingBlob(idx) { + if largeFile { packsMap[packID] = append(packsMap[packID], fileBlobInfo{id: blob.ID, offset: fileOffset}) - } else { - r.reportBlobProgress(file, uint64(blob.DataLength())) } + restoredBlobs = true + } else { + r.reportBlobProgress(file, uint64(blob.DataLength())) } fileOffset += int64(blob.DataLength()) pack, ok := packs[packID] @@ -175,6 +170,19 @@ func (r *fileRestorer) restoreFiles(ctx context.Context) error { if largeFile { file.blobs = packsMap } + + // empty file or one with already uptodate content. Make sure that the file size is correct + if !restoredBlobs { + err := r.truncateFileToSize(file.location, file.size) + if errFile := r.sanitizeError(file, err); errFile != nil { + return errFile + } + + // the progress events were already sent for non-zero size files + if file.size == 0 { + r.reportBlobProgress(file, 0) + } + } } // drop no longer necessary file list r.files = nil @@ -214,17 +222,12 @@ func (r *fileRestorer) restoreFiles(ctx context.Context) error { return wg.Wait() } -func (r *fileRestorer) restoreEmptyFileAt(location string) error { - f, err := createFile(r.targetPath(location), 0, false, r.allowRecursiveDelete) +func (r *fileRestorer) truncateFileToSize(location string, size int64) error { + f, err := createFile(r.targetPath(location), size, false, r.allowRecursiveDelete) if err != nil { return err } - if err = f.Close(); err != nil { - return err - } - - r.progress.AddProgress(location, restore.ActionFileRestored, 0, 0) - return nil + return f.Close() } type blobToFileOffsetsMapping map[restic.ID]struct { @@ -248,12 +251,8 @@ func (r *fileRestorer) downloadPack(ctx context.Context, pack *packInfo) error { if fileBlobs, ok := file.blobs.(restic.IDs); ok { fileOffset := int64(0) err := r.forEachBlob(fileBlobs, func(packID restic.ID, blob restic.Blob, idx int) { - if packID.Equal(pack.id) { - if !file.state.HasMatchingBlob(idx) { - addBlob(blob, fileOffset) - } else { - r.reportBlobProgress(file, uint64(blob.DataLength())) - } + if packID.Equal(pack.id) && !file.state.HasMatchingBlob(idx) { + addBlob(blob, fileOffset) } fileOffset += int64(blob.DataLength()) })