restic/cmd/restic/cmd_check.go

170 lines
3.6 KiB
Go
Raw Normal View History

package main
import (
2017-06-04 09:16:55 +00:00
"context"
2015-07-11 14:00:49 +00:00
"fmt"
"os"
2015-12-06 16:29:31 +00:00
"time"
2016-09-17 10:36:05 +00:00
"github.com/spf13/cobra"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal"
"github.com/restic/restic/internal/checker"
"github.com/restic/restic/internal/errors"
)
2016-09-17 10:36:05 +00:00
var cmdCheck = &cobra.Command{
Use: "check [flags]",
Short: "check the repository for errors",
Long: `
The "check" command tests the repository for errors and reports any errors it
finds. It can also be used to read all data and therefore simulate a restore.
`,
RunE: func(cmd *cobra.Command, args []string) error {
return runCheck(checkOptions, globalOptions, args)
},
}
2017-03-08 19:09:24 +00:00
// CheckOptions bundles all options for the 'check' command.
2016-09-17 10:36:05 +00:00
type CheckOptions struct {
ReadData bool
CheckUnused bool
}
2016-09-17 10:36:05 +00:00
var checkOptions CheckOptions
func init() {
2016-09-17 10:36:05 +00:00
cmdRoot.AddCommand(cmdCheck)
2016-09-17 10:36:05 +00:00
f := cmdCheck.Flags()
f.BoolVar(&checkOptions.ReadData, "read-data", false, "read all data blobs")
f.BoolVar(&checkOptions.CheckUnused, "check-unused", false, "find unused blobs")
}
2016-09-17 10:36:05 +00:00
func newReadProgress(gopts GlobalOptions, todo restic.Stat) *restic.Progress {
if gopts.Quiet {
2015-12-06 16:29:31 +00:00
return nil
}
readProgress := restic.NewProgress()
2015-12-06 16:29:31 +00:00
readProgress.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
status := fmt.Sprintf("[%s] %s %d / %d items",
formatDuration(d),
formatPercent(s.Blobs, todo.Blobs),
s.Blobs, todo.Blobs)
if w := stdoutTerminalWidth(); w > 0 {
2015-12-06 16:29:31 +00:00
if len(status) > w {
max := w - len(status) - 4
status = status[:max] + "... "
}
}
PrintProgress("%s", status)
2015-12-06 16:29:31 +00:00
}
readProgress.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
fmt.Printf("\nduration: %s\n", formatDuration(d))
}
return readProgress
}
2016-09-17 10:36:05 +00:00
func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
if len(args) != 0 {
2016-09-01 20:17:37 +00:00
return errors.Fatal("check has no arguments")
}
2016-09-17 10:36:05 +00:00
repo, err := OpenRepository(gopts)
if err != nil {
return err
}
2016-09-17 10:36:05 +00:00
if !gopts.NoLock {
Verbosef("Create exclusive lock for repository\n")
lock, err := lockRepoExclusive(repo)
defer unlockRepo(lock)
if err != nil {
return err
}
}
chkr := checker.New(repo)
2016-09-17 10:36:05 +00:00
Verbosef("Load indexes\n")
2017-06-04 09:16:55 +00:00
hints, errs := chkr.LoadIndex(context.TODO())
2015-10-25 16:24:52 +00:00
dupFound := false
for _, hint := range hints {
2016-09-17 10:36:05 +00:00
Printf("%v\n", hint)
2015-10-25 16:24:52 +00:00
if _, ok := hint.(checker.ErrDuplicatePacks); ok {
dupFound = true
}
}
if dupFound {
2016-09-17 10:36:05 +00:00
Printf("\nrun `restic rebuild-index' to correct this\n")
}
if len(errs) > 0 {
for _, err := range errs {
2016-09-17 10:36:05 +00:00
Warnf("error: %v\n", err)
}
2016-09-01 20:17:37 +00:00
return errors.Fatal("LoadIndex returned errors")
}
2015-07-11 14:00:49 +00:00
errorsFound := false
errChan := make(chan error)
2016-09-17 10:36:05 +00:00
Verbosef("Check all packs\n")
2017-06-04 09:16:55 +00:00
go chkr.Packs(context.TODO(), errChan)
for err := range errChan {
2015-07-11 14:00:49 +00:00
errorsFound = true
fmt.Fprintf(os.Stderr, "%v\n", err)
2015-07-11 14:00:49 +00:00
}
2016-09-17 10:36:05 +00:00
Verbosef("Check snapshots, trees and blobs\n")
errChan = make(chan error)
2017-06-04 09:16:55 +00:00
go chkr.Structure(context.TODO(), errChan)
for err := range errChan {
2015-07-11 14:00:49 +00:00
errorsFound = true
if e, ok := err.(checker.TreeError); ok {
fmt.Fprintf(os.Stderr, "error for tree %v:\n", e.ID.Str())
for _, treeErr := range e.Errors {
fmt.Fprintf(os.Stderr, " %v\n", treeErr)
}
} else {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
}
2015-07-11 14:00:49 +00:00
}
2016-09-17 10:36:05 +00:00
if opts.CheckUnused {
for _, id := range chkr.UnusedBlobs() {
2016-09-17 10:36:05 +00:00
Verbosef("unused blob %v\n", id.Str())
errorsFound = true
}
}
2016-09-17 10:36:05 +00:00
if opts.ReadData {
Verbosef("Read all data\n")
2015-12-06 16:09:06 +00:00
2016-09-17 10:36:05 +00:00
p := newReadProgress(gopts, restic.Stat{Blobs: chkr.CountPacks()})
2015-12-06 16:09:06 +00:00
errChan := make(chan error)
2017-06-04 09:16:55 +00:00
go chkr.ReadData(context.TODO(), p, errChan)
2015-12-06 16:09:06 +00:00
for err := range errChan {
errorsFound = true
fmt.Fprintf(os.Stderr, "%v\n", err)
}
}
2015-07-11 14:00:49 +00:00
if errorsFound {
2016-09-01 20:17:37 +00:00
return errors.Fatal("repository contains errors")
2015-07-11 14:00:49 +00:00
}
return nil
}