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