Move Server and Key to new sub-package

This commit is contained in:
Alexander Neumann 2015-04-26 14:46:15 +02:00
parent 8498753eb7
commit d19b23d4f1
28 changed files with 317 additions and 356 deletions

View File

@ -15,6 +15,7 @@ import (
"github.com/restic/restic/chunker" "github.com/restic/restic/chunker"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/pipe" "github.com/restic/restic/pipe"
"github.com/restic/restic/server"
) )
const ( const (
@ -27,7 +28,7 @@ const (
) )
type Archiver struct { type Archiver struct {
s Server s *server.Server
m *Map m *Map
c *Cache c *Cache
@ -37,7 +38,7 @@ type Archiver struct {
Filter func(item string, fi os.FileInfo) bool Filter func(item string, fi os.FileInfo) bool
} }
func NewArchiver(s Server) (*Archiver, error) { func NewArchiver(s *server.Server) (*Archiver, error) {
var err error var err error
arch := &Archiver{ arch := &Archiver{
s: s, s: s,
@ -101,7 +102,7 @@ func (arch *Archiver) Preload() error {
return nil return nil
} }
func (arch *Archiver) Save(t backend.Type, id backend.ID, length uint, rd io.Reader) (Blob, error) { func (arch *Archiver) Save(t backend.Type, id backend.ID, length uint, rd io.Reader) (server.Blob, error) {
debug.Log("Archiver.Save", "Save(%v, %v)\n", t, id.Str()) debug.Log("Archiver.Save", "Save(%v, %v)\n", t, id.Str())
// test if this blob is already known // test if this blob is already known
@ -127,7 +128,7 @@ func (arch *Archiver) Save(t backend.Type, id backend.ID, length uint, rd io.Rea
// TODO: implement a list of blobs in transport, so this doesn't happen so often // TODO: implement a list of blobs in transport, so this doesn't happen so often
err = arch.s.Remove(t, blob.Storage.String()) err = arch.s.Remove(t, blob.Storage.String())
if err != nil { if err != nil {
return Blob{}, err return server.Blob{}, err
} }
} }
@ -136,13 +137,13 @@ func (arch *Archiver) Save(t backend.Type, id backend.ID, length uint, rd io.Rea
return smapblob, nil return smapblob, nil
} }
func (arch *Archiver) SaveTreeJSON(item interface{}) (Blob, error) { func (arch *Archiver) SaveTreeJSON(item interface{}) (server.Blob, error) {
// convert to json // convert to json
data, err := json.Marshal(item) data, err := json.Marshal(item)
// append newline // append newline
data = append(data, '\n') data = append(data, '\n')
if err != nil { if err != nil {
return Blob{}, err return server.Blob{}, err
} }
// check if tree has been saved before // check if tree has been saved before
@ -157,7 +158,7 @@ func (arch *Archiver) SaveTreeJSON(item interface{}) (Blob, error) {
// otherwise save the data // otherwise save the data
blob, err = arch.s.SaveJSON(backend.Tree, item) blob, err = arch.s.SaveJSON(backend.Tree, item)
if err != nil { if err != nil {
return Blob{}, err return server.Blob{}, err
} }
// store blob in storage map // store blob in storage map
@ -168,7 +169,7 @@ func (arch *Archiver) SaveTreeJSON(item interface{}) (Blob, error) {
// SaveFile stores the content of the file on the backend as a Blob by calling // SaveFile stores the content of the file on the backend as a Blob by calling
// Save for each chunk. // Save for each chunk.
func (arch *Archiver) SaveFile(p *Progress, node *Node) (Blobs, error) { func (arch *Archiver) SaveFile(p *Progress, node *Node) (server.Blobs, error) {
file, err := node.OpenForReading() file, err := node.OpenForReading()
defer file.Close() defer file.Close()
if err != nil { if err != nil {
@ -197,12 +198,12 @@ func (arch *Archiver) SaveFile(p *Progress, node *Node) (Blobs, error) {
} }
} }
var blobs Blobs var blobs server.Blobs
// store all chunks // store all chunks
chnker := GetChunker("archiver.SaveFile") chnker := GetChunker("archiver.SaveFile")
chnker.Reset(file, arch.s.ChunkerPolynomial()) chnker.Reset(file, arch.s.ChunkerPolynomial())
chans := [](<-chan Blob){} chans := [](<-chan server.Blob){}
defer FreeChunker("archiver.SaveFile", chnker) defer FreeChunker("archiver.SaveFile", chnker)
chunks := 0 chunks := 0
@ -221,9 +222,9 @@ func (arch *Archiver) SaveFile(p *Progress, node *Node) (Blobs, error) {
// acquire token, start goroutine to save chunk // acquire token, start goroutine to save chunk
token := <-arch.blobToken token := <-arch.blobToken
resCh := make(chan Blob, 1) resCh := make(chan server.Blob, 1)
go func(ch chan<- Blob) { go func(ch chan<- server.Blob) {
blob, err := arch.Save(backend.Data, chunk.Digest, chunk.Length, chunk.Reader(file)) blob, err := arch.Save(backend.Data, chunk.Digest, chunk.Length, chunk.Reader(file))
// TODO handle error // TODO handle error
if err != nil { if err != nil {
@ -238,7 +239,7 @@ func (arch *Archiver) SaveFile(p *Progress, node *Node) (Blobs, error) {
chans = append(chans, resCh) chans = append(chans, resCh)
} }
blobs = []Blob{} blobs = []server.Blob{}
for _, ch := range chans { for _, ch := range chans {
blobs = append(blobs, <-ch) blobs = append(blobs, <-ch)
} }
@ -267,7 +268,7 @@ func (arch *Archiver) SaveFile(p *Progress, node *Node) (Blobs, error) {
return blobs, nil return blobs, nil
} }
func (arch *Archiver) saveTree(p *Progress, t *Tree) (Blob, error) { func (arch *Archiver) saveTree(p *Progress, t *Tree) (server.Blob, error) {
debug.Log("Archiver.saveTree", "saveTree(%v)\n", t) debug.Log("Archiver.saveTree", "saveTree(%v)\n", t)
var wg sync.WaitGroup var wg sync.WaitGroup
@ -279,7 +280,7 @@ func (arch *Archiver) saveTree(p *Progress, t *Tree) (Blob, error) {
if node.tree != nil { if node.tree != nil {
b, err := arch.saveTree(p, node.tree) b, err := arch.saveTree(p, node.tree)
if err != nil { if err != nil {
return Blob{}, err return server.Blob{}, err
} }
node.Subtree = b.ID node.Subtree = b.ID
t.Map.Insert(b) t.Map.Insert(b)
@ -321,7 +322,7 @@ func (arch *Archiver) saveTree(p *Progress, t *Tree) (Blob, error) {
go func(n *Node) { go func(n *Node) {
defer wg.Done() defer wg.Done()
var blobs Blobs var blobs server.Blobs
blobs, n.err = arch.SaveFile(p, n) blobs, n.err = arch.SaveFile(p, n)
for _, b := range blobs { for _, b := range blobs {
t.Map.Insert(b) t.Map.Insert(b)
@ -340,7 +341,7 @@ func (arch *Archiver) saveTree(p *Progress, t *Tree) (Blob, error) {
// check for invalid file nodes // check for invalid file nodes
for _, node := range t.Nodes { for _, node := range t.Nodes {
if node.Type == "file" && node.Content == nil && node.err == nil { if node.Type == "file" && node.Content == nil && node.err == nil {
return Blob{}, fmt.Errorf("node %v has empty content", node.Name) return server.Blob{}, fmt.Errorf("node %v has empty content", node.Name)
} }
// remember used hashes // remember used hashes
@ -357,7 +358,7 @@ func (arch *Archiver) saveTree(p *Progress, t *Tree) (Blob, error) {
if node.err != nil { if node.err != nil {
err := arch.Error(node.path, nil, node.err) err := arch.Error(node.path, nil, node.err)
if err != nil { if err != nil {
return Blob{}, err return server.Blob{}, err
} }
// save error message in node // save error message in node
@ -375,7 +376,7 @@ func (arch *Archiver) saveTree(p *Progress, t *Tree) (Blob, error) {
blob, err := arch.SaveTreeJSON(t) blob, err := arch.SaveTreeJSON(t)
if err != nil { if err != nil {
return Blob{}, err return server.Blob{}, err
} }
return blob, nil return blob, nil
@ -531,7 +532,7 @@ func (arch *Archiver) dirWorker(wg *sync.WaitGroup, p *Progress, done <-chan str
debug.Log("Archiver.dirWorker", "save tree for %s: %v", dir.Path(), blob) debug.Log("Archiver.dirWorker", "save tree for %s: %v", dir.Path(), blob)
node.Subtree = blob.ID node.Subtree = blob.ID
node.blobs = Blobs{blob} node.blobs = server.Blobs{blob}
dir.Result() <- node dir.Result() <- node
if dir.Path() != "" { if dir.Path() != "" {

View File

@ -9,6 +9,7 @@ import (
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/chunker" "github.com/restic/restic/chunker"
"github.com/restic/restic/server"
. "github.com/restic/restic/test" . "github.com/restic/restic/test"
) )
@ -22,7 +23,7 @@ type Rdr interface {
io.ReaderAt io.ReaderAt
} }
func benchmarkChunkEncrypt(b testing.TB, buf, buf2 []byte, rd Rdr, key *restic.Key) { func benchmarkChunkEncrypt(b testing.TB, buf, buf2 []byte, rd Rdr, key *server.Key) {
ch := restic.GetChunker("BenchmarkChunkEncrypt") ch := restic.GetChunker("BenchmarkChunkEncrypt")
rd.Seek(0, 0) rd.Seek(0, 0)
ch.Reset(rd, testPol) ch.Reset(rd, testPol)
@ -53,9 +54,9 @@ func BenchmarkChunkEncrypt(b *testing.B) {
data := Random(23, 10<<20) // 10MiB data := Random(23, 10<<20) // 10MiB
rd := bytes.NewReader(data) rd := bytes.NewReader(data)
be := setupBackend(b) be := SetupBackend(b)
defer teardownBackend(b, be) defer TeardownBackend(b, be)
key := setupKey(b, be, "geheim") key := SetupKey(b, be, "geheim")
buf := restic.GetChunkBuf("BenchmarkChunkEncrypt") buf := restic.GetChunkBuf("BenchmarkChunkEncrypt")
buf2 := restic.GetChunkBuf("BenchmarkChunkEncrypt") buf2 := restic.GetChunkBuf("BenchmarkChunkEncrypt")
@ -71,7 +72,7 @@ func BenchmarkChunkEncrypt(b *testing.B) {
restic.FreeChunkBuf("BenchmarkChunkEncrypt", buf2) restic.FreeChunkBuf("BenchmarkChunkEncrypt", buf2)
} }
func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *restic.Key) { func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *server.Key) {
ch := restic.GetChunker("BenchmarkChunkEncryptP") ch := restic.GetChunker("BenchmarkChunkEncryptP")
rd.Seek(0, 0) rd.Seek(0, 0)
ch.Reset(rd, testPol) ch.Reset(rd, testPol)
@ -92,9 +93,9 @@ func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *restic.Key)
} }
func BenchmarkChunkEncryptParallel(b *testing.B) { func BenchmarkChunkEncryptParallel(b *testing.B) {
be := setupBackend(b) be := SetupBackend(b)
defer teardownBackend(b, be) defer TeardownBackend(b, be)
key := setupKey(b, be, "geheim") key := SetupKey(b, be, "geheim")
data := Random(23, 10<<20) // 10MiB data := Random(23, 10<<20) // 10MiB
@ -118,9 +119,9 @@ func BenchmarkArchiveDirectory(b *testing.B) {
b.Skip("benchdir not set, skipping BenchmarkArchiveDirectory") b.Skip("benchdir not set, skipping BenchmarkArchiveDirectory")
} }
server := setupBackend(b) server := SetupBackend(b)
defer teardownBackend(b, server) defer TeardownBackend(b, server)
key := setupKey(b, server, "geheim") key := SetupKey(b, server, "geheim")
server.SetKey(key) server.SetKey(key)
arch, err := restic.NewArchiver(server) arch, err := restic.NewArchiver(server)
@ -131,16 +132,7 @@ func BenchmarkArchiveDirectory(b *testing.B) {
b.Logf("snapshot archived as %v", id) b.Logf("snapshot archived as %v", id)
} }
func snapshot(t testing.TB, server restic.Server, path string, parent backend.ID) *restic.Snapshot { func countBlobs(t testing.TB, server *server.Server) (trees int, data int) {
arch, err := restic.NewArchiver(server)
OK(t, err)
OK(t, arch.Preload())
sn, _, err := arch.Snapshot(nil, []string{path}, parent)
OK(t, err)
return sn
}
func countBlobs(t testing.TB, server restic.Server) (trees int, data int) {
return server.Count(backend.Tree), server.Count(backend.Data) return server.Count(backend.Tree), server.Count(backend.Data)
} }
@ -149,13 +141,13 @@ func archiveWithPreload(t testing.TB) {
t.Skip("benchdir not set, skipping TestArchiverPreload") t.Skip("benchdir not set, skipping TestArchiverPreload")
} }
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
// archive a few files // archive a few files
sn := snapshot(t, server, *benchArchiveDirectory, nil) sn := SnapshotDir(t, server, *benchArchiveDirectory, nil)
t.Logf("archived snapshot %v", sn.ID().Str()) t.Logf("archived snapshot %v", sn.ID().Str())
// get archive stats // get archive stats
@ -163,7 +155,7 @@ func archiveWithPreload(t testing.TB) {
t.Logf("found %v trees, %v data blobs", beforeTrees, beforeData) t.Logf("found %v trees, %v data blobs", beforeTrees, beforeData)
// archive the same files again, without parent snapshot // archive the same files again, without parent snapshot
sn2 := snapshot(t, server, *benchArchiveDirectory, nil) sn2 := SnapshotDir(t, server, *benchArchiveDirectory, nil)
t.Logf("archived snapshot %v", sn2.ID().Str()) t.Logf("archived snapshot %v", sn2.ID().Str())
// get archive stats // get archive stats
@ -177,7 +169,7 @@ func archiveWithPreload(t testing.TB) {
} }
// archive the same files again, with a parent snapshot // archive the same files again, with a parent snapshot
sn3 := snapshot(t, server, *benchArchiveDirectory, sn2.ID()) sn3 := SnapshotDir(t, server, *benchArchiveDirectory, sn2.ID())
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 // get archive stats
@ -200,9 +192,9 @@ func BenchmarkPreload(t *testing.B) {
t.Skip("benchdir not set, skipping TestArchiverPreload") t.Skip("benchdir not set, skipping TestArchiverPreload")
} }
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
// archive a few files // archive a few files
@ -228,13 +220,13 @@ func BenchmarkLoadTree(t *testing.B) {
t.Skip("benchdir not set, skipping TestArchiverPreload") t.Skip("benchdir not set, skipping TestArchiverPreload")
} }
server := setupBackend(t) s := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, s)
key := setupKey(t, server, "geheim") key := SetupKey(t, s, "geheim")
server.SetKey(key) s.SetKey(key)
// archive a few files // archive a few files
arch, err := restic.NewArchiver(server) arch, err := restic.NewArchiver(s)
OK(t, err) OK(t, err)
sn, _, err := arch.Snapshot(nil, []string{*benchArchiveDirectory}, nil) sn, _, err := arch.Snapshot(nil, []string{*benchArchiveDirectory}, nil)
OK(t, err) OK(t, err)
@ -243,7 +235,7 @@ func BenchmarkLoadTree(t *testing.B) {
list := make([]backend.ID, 0, 10) list := make([]backend.ID, 0, 10)
done := make(chan struct{}) done := make(chan struct{})
for name := range server.List(backend.Tree, done) { for name := range s.List(backend.Tree, done) {
id, err := backend.ParseID(name) id, err := backend.ParseID(name)
if err != nil { if err != nil {
t.Logf("invalid id for tree %v", name) t.Logf("invalid id for tree %v", name)
@ -262,7 +254,7 @@ func BenchmarkLoadTree(t *testing.B) {
for i := 0; i < t.N; i++ { for i := 0; i < t.N; i++ {
for _, id := range list { for _, id := range list {
_, err := restic.LoadTree(server, restic.Blob{Storage: id}) _, err := restic.LoadTree(s, server.Blob{Storage: id})
OK(t, err) OK(t, err)
} }
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/server"
) )
type Cache struct { type Cache struct {
@ -106,7 +107,7 @@ func (c *Cache) Purge(t backend.Type, subtype string, id backend.ID) error {
return err return err
} }
func (c *Cache) Clear(s Server) error { func (c *Cache) Clear(s *server.Server) error {
list, err := c.List(backend.Snapshot) list, err := c.List(backend.Snapshot)
if err != nil { if err != nil {
return err return err
@ -211,7 +212,7 @@ func (c *Cache) filename(t backend.Type, subtype string, id backend.ID) (string,
// RefreshSnapshots loads the maps for all snapshots and saves them to the // RefreshSnapshots loads the maps for all snapshots and saves them to the
// local cache. Old cache entries are purged. // local cache. Old cache entries are purged.
func (c *Cache) RefreshSnapshots(s Server, p *Progress) error { func (c *Cache) RefreshSnapshots(s *server.Server, p *Progress) error {
defer p.Done() defer p.Done()
// list cache entries // list cache entries
@ -274,7 +275,7 @@ func (c *Cache) RefreshSnapshots(s Server, p *Progress) error {
// cacheSnapshotBlobs creates a cache of all the blobs used within the // cacheSnapshotBlobs creates a cache of all the blobs used within the
// snapshot. It collects all blobs from all trees and saves the resulting map // snapshot. It collects all blobs from all trees and saves the resulting map
// to the cache and returns the map. // to the cache and returns the map.
func cacheSnapshotBlobs(p *Progress, s Server, c *Cache, id backend.ID) (*Map, error) { func cacheSnapshotBlobs(p *Progress, s *server.Server, c *Cache, id backend.ID) (*Map, error) {
debug.Log("CacheSnapshotBlobs", "create cache for snapshot %v", id.Str()) debug.Log("CacheSnapshotBlobs", "create cache for snapshot %v", id.Str())
sn, err := LoadSnapshot(s, id) sn, err := LoadSnapshot(s, id)
@ -338,7 +339,7 @@ func (c *Cache) StoreMap(snid backend.ID, m *Map) error {
return nil return nil
} }
func (c *Cache) LoadMap(s Server, snid backend.ID) (*Map, error) { func (c *Cache) LoadMap(s *server.Server, snid backend.ID) (*Map, error) {
rd, err := c.Load(backend.Snapshot, "blobs", snid) rd, err := c.Load(backend.Snapshot, "blobs", snid)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -10,9 +10,9 @@ import (
) )
func TestCache(t *testing.T) { func TestCache(t *testing.T) {
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
cache, err := restic.NewCache(server) cache, err := restic.NewCache(server)

View File

@ -8,6 +8,7 @@ import (
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/server"
) )
type CmdCat struct{} type CmdCat struct{}
@ -112,7 +113,7 @@ func (cmd CmdCat) Execute(args []string) error {
dec := json.NewDecoder(rd) dec := json.NewDecoder(rd)
var key restic.Key var key server.Key
err = dec.Decode(&key) err = dec.Decode(&key)
if err != nil { if err != nil {
return err return err

View File

@ -8,6 +8,7 @@ import (
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/server"
) )
type findResult struct { type findResult struct {
@ -58,7 +59,7 @@ func parseTime(str string) (time.Time, error) {
return time.Time{}, fmt.Errorf("unable to parse time: %q", str) return time.Time{}, fmt.Errorf("unable to parse time: %q", str)
} }
func (c CmdFind) findInTree(s restic.Server, blob restic.Blob, path string) ([]findResult, error) { func (c CmdFind) findInTree(s *server.Server, blob server.Blob, path string) ([]findResult, error) {
debug.Log("restic.find", "checking tree %v\n", blob) debug.Log("restic.find", "checking tree %v\n", blob)
tree, err := restic.LoadTree(s, blob) tree, err := restic.LoadTree(s, blob)
if err != nil { if err != nil {
@ -109,7 +110,7 @@ func (c CmdFind) findInTree(s restic.Server, blob restic.Blob, path string) ([]f
return results, nil return results, nil
} }
func (c CmdFind) findInSnapshot(s restic.Server, name string) error { func (c CmdFind) findInSnapshot(s *server.Server, name string) error {
debug.Log("restic.find", "searching in snapshot %s\n for entries within [%s %s]", name, c.oldest, c.newest) debug.Log("restic.find", "searching in snapshot %s\n for entries within [%s %s]", name, c.oldest, c.newest)
id, err := backend.ParseID(name) id, err := backend.ParseID(name)

View File

@ -8,6 +8,7 @@ import (
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/server"
) )
type CmdFsck struct { type CmdFsck struct {
@ -31,7 +32,7 @@ func init() {
} }
} }
func fsckFile(opts CmdFsck, s restic.Server, m *restic.Map, IDs []backend.ID) (uint64, error) { func fsckFile(opts CmdFsck, s *server.Server, m *restic.Map, IDs []backend.ID) (uint64, error) {
debug.Log("restic.fsckFile", "checking file %v", IDs) debug.Log("restic.fsckFile", "checking file %v", IDs)
var bytes uint64 var bytes uint64
@ -74,7 +75,7 @@ func fsckFile(opts CmdFsck, s restic.Server, m *restic.Map, IDs []backend.ID) (u
return bytes, nil return bytes, nil
} }
func fsckTree(opts CmdFsck, s restic.Server, blob restic.Blob) error { func fsckTree(opts CmdFsck, s *server.Server, blob server.Blob) error {
debug.Log("restic.fsckTree", "checking tree %v", blob) debug.Log("restic.fsckTree", "checking tree %v", blob)
tree, err := restic.LoadTree(s, blob) tree, err := restic.LoadTree(s, blob)
@ -161,7 +162,7 @@ func fsckTree(opts CmdFsck, s restic.Server, blob restic.Blob) error {
return firstErr return firstErr
} }
func fsckSnapshot(opts CmdFsck, s restic.Server, id backend.ID) error { func fsckSnapshot(opts CmdFsck, s *server.Server, id backend.ID) error {
debug.Log("restic.fsck", "checking snapshot %v\n", id) debug.Log("restic.fsck", "checking snapshot %v\n", id)
sn, err := restic.LoadSnapshot(s, id) sn, err := restic.LoadSnapshot(s, id)

View File

@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/server"
) )
type CmdKey struct{} type CmdKey struct{}
@ -21,7 +21,7 @@ func init() {
} }
} }
func listKeys(s restic.Server) error { func listKeys(s *server.Server) error {
tab := NewTable() tab := NewTable()
tab.Header = fmt.Sprintf(" %-10s %-10s %-10s %s", "ID", "User", "Host", "Created") tab.Header = fmt.Sprintf(" %-10s %-10s %-10s %s", "ID", "User", "Host", "Created")
tab.RowFormat = "%s%-10s %-10s %-10s %s" tab.RowFormat = "%s%-10s %-10s %-10s %s"
@ -35,7 +35,7 @@ func listKeys(s restic.Server) error {
defer close(done) defer close(done)
for name := range s.List(backend.Key, done) { for name := range s.List(backend.Key, done) {
k, err := restic.LoadKey(s, name) k, err := server.LoadKey(s, name)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "LoadKey() failed: %v\n", err) fmt.Fprintf(os.Stderr, "LoadKey() failed: %v\n", err)
continue continue
@ -56,7 +56,7 @@ func listKeys(s restic.Server) error {
return nil return nil
} }
func addKey(s restic.Server) error { func addKey(s *server.Server) error {
pw := readPassword("RESTIC_NEWPASSWORD", "enter password for new key: ") pw := readPassword("RESTIC_NEWPASSWORD", "enter password for new key: ")
pw2 := readPassword("RESTIC_NEWPASSWORD", "enter password again: ") pw2 := readPassword("RESTIC_NEWPASSWORD", "enter password again: ")
@ -64,7 +64,7 @@ func addKey(s restic.Server) error {
return errors.New("passwords do not match") return errors.New("passwords do not match")
} }
id, err := restic.AddKey(s, pw, s.Key()) id, err := server.AddKey(s, pw, s.Key())
if err != nil { if err != nil {
return fmt.Errorf("creating new key failed: %v\n", err) return fmt.Errorf("creating new key failed: %v\n", err)
} }
@ -74,7 +74,7 @@ func addKey(s restic.Server) error {
return nil return nil
} }
func deleteKey(s restic.Server, name string) error { func deleteKey(s *server.Server, name string) error {
if name == s.Key().Name() { if name == s.Key().Name() {
return errors.New("refusing to remove key currently used to access repository") return errors.New("refusing to remove key currently used to access repository")
} }
@ -88,7 +88,7 @@ func deleteKey(s restic.Server, name string) error {
return nil return nil
} }
func changePassword(s restic.Server) error { func changePassword(s *server.Server) error {
pw := readPassword("RESTIC_NEWPASSWORD", "enter password for new key: ") pw := readPassword("RESTIC_NEWPASSWORD", "enter password for new key: ")
pw2 := readPassword("RESTIC_NEWPASSWORD", "enter password again: ") pw2 := readPassword("RESTIC_NEWPASSWORD", "enter password again: ")
@ -97,7 +97,7 @@ func changePassword(s restic.Server) error {
} }
// add new key // add new key
id, err := restic.AddKey(s, pw, s.Key()) id, err := server.AddKey(s, pw, s.Key())
if err != nil { if err != nil {
return fmt.Errorf("creating new key failed: %v\n", err) return fmt.Errorf("creating new key failed: %v\n", err)
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/server"
) )
type CmdLs struct{} type CmdLs struct{}
@ -37,7 +38,7 @@ func printNode(prefix string, n *restic.Node) string {
} }
} }
func printTree(prefix string, s restic.Server, blob restic.Blob) error { func printTree(prefix string, s *server.Server, blob server.Blob) error {
tree, err := restic.LoadTree(s, blob) tree, err := restic.LoadTree(s, blob)
if err != nil { if err != nil {
return err return err

View File

@ -15,6 +15,7 @@ import (
"github.com/restic/restic/backend/local" "github.com/restic/restic/backend/local"
"github.com/restic/restic/backend/sftp" "github.com/restic/restic/backend/sftp"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/server"
) )
var version = "compiled manually" var version = "compiled manually"
@ -72,9 +73,9 @@ func (cmd CmdInit) Execute(args []string) error {
os.Exit(1) os.Exit(1)
} }
s := restic.NewServer(be) s := server.NewServer(be)
_, err = restic.CreateKey(s, pw) _, err = server.CreateKey(s, pw)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", opts.Repo, err) fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", opts.Repo, err)
os.Exit(1) os.Exit(1)
@ -134,21 +135,21 @@ func create(u string) (backend.Backend, error) {
return sftp.Create(url.Path[1:], "ssh", args...) return sftp.Create(url.Path[1:], "ssh", args...)
} }
func OpenRepo() (restic.Server, error) { func OpenRepo() (*server.Server, error) {
if opts.Repo == "" { if opts.Repo == "" {
return restic.Server{}, errors.New("Please specify repository location (-r)") return nil, errors.New("Please specify repository location (-r)")
} }
be, err := open(opts.Repo) be, err := open(opts.Repo)
if err != nil { if err != nil {
return restic.Server{}, err return nil, err
} }
s := restic.NewServer(be) s := server.NewServer(be)
err = s.SearchKey(readPassword("RESTIC_PASSWORD", "enter password for repository: ")) err = s.SearchKey(readPassword("RESTIC_PASSWORD", "enter password for repository: "))
if err != nil { if err != nil {
return restic.Server{}, fmt.Errorf("unable to open repo: %v", err) return nil, fmt.Errorf("unable to open repo: %v", err)
} }
return s, nil return s, nil

View File

@ -1,55 +0,0 @@
package restic_test
import (
"flag"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/restic/restic"
"github.com/restic/restic/backend/local"
. "github.com/restic/restic/test"
)
var testPassword = "foobar"
var testCleanup = flag.Bool("test.cleanup", true, "clean up after running tests (remove local backend directory with all content)")
var testTempDir = flag.String("test.tempdir", "", "use this directory for temporary storage (default: system temp dir)")
func setupBackend(t testing.TB) restic.Server {
tempdir, err := ioutil.TempDir(*testTempDir, "restic-test-")
OK(t, err)
// create repository below temp dir
b, err := local.Create(filepath.Join(tempdir, "repo"))
OK(t, err)
// set cache dir
err = os.Setenv("RESTIC_CACHE", filepath.Join(tempdir, "cache"))
OK(t, err)
return restic.NewServer(b)
}
func teardownBackend(t testing.TB, s restic.Server) {
if !*testCleanup {
l := s.Backend().(*local.Local)
t.Logf("leaving local backend at %s\n", l.Location())
return
}
OK(t, s.Delete())
}
func setupKey(t testing.TB, s restic.Server, password string) *restic.Key {
k, err := restic.CreateKey(s, password)
OK(t, err)
return k
}
func TestRepo(t *testing.T) {
s := setupBackend(t)
defer teardownBackend(t, s)
_ = setupKey(t, s, testPassword)
}

49
map.go
View File

@ -1,7 +1,6 @@
package restic package restic
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"sort" "sort"
@ -9,10 +8,11 @@ import (
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/server"
) )
type Map struct { type Map struct {
list []Blob list []server.Blob
m sync.Mutex m sync.Mutex
} }
@ -20,11 +20,11 @@ var ErrBlobNotFound = errors.New("Blob not found")
func NewMap() *Map { func NewMap() *Map {
return &Map{ return &Map{
list: []Blob{}, list: []server.Blob{},
} }
} }
func (bl *Map) find(blob Blob, checkSize bool) (int, Blob, error) { func (bl *Map) find(blob server.Blob, checkSize bool) (int, server.Blob, error) {
pos := sort.Search(len(bl.list), func(i int) bool { pos := sort.Search(len(bl.list), func(i int) bool {
return blob.ID.Compare(bl.list[i].ID) >= 0 return blob.ID.Compare(bl.list[i].ID) >= 0
}) })
@ -36,10 +36,10 @@ func (bl *Map) find(blob Blob, checkSize bool) (int, Blob, error) {
} }
} }
return pos, Blob{}, ErrBlobNotFound return pos, server.Blob{}, ErrBlobNotFound
} }
func (bl *Map) Find(blob Blob) (Blob, error) { func (bl *Map) Find(blob server.Blob) (server.Blob, error) {
bl.m.Lock() bl.m.Lock()
defer bl.m.Unlock() defer bl.m.Unlock()
@ -47,11 +47,11 @@ func (bl *Map) Find(blob Blob) (Blob, error) {
return blob, err return blob, err
} }
func (bl *Map) FindID(id backend.ID) (Blob, error) { func (bl *Map) FindID(id backend.ID) (server.Blob, error) {
bl.m.Lock() bl.m.Lock()
defer bl.m.Unlock() defer bl.m.Unlock()
_, blob, err := bl.find(Blob{ID: id}, false) _, blob, err := bl.find(server.Blob{ID: id}, false)
return blob, err return blob, err
} }
@ -66,7 +66,7 @@ func (bl *Map) Merge(other *Map) {
} }
} }
func (bl *Map) insert(blob Blob) Blob { func (bl *Map) insert(blob server.Blob) server.Blob {
pos, b, err := bl.find(blob, true) pos, b, err := bl.find(blob, true)
if err == nil { if err == nil {
// already present // already present
@ -75,14 +75,14 @@ func (bl *Map) insert(blob Blob) Blob {
// insert blob // insert blob
// https://code.google.com/p/go-wiki/wiki/SliceTricks // https://code.google.com/p/go-wiki/wiki/SliceTricks
bl.list = append(bl.list, Blob{}) bl.list = append(bl.list, server.Blob{})
copy(bl.list[pos+1:], bl.list[pos:]) copy(bl.list[pos+1:], bl.list[pos:])
bl.list[pos] = blob bl.list[pos] = blob
return blob return blob
} }
func (bl *Map) Insert(blob Blob) Blob { func (bl *Map) Insert(blob server.Blob) server.Blob {
bl.m.Lock() bl.m.Lock()
defer bl.m.Unlock() defer bl.m.Unlock()
@ -152,7 +152,7 @@ func (bl *Map) Equals(other *Map) bool {
// Each calls f for each blob in the Map. While Each is running, no other // Each calls f for each blob in the Map. While Each is running, no other
// operation is possible, since a mutex is held for the whole time. // operation is possible, since a mutex is held for the whole time.
func (bl *Map) Each(f func(blob Blob)) { func (bl *Map) Each(f func(blob server.Blob)) {
bl.m.Lock() bl.m.Lock()
defer bl.m.Unlock() defer bl.m.Unlock()
@ -162,13 +162,13 @@ func (bl *Map) Each(f func(blob Blob)) {
} }
// Select returns a list of of blobs from the plaintext IDs given in list. // Select returns a list of of blobs from the plaintext IDs given in list.
func (bl *Map) Select(list backend.IDs) (Blobs, error) { func (bl *Map) Select(list backend.IDs) (server.Blobs, error) {
bl.m.Lock() bl.m.Lock()
defer bl.m.Unlock() defer bl.m.Unlock()
blobs := make(Blobs, 0, len(list)) blobs := make(server.Blobs, 0, len(list))
for _, id := range list { for _, id := range list {
_, blob, err := bl.find(Blob{ID: id}, false) _, blob, err := bl.find(server.Blob{ID: id}, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -210,27 +210,10 @@ func (m *Map) DeleteID(id backend.ID) {
m.m.Lock() m.m.Lock()
defer m.m.Unlock() defer m.m.Unlock()
pos, _, err := m.find(Blob{ID: id}, false) pos, _, err := m.find(server.Blob{ID: id}, false)
if err != nil { if err != nil {
return return
} }
m.list = append(m.list[:pos], m.list[pos+1:]...) m.list = append(m.list[:pos], m.list[pos+1:]...)
} }
// Compare compares two blobs by comparing the ID and the size. It returns -1,
// 0, or 1.
func (blob Blob) Compare(other Blob) int {
if res := bytes.Compare(other.ID, blob.ID); res != 0 {
return res
}
if blob.Size < other.Size {
return -1
}
if blob.Size > other.Size {
return 1
}
return 0
}

View File

@ -12,6 +12,7 @@ import (
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/server"
. "github.com/restic/restic/test" . "github.com/restic/restic/test"
) )
@ -26,8 +27,8 @@ func randomID() []byte {
return buf return buf
} }
func newBlob() restic.Blob { func newBlob() server.Blob {
return restic.Blob{ return server.Blob{
ID: randomID(), ID: randomID(),
Size: uint64(mrand.Uint32()), Size: uint64(mrand.Uint32()),
Storage: randomID(), Storage: randomID(),
@ -46,7 +47,7 @@ func TestMap(t *testing.T) {
bl.Insert(newBlob()) bl.Insert(newBlob())
} }
b2, err := bl.Find(restic.Blob{ID: b.ID, Size: b.Size}) b2, err := bl.Find(server.Blob{ID: b.ID, Size: b.Size})
OK(t, err) OK(t, err)
Assert(t, b2.Compare(b) == 0, "items are not equal: want %v, got %v", b, b2) Assert(t, b2.Compare(b) == 0, "items are not equal: want %v, got %v", b, b2)
@ -78,7 +79,7 @@ func TestMap(t *testing.T) {
// Test JSON encode/decode // Test JSON encode/decode
func TestMapJSON(t *testing.T) { func TestMapJSON(t *testing.T) {
bl := restic.NewMap() bl := restic.NewMap()
b := restic.Blob{ID: randomID()} b := server.Blob{ID: randomID()}
bl.Insert(b) bl.Insert(b)
b2, err := bl.Find(b) b2, err := bl.Find(b)

View File

@ -10,6 +10,7 @@ import (
"github.com/juju/arrar" "github.com/juju/arrar"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/server"
) )
type Node struct { type Node struct {
@ -37,7 +38,7 @@ type Node struct {
path string path string
err error err error
blobs Blobs blobs server.Blobs
} }
func (n Node) String() string { func (n Node) String() string {
@ -95,7 +96,7 @@ func nodeTypeFromFileInfo(path string, fi os.FileInfo) string {
return "" return ""
} }
func CreateNodeAt(node *Node, m *Map, s Server, path string) error { func CreateNodeAt(node *Node, m *Map, s *server.Server, path string) error {
switch node.Type { switch node.Type {
case "dir": case "dir":
err := os.Mkdir(path, node.Mode) err := os.Mkdir(path, node.Mode)

View File

@ -9,10 +9,11 @@ import (
"github.com/juju/arrar" "github.com/juju/arrar"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/server"
) )
type Restorer struct { type Restorer struct {
s Server s *server.Server
sn *Snapshot sn *Snapshot
Error func(dir string, node *Node, err error) error Error func(dir string, node *Node, err error) error
@ -20,7 +21,7 @@ type Restorer struct {
} }
// NewRestorer creates a restorer preloaded with the content from the snapshot snid. // NewRestorer creates a restorer preloaded with the content from the snapshot snid.
func NewRestorer(s Server, snid backend.ID) (*Restorer, error) { func NewRestorer(s *server.Server, snid backend.ID) (*Restorer, error) {
r := &Restorer{s: s} r := &Restorer{s: s}
var err error var err error
@ -36,7 +37,7 @@ func NewRestorer(s Server, snid backend.ID) (*Restorer, error) {
return r, nil return r, nil
} }
func (res *Restorer) to(dst string, dir string, treeBlob Blob) error { func (res *Restorer) to(dst string, dir string, treeBlob server.Blob) error {
tree, err := LoadTree(res.s, treeBlob) tree, err := LoadTree(res.s, treeBlob)
if err != nil { if err != nil {
return res.Error(dir, nil, arrar.Annotate(err, "LoadTree")) return res.Error(dir, nil, arrar.Annotate(err, "LoadTree"))

View File

@ -1,6 +1,7 @@
package restic package server
import ( import (
"bytes"
"fmt" "fmt"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
@ -28,3 +29,20 @@ func (b Blob) String() string {
b.ID.Str(), b.Size, b.ID.Str(), b.Size,
b.Storage.Str(), b.StorageSize) b.Storage.Str(), b.StorageSize)
} }
// Compare compares two blobs by comparing the ID and the size. It returns -1,
// 0, or 1.
func (blob Blob) Compare(other Blob) int {
if res := bytes.Compare(other.ID, blob.ID); res != 0 {
return res
}
if blob.Size < other.Size {
return -1
}
if blob.Size > other.Size {
return 1
}
return 0
}

View File

@ -1,4 +1,4 @@
package restic package server
import ( import (
"crypto/rand" "crypto/rand"
@ -52,12 +52,12 @@ type Key struct {
// CreateKey initializes a master key in the given backend and encrypts it with // CreateKey initializes a master key in the given backend and encrypts it with
// the password. // the password.
func CreateKey(s Server, password string) (*Key, error) { func CreateKey(s *Server, password string) (*Key, error) {
return AddKey(s, password, nil) return AddKey(s, password, nil)
} }
// OpenKey tries do decrypt the key specified by name with the given password. // OpenKey tries do decrypt the key specified by name with the given password.
func OpenKey(s Server, name string, password string) (*Key, error) { func OpenKey(s *Server, name string, password string) (*Key, error) {
k, err := LoadKey(s, name) k, err := LoadKey(s, name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -104,7 +104,7 @@ func OpenKey(s Server, name string, password string) (*Key, error) {
// SearchKey tries to decrypt all keys in the backend with the given password. // SearchKey tries to decrypt all keys in the backend with the given password.
// If none could be found, ErrNoKeyFound is returned. // If none could be found, ErrNoKeyFound is returned.
func SearchKey(s Server, password string) (*Key, error) { func SearchKey(s *Server, password string) (*Key, error) {
// try all keys in repo // try all keys in repo
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
@ -121,7 +121,7 @@ func SearchKey(s Server, password string) (*Key, error) {
} }
// LoadKey loads a key from the backend. // LoadKey loads a key from the backend.
func LoadKey(s Server, name string) (*Key, error) { func LoadKey(s *Server, name string) (*Key, error) {
// extract data from repo // extract data from repo
rd, err := s.be.Get(backend.Key, name) rd, err := s.be.Get(backend.Key, name)
if err != nil { if err != nil {
@ -141,7 +141,7 @@ func LoadKey(s Server, name string) (*Key, error) {
} }
// AddKey adds a new key to an already existing repository. // AddKey adds a new key to an already existing repository.
func AddKey(s Server, password string, template *Key) (*Key, error) { func AddKey(s *Server, password string, template *Key) (*Key, error) {
// fill meta data about key // fill meta data about key
newkey := &Key{ newkey := &Key{
Created: time.Now(), Created: time.Now(),
@ -196,7 +196,7 @@ func AddKey(s Server, password string, template *Key) (*Key, error) {
return nil, err return nil, err
} }
newkey.Data, err = crypto.Encrypt(newkey.user, GetChunkBuf("key"), buf) newkey.Data, err = crypto.Encrypt(newkey.user, nil, buf)
// dump as json // dump as json
buf, err = json.Marshal(newkey) buf, err = json.Marshal(newkey)
@ -226,8 +226,6 @@ func AddKey(s Server, password string, template *Key) (*Key, error) {
newkey.name = name newkey.name = name
FreeChunkBuf("key", newkey.Data)
return newkey, nil return newkey, nil
} }

13
server/key_test.go Normal file
View File

@ -0,0 +1,13 @@
package server_test
import (
"testing"
. "github.com/restic/restic/test"
)
func TestRepo(t *testing.T) {
s := SetupBackend(t)
defer TeardownBackend(t, s)
_ = SetupKey(t, s, TestPassword)
}

21
server/pool.go Normal file
View File

@ -0,0 +1,21 @@
package server
import (
"sync"
"github.com/restic/restic/chunker"
)
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, chunker.MinSize)
},
}
func getBuf() []byte {
return bufPool.Get().([]byte)
}
func freeBuf(data []byte) {
bufPool.Put(data)
}

View File

@ -1,4 +1,4 @@
package restic package server
import ( import (
"crypto/sha256" "crypto/sha256"
@ -7,12 +7,9 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"sync"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/chunker" "github.com/restic/restic/chunker"
"github.com/restic/restic/crypto"
"github.com/restic/restic/debug"
) )
type Server struct { type Server struct {
@ -20,8 +17,8 @@ type Server struct {
key *Key key *Key
} }
func NewServer(be backend.Backend) Server { func NewServer(be backend.Backend) *Server {
return Server{be: be} return &Server{be: be}
} }
func (s *Server) SetKey(k *Key) { func (s *Server) SetKey(k *Key) {
@ -36,19 +33,19 @@ func (s *Server) ChunkerPolynomial() chunker.Pol {
// Find loads the list of all blobs of type t and searches for names which start // Find loads the list of all blobs of type t and searches for names which start
// with prefix. If none is found, nil and ErrNoIDPrefixFound is returned. If // with prefix. If none is found, nil and ErrNoIDPrefixFound is returned. If
// more than one is found, nil and ErrMultipleIDMatches is returned. // more than one is found, nil and ErrMultipleIDMatches is returned.
func (s Server) Find(t backend.Type, prefix string) (string, error) { func (s *Server) Find(t backend.Type, prefix string) (string, error) {
return backend.Find(s.be, t, prefix) return backend.Find(s.be, t, prefix)
} }
// FindSnapshot takes a string and tries to find a snapshot whose ID matches // FindSnapshot takes a string and tries to find a snapshot whose ID matches
// the string as closely as possible. // the string as closely as possible.
func (s Server) FindSnapshot(name string) (string, error) { func (s *Server) FindSnapshot(name string) (string, error) {
return backend.FindSnapshot(s.be, name) return backend.FindSnapshot(s.be, name)
} }
// PrefixLength returns the number of bytes required so that all prefixes of // PrefixLength returns the number of bytes required so that all prefixes of
// all IDs of type t are unique. // all IDs of type t are unique.
func (s Server) PrefixLength(t backend.Type) (int, error) { func (s *Server) PrefixLength(t backend.Type) (int, error) {
return backend.PrefixLength(s.be, t) return backend.PrefixLength(s.be, t)
} }
@ -56,7 +53,7 @@ func (s Server) PrefixLength(t backend.Type) (int, error) {
// backend. If the blob specifies an ID, the decrypted plaintext is checked // backend. If the blob specifies an ID, the decrypted plaintext is checked
// against this ID. The same goes for blob.Size and blob.StorageSize: If they // against this ID. The same goes for blob.Size and blob.StorageSize: If they
// are set to a value > 0, this value is checked. // are set to a value > 0, this value is checked.
func (s Server) Load(t backend.Type, blob Blob) ([]byte, error) { func (s *Server) Load(t backend.Type, blob Blob) ([]byte, error) {
// load data // load data
rd, err := s.be.Get(t, blob.Storage.String()) rd, err := s.be.Get(t, blob.Storage.String())
if err != nil { if err != nil {
@ -101,13 +98,13 @@ func (s Server) Load(t backend.Type, blob Blob) ([]byte, error) {
} }
// Load tries to load and decrypt content identified by t and id from the backend. // Load tries to load and decrypt content identified by t and id from the backend.
func (s Server) LoadID(t backend.Type, storageID backend.ID) ([]byte, error) { func (s *Server) LoadID(t backend.Type, storageID backend.ID) ([]byte, error) {
return s.Load(t, Blob{Storage: storageID}) return s.Load(t, Blob{Storage: storageID})
} }
// LoadJSON calls Load() to get content from the backend and afterwards calls // LoadJSON calls Load() to get content from the backend and afterwards calls
// json.Unmarshal on the item. // json.Unmarshal on the item.
func (s Server) LoadJSON(t backend.Type, blob Blob, item interface{}) error { func (s *Server) LoadJSON(t backend.Type, blob Blob, item interface{}) error {
buf, err := s.Load(t, blob) buf, err := s.Load(t, blob)
if err != nil { if err != nil {
return err return err
@ -118,7 +115,7 @@ func (s Server) LoadJSON(t backend.Type, blob Blob, item interface{}) error {
// LoadJSONID calls Load() to get content from the backend and afterwards calls // LoadJSONID calls Load() to get content from the backend and afterwards calls
// json.Unmarshal on the item. // json.Unmarshal on the item.
func (s Server) LoadJSONID(t backend.Type, id backend.ID, item interface{}) error { func (s *Server) LoadJSONID(t backend.Type, id backend.ID, item interface{}) error {
// read // read
rd, err := s.be.Get(t, id.String()) rd, err := s.be.Get(t, id.String())
if err != nil { if err != nil {
@ -144,7 +141,7 @@ func (s Server) LoadJSONID(t backend.Type, id backend.ID, item interface{}) erro
} }
// Save encrypts data and stores it to the backend as type t. // Save encrypts data and stores it to the backend as type t.
func (s Server) Save(t backend.Type, data []byte, id backend.ID) (Blob, error) { func (s *Server) Save(t backend.Type, data []byte, id backend.ID) (Blob, error) {
if id == nil { if id == nil {
// compute plaintext hash // compute plaintext hash
id = backend.Hash(data) id = backend.Hash(data)
@ -156,20 +153,8 @@ func (s Server) Save(t backend.Type, data []byte, id backend.ID) (Blob, error) {
Size: uint64(len(data)), Size: uint64(len(data)),
} }
var ciphertext []byte ciphertext := getBuf()
defer freeBuf(ciphertext)
// if the data is small enough, use a slice from the pool
if len(data) <= maxCiphertextSize-crypto.Extension {
ciphertext = GetChunkBuf("ch.Save()")
defer FreeChunkBuf("ch.Save()", ciphertext)
} else {
l := len(data) + crypto.Extension
debug.Log("Server.Save", "create large slice of %d bytes for ciphertext", l)
// use a new slice
ciphertext = make([]byte, l)
}
// encrypt blob // encrypt blob
ciphertext, err := s.Encrypt(ciphertext, data) ciphertext, err := s.Encrypt(ciphertext, data)
@ -203,7 +188,7 @@ func (s Server) Save(t backend.Type, data []byte, id backend.ID) (Blob, error) {
} }
// SaveFrom encrypts data read from rd and stores it to the backend as type t. // SaveFrom encrypts data read from rd and stores it to the backend as type t.
func (s Server) SaveFrom(t backend.Type, id backend.ID, length uint, rd io.Reader) (Blob, error) { func (s *Server) SaveFrom(t backend.Type, id backend.ID, length uint, rd io.Reader) (Blob, error) {
if id == nil { if id == nil {
return Blob{}, errors.New("id is nil") return Blob{}, errors.New("id is nil")
} }
@ -244,7 +229,7 @@ func (s Server) SaveFrom(t backend.Type, id backend.ID, length uint, rd io.Reade
// SaveJSON serialises item as JSON and encrypts and saves it in the backend as // SaveJSON serialises item as JSON and encrypts and saves it in the backend as
// type t. // type t.
func (s Server) SaveJSON(t backend.Type, item interface{}) (Blob, error) { func (s *Server) SaveJSON(t backend.Type, item interface{}) (Blob, error) {
backendBlob, err := s.be.Create() backendBlob, err := s.be.Create()
if err != nil { if err != nil {
return Blob{}, fmt.Errorf("Create: %v", err) return Blob{}, fmt.Errorf("Create: %v", err)
@ -284,12 +269,12 @@ func (s Server) SaveJSON(t backend.Type, item interface{}) (Blob, error) {
} }
// Returns the backend used for this server. // Returns the backend used for this server.
func (s Server) Backend() backend.Backend { func (s *Server) Backend() backend.Backend {
return s.be return s.be
} }
func (s *Server) SearchKey(password string) error { func (s *Server) SearchKey(password string) error {
key, err := SearchKey(*s, password) key, err := SearchKey(s, password)
if err != nil { if err != nil {
return err return err
} }
@ -299,7 +284,7 @@ func (s *Server) SearchKey(password string) error {
return nil return nil
} }
func (s Server) Decrypt(ciphertext []byte) ([]byte, error) { func (s *Server) Decrypt(ciphertext []byte) ([]byte, error) {
if s.key == nil { if s.key == nil {
return nil, errors.New("key for server not set") return nil, errors.New("key for server not set")
} }
@ -307,7 +292,7 @@ func (s Server) Decrypt(ciphertext []byte) ([]byte, error) {
return s.key.Decrypt([]byte{}, ciphertext) return s.key.Decrypt([]byte{}, ciphertext)
} }
func (s Server) Encrypt(ciphertext, plaintext []byte) ([]byte, error) { func (s *Server) Encrypt(ciphertext, plaintext []byte) ([]byte, error) {
if s.key == nil { if s.key == nil {
return nil, errors.New("key for server not set") return nil, errors.New("key for server not set")
} }
@ -315,67 +300,12 @@ func (s Server) Encrypt(ciphertext, plaintext []byte) ([]byte, error) {
return s.key.Encrypt(ciphertext, plaintext) return s.key.Encrypt(ciphertext, plaintext)
} }
func (s Server) Key() *Key { func (s *Server) Key() *Key {
return s.key return s.key
} }
type ServerStats struct {
Blobs, Trees uint
}
// Stats returns statistics for this backend and the server.
func (s Server) Stats() (ServerStats, error) {
blobs := backend.NewIDSet()
// load all trees, in parallel
worker := func(wg *sync.WaitGroup, b <-chan Blob) {
for blob := range b {
tree, err := LoadTree(s, blob)
// ignore error and advance to next tree
if err != nil {
return
}
for _, id := range tree.Map.StorageIDs() {
blobs.Insert(id)
}
}
wg.Done()
}
blobCh := make(chan Blob)
// start workers
var wg sync.WaitGroup
for i := 0; i < maxConcurrency; i++ {
wg.Add(1)
go worker(&wg, blobCh)
}
// list ids
trees := 0
done := make(chan struct{})
defer close(done)
for name := range s.List(backend.Tree, done) {
trees++
id, err := backend.ParseID(name)
if err != nil {
debug.Log("Server.Stats", "unable to parse name %v as id: %v", name, err)
continue
}
blobCh <- Blob{Storage: id}
}
close(blobCh)
// wait for workers
wg.Wait()
return ServerStats{Blobs: uint(blobs.Len()), Trees: uint(trees)}, nil
}
// Count returns the number of blobs of a given type in the backend. // Count returns the number of blobs of a given type in the backend.
func (s Server) Count(t backend.Type) (n int) { func (s *Server) Count(t backend.Type) (n int) {
for _ = range s.List(t, nil) { for _ = range s.List(t, nil) {
n++ n++
} }
@ -385,23 +315,27 @@ func (s Server) Count(t backend.Type) (n int) {
// Proxy methods to backend // Proxy methods to backend
func (s Server) List(t backend.Type, done <-chan struct{}) <-chan string { func (s *Server) Get(t backend.Type, name string) (io.ReadCloser, error) {
return s.be.Get(t, name)
}
func (s *Server) List(t backend.Type, done <-chan struct{}) <-chan string {
return s.be.List(t, done) return s.be.List(t, done)
} }
func (s Server) Test(t backend.Type, name string) (bool, error) { func (s *Server) Test(t backend.Type, name string) (bool, error) {
return s.be.Test(t, name) return s.be.Test(t, name)
} }
func (s Server) Remove(t backend.Type, name string) error { func (s *Server) Remove(t backend.Type, name string) error {
return s.be.Remove(t, name) return s.be.Remove(t, name)
} }
func (s Server) Close() error { func (s *Server) Close() error {
return s.be.Close() return s.be.Close()
} }
func (s Server) Delete() error { func (s *Server) Delete() error {
if b, ok := s.be.(backend.Deleter); ok { if b, ok := s.be.(backend.Deleter); ok {
return b.Delete() return b.Delete()
} }
@ -409,10 +343,10 @@ func (s Server) Delete() error {
return errors.New("Delete() called for backend that does not implement this method") return errors.New("Delete() called for backend that does not implement this method")
} }
func (s Server) ID() string { func (s *Server) ID() string {
return s.be.ID() return s.be.ID()
} }
func (s Server) Location() string { func (s *Server) Location() string {
return s.be.Location() return s.be.Location()
} }

View File

@ -1,10 +1,11 @@
package restic_test package server_test
import ( import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"flag"
"io" "io"
"testing" "testing"
@ -13,6 +14,8 @@ import (
. "github.com/restic/restic/test" . "github.com/restic/restic/test"
) )
var benchTestDir = flag.String("test.dir", ".", "dir used in benchmarks (default: .)")
type testJSONStruct struct { type testJSONStruct struct {
Foo uint32 Foo uint32
Bar string Bar string
@ -24,9 +27,9 @@ var serverTests = []testJSONStruct{
} }
func TestSaveJSON(t *testing.T) { func TestSaveJSON(t *testing.T) {
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
for _, obj := range serverTests { for _, obj := range serverTests {
@ -45,9 +48,9 @@ func TestSaveJSON(t *testing.T) {
} }
func BenchmarkSaveJSON(t *testing.B) { func BenchmarkSaveJSON(t *testing.B) {
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
obj := serverTests[0] obj := serverTests[0]
@ -72,9 +75,9 @@ func BenchmarkSaveJSON(t *testing.B) {
var testSizes = []int{5, 23, 2<<18 + 23, 1 << 20} var testSizes = []int{5, 23, 2<<18 + 23, 1 << 20}
func TestSaveFrom(t *testing.T) { func TestSaveFrom(t *testing.T) {
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
for _, size := range testSizes { for _, size := range testSizes {
@ -102,9 +105,9 @@ func TestSaveFrom(t *testing.T) {
} }
func BenchmarkSaveFrom(t *testing.B) { func BenchmarkSaveFrom(t *testing.B) {
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
size := 4 << 20 // 4MiB size := 4 << 20 // 4MiB
@ -125,37 +128,18 @@ func BenchmarkSaveFrom(t *testing.B) {
} }
} }
func TestServerStats(t *testing.T) {
if *benchArchiveDirectory == "" {
t.Skip("benchdir not set, skipping TestServerStats")
}
server := setupBackend(t)
defer teardownBackend(t, server)
key := setupKey(t, server, "geheim")
server.SetKey(key)
// archive a few files
sn := snapshot(t, server, *benchArchiveDirectory, nil)
t.Logf("archived snapshot %v", sn.ID())
stats, err := server.Stats()
OK(t, err)
t.Logf("stats: %v", stats)
}
func TestLoadJSONID(t *testing.T) { func TestLoadJSONID(t *testing.T) {
if *benchArchiveDirectory == "" { if *benchTestDir == "" {
t.Skip("benchdir not set, skipping TestServerStats") t.Skip("benchdir not set, skipping TestServerStats")
} }
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
// archive a few files // archive a few files
sn := snapshot(t, server, *benchArchiveDirectory, nil) sn := SnapshotDir(t, server, *benchTestDir, nil)
t.Logf("archived snapshot %v", sn.ID()) t.Logf("archived snapshot %v", sn.ID())
// benchmark loading first tree // benchmark loading first tree
@ -173,17 +157,17 @@ func TestLoadJSONID(t *testing.T) {
} }
func BenchmarkLoadJSONID(t *testing.B) { func BenchmarkLoadJSONID(t *testing.B) {
if *benchArchiveDirectory == "" { if *benchTestDir == "" {
t.Skip("benchdir not set, skipping TestServerStats") t.Skip("benchdir not set, skipping TestServerStats")
} }
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
// archive a few files // archive a few files
sn := snapshot(t, server, *benchArchiveDirectory, nil) sn := SnapshotDir(t, server, *benchTestDir, nil)
t.Logf("archived snapshot %v", sn.ID()) t.Logf("archived snapshot %v", sn.ID())
t.ResetTimer() t.ResetTimer()

View File

@ -7,19 +7,20 @@ import (
"time" "time"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/server"
) )
type Snapshot struct { type Snapshot struct {
Time time.Time `json:"time"` Time time.Time `json:"time"`
Parent backend.ID `json:"parent,omitempty"` Parent backend.ID `json:"parent,omitempty"`
Tree Blob `json:"tree"` Tree server.Blob `json:"tree"`
Paths []string `json:"paths"` Paths []string `json:"paths"`
Hostname string `json:"hostname,omitempty"` Hostname string `json:"hostname,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
UID uint32 `json:"uid,omitempty"` UID uint32 `json:"uid,omitempty"`
GID uint32 `json:"gid,omitempty"` GID uint32 `json:"gid,omitempty"`
UserID string `json:"userid,omitempty"` UserID string `json:"userid,omitempty"`
GroupID string `json:"groupid,omitempty"` GroupID string `json:"groupid,omitempty"`
id backend.ID // plaintext ID, used during restore id backend.ID // plaintext ID, used during restore
} }
@ -49,7 +50,7 @@ func NewSnapshot(paths []string) (*Snapshot, error) {
return sn, nil return sn, nil
} }
func LoadSnapshot(s Server, id backend.ID) (*Snapshot, error) { func LoadSnapshot(s *server.Server, id backend.ID) (*Snapshot, error) {
sn := &Snapshot{id: id} sn := &Snapshot{id: id}
err := s.LoadJSONID(backend.Snapshot, id, sn) err := s.LoadJSONID(backend.Snapshot, id, sn)
if err != nil { if err != nil {

View File

@ -5,10 +5,11 @@ import (
"time" "time"
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/server"
. "github.com/restic/restic/test" . "github.com/restic/restic/test"
) )
func testSnapshot(t *testing.T, s restic.Server) { func testSnapshot(t *testing.T, s *server.Server) {
var err error var err error
sn, err := restic.NewSnapshot([]string{"/home/foobar"}) sn, err := restic.NewSnapshot([]string{"/home/foobar"})
OK(t, err) OK(t, err)
@ -22,8 +23,8 @@ func testSnapshot(t *testing.T, s restic.Server) {
} }
func TestSnapshot(t *testing.T) { func TestSnapshot(t *testing.T) {
repo := setupBackend(t) s := SetupBackend(t)
defer teardownBackend(t, repo) defer TeardownBackend(t, s)
testSnapshot(t, repo) testSnapshot(t, s)
} }

59
test/backend.go Normal file
View File

@ -0,0 +1,59 @@
package test_helper
import (
"flag"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/restic/restic"
"github.com/restic/restic/backend"
"github.com/restic/restic/backend/local"
"github.com/restic/restic/server"
)
var TestPassword = "foobar"
var TestCleanup = flag.Bool("test.cleanup", true, "clean up after running tests (remove local backend directory with all content)")
var TestTempDir = flag.String("test.tempdir", "", "use this directory for temporary storage (default: system temp dir)")
func SetupBackend(t testing.TB) *server.Server {
tempdir, err := ioutil.TempDir(*TestTempDir, "restic-test-")
OK(t, err)
// create repository below temp dir
b, err := local.Create(filepath.Join(tempdir, "repo"))
OK(t, err)
// set cache dir
err = os.Setenv("RESTIC_CACHE", filepath.Join(tempdir, "cache"))
OK(t, err)
return server.NewServer(b)
}
func TeardownBackend(t testing.TB, s *server.Server) {
if !*TestCleanup {
l := s.Backend().(*local.Local)
t.Logf("leaving local backend at %s\n", l.Location())
return
}
OK(t, s.Delete())
}
func SetupKey(t testing.TB, s *server.Server, password string) *server.Key {
k, err := server.CreateKey(s, password)
OK(t, err)
return k
}
func SnapshotDir(t testing.TB, server *server.Server, path string, parent backend.ID) *restic.Snapshot {
arch, err := restic.NewArchiver(server)
OK(t, err)
OK(t, arch.Preload())
sn, _, err := arch.Snapshot(nil, []string{path}, parent)
OK(t, err)
return sn
}

View File

@ -7,6 +7,7 @@ import (
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/server"
) )
type Tree struct { type Tree struct {
@ -30,7 +31,7 @@ func (t Tree) String() string {
return fmt.Sprintf("Tree<%d nodes, %d blobs>", len(t.Nodes), len(t.Map.list)) return fmt.Sprintf("Tree<%d nodes, %d blobs>", len(t.Nodes), len(t.Map.list))
} }
func LoadTree(s Server, blob Blob) (*Tree, error) { func LoadTree(s *server.Server, blob server.Blob) (*Tree, error) {
tree := &Tree{} tree := &Tree{}
err := s.LoadJSON(backend.Tree, blob, tree) err := s.LoadJSON(backend.Tree, blob, tree)
if err != nil { if err != nil {

View File

@ -22,7 +22,7 @@ var testFiles = []struct {
// prepareDir creates a temporary directory and returns it. // prepareDir creates a temporary directory and returns it.
func prepareDir(t *testing.T) string { func prepareDir(t *testing.T) string {
tempdir, err := ioutil.TempDir(*testTempDir, "restic-test-") tempdir, err := ioutil.TempDir(*TestTempDir, "restic-test-")
OK(t, err) OK(t, err)
for _, test := range testFiles { for _, test := range testFiles {
@ -49,7 +49,7 @@ func prepareDir(t *testing.T) string {
func TestTree(t *testing.T) { func TestTree(t *testing.T) {
dir := prepareDir(t) dir := prepareDir(t)
defer func() { defer func() {
if *testCleanup { if *TestCleanup {
OK(t, os.RemoveAll(dir)) OK(t, os.RemoveAll(dir))
} }
}() }()

View File

@ -4,6 +4,7 @@ import (
"path/filepath" "path/filepath"
"github.com/restic/restic/debug" "github.com/restic/restic/debug"
"github.com/restic/restic/server"
) )
type WalkTreeJob struct { type WalkTreeJob struct {
@ -14,7 +15,7 @@ type WalkTreeJob struct {
Tree *Tree Tree *Tree
} }
func walkTree(s Server, path string, treeBlob Blob, done chan struct{}, jobCh chan<- WalkTreeJob) { func walkTree(s *server.Server, path string, treeBlob server.Blob, done chan struct{}, jobCh chan<- WalkTreeJob) {
debug.Log("walkTree", "start on %q (%v)", path, treeBlob) debug.Log("walkTree", "start on %q (%v)", path, treeBlob)
// load tree // load tree
t, err := LoadTree(s, treeBlob) t, err := LoadTree(s, treeBlob)
@ -49,7 +50,7 @@ func walkTree(s Server, path string, treeBlob Blob, done chan struct{}, jobCh ch
// WalkTree walks the tree specified by ID recursively and sends a job for each // WalkTree walks the tree specified by ID recursively and sends a job for each
// file and directory it finds. When the channel done is closed, processing // file and directory it finds. When the channel done is closed, processing
// stops. // stops.
func WalkTree(server Server, blob Blob, done chan struct{}, jobCh chan<- WalkTreeJob) { func WalkTree(server *server.Server, blob server.Blob, done chan struct{}, jobCh chan<- WalkTreeJob) {
debug.Log("WalkTree", "start on %v", blob) debug.Log("WalkTree", "start on %v", blob)
walkTree(server, "", blob, done, jobCh) walkTree(server, "", blob, done, jobCh)
close(jobCh) close(jobCh)

View File

@ -16,9 +16,9 @@ func TestWalkTree(t *testing.T) {
dirs, err := filepath.Glob(*testWalkDirectory) dirs, err := filepath.Glob(*testWalkDirectory)
OK(t, err) OK(t, err)
server := setupBackend(t) server := SetupBackend(t)
defer teardownBackend(t, server) defer TeardownBackend(t, server)
key := setupKey(t, server, "geheim") key := SetupKey(t, server, "geheim")
server.SetKey(key) server.SetKey(key)
// archive a few files // archive a few files