github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/backend/mem/mem_backend.go (about) 1 package mem 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "io/ioutil" 8 "sync" 9 10 "github.com/restic/restic/internal/errors" 11 "github.com/restic/restic/internal/restic" 12 13 "github.com/restic/restic/internal/debug" 14 ) 15 16 type memMap map[restic.Handle][]byte 17 18 // make sure that MemoryBackend implements backend.Backend 19 var _ restic.Backend = &MemoryBackend{} 20 21 var errNotFound = errors.New("not found") 22 23 // MemoryBackend is a mock backend that uses a map for storing all data in 24 // memory. This should only be used for tests. 25 type MemoryBackend struct { 26 data memMap 27 m sync.Mutex 28 } 29 30 // New returns a new backend that saves all data in a map in memory. 31 func New() *MemoryBackend { 32 be := &MemoryBackend{ 33 data: make(memMap), 34 } 35 36 debug.Log("created new memory backend") 37 38 return be 39 } 40 41 // Test returns whether a file exists. 42 func (be *MemoryBackend) Test(ctx context.Context, h restic.Handle) (bool, error) { 43 be.m.Lock() 44 defer be.m.Unlock() 45 46 debug.Log("Test %v", h) 47 48 if _, ok := be.data[h]; ok { 49 return true, nil 50 } 51 52 return false, nil 53 } 54 55 // IsNotExist returns true if the file does not exist. 56 func (be *MemoryBackend) IsNotExist(err error) bool { 57 return errors.Cause(err) == errNotFound 58 } 59 60 // Save adds new Data to the backend. 61 func (be *MemoryBackend) Save(ctx context.Context, h restic.Handle, rd io.Reader) error { 62 if err := h.Valid(); err != nil { 63 return err 64 } 65 66 be.m.Lock() 67 defer be.m.Unlock() 68 69 if h.Type == restic.ConfigFile { 70 h.Name = "" 71 } 72 73 if _, ok := be.data[h]; ok { 74 return errors.New("file already exists") 75 } 76 77 buf, err := ioutil.ReadAll(rd) 78 if err != nil { 79 return err 80 } 81 82 be.data[h] = buf 83 debug.Log("saved %v bytes at %v", len(buf), h) 84 85 return nil 86 } 87 88 // Load returns a reader that yields the contents of the file at h at the 89 // given offset. If length is nonzero, only a portion of the file is 90 // returned. rd must be closed after use. 91 func (be *MemoryBackend) Load(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) { 92 if err := h.Valid(); err != nil { 93 return nil, err 94 } 95 96 be.m.Lock() 97 defer be.m.Unlock() 98 99 if h.Type == restic.ConfigFile { 100 h.Name = "" 101 } 102 103 debug.Log("Load %v offset %v len %v", h, offset, length) 104 105 if offset < 0 { 106 return nil, errors.New("offset is negative") 107 } 108 109 if _, ok := be.data[h]; !ok { 110 return nil, errNotFound 111 } 112 113 buf := be.data[h] 114 if offset > int64(len(buf)) { 115 return nil, errors.New("offset beyond end of file") 116 } 117 118 buf = buf[offset:] 119 if length > 0 && len(buf) > length { 120 buf = buf[:length] 121 } 122 123 return ioutil.NopCloser(bytes.NewReader(buf)), nil 124 } 125 126 // Stat returns information about a file in the backend. 127 func (be *MemoryBackend) Stat(ctx context.Context, h restic.Handle) (restic.FileInfo, error) { 128 be.m.Lock() 129 defer be.m.Unlock() 130 131 if err := h.Valid(); err != nil { 132 return restic.FileInfo{}, err 133 } 134 135 if h.Type == restic.ConfigFile { 136 h.Name = "" 137 } 138 139 debug.Log("stat %v", h) 140 141 e, ok := be.data[h] 142 if !ok { 143 return restic.FileInfo{}, errNotFound 144 } 145 146 return restic.FileInfo{Size: int64(len(e))}, nil 147 } 148 149 // Remove deletes a file from the backend. 150 func (be *MemoryBackend) Remove(ctx context.Context, h restic.Handle) error { 151 be.m.Lock() 152 defer be.m.Unlock() 153 154 debug.Log("Remove %v", h) 155 156 if _, ok := be.data[h]; !ok { 157 return errNotFound 158 } 159 160 delete(be.data, h) 161 162 return nil 163 } 164 165 // List returns a channel which yields entries from the backend. 166 func (be *MemoryBackend) List(ctx context.Context, t restic.FileType) <-chan string { 167 be.m.Lock() 168 defer be.m.Unlock() 169 170 ch := make(chan string) 171 172 var ids []string 173 for entry := range be.data { 174 if entry.Type != t { 175 continue 176 } 177 ids = append(ids, entry.Name) 178 } 179 180 debug.Log("list %v: %v", t, ids) 181 182 go func() { 183 defer close(ch) 184 for _, id := range ids { 185 select { 186 case ch <- id: 187 case <-ctx.Done(): 188 return 189 } 190 } 191 }() 192 193 return ch 194 } 195 196 // Location returns the location of the backend (RAM). 197 func (be *MemoryBackend) Location() string { 198 return "RAM" 199 } 200 201 // Delete removes all data in the backend. 202 func (be *MemoryBackend) Delete(ctx context.Context) error { 203 be.m.Lock() 204 defer be.m.Unlock() 205 206 be.data = make(memMap) 207 return nil 208 } 209 210 // Close closes the backend. 211 func (be *MemoryBackend) Close() error { 212 return nil 213 }