forked from TrueCloudLab/restic
2dbb18128c
Some tests have to explicitly create pack files with blobs that don't match their ID. For those blobs the builtin verification of the repository must be disabled.
131 lines
4.3 KiB
Go
131 lines
4.3 KiB
Go
package repository_test
|
|
|
|
import (
|
|
"context"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/restic/restic/internal/backend"
|
|
"github.com/restic/restic/internal/index"
|
|
"github.com/restic/restic/internal/repository"
|
|
"github.com/restic/restic/internal/restic"
|
|
"github.com/restic/restic/internal/test"
|
|
rtest "github.com/restic/restic/internal/test"
|
|
"github.com/restic/restic/internal/ui/progress"
|
|
)
|
|
|
|
func listBlobs(repo restic.Repository) restic.BlobSet {
|
|
blobs := restic.NewBlobSet()
|
|
repo.Index().Each(context.TODO(), func(pb restic.PackedBlob) {
|
|
blobs.Insert(pb.BlobHandle)
|
|
})
|
|
return blobs
|
|
}
|
|
|
|
func replaceFile(t *testing.T, repo restic.Repository, h backend.Handle, damage func([]byte) []byte) {
|
|
buf, err := backend.LoadAll(context.TODO(), nil, repo.Backend(), h)
|
|
test.OK(t, err)
|
|
buf = damage(buf)
|
|
test.OK(t, repo.Backend().Remove(context.TODO(), h))
|
|
test.OK(t, repo.Backend().Save(context.TODO(), h, backend.NewByteReader(buf, repo.Backend().Hasher())))
|
|
}
|
|
|
|
func TestRepairBrokenPack(t *testing.T) {
|
|
repository.TestAllVersions(t, testRepairBrokenPack)
|
|
}
|
|
|
|
func testRepairBrokenPack(t *testing.T, version uint) {
|
|
tests := []struct {
|
|
name string
|
|
damage func(t *testing.T, repo restic.Repository, packsBefore restic.IDSet) (restic.IDSet, restic.BlobSet)
|
|
}{
|
|
{
|
|
"valid pack",
|
|
func(t *testing.T, repo restic.Repository, packsBefore restic.IDSet) (restic.IDSet, restic.BlobSet) {
|
|
return packsBefore, restic.NewBlobSet()
|
|
},
|
|
},
|
|
{
|
|
"broken pack",
|
|
func(t *testing.T, repo restic.Repository, packsBefore restic.IDSet) (restic.IDSet, restic.BlobSet) {
|
|
wrongBlob := createRandomWrongBlob(t, repo)
|
|
damagedPacks := findPacksForBlobs(t, repo, restic.NewBlobSet(wrongBlob))
|
|
return damagedPacks, restic.NewBlobSet(wrongBlob)
|
|
},
|
|
},
|
|
{
|
|
"partially broken pack",
|
|
func(t *testing.T, repo restic.Repository, packsBefore restic.IDSet) (restic.IDSet, restic.BlobSet) {
|
|
// damage one of the pack files
|
|
damagedID := packsBefore.List()[0]
|
|
replaceFile(t, repo, backend.Handle{Type: backend.PackFile, Name: damagedID.String()},
|
|
func(buf []byte) []byte {
|
|
buf[0] ^= 0xff
|
|
return buf
|
|
})
|
|
|
|
// find blob that starts at offset 0
|
|
var damagedBlob restic.BlobHandle
|
|
for blobs := range repo.Index().ListPacks(context.TODO(), restic.NewIDSet(damagedID)) {
|
|
for _, blob := range blobs.Blobs {
|
|
if blob.Offset == 0 {
|
|
damagedBlob = blob.BlobHandle
|
|
}
|
|
}
|
|
}
|
|
|
|
return restic.NewIDSet(damagedID), restic.NewBlobSet(damagedBlob)
|
|
},
|
|
}, {
|
|
"truncated pack",
|
|
func(t *testing.T, repo restic.Repository, packsBefore restic.IDSet) (restic.IDSet, restic.BlobSet) {
|
|
// damage one of the pack files
|
|
damagedID := packsBefore.List()[0]
|
|
replaceFile(t, repo, backend.Handle{Type: backend.PackFile, Name: damagedID.String()},
|
|
func(buf []byte) []byte {
|
|
buf = buf[0:10]
|
|
return buf
|
|
})
|
|
|
|
// all blobs in the file are broken
|
|
damagedBlobs := restic.NewBlobSet()
|
|
for blobs := range repo.Index().ListPacks(context.TODO(), restic.NewIDSet(damagedID)) {
|
|
for _, blob := range blobs.Blobs {
|
|
damagedBlobs.Insert(blob.BlobHandle)
|
|
}
|
|
}
|
|
return restic.NewIDSet(damagedID), damagedBlobs
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
// disable verification to allow adding corrupted blobs to the repository
|
|
repo := repository.TestRepositoryWithBackend(t, nil, version, repository.Options{NoVerifyPack: true})
|
|
|
|
seed := time.Now().UnixNano()
|
|
rand.Seed(seed)
|
|
t.Logf("rand seed is %v", seed)
|
|
|
|
createRandomBlobs(t, repo, 5, 0.7)
|
|
packsBefore := listPacks(t, repo)
|
|
blobsBefore := listBlobs(repo)
|
|
|
|
toRepair, damagedBlobs := test.damage(t, repo, packsBefore)
|
|
|
|
rtest.OK(t, repository.RepairPacks(context.TODO(), repo, toRepair, &progress.NoopPrinter{}))
|
|
// reload index
|
|
rtest.OK(t, repo.SetIndex(index.NewMasterIndex()))
|
|
rtest.OK(t, repo.LoadIndex(context.TODO(), nil))
|
|
|
|
packsAfter := listPacks(t, repo)
|
|
blobsAfter := listBlobs(repo)
|
|
|
|
rtest.Assert(t, len(packsAfter.Intersect(toRepair)) == 0, "some damaged packs were not removed")
|
|
rtest.Assert(t, len(packsBefore.Sub(toRepair).Sub(packsAfter)) == 0, "not-damaged packs were removed")
|
|
rtest.Assert(t, blobsBefore.Sub(damagedBlobs).Equals(blobsAfter), "diverging blob lists")
|
|
})
|
|
}
|
|
}
|