restic/cmd/restic/cmd_repair_index.go

167 lines
4.4 KiB
Go
Raw Normal View History

2015-10-25 17:24:52 +01:00
package main
2016-09-17 12:36:05 +02:00
import (
"context"
"github.com/restic/restic/internal/index"
"github.com/restic/restic/internal/pack"
2020-10-10 21:51:11 +02:00
"github.com/restic/restic/internal/repository"
2017-07-24 17:42:25 +02:00
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui/progress"
"github.com/restic/restic/internal/ui/termstatus"
2016-09-17 12:36:05 +02:00
"github.com/spf13/cobra"
"github.com/spf13/pflag"
2016-09-17 12:36:05 +02:00
)
2015-10-25 17:24:52 +01:00
var cmdRepairIndex = &cobra.Command{
Use: "index [flags]",
2020-10-10 21:51:11 +02:00
Short: "Build a new index",
2016-09-17 12:36:05 +02:00
Long: `
The "repair index" command creates a new index based on the pack files in the
repository.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
2016-09-17 12:36:05 +02:00
`,
DisableAutoGenTag: true,
2024-02-10 22:58:10 +01:00
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRebuildIndex(cmd.Context(), repairIndexOptions, globalOptions, term)
2016-09-17 12:36:05 +02:00
},
2015-10-25 17:24:52 +01:00
}
var cmdRebuildIndex = &cobra.Command{
Use: "rebuild-index [flags]",
Short: cmdRepairIndex.Short,
Long: cmdRepairIndex.Long,
Deprecated: `Use "repair index" instead`,
DisableAutoGenTag: true,
RunE: cmdRepairIndex.RunE,
}
// RepairIndexOptions collects all options for the repair index command.
type RepairIndexOptions struct {
2020-10-10 21:51:11 +02:00
ReadAllPacks bool
}
var repairIndexOptions RepairIndexOptions
2020-10-10 21:51:11 +02:00
2015-10-25 17:24:52 +01:00
func init() {
cmdRepair.AddCommand(cmdRepairIndex)
// add alias for old name
2016-09-17 12:36:05 +02:00
cmdRoot.AddCommand(cmdRebuildIndex)
2020-10-10 21:51:11 +02:00
for _, f := range []*pflag.FlagSet{cmdRepairIndex.Flags(), cmdRebuildIndex.Flags()} {
f.BoolVar(&repairIndexOptions.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch")
}
2015-10-25 17:24:52 +01:00
}
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term *termstatus.Terminal) error {
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false)
2015-10-25 17:24:52 +01:00
if err != nil {
return err
}
defer unlock()
2015-10-25 17:24:52 +01:00
printer := newTerminalProgressPrinter(gopts.verbosity, term)
return rebuildIndex(ctx, opts, repo, printer)
}
func rebuildIndex(ctx context.Context, opts RepairIndexOptions, repo *repository.Repository, printer progress.Printer) error {
2020-10-10 21:51:31 +02:00
var obsoleteIndexes restic.IDs
2020-10-10 21:51:11 +02:00
packSizeFromList := make(map[restic.ID]int64)
2020-12-05 16:10:18 +01:00
packSizeFromIndex := make(map[restic.ID]int64)
2020-10-10 21:51:31 +02:00
removePacks := restic.NewIDSet()
2020-10-10 21:51:11 +02:00
if opts.ReadAllPacks {
2020-12-05 16:10:18 +01:00
// get list of old index files but start with empty index
2024-02-10 22:58:10 +01:00
err := repo.List(ctx, restic.IndexFile, func(id restic.ID, _ int64) error {
2020-10-10 21:51:31 +02:00
obsoleteIndexes = append(obsoleteIndexes, id)
2020-10-10 21:51:11 +02:00
return nil
})
if err != nil {
return err
}
} else {
printer.P("loading indexes...\n")
mi := index.NewMasterIndex()
2024-02-10 22:58:10 +01:00
err := index.ForAllIndexes(ctx, repo, repo, func(id restic.ID, idx *index.Index, _ bool, err error) error {
if err != nil {
printer.E("removing invalid index %v: %v\n", id, err)
obsoleteIndexes = append(obsoleteIndexes, id)
return nil
}
mi.Insert(idx)
return nil
})
if err != nil {
return err
}
err = mi.MergeFinalIndexes()
if err != nil {
return err
}
err = repo.SetIndex(mi)
2020-10-10 21:51:11 +02:00
if err != nil {
return err
}
packSizeFromIndex = pack.Size(ctx, repo.Index(), false)
2020-12-05 16:10:18 +01:00
}
printer.P("getting pack files to read...\n")
2020-12-05 16:10:18 +01:00
err := repo.List(ctx, restic.PackFile, func(id restic.ID, packSize int64) error {
size, ok := packSizeFromIndex[id]
if !ok || size != packSize {
// Pack was not referenced in index or size does not match
packSizeFromList[id] = packSize
2020-10-10 21:51:31 +02:00
removePacks.Insert(id)
}
if !ok {
printer.E("adding pack file to index %v\n", id)
} else if size != packSize {
printer.E("reindexing pack file %v with unexpected size %v instead of %v\n", id, packSize, size)
}
2020-12-05 16:10:18 +01:00
delete(packSizeFromIndex, id)
return nil
})
if err != nil {
return err
}
for id := range packSizeFromIndex {
// forget pack files that are referenced in the index but do not exist
// when rebuilding the index
removePacks.Insert(id)
printer.E("removing not found pack file %v\n", id)
}
2020-10-10 21:51:11 +02:00
if len(packSizeFromList) > 0 {
printer.P("reading pack files\n")
bar := printer.NewCounter("packs")
bar.SetMax(uint64(len(packSizeFromList)))
2020-10-10 21:51:31 +02:00
invalidFiles, err := repo.CreateIndexFromPacks(ctx, packSizeFromList, bar)
2021-01-31 18:28:02 +01:00
bar.Done()
2020-10-10 21:51:11 +02:00
if err != nil {
return err
}
2020-10-10 21:51:11 +02:00
for _, id := range invalidFiles {
printer.V("skipped incomplete pack file: %v\n", id)
2020-10-10 21:51:11 +02:00
}
}
err = rebuildIndexFiles(ctx, repo, removePacks, obsoleteIndexes, false, printer)
if err != nil {
2020-10-10 21:51:11 +02:00
return err
}
printer.P("done\n")
return nil
2015-10-25 17:24:52 +01:00
}