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  }