| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | package backend | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2015-11-22 16:30:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/restic/restic/debug" | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type entry struct { | 
					
						
							|  |  |  | 	Type Type | 
					
						
							|  |  |  | 	Name string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type memMap map[entry][]byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	MockBackend | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewMemoryBackend returns a new backend that saves all data in a map in | 
					
						
							|  |  |  | // memory. | 
					
						
							|  |  |  | func NewMemoryBackend() *MemoryBackend { | 
					
						
							|  |  |  | 	be := &MemoryBackend{ | 
					
						
							|  |  |  | 		data: make(memMap), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.MockBackend.TestFn = func(t Type, name string) (bool, error) { | 
					
						
							|  |  |  | 		return memTest(be, t, name) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.MockBackend.CreateFn = func() (Blob, error) { | 
					
						
							|  |  |  | 		return memCreate(be) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.MockBackend.GetFn = func(t Type, name string) (io.ReadCloser, error) { | 
					
						
							|  |  |  | 		return memGet(be, t, name) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.MockBackend.GetReaderFn = func(t Type, name string, offset, length uint) (io.ReadCloser, error) { | 
					
						
							|  |  |  | 		return memGetReader(be, t, name, offset, length) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.MockBackend.RemoveFn = func(t Type, name string) error { | 
					
						
							|  |  |  | 		return memRemove(be, t, name) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.MockBackend.ListFn = func(t Type, done <-chan struct{}) <-chan string { | 
					
						
							|  |  |  | 		return memList(be, t, done) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:30:13 +01:00
										 |  |  | 	debug.Log("MemoryBackend.New", "created new memory backend") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 	return be | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (be *MemoryBackend) insert(t 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 Type, name string) (bool, error) { | 
					
						
							|  |  |  | 	be.m.Lock() | 
					
						
							|  |  |  | 	defer be.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:30:13 +01:00
										 |  |  | 	debug.Log("MemoryBackend.Test", "test %v %v", t, name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 	if _, ok := be.data[entry{t, name}]; ok { | 
					
						
							|  |  |  | 		return true, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tempMemEntry temporarily holds data written to the memory backend before it | 
					
						
							|  |  |  | // is finalized. | 
					
						
							|  |  |  | type tempMemEntry struct { | 
					
						
							|  |  |  | 	be   *MemoryBackend | 
					
						
							|  |  |  | 	data bytes.Buffer | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *tempMemEntry) Write(p []byte) (int, error) { | 
					
						
							|  |  |  | 	return e.data.Write(p) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *tempMemEntry) Size() uint { | 
					
						
							|  |  |  | 	return uint(len(e.data.Bytes())) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *tempMemEntry) Finalize(t Type, name string) error { | 
					
						
							| 
									
										
										
										
											2015-11-22 16:38:58 +01:00
										 |  |  | 	if t == Config { | 
					
						
							|  |  |  | 		name = "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("MemoryBackend", "save blob %p as %v %v", e, t, name) | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 	return e.be.insert(t, name, e.data.Bytes()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func memCreate(be *MemoryBackend) (Blob, error) { | 
					
						
							| 
									
										
										
										
											2015-11-22 16:38:58 +01:00
										 |  |  | 	blob := &tempMemEntry{be: be} | 
					
						
							|  |  |  | 	debug.Log("MemoryBackend.Create", "create new blob %p", blob) | 
					
						
							|  |  |  | 	return blob, nil | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // readCloser wraps a reader and adds a noop Close method. | 
					
						
							|  |  |  | type readCloser struct { | 
					
						
							|  |  |  | 	io.Reader | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (rd readCloser) Close() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func memGet(be *MemoryBackend, t Type, name string) (io.ReadCloser, error) { | 
					
						
							|  |  |  | 	be.m.Lock() | 
					
						
							|  |  |  | 	defer be.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:38:58 +01:00
										 |  |  | 	if t == Config { | 
					
						
							|  |  |  | 		name = "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:30:13 +01:00
										 |  |  | 	debug.Log("MemoryBackend.Get", "get %v %v", t, name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 	if _, ok := be.data[entry{t, name}]; !ok { | 
					
						
							|  |  |  | 		return nil, errors.New("no such data") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return readCloser{bytes.NewReader(be.data[entry{t, name}])}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func memGetReader(be *MemoryBackend, t Type, name string, offset, length uint) (io.ReadCloser, error) { | 
					
						
							|  |  |  | 	be.m.Lock() | 
					
						
							|  |  |  | 	defer be.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:38:58 +01:00
										 |  |  | 	if t == Config { | 
					
						
							|  |  |  | 		name = "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 17:27:02 +01:00
										 |  |  | 	debug.Log("MemoryBackend.GetReader", "get %v %v offset %v len %v", t, name, offset, length) | 
					
						
							| 
									
										
										
										
											2015-11-22 16:30:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 	if _, ok := be.data[entry{t, name}]; !ok { | 
					
						
							|  |  |  | 		return nil, errors.New("no such data") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf := be.data[entry{t, name}] | 
					
						
							|  |  |  | 	if offset > uint(len(buf)) { | 
					
						
							|  |  |  | 		return nil, errors.New("offset beyond end of file") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf = buf[offset:] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 17:27:02 +01:00
										 |  |  | 	if length > 0 { | 
					
						
							|  |  |  | 		if length > uint(len(buf)) { | 
					
						
							|  |  |  | 			length = uint(len(buf)) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 17:27:02 +01:00
										 |  |  | 		buf = buf[:length] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return readCloser{bytes.NewReader(buf)}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func memRemove(be *MemoryBackend, t Type, name string) error { | 
					
						
							|  |  |  | 	be.m.Lock() | 
					
						
							|  |  |  | 	defer be.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:30:13 +01:00
										 |  |  | 	debug.Log("MemoryBackend.Remove", "get %v %v", t, name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 	if _, ok := be.data[entry{t, name}]; !ok { | 
					
						
							|  |  |  | 		return errors.New("no such data") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	delete(be.data, entry{t, name}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func memList(be *MemoryBackend, t Type, done <-chan struct{}) <-chan string { | 
					
						
							|  |  |  | 	be.m.Lock() | 
					
						
							|  |  |  | 	defer be.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ch := make(chan string) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ids []string | 
					
						
							|  |  |  | 	for entry := range be.data { | 
					
						
							|  |  |  | 		if entry.Type != t { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ids = append(ids, entry.Name) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sort.Strings(ids) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:30:13 +01:00
										 |  |  | 	debug.Log("MemoryBackend.List", "list %v: %v", t, ids) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-22 16:12:00 +01:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer close(ch) | 
					
						
							|  |  |  | 		for _, id := range ids { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case ch <- id: | 
					
						
							|  |  |  | 			case <-done: | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ch | 
					
						
							|  |  |  | } |