From e8bbb05328652cae3fd7ae65eb37c2bcf51d44ad Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Tue, 29 Jun 2021 20:54:16 +0200 Subject: [PATCH] restore: Correctly handle partial pack download errors Failed pack/blob downloads should be retried. For blobs that fail decryption assume that the pack file is really damaged and try to restore the remaining blobs. --- internal/restorer/filerestorer.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/internal/restorer/filerestorer.go b/internal/restorer/filerestorer.go index d2f71ce8f..2e503c4aa 100644 --- a/internal/restorer/filerestorer.go +++ b/internal/restorer/filerestorer.go @@ -258,7 +258,11 @@ func (r *fileRestorer) downloadPack(ctx context.Context, pack *packInfo) error { if err != nil { return err } - blobData, buf, err = r.loadBlob(bufRd, blobID, blob.length, buf) + buf, err = r.downloadBlob(bufRd, blobID, blob.length, buf) + if err != nil { + return err + } + blobData, err = r.decryptBlob(blobID, buf) if err != nil { for file := range blob.files { if errFile := sanitizeError(file, err); errFile != nil { @@ -309,7 +313,7 @@ func (r *fileRestorer) downloadPack(ctx context.Context, pack *packInfo) error { return nil } -func (r *fileRestorer) loadBlob(rd io.Reader, blobID restic.ID, length int, buf []byte) ([]byte, []byte, error) { +func (r *fileRestorer) downloadBlob(rd io.Reader, blobID restic.ID, length int, buf []byte) ([]byte, error) { // TODO reconcile with Repository#loadBlob implementation if cap(buf) < length { @@ -320,24 +324,29 @@ func (r *fileRestorer) loadBlob(rd io.Reader, blobID restic.ID, length int, buf n, err := io.ReadFull(rd, buf) if err != nil { - return nil, nil, err + return nil, err } if n != length { - return nil, nil, errors.Errorf("error loading blob %v: wrong length returned, want %d, got %d", blobID.Str(), length, n) + return nil, errors.Errorf("error loading blob %v: wrong length returned, want %d, got %d", blobID.Str(), length, n) } + return buf, nil +} + +func (r *fileRestorer) decryptBlob(blobID restic.ID, buf []byte) ([]byte, error) { + // TODO reconcile with Repository#loadBlob implementation // decrypt nonce, ciphertext := buf[:r.key.NonceSize()], buf[r.key.NonceSize():] plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil) if err != nil { - return nil, nil, errors.Errorf("decrypting blob %v failed: %v", blobID, err) + return nil, errors.Errorf("decrypting blob %v failed: %v", blobID, err) } // check hash if !restic.Hash(plaintext).Equal(blobID) { - return nil, nil, errors.Errorf("blob %v returned invalid hash", blobID) + return nil, errors.Errorf("blob %v returned invalid hash", blobID) } - return plaintext, buf, nil + return plaintext, nil }