2
2
mirror of https://github.com/octoleo/restic.git synced 2024-05-30 23:50:48 +00:00

debug: cleanup repair code

This commit is contained in:
Michael Eischer 2021-03-10 21:07:52 +01:00
parent fa7b9d5dfe
commit 547d9b384d

View File

@ -213,86 +213,80 @@ func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte, by
buf := make([]byte, len(input)) buf := make([]byte, len(input))
copy(buf, input) copy(buf, input)
for { testFlip := func(idx int, pattern byte) bool {
select { // flip bits
case i, ok := <-ch: buf[idx] ^= pattern
if !ok {
return nil
}
if bytewise {
for j := 0; j < 255; j++ {
// flip bits
buf[i] ^= byte(j)
nonce, plaintext := buf[:key.NonceSize()], buf[key.NonceSize():] nonce, plaintext := buf[:key.NonceSize()], buf[key.NonceSize():]
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.Printf("\n") fmt.Printf("\n")
fmt.Printf(" blob could be repaired by XORing byte %v with 0x%02x\n", i, j) fmt.Printf(" blob could be repaired by XORing byte %v with 0x%02x\n", idx, pattern)
fmt.Printf(" hash is %v\n", restic.Hash(plaintext)) fmt.Printf(" hash is %v\n", restic.Hash(plaintext))
close(done) close(done)
found = true found = true
fixed = plaintext fixed = plaintext
return nil return true
} }
// flip bits back // flip bits back
buf[i] ^= byte(j) buf[idx] ^= pattern
return false
}
for i := range ch {
if bytewise {
for j := 0; j < 255; j++ {
if testFlip(i, byte(j)) {
return nil
} }
} else { }
for j := 0; j < 7; j++ { } else {
// flip bit for j := 0; j < 7; j++ {
buf[i] ^= (1 << uint(j)) // flip each bit once
if testFlip(i, (1 << uint(j))) {
nonce, plaintext := buf[:key.NonceSize()], buf[key.NonceSize():] return nil
plaintext, err := key.Open(plaintext[:0], nonce, plaintext, nil)
if err == nil {
fmt.Printf("\n")
fmt.Printf(" blob could be repaired by flipping bit %v in byte %v\n", j, i)
fmt.Printf(" hash is %v\n", restic.Hash(plaintext))
close(done)
found = true
fixed = plaintext
return nil
}
// flip bit back
buf[i] ^= (1 << uint(j))
} }
} }
} }
} }
return nil
}) })
} }
start := time.Now() wg.Go(func() error {
info := time.Now() defer close(ch)
outer:
for i := range input {
select {
case ch <- i:
case <-done:
fmt.Printf(" done after %v\n", time.Since(start))
break outer
}
if time.Since(info) > time.Second { start := time.Now()
secs := time.Since(start).Seconds() info := time.Now()
gps := float64(i) / secs for i := range input {
remaining := len(input) - i select {
eta := time.Duration(float64(remaining)/gps) * time.Second case ch <- i:
case <-done:
fmt.Printf(" done after %v\n", time.Since(start))
return nil
}
fmt.Printf("\r%d byte of %d done (%.2f%%), %.0f byte per second, ETA %v", if time.Since(info) > time.Second {
i, len(input), float32(i)/float32(len(input)*100), secs := time.Since(start).Seconds()
gps, eta) gps := float64(i) / secs
info = time.Now() remaining := len(input) - i
eta := time.Duration(float64(remaining)/gps) * time.Second
fmt.Printf("\r%d byte of %d done (%.2f%%), %.0f byte per second, ETA %v",
i, len(input), float32(i)/float32(len(input))*100, gps, eta)
info = time.Now()
}
} }
return nil
})
err := wg.Wait()
if err != nil {
panic("all go rountines can only return nil")
} }
close(ch)
wg.Wait()
if !found { if !found {
fmt.Printf("\n blob could not be repaired by single bit flip\n") fmt.Printf("\n blob could not be repaired\n")
} }
return fixed return fixed
} }
@ -315,18 +309,17 @@ func decryptUnsigned(ctx context.Context, k *crypto.Key, buf []byte) []byte {
func loadBlobs(ctx context.Context, repo restic.Repository, pack restic.ID, list []restic.Blob) error { func loadBlobs(ctx context.Context, repo restic.Repository, pack restic.ID, list []restic.Blob) error {
be := repo.Backend() be := repo.Backend()
h := restic.Handle{
Name: pack.String(),
Type: restic.PackFile,
}
for _, blob := range list { for _, blob := range list {
fmt.Printf(" loading blob %v at %v (length %v)\n", blob.ID, blob.Offset, blob.Length) fmt.Printf(" loading blob %v at %v (length %v)\n", blob.ID, blob.Offset, blob.Length)
buf := make([]byte, blob.Length) buf := make([]byte, blob.Length)
h := restic.Handle{
Name: pack.String(),
Type: restic.PackFile,
}
err := be.Load(ctx, h, int(blob.Length), int64(blob.Offset), func(rd io.Reader) error { err := be.Load(ctx, h, int(blob.Length), int64(blob.Offset), func(rd io.Reader) error {
n, err := io.ReadFull(rd, buf) n, err := io.ReadFull(rd, buf)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "read error after %d bytes: %v\n", n, err) return fmt.Errorf("read error after %d bytes: %v\n", n, err)
return err
} }
return nil return nil
}) })
@ -349,9 +342,10 @@ func loadBlobs(ctx context.Context, repo restic.Repository, pack restic.ID, list
if plain != nil { if plain != nil {
id := restic.Hash(plain) id := restic.Hash(plain)
if !id.Equal(blob.ID) { if !id.Equal(blob.ID) {
fmt.Printf(" successfully repaired blob (length %v), hash is %v, ID does not match, wanted %v\n", len(plain), id, blob.ID) fmt.Printf(" repaired blob (length %v), hash is %v, ID does not match, wanted %v\n", len(plain), id, blob.ID)
prefix = "repaired-wrong-hash-" prefix = "repaired-wrong-hash-"
} else { } else {
fmt.Printf(" successfully repaired blob (length %v), hash is %v, ID matches\n", len(plain), id)
prefix = "repaired-" prefix = "repaired-"
} }
} else { } else {