mirror of
https://github.com/kopia/kopia.git
synced 2026-01-25 23:08:01 -05:00
removed GetStorageBlock(), replaced with Verify() that also returns storage blocks added blockSizeCache.put() so that object writer can update the cache
143 lines
3.1 KiB
Go
143 lines
3.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/snapshot"
|
|
|
|
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
|
)
|
|
|
|
var (
|
|
verifyCommand = objectCommands.Command("verify", "Verify the contents of stored object")
|
|
verifyCommandRecursive = verifyCommand.Flag("recursive", "Recursive verification of directories").Short('r').Bool()
|
|
verifyCommandErrorThreshold = verifyCommand.Flag("max-errors", "Maximum number of errors before stopping").Default("0").Int()
|
|
verifyCommandPath = verifyCommand.Arg("path", "Path").Required().String()
|
|
)
|
|
|
|
type verifier struct {
|
|
mgr *snapshot.Manager
|
|
om *repo.ObjectManager
|
|
visited map[string]bool
|
|
errors []error
|
|
}
|
|
|
|
func (v *verifier) reportError(path string, err error) bool {
|
|
err = fmt.Errorf("error validating %q: %v", path, err)
|
|
log.Printf("%v", err)
|
|
v.errors = append(v.errors, err)
|
|
return len(v.errors) >= *verifyCommandErrorThreshold
|
|
}
|
|
|
|
func (v *verifier) verifyDirectory(oid repo.ObjectID, path string) error {
|
|
if v.visited[oid.String()] {
|
|
return nil
|
|
}
|
|
v.visited[oid.String()] = true
|
|
|
|
log.Printf("verifying directory %q (%v)", path, oid)
|
|
|
|
d := v.mgr.DirectoryEntry(oid)
|
|
entries, err := d.Readdir()
|
|
if err != nil {
|
|
if v.reportError(path, err) {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, e := range entries {
|
|
m := e.Metadata()
|
|
objectID := e.(repo.HasObjectID).ObjectID()
|
|
childPath := path + "/" + m.Name
|
|
if m.FileMode().IsDir() {
|
|
if *verifyCommandRecursive {
|
|
if err := v.verifyDirectory(objectID, childPath); err != nil {
|
|
if v.reportError(childPath, err) {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := v.verifyObject(objectID, childPath, m.FileSize); err != nil {
|
|
if v.reportError(childPath, err) {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (v *verifier) verifyObject(oid repo.ObjectID, path string, expectedLength int64) error {
|
|
if v.visited[oid.String()] {
|
|
return nil
|
|
}
|
|
v.visited[oid.String()] = true
|
|
|
|
if expectedLength < 0 {
|
|
log.Printf("verifying object %v", oid)
|
|
} else {
|
|
log.Printf("verifying object %v (%v) with length %v", path, oid, expectedLength)
|
|
}
|
|
|
|
length, _, err := v.om.VerifyObject(oid)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid object %q: %v", oid, err)
|
|
}
|
|
|
|
if expectedLength == -1 {
|
|
log.Printf("object length: %v", length)
|
|
} else if length != expectedLength {
|
|
return fmt.Errorf("invalid object length %q, %v, expected %v", oid, length, expectedLength)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func runVerifyCommand(context *kingpin.ParseContext) error {
|
|
rep := mustOpenRepository(nil)
|
|
defer rep.Close()
|
|
|
|
mgr := snapshot.NewManager(rep)
|
|
|
|
oid, err := parseObjectID(mgr, *verifyCommandPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v := verifier{
|
|
mgr,
|
|
rep.Objects,
|
|
make(map[string]bool),
|
|
nil,
|
|
}
|
|
|
|
if *verifyCommandRecursive {
|
|
v.verifyDirectory(oid, oid.String())
|
|
}
|
|
|
|
v.verifyObject(oid, oid.String(), -1)
|
|
|
|
if len(v.errors) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if len(v.errors) == 1 {
|
|
return v.errors[0]
|
|
}
|
|
|
|
for i, e := range v.errors {
|
|
fmt.Fprintf(os.Stderr, " %-3v: %v\n", i, e)
|
|
}
|
|
|
|
return fmt.Errorf("encountered %v errors", len(v.errors))
|
|
}
|
|
|
|
func init() {
|
|
verifyCommand.Action(runVerifyCommand)
|
|
}
|