restic/cmd/restic/cmd_rebuild_index.go

152 lines
3.8 KiB
Go
Raw Normal View History

2015-10-25 16:24:52 +00:00
package main
2016-09-17 10:36:05 +00:00
import (
"context"
"github.com/restic/restic/internal/pack"
2020-10-10 19:51:11 +00:00
"github.com/restic/restic/internal/repository"
2017-07-24 15:42:25 +00:00
"github.com/restic/restic/internal/restic"
2015-10-25 16:24:52 +00:00
2016-09-17 10:36:05 +00:00
"github.com/spf13/cobra"
)
2015-10-25 16:24:52 +00:00
2016-09-17 10:36:05 +00:00
var cmdRebuildIndex = &cobra.Command{
Use: "rebuild-index [flags]",
2020-10-10 19:51:11 +00:00
Short: "Build a new index",
2016-09-17 10:36:05 +00:00
Long: `
The "rebuild-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 10:36:05 +00:00
`,
DisableAutoGenTag: true,
2016-09-17 10:36:05 +00:00
RunE: func(cmd *cobra.Command, args []string) error {
2022-10-02 21:24:37 +00:00
return runRebuildIndex(cmd.Context(), rebuildIndexOptions, globalOptions)
2016-09-17 10:36:05 +00:00
},
2015-10-25 16:24:52 +00:00
}
2020-10-10 19:51:11 +00:00
// RebuildIndexOptions collects all options for the rebuild-index command.
type RebuildIndexOptions struct {
ReadAllPacks bool
}
var rebuildIndexOptions RebuildIndexOptions
2015-10-25 16:24:52 +00:00
func init() {
2016-09-17 10:36:05 +00:00
cmdRoot.AddCommand(cmdRebuildIndex)
2020-10-10 19:51:11 +00:00
f := cmdRebuildIndex.Flags()
f.BoolVar(&rebuildIndexOptions.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch")
2015-10-25 16:24:52 +00:00
}
func runRebuildIndex(ctx context.Context, opts RebuildIndexOptions, gopts GlobalOptions) error {
repo, err := OpenRepository(ctx, gopts)
2015-10-25 16:24:52 +00:00
if err != nil {
return err
}
lock, ctx, err := lockRepoExclusive(ctx, repo)
2015-10-25 16:24:52 +00:00
defer unlockRepo(lock)
if err != nil {
return err
}
return rebuildIndex(ctx, opts, gopts, repo, restic.NewIDSet())
}
func rebuildIndex(ctx context.Context, opts RebuildIndexOptions, gopts GlobalOptions, repo *repository.Repository, ignorePacks restic.IDSet) error {
2020-10-10 19:51:31 +00:00
var obsoleteIndexes restic.IDs
2020-10-10 19:51:11 +00:00
packSizeFromList := make(map[restic.ID]int64)
2020-12-05 15:10:18 +00:00
packSizeFromIndex := make(map[restic.ID]int64)
2020-10-10 19:51:31 +00:00
removePacks := restic.NewIDSet()
2020-10-10 19:51:11 +00:00
if opts.ReadAllPacks {
2020-12-05 15:10:18 +00:00
// get list of old index files but start with empty index
2020-10-10 19:51:11 +00:00
err := repo.List(ctx, restic.IndexFile, func(id restic.ID, size int64) error {
2020-10-10 19:51:31 +00:00
obsoleteIndexes = append(obsoleteIndexes, id)
2020-10-10 19:51:11 +00:00
return nil
})
if err != nil {
return err
}
} else {
Verbosef("loading indexes...\n")
mi := repository.NewMasterIndex()
err := repository.ForAllIndexes(ctx, repo, func(id restic.ID, idx *repository.Index, oldFormat bool, err error) error {
if err != nil {
Warnf("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 19:51:11 +00:00
if err != nil {
return err
}
packSizeFromIndex = pack.Size(ctx, repo.Index(), false)
2020-12-05 15:10:18 +00:00
}
2020-12-05 15:10:18 +00:00
Verbosef("getting pack files to read...\n")
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 19:51:31 +00:00
removePacks.Insert(id)
}
if !ok {
Warnf("adding pack file to index %v\n", id)
} else if size != packSize {
Warnf("reindexing pack file %v with unexpected size %v instead of %v\n", id, packSize, size)
}
2020-12-05 15:10:18 +00: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)
Warnf("removing not found pack file %v\n", id)
}
2020-10-10 19:51:11 +00:00
if len(packSizeFromList) > 0 {
Verbosef("reading pack files\n")
bar := newProgressMax(!globalOptions.Quiet, uint64(len(packSizeFromList)), "packs")
2020-10-10 19:51:31 +00:00
invalidFiles, err := repo.CreateIndexFromPacks(ctx, packSizeFromList, bar)
2021-01-31 17:28:02 +00:00
bar.Done()
2020-10-10 19:51:11 +00:00
if err != nil {
return err
}
2020-10-10 19:51:11 +00:00
for _, id := range invalidFiles {
Verboseff("skipped incomplete pack file: %v\n", id)
}
}
err = rebuildIndexFiles(ctx, gopts, repo, removePacks, obsoleteIndexes)
if err != nil {
2020-10-10 19:51:11 +00:00
return err
}
2020-10-10 19:51:11 +00:00
Verbosef("done\n")
return nil
2015-10-25 16:24:52 +00:00
}