diff --git a/src/restic/archive_reader.go b/src/restic/archive_reader.go index 02b630b1d..c25cd0996 100644 --- a/src/restic/archive_reader.go +++ b/src/restic/archive_reader.go @@ -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, diff --git a/src/restic/archiver.go b/src/restic/archiver.go index 5f72633a2..24d352bc9 100644 --- a/src/restic/archiver.go +++ b/src/restic/archiver.go @@ -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() { diff --git a/src/restic/archiver_duplication_test.go b/src/restic/archiver_duplication_test.go index 61f7aafb9..52afb6bd6 100644 --- a/src/restic/archiver_duplication_test.go +++ b/src/restic/archiver_duplication_test.go @@ -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 diff --git a/src/restic/backend.go b/src/restic/backend.go new file mode 100644 index 000000000..f00c4699e --- /dev/null +++ b/src/restic/backend.go @@ -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 +} diff --git a/src/restic/backend/mem/mem_backend.go b/src/restic/backend/mem/mem_backend.go index 239e2c899..339d86c5d 100644 --- a/src/restic/backend/mem/mem_backend.go +++ b/src/restic/backend/mem/mem_backend.go @@ -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 +} diff --git a/src/restic/backend/mock_backend.go b/src/restic/backend/mock_backend.go deleted file mode 100644 index 70429acfd..000000000 --- a/src/restic/backend/mock_backend.go +++ /dev/null @@ -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{} diff --git a/src/restic/checker/checker.go b/src/restic/checker/checker.go index 1755bd3ac..51b473641 100644 --- a/src/restic/checker/checker.go +++ b/src/restic/checker/checker.go @@ -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 == "" { diff --git a/src/restic/find.go b/src/restic/find.go index 63c8bd813..067754e73 100644 --- a/src/restic/find.go +++ b/src/restic/find.go @@ -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}) diff --git a/src/restic/fuse/dir.go b/src/restic/fuse/dir.go index a89617e5f..b553da1ad 100644 --- a/src/restic/fuse/dir.go +++ b/src/restic/fuse/dir.go @@ -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 } } diff --git a/src/restic/handle.go b/src/restic/handle.go new file mode 100644 index 000000000..dd7439d39 --- /dev/null +++ b/src/restic/handle.go @@ -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 +} diff --git a/src/restic/handle_test.go b/src/restic/handle_test.go new file mode 100644 index 000000000..d5044558e --- /dev/null +++ b/src/restic/handle_test.go @@ -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) + } + } +} diff --git a/src/restic/mock/backend.go b/src/restic/mock/backend.go new file mode 100644 index 000000000..b27b64a1c --- /dev/null +++ b/src/restic/mock/backend.go @@ -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{} diff --git a/src/restic/node.go b/src/restic/node.go index 37ef5e04c..72565342f 100644 --- a/src/restic/node.go +++ b/src/restic/node.go @@ -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.Type, node.Name) + return fmt.Sprintf("", 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 diff --git a/src/restic/node_test.go b/src/restic/node_test.go index e3b458c47..a1d2be8e8 100644 --- a/src/restic/node_test.go +++ b/src/restic/node_test.go @@ -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) } } diff --git a/src/restic/repository.go b/src/restic/repository.go new file mode 100644 index 000000000..cb95463f0 --- /dev/null +++ b/src/restic/repository.go @@ -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) +} diff --git a/src/restic/restorer.go b/src/restic/restorer.go index 74cdfc34d..36ea28f87 100644 --- a/src/restic/restorer.go +++ b/src/restic/restorer.go @@ -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()) } diff --git a/src/restic/testing.go b/src/restic/testing.go index 78783ee44..0b6ed6b49 100644 --- a/src/restic/testing.go +++ b/src/restic/testing.go @@ -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 { diff --git a/src/restic/tree.go b/src/restic/tree.go index 9bfcfd7ee..3da5cde22 100644 --- a/src/restic/tree.go +++ b/src/restic/tree.go @@ -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) } } diff --git a/src/restic/walk.go b/src/restic/walk.go index 2978e8500..a50438f7c 100644 --- a/src/restic/walk.go +++ b/src/restic/walk.go @@ -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") }