github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/repository/repository.go (about)

     1  package repository
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"os"
     9  
    10  	"github.com/restic/restic/internal/cache"
    11  	"github.com/restic/restic/internal/errors"
    12  	"github.com/restic/restic/internal/restic"
    13  
    14  	"github.com/restic/restic/internal/backend"
    15  	"github.com/restic/restic/internal/crypto"
    16  	"github.com/restic/restic/internal/debug"
    17  	"github.com/restic/restic/internal/pack"
    18  )
    19  
    20  // Repository is used to access a repository in a backend.
    21  type Repository struct {
    22  	be      restic.Backend
    23  	cfg     restic.Config
    24  	key     *crypto.Key
    25  	keyName string
    26  	idx     *MasterIndex
    27  	restic.Cache
    28  
    29  	treePM *packerManager
    30  	dataPM *packerManager
    31  }
    32  
    33  // New returns a new repository with backend be.
    34  func New(be restic.Backend) *Repository {
    35  	repo := &Repository{
    36  		be:     be,
    37  		idx:    NewMasterIndex(),
    38  		dataPM: newPackerManager(be, nil),
    39  		treePM: newPackerManager(be, nil),
    40  	}
    41  
    42  	return repo
    43  }
    44  
    45  // Config returns the repository configuration.
    46  func (r *Repository) Config() restic.Config {
    47  	return r.cfg
    48  }
    49  
    50  // UseCache replaces the backend with the wrapped cache.
    51  func (r *Repository) UseCache(c restic.Cache) {
    52  	if c == nil {
    53  		return
    54  	}
    55  	debug.Log("using cache")
    56  	r.Cache = c
    57  	r.be = c.Wrap(r.be)
    58  }
    59  
    60  // PrefixLength returns the number of bytes required so that all prefixes of
    61  // all IDs of type t are unique.
    62  func (r *Repository) PrefixLength(t restic.FileType) (int, error) {
    63  	return restic.PrefixLength(r.be, t)
    64  }
    65  
    66  // LoadAndDecrypt loads and decrypts data identified by t and id from the
    67  // backend.
    68  func (r *Repository) LoadAndDecrypt(ctx context.Context, t restic.FileType, id restic.ID) (buf []byte, err error) {
    69  	debug.Log("load %v with id %v", t, id.Str())
    70  
    71  	h := restic.Handle{Type: t, Name: id.String()}
    72  	buf, err = backend.LoadAll(ctx, r.be, h)
    73  	if err != nil {
    74  		debug.Log("error loading %v: %v", h, err)
    75  		return nil, err
    76  	}
    77  
    78  	if t != restic.ConfigFile && !restic.Hash(buf).Equal(id) {
    79  		return nil, errors.Errorf("load %v: invalid data returned", h)
    80  	}
    81  
    82  	nonce, ciphertext := buf[:r.key.NonceSize()], buf[r.key.NonceSize():]
    83  	plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	return plaintext, nil
    89  }
    90  
    91  // sortCachedPacks moves all cached pack files to the front of blobs.
    92  func (r *Repository) sortCachedPacks(blobs []restic.PackedBlob) []restic.PackedBlob {
    93  	if r.Cache == nil {
    94  		return blobs
    95  	}
    96  
    97  	cached := make([]restic.PackedBlob, 0, len(blobs)/2)
    98  	noncached := make([]restic.PackedBlob, 0, len(blobs)/2)
    99  
   100  	for _, blob := range blobs {
   101  		if r.Cache.Has(restic.Handle{Type: restic.DataFile, Name: blob.PackID.String()}) {
   102  			cached = append(cached, blob)
   103  			continue
   104  		}
   105  		noncached = append(noncached, blob)
   106  	}
   107  
   108  	return append(cached, noncached...)
   109  }
   110  
   111  // loadBlob tries to load and decrypt content identified by t and id from a
   112  // pack from the backend, the result is stored in plaintextBuf, which must be
   113  // large enough to hold the complete blob.
   114  func (r *Repository) loadBlob(ctx context.Context, id restic.ID, t restic.BlobType, plaintextBuf []byte) (int, error) {
   115  	debug.Log("load %v with id %v (buf len %v, cap %d)", t, id.Str(), len(plaintextBuf), cap(plaintextBuf))
   116  
   117  	// lookup packs
   118  	blobs, err := r.idx.Lookup(id, t)
   119  	if err != nil {
   120  		debug.Log("id %v not found in index: %v", id.Str(), err)
   121  		return 0, err
   122  	}
   123  
   124  	// try cached pack files first
   125  	blobs = r.sortCachedPacks(blobs)
   126  
   127  	var lastError error
   128  	for _, blob := range blobs {
   129  		debug.Log("blob %v/%v found: %v", t, id.Str(), blob)
   130  
   131  		if blob.Type != t {
   132  			debug.Log("blob %v has wrong block type, want %v", blob, t)
   133  		}
   134  
   135  		// load blob from pack
   136  		h := restic.Handle{Type: restic.DataFile, Name: blob.PackID.String()}
   137  
   138  		if uint(cap(plaintextBuf)) < blob.Length {
   139  			return 0, errors.Errorf("buffer is too small: %v < %v", cap(plaintextBuf), blob.Length)
   140  		}
   141  
   142  		plaintextBuf = plaintextBuf[:blob.Length]
   143  
   144  		n, err := restic.ReadAt(ctx, r.be, h, int64(blob.Offset), plaintextBuf)
   145  		if err != nil {
   146  			debug.Log("error loading blob %v: %v", blob, err)
   147  			lastError = err
   148  			continue
   149  		}
   150  
   151  		if uint(n) != blob.Length {
   152  			lastError = errors.Errorf("error loading blob %v: wrong length returned, want %d, got %d",
   153  				id.Str(), blob.Length, uint(n))
   154  			debug.Log("lastError: %v", lastError)
   155  			continue
   156  		}
   157  
   158  		// decrypt
   159  		nonce, ciphertext := plaintextBuf[:r.key.NonceSize()], plaintextBuf[r.key.NonceSize():]
   160  		plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil)
   161  		if err != nil {
   162  			lastError = errors.Errorf("decrypting blob %v failed: %v", id, err)
   163  			continue
   164  		}
   165  
   166  		// check hash
   167  		if !restic.Hash(plaintext).Equal(id) {
   168  			lastError = errors.Errorf("blob %v returned invalid hash", id)
   169  			continue
   170  		}
   171  
   172  		// move decrypted data to the start of the provided buffer
   173  		copy(plaintextBuf[0:], plaintext)
   174  		return len(plaintext), nil
   175  	}
   176  
   177  	if lastError != nil {
   178  		return 0, lastError
   179  	}
   180  
   181  	return 0, errors.Errorf("loading blob %v from %v packs failed", id.Str(), len(blobs))
   182  }
   183  
   184  // LoadJSONUnpacked decrypts the data and afterwards calls json.Unmarshal on
   185  // the item.
   186  func (r *Repository) LoadJSONUnpacked(ctx context.Context, t restic.FileType, id restic.ID, item interface{}) (err error) {
   187  	buf, err := r.LoadAndDecrypt(ctx, t, id)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	return json.Unmarshal(buf, item)
   193  }
   194  
   195  // LookupBlobSize returns the size of blob id.
   196  func (r *Repository) LookupBlobSize(id restic.ID, tpe restic.BlobType) (uint, error) {
   197  	return r.idx.LookupSize(id, tpe)
   198  }
   199  
   200  // SaveAndEncrypt encrypts data and stores it to the backend as type t. If data
   201  // is small enough, it will be packed together with other small blobs.
   202  func (r *Repository) SaveAndEncrypt(ctx context.Context, t restic.BlobType, data []byte, id *restic.ID) (restic.ID, error) {
   203  	if id == nil {
   204  		// compute plaintext hash
   205  		hashedID := restic.Hash(data)
   206  		id = &hashedID
   207  	}
   208  
   209  	debug.Log("save id %v (%v, %d bytes)", id.Str(), t, len(data))
   210  
   211  	// get buf from the pool
   212  	ciphertext := getBuf()
   213  	defer freeBuf(ciphertext)
   214  
   215  	ciphertext = ciphertext[:0]
   216  	nonce := crypto.NewRandomNonce()
   217  	ciphertext = append(ciphertext, nonce...)
   218  
   219  	// encrypt blob
   220  	ciphertext = r.key.Seal(ciphertext, nonce, data, nil)
   221  
   222  	// find suitable packer and add blob
   223  	var pm *packerManager
   224  
   225  	switch t {
   226  	case restic.TreeBlob:
   227  		pm = r.treePM
   228  	case restic.DataBlob:
   229  		pm = r.dataPM
   230  	default:
   231  		panic(fmt.Sprintf("invalid type: %v", t))
   232  	}
   233  
   234  	packer, err := pm.findPacker()
   235  	if err != nil {
   236  		return restic.ID{}, err
   237  	}
   238  
   239  	// save ciphertext
   240  	_, err = packer.Add(t, *id, ciphertext)
   241  	if err != nil {
   242  		return restic.ID{}, err
   243  	}
   244  
   245  	// if the pack is not full enough, put back to the list
   246  	if packer.Size() < minPackSize {
   247  		debug.Log("pack is not full enough (%d bytes)", packer.Size())
   248  		pm.insertPacker(packer)
   249  		return *id, nil
   250  	}
   251  
   252  	// else write the pack to the backend
   253  	return *id, r.savePacker(t, packer)
   254  }
   255  
   256  // SaveJSONUnpacked serialises item as JSON and encrypts and saves it in the
   257  // backend as type t, without a pack. It returns the storage hash.
   258  func (r *Repository) SaveJSONUnpacked(ctx context.Context, t restic.FileType, item interface{}) (restic.ID, error) {
   259  	debug.Log("save new blob %v", t)
   260  	plaintext, err := json.Marshal(item)
   261  	if err != nil {
   262  		return restic.ID{}, errors.Wrap(err, "json.Marshal")
   263  	}
   264  
   265  	return r.SaveUnpacked(ctx, t, plaintext)
   266  }
   267  
   268  // SaveUnpacked encrypts data and stores it in the backend. Returned is the
   269  // storage hash.
   270  func (r *Repository) SaveUnpacked(ctx context.Context, t restic.FileType, p []byte) (id restic.ID, err error) {
   271  	ciphertext := restic.NewBlobBuffer(len(p))
   272  	ciphertext = ciphertext[:0]
   273  	nonce := crypto.NewRandomNonce()
   274  	ciphertext = append(ciphertext, nonce...)
   275  
   276  	ciphertext = r.key.Seal(ciphertext, nonce, p, nil)
   277  
   278  	id = restic.Hash(ciphertext)
   279  	h := restic.Handle{Type: t, Name: id.String()}
   280  
   281  	err = r.be.Save(ctx, h, bytes.NewReader(ciphertext))
   282  	if err != nil {
   283  		debug.Log("error saving blob %v: %v", h, err)
   284  		return restic.ID{}, err
   285  	}
   286  
   287  	debug.Log("blob %v saved", h)
   288  	return id, nil
   289  }
   290  
   291  // Flush saves all remaining packs.
   292  func (r *Repository) Flush() error {
   293  	pms := []struct {
   294  		t  restic.BlobType
   295  		pm *packerManager
   296  	}{
   297  		{restic.DataBlob, r.dataPM},
   298  		{restic.TreeBlob, r.treePM},
   299  	}
   300  
   301  	for _, p := range pms {
   302  		p.pm.pm.Lock()
   303  
   304  		debug.Log("manually flushing %d packs", len(p.pm.packers))
   305  		for _, packer := range p.pm.packers {
   306  			err := r.savePacker(p.t, packer)
   307  			if err != nil {
   308  				p.pm.pm.Unlock()
   309  				return err
   310  			}
   311  		}
   312  		p.pm.packers = p.pm.packers[:0]
   313  		p.pm.pm.Unlock()
   314  	}
   315  
   316  	return nil
   317  }
   318  
   319  // Backend returns the backend for the repository.
   320  func (r *Repository) Backend() restic.Backend {
   321  	return r.be
   322  }
   323  
   324  // Index returns the currently used MasterIndex.
   325  func (r *Repository) Index() restic.Index {
   326  	return r.idx
   327  }
   328  
   329  // SetIndex instructs the repository to use the given index.
   330  func (r *Repository) SetIndex(i restic.Index) {
   331  	r.idx = i.(*MasterIndex)
   332  }
   333  
   334  // SaveIndex saves an index in the repository.
   335  func SaveIndex(ctx context.Context, repo restic.Repository, index *Index) (restic.ID, error) {
   336  	buf := bytes.NewBuffer(nil)
   337  
   338  	err := index.Finalize(buf)
   339  	if err != nil {
   340  		return restic.ID{}, err
   341  	}
   342  
   343  	return repo.SaveUnpacked(ctx, restic.IndexFile, buf.Bytes())
   344  }
   345  
   346  // saveIndex saves all indexes in the backend.
   347  func (r *Repository) saveIndex(ctx context.Context, indexes ...*Index) error {
   348  	for i, idx := range indexes {
   349  		debug.Log("Saving index %d", i)
   350  
   351  		sid, err := SaveIndex(ctx, r, idx)
   352  		if err != nil {
   353  			return err
   354  		}
   355  
   356  		debug.Log("Saved index %d as %v", i, sid.Str())
   357  	}
   358  
   359  	return nil
   360  }
   361  
   362  // SaveIndex saves all new indexes in the backend.
   363  func (r *Repository) SaveIndex(ctx context.Context) error {
   364  	return r.saveIndex(ctx, r.idx.NotFinalIndexes()...)
   365  }
   366  
   367  // SaveFullIndex saves all full indexes in the backend.
   368  func (r *Repository) SaveFullIndex(ctx context.Context) error {
   369  	return r.saveIndex(ctx, r.idx.FullIndexes()...)
   370  }
   371  
   372  const loadIndexParallelism = 20
   373  
   374  // LoadIndex loads all index files from the backend in parallel and stores them
   375  // in the master index. The first error that occurred is returned.
   376  func (r *Repository) LoadIndex(ctx context.Context) error {
   377  	debug.Log("Loading index")
   378  
   379  	errCh := make(chan error, 1)
   380  	indexes := make(chan *Index)
   381  
   382  	worker := func(ctx context.Context, id restic.ID) error {
   383  		idx, err := LoadIndex(ctx, r, id)
   384  		if err != nil {
   385  			fmt.Fprintf(os.Stderr, "%v, ignoring\n", err)
   386  			return nil
   387  		}
   388  
   389  		select {
   390  		case indexes <- idx:
   391  		case <-ctx.Done():
   392  		}
   393  
   394  		return nil
   395  	}
   396  
   397  	go func() {
   398  		defer close(indexes)
   399  		errCh <- FilesInParallel(ctx, r.be, restic.IndexFile, loadIndexParallelism,
   400  			ParallelWorkFuncParseID(worker))
   401  	}()
   402  
   403  	validIndex := restic.NewIDSet()
   404  	for idx := range indexes {
   405  		id, err := idx.ID()
   406  		if err == nil {
   407  			validIndex.Insert(id)
   408  		}
   409  		r.idx.Insert(idx)
   410  	}
   411  
   412  	if r.Cache != nil {
   413  		// clear old index files
   414  		err := r.Cache.Clear(restic.IndexFile, validIndex)
   415  		if err != nil {
   416  			fmt.Fprintf(os.Stderr, "error clearing index files in cache: %v\n", err)
   417  		}
   418  
   419  		packs := restic.NewIDSet()
   420  		for _, idx := range r.idx.All() {
   421  			for id := range idx.Packs() {
   422  				packs.Insert(id)
   423  			}
   424  		}
   425  
   426  		// clear old data files
   427  		err = r.Cache.Clear(restic.DataFile, packs)
   428  		if err != nil {
   429  			fmt.Fprintf(os.Stderr, "error clearing data files in cache: %v\n", err)
   430  		}
   431  
   432  		treePacks := restic.NewIDSet()
   433  		for _, idx := range r.idx.All() {
   434  			for _, id := range idx.TreePacks() {
   435  				treePacks.Insert(id)
   436  			}
   437  		}
   438  
   439  		// use readahead
   440  		cache := r.Cache.(*cache.Cache)
   441  		cache.PerformReadahead = func(h restic.Handle) bool {
   442  			if h.Type != restic.DataFile {
   443  				return false
   444  			}
   445  
   446  			id, err := restic.ParseID(h.Name)
   447  			if err != nil {
   448  				return false
   449  			}
   450  
   451  			return treePacks.Has(id)
   452  		}
   453  	}
   454  
   455  	if err := <-errCh; err != nil {
   456  		return err
   457  	}
   458  
   459  	return nil
   460  }
   461  
   462  // LoadIndex loads the index id from backend and returns it.
   463  func LoadIndex(ctx context.Context, repo restic.Repository, id restic.ID) (*Index, error) {
   464  	idx, err := LoadIndexWithDecoder(ctx, repo, id, DecodeIndex)
   465  	if err == nil {
   466  		return idx, nil
   467  	}
   468  
   469  	if errors.Cause(err) == ErrOldIndexFormat {
   470  		fmt.Fprintf(os.Stderr, "index %v has old format\n", id.Str())
   471  		return LoadIndexWithDecoder(ctx, repo, id, DecodeOldIndex)
   472  	}
   473  
   474  	return nil, err
   475  }
   476  
   477  // SearchKey finds a key with the supplied password, afterwards the config is
   478  // read and parsed. It tries at most maxKeys key files in the repo.
   479  func (r *Repository) SearchKey(ctx context.Context, password string, maxKeys int) error {
   480  	key, err := SearchKey(ctx, r, password, maxKeys)
   481  	if err != nil {
   482  		return err
   483  	}
   484  
   485  	r.key = key.master
   486  	r.dataPM.key = key.master
   487  	r.treePM.key = key.master
   488  	r.keyName = key.Name()
   489  	r.cfg, err = restic.LoadConfig(ctx, r)
   490  	return err
   491  }
   492  
   493  // Init creates a new master key with the supplied password, initializes and
   494  // saves the repository config.
   495  func (r *Repository) Init(ctx context.Context, password string) error {
   496  	has, err := r.be.Test(ctx, restic.Handle{Type: restic.ConfigFile})
   497  	if err != nil {
   498  		return err
   499  	}
   500  	if has {
   501  		return errors.New("repository master key and config already initialized")
   502  	}
   503  
   504  	cfg, err := restic.CreateConfig()
   505  	if err != nil {
   506  		return err
   507  	}
   508  
   509  	return r.init(ctx, password, cfg)
   510  }
   511  
   512  // init creates a new master key with the supplied password and uses it to save
   513  // the config into the repo.
   514  func (r *Repository) init(ctx context.Context, password string, cfg restic.Config) error {
   515  	key, err := createMasterKey(r, password)
   516  	if err != nil {
   517  		return err
   518  	}
   519  
   520  	r.key = key.master
   521  	r.dataPM.key = key.master
   522  	r.treePM.key = key.master
   523  	r.keyName = key.Name()
   524  	r.cfg = cfg
   525  	_, err = r.SaveJSONUnpacked(ctx, restic.ConfigFile, cfg)
   526  	return err
   527  }
   528  
   529  // Key returns the current master key.
   530  func (r *Repository) Key() *crypto.Key {
   531  	return r.key
   532  }
   533  
   534  // KeyName returns the name of the current key in the backend.
   535  func (r *Repository) KeyName() string {
   536  	return r.keyName
   537  }
   538  
   539  // List returns a channel that yields all IDs of type t in the backend.
   540  func (r *Repository) List(ctx context.Context, t restic.FileType) <-chan restic.ID {
   541  	out := make(chan restic.ID)
   542  	go func() {
   543  		defer close(out)
   544  		for strID := range r.be.List(ctx, t) {
   545  			if id, err := restic.ParseID(strID); err == nil {
   546  				select {
   547  				case out <- id:
   548  				case <-ctx.Done():
   549  					return
   550  				}
   551  			}
   552  		}
   553  	}()
   554  	return out
   555  }
   556  
   557  // ListPack returns the list of blobs saved in the pack id and the length of
   558  // the file as stored in the backend.
   559  func (r *Repository) ListPack(ctx context.Context, id restic.ID) ([]restic.Blob, int64, error) {
   560  	h := restic.Handle{Type: restic.DataFile, Name: id.String()}
   561  
   562  	blobInfo, err := r.Backend().Stat(ctx, h)
   563  	if err != nil {
   564  		return nil, 0, err
   565  	}
   566  
   567  	blobs, err := pack.List(r.Key(), restic.ReaderAt(r.Backend(), h), blobInfo.Size)
   568  	if err != nil {
   569  		return nil, 0, err
   570  	}
   571  
   572  	return blobs, blobInfo.Size, nil
   573  }
   574  
   575  // Delete calls backend.Delete() if implemented, and returns an error
   576  // otherwise.
   577  func (r *Repository) Delete(ctx context.Context) error {
   578  	return r.be.Delete(ctx)
   579  }
   580  
   581  // Close closes the repository by closing the backend.
   582  func (r *Repository) Close() error {
   583  	return r.be.Close()
   584  }
   585  
   586  // LoadBlob loads a blob of type t from the repository to the buffer. buf must
   587  // be large enough to hold the encrypted blob, since it is used as scratch
   588  // space.
   589  func (r *Repository) LoadBlob(ctx context.Context, t restic.BlobType, id restic.ID, buf []byte) (int, error) {
   590  	debug.Log("load blob %v into buf (len %v, cap %v)", id.Str(), len(buf), cap(buf))
   591  	size, err := r.idx.LookupSize(id, t)
   592  	if err != nil {
   593  		return 0, err
   594  	}
   595  
   596  	if cap(buf) < restic.CiphertextLength(int(size)) {
   597  		return 0, errors.Errorf("buffer is too small for data blob (%d < %d)", cap(buf), restic.CiphertextLength(int(size)))
   598  	}
   599  
   600  	n, err := r.loadBlob(ctx, id, t, buf)
   601  	if err != nil {
   602  		return 0, err
   603  	}
   604  	buf = buf[:n]
   605  
   606  	debug.Log("loaded %d bytes into buf %p", len(buf), buf)
   607  
   608  	return len(buf), err
   609  }
   610  
   611  // SaveBlob saves a blob of type t into the repository. If id is the null id, it
   612  // will be computed and returned.
   613  func (r *Repository) SaveBlob(ctx context.Context, t restic.BlobType, buf []byte, id restic.ID) (restic.ID, error) {
   614  	var i *restic.ID
   615  	if !id.IsNull() {
   616  		i = &id
   617  	}
   618  	return r.SaveAndEncrypt(ctx, t, buf, i)
   619  }
   620  
   621  // LoadTree loads a tree from the repository.
   622  func (r *Repository) LoadTree(ctx context.Context, id restic.ID) (*restic.Tree, error) {
   623  	debug.Log("load tree %v", id.Str())
   624  
   625  	size, err := r.idx.LookupSize(id, restic.TreeBlob)
   626  	if err != nil {
   627  		return nil, err
   628  	}
   629  
   630  	debug.Log("size is %d, create buffer", size)
   631  	buf := restic.NewBlobBuffer(int(size))
   632  
   633  	n, err := r.loadBlob(ctx, id, restic.TreeBlob, buf)
   634  	if err != nil {
   635  		return nil, err
   636  	}
   637  	buf = buf[:n]
   638  
   639  	t := &restic.Tree{}
   640  	err = json.Unmarshal(buf, t)
   641  	if err != nil {
   642  		return nil, err
   643  	}
   644  
   645  	return t, nil
   646  }
   647  
   648  // SaveTree stores a tree into the repository and returns the ID. The ID is
   649  // checked against the index. The tree is only stored when the index does not
   650  // contain the ID.
   651  func (r *Repository) SaveTree(ctx context.Context, t *restic.Tree) (restic.ID, error) {
   652  	buf, err := json.Marshal(t)
   653  	if err != nil {
   654  		return restic.ID{}, errors.Wrap(err, "MarshalJSON")
   655  	}
   656  
   657  	// append a newline so that the data is always consistent (json.Encoder
   658  	// adds a newline after each object)
   659  	buf = append(buf, '\n')
   660  
   661  	id := restic.Hash(buf)
   662  	if r.idx.Has(id, restic.TreeBlob) {
   663  		return id, nil
   664  	}
   665  
   666  	_, err = r.SaveBlob(ctx, restic.TreeBlob, buf, id)
   667  	return id, err
   668  }