github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/cmd/restic/cmd_check.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "time" 8 9 "github.com/spf13/cobra" 10 11 "github.com/restic/restic/internal/checker" 12 "github.com/restic/restic/internal/errors" 13 "github.com/restic/restic/internal/restic" 14 ) 15 16 var cmdCheck = &cobra.Command{ 17 Use: "check [flags]", 18 Short: "Check the repository for errors", 19 Long: ` 20 The "check" command tests the repository for errors and reports any errors it 21 finds. It can also be used to read all data and therefore simulate a restore. 22 23 By default, the "check" command will always load all data directly from the 24 repository and not use a local cache. 25 `, 26 DisableAutoGenTag: true, 27 RunE: func(cmd *cobra.Command, args []string) error { 28 return runCheck(checkOptions, globalOptions, args) 29 }, 30 } 31 32 // CheckOptions bundles all options for the 'check' command. 33 type CheckOptions struct { 34 ReadData bool 35 CheckUnused bool 36 WithCache bool 37 } 38 39 var checkOptions CheckOptions 40 41 func init() { 42 cmdRoot.AddCommand(cmdCheck) 43 44 f := cmdCheck.Flags() 45 f.BoolVar(&checkOptions.ReadData, "read-data", false, "read all data blobs") 46 f.BoolVar(&checkOptions.CheckUnused, "check-unused", false, "find unused blobs") 47 f.BoolVar(&checkOptions.WithCache, "with-cache", false, "use the cache") 48 } 49 50 func newReadProgress(gopts GlobalOptions, todo restic.Stat) *restic.Progress { 51 if gopts.Quiet { 52 return nil 53 } 54 55 readProgress := restic.NewProgress() 56 57 readProgress.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) { 58 status := fmt.Sprintf("[%s] %s %d / %d items", 59 formatDuration(d), 60 formatPercent(s.Blobs, todo.Blobs), 61 s.Blobs, todo.Blobs) 62 63 if w := stdoutTerminalWidth(); w > 0 { 64 if len(status) > w { 65 max := w - len(status) - 4 66 status = status[:max] + "... " 67 } 68 } 69 70 PrintProgress("%s", status) 71 } 72 73 readProgress.OnDone = func(s restic.Stat, d time.Duration, ticker bool) { 74 fmt.Printf("\nduration: %s\n", formatDuration(d)) 75 } 76 77 return readProgress 78 } 79 80 func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error { 81 if len(args) != 0 { 82 return errors.Fatal("check has no arguments") 83 } 84 85 if !opts.WithCache { 86 // do not use a cache for the checker 87 gopts.NoCache = true 88 } 89 90 repo, err := OpenRepository(gopts) 91 if err != nil { 92 return err 93 } 94 95 if !gopts.NoLock { 96 Verbosef("create exclusive lock for repository\n") 97 lock, err := lockRepoExclusive(repo) 98 defer unlockRepo(lock) 99 if err != nil { 100 return err 101 } 102 } 103 104 chkr := checker.New(repo) 105 106 Verbosef("load indexes\n") 107 hints, errs := chkr.LoadIndex(context.TODO()) 108 109 dupFound := false 110 for _, hint := range hints { 111 Printf("%v\n", hint) 112 if _, ok := hint.(checker.ErrDuplicatePacks); ok { 113 dupFound = true 114 } 115 } 116 117 if dupFound { 118 Printf("\nrun `restic rebuild-index' to correct this\n") 119 } 120 121 if len(errs) > 0 { 122 for _, err := range errs { 123 Warnf("error: %v\n", err) 124 } 125 return errors.Fatal("LoadIndex returned errors") 126 } 127 128 errorsFound := false 129 errChan := make(chan error) 130 131 Verbosef("check all packs\n") 132 go chkr.Packs(context.TODO(), errChan) 133 134 for err := range errChan { 135 errorsFound = true 136 fmt.Fprintf(os.Stderr, "%v\n", err) 137 } 138 139 Verbosef("check snapshots, trees and blobs\n") 140 errChan = make(chan error) 141 go chkr.Structure(context.TODO(), errChan) 142 143 for err := range errChan { 144 errorsFound = true 145 if e, ok := err.(checker.TreeError); ok { 146 fmt.Fprintf(os.Stderr, "error for tree %v:\n", e.ID.Str()) 147 for _, treeErr := range e.Errors { 148 fmt.Fprintf(os.Stderr, " %v\n", treeErr) 149 } 150 } else { 151 fmt.Fprintf(os.Stderr, "error: %v\n", err) 152 } 153 } 154 155 if opts.CheckUnused { 156 for _, id := range chkr.UnusedBlobs() { 157 Verbosef("unused blob %v\n", id.Str()) 158 errorsFound = true 159 } 160 } 161 162 if opts.ReadData { 163 Verbosef("read all data\n") 164 165 p := newReadProgress(gopts, restic.Stat{Blobs: chkr.CountPacks()}) 166 errChan := make(chan error) 167 168 go chkr.ReadData(context.TODO(), p, errChan) 169 170 for err := range errChan { 171 errorsFound = true 172 fmt.Fprintf(os.Stderr, "%v\n", err) 173 } 174 } 175 176 if errorsFound { 177 return errors.Fatal("repository contains errors") 178 } 179 180 Verbosef("no errors were found\n") 181 182 return nil 183 }