mirror of
https://github.com/octoleo/restic.git
synced 2024-11-26 14:56:29 +00:00
Rework crypto, use restic.Repository everywhere
This commit is contained in:
parent
84f95a09d7
commit
ffbe05af9b
@ -11,16 +11,16 @@ import (
|
|||||||
"github.com/restic/chunker"
|
"github.com/restic/chunker"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadBlob(t *testing.T, repo *repository.Repository, id restic.ID, buf []byte) []byte {
|
func loadBlob(t *testing.T, repo restic.Repository, id restic.ID, buf []byte) []byte {
|
||||||
buf, err := repo.LoadBlob(id, restic.DataBlob, buf)
|
n, err := repo.LoadDataBlob(id, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("LoadBlob(%v) returned error %v", id, err)
|
t.Fatalf("LoadBlob(%v) returned error %v", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf
|
return buf[:n]
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSavedFile(t *testing.T, repo *repository.Repository, treeID restic.ID, name string, rd io.Reader) {
|
func checkSavedFile(t *testing.T, repo restic.Repository, treeID restic.ID, name string, rd io.Reader) {
|
||||||
tree, err := repo.LoadTree(treeID)
|
tree, err := repo.LoadTree(treeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("LoadTree() returned error %v", err)
|
t.Fatalf("LoadTree() returned error %v", err)
|
||||||
|
@ -12,8 +12,9 @@ import (
|
|||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
. "restic/test"
|
. "restic/test"
|
||||||
|
|
||||||
"github.com/restic/chunker"
|
|
||||||
"restic/errors"
|
"restic/errors"
|
||||||
|
|
||||||
|
"github.com/restic/chunker"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testPol = chunker.Pol(0x3DA3358B4DC173)
|
var testPol = chunker.Pol(0x3DA3358B4DC173)
|
||||||
@ -126,6 +127,14 @@ func BenchmarkArchiveDirectory(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func countPacks(repo restic.Repository, t restic.FileType) (n uint) {
|
||||||
|
for _ = range repo.Backend().List(t, nil) {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func archiveWithDedup(t testing.TB) {
|
func archiveWithDedup(t testing.TB) {
|
||||||
repo := SetupRepo()
|
repo := SetupRepo()
|
||||||
defer TeardownRepo(repo)
|
defer TeardownRepo(repo)
|
||||||
@ -145,7 +154,7 @@ func archiveWithDedup(t testing.TB) {
|
|||||||
t.Logf("archived snapshot %v", sn.ID().Str())
|
t.Logf("archived snapshot %v", sn.ID().Str())
|
||||||
|
|
||||||
// get archive stats
|
// get archive stats
|
||||||
cnt.before.packs = repo.Count(restic.DataFile)
|
cnt.before.packs = countPacks(repo, restic.DataFile)
|
||||||
cnt.before.dataBlobs = repo.Index().Count(restic.DataBlob)
|
cnt.before.dataBlobs = repo.Index().Count(restic.DataBlob)
|
||||||
cnt.before.treeBlobs = repo.Index().Count(restic.TreeBlob)
|
cnt.before.treeBlobs = repo.Index().Count(restic.TreeBlob)
|
||||||
t.Logf("packs %v, data blobs %v, tree blobs %v",
|
t.Logf("packs %v, data blobs %v, tree blobs %v",
|
||||||
@ -156,7 +165,7 @@ func archiveWithDedup(t testing.TB) {
|
|||||||
t.Logf("archived snapshot %v", sn2.ID().Str())
|
t.Logf("archived snapshot %v", sn2.ID().Str())
|
||||||
|
|
||||||
// get archive stats again
|
// get archive stats again
|
||||||
cnt.after.packs = repo.Count(restic.DataFile)
|
cnt.after.packs = countPacks(repo, restic.DataFile)
|
||||||
cnt.after.dataBlobs = repo.Index().Count(restic.DataBlob)
|
cnt.after.dataBlobs = repo.Index().Count(restic.DataBlob)
|
||||||
cnt.after.treeBlobs = repo.Index().Count(restic.TreeBlob)
|
cnt.after.treeBlobs = repo.Index().Count(restic.TreeBlob)
|
||||||
t.Logf("packs %v, data blobs %v, tree blobs %v",
|
t.Logf("packs %v, data blobs %v, tree blobs %v",
|
||||||
@ -173,7 +182,7 @@ func archiveWithDedup(t testing.TB) {
|
|||||||
t.Logf("archived snapshot %v, parent %v", sn3.ID().Str(), sn2.ID().Str())
|
t.Logf("archived snapshot %v, parent %v", sn3.ID().Str(), sn2.ID().Str())
|
||||||
|
|
||||||
// get archive stats again
|
// get archive stats again
|
||||||
cnt.after2.packs = repo.Count(restic.DataFile)
|
cnt.after2.packs = countPacks(repo, restic.DataFile)
|
||||||
cnt.after2.dataBlobs = repo.Index().Count(restic.DataBlob)
|
cnt.after2.dataBlobs = repo.Index().Count(restic.DataBlob)
|
||||||
cnt.after2.treeBlobs = repo.Index().Count(restic.TreeBlob)
|
cnt.after2.treeBlobs = repo.Index().Count(restic.TreeBlob)
|
||||||
t.Logf("packs %v, data blobs %v, tree blobs %v",
|
t.Logf("packs %v, data blobs %v, tree blobs %v",
|
||||||
|
@ -684,12 +684,13 @@ func checkPack(r restic.Repository, id restic.ID) error {
|
|||||||
debug.Log("Checker.checkPack", " check blob %d: %v", i, blob.ID.Str())
|
debug.Log("Checker.checkPack", " check blob %d: %v", i, blob.ID.Str())
|
||||||
|
|
||||||
plainBuf := make([]byte, blob.Length)
|
plainBuf := make([]byte, blob.Length)
|
||||||
plainBuf, err = crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length])
|
n, err := crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Checker.checkPack", " error decrypting blob %v: %v", blob.ID.Str(), err)
|
debug.Log("Checker.checkPack", " error decrypting blob %v: %v", blob.ID.Str(), err)
|
||||||
errs = append(errs, errors.Errorf("blob %v: %v", i, err))
|
errs = append(errs, errors.Errorf("blob %v: %v", i, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
plainBuf = plainBuf[:n]
|
||||||
|
|
||||||
hash := restic.Hash(plainBuf)
|
hash := restic.Hash(plainBuf)
|
||||||
if !hash.Equal(blob.ID) {
|
if !hash.Equal(blob.ID) {
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
var checkerTestData = filepath.Join("testdata", "checker-test-repo.tar.gz")
|
var checkerTestData = filepath.Join("testdata", "checker-test-repo.tar.gz")
|
||||||
|
|
||||||
func list(repo *repository.Repository, t restic.FileType) (IDs []string) {
|
func list(repo restic.Repository, t restic.FileType) (IDs []string) {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package checker
|
package checker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"restic/repository"
|
"restic"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestCheckRepo runs the checker on repo.
|
// TestCheckRepo runs the checker on repo.
|
||||||
func TestCheckRepo(t testing.TB, repo *repository.Repository) {
|
func TestCheckRepo(t testing.TB, repo restic.Repository) {
|
||||||
chkr := New(repo)
|
chkr := New(repo)
|
||||||
|
|
||||||
hints, errs := chkr.LoadIndex()
|
hints, errs := chkr.LoadIndex()
|
||||||
|
@ -274,9 +274,9 @@ func Encrypt(ks *Key, ciphertext []byte, plaintext []byte) ([]byte, error) {
|
|||||||
// Decrypt verifies and decrypts the ciphertext. Ciphertext must be in the form
|
// Decrypt verifies and decrypts the ciphertext. Ciphertext must be in the form
|
||||||
// IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the
|
// IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the
|
||||||
// same slice.
|
// same slice.
|
||||||
func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) ([]byte, error) {
|
func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) (int, error) {
|
||||||
if !ks.Valid() {
|
if !ks.Valid() {
|
||||||
return nil, errors.New("invalid key")
|
return 0, errors.New("invalid key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for plausible length
|
// check for plausible length
|
||||||
@ -284,21 +284,26 @@ func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) ([]byte, error
|
|||||||
panic("trying to decrypt invalid data: ciphertext too small")
|
panic("trying to decrypt invalid data: ciphertext too small")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check buffer length for plaintext
|
||||||
|
plaintextLength := len(ciphertextWithMac) - ivSize - macSize
|
||||||
|
if len(plaintext) < plaintextLength {
|
||||||
|
return 0, errors.Errorf("plaintext buffer too small, %d < %d", len(plaintext), plaintextLength)
|
||||||
|
}
|
||||||
|
|
||||||
// extract mac
|
// extract mac
|
||||||
l := len(ciphertextWithMac) - macSize
|
l := len(ciphertextWithMac) - macSize
|
||||||
ciphertextWithIV, mac := ciphertextWithMac[:l], ciphertextWithMac[l:]
|
ciphertextWithIV, mac := ciphertextWithMac[:l], ciphertextWithMac[l:]
|
||||||
|
|
||||||
// verify mac
|
// verify mac
|
||||||
if !poly1305Verify(ciphertextWithIV[ivSize:], ciphertextWithIV[:ivSize], &ks.MAC, mac) {
|
if !poly1305Verify(ciphertextWithIV[ivSize:], ciphertextWithIV[:ivSize], &ks.MAC, mac) {
|
||||||
return nil, ErrUnauthenticated
|
return 0, ErrUnauthenticated
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract iv
|
// extract iv
|
||||||
iv, ciphertext := ciphertextWithIV[:ivSize], ciphertextWithIV[ivSize:]
|
iv, ciphertext := ciphertextWithIV[:ivSize], ciphertextWithIV[ivSize:]
|
||||||
|
|
||||||
if cap(plaintext) < len(ciphertext) {
|
if len(ciphertext) != plaintextLength {
|
||||||
// extend plaintext
|
return 0, errors.Errorf("plaintext and ciphertext lengths do not match: %d != %d", len(ciphertext), plaintextLength)
|
||||||
plaintext = append(plaintext, make([]byte, len(ciphertext)-cap(plaintext))...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt data
|
// decrypt data
|
||||||
@ -312,7 +317,7 @@ func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) ([]byte, error
|
|||||||
plaintext = plaintext[:len(ciphertext)]
|
plaintext = plaintext[:len(ciphertext)]
|
||||||
e.XORKeyStream(plaintext, ciphertext)
|
e.XORKeyStream(plaintext, ciphertext)
|
||||||
|
|
||||||
return plaintext, nil
|
return plaintextLength, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid tests if the key is valid.
|
// Valid tests if the key is valid.
|
||||||
|
@ -100,15 +100,17 @@ func TestCrypto(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// decrypt message
|
// decrypt message
|
||||||
_, err = Decrypt(k, []byte{}, msg)
|
buf := make([]byte, len(tv.plaintext))
|
||||||
|
n, err := Decrypt(k, buf, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
buf = buf[:n]
|
||||||
|
|
||||||
// change mac, this must fail
|
// change mac, this must fail
|
||||||
msg[len(msg)-8] ^= 0x23
|
msg[len(msg)-8] ^= 0x23
|
||||||
|
|
||||||
if _, err = Decrypt(k, []byte{}, msg); err != ErrUnauthenticated {
|
if _, err = Decrypt(k, buf, msg); err != ErrUnauthenticated {
|
||||||
t.Fatal("wrong MAC value not detected")
|
t.Fatal("wrong MAC value not detected")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,15 +120,17 @@ func TestCrypto(t *testing.T) {
|
|||||||
// tamper with message, this must fail
|
// tamper with message, this must fail
|
||||||
msg[16+5] ^= 0x85
|
msg[16+5] ^= 0x85
|
||||||
|
|
||||||
if _, err = Decrypt(k, []byte{}, msg); err != ErrUnauthenticated {
|
if _, err = Decrypt(k, buf, msg); err != ErrUnauthenticated {
|
||||||
t.Fatal("tampered message not detected")
|
t.Fatal("tampered message not detected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// test decryption
|
// test decryption
|
||||||
p, err := Decrypt(k, []byte{}, tv.ciphertext)
|
p := make([]byte, len(tv.ciphertext))
|
||||||
|
n, err = Decrypt(k, p, tv.ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
p = p[:n]
|
||||||
|
|
||||||
if !bytes.Equal(p, tv.plaintext) {
|
if !bytes.Equal(p, tv.plaintext) {
|
||||||
t.Fatalf("wrong plaintext: expected %q but got %q\n", tv.plaintext, p)
|
t.Fatalf("wrong plaintext: expected %q but got %q\n", tv.plaintext, p)
|
||||||
|
@ -32,8 +32,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
|||||||
"ciphertext length does not match: want %d, got %d",
|
"ciphertext length does not match: want %d, got %d",
|
||||||
len(data)+crypto.Extension, len(ciphertext))
|
len(data)+crypto.Extension, len(ciphertext))
|
||||||
|
|
||||||
plaintext, err := crypto.Decrypt(k, nil, ciphertext)
|
plaintext := make([]byte, len(ciphertext))
|
||||||
|
n, err := crypto.Decrypt(k, plaintext, ciphertext)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
plaintext = plaintext[:n]
|
||||||
Assert(t, len(plaintext) == len(data),
|
Assert(t, len(plaintext) == len(data),
|
||||||
"plaintext length does not match: want %d, got %d",
|
"plaintext length does not match: want %d, got %d",
|
||||||
len(data), len(plaintext))
|
len(data), len(plaintext))
|
||||||
@ -58,8 +60,10 @@ func TestSmallBuffer(t *testing.T) {
|
|||||||
cap(ciphertext))
|
cap(ciphertext))
|
||||||
|
|
||||||
// check for the correct plaintext
|
// check for the correct plaintext
|
||||||
plaintext, err := crypto.Decrypt(k, nil, ciphertext)
|
plaintext := make([]byte, len(ciphertext))
|
||||||
|
n, err := crypto.Decrypt(k, plaintext, ciphertext)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
plaintext = plaintext[:n]
|
||||||
Assert(t, bytes.Equal(plaintext, data),
|
Assert(t, bytes.Equal(plaintext, data),
|
||||||
"wrong plaintext returned")
|
"wrong plaintext returned")
|
||||||
}
|
}
|
||||||
@ -78,8 +82,9 @@ func TestSameBuffer(t *testing.T) {
|
|||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
// use the same buffer for decryption
|
// use the same buffer for decryption
|
||||||
ciphertext, err = crypto.Decrypt(k, ciphertext, ciphertext)
|
n, err := crypto.Decrypt(k, ciphertext, ciphertext)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
ciphertext = ciphertext[:n]
|
||||||
Assert(t, bytes.Equal(ciphertext, data),
|
Assert(t, bytes.Equal(ciphertext, data),
|
||||||
"wrong plaintext returned")
|
"wrong plaintext returned")
|
||||||
}
|
}
|
||||||
@ -97,9 +102,9 @@ func TestCornerCases(t *testing.T) {
|
|||||||
len(c))
|
len(c))
|
||||||
|
|
||||||
// this should decrypt to nil
|
// this should decrypt to nil
|
||||||
p, err := crypto.Decrypt(k, nil, c)
|
n, err := crypto.Decrypt(k, nil, c)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
Equals(t, []byte(nil), p)
|
Equals(t, 0, n)
|
||||||
|
|
||||||
// test encryption for same slice, this should return an error
|
// test encryption for same slice, this should return an error
|
||||||
_, err = crypto.Encrypt(k, c, c)
|
_, err = crypto.Encrypt(k, c, c)
|
||||||
@ -160,7 +165,7 @@ func BenchmarkDecrypt(b *testing.B) {
|
|||||||
b.SetBytes(int64(size))
|
b.SetBytes(int64(size))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
plaintext, err = crypto.Decrypt(k, plaintext, ciphertext)
|
_, err = crypto.Decrypt(k, plaintext, ciphertext)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/repository"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Statically ensure that *dir implement those interface
|
// Statically ensure that *dir implement those interface
|
||||||
@ -20,14 +19,14 @@ var _ = fs.HandleReadDirAller(&dir{})
|
|||||||
var _ = fs.NodeStringLookuper(&dir{})
|
var _ = fs.NodeStringLookuper(&dir{})
|
||||||
|
|
||||||
type dir struct {
|
type dir struct {
|
||||||
repo *repository.Repository
|
repo restic.Repository
|
||||||
items map[string]*restic.Node
|
items map[string]*restic.Node
|
||||||
inode uint64
|
inode uint64
|
||||||
node *restic.Node
|
node *restic.Node
|
||||||
ownerIsRoot bool
|
ownerIsRoot bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDir(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*dir, error) {
|
func newDir(repo restic.Repository, node *restic.Node, ownerIsRoot bool) (*dir, error) {
|
||||||
debug.Log("newDir", "new dir for %v (%v)", node.Name, node.Subtree.Str())
|
debug.Log("newDir", "new dir for %v (%v)", node.Name, node.Subtree.Str())
|
||||||
tree, err := repo.LoadTree(*node.Subtree)
|
tree, err := repo.LoadTree(*node.Subtree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -50,7 +49,7 @@ func newDir(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*
|
|||||||
|
|
||||||
// replaceSpecialNodes replaces nodes with name "." and "/" by their contents.
|
// replaceSpecialNodes replaces nodes with name "." and "/" by their contents.
|
||||||
// Otherwise, the node is returned.
|
// Otherwise, the node is returned.
|
||||||
func replaceSpecialNodes(repo *repository.Repository, node *restic.Node) ([]*restic.Node, error) {
|
func replaceSpecialNodes(repo restic.Repository, node *restic.Node) ([]*restic.Node, error) {
|
||||||
if node.Type != "dir" || node.Subtree == nil {
|
if node.Type != "dir" || node.Subtree == nil {
|
||||||
return []*restic.Node{node}, nil
|
return []*restic.Node{node}, nil
|
||||||
}
|
}
|
||||||
@ -67,7 +66,7 @@ func replaceSpecialNodes(repo *repository.Repository, node *restic.Node) ([]*res
|
|||||||
return tree.Nodes, nil
|
return tree.Nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId, ownerIsRoot bool) (*dir, error) {
|
func newDirFromSnapshot(repo restic.Repository, snapshot SnapshotWithId, ownerIsRoot bool) (*dir, error) {
|
||||||
debug.Log("newDirFromSnapshot", "new dir for snapshot %v (%v)", snapshot.ID.Str(), snapshot.Tree.Str())
|
debug.Log("newDirFromSnapshot", "new dir for snapshot %v (%v)", snapshot.ID.Str(), snapshot.Tree.Str())
|
||||||
tree, err := repo.LoadTree(*snapshot.Tree)
|
tree, err := repo.LoadTree(*snapshot.Tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,7 +27,7 @@ var _ = fs.HandleReleaser(&file{})
|
|||||||
// for fuse operations.
|
// for fuse operations.
|
||||||
type BlobLoader interface {
|
type BlobLoader interface {
|
||||||
LookupBlobSize(restic.ID, restic.BlobType) (uint, error)
|
LookupBlobSize(restic.ID, restic.BlobType) (uint, error)
|
||||||
LoadBlob(restic.ID, restic.BlobType, []byte) ([]byte, error)
|
LoadDataBlob(restic.ID, []byte) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type file struct {
|
type file struct {
|
||||||
@ -109,12 +109,12 @@ func (f *file) getBlobAt(i int) (blob []byte, err error) {
|
|||||||
buf = make([]byte, f.sizes[i])
|
buf = make([]byte, f.sizes[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
blob, err = f.repo.LoadBlob(f.node.Content[i], restic.DataBlob, buf)
|
n, err := f.repo.LoadDataBlob(f.node.Content[i], buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("file.getBlobAt", "LoadBlob(%v, %v) failed: %v", f.node.Name, f.node.Content[i], err)
|
debug.Log("file.getBlobAt", "LoadBlob(%v, %v) failed: %v", f.node.Name, f.node.Content[i], err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.blobs[i] = blob
|
f.blobs[i] = buf[:n]
|
||||||
|
|
||||||
return blob, nil
|
return blob, nil
|
||||||
}
|
}
|
||||||
|
@ -34,19 +34,19 @@ func (m *MockRepo) LookupBlobSize(id restic.ID, t restic.BlobType) (uint, error)
|
|||||||
return uint(len(buf)), nil
|
return uint(len(buf)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockRepo) LoadBlob(id restic.ID, t restic.BlobType, buf []byte) ([]byte, error) {
|
func (m *MockRepo) LoadDataBlob(id restic.ID, buf []byte) (int, error) {
|
||||||
size, err := m.LookupBlobSize(id, t)
|
size, err := m.LookupBlobSize(id, restic.DataBlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if uint(cap(buf)) < size {
|
if uint(cap(buf)) < size {
|
||||||
return nil, errors.New("buffer too small")
|
return 0, errors.New("buffer too small")
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = buf[:size]
|
buf = buf[:size]
|
||||||
copy(buf, m.blobs[id])
|
copy(buf, m.blobs[id])
|
||||||
return buf, nil
|
return int(size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type MockContext struct{}
|
type MockContext struct{}
|
||||||
|
@ -5,7 +5,6 @@ package fuse
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"restic"
|
"restic"
|
||||||
"restic/repository"
|
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
"bazil.org/fuse/fs"
|
"bazil.org/fuse/fs"
|
||||||
@ -20,7 +19,7 @@ type link struct {
|
|||||||
ownerIsRoot bool
|
ownerIsRoot bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLink(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*link, error) {
|
func newLink(repo restic.Repository, node *restic.Node, ownerIsRoot bool) (*link, error) {
|
||||||
return &link{node: node, ownerIsRoot: ownerIsRoot}, nil
|
return &link{node: node, ownerIsRoot: ownerIsRoot}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/repository"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
@ -30,7 +29,7 @@ var _ = fs.HandleReadDirAller(&SnapshotsDir{})
|
|||||||
var _ = fs.NodeStringLookuper(&SnapshotsDir{})
|
var _ = fs.NodeStringLookuper(&SnapshotsDir{})
|
||||||
|
|
||||||
type SnapshotsDir struct {
|
type SnapshotsDir struct {
|
||||||
repo *repository.Repository
|
repo restic.Repository
|
||||||
ownerIsRoot bool
|
ownerIsRoot bool
|
||||||
|
|
||||||
// knownSnapshots maps snapshot timestamp to the snapshot
|
// knownSnapshots maps snapshot timestamp to the snapshot
|
||||||
@ -38,7 +37,7 @@ type SnapshotsDir struct {
|
|||||||
knownSnapshots map[string]SnapshotWithId
|
knownSnapshots map[string]SnapshotWithId
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSnapshotsDir(repo *repository.Repository, ownerIsRoot bool) *SnapshotsDir {
|
func NewSnapshotsDir(repo restic.Repository, ownerIsRoot bool) *SnapshotsDir {
|
||||||
debug.Log("NewSnapshotsDir", "fuse mount initiated")
|
debug.Log("NewSnapshotsDir", "fuse mount initiated")
|
||||||
return &SnapshotsDir{
|
return &SnapshotsDir{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
@ -15,7 +15,7 @@ var (
|
|||||||
depth = 3
|
depth = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
func createFilledRepo(t testing.TB, snapshots int, dup float32) (*repository.Repository, func()) {
|
func createFilledRepo(t testing.TB, snapshots int, dup float32) (restic.Repository, func()) {
|
||||||
repo, cleanup := repository.TestRepository(t)
|
repo, cleanup := repository.TestRepository(t)
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
@ -25,7 +25,7 @@ func createFilledRepo(t testing.TB, snapshots int, dup float32) (*repository.Rep
|
|||||||
return repo, cleanup
|
return repo, cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateIndex(t testing.TB, repo *repository.Repository, idx *Index) {
|
func validateIndex(t testing.TB, repo restic.Repository, idx *Index) {
|
||||||
for id := range repo.List(restic.DataFile, nil) {
|
for id := range repo.List(restic.DataFile, nil) {
|
||||||
if _, ok := idx.Packs[id]; !ok {
|
if _, ok := idx.Packs[id]; !ok {
|
||||||
t.Errorf("pack %v missing from index", id.Str())
|
t.Errorf("pack %v missing from index", id.Str())
|
||||||
@ -162,7 +162,7 @@ func TestIndexDuplicateBlobs(t *testing.T) {
|
|||||||
t.Logf("%d packs with duplicate blobs", len(packs))
|
t.Logf("%d packs with duplicate blobs", len(packs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadIndex(t testing.TB, repo *repository.Repository) *Index {
|
func loadIndex(t testing.TB, repo restic.Repository) *Index {
|
||||||
idx, err := Load(repo, nil)
|
idx, err := Load(repo, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Load() returned error %v", err)
|
t.Fatalf("Load() returned error %v", err)
|
||||||
|
@ -225,12 +225,13 @@ func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr, err := crypto.Decrypt(k, buf, buf)
|
n, err := crypto.Decrypt(k, buf, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
buf = buf[:n]
|
||||||
|
|
||||||
hdrRd := bytes.NewReader(hdr)
|
hdrRd := bytes.NewReader(buf)
|
||||||
|
|
||||||
pos := uint(0)
|
pos := uint(0)
|
||||||
for {
|
for {
|
||||||
|
@ -16,6 +16,7 @@ type Repository interface {
|
|||||||
Index() Index
|
Index() Index
|
||||||
SaveFullIndex() error
|
SaveFullIndex() error
|
||||||
SaveIndex() error
|
SaveIndex() error
|
||||||
|
LoadIndex() error
|
||||||
|
|
||||||
Config() Config
|
Config() Config
|
||||||
|
|
||||||
|
@ -85,10 +85,12 @@ func OpenKey(s *Repository, name string, password string) (*Key, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// decrypt master keys
|
// decrypt master keys
|
||||||
buf, err := crypto.Decrypt(k.user, []byte{}, k.Data)
|
buf := make([]byte, len(k.Data))
|
||||||
|
n, err := crypto.Decrypt(k.user, buf, k.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
buf = buf[:n]
|
||||||
|
|
||||||
// restore json
|
// restore json
|
||||||
k.master = &crypto.Key{}
|
k.master = &crypto.Key{}
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
// these packs. Each pack is loaded and the blobs listed in keepBlobs is saved
|
// these packs. Each pack is loaded and the blobs listed in keepBlobs is saved
|
||||||
// into a new pack. Afterwards, the packs are removed. This operation requires
|
// into a new pack. Afterwards, the packs are removed. This operation requires
|
||||||
// an exclusive lock on the repo.
|
// an exclusive lock on the repo.
|
||||||
func Repack(repo *Repository, packs restic.IDSet, keepBlobs restic.BlobSet) (err error) {
|
func Repack(repo restic.Repository, packs restic.IDSet, keepBlobs restic.BlobSet) (err error) {
|
||||||
debug.Log("Repack", "repacking %d packs while keeping %d blobs", len(packs), len(keepBlobs))
|
debug.Log("Repack", "repacking %d packs while keeping %d blobs", len(packs), len(keepBlobs))
|
||||||
|
|
||||||
buf := make([]byte, 0, maxPackSize)
|
buf := make([]byte, 0, maxPackSize)
|
||||||
@ -48,16 +48,21 @@ func Repack(repo *Repository, packs restic.IDSet, keepBlobs restic.BlobSet) (err
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphertext := buf[entry.Offset : entry.Offset+entry.Length]
|
debug.Log("Repack", " process blob %v", h)
|
||||||
|
|
||||||
if cap(plaintext) < len(ciphertext) {
|
ciphertext := buf[entry.Offset : entry.Offset+entry.Length]
|
||||||
|
plaintext = plaintext[:len(plaintext)]
|
||||||
|
if len(plaintext) < len(ciphertext) {
|
||||||
plaintext = make([]byte, len(ciphertext))
|
plaintext = make([]byte, len(ciphertext))
|
||||||
}
|
}
|
||||||
|
|
||||||
plaintext, err = crypto.Decrypt(repo.Key(), plaintext, ciphertext)
|
debug.Log("Repack", " ciphertext %d, plaintext %d", len(plaintext), len(ciphertext))
|
||||||
|
|
||||||
|
n, err := crypto.Decrypt(repo.Key(), plaintext, ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
plaintext = plaintext[:n]
|
||||||
|
|
||||||
_, err = repo.SaveAndEncrypt(entry.Type, plaintext, &entry.ID)
|
_, err = repo.SaveAndEncrypt(entry.Type, plaintext, &entry.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,7 +23,7 @@ func random(t testing.TB, length int) []byte {
|
|||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRandomBlobs(t testing.TB, repo *repository.Repository, blobs int, pData float32) {
|
func createRandomBlobs(t testing.TB, repo restic.Repository, blobs int, pData float32) {
|
||||||
for i := 0; i < blobs; i++ {
|
for i := 0; i < blobs; i++ {
|
||||||
var (
|
var (
|
||||||
tpe restic.BlobType
|
tpe restic.BlobType
|
||||||
@ -65,7 +65,7 @@ func createRandomBlobs(t testing.TB, repo *repository.Repository, blobs int, pDa
|
|||||||
|
|
||||||
// selectBlobs splits the list of all blobs randomly into two lists. A blob
|
// selectBlobs splits the list of all blobs randomly into two lists. A blob
|
||||||
// will be contained in the firstone ith probability p.
|
// will be contained in the firstone ith probability p.
|
||||||
func selectBlobs(t *testing.T, repo *repository.Repository, p float32) (list1, list2 restic.BlobSet) {
|
func selectBlobs(t *testing.T, repo restic.Repository, p float32) (list1, list2 restic.BlobSet) {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ func selectBlobs(t *testing.T, repo *repository.Repository, p float32) (list1, l
|
|||||||
return list1, list2
|
return list1, list2
|
||||||
}
|
}
|
||||||
|
|
||||||
func listPacks(t *testing.T, repo *repository.Repository) restic.IDSet {
|
func listPacks(t *testing.T, repo restic.Repository) restic.IDSet {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ func listPacks(t *testing.T, repo *repository.Repository) restic.IDSet {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func findPacksForBlobs(t *testing.T, repo *repository.Repository, blobs restic.BlobSet) restic.IDSet {
|
func findPacksForBlobs(t *testing.T, repo restic.Repository, blobs restic.BlobSet) restic.IDSet {
|
||||||
packs := restic.NewIDSet()
|
packs := restic.NewIDSet()
|
||||||
|
|
||||||
idx := repo.Index()
|
idx := repo.Index()
|
||||||
@ -130,26 +130,26 @@ func findPacksForBlobs(t *testing.T, repo *repository.Repository, blobs restic.B
|
|||||||
return packs
|
return packs
|
||||||
}
|
}
|
||||||
|
|
||||||
func repack(t *testing.T, repo *repository.Repository, packs restic.IDSet, blobs restic.BlobSet) {
|
func repack(t *testing.T, repo restic.Repository, packs restic.IDSet, blobs restic.BlobSet) {
|
||||||
err := repository.Repack(repo, packs, blobs)
|
err := repository.Repack(repo, packs, blobs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveIndex(t *testing.T, repo *repository.Repository) {
|
func saveIndex(t *testing.T, repo restic.Repository) {
|
||||||
if err := repo.SaveIndex(); err != nil {
|
if err := repo.SaveIndex(); err != nil {
|
||||||
t.Fatalf("repo.SaveIndex() %v", err)
|
t.Fatalf("repo.SaveIndex() %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rebuildIndex(t *testing.T, repo *repository.Repository) {
|
func rebuildIndex(t *testing.T, repo restic.Repository) {
|
||||||
if err := repository.RebuildIndex(repo); err != nil {
|
if err := repository.RebuildIndex(repo); err != nil {
|
||||||
t.Fatalf("error rebuilding index: %v", err)
|
t.Fatalf("error rebuilding index: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadIndex(t *testing.T, repo *repository.Repository) {
|
func reloadIndex(t *testing.T, repo restic.Repository) {
|
||||||
repo.SetIndex(repository.NewMasterIndex())
|
repo.SetIndex(repository.NewMasterIndex())
|
||||||
if err := repo.LoadIndex(); err != nil {
|
if err := repo.LoadIndex(); err != nil {
|
||||||
t.Fatalf("error loading new index: %v", err)
|
t.Fatalf("error loading new index: %v", err)
|
||||||
|
@ -72,20 +72,22 @@ func (r *Repository) LoadAndDecrypt(t restic.FileType, id restic.ID) ([]byte, er
|
|||||||
return nil, errors.New("invalid data returned")
|
return nil, errors.New("invalid data returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plain := make([]byte, len(buf))
|
||||||
|
|
||||||
// decrypt
|
// decrypt
|
||||||
plain, err := r.Decrypt(buf)
|
n, err := r.decryptTo(plain, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return plain, nil
|
return plain[:n], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadBlob tries to load and decrypt content identified by t and id from a
|
// LoadBlob tries to load and decrypt content identified by t and id from a
|
||||||
// pack from the backend, the result is stored in plaintextBuf, which must be
|
// pack from the backend, the result is stored in plaintextBuf, which must be
|
||||||
// large enough to hold the complete blob.
|
// large enough to hold the complete blob.
|
||||||
func (r *Repository) LoadBlob(id restic.ID, t restic.BlobType, plaintextBuf []byte) ([]byte, error) {
|
func (r *Repository) LoadBlob(id restic.ID, t restic.BlobType, plaintextBuf []byte) ([]byte, error) {
|
||||||
debug.Log("Repo.LoadBlob", "load %v with id %v", t, id.Str())
|
debug.Log("Repo.LoadBlob", "load %v with id %v (buf %d)", t, id.Str(), len(plaintextBuf))
|
||||||
|
|
||||||
// lookup plaintext size of blob
|
// lookup plaintext size of blob
|
||||||
size, err := r.idx.LookupSize(id, t)
|
size, err := r.idx.LookupSize(id, t)
|
||||||
@ -94,11 +96,8 @@ func (r *Repository) LoadBlob(id restic.ID, t restic.BlobType, plaintextBuf []by
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make sure the plaintext buffer is large enough, extend otherwise
|
// make sure the plaintext buffer is large enough, extend otherwise
|
||||||
plaintextBufSize := uint(cap(plaintextBuf))
|
if len(plaintextBuf) < int(size) {
|
||||||
if size > plaintextBufSize {
|
return nil, errors.Errorf("buffer is too small: %d < %d", len(plaintextBuf), size)
|
||||||
debug.Log("Repo.LoadBlob", "need to expand buffer: want %d bytes, got %d",
|
|
||||||
size, plaintextBufSize)
|
|
||||||
plaintextBuf = make([]byte, size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup packs
|
// lookup packs
|
||||||
@ -134,11 +133,12 @@ func (r *Repository) LoadBlob(id restic.ID, t restic.BlobType, plaintextBuf []by
|
|||||||
}
|
}
|
||||||
|
|
||||||
// decrypt
|
// decrypt
|
||||||
plaintextBuf, err = r.decryptTo(plaintextBuf, ciphertextBuf)
|
n, err = r.decryptTo(plaintextBuf, ciphertextBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lastError = errors.Errorf("decrypting blob %v failed: %v", id, err)
|
lastError = errors.Errorf("decrypting blob %v failed: %v", id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
plaintextBuf = plaintextBuf[:n]
|
||||||
|
|
||||||
// check hash
|
// check hash
|
||||||
if !restic.Hash(plaintextBuf).Equal(id) {
|
if !restic.Hash(plaintextBuf).Equal(id) {
|
||||||
@ -403,7 +403,7 @@ func (r *Repository) LoadIndex() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadIndex loads the index id from backend and returns it.
|
// LoadIndex loads the index id from backend and returns it.
|
||||||
func LoadIndex(repo *Repository, id restic.ID) (*Index, error) {
|
func LoadIndex(repo restic.Repository, id restic.ID) (*Index, error) {
|
||||||
idx, err := LoadIndexWithDecoder(repo, id, DecodeIndex)
|
idx, err := LoadIndexWithDecoder(repo, id, DecodeIndex)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return idx, nil
|
return idx, nil
|
||||||
@ -467,19 +467,14 @@ func (r *Repository) init(password string, cfg restic.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt authenticates and decrypts ciphertext and returns the plaintext.
|
|
||||||
func (r *Repository) Decrypt(ciphertext []byte) ([]byte, error) {
|
|
||||||
return r.decryptTo(nil, ciphertext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt authenticates and decrypts ciphertext and stores the result in
|
// decrypt authenticates and decrypts ciphertext and stores the result in
|
||||||
// plaintext.
|
// plaintext.
|
||||||
func (r *Repository) decryptTo(plaintext, ciphertext []byte) ([]byte, error) {
|
func (r *Repository) decryptTo(plaintext, ciphertext []byte) (int, error) {
|
||||||
if r.key == nil {
|
if r.key == nil {
|
||||||
return nil, errors.New("key for repository not set")
|
return 0, errors.New("key for repository not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
return crypto.Decrypt(r.key, nil, ciphertext)
|
return crypto.Decrypt(r.key, plaintext, ciphertext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt encrypts and authenticates the plaintext and saves the result in
|
// Encrypt encrypts and authenticates the plaintext and saves the result in
|
||||||
@ -502,15 +497,6 @@ func (r *Repository) KeyName() string {
|
|||||||
return r.keyName
|
return r.keyName
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count returns the number of blobs of a given type in the backend.
|
|
||||||
func (r *Repository) Count(t restic.FileType) (n uint) {
|
|
||||||
for _ = range r.be.List(t, nil) {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Repository) list(t restic.FileType, done <-chan struct{}, out chan<- restic.ID) {
|
func (r *Repository) list(t restic.FileType, done <-chan struct{}, out chan<- restic.ID) {
|
||||||
defer close(out)
|
defer close(out)
|
||||||
in := r.be.List(t, done)
|
in := r.be.List(t, done)
|
||||||
@ -592,14 +578,17 @@ func (r *Repository) Close() error {
|
|||||||
|
|
||||||
// LoadTree loads a tree from the repository.
|
// LoadTree loads a tree from the repository.
|
||||||
func (r *Repository) LoadTree(id restic.ID) (*restic.Tree, error) {
|
func (r *Repository) LoadTree(id restic.ID) (*restic.Tree, error) {
|
||||||
|
debug.Log("repo.LoadTree", "load tree %v", id.Str())
|
||||||
|
|
||||||
size, err := r.idx.LookupSize(id, restic.TreeBlob)
|
size, err := r.idx.LookupSize(id, restic.TreeBlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug.Log("repo.LoadTree", "size is %d, create buffer", size)
|
||||||
buf := make([]byte, size)
|
buf := make([]byte, size)
|
||||||
|
|
||||||
buf, err = r.LoadBlob(id, restic.TreeBlob, nil)
|
buf, err = r.LoadBlob(id, restic.TreeBlob, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -615,6 +604,7 @@ func (r *Repository) LoadTree(id restic.ID) (*restic.Tree, error) {
|
|||||||
|
|
||||||
// LoadDataBlob loads a data blob from the repository to the buffer.
|
// LoadDataBlob loads a data blob from the repository to the buffer.
|
||||||
func (r *Repository) LoadDataBlob(id restic.ID, buf []byte) (int, error) {
|
func (r *Repository) LoadDataBlob(id restic.ID, buf []byte) (int, error) {
|
||||||
|
debug.Log("repo.LoadDataBlob", "load blob %v into buf %p", id.Str(), buf)
|
||||||
size, err := r.idx.LookupSize(id, restic.DataBlob)
|
size, err := r.idx.LookupSize(id, restic.DataBlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -629,5 +619,7 @@ func (r *Repository) LoadDataBlob(id restic.ID, buf []byte) (int, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug.Log("repo.LoadDataBlob", "loaded %d bytes into buf %p", len(buf), buf)
|
||||||
|
|
||||||
return len(buf), err
|
return len(buf), err
|
||||||
}
|
}
|
||||||
|
@ -90,8 +90,10 @@ func TestSave(t *testing.T) {
|
|||||||
// OK(t, repo.SaveIndex())
|
// OK(t, repo.SaveIndex())
|
||||||
|
|
||||||
// read back
|
// read back
|
||||||
buf, err := repo.LoadBlob(id, restic.DataBlob, make([]byte, size))
|
buf := make([]byte, size)
|
||||||
|
n, err := repo.LoadDataBlob(id, buf)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
Equals(t, len(buf), n)
|
||||||
|
|
||||||
Assert(t, len(buf) == len(data),
|
Assert(t, len(buf) == len(data),
|
||||||
"number of bytes read back does not match: expected %d, got %d",
|
"number of bytes read back does not match: expected %d, got %d",
|
||||||
@ -122,8 +124,10 @@ func TestSaveFrom(t *testing.T) {
|
|||||||
OK(t, repo.Flush())
|
OK(t, repo.Flush())
|
||||||
|
|
||||||
// read back
|
// read back
|
||||||
buf, err := repo.LoadBlob(id, restic.DataBlob, make([]byte, size))
|
buf := make([]byte, size)
|
||||||
|
n, err := repo.LoadDataBlob(id, buf)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
Equals(t, len(buf), n)
|
||||||
|
|
||||||
Assert(t, len(buf) == len(data),
|
Assert(t, len(buf) == len(data),
|
||||||
"number of bytes read back does not match: expected %d, got %d",
|
"number of bytes read back does not match: expected %d, got %d",
|
||||||
@ -157,7 +161,7 @@ func BenchmarkSaveAndEncrypt(t *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadJSONPack(t *testing.T) {
|
func TestLoadTree(t *testing.T) {
|
||||||
repo := SetupRepo()
|
repo := SetupRepo()
|
||||||
defer TeardownRepo(repo)
|
defer TeardownRepo(repo)
|
||||||
|
|
||||||
@ -169,12 +173,11 @@ func TestLoadJSONPack(t *testing.T) {
|
|||||||
sn := SnapshotDir(t, repo, BenchArchiveDirectory, nil)
|
sn := SnapshotDir(t, repo, BenchArchiveDirectory, nil)
|
||||||
OK(t, repo.Flush())
|
OK(t, repo.Flush())
|
||||||
|
|
||||||
tree := restic.NewTree()
|
_, err := repo.LoadTree(*sn.Tree)
|
||||||
err := repo.LoadJSONPack(restic.TreeBlob, *sn.Tree, &tree)
|
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkLoadJSONPack(t *testing.B) {
|
func BenchmarkLoadTree(t *testing.B) {
|
||||||
repo := SetupRepo()
|
repo := SetupRepo()
|
||||||
defer TeardownRepo(repo)
|
defer TeardownRepo(repo)
|
||||||
|
|
||||||
@ -186,12 +189,10 @@ func BenchmarkLoadJSONPack(t *testing.B) {
|
|||||||
sn := SnapshotDir(t, repo, BenchArchiveDirectory, nil)
|
sn := SnapshotDir(t, repo, BenchArchiveDirectory, nil)
|
||||||
OK(t, repo.Flush())
|
OK(t, repo.Flush())
|
||||||
|
|
||||||
tree := restic.NewTree()
|
|
||||||
|
|
||||||
t.ResetTimer()
|
t.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
err := repo.LoadJSONPack(restic.TreeBlob, *sn.Tree, &tree)
|
_, err := repo.LoadTree(*sn.Tree)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +245,7 @@ func BenchmarkLoadIndex(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// saveRandomDataBlobs generates random data blobs and saves them to the repository.
|
// saveRandomDataBlobs generates random data blobs and saves them to the repository.
|
||||||
func saveRandomDataBlobs(t testing.TB, repo *repository.Repository, num int, sizeMax int) {
|
func saveRandomDataBlobs(t testing.TB, repo restic.Repository, num int, sizeMax int) {
|
||||||
for i := 0; i < num; i++ {
|
for i := 0; i < num; i++ {
|
||||||
size := mrand.Int() % sizeMax
|
size := mrand.Int() % sizeMax
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ func getBoolVar(name string, defaultValue bool) bool {
|
|||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupRepo() *repository.Repository {
|
func SetupRepo() restic.Repository {
|
||||||
tempdir, err := ioutil.TempDir(TestTempDir, "restic-test-")
|
tempdir, err := ioutil.TempDir(TestTempDir, "restic-test-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -70,27 +70,29 @@ func SetupRepo() *repository.Repository {
|
|||||||
return repo
|
return repo
|
||||||
}
|
}
|
||||||
|
|
||||||
func TeardownRepo(repo *repository.Repository) {
|
func TeardownRepo(repo restic.Repository) {
|
||||||
if !TestCleanupTempDirs {
|
if !TestCleanupTempDirs {
|
||||||
l := repo.Backend().(*local.Local)
|
l := repo.Backend().(*local.Local)
|
||||||
fmt.Printf("leaving local backend at %s\n", l.Location())
|
fmt.Printf("leaving local backend at %s\n", l.Location())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := repo.Delete()
|
if r, ok := repo.(restic.Deleter); ok {
|
||||||
if err != nil {
|
err := r.Delete()
|
||||||
panic(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SnapshotDir(t testing.TB, repo *repository.Repository, path string, parent *restic.ID) *restic.Snapshot {
|
func SnapshotDir(t testing.TB, repo restic.Repository, path string, parent *restic.ID) *restic.Snapshot {
|
||||||
arch := archiver.New(repo)
|
arch := archiver.New(repo)
|
||||||
sn, _, err := arch.Snapshot(nil, []string{path}, parent)
|
sn, _, err := arch.Snapshot(nil, []string{path}, parent)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
return sn
|
return sn
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithRepo(t testing.TB, f func(*repository.Repository)) {
|
func WithRepo(t testing.TB, f func(restic.Repository)) {
|
||||||
repo := SetupRepo()
|
repo := SetupRepo()
|
||||||
f(repo)
|
f(repo)
|
||||||
TeardownRepo(repo)
|
TeardownRepo(repo)
|
||||||
|
@ -34,7 +34,7 @@ func Assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
|
|||||||
func OK(tb testing.TB, err error) {
|
func OK(tb testing.TB, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, file, line, _ := runtime.Caller(1)
|
_, file, line, _ := runtime.Caller(1)
|
||||||
fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
|
fmt.Printf("\033[31m%s:%d: unexpected error: %+v\033[39m\n\n", filepath.Base(file), line, err)
|
||||||
tb.FailNow()
|
tb.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ func WithTestEnvironment(t testing.TB, repoFixture string, f func(repodir string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OpenLocalRepo opens the local repository located at dir.
|
// OpenLocalRepo opens the local repository located at dir.
|
||||||
func OpenLocalRepo(t testing.TB, dir string) *repository.Repository {
|
func OpenLocalRepo(t testing.TB, dir string) restic.Repository {
|
||||||
be, err := local.Open(dir)
|
be, err := local.Open(dir)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
|
@ -8,8 +8,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/restic/chunker"
|
|
||||||
"restic/errors"
|
"restic/errors"
|
||||||
|
|
||||||
|
"github.com/restic/chunker"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeFile returns a reader which yields deterministic pseudo-random data.
|
// fakeFile returns a reader which yields deterministic pseudo-random data.
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"restic"
|
"restic"
|
||||||
"restic/archiver"
|
"restic/archiver"
|
||||||
"restic/pipe"
|
"restic/pipe"
|
||||||
"restic/repository"
|
|
||||||
. "restic/test"
|
. "restic/test"
|
||||||
"restic/walk"
|
"restic/walk"
|
||||||
)
|
)
|
||||||
@ -91,7 +90,7 @@ func TestWalkTree(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type delayRepo struct {
|
type delayRepo struct {
|
||||||
repo *repository.Repository
|
repo restic.Repository
|
||||||
delay time.Duration
|
delay time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user