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