2
2
mirror of https://github.com/octoleo/restic.git synced 2024-06-21 02:02:21 +00:00

Copy interfaces and basic types to package restic/

This commit is contained in:
Alexander Neumann 2016-08-31 19:10:10 +02:00
parent bfdd26c541
commit 82c2dafb23
19 changed files with 351 additions and 224 deletions

View File

@ -83,7 +83,7 @@ func ArchiveReader(repo *repository.Repository, p *Progress, rd io.Reader, name
Name: name, Name: name,
AccessTime: time.Now(), AccessTime: time.Now(),
ModTime: time.Now(), ModTime: time.Now(),
Type: "file", FileType: "file",
Mode: 0644, Mode: 0644,
Size: fileSize, Size: fileSize,
UID: sn.UID, UID: sn.UID,

View File

@ -307,7 +307,7 @@ func (arch *Archiver) fileWorker(wg *sync.WaitGroup, p *Progress, done <-chan st
} }
// otherwise read file normally // 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) debug.Log("Archiver.fileWorker", " read and save %v, content: %v", e.Path(), node.Content)
err = arch.SaveFile(p, node) err = arch.SaveFile(p, node)
if err != nil { if err != nil {
@ -374,7 +374,7 @@ func (arch *Archiver) dirWorker(wg *sync.WaitGroup, p *Progress, done <-chan str
node := res.(*Node) node := res.(*Node)
tree.Insert(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) debug.Log("Archiver.dirWorker", "got tree node for %s: %v", node.path, node.Subtree)
if node.Subtree.IsNull() { if node.Subtree.IsNull() {

View File

@ -12,6 +12,7 @@ import (
"restic" "restic"
"restic/backend" "restic/backend"
"restic/mock"
"restic/pack" "restic/pack"
"restic/repository" "restic/repository"
) )
@ -36,30 +37,30 @@ func randomID() backend.ID {
} }
// forgetfulBackend returns a backend that forgets everything. // forgetfulBackend returns a backend that forgets everything.
func forgetfulBackend() backend.Backend { func forgetfulBackend() restic.Backend {
be := &backend.MockBackend{} 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 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") 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 return nil
} }
be.StatFn = func(h backend.Handle) (backend.BlobInfo, error) { be.StatFn = func(h restic.Handle) (restic.BlobInfo, error) {
return backend.BlobInfo{}, errors.New("not found") 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 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) ch := make(chan string)
close(ch) close(ch)
return ch return ch

55
src/restic/backend.go Normal file
View 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
}

View File

@ -17,13 +17,14 @@ type entry struct {
type memMap map[entry][]byte 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 // MemoryBackend is a mock backend that uses a map for storing all data in
// memory. This should only be used for tests. // memory. This should only be used for tests.
type MemoryBackend struct { type MemoryBackend struct {
data memMap data memMap
m sync.Mutex m sync.Mutex
backend.MockBackend
} }
// New returns a new backend that saves all data in a map in memory. // New returns a new backend that saves all data in a map in memory.
@ -32,60 +33,13 @@ func New() *MemoryBackend {
data: make(memMap), 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") debug.Log("MemoryBackend.New", "created new memory backend")
return be return be
} }
func (be *MemoryBackend) insert(t backend.Type, name string, data []byte) error { // Test returns whether a file exists.
be.m.Lock() func (be *MemoryBackend) Test(t backend.Type, name string) (bool, error) {
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) {
be.m.Lock() be.m.Lock()
defer be.m.Unlock() defer be.m.Unlock()
@ -98,7 +52,8 @@ func memTest(be *MemoryBackend, t backend.Type, name string) (bool, error) {
return false, nil 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 { if err := h.Valid(); err != nil {
return 0, err return 0, err
} }
@ -137,7 +92,8 @@ func memLoad(be *MemoryBackend, h backend.Handle, p []byte, off int64) (int, err
return n, nil 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 { if err := h.Valid(); err != nil {
return err return err
} }
@ -161,7 +117,8 @@ func memSave(be *MemoryBackend, h backend.Handle, p []byte) error {
return nil 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() be.m.Lock()
defer be.m.Unlock() 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 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() be.m.Lock()
defer be.m.Unlock() defer be.m.Unlock()
@ -198,7 +156,8 @@ func memRemove(be *MemoryBackend, t backend.Type, name string) error {
return nil 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() be.m.Lock()
defer be.m.Unlock() defer be.m.Unlock()
@ -227,3 +186,22 @@ func memList(be *MemoryBackend, t backend.Type, done <-chan struct{}) <-chan str
return ch 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
}

View File

@ -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{}

View File

@ -581,7 +581,7 @@ func (c *Checker) checkTree(id backend.ID, tree *restic.Tree) (errs []error) {
var blobs []backend.ID var blobs []backend.ID
for _, node := range tree.Nodes { for _, node := range tree.Nodes {
switch node.Type { switch node.FileType {
case "file": case "file":
if node.Content == nil { if node.Content == nil {
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("file %q has nil blob list", node.Name)}) 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 // nothing to check
default: 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 == "" { if node.Name == "" {

View File

@ -18,7 +18,7 @@ func FindUsedBlobs(repo *repository.Repository, treeID backend.ID, blobs pack.Bl
} }
for _, node := range tree.Nodes { for _, node := range tree.Nodes {
switch node.Type { switch node.FileType {
case "file": case "file":
for _, blob := range node.Content { for _, blob := range node.Content {
blobs.Insert(pack.Handle{ID: blob, Type: pack.Data}) blobs.Insert(pack.Handle{ID: blob, Type: pack.Data})

View File

@ -51,7 +51,7 @@ func newDir(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*
// replaceSpecialNodes replaces nodes with name "." and "/" by their contents. // replaceSpecialNodes replaces nodes with name "." and "/" by their contents.
// Otherwise, the node is returned. // Otherwise, the node is returned.
func replaceSpecialNodes(repo *repository.Repository, node *restic.Node) ([]*restic.Node, error) { func replaceSpecialNodes(repo *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 return []*restic.Node{node}, nil
} }
@ -124,7 +124,7 @@ func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
for _, node := range d.items { for _, node := range d.items {
var typ fuse.DirentType var typ fuse.DirentType
switch node.Type { switch node.FileType {
case "dir": case "dir":
typ = fuse.DT_Dir typ = fuse.DT_Dir
case "file": 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) debug.Log("dir.Lookup", " Lookup(%v) -> not found", name)
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
switch node.Type { switch node.FileType {
case "dir": case "dir":
return newDir(d.repo, node, d.ownerIsRoot) return newDir(d.repo, node, d.ownerIsRoot)
case "file": case "file":
@ -158,7 +158,7 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
case "symlink": case "symlink":
return newLink(d.repo, node, d.ownerIsRoot) return newLink(d.repo, node, d.ownerIsRoot)
default: 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 return nil, fuse.ENOENT
} }
} }

49
src/restic/handle.go Normal file
View 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
View 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
View 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{}

View File

@ -24,7 +24,7 @@ import (
// Node is a file, directory or other item in a backup. // Node is a file, directory or other item in a backup.
type Node struct { type Node struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` FileType string `json:"type"`
Mode os.FileMode `json:"mode,omitempty"` Mode os.FileMode `json:"mode,omitempty"`
ModTime time.Time `json:"mtime,omitempty"` ModTime time.Time `json:"mtime,omitempty"`
AccessTime time.Time `json:"atime,omitempty"` AccessTime time.Time `json:"atime,omitempty"`
@ -51,7 +51,7 @@ type Node struct {
} }
func (node Node) String() string { func (node Node) String() string {
switch node.Type { switch node.FileType {
case "file": case "file":
return fmt.Sprintf("%s %5d %5d %6d %s %s", return fmt.Sprintf("%s %5d %5d %6d %s %s",
node.Mode, node.UID, node.GID, node.Size, node.ModTime, node.Name) 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) 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 { func (node Node) Tree() *Tree {
@ -77,8 +77,8 @@ func NodeFromFileInfo(path string, fi os.FileInfo) (*Node, error) {
ModTime: fi.ModTime(), ModTime: fi.ModTime(),
} }
node.Type = nodeTypeFromFileInfo(fi) node.FileType = nodeTypeFromFileInfo(fi)
if node.Type == "file" { if node.FileType == "file" {
node.Size = uint64(fi.Size()) 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 { func (node *Node) CreateAt(path string, repo *repository.Repository) error {
debug.Log("Node.CreateAt", "create node %v at %v", node.Name, path) debug.Log("Node.CreateAt", "create node %v at %v", node.Name, path)
switch node.Type { switch node.FileType {
case "dir": case "dir":
if err := node.createDirAt(path); err != nil { if err := node.createDirAt(path); err != nil {
return err return err
@ -139,7 +139,7 @@ func (node *Node) CreateAt(path string, repo *repository.Repository) error {
case "socket": case "socket":
return nil return nil
default: 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) err := node.restoreMetadata(path)
@ -158,14 +158,14 @@ func (node Node) restoreMetadata(path string) error {
return errors.Wrap(err, "Lchown") return errors.Wrap(err, "Lchown")
} }
if node.Type != "symlink" { if node.FileType != "symlink" {
err = fs.Chmod(path, node.Mode) err = fs.Chmod(path, node.Mode)
if err != nil { if err != nil {
return errors.Wrap(err, "Chmod") return errors.Wrap(err, "Chmod")
} }
} }
if node.Type != "dir" { if node.FileType != "dir" {
err = node.RestoreTimestamps(path) err = node.RestoreTimestamps(path)
if err != nil { if err != nil {
debug.Log("Node.restoreMetadata", "error restoring timestamps for dir %v: %v", path, err) 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()), syscall.NsecToTimespec(node.ModTime.UnixNano()),
} }
if node.Type == "symlink" { if node.FileType == "symlink" {
return node.restoreSymlinkTimestamps(path, utimes) return node.restoreSymlinkTimestamps(path, utimes)
} }
@ -287,7 +287,7 @@ func (node Node) Equals(other Node) bool {
if node.Name != other.Name { if node.Name != other.Name {
return false return false
} }
if node.Type != other.Type { if node.FileType != other.FileType {
return false return false
} }
if node.Mode != other.Mode { 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 { 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) debug.Log("node.isNewer", "node %v is newer: not file", path)
return true return true
} }
tpe := nodeTypeFromFileInfo(fi) 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) debug.Log("node.isNewer", "node %v is newer: name or type changed", path)
return true return true
} }
@ -469,7 +469,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
return err return err
} }
switch node.Type { switch node.FileType {
case "file": case "file":
node.Size = uint64(stat.size()) node.Size = uint64(stat.size())
node.Links = uint64(stat.nlink()) node.Links = uint64(stat.nlink())
@ -484,7 +484,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
case "fifo": case "fifo":
case "socket": case "socket":
default: default:
err = errors.Errorf("invalid node type %q", node.Type) err = errors.Errorf("invalid node type %q", node.FileType)
} }
return err return err

View File

@ -74,7 +74,7 @@ func parseTime(s string) time.Time {
var nodeTests = []restic.Node{ var nodeTests = []restic.Node{
restic.Node{ restic.Node{
Name: "testFile", Name: "testFile",
Type: "file", FileType: "file",
Content: []backend.ID{}, Content: []backend.ID{},
UID: uint32(os.Getuid()), UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()), GID: uint32(os.Getgid()),
@ -85,7 +85,7 @@ var nodeTests = []restic.Node{
}, },
restic.Node{ restic.Node{
Name: "testSuidFile", Name: "testSuidFile",
Type: "file", FileType: "file",
Content: []backend.ID{}, Content: []backend.ID{},
UID: uint32(os.Getuid()), UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()), GID: uint32(os.Getgid()),
@ -96,7 +96,7 @@ var nodeTests = []restic.Node{
}, },
restic.Node{ restic.Node{
Name: "testSuidFile2", Name: "testSuidFile2",
Type: "file", FileType: "file",
Content: []backend.ID{}, Content: []backend.ID{},
UID: uint32(os.Getuid()), UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()), GID: uint32(os.Getgid()),
@ -107,7 +107,7 @@ var nodeTests = []restic.Node{
}, },
restic.Node{ restic.Node{
Name: "testSticky", Name: "testSticky",
Type: "file", FileType: "file",
Content: []backend.ID{}, Content: []backend.ID{},
UID: uint32(os.Getuid()), UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()), GID: uint32(os.Getgid()),
@ -118,7 +118,7 @@ var nodeTests = []restic.Node{
}, },
restic.Node{ restic.Node{
Name: "testDir", Name: "testDir",
Type: "dir", FileType: "dir",
Subtree: nil, Subtree: nil,
UID: uint32(os.Getuid()), UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()), GID: uint32(os.Getgid()),
@ -129,7 +129,7 @@ var nodeTests = []restic.Node{
}, },
restic.Node{ restic.Node{
Name: "testSymlink", Name: "testSymlink",
Type: "symlink", FileType: "symlink",
LinkTarget: "invalid", LinkTarget: "invalid",
UID: uint32(os.Getuid()), UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()), GID: uint32(os.Getgid()),
@ -156,10 +156,10 @@ func TestNodeRestoreAt(t *testing.T) {
nodePath := filepath.Join(tempdir, test.Name) nodePath := filepath.Join(tempdir, test.Name)
OK(t, test.CreateAt(nodePath, nil)) OK(t, test.CreateAt(nodePath, nil))
if test.Type == "symlink" && runtime.GOOS == "windows" { if test.FileType == "symlink" && runtime.GOOS == "windows" {
continue continue
} }
if test.Type == "dir" { if test.FileType == "dir" {
OK(t, test.RestoreTimestamps(nodePath)) OK(t, test.RestoreTimestamps(nodePath))
} }
@ -170,25 +170,25 @@ func TestNodeRestoreAt(t *testing.T) {
OK(t, err) OK(t, err)
Assert(t, test.Name == n2.Name, Assert(t, test.Name == n2.Name,
"%v: name doesn't match (%v != %v)", test.Type, test.Name, n2.Name) "%v: name doesn't match (%v != %v)", test.FileType, test.Name, n2.Name)
Assert(t, test.Type == n2.Type, Assert(t, test.FileType == n2.FileType,
"%v: type doesn't match (%v != %v)", test.Type, test.Type, n2.Type) "%v: type doesn't match (%v != %v)", test.FileType, test.FileType, n2.FileType)
Assert(t, test.Size == n2.Size, Assert(t, test.Size == n2.Size,
"%v: size doesn't match (%v != %v)", test.Size, test.Size, n2.Size) "%v: size doesn't match (%v != %v)", test.Size, test.Size, n2.Size)
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
Assert(t, test.UID == n2.UID, 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, Assert(t, test.GID == n2.GID,
"%v: GID doesn't match (%v != %v)", test.Type, test.GID, n2.GID) "%v: GID doesn't match (%v != %v)", test.FileType, test.GID, n2.GID)
if test.Type != "symlink" { if test.FileType != "symlink" {
Assert(t, test.Mode == n2.Mode, 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, "AccessTime", test.FileType, test.AccessTime, n2.AccessTime)
AssertFsTimeEqual(t, "ModTime", test.Type, test.ModTime, n2.ModTime) AssertFsTimeEqual(t, "ModTime", test.FileType, test.ModTime, n2.ModTime)
} }
} }

13
src/restic/repository.go Normal file
View 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)
}

View File

@ -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 { if node.Subtree == nil {
return errors.Errorf("Dir without subtree in tree %v", treeID.Str()) return errors.Errorf("Dir without subtree in tree %v", treeID.Str())
} }

View File

@ -110,10 +110,10 @@ func (fs fakeFileSystem) saveTree(seed int64, depth int) backend.ID {
id := fs.saveTree(treeSeed, depth-1) id := fs.saveTree(treeSeed, depth-1)
node := &Node{ node := &Node{
Name: fmt.Sprintf("dir-%v", treeSeed), Name: fmt.Sprintf("dir-%v", treeSeed),
Type: "dir", FileType: "dir",
Mode: 0755, Mode: 0755,
Subtree: &id, Subtree: &id,
} }
tree.Nodes = append(tree.Nodes, node) tree.Nodes = append(tree.Nodes, node)
@ -124,10 +124,10 @@ func (fs fakeFileSystem) saveTree(seed int64, depth int) backend.ID {
fileSize := (maxFileSize / maxSeed) * fileSeed fileSize := (maxFileSize / maxSeed) * fileSeed
node := &Node{ node := &Node{
Name: fmt.Sprintf("file-%v", fileSeed), Name: fmt.Sprintf("file-%v", fileSeed),
Type: "file", FileType: "file",
Mode: 0644, Mode: 0644,
Size: uint64(fileSize), Size: uint64(fileSize),
} }
node.Content = fs.saveFile(fakeFile(fs.t, fileSeed, 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. // 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{}) done := make(chan struct{})
defer close(done) 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) { for id := range repo.Backend().List(tpe, done) {
err := repo.Backend().Remove(tpe, id) err := repo.Backend().Remove(tpe, id)
if err != nil { if err != nil {

View File

@ -97,7 +97,7 @@ func (t Tree) Find(name string) (*Node, error) {
// Subtrees returns a slice of all subtree IDs of the tree. // Subtrees returns a slice of all subtree IDs of the tree.
func (t Tree) Subtrees() (trees backend.IDs) { func (t Tree) Subtrees() (trees backend.IDs) {
for _, node := range t.Nodes { for _, node := range t.Nodes {
if node.Type == "dir" && node.Subtree != nil { if node.FileType == "dir" && node.Subtree != nil {
trees = append(trees, *node.Subtree) trees = append(trees, *node.Subtree)
} }
} }

View File

@ -73,7 +73,7 @@ func (tw *TreeWalker) walk(path string, tree *Tree, done chan struct{}) {
// load all subtrees in parallel // load all subtrees in parallel
results := make([]<-chan loadTreeResult, len(tree.Nodes)) results := make([]<-chan loadTreeResult, len(tree.Nodes))
for i, node := range tree.Nodes { for i, node := range tree.Nodes {
if node.Type == "dir" { if node.FileType == "dir" {
resCh := make(chan loadTreeResult, 1) resCh := make(chan loadTreeResult, 1)
tw.ch <- loadTreeJob{ tw.ch <- loadTreeJob{
id: *node.Subtree, id: *node.Subtree,
@ -88,7 +88,7 @@ func (tw *TreeWalker) walk(path string, tree *Tree, done chan struct{}) {
p := filepath.Join(path, node.Name) p := filepath.Join(path, node.Name)
var job WalkTreeJob var job WalkTreeJob
if node.Type == "dir" { if node.FileType == "dir" {
if results[i] == nil { if results[i] == nil {
panic("result chan should not be nil") panic("result chan should not be nil")
} }