2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-27 07:16:40 +00:00

Fix cmds/restic for new structure

This commit is contained in:
Alexander Neumann 2016-09-01 16:04:29 +02:00
parent 3695ba5882
commit f7ae0cb78f
20 changed files with 202 additions and 320 deletions

View File

@ -6,7 +6,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"restic" "restic"
"restic/backend" "restic/archiver"
"restic/debug" "restic/debug"
"restic/filter" "restic/filter"
"restic/fs" "restic/fs"
@ -259,7 +259,7 @@ func (cmd CmdBackup) readFromStdin(args []string) error {
return err return err
} }
_, id, err := restic.ArchiveReader(repo, cmd.newArchiveStdinProgress(), os.Stdin, cmd.StdinFilename) _, id, err := archiver.ArchiveReader(repo, cmd.newArchiveStdinProgress(), os.Stdin, cmd.StdinFilename)
if err != nil { if err != nil {
return err return err
} }
@ -306,7 +306,7 @@ func (cmd CmdBackup) Execute(args []string) error {
return err return err
} }
var parentSnapshotID *backend.ID var parentSnapshotID *restic.ID
// Force using a parent // Force using a parent
if !cmd.Force && cmd.Parent != "" { if !cmd.Force && cmd.Parent != "" {
@ -365,12 +365,12 @@ func (cmd CmdBackup) Execute(args []string) error {
return !matched return !matched
} }
stat, err := restic.Scan(target, selectFilter, cmd.newScanProgress()) stat, err := archiver.Scan(target, selectFilter, cmd.newScanProgress())
if err != nil { if err != nil {
return err return err
} }
arch := restic.NewArchiver(repo) arch := archiver.New(repo)
arch.Excludes = cmd.Excludes arch.Excludes = cmd.Excludes
arch.SelectFilter = selectFilter arch.SelectFilter = selectFilter

View File

@ -1,52 +0,0 @@
package main
import (
"fmt"
"restic"
)
type CmdCache struct {
global *GlobalOptions
}
func init() {
_, err := parser.AddCommand("cache",
"manage cache",
"The cache command creates and manages the local cache",
&CmdCache{global: &globalOpts})
if err != nil {
panic(err)
}
}
func (cmd CmdCache) Usage() string {
return "[update|clear]"
}
func (cmd CmdCache) Execute(args []string) error {
repo, err := cmd.global.OpenRepository()
if err != nil {
return err
}
lock, err := lockRepo(repo)
defer unlockRepo(lock)
if err != nil {
return err
}
cache, err := restic.NewCache(repo, cmd.global.CacheDir)
if err != nil {
return err
}
fmt.Printf("clear cache for old snapshots\n")
err = cache.Clear(repo)
if err != nil {
return err
}
fmt.Printf("done\n")
return nil
}

View File

@ -8,7 +8,6 @@ import (
"restic" "restic"
"restic/backend" "restic/backend"
"restic/debug" "restic/debug"
"restic/pack"
"restic/repository" "restic/repository"
) )
@ -48,9 +47,9 @@ func (cmd CmdCat) Execute(args []string) error {
tpe := args[0] tpe := args[0]
var id backend.ID var id restic.ID
if tpe != "masterkey" && tpe != "config" { if tpe != "masterkey" && tpe != "config" {
id, err = backend.ParseID(args[1]) id, err = restic.ParseID(args[1])
if err != nil { if err != nil {
if tpe != "snapshot" { if tpe != "snapshot" {
return restic.Fatalf("unable to parse ID: %v\n", err) return restic.Fatalf("unable to parse ID: %v\n", err)
@ -67,7 +66,7 @@ func (cmd CmdCat) Execute(args []string) error {
// handle all types that don't need an index // handle all types that don't need an index
switch tpe { switch tpe {
case "config": case "config":
buf, err := json.MarshalIndent(repo.Config, "", " ") buf, err := json.MarshalIndent(repo.Config(), "", " ")
if err != nil { if err != nil {
return err return err
} }
@ -75,7 +74,7 @@ func (cmd CmdCat) Execute(args []string) error {
fmt.Println(string(buf)) fmt.Println(string(buf))
return nil return nil
case "index": case "index":
buf, err := repo.LoadAndDecrypt(backend.Index, id) buf, err := repo.LoadAndDecrypt(restic.IndexFile, id)
if err != nil { if err != nil {
return err return err
} }
@ -85,7 +84,7 @@ func (cmd CmdCat) Execute(args []string) error {
case "snapshot": case "snapshot":
sn := &restic.Snapshot{} sn := &restic.Snapshot{}
err = repo.LoadJSONUnpacked(backend.Snapshot, id, sn) err = repo.LoadJSONUnpacked(restic.SnapshotFile, id, sn)
if err != nil { if err != nil {
return err return err
} }
@ -99,7 +98,7 @@ func (cmd CmdCat) Execute(args []string) error {
return nil return nil
case "key": case "key":
h := backend.Handle{Type: backend.Key, Name: id.String()} h := restic.Handle{FileType: restic.KeyFile, Name: id.String()}
buf, err := backend.LoadAll(repo.Backend(), h, nil) buf, err := backend.LoadAll(repo.Backend(), h, nil)
if err != nil { if err != nil {
return err return err
@ -150,13 +149,13 @@ func (cmd CmdCat) Execute(args []string) error {
switch tpe { switch tpe {
case "pack": case "pack":
h := backend.Handle{Type: backend.Data, Name: id.String()} h := restic.Handle{FileType: restic.DataFile, Name: id.String()}
buf, err := backend.LoadAll(repo.Backend(), h, nil) buf, err := backend.LoadAll(repo.Backend(), h, nil)
if err != nil { if err != nil {
return err return err
} }
hash := backend.Hash(buf) hash := restic.Hash(buf)
if !hash.Equal(id) { if !hash.Equal(id) {
fmt.Fprintf(cmd.global.stderr, "Warning: hash of data does not match ID, want\n %v\ngot:\n %v\n", id.String(), hash.String()) fmt.Fprintf(cmd.global.stderr, "Warning: hash of data does not match ID, want\n %v\ngot:\n %v\n", id.String(), hash.String())
} }
@ -165,7 +164,7 @@ func (cmd CmdCat) Execute(args []string) error {
return err return err
case "blob": case "blob":
for _, t := range []pack.BlobType{pack.Data, pack.Tree} { for _, t := range []restic.BlobType{restic.DataBlob, restic.TreeBlob} {
list, err := repo.Index().Lookup(id, t) list, err := repo.Index().Lookup(id, t)
if err != nil { if err != nil {
continue continue
@ -187,7 +186,7 @@ func (cmd CmdCat) Execute(args []string) error {
case "tree": case "tree":
debug.Log("cat", "cat tree %v", id.Str()) debug.Log("cat", "cat tree %v", id.Str())
tree := restic.NewTree() tree := restic.NewTree()
err = repo.LoadJSONPack(pack.Tree, id, tree) err = repo.LoadJSONPack(restic.TreeBlob, id, tree)
if err != nil { if err != nil {
debug.Log("cat", "unable to load tree %v: %v", id.Str(), err) debug.Log("cat", "unable to load tree %v: %v", id.Str(), err)
return err return err

View File

@ -9,7 +9,6 @@ import (
"os" "os"
"restic" "restic"
"restic/backend"
"restic/pack" "restic/pack"
"restic/repository" "restic/repository"
@ -50,7 +49,7 @@ func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error {
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
for id := range repo.List(backend.Snapshot, done) { for id := range repo.List(restic.SnapshotFile, done) {
snapshot, err := restic.LoadSnapshot(repo, id) snapshot, err := restic.LoadSnapshot(repo, id)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "LoadSnapshot(%v): %v", id.Str(), err) fmt.Fprintf(os.Stderr, "LoadSnapshot(%v): %v", id.Str(), err)
@ -68,36 +67,36 @@ func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error {
return nil return nil
} }
func printTrees(repo *repository.Repository, wr io.Writer) error { // func printTrees(repo *repository.Repository, wr io.Writer) error {
done := make(chan struct{}) // done := make(chan struct{})
defer close(done) // defer close(done)
trees := []backend.ID{} // trees := []restic.ID{}
for _, idx := range repo.Index().All() { // for _, idx := range repo.Index().All() {
for blob := range idx.Each(nil) { // for blob := range idx.Each(nil) {
if blob.Type != pack.Tree { // if blob.Type != pack.Tree {
continue // continue
} // }
trees = append(trees, blob.ID) // trees = append(trees, blob.ID)
} // }
} // }
for _, id := range trees { // for _, id := range trees {
tree, err := restic.LoadTree(repo, id) // tree, err := restic.LoadTree(repo, id)
if err != nil { // if err != nil {
fmt.Fprintf(os.Stderr, "LoadTree(%v): %v", id.Str(), err) // fmt.Fprintf(os.Stderr, "LoadTree(%v): %v", id.Str(), err)
continue // continue
} // }
fmt.Fprintf(wr, "tree_id: %v\n", id) // fmt.Fprintf(wr, "tree_id: %v\n", id)
prettyPrintJSON(wr, tree) // prettyPrintJSON(wr, tree)
} // }
return nil // return nil
} // }
const dumpPackWorkers = 10 const dumpPackWorkers = 10
@ -110,10 +109,10 @@ type Pack struct {
// Blob is the struct used in printPacks. // Blob is the struct used in printPacks.
type Blob struct { type Blob struct {
Type pack.BlobType `json:"type"` Type restic.BlobType `json:"type"`
Length uint `json:"length"` Length uint `json:"length"`
ID backend.ID `json:"id"` ID restic.ID `json:"id"`
Offset uint `json:"offset"` Offset uint `json:"offset"`
} }
func printPacks(repo *repository.Repository, wr io.Writer) error { func printPacks(repo *repository.Repository, wr io.Writer) error {
@ -123,14 +122,14 @@ func printPacks(repo *repository.Repository, wr io.Writer) error {
f := func(job worker.Job, done <-chan struct{}) (interface{}, error) { f := func(job worker.Job, done <-chan struct{}) (interface{}, error) {
name := job.Data.(string) name := job.Data.(string)
h := backend.Handle{Type: backend.Data, Name: name} h := restic.Handle{FileType: restic.DataFile, Name: name}
blobInfo, err := repo.Backend().Stat(h) blobInfo, err := repo.Backend().Stat(h)
if err != nil { if err != nil {
return nil, err return nil, err
} }
blobs, err := pack.List(repo.Key(), backend.ReaderAt(repo.Backend(), h), blobInfo.Size) blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), blobInfo.Size)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -143,7 +142,7 @@ func printPacks(repo *repository.Repository, wr io.Writer) error {
wp := worker.New(dumpPackWorkers, f, jobCh, resCh) wp := worker.New(dumpPackWorkers, f, jobCh, resCh)
go func() { go func() {
for name := range repo.Backend().List(backend.Data, done) { for name := range repo.Backend().List(restic.DataFile, done) {
jobCh <- worker.Job{Data: name} jobCh <- worker.Job{Data: name}
} }
close(jobCh) close(jobCh)
@ -157,7 +156,7 @@ func printPacks(repo *repository.Repository, wr io.Writer) error {
continue continue
} }
entries := job.Result.([]pack.Blob) entries := job.Result.([]restic.Blob)
p := Pack{ p := Pack{
Name: name, Name: name,
Blobs: make([]Blob, len(entries)), Blobs: make([]Blob, len(entries)),
@ -183,7 +182,7 @@ func (cmd CmdDump) DumpIndexes() error {
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
for id := range cmd.repo.List(backend.Index, done) { for id := range cmd.repo.List(restic.IndexFile, done) {
fmt.Printf("index_id: %v\n", id) fmt.Printf("index_id: %v\n", id)
idx, err := repository.LoadIndex(cmd.repo, id) idx, err := repository.LoadIndex(cmd.repo, id)
@ -229,8 +228,8 @@ func (cmd CmdDump) Execute(args []string) error {
return cmd.DumpIndexes() return cmd.DumpIndexes()
case "snapshots": case "snapshots":
return debugPrintSnapshots(repo, os.Stdout) return debugPrintSnapshots(repo, os.Stdout)
case "trees": // case "trees":
return printTrees(repo, os.Stdout) // return printTrees(repo, os.Stdout)
case "packs": case "packs":
return printPacks(repo, os.Stdout) return printPacks(repo, os.Stdout)
case "all": case "all":
@ -240,12 +239,12 @@ func (cmd CmdDump) Execute(args []string) error {
return err return err
} }
fmt.Printf("\ntrees:\n") // fmt.Printf("\ntrees:\n")
err = printTrees(repo, os.Stdout) // err = printTrees(repo, os.Stdout)
if err != nil { // if err != nil {
return err // return err
} // }
fmt.Printf("\nindexes:\n") fmt.Printf("\nindexes:\n")
err = cmd.DumpIndexes() err = cmd.DumpIndexes()

View File

@ -5,7 +5,6 @@ import (
"time" "time"
"restic" "restic"
"restic/backend"
"restic/debug" "restic/debug"
"restic/repository" "restic/repository"
) )
@ -59,7 +58,7 @@ func parseTime(str string) (time.Time, error) {
return time.Time{}, restic.Fatalf("unable to parse time: %q", str) return time.Time{}, restic.Fatalf("unable to parse time: %q", str)
} }
func (c CmdFind) findInTree(repo *repository.Repository, id backend.ID, path string) ([]findResult, error) { func (c CmdFind) findInTree(repo *repository.Repository, id restic.ID, path string) ([]findResult, error) {
debug.Log("restic.find", "checking tree %v\n", id) debug.Log("restic.find", "checking tree %v\n", id)
tree, err := restic.LoadTree(repo, id) tree, err := restic.LoadTree(repo, id)
if err != nil { if err != nil {
@ -92,7 +91,7 @@ func (c CmdFind) findInTree(repo *repository.Repository, id backend.ID, path str
debug.Log("restic.find", " pattern does not match\n") debug.Log("restic.find", " pattern does not match\n")
} }
if node.Type == "dir" { if node.FileType == "dir" {
subdirResults, err := c.findInTree(repo, *node.Subtree, filepath.Join(path, node.Name)) subdirResults, err := c.findInTree(repo, *node.Subtree, filepath.Join(path, node.Name))
if err != nil { if err != nil {
return nil, err return nil, err
@ -105,7 +104,7 @@ func (c CmdFind) findInTree(repo *repository.Repository, id backend.ID, path str
return results, nil return results, nil
} }
func (c CmdFind) findInSnapshot(repo *repository.Repository, id backend.ID) error { func (c CmdFind) findInSnapshot(repo *repository.Repository, id restic.ID) error {
debug.Log("restic.find", "searching in snapshot %s\n for entries within [%s %s]", id.Str(), c.oldest, c.newest) debug.Log("restic.find", "searching in snapshot %s\n for entries within [%s %s]", id.Str(), c.oldest, c.newest)
sn, err := restic.LoadSnapshot(repo, id) sn, err := restic.LoadSnapshot(repo, id)
@ -184,7 +183,7 @@ func (c CmdFind) Execute(args []string) error {
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
for snapshotID := range repo.List(backend.Snapshot, done) { for snapshotID := range repo.List(restic.SnapshotFile, done) {
err := c.findInSnapshot(repo, snapshotID) err := c.findInSnapshot(repo, snapshotID)
if err != nil { if err != nil {

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"io" "io"
"restic" "restic"
"restic/backend"
"strings" "strings"
) )
@ -93,7 +92,7 @@ func (cmd CmdForget) Execute(args []string) error {
} }
if !cmd.DryRun { if !cmd.DryRun {
err = repo.Backend().Remove(backend.Snapshot, id.String()) err = repo.Backend().Remove(restic.SnapshotFile, id.String())
if err != nil { if err != nil {
return err return err
} }
@ -156,7 +155,7 @@ func (cmd CmdForget) Execute(args []string) error {
if !cmd.DryRun { if !cmd.DryRun {
for _, sn := range remove { for _, sn := range remove {
err = repo.Backend().Remove(backend.Snapshot, sn.ID().String()) err = repo.Backend().Remove(restic.SnapshotFile, sn.ID().String())
if err != nil { if err != nil {
return err return err
} }

View File

@ -32,7 +32,7 @@ func (cmd CmdInit) Execute(args []string) error {
cmd.global.Exitf(1, "creating key in backend at %s failed: %v\n", cmd.global.Repo, err) cmd.global.Exitf(1, "creating key in backend at %s failed: %v\n", cmd.global.Repo, err)
} }
cmd.global.Verbosef("created restic backend %v at %s\n", s.Config.ID[:10], cmd.global.Repo) cmd.global.Verbosef("created restic backend %v at %s\n", s.Config().ID[:10], cmd.global.Repo)
cmd.global.Verbosef("\n") cmd.global.Verbosef("\n")
cmd.global.Verbosef("Please note that knowledge of your password is required to access\n") cmd.global.Verbosef("Please note that knowledge of your password is required to access\n")
cmd.global.Verbosef("the repository. Losing your password means that your data is\n") cmd.global.Verbosef("the repository. Losing your password means that your data is\n")

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"restic" "restic"
"restic/backend"
"restic/repository" "restic/repository"
) )
@ -28,7 +27,7 @@ func (cmd CmdKey) listKeys(s *repository.Repository) error {
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"
plen, err := s.PrefixLength(backend.Key) plen, err := s.PrefixLength(restic.KeyFile)
if err != nil { if err != nil {
return err return err
} }
@ -36,7 +35,7 @@ func (cmd CmdKey) listKeys(s *repository.Repository) error {
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
for id := range s.List(backend.Key, done) { for id := range s.List(restic.KeyFile, done) {
k, err := repository.LoadKey(s, id.String()) k, err := repository.LoadKey(s, id.String())
if err != nil { if err != nil {
cmd.global.Warnf("LoadKey() failed: %v\n", err) cmd.global.Warnf("LoadKey() failed: %v\n", err)
@ -82,7 +81,7 @@ func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error {
return restic.Fatal("refusing to remove key currently used to access repository") return restic.Fatal("refusing to remove key currently used to access repository")
} }
err := repo.Backend().Remove(backend.Key, name) err := repo.Backend().Remove(restic.KeyFile, name)
if err != nil { if err != nil {
return err return err
} }
@ -97,7 +96,7 @@ func (cmd CmdKey) changePassword(repo *repository.Repository) error {
return restic.Fatalf("creating new key failed: %v\n", err) return restic.Fatalf("creating new key failed: %v\n", err)
} }
err = repo.Backend().Remove(backend.Key, repo.KeyName()) err = repo.Backend().Remove(restic.KeyFile, repo.KeyName())
if err != nil { if err != nil {
return err return err
} }
@ -145,7 +144,7 @@ func (cmd CmdKey) Execute(args []string) error {
return err return err
} }
id, err := backend.Find(repo.Backend(), backend.Key, args[1]) id, err := restic.Find(repo.Backend(), restic.KeyFile, args[1])
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,9 +1,6 @@
package main package main
import ( import "restic"
"restic"
"restic/backend"
)
type CmdList struct { type CmdList struct {
global *GlobalOptions global *GlobalOptions
@ -41,31 +38,32 @@ func (cmd CmdList) Execute(args []string) error {
} }
} }
var t backend.Type var t restic.FileType
switch args[0] { switch args[0] {
case "blobs": // case "blobs":
err = repo.LoadIndex() // restic.Lister
if err != nil { // err = repo.LoadIndex()
return err // if err != nil {
} // return err
// }
for _, idx := range repo.Index().All() { // for _, idx := range repo.Index().All() {
for blob := range idx.Each(nil) { // for blob := range idx.Each(nil) {
cmd.global.Printf("%s\n", blob.ID) // cmd.global.Printf("%s\n", blob.ID)
} // }
} // }
return nil // return nil
case "packs": case "packs":
t = backend.Data t = restic.DataFile
case "index": case "index":
t = backend.Index t = restic.IndexFile
case "snapshots": case "snapshots":
t = backend.Snapshot t = restic.SnapshotFile
case "keys": case "keys":
t = backend.Key t = restic.KeyFile
case "locks": case "locks":
t = backend.Lock t = restic.LockFile
default: default:
return restic.Fatal("invalid type") return restic.Fatal("invalid type")
} }

View File

@ -6,7 +6,6 @@ import (
"path/filepath" "path/filepath"
"restic" "restic"
"restic/backend"
"restic/repository" "restic/repository"
) )
@ -31,7 +30,7 @@ func (cmd CmdLs) printNode(prefix string, n *restic.Node) string {
return filepath.Join(prefix, n.Name) return filepath.Join(prefix, n.Name)
} }
switch n.Type { switch n.FileType {
case "file": case "file":
return fmt.Sprintf("%s %5d %5d %6d %s %s", return fmt.Sprintf("%s %5d %5d %6d %s %s",
n.Mode, n.UID, n.GID, n.Size, n.ModTime, filepath.Join(prefix, n.Name)) n.Mode, n.UID, n.GID, n.Size, n.ModTime, filepath.Join(prefix, n.Name))
@ -42,11 +41,11 @@ func (cmd CmdLs) printNode(prefix string, n *restic.Node) string {
return fmt.Sprintf("%s %5d %5d %6d %s %s -> %s", return fmt.Sprintf("%s %5d %5d %6d %s %s -> %s",
n.Mode|os.ModeSymlink, n.UID, n.GID, n.Size, n.ModTime, filepath.Join(prefix, n.Name), n.LinkTarget) n.Mode|os.ModeSymlink, n.UID, n.GID, n.Size, n.ModTime, filepath.Join(prefix, n.Name), n.LinkTarget)
default: default:
return fmt.Sprintf("<Node(%s) %s>", n.Type, n.Name) return fmt.Sprintf("<Node(%s) %s>", n.FileType, n.Name)
} }
} }
func (cmd CmdLs) printTree(prefix string, repo *repository.Repository, id backend.ID) error { func (cmd CmdLs) printTree(prefix string, repo *repository.Repository, id restic.ID) error {
tree, err := restic.LoadTree(repo, id) tree, err := restic.LoadTree(repo, id)
if err != nil { if err != nil {
return err return err
@ -55,7 +54,7 @@ func (cmd CmdLs) printTree(prefix string, repo *repository.Repository, id backen
for _, entry := range tree.Nodes { for _, entry := range tree.Nodes {
cmd.global.Printf(cmd.printNode(prefix, entry) + "\n") cmd.global.Printf(cmd.printNode(prefix, entry) + "\n")
if entry.Type == "dir" && entry.Subtree != nil { if entry.FileType == "dir" && entry.Subtree != nil {
err = cmd.printTree(filepath.Join(prefix, entry.Name), repo, *entry.Subtree) err = cmd.printTree(filepath.Join(prefix, entry.Name), repo, *entry.Subtree)
if err != nil { if err != nil {
return err return err

View File

@ -4,10 +4,8 @@ import (
"fmt" "fmt"
"os" "os"
"restic" "restic"
"restic/backend"
"restic/debug" "restic/debug"
"restic/index" "restic/index"
"restic/pack"
"restic/repository" "restic/repository"
"time" "time"
@ -94,7 +92,7 @@ func (cmd CmdPrune) Execute(args []string) error {
} }
cmd.global.Verbosef("counting files in repo\n") cmd.global.Verbosef("counting files in repo\n")
for _ = range repo.List(backend.Data, done) { for _ = range repo.List(restic.DataFile, done) {
stats.packs++ stats.packs++
} }
@ -112,7 +110,7 @@ func (cmd CmdPrune) Execute(args []string) error {
cmd.global.Verbosef("repository contains %v packs (%v blobs) with %v bytes\n", cmd.global.Verbosef("repository contains %v packs (%v blobs) with %v bytes\n",
len(idx.Packs), len(idx.Blobs), formatBytes(uint64(stats.bytes))) len(idx.Packs), len(idx.Blobs), formatBytes(uint64(stats.bytes)))
blobCount := make(map[pack.Handle]int) blobCount := make(map[restic.BlobHandle]int)
duplicateBlobs := 0 duplicateBlobs := 0
duplicateBytes := 0 duplicateBytes := 0
@ -120,7 +118,7 @@ func (cmd CmdPrune) Execute(args []string) error {
for _, p := range idx.Packs { for _, p := range idx.Packs {
for _, entry := range p.Entries { for _, entry := range p.Entries {
stats.blobs++ stats.blobs++
h := pack.Handle{ID: entry.ID, Type: entry.Type} h := restic.BlobHandle{ID: entry.ID, Type: entry.Type}
blobCount[h]++ blobCount[h]++
if blobCount[h] > 1 { if blobCount[h] > 1 {
@ -144,8 +142,8 @@ func (cmd CmdPrune) Execute(args []string) error {
cmd.global.Verbosef("find data that is still in use for %d snapshots\n", stats.snapshots) cmd.global.Verbosef("find data that is still in use for %d snapshots\n", stats.snapshots)
usedBlobs := pack.NewBlobSet() usedBlobs := restic.NewBlobSet()
seenBlobs := pack.NewBlobSet() seenBlobs := restic.NewBlobSet()
bar = newProgressMax(cmd.global.ShowProgress(), uint64(len(snapshots)), "snapshots") bar = newProgressMax(cmd.global.ShowProgress(), uint64(len(snapshots)), "snapshots")
bar.Start() bar.Start()
@ -165,7 +163,7 @@ func (cmd CmdPrune) Execute(args []string) error {
cmd.global.Verbosef("found %d of %d data blobs still in use\n", len(usedBlobs), stats.blobs) cmd.global.Verbosef("found %d of %d data blobs still in use\n", len(usedBlobs), stats.blobs)
// find packs that need a rewrite // find packs that need a rewrite
rewritePacks := backend.NewIDSet() rewritePacks := restic.NewIDSet()
for h, blob := range idx.Blobs { for h, blob := range idx.Blobs {
if !usedBlobs.Has(h) { if !usedBlobs.Has(h) {
rewritePacks.Merge(blob.Packs) rewritePacks.Merge(blob.Packs)
@ -178,11 +176,11 @@ func (cmd CmdPrune) Execute(args []string) error {
} }
// find packs that are unneeded // find packs that are unneeded
removePacks := backend.NewIDSet() removePacks := restic.NewIDSet()
nextPack: nextPack:
for packID, p := range idx.Packs { for packID, p := range idx.Packs {
for _, blob := range p.Entries { for _, blob := range p.Entries {
h := pack.Handle{ID: blob.ID, Type: blob.Type} h := restic.BlobHandle{ID: blob.ID, Type: blob.Type}
if usedBlobs.Has(h) { if usedBlobs.Has(h) {
continue nextPack continue nextPack
} }
@ -205,7 +203,7 @@ nextPack:
} }
for packID := range removePacks { for packID := range removePacks {
err = repo.Backend().Remove(backend.Data, packID.String()) err = repo.Backend().Remove(restic.DataFile, packID.String())
if err != nil { if err != nil {
cmd.global.Warnf("unable to remove file %v from the repository\n", packID.Str()) cmd.global.Warnf("unable to remove file %v from the repository\n", packID.Str())
} }
@ -214,7 +212,7 @@ nextPack:
cmd.global.Verbosef("creating new index\n") cmd.global.Verbosef("creating new index\n")
stats.packs = 0 stats.packs = 0
for _ = range repo.List(backend.Data, done) { for _ = range repo.List(restic.DataFile, done) {
stats.packs++ stats.packs++
} }
bar = newProgressMax(cmd.global.ShowProgress(), uint64(stats.packs), "packs") bar = newProgressMax(cmd.global.ShowProgress(), uint64(stats.packs), "packs")
@ -223,9 +221,9 @@ nextPack:
return err return err
} }
var supersedes backend.IDs var supersedes restic.IDs
for idxID := range repo.List(backend.Index, done) { for idxID := range repo.List(restic.IndexFile, done) {
err := repo.Backend().Remove(backend.Index, idxID.String()) err := repo.Backend().Remove(restic.IndexFile, idxID.String())
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "unable to remove index %v: %v\n", idxID.Str(), err) fmt.Fprintf(os.Stderr, "unable to remove index %v: %v\n", idxID.Str(), err)
} }

View File

@ -2,7 +2,6 @@ package main
import ( import (
"restic" "restic"
"restic/backend"
"restic/debug" "restic/debug"
"restic/filter" "restic/filter"
) )
@ -66,7 +65,7 @@ func (cmd CmdRestore) Execute(args []string) error {
return err return err
} }
var id backend.ID var id restic.ID
if snapshotIDString == "latest" { if snapshotIDString == "latest" {
id, err = restic.FindLatestSnapshot(repo, cmd.Paths, cmd.Host) id, err = restic.FindLatestSnapshot(repo, cmd.Paths, cmd.Host)

View File

@ -9,7 +9,6 @@ import (
"strings" "strings"
"restic" "restic"
"restic/backend"
) )
type Table struct { type Table struct {
@ -92,7 +91,7 @@ func (cmd CmdSnapshots) Execute(args []string) error {
defer close(done) defer close(done)
list := []*restic.Snapshot{} list := []*restic.Snapshot{}
for id := range repo.List(backend.Snapshot, done) { for id := range repo.List(restic.SnapshotFile, done) {
sn, err := restic.LoadSnapshot(repo, id) sn, err := restic.LoadSnapshot(repo, id)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "error loading snapshot %s: %v\n", id, err) fmt.Fprintf(os.Stderr, "error loading snapshot %s: %v\n", id, err)
@ -115,7 +114,7 @@ func (cmd CmdSnapshots) Execute(args []string) error {
} }
plen, err := repo.PrefixLength(backend.Snapshot) plen, err := repo.PrefixLength(restic.SnapshotFile)
if err != nil { if err != nil {
return err return err
} }

View File

@ -9,7 +9,6 @@ import (
"strings" "strings"
"syscall" "syscall"
"restic/backend"
"restic/backend/local" "restic/backend/local"
"restic/backend/rest" "restic/backend/rest"
"restic/backend/s3" "restic/backend/s3"
@ -270,7 +269,7 @@ func (o GlobalOptions) OpenRepository() (*repository.Repository, error) {
} }
// Open the backend specified by a location config. // Open the backend specified by a location config.
func open(s string) (backend.Backend, error) { func open(s string) (restic.Backend, error) {
debug.Log("open", "parsing location %v", s) debug.Log("open", "parsing location %v", s)
loc, err := location.Parse(s) loc, err := location.Parse(s)
if err != nil { if err != nil {
@ -305,7 +304,7 @@ func open(s string) (backend.Backend, error) {
} }
// Create the backend specified by URI. // Create the backend specified by URI.
func create(s string) (backend.Backend, error) { func create(s string) (restic.Backend, error) {
debug.Log("open", "parsing location %v", s) debug.Log("open", "parsing location %v", s)
loc, err := location.Parse(s) loc, err := location.Parse(s)
if err != nil { if err != nil {

View File

@ -71,7 +71,7 @@ func TestMount(t *testing.T) {
t.Skip("Skipping fuse tests") t.Skip("Skipping fuse tests")
} }
checkSnapshots := func(repo *repository.Repository, mountpoint string, snapshotIDs []backend.ID) { checkSnapshots := func(repo *repository.Repository, mountpoint string, snapshotIDs []restic.ID) {
snapshotsDir, err := os.Open(filepath.Join(mountpoint, "snapshots")) snapshotsDir, err := os.Open(filepath.Join(mountpoint, "snapshots"))
OK(t, err) OK(t, err)
namesInSnapshots, err := snapshotsDir.Readdirnames(-1) namesInSnapshots, err := snapshotsDir.Readdirnames(-1)
@ -123,7 +123,7 @@ func TestMount(t *testing.T) {
Assert(t, len(names) == 1 && names[0] == "snapshots", `The fuse virtual directory "snapshots" doesn't exist`) Assert(t, len(names) == 1 && names[0] == "snapshots", `The fuse virtual directory "snapshots" doesn't exist`)
OK(t, mountpointDir.Close()) OK(t, mountpointDir.Close())
checkSnapshots(repo, mountpoint, []backend.ID{}) checkSnapshots(repo, mountpoint, []restic.ID{})
datafile := filepath.Join("testdata", "backup-data.tar.gz") datafile := filepath.Join("testdata", "backup-data.tar.gz")
fd, err := os.Open(datafile) fd, err := os.Open(datafile)

View File

@ -25,8 +25,8 @@ import (
. "restic/test" . "restic/test"
) )
func parseIDsFromReader(t testing.TB, rd io.Reader) backend.IDs { func parseIDsFromReader(t testing.TB, rd io.Reader) restic.IDs {
IDs := backend.IDs{} IDs := restic.IDs{}
sc := bufio.NewScanner(rd) sc := bufio.NewScanner(rd)
for sc.Scan() { for sc.Scan() {
@ -51,11 +51,11 @@ func cmdInit(t testing.TB, global GlobalOptions) {
t.Logf("repository initialized at %v", global.Repo) t.Logf("repository initialized at %v", global.Repo)
} }
func cmdBackup(t testing.TB, global GlobalOptions, target []string, parentID *backend.ID) { func cmdBackup(t testing.TB, global GlobalOptions, target []string, parentID *restic.ID) {
cmdBackupExcludes(t, global, target, parentID, nil) cmdBackupExcludes(t, global, target, parentID, nil)
} }
func cmdBackupExcludes(t testing.TB, global GlobalOptions, target []string, parentID *backend.ID, excludes []string) { func cmdBackupExcludes(t testing.TB, global GlobalOptions, target []string, parentID *restic.ID, excludes []string) {
cmd := &CmdBackup{global: &global, Excludes: excludes} cmd := &CmdBackup{global: &global, Excludes: excludes}
if parentID != nil { if parentID != nil {
cmd.Parent = parentID.String() cmd.Parent = parentID.String()
@ -66,19 +66,19 @@ func cmdBackupExcludes(t testing.TB, global GlobalOptions, target []string, pare
OK(t, cmd.Execute(target)) OK(t, cmd.Execute(target))
} }
func cmdList(t testing.TB, global GlobalOptions, tpe string) backend.IDs { func cmdList(t testing.TB, global GlobalOptions, tpe string) restic.IDs {
cmd := &CmdList{global: &global} cmd := &CmdList{global: &global}
return executeAndParseIDs(t, cmd, tpe) return executeAndParseIDs(t, cmd, tpe)
} }
func executeAndParseIDs(t testing.TB, cmd *CmdList, args ...string) backend.IDs { func executeAndParseIDs(t testing.TB, cmd *CmdList, args ...string) restic.IDs {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
cmd.global.stdout = buf cmd.global.stdout = buf
OK(t, cmd.Execute(args)) OK(t, cmd.Execute(args))
return parseIDsFromReader(t, buf) return parseIDsFromReader(t, buf)
} }
func cmdRestore(t testing.TB, global GlobalOptions, dir string, snapshotID backend.ID) { func cmdRestore(t testing.TB, global GlobalOptions, dir string, snapshotID restic.ID) {
cmdRestoreExcludes(t, global, dir, snapshotID, nil) cmdRestoreExcludes(t, global, dir, snapshotID, nil)
} }
@ -87,12 +87,12 @@ func cmdRestoreLatest(t testing.TB, global GlobalOptions, dir string, paths []st
OK(t, cmd.Execute([]string{"latest"})) OK(t, cmd.Execute([]string{"latest"}))
} }
func cmdRestoreExcludes(t testing.TB, global GlobalOptions, dir string, snapshotID backend.ID, excludes []string) { func cmdRestoreExcludes(t testing.TB, global GlobalOptions, dir string, snapshotID restic.ID, excludes []string) {
cmd := &CmdRestore{global: &global, Target: dir, Exclude: excludes} cmd := &CmdRestore{global: &global, Target: dir, Exclude: excludes}
OK(t, cmd.Execute([]string{snapshotID.String()})) OK(t, cmd.Execute([]string{snapshotID.String()}))
} }
func cmdRestoreIncludes(t testing.TB, global GlobalOptions, dir string, snapshotID backend.ID, includes []string) { func cmdRestoreIncludes(t testing.TB, global GlobalOptions, dir string, snapshotID restic.ID, includes []string) {
cmd := &CmdRestore{global: &global, Target: dir, Include: includes} cmd := &CmdRestore{global: &global, Target: dir, Include: includes}
OK(t, cmd.Execute([]string{snapshotID.String()})) OK(t, cmd.Execute([]string{snapshotID.String()}))
} }

View File

@ -1,74 +0,0 @@
package backend
import (
"restic"
"github.com/pkg/errors"
)
// ErrNoIDPrefixFound is returned by Find() when no ID for the given prefix
// could be found.
var ErrNoIDPrefixFound = errors.New("no ID found")
// ErrMultipleIDMatches is returned by Find() when multiple IDs with the given
// prefix are found.
var ErrMultipleIDMatches = errors.New("multiple IDs with prefix found")
// 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 more than one is found, nil and ErrMultipleIDMatches is returned.
func Find(be restic.Lister, t restic.FileType, prefix string) (string, error) {
done := make(chan struct{})
defer close(done)
match := ""
// TODO: optimize by sorting list etc.
for name := range be.List(t, done) {
if prefix == name[:len(prefix)] {
if match == "" {
match = name
} else {
return "", ErrMultipleIDMatches
}
}
}
if match != "" {
return match, nil
}
return "", ErrNoIDPrefixFound
}
const minPrefixLength = 8
// PrefixLength returns the number of bytes required so that all prefixes of
// all names of type t are unique.
func PrefixLength(be restic.Lister, t restic.FileType) (int, error) {
done := make(chan struct{})
defer close(done)
// load all IDs of the given type
list := make([]string, 0, 100)
for name := range be.List(t, done) {
list = append(list, name)
}
// select prefixes of length l, test if the last one is the same as the current one
outer:
for l := minPrefixLength; l < restic.IDSize; l++ {
var last string
for _, name := range list {
if last == name[:l] {
continue outer
}
last = name[:l]
}
return l, nil
}
return restic.IDSize, nil
}

View File

@ -1,62 +0,0 @@
package backend_test
import (
"restic"
"testing"
"restic/backend"
. "restic/test"
)
type mockBackend struct {
list func(restic.FileType, <-chan struct{}) <-chan string
}
func (m mockBackend) List(t restic.FileType, done <-chan struct{}) <-chan string {
return m.list(t, done)
}
var samples = restic.IDs{
ParseID("20bdc1402a6fc9b633aaffffffffffffffffffffffffffffffffffffffffffff"),
ParseID("20bdc1402a6fc9b633ccd578c4a92d0f4ef1a457fa2e16c596bc73fb409d6cc0"),
ParseID("20bdc1402a6fc9b633ffffffffffffffffffffffffffffffffffffffffffffff"),
ParseID("20ff988befa5fc40350f00d531a767606efefe242c837aaccb80673f286be53d"),
ParseID("326cb59dfe802304f96ee9b5b9af93bdee73a30f53981e5ec579aedb6f1d0f07"),
ParseID("86b60b9594d1d429c4aa98fa9562082cabf53b98c7dc083abe5dae31074dd15a"),
ParseID("96c8dbe225079e624b5ce509f5bd817d1453cd0a85d30d536d01b64a8669aeae"),
ParseID("fa31d65b87affcd167b119e9d3d2a27b8236ca4836cb077ed3e96fcbe209b792"),
}
func TestPrefixLength(t *testing.T) {
list := samples
m := mockBackend{}
m.list = func(t restic.FileType, done <-chan struct{}) <-chan string {
ch := make(chan string)
go func() {
defer close(ch)
for _, id := range list {
select {
case ch <- id.String():
case <-done:
return
}
}
}()
return ch
}
l, err := backend.PrefixLength(m, restic.SnapshotFile)
OK(t, err)
Equals(t, 19, l)
list = samples[:3]
l, err = backend.PrefixLength(m, restic.SnapshotFile)
OK(t, err)
Equals(t, 19, l)
list = samples[3:]
l, err = backend.PrefixLength(m, restic.SnapshotFile)
OK(t, err)
Equals(t, 8, l)
}

View File

@ -0,0 +1,70 @@
package restic
import (
"testing"
)
type mockBackend struct {
list func(FileType, <-chan struct{}) <-chan string
}
func (m mockBackend) List(t FileType, done <-chan struct{}) <-chan string {
return m.list(t, done)
}
var samples = IDs{
TestParseID("20bdc1402a6fc9b633aaffffffffffffffffffffffffffffffffffffffffffff"),
TestParseID("20bdc1402a6fc9b633ccd578c4a92d0f4ef1a457fa2e16c596bc73fb409d6cc0"),
TestParseID("20bdc1402a6fc9b633ffffffffffffffffffffffffffffffffffffffffffffff"),
TestParseID("20ff988befa5fc40350f00d531a767606efefe242c837aaccb80673f286be53d"),
TestParseID("326cb59dfe802304f96ee9b5b9af93bdee73a30f53981e5ec579aedb6f1d0f07"),
TestParseID("86b60b9594d1d429c4aa98fa9562082cabf53b98c7dc083abe5dae31074dd15a"),
TestParseID("96c8dbe225079e624b5ce509f5bd817d1453cd0a85d30d536d01b64a8669aeae"),
TestParseID("fa31d65b87affcd167b119e9d3d2a27b8236ca4836cb077ed3e96fcbe209b792"),
}
func TestPrefixLength(t *testing.T) {
list := samples
m := mockBackend{}
m.list = func(t FileType, done <-chan struct{}) <-chan string {
ch := make(chan string)
go func() {
defer close(ch)
for _, id := range list {
select {
case ch <- id.String():
case <-done:
return
}
}
}()
return ch
}
l, err := PrefixLength(m, SnapshotFile)
if err != nil {
t.Error(err)
}
if l != 19 {
t.Errorf("wrong prefix length returned, want %d, got %d", 19, l)
}
list = samples[:3]
l, err = PrefixLength(m, SnapshotFile)
if err != nil {
t.Error(err)
}
if l != 19 {
t.Errorf("wrong prefix length returned, want %d, got %d", 19, l)
}
list = samples[3:]
l, err = PrefixLength(m, SnapshotFile)
if err != nil {
t.Error(err)
}
if l != 8 {
t.Errorf("wrong prefix length returned, want %d, got %d", 8, l)
}
}

View File

@ -153,3 +153,16 @@ func FindLatestSnapshot(repo Repository, targets []string, source string) (ID, e
return latestID, nil return latestID, nil
} }
// FindSnapshot takes a string and tries to find a snapshot whose ID matches
// the string as closely as possible.
func FindSnapshot(repo Repository, s string) (ID, error) {
// find snapshot id with prefix
name, err := Find(repo.Backend(), SnapshotFile, s)
if err != nil {
return ID{}, err
}
return ParseID(name)
}