2
2
mirror of https://github.com/octoleo/restic.git synced 2024-12-23 11:28:54 +00:00

examine: add byte repair mode

This commit is contained in:
Alexander Neumann 2018-09-29 18:28:39 +02:00 committed by Michael Eischer
parent 52061e817c
commit ce4b6d0874

View File

@ -47,12 +47,14 @@ Exit status is 0 if the command was successful, and non-zero if there was any er
} }
var tryRepair bool var tryRepair bool
var repairByte bool
func init() { func init() {
cmdRoot.AddCommand(cmdDebug) cmdRoot.AddCommand(cmdDebug)
cmdDebug.AddCommand(cmdDebugDump) cmdDebug.AddCommand(cmdDebugDump)
cmdDebug.AddCommand(cmdDebugExamine) cmdDebug.AddCommand(cmdDebugExamine)
cmdDebugExamine.Flags().BoolVar(&tryRepair, "try-repair", false, "try to repair broken blobs with single bit flips") cmdDebugExamine.Flags().BoolVar(&tryRepair, "try-repair", false, "try to repair broken blobs with single bit flips")
cmdDebugExamine.Flags().BoolVar(&repairByte, "repair-byte", false, "try to repair broken blobs by trying bytes")
} }
func prettyPrintJSON(wr io.Writer, item interface{}) error { func prettyPrintJSON(wr io.Writer, item interface{}) error {
@ -186,8 +188,12 @@ var cmdDebugExamine = &cobra.Command{
}, },
} }
func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte) { func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte, bytewise bool) {
if bytewise {
fmt.Printf(" trying to repair blob by finding a broken byte\n")
} else {
fmt.Printf(" trying to repair blob with single bit flip\n") fmt.Printf(" trying to repair blob with single bit flip\n")
}
ch := make(chan int) ch := make(chan int)
var wg errgroup.Group var wg errgroup.Group
@ -205,6 +211,25 @@ func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte) {
case <-done: case <-done:
return nil return nil
case i := <-ch: case i := <-ch:
if bytewise {
for j := 0; j < 255; j++ {
// flip bits
buf[i] ^= byte(j)
nonce, plaintext := buf[:key.NonceSize()], buf[key.NonceSize():]
plaintext, err := key.Open(plaintext[:0], nonce, plaintext, nil)
if err == nil {
fmt.Printf("\n")
fmt.Printf(" blob could be repaired by XORing byte %v with 0x%02x\n", i, j)
fmt.Printf(" hash is %v\n", restic.Hash(plaintext))
close(done)
return nil
}
// flip bits back
buf[i] ^= byte(j)
}
} else {
for j := 0; j < 7; j++ { for j := 0; j < 7; j++ {
// flip bit // flip bit
buf[i] ^= (1 << uint(j)) buf[i] ^= (1 << uint(j))
@ -224,6 +249,7 @@ func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte) {
} }
} }
} }
}
}) })
} }
@ -244,7 +270,7 @@ outer:
remaining := len(input) - i remaining := len(input) - i
eta := time.Duration(float64(remaining)/gps) * time.Second eta := time.Duration(float64(remaining)/gps) * time.Second
fmt.Printf("\r%d byte of %d done (%.2f%%), %.2f guesses per second, ETA %v", fmt.Printf("\r%d byte of %d done (%.2f%%), %.0f byte per second, ETA %v",
i, len(input), float32(i)/float32(len(input)*100), i, len(input), float32(i)/float32(len(input)*100),
gps, eta) gps, eta)
info = time.Now() info = time.Now()
@ -294,8 +320,8 @@ func loadBlobs(ctx context.Context, repo restic.Repository, pack string, list []
plaintext, err = key.Open(plaintext[:0], nonce, plaintext, nil) plaintext, err = key.Open(plaintext[:0], nonce, plaintext, nil)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "error decrypting blob: %v\n", err) fmt.Fprintf(os.Stderr, "error decrypting blob: %v\n", err)
if tryRepair { if tryRepair || repairByte {
tryRepairWithBitflip(ctx, key, buf) tryRepairWithBitflip(ctx, key, buf, repairByte)
} }
continue continue
} }