repository: StreamPack delivers blobs at most once

If an error occurred while streaming a pack file, this could result in
passing some of the blobs multiple times to the callback function. This
significantly complicates using StreamPack correctly and is unnecessary.
Retries do not change the content of a blob and thus only deliver the
same result over and over again.
This commit is contained in:
Michael Eischer 2023-12-30 21:40:41 +01:00
parent 76f507c775
commit fe5c337ca2
1 changed files with 8 additions and 4 deletions

View File

@ -881,9 +881,9 @@ type BackendLoadFn func(ctx context.Context, h backend.Handle, length int, offse
const maxUnusedRange = 4 * 1024 * 1024 const maxUnusedRange = 4 * 1024 * 1024
// StreamPack loads the listed blobs from the specified pack file. The plaintext blob is passed to // StreamPack loads the listed blobs from the specified pack file. The plaintext blob is passed to
// the handleBlobFn callback or an error if decryption failed or the blob hash does not match. In // the handleBlobFn callback or an error if decryption failed or the blob hash does not match.
// case of download errors handleBlobFn might be called multiple times for the same blob. If the // handleBlobFn is never called multiple times for the same blob. If the callback returns an error,
// callback returns an error, then StreamPack will abort and not retry it. // then StreamPack will abort and not retry it.
func StreamPack(ctx context.Context, beLoad BackendLoadFn, key *crypto.Key, packID restic.ID, blobs []restic.Blob, handleBlobFn func(blob restic.BlobHandle, buf []byte, err error) error) error { func StreamPack(ctx context.Context, beLoad BackendLoadFn, key *crypto.Key, packID restic.ID, blobs []restic.Blob, handleBlobFn func(blob restic.BlobHandle, buf []byte, err error) error) error {
if len(blobs) == 0 { if len(blobs) == 0 {
// nothing to do // nothing to do
@ -945,7 +945,9 @@ func streamPackPart(ctx context.Context, beLoad BackendLoadFn, key *crypto.Key,
currentBlobEnd := dataStart currentBlobEnd := dataStart
var buf []byte var buf []byte
var decode []byte var decode []byte
for _, entry := range blobs { for len(blobs) > 0 {
entry := blobs[0]
skipBytes := int(entry.Offset - currentBlobEnd) skipBytes := int(entry.Offset - currentBlobEnd)
if skipBytes < 0 { if skipBytes < 0 {
return errors.Errorf("overlapping blobs in pack %v", packID) return errors.Errorf("overlapping blobs in pack %v", packID)
@ -1008,6 +1010,8 @@ func streamPackPart(ctx context.Context, beLoad BackendLoadFn, key *crypto.Key,
cancel() cancel()
return backoff.Permanent(err) return backoff.Permanent(err)
} }
// ensure that each blob is only passed once to handleBlobFn
blobs = blobs[1:]
} }
return nil return nil
}) })