mirror of
https://github.com/octoleo/restic.git
synced 2024-11-22 21:05:10 +00:00
Copy interfaces and basic types to package restic/
This commit is contained in:
parent
bfdd26c541
commit
82c2dafb23
@ -83,7 +83,7 @@ func ArchiveReader(repo *repository.Repository, p *Progress, rd io.Reader, name
|
||||
Name: name,
|
||||
AccessTime: time.Now(),
|
||||
ModTime: time.Now(),
|
||||
Type: "file",
|
||||
FileType: "file",
|
||||
Mode: 0644,
|
||||
Size: fileSize,
|
||||
UID: sn.UID,
|
||||
|
@ -307,7 +307,7 @@ func (arch *Archiver) fileWorker(wg *sync.WaitGroup, p *Progress, done <-chan st
|
||||
}
|
||||
|
||||
// otherwise read file normally
|
||||
if node.Type == "file" && len(node.Content) == 0 {
|
||||
if node.FileType == "file" && len(node.Content) == 0 {
|
||||
debug.Log("Archiver.fileWorker", " read and save %v, content: %v", e.Path(), node.Content)
|
||||
err = arch.SaveFile(p, node)
|
||||
if err != nil {
|
||||
@ -374,7 +374,7 @@ func (arch *Archiver) dirWorker(wg *sync.WaitGroup, p *Progress, done <-chan str
|
||||
node := res.(*Node)
|
||||
tree.Insert(node)
|
||||
|
||||
if node.Type == "dir" {
|
||||
if node.FileType == "dir" {
|
||||
debug.Log("Archiver.dirWorker", "got tree node for %s: %v", node.path, node.Subtree)
|
||||
|
||||
if node.Subtree.IsNull() {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"restic"
|
||||
"restic/backend"
|
||||
"restic/mock"
|
||||
"restic/pack"
|
||||
"restic/repository"
|
||||
)
|
||||
@ -36,30 +37,30 @@ func randomID() backend.ID {
|
||||
}
|
||||
|
||||
// forgetfulBackend returns a backend that forgets everything.
|
||||
func forgetfulBackend() backend.Backend {
|
||||
be := &backend.MockBackend{}
|
||||
func forgetfulBackend() restic.Backend {
|
||||
be := &mock.Backend{}
|
||||
|
||||
be.TestFn = func(t backend.Type, name string) (bool, error) {
|
||||
be.TestFn = func(t restic.FileType, name string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
be.LoadFn = func(h backend.Handle, p []byte, off int64) (int, error) {
|
||||
be.LoadFn = func(h restic.Handle, p []byte, off int64) (int, error) {
|
||||
return 0, errors.New("not found")
|
||||
}
|
||||
|
||||
be.SaveFn = func(h backend.Handle, p []byte) error {
|
||||
be.SaveFn = func(h restic.Handle, p []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
be.StatFn = func(h backend.Handle) (backend.BlobInfo, error) {
|
||||
return backend.BlobInfo{}, errors.New("not found")
|
||||
be.StatFn = func(h restic.Handle) (restic.BlobInfo, error) {
|
||||
return restic.BlobInfo{}, errors.New("not found")
|
||||
}
|
||||
|
||||
be.RemoveFn = func(t backend.Type, name string) error {
|
||||
be.RemoveFn = func(t restic.FileType, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
be.ListFn = func(t backend.Type, done <-chan struct{}) <-chan string {
|
||||
be.ListFn = func(t restic.FileType, done <-chan struct{}) <-chan string {
|
||||
ch := make(chan string)
|
||||
close(ch)
|
||||
return ch
|
||||
|
55
src/restic/backend.go
Normal file
55
src/restic/backend.go
Normal file
@ -0,0 +1,55 @@
|
||||
package restic
|
||||
|
||||
// FileType is the type of a file in the backend.
|
||||
type FileType string
|
||||
|
||||
// These are the different data types a backend can store.
|
||||
const (
|
||||
DataFile FileType = "data"
|
||||
KeyFile = "key"
|
||||
LockFile = "lock"
|
||||
SnapshotFile = "snapshot"
|
||||
IndexFile = "index"
|
||||
ConfigFile = "config"
|
||||
)
|
||||
|
||||
// Backend is used to store and access data.
|
||||
type Backend interface {
|
||||
// Location returns a string that describes the type and location of the
|
||||
// repository.
|
||||
Location() string
|
||||
|
||||
// Test a boolean value whether a Blob with the name and type exists.
|
||||
Test(t FileType, name string) (bool, error)
|
||||
|
||||
// Remove removes a Blob with type t and name.
|
||||
Remove(t FileType, name string) error
|
||||
|
||||
// Close the backend
|
||||
Close() error
|
||||
|
||||
// Load returns the data stored in the backend for h at the given offset
|
||||
// and saves it in p. Load has the same semantics as io.ReaderAt, except
|
||||
// that a negative offset is also allowed. In this case it references a
|
||||
// position relative to the end of the file (similar to Seek()).
|
||||
Load(h Handle, p []byte, off int64) (int, error)
|
||||
|
||||
// Save stores the data in the backend under the given handle.
|
||||
Save(h Handle, p []byte) error
|
||||
|
||||
// Stat returns information about the blob identified by h.
|
||||
Stat(h Handle) (BlobInfo, error)
|
||||
|
||||
// List returns a channel that yields all names of blobs of type t in an
|
||||
// arbitrary order. A goroutine is started for this. If the channel done is
|
||||
// closed, sending stops.
|
||||
List(t FileType, done <-chan struct{}) <-chan string
|
||||
|
||||
// Delete the complete repository.
|
||||
Delete() error
|
||||
}
|
||||
|
||||
// BlobInfo is returned by Stat() and contains information about a stored blob.
|
||||
type BlobInfo struct {
|
||||
Size int64
|
||||
}
|
@ -17,13 +17,14 @@ type entry struct {
|
||||
|
||||
type memMap map[entry][]byte
|
||||
|
||||
// make sure that MemoryBackend implements backend.Backend
|
||||
var _ backend.Backend = &MemoryBackend{}
|
||||
|
||||
// MemoryBackend is a mock backend that uses a map for storing all data in
|
||||
// memory. This should only be used for tests.
|
||||
type MemoryBackend struct {
|
||||
data memMap
|
||||
m sync.Mutex
|
||||
|
||||
backend.MockBackend
|
||||
}
|
||||
|
||||
// New returns a new backend that saves all data in a map in memory.
|
||||
@ -32,60 +33,13 @@ func New() *MemoryBackend {
|
||||
data: make(memMap),
|
||||
}
|
||||
|
||||
be.MockBackend.TestFn = func(t backend.Type, name string) (bool, error) {
|
||||
return memTest(be, t, name)
|
||||
}
|
||||
|
||||
be.MockBackend.LoadFn = func(h backend.Handle, p []byte, off int64) (int, error) {
|
||||
return memLoad(be, h, p, off)
|
||||
}
|
||||
|
||||
be.MockBackend.SaveFn = func(h backend.Handle, p []byte) error {
|
||||
return memSave(be, h, p)
|
||||
}
|
||||
|
||||
be.MockBackend.StatFn = func(h backend.Handle) (backend.BlobInfo, error) {
|
||||
return memStat(be, h)
|
||||
}
|
||||
|
||||
be.MockBackend.RemoveFn = func(t backend.Type, name string) error {
|
||||
return memRemove(be, t, name)
|
||||
}
|
||||
|
||||
be.MockBackend.ListFn = func(t backend.Type, done <-chan struct{}) <-chan string {
|
||||
return memList(be, t, done)
|
||||
}
|
||||
|
||||
be.MockBackend.DeleteFn = func() error {
|
||||
be.m.Lock()
|
||||
defer be.m.Unlock()
|
||||
|
||||
be.data = make(memMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
be.MockBackend.LocationFn = func() string {
|
||||
return "Memory Backend"
|
||||
}
|
||||
|
||||
debug.Log("MemoryBackend.New", "created new memory backend")
|
||||
|
||||
return be
|
||||
}
|
||||
|
||||
func (be *MemoryBackend) insert(t backend.Type, name string, data []byte) error {
|
||||
be.m.Lock()
|
||||
defer be.m.Unlock()
|
||||
|
||||
if _, ok := be.data[entry{t, name}]; ok {
|
||||
return errors.New("already present")
|
||||
}
|
||||
|
||||
be.data[entry{t, name}] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func memTest(be *MemoryBackend, t backend.Type, name string) (bool, error) {
|
||||
// Test returns whether a file exists.
|
||||
func (be *MemoryBackend) Test(t backend.Type, name string) (bool, error) {
|
||||
be.m.Lock()
|
||||
defer be.m.Unlock()
|
||||
|
||||
@ -98,7 +52,8 @@ func memTest(be *MemoryBackend, t backend.Type, name string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func memLoad(be *MemoryBackend, h backend.Handle, p []byte, off int64) (int, error) {
|
||||
// Load reads data from the backend.
|
||||
func (be *MemoryBackend) Load(h backend.Handle, p []byte, off int64) (int, error) {
|
||||
if err := h.Valid(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -137,7 +92,8 @@ func memLoad(be *MemoryBackend, h backend.Handle, p []byte, off int64) (int, err
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func memSave(be *MemoryBackend, h backend.Handle, p []byte) error {
|
||||
// Save adds new Data to the backend.
|
||||
func (be *MemoryBackend) Save(h backend.Handle, p []byte) error {
|
||||
if err := h.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -161,7 +117,8 @@ func memSave(be *MemoryBackend, h backend.Handle, p []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func memStat(be *MemoryBackend, h backend.Handle) (backend.BlobInfo, error) {
|
||||
// Stat returns information about a file in the backend.
|
||||
func (be *MemoryBackend) Stat(h backend.Handle) (backend.BlobInfo, error) {
|
||||
be.m.Lock()
|
||||
defer be.m.Unlock()
|
||||
|
||||
@ -183,7 +140,8 @@ func memStat(be *MemoryBackend, h backend.Handle) (backend.BlobInfo, error) {
|
||||
return backend.BlobInfo{Size: int64(len(e))}, nil
|
||||
}
|
||||
|
||||
func memRemove(be *MemoryBackend, t backend.Type, name string) error {
|
||||
// Remove deletes a file from the backend.
|
||||
func (be *MemoryBackend) Remove(t backend.Type, name string) error {
|
||||
be.m.Lock()
|
||||
defer be.m.Unlock()
|
||||
|
||||
@ -198,7 +156,8 @@ func memRemove(be *MemoryBackend, t backend.Type, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func memList(be *MemoryBackend, t backend.Type, done <-chan struct{}) <-chan string {
|
||||
// List returns a channel which yields entries from the backend.
|
||||
func (be *MemoryBackend) List(t backend.Type, done <-chan struct{}) <-chan string {
|
||||
be.m.Lock()
|
||||
defer be.m.Unlock()
|
||||
|
||||
@ -227,3 +186,22 @@ func memList(be *MemoryBackend, t backend.Type, done <-chan struct{}) <-chan str
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// Location returns the location of the backend (RAM).
|
||||
func (be *MemoryBackend) Location() string {
|
||||
return "RAM"
|
||||
}
|
||||
|
||||
// Delete removes all data in the backend.
|
||||
func (be *MemoryBackend) Delete() error {
|
||||
be.m.Lock()
|
||||
defer be.m.Unlock()
|
||||
|
||||
be.data = make(memMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the backend.
|
||||
func (be *MemoryBackend) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
package backend
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// MockBackend implements a backend whose functions can be specified. This
|
||||
// should only be used for tests.
|
||||
type MockBackend struct {
|
||||
CloseFn func() error
|
||||
LoadFn func(h Handle, p []byte, off int64) (int, error)
|
||||
SaveFn func(h Handle, p []byte) error
|
||||
StatFn func(h Handle) (BlobInfo, error)
|
||||
ListFn func(Type, <-chan struct{}) <-chan string
|
||||
RemoveFn func(Type, string) error
|
||||
TestFn func(Type, string) (bool, error)
|
||||
DeleteFn func() error
|
||||
LocationFn func() string
|
||||
}
|
||||
|
||||
// Close the backend.
|
||||
func (m *MockBackend) Close() error {
|
||||
if m.CloseFn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.CloseFn()
|
||||
}
|
||||
|
||||
// Location returns a location string.
|
||||
func (m *MockBackend) Location() string {
|
||||
if m.LocationFn == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return m.LocationFn()
|
||||
}
|
||||
|
||||
// Load loads data from the backend.
|
||||
func (m *MockBackend) Load(h Handle, p []byte, off int64) (int, error) {
|
||||
if m.LoadFn == nil {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.LoadFn(h, p, off)
|
||||
}
|
||||
|
||||
// Save data in the backend.
|
||||
func (m *MockBackend) Save(h Handle, p []byte) error {
|
||||
if m.SaveFn == nil {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.SaveFn(h, p)
|
||||
}
|
||||
|
||||
// Stat an object in the backend.
|
||||
func (m *MockBackend) Stat(h Handle) (BlobInfo, error) {
|
||||
if m.StatFn == nil {
|
||||
return BlobInfo{}, errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.StatFn(h)
|
||||
}
|
||||
|
||||
// List items of type t.
|
||||
func (m *MockBackend) List(t Type, done <-chan struct{}) <-chan string {
|
||||
if m.ListFn == nil {
|
||||
ch := make(chan string)
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
return m.ListFn(t, done)
|
||||
}
|
||||
|
||||
// Remove data from the backend.
|
||||
func (m *MockBackend) Remove(t Type, name string) error {
|
||||
if m.RemoveFn == nil {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.RemoveFn(t, name)
|
||||
}
|
||||
|
||||
// Test for the existence of a specific item.
|
||||
func (m *MockBackend) Test(t Type, name string) (bool, error) {
|
||||
if m.TestFn == nil {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.TestFn(t, name)
|
||||
}
|
||||
|
||||
// Delete all data.
|
||||
func (m *MockBackend) Delete() error {
|
||||
if m.DeleteFn == nil {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.DeleteFn()
|
||||
}
|
||||
|
||||
// Make sure that MockBackend implements the backend interface.
|
||||
var _ Backend = &MockBackend{}
|
@ -581,7 +581,7 @@ func (c *Checker) checkTree(id backend.ID, tree *restic.Tree) (errs []error) {
|
||||
var blobs []backend.ID
|
||||
|
||||
for _, node := range tree.Nodes {
|
||||
switch node.Type {
|
||||
switch node.FileType {
|
||||
case "file":
|
||||
if node.Content == nil {
|
||||
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("file %q has nil blob list", node.Name)})
|
||||
@ -609,7 +609,7 @@ func (c *Checker) checkTree(id backend.ID, tree *restic.Tree) (errs []error) {
|
||||
// nothing to check
|
||||
|
||||
default:
|
||||
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("node %q with invalid type %q", node.Name, node.Type)})
|
||||
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("node %q with invalid type %q", node.Name, node.FileType)})
|
||||
}
|
||||
|
||||
if node.Name == "" {
|
||||
|
@ -18,7 +18,7 @@ func FindUsedBlobs(repo *repository.Repository, treeID backend.ID, blobs pack.Bl
|
||||
}
|
||||
|
||||
for _, node := range tree.Nodes {
|
||||
switch node.Type {
|
||||
switch node.FileType {
|
||||
case "file":
|
||||
for _, blob := range node.Content {
|
||||
blobs.Insert(pack.Handle{ID: blob, Type: pack.Data})
|
||||
|
@ -51,7 +51,7 @@ func newDir(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*
|
||||
// replaceSpecialNodes replaces nodes with name "." and "/" by their contents.
|
||||
// Otherwise, the node is returned.
|
||||
func replaceSpecialNodes(repo *repository.Repository, node *restic.Node) ([]*restic.Node, error) {
|
||||
if node.Type != "dir" || node.Subtree == nil {
|
||||
if node.FileType != "dir" || node.Subtree == nil {
|
||||
return []*restic.Node{node}, nil
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
||||
|
||||
for _, node := range d.items {
|
||||
var typ fuse.DirentType
|
||||
switch node.Type {
|
||||
switch node.FileType {
|
||||
case "dir":
|
||||
typ = fuse.DT_Dir
|
||||
case "file":
|
||||
@ -150,7 +150,7 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||
debug.Log("dir.Lookup", " Lookup(%v) -> not found", name)
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
switch node.Type {
|
||||
switch node.FileType {
|
||||
case "dir":
|
||||
return newDir(d.repo, node, d.ownerIsRoot)
|
||||
case "file":
|
||||
@ -158,7 +158,7 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||
case "symlink":
|
||||
return newLink(d.repo, node, d.ownerIsRoot)
|
||||
default:
|
||||
debug.Log("dir.Lookup", " node %v has unknown type %v", name, node.Type)
|
||||
debug.Log("dir.Lookup", " node %v has unknown type %v", name, node.FileType)
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
}
|
||||
|
49
src/restic/handle.go
Normal file
49
src/restic/handle.go
Normal file
@ -0,0 +1,49 @@
|
||||
package restic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Handle is used to store and access data in a backend.
|
||||
type Handle struct {
|
||||
FileType FileType
|
||||
Name string
|
||||
}
|
||||
|
||||
func (h Handle) String() string {
|
||||
name := h.Name
|
||||
if len(name) > 10 {
|
||||
name = name[:10]
|
||||
}
|
||||
return fmt.Sprintf("<%s/%s>", h.FileType, name)
|
||||
}
|
||||
|
||||
// Valid returns an error if h is not valid.
|
||||
func (h Handle) Valid() error {
|
||||
if h.FileType == "" {
|
||||
return errors.New("type is empty")
|
||||
}
|
||||
|
||||
switch h.FileType {
|
||||
case DataFile:
|
||||
case KeyFile:
|
||||
case LockFile:
|
||||
case SnapshotFile:
|
||||
case IndexFile:
|
||||
case ConfigFile:
|
||||
default:
|
||||
return errors.Errorf("invalid Type %q", h.FileType)
|
||||
}
|
||||
|
||||
if h.FileType == ConfigFile {
|
||||
return nil
|
||||
}
|
||||
|
||||
if h.Name == "" {
|
||||
return errors.New("invalid Name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
28
src/restic/handle_test.go
Normal file
28
src/restic/handle_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package restic
|
||||
|
||||
import "testing"
|
||||
|
||||
var handleTests = []struct {
|
||||
h Handle
|
||||
valid bool
|
||||
}{
|
||||
{Handle{Name: "foo"}, false},
|
||||
{Handle{FileType: "foobar"}, false},
|
||||
{Handle{FileType: ConfigFile, Name: ""}, true},
|
||||
{Handle{FileType: DataFile, Name: ""}, false},
|
||||
{Handle{FileType: "", Name: "x"}, false},
|
||||
{Handle{FileType: LockFile, Name: "010203040506"}, true},
|
||||
}
|
||||
|
||||
func TestHandleValid(t *testing.T) {
|
||||
for i, test := range handleTests {
|
||||
err := test.h.Valid()
|
||||
if err != nil && test.valid {
|
||||
t.Errorf("test %v failed: error returned for valid handle: %v", i, err)
|
||||
}
|
||||
|
||||
if !test.valid && err == nil {
|
||||
t.Errorf("test %v failed: expected error for invalid handle not found", i)
|
||||
}
|
||||
}
|
||||
}
|
106
src/restic/mock/backend.go
Normal file
106
src/restic/mock/backend.go
Normal file
@ -0,0 +1,106 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"restic"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Backend implements a mock backend.
|
||||
type Backend struct {
|
||||
CloseFn func() error
|
||||
LoadFn func(h restic.Handle, p []byte, off int64) (int, error)
|
||||
SaveFn func(h restic.Handle, p []byte) error
|
||||
StatFn func(h restic.Handle) (restic.BlobInfo, error)
|
||||
ListFn func(restic.FileType, <-chan struct{}) <-chan string
|
||||
RemoveFn func(restic.FileType, string) error
|
||||
TestFn func(restic.FileType, string) (bool, error)
|
||||
DeleteFn func() error
|
||||
LocationFn func() string
|
||||
}
|
||||
|
||||
// Close the backend.
|
||||
func (m *Backend) Close() error {
|
||||
if m.CloseFn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.CloseFn()
|
||||
}
|
||||
|
||||
// Location returns a location string.
|
||||
func (m *Backend) Location() string {
|
||||
if m.LocationFn == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return m.LocationFn()
|
||||
}
|
||||
|
||||
// Load loads data from the backend.
|
||||
func (m *Backend) Load(h restic.Handle, p []byte, off int64) (int, error) {
|
||||
if m.LoadFn == nil {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.LoadFn(h, p, off)
|
||||
}
|
||||
|
||||
// Save data in the backend.
|
||||
func (m *Backend) Save(h restic.Handle, p []byte) error {
|
||||
if m.SaveFn == nil {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.SaveFn(h, p)
|
||||
}
|
||||
|
||||
// Stat an object in the backend.
|
||||
func (m *Backend) Stat(h restic.Handle) (restic.BlobInfo, error) {
|
||||
if m.StatFn == nil {
|
||||
return restic.BlobInfo{}, errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.StatFn(h)
|
||||
}
|
||||
|
||||
// List items of type t.
|
||||
func (m *Backend) List(t restic.FileType, done <-chan struct{}) <-chan string {
|
||||
if m.ListFn == nil {
|
||||
ch := make(chan string)
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
return m.ListFn(t, done)
|
||||
}
|
||||
|
||||
// Remove data from the backend.
|
||||
func (m *Backend) Remove(t restic.FileType, name string) error {
|
||||
if m.RemoveFn == nil {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.RemoveFn(t, name)
|
||||
}
|
||||
|
||||
// Test for the existence of a specific item.
|
||||
func (m *Backend) Test(t restic.FileType, name string) (bool, error) {
|
||||
if m.TestFn == nil {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.TestFn(t, name)
|
||||
}
|
||||
|
||||
// Delete all data.
|
||||
func (m *Backend) Delete() error {
|
||||
if m.DeleteFn == nil {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
return m.DeleteFn()
|
||||
}
|
||||
|
||||
// Make sure that Backend implements the backend interface.
|
||||
var _ restic.Backend = &Backend{}
|
@ -24,7 +24,7 @@ import (
|
||||
// Node is a file, directory or other item in a backup.
|
||||
type Node struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
FileType string `json:"type"`
|
||||
Mode os.FileMode `json:"mode,omitempty"`
|
||||
ModTime time.Time `json:"mtime,omitempty"`
|
||||
AccessTime time.Time `json:"atime,omitempty"`
|
||||
@ -51,7 +51,7 @@ type Node struct {
|
||||
}
|
||||
|
||||
func (node Node) String() string {
|
||||
switch node.Type {
|
||||
switch node.FileType {
|
||||
case "file":
|
||||
return fmt.Sprintf("%s %5d %5d %6d %s %s",
|
||||
node.Mode, node.UID, node.GID, node.Size, node.ModTime, node.Name)
|
||||
@ -60,7 +60,7 @@ func (node Node) String() string {
|
||||
node.Mode|os.ModeDir, node.UID, node.GID, node.Size, node.ModTime, node.Name)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<Node(%s) %s>", node.Type, node.Name)
|
||||
return fmt.Sprintf("<Node(%s) %s>", node.FileType, node.Name)
|
||||
}
|
||||
|
||||
func (node Node) Tree() *Tree {
|
||||
@ -77,8 +77,8 @@ func NodeFromFileInfo(path string, fi os.FileInfo) (*Node, error) {
|
||||
ModTime: fi.ModTime(),
|
||||
}
|
||||
|
||||
node.Type = nodeTypeFromFileInfo(fi)
|
||||
if node.Type == "file" {
|
||||
node.FileType = nodeTypeFromFileInfo(fi)
|
||||
if node.FileType == "file" {
|
||||
node.Size = uint64(fi.Size())
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ func nodeTypeFromFileInfo(fi os.FileInfo) string {
|
||||
func (node *Node) CreateAt(path string, repo *repository.Repository) error {
|
||||
debug.Log("Node.CreateAt", "create node %v at %v", node.Name, path)
|
||||
|
||||
switch node.Type {
|
||||
switch node.FileType {
|
||||
case "dir":
|
||||
if err := node.createDirAt(path); err != nil {
|
||||
return err
|
||||
@ -139,7 +139,7 @@ func (node *Node) CreateAt(path string, repo *repository.Repository) error {
|
||||
case "socket":
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("filetype %q not implemented!\n", node.Type)
|
||||
return errors.Errorf("filetype %q not implemented!\n", node.FileType)
|
||||
}
|
||||
|
||||
err := node.restoreMetadata(path)
|
||||
@ -158,14 +158,14 @@ func (node Node) restoreMetadata(path string) error {
|
||||
return errors.Wrap(err, "Lchown")
|
||||
}
|
||||
|
||||
if node.Type != "symlink" {
|
||||
if node.FileType != "symlink" {
|
||||
err = fs.Chmod(path, node.Mode)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Chmod")
|
||||
}
|
||||
}
|
||||
|
||||
if node.Type != "dir" {
|
||||
if node.FileType != "dir" {
|
||||
err = node.RestoreTimestamps(path)
|
||||
if err != nil {
|
||||
debug.Log("Node.restoreMetadata", "error restoring timestamps for dir %v: %v", path, err)
|
||||
@ -182,7 +182,7 @@ func (node Node) RestoreTimestamps(path string) error {
|
||||
syscall.NsecToTimespec(node.ModTime.UnixNano()),
|
||||
}
|
||||
|
||||
if node.Type == "symlink" {
|
||||
if node.FileType == "symlink" {
|
||||
return node.restoreSymlinkTimestamps(path, utimes)
|
||||
}
|
||||
|
||||
@ -287,7 +287,7 @@ func (node Node) Equals(other Node) bool {
|
||||
if node.Name != other.Name {
|
||||
return false
|
||||
}
|
||||
if node.Type != other.Type {
|
||||
if node.FileType != other.FileType {
|
||||
return false
|
||||
}
|
||||
if node.Mode != other.Mode {
|
||||
@ -375,13 +375,13 @@ func (node Node) sameContent(other Node) bool {
|
||||
}
|
||||
|
||||
func (node *Node) isNewer(path string, fi os.FileInfo) bool {
|
||||
if node.Type != "file" {
|
||||
if node.FileType != "file" {
|
||||
debug.Log("node.isNewer", "node %v is newer: not file", path)
|
||||
return true
|
||||
}
|
||||
|
||||
tpe := nodeTypeFromFileInfo(fi)
|
||||
if node.Name != fi.Name() || node.Type != tpe {
|
||||
if node.Name != fi.Name() || node.FileType != tpe {
|
||||
debug.Log("node.isNewer", "node %v is newer: name or type changed", path)
|
||||
return true
|
||||
}
|
||||
@ -469,7 +469,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
|
||||
return err
|
||||
}
|
||||
|
||||
switch node.Type {
|
||||
switch node.FileType {
|
||||
case "file":
|
||||
node.Size = uint64(stat.size())
|
||||
node.Links = uint64(stat.nlink())
|
||||
@ -484,7 +484,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
|
||||
case "fifo":
|
||||
case "socket":
|
||||
default:
|
||||
err = errors.Errorf("invalid node type %q", node.Type)
|
||||
err = errors.Errorf("invalid node type %q", node.FileType)
|
||||
}
|
||||
|
||||
return err
|
||||
|
@ -74,7 +74,7 @@ func parseTime(s string) time.Time {
|
||||
var nodeTests = []restic.Node{
|
||||
restic.Node{
|
||||
Name: "testFile",
|
||||
Type: "file",
|
||||
FileType: "file",
|
||||
Content: []backend.ID{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -85,7 +85,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
restic.Node{
|
||||
Name: "testSuidFile",
|
||||
Type: "file",
|
||||
FileType: "file",
|
||||
Content: []backend.ID{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -96,7 +96,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
restic.Node{
|
||||
Name: "testSuidFile2",
|
||||
Type: "file",
|
||||
FileType: "file",
|
||||
Content: []backend.ID{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -107,7 +107,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
restic.Node{
|
||||
Name: "testSticky",
|
||||
Type: "file",
|
||||
FileType: "file",
|
||||
Content: []backend.ID{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -118,7 +118,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
restic.Node{
|
||||
Name: "testDir",
|
||||
Type: "dir",
|
||||
FileType: "dir",
|
||||
Subtree: nil,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -129,7 +129,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
restic.Node{
|
||||
Name: "testSymlink",
|
||||
Type: "symlink",
|
||||
FileType: "symlink",
|
||||
LinkTarget: "invalid",
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -156,10 +156,10 @@ func TestNodeRestoreAt(t *testing.T) {
|
||||
nodePath := filepath.Join(tempdir, test.Name)
|
||||
OK(t, test.CreateAt(nodePath, nil))
|
||||
|
||||
if test.Type == "symlink" && runtime.GOOS == "windows" {
|
||||
if test.FileType == "symlink" && runtime.GOOS == "windows" {
|
||||
continue
|
||||
}
|
||||
if test.Type == "dir" {
|
||||
if test.FileType == "dir" {
|
||||
OK(t, test.RestoreTimestamps(nodePath))
|
||||
}
|
||||
|
||||
@ -170,25 +170,25 @@ func TestNodeRestoreAt(t *testing.T) {
|
||||
OK(t, err)
|
||||
|
||||
Assert(t, test.Name == n2.Name,
|
||||
"%v: name doesn't match (%v != %v)", test.Type, test.Name, n2.Name)
|
||||
Assert(t, test.Type == n2.Type,
|
||||
"%v: type doesn't match (%v != %v)", test.Type, test.Type, n2.Type)
|
||||
"%v: name doesn't match (%v != %v)", test.FileType, test.Name, n2.Name)
|
||||
Assert(t, test.FileType == n2.FileType,
|
||||
"%v: type doesn't match (%v != %v)", test.FileType, test.FileType, n2.FileType)
|
||||
Assert(t, test.Size == n2.Size,
|
||||
"%v: size doesn't match (%v != %v)", test.Size, test.Size, n2.Size)
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
Assert(t, test.UID == n2.UID,
|
||||
"%v: UID doesn't match (%v != %v)", test.Type, test.UID, n2.UID)
|
||||
"%v: UID doesn't match (%v != %v)", test.FileType, test.UID, n2.UID)
|
||||
Assert(t, test.GID == n2.GID,
|
||||
"%v: GID doesn't match (%v != %v)", test.Type, test.GID, n2.GID)
|
||||
if test.Type != "symlink" {
|
||||
"%v: GID doesn't match (%v != %v)", test.FileType, test.GID, n2.GID)
|
||||
if test.FileType != "symlink" {
|
||||
Assert(t, test.Mode == n2.Mode,
|
||||
"%v: mode doesn't match (0%o != 0%o)", test.Type, test.Mode, n2.Mode)
|
||||
"%v: mode doesn't match (0%o != 0%o)", test.FileType, test.Mode, n2.Mode)
|
||||
}
|
||||
}
|
||||
|
||||
AssertFsTimeEqual(t, "AccessTime", test.Type, test.AccessTime, n2.AccessTime)
|
||||
AssertFsTimeEqual(t, "ModTime", test.Type, test.ModTime, n2.ModTime)
|
||||
AssertFsTimeEqual(t, "AccessTime", test.FileType, test.AccessTime, n2.AccessTime)
|
||||
AssertFsTimeEqual(t, "ModTime", test.FileType, test.ModTime, n2.ModTime)
|
||||
}
|
||||
}
|
||||
|
||||
|
13
src/restic/repository.go
Normal file
13
src/restic/repository.go
Normal file
@ -0,0 +1,13 @@
|
||||
package restic
|
||||
|
||||
import "restic/repository"
|
||||
|
||||
// Repository stores data in a backend. It provides high-level functions and
|
||||
// transparently encrypts/decrypts data.
|
||||
type Repository interface {
|
||||
|
||||
// Backend returns the backend used by the repository
|
||||
Backend() Backend
|
||||
|
||||
SetIndex(*repository.MasterIndex)
|
||||
}
|
@ -58,7 +58,7 @@ func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error
|
||||
}
|
||||
}
|
||||
|
||||
if node.Type == "dir" {
|
||||
if node.FileType == "dir" {
|
||||
if node.Subtree == nil {
|
||||
return errors.Errorf("Dir without subtree in tree %v", treeID.Str())
|
||||
}
|
||||
|
@ -110,10 +110,10 @@ func (fs fakeFileSystem) saveTree(seed int64, depth int) backend.ID {
|
||||
id := fs.saveTree(treeSeed, depth-1)
|
||||
|
||||
node := &Node{
|
||||
Name: fmt.Sprintf("dir-%v", treeSeed),
|
||||
Type: "dir",
|
||||
Mode: 0755,
|
||||
Subtree: &id,
|
||||
Name: fmt.Sprintf("dir-%v", treeSeed),
|
||||
FileType: "dir",
|
||||
Mode: 0755,
|
||||
Subtree: &id,
|
||||
}
|
||||
|
||||
tree.Nodes = append(tree.Nodes, node)
|
||||
@ -124,10 +124,10 @@ func (fs fakeFileSystem) saveTree(seed int64, depth int) backend.ID {
|
||||
fileSize := (maxFileSize / maxSeed) * fileSeed
|
||||
|
||||
node := &Node{
|
||||
Name: fmt.Sprintf("file-%v", fileSeed),
|
||||
Type: "file",
|
||||
Mode: 0644,
|
||||
Size: uint64(fileSize),
|
||||
Name: fmt.Sprintf("file-%v", fileSeed),
|
||||
FileType: "file",
|
||||
Mode: 0644,
|
||||
Size: uint64(fileSize),
|
||||
}
|
||||
|
||||
node.Content = fs.saveFile(fakeFile(fs.t, fileSeed, fileSize))
|
||||
@ -195,11 +195,11 @@ func TestCreateSnapshot(t testing.TB, repo *repository.Repository, at time.Time,
|
||||
}
|
||||
|
||||
// TestResetRepository removes all packs and indexes from the repository.
|
||||
func TestResetRepository(t testing.TB, repo *repository.Repository) {
|
||||
func TestResetRepository(t testing.TB, repo Repository) {
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
|
||||
for _, tpe := range []backend.Type{backend.Snapshot, backend.Index, backend.Data} {
|
||||
for _, tpe := range []FileType{SnapshotFile, IndexFile, DataFile} {
|
||||
for id := range repo.Backend().List(tpe, done) {
|
||||
err := repo.Backend().Remove(tpe, id)
|
||||
if err != nil {
|
||||
|
@ -97,7 +97,7 @@ func (t Tree) Find(name string) (*Node, error) {
|
||||
// Subtrees returns a slice of all subtree IDs of the tree.
|
||||
func (t Tree) Subtrees() (trees backend.IDs) {
|
||||
for _, node := range t.Nodes {
|
||||
if node.Type == "dir" && node.Subtree != nil {
|
||||
if node.FileType == "dir" && node.Subtree != nil {
|
||||
trees = append(trees, *node.Subtree)
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ func (tw *TreeWalker) walk(path string, tree *Tree, done chan struct{}) {
|
||||
// load all subtrees in parallel
|
||||
results := make([]<-chan loadTreeResult, len(tree.Nodes))
|
||||
for i, node := range tree.Nodes {
|
||||
if node.Type == "dir" {
|
||||
if node.FileType == "dir" {
|
||||
resCh := make(chan loadTreeResult, 1)
|
||||
tw.ch <- loadTreeJob{
|
||||
id: *node.Subtree,
|
||||
@ -88,7 +88,7 @@ func (tw *TreeWalker) walk(path string, tree *Tree, done chan struct{}) {
|
||||
p := filepath.Join(path, node.Name)
|
||||
var job WalkTreeJob
|
||||
|
||||
if node.Type == "dir" {
|
||||
if node.FileType == "dir" {
|
||||
if results[i] == nil {
|
||||
panic("result chan should not be nil")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user