check: consider split pack index entries as repository damage

This commit is contained in:
Michael Eischer
2026-05-30 20:28:57 +02:00
parent 5b39ad861e
commit 78b3411076
2 changed files with 35 additions and 3 deletions
+8 -2
View File
@@ -261,8 +261,15 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts global.Options, args
}
errorsFound := false
salvagePacks := restic.NewIDSet()
for _, hint := range hints {
switch hint.(type) {
switch hint := hint.(type) {
case *repository.ErrIncompletePackEntry:
printer.E("%s", hint.Error())
salvagePacks.Insert(hint.PackID)
errorsFound = true
summary.NumErrors++
case *repository.ErrDuplicatePacks:
printer.S("%s", hint.Error())
summary.HintRepairIndex = true
@@ -295,7 +302,6 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts global.Options, args
orphanedPacks := 0
errChan := make(chan error)
salvagePacks := restic.NewIDSet()
printer.P("check all packs\n")
go chkr.Packs(ctx, errChan)
+27 -1
View File
@@ -17,6 +17,16 @@ import (
const maxStreamBufferSize = 4 * 1024 * 1024
// ErrIncompletePackEntry is returned when indexes contain different data for a pack.
type ErrIncompletePackEntry struct {
PackID restic.ID
Indexes restic.IDSet
}
func (e *ErrIncompletePackEntry) Error() string {
return fmt.Sprintf("pack %v has different data in indexes: %v", e.PackID, e.Indexes)
}
// ErrDuplicatePacks is returned when a pack is found in more than one index.
type ErrDuplicatePacks struct {
PackID restic.ID
@@ -79,6 +89,9 @@ func computePackTypes(ctx context.Context, idx restic.ListBlobser) (map[restic.I
func (c *Checker) LoadIndex(ctx context.Context, p restic.TerminalCounterFactory) (hints []error, errs []error) {
debug.Log("Start")
packToIndex := make(map[restic.ID]restic.IDSet)
// in restic < 0.10.0, the blobs of a pack could be split over multiple indexes.
// by now this is considered as repository damage.
packToPackBlobHash := make(map[restic.ID]restic.IDSet)
// Use the repository's internal loadIndexWithCallback to handle per-index errors
err := c.repo.loadIndexWithCallback(ctx, p, func(id restic.ID, idx *index.Index, err error) error {
@@ -104,6 +117,14 @@ func (c *Checker) LoadIndex(ctx context.Context, p restic.TerminalCounterFactory
packToIndex[blob.PackID].Insert(id)
}
for pbs := range idx.EachByPack(ctx, restic.NewIDSet()) {
packBlobHash := index.PackBlobsHash(pbs)
if _, ok := packToPackBlobHash[pbs.PackID]; !ok {
packToPackBlobHash[pbs.PackID] = restic.NewIDSet()
}
packToPackBlobHash[pbs.PackID].Insert(packBlobHash)
}
debug.Log("%d blobs processed", cnt)
return nil
})
@@ -120,7 +141,12 @@ func (c *Checker) LoadIndex(ctx context.Context, p restic.TerminalCounterFactory
debug.Log("checking for duplicate packs")
for packID := range packTypes {
debug.Log(" check pack %v: contained in %d indexes", packID, len(packToIndex[packID]))
if len(packToIndex[packID]) > 1 {
if len(packToPackBlobHash[packID]) > 1 {
hints = append(hints, &ErrIncompletePackEntry{
PackID: packID,
Indexes: packToIndex[packID],
})
} else if len(packToIndex[packID]) > 1 {
hints = append(hints, &ErrDuplicatePacks{
PackID: packID,
Indexes: packToIndex[packID],