| 
									
										
										
										
											2015-05-09 23:52:03 +02:00
										 |  |  | package repository | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2018-02-11 22:41:59 -05:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2015-07-26 21:58:03 +02:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2020-10-10 21:31:40 +02:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-05 12:20:07 +02:00
										 |  |  | 	"github.com/cenkalti/backoff/v4" | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | 	"github.com/klauspost/compress/zstd" | 
					
						
							| 
									
										
										
										
											2020-09-19 12:41:52 +02:00
										 |  |  | 	"github.com/restic/chunker" | 
					
						
							| 
									
										
											  
											
												backup: add --dry-run/-n flag to show what would happen.
This can be used to check how large a backup is or validate exclusions.
It does not actually write any data to the underlying backend. This is
implemented as a simple overlay backend that accepts writes without
forwarding them, passes through reads, and generally does the minimal
necessary to pretend that progress is actually happening.
Fixes #1542
Example usage:
$ restic -vv --dry-run . | grep add
new       /changelog/unreleased/issue-1542, saved in 0.000s (350 B added)
modified  /cmd/restic/cmd_backup.go, saved in 0.000s (16.543 KiB added)
modified  /cmd/restic/global.go, saved in 0.000s (0 B added)
new       /internal/backend/dry/dry_backend_test.go, saved in 0.000s (3.866 KiB added)
new       /internal/backend/dry/dry_backend.go, saved in 0.000s (3.744 KiB added)
modified  /internal/backend/test/tests.go, saved in 0.000s (0 B added)
modified  /internal/repository/repository.go, saved in 0.000s (20.707 KiB added)
modified  /internal/ui/backup.go, saved in 0.000s (9.110 KiB added)
modified  /internal/ui/jsonstatus/status.go, saved in 0.001s (11.055 KiB added)
modified  /restic, saved in 0.131s (25.542 MiB added)
Would add to the repo: 25.892 MiB
											
										 
											2019-06-12 20:39:13 -07:00
										 |  |  | 	"github.com/restic/restic/internal/backend/dryrun" | 
					
						
							| 
									
										
										
										
											2017-09-24 22:54:04 +02:00
										 |  |  | 	"github.com/restic/restic/internal/cache" | 
					
						
							| 
									
										
										
										
											2018-10-28 21:12:15 +01:00
										 |  |  | 	"github.com/restic/restic/internal/crypto" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/debug" | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 	"github.com/restic/restic/internal/errors" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/pack" | 
					
						
							| 
									
										
										
										
											2018-10-28 21:12:15 +01:00
										 |  |  | 	"github.com/restic/restic/internal/restic" | 
					
						
							| 
									
										
										
										
											2020-10-10 21:31:40 +02:00
										 |  |  | 	"github.com/restic/restic/internal/ui/progress" | 
					
						
							| 
									
										
										
										
											2020-03-19 11:27:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 21:27:28 +01:00
										 |  |  | 	"golang.org/x/sync/errgroup" | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 16:15:40 +02:00
										 |  |  | const MaxStreamBufferSize = 4 * 1024 * 1024 | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-09 23:59:58 +02:00
										 |  |  | // Repository is used to access a repository in a backend. | 
					
						
							|  |  |  | type Repository struct { | 
					
						
							| 
									
										
										
										
											2016-08-31 20:29:54 +02:00
										 |  |  | 	be      restic.Backend | 
					
						
							| 
									
										
										
										
											2016-08-31 22:39:36 +02:00
										 |  |  | 	cfg     restic.Config | 
					
						
							| 
									
										
										
										
											2015-05-03 18:04:13 +02:00
										 |  |  | 	key     *crypto.Key | 
					
						
							|  |  |  | 	keyName string | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 	idx     *MasterIndex | 
					
						
							| 
									
										
										
										
											2020-03-02 18:27:52 +01:00
										 |  |  | 	Cache   *cache.Cache | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 20:34:05 +02:00
										 |  |  | 	opts Options | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-12 09:24:38 +02:00
										 |  |  | 	noAutoIndexUpdate bool | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	packerWg *errgroup.Group | 
					
						
							|  |  |  | 	uploader *packerUploader | 
					
						
							|  |  |  | 	treePM   *packerManager | 
					
						
							|  |  |  | 	dataPM   *packerManager | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-19 21:15:31 +01:00
										 |  |  | 	allocEnc sync.Once | 
					
						
							|  |  |  | 	allocDec sync.Once | 
					
						
							|  |  |  | 	enc      *zstd.Encoder | 
					
						
							|  |  |  | 	dec      *zstd.Decoder | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 20:34:05 +02:00
										 |  |  | type Options struct { | 
					
						
							|  |  |  | 	Compression CompressionMode | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CompressionMode configures if data should be compressed. | 
					
						
							|  |  |  | type CompressionMode uint | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Constants for the different compression levels. | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	CompressionAuto CompressionMode = 0 | 
					
						
							|  |  |  | 	CompressionOff  CompressionMode = 1 | 
					
						
							|  |  |  | 	CompressionMax  CompressionMode = 2 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Set implements the method needed for pflag command flag parsing. | 
					
						
							|  |  |  | func (c *CompressionMode) Set(s string) error { | 
					
						
							|  |  |  | 	switch s { | 
					
						
							|  |  |  | 	case "auto": | 
					
						
							|  |  |  | 		*c = CompressionAuto | 
					
						
							|  |  |  | 	case "off": | 
					
						
							|  |  |  | 		*c = CompressionOff | 
					
						
							|  |  |  | 	case "max": | 
					
						
							|  |  |  | 		*c = CompressionMax | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return fmt.Errorf("invalid compression mode %q, must be one of (auto|off|max)", s) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *CompressionMode) String() string { | 
					
						
							|  |  |  | 	switch *c { | 
					
						
							|  |  |  | 	case CompressionAuto: | 
					
						
							|  |  |  | 		return "auto" | 
					
						
							|  |  |  | 	case CompressionOff: | 
					
						
							|  |  |  | 		return "off" | 
					
						
							|  |  |  | 	case CompressionMax: | 
					
						
							|  |  |  | 		return "max" | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return "invalid" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | func (c *CompressionMode) Type() string { | 
					
						
							|  |  |  | 	return "mode" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 22:53:03 +02:00
										 |  |  | // New returns a new repository with backend be. | 
					
						
							| 
									
										
										
										
											2022-04-13 20:34:05 +02:00
										 |  |  | func New(be restic.Backend, opts Options) *Repository { | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	repo := &Repository{ | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		be:   be, | 
					
						
							|  |  |  | 		opts: opts, | 
					
						
							|  |  |  | 		idx:  NewMasterIndex(), | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 13:14:06 +01:00
										 |  |  | 	return repo | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-28 22:24:43 +02:00
										 |  |  | // DisableAutoIndexUpdate deactives the automatic finalization and upload of new | 
					
						
							|  |  |  | // indexes once these are full | 
					
						
							| 
									
										
										
										
											2020-06-12 09:24:38 +02:00
										 |  |  | func (r *Repository) DisableAutoIndexUpdate() { | 
					
						
							|  |  |  | 	r.noAutoIndexUpdate = true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 23:12:43 +02:00
										 |  |  | // setConfig assigns the given config and updates the repository parameters accordingly | 
					
						
							|  |  |  | func (r *Repository) setConfig(cfg restic.Config) { | 
					
						
							|  |  |  | 	r.cfg = cfg | 
					
						
							|  |  |  | 	if r.cfg.Version >= 2 { | 
					
						
							|  |  |  | 		r.idx.markCompressed() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-31 22:51:35 +02:00
										 |  |  | // Config returns the repository configuration. | 
					
						
							| 
									
										
										
										
											2016-08-31 22:39:36 +02:00
										 |  |  | func (r *Repository) Config() restic.Config { | 
					
						
							|  |  |  | 	return r.cfg | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-10 13:10:08 +02:00
										 |  |  | // UseCache replaces the backend with the wrapped cache. | 
					
						
							| 
									
										
										
										
											2020-03-02 18:27:52 +01:00
										 |  |  | func (r *Repository) UseCache(c *cache.Cache) { | 
					
						
							| 
									
										
										
										
											2017-06-10 13:10:08 +02:00
										 |  |  | 	if c == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	debug.Log("using cache") | 
					
						
							|  |  |  | 	r.Cache = c | 
					
						
							|  |  |  | 	r.be = c.Wrap(r.be) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												backup: add --dry-run/-n flag to show what would happen.
This can be used to check how large a backup is or validate exclusions.
It does not actually write any data to the underlying backend. This is
implemented as a simple overlay backend that accepts writes without
forwarding them, passes through reads, and generally does the minimal
necessary to pretend that progress is actually happening.
Fixes #1542
Example usage:
$ restic -vv --dry-run . | grep add
new       /changelog/unreleased/issue-1542, saved in 0.000s (350 B added)
modified  /cmd/restic/cmd_backup.go, saved in 0.000s (16.543 KiB added)
modified  /cmd/restic/global.go, saved in 0.000s (0 B added)
new       /internal/backend/dry/dry_backend_test.go, saved in 0.000s (3.866 KiB added)
new       /internal/backend/dry/dry_backend.go, saved in 0.000s (3.744 KiB added)
modified  /internal/backend/test/tests.go, saved in 0.000s (0 B added)
modified  /internal/repository/repository.go, saved in 0.000s (20.707 KiB added)
modified  /internal/ui/backup.go, saved in 0.000s (9.110 KiB added)
modified  /internal/ui/jsonstatus/status.go, saved in 0.001s (11.055 KiB added)
modified  /restic, saved in 0.131s (25.542 MiB added)
Would add to the repo: 25.892 MiB
											
										 
											2019-06-12 20:39:13 -07:00
										 |  |  | // SetDryRun sets the repo backend into dry-run mode. | 
					
						
							|  |  |  | func (r *Repository) SetDryRun() { | 
					
						
							|  |  |  | 	r.be = dryrun.New(r.be) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | // PrefixLength returns the number of bytes required so that all prefixes of | 
					
						
							|  |  |  | // all IDs of type t are unique. | 
					
						
							| 
									
										
										
										
											2020-04-10 11:31:32 +02:00
										 |  |  | func (r *Repository) PrefixLength(ctx context.Context, t restic.FileType) (int, error) { | 
					
						
							|  |  |  | 	return restic.PrefixLength(ctx, r.be, t) | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 00:01:27 +01:00
										 |  |  | // LoadUnpacked loads and decrypts the file with the given type and ID, using | 
					
						
							| 
									
										
										
										
											2019-03-24 21:59:14 +01:00
										 |  |  | // the supplied buffer (which must be empty). If the buffer is nil, a new | 
					
						
							|  |  |  | // buffer will be allocated and returned. | 
					
						
							| 
									
										
										
										
											2022-02-13 00:01:27 +01:00
										 |  |  | func (r *Repository) LoadUnpacked(ctx context.Context, buf []byte, t restic.FileType, id restic.ID) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2019-03-24 21:59:14 +01:00
										 |  |  | 	if len(buf) != 0 { | 
					
						
							|  |  |  | 		panic("buf is not empty") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 20:49:41 +01:00
										 |  |  | 	debug.Log("load %v with id %v", t, id) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 14:29:34 +01:00
										 |  |  | 	if t == restic.ConfigFile { | 
					
						
							|  |  |  | 		id = restic.ID{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-01 21:19:30 +02:00
										 |  |  | 	h := restic.Handle{Type: t, Name: id.String()} | 
					
						
							| 
									
										
										
										
											2019-03-24 21:59:14 +01:00
										 |  |  | 	err := r.be.Load(ctx, h, 0, 0, func(rd io.Reader) error { | 
					
						
							|  |  |  | 		// make sure this call is idempotent, in case an error occurs | 
					
						
							|  |  |  | 		wr := bytes.NewBuffer(buf[:0]) | 
					
						
							|  |  |  | 		_, cerr := io.Copy(wr, rd) | 
					
						
							|  |  |  | 		if cerr != nil { | 
					
						
							|  |  |  | 			return cerr | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf = wr.Bytes() | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-28 11:50:23 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-31 20:29:54 +02:00
										 |  |  | 	if t != restic.ConfigFile && !restic.Hash(buf).Equal(id) { | 
					
						
							| 
									
										
										
										
											2017-02-11 14:28:15 +01:00
										 |  |  | 		return nil, errors.Errorf("load %v: invalid data returned", h) | 
					
						
							| 
									
										
										
										
											2015-03-28 11:50:23 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	nonce, ciphertext := buf[:r.key.NonceSize()], buf[r.key.NonceSize():] | 
					
						
							|  |  |  | 	plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | 	if t != restic.ConfigFile { | 
					
						
							|  |  |  | 		return r.decompressUnpacked(plaintext) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	return plaintext, nil | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 09:17:33 +01:00
										 |  |  | type haver interface { | 
					
						
							|  |  |  | 	Has(restic.Handle) bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sortCachedPacksFirst moves all cached pack files to the front of blobs. | 
					
						
							| 
									
										
										
										
											2020-03-10 15:56:08 +01:00
										 |  |  | func sortCachedPacksFirst(cache haver, blobs []restic.PackedBlob) { | 
					
						
							| 
									
										
										
										
											2020-03-06 09:17:33 +01:00
										 |  |  | 	if cache == nil { | 
					
						
							| 
									
										
										
										
											2020-03-10 15:56:08 +01:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2017-07-16 21:06:43 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:42:29 +02:00
										 |  |  | 	// no need to sort a list with one element | 
					
						
							|  |  |  | 	if len(blobs) == 1 { | 
					
						
							| 
									
										
										
										
											2020-03-10 15:56:08 +01:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2019-07-06 17:42:29 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 09:18:38 +01:00
										 |  |  | 	cached := blobs[:0] | 
					
						
							| 
									
										
										
										
											2017-07-16 21:06:43 +02:00
										 |  |  | 	noncached := make([]restic.PackedBlob, 0, len(blobs)/2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, blob := range blobs { | 
					
						
							| 
									
										
										
										
											2020-08-16 11:16:38 +02:00
										 |  |  | 		if cache.Has(restic.Handle{Type: restic.PackFile, Name: blob.PackID.String()}) { | 
					
						
							| 
									
										
										
										
											2017-07-16 21:06:43 +02:00
										 |  |  | 			cached = append(cached, blob) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		noncached = append(noncached, blob) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 15:56:08 +01:00
										 |  |  | 	copy(blobs[len(cached):], noncached) | 
					
						
							| 
									
										
										
										
											2017-07-16 21:06:43 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | // LoadBlob loads a blob of type t from the repository. | 
					
						
							|  |  |  | // It may use all of buf[:cap(buf)] as scratch space. | 
					
						
							|  |  |  | func (r *Repository) LoadBlob(ctx context.Context, t restic.BlobType, id restic.ID, buf []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	debug.Log("load %v with id %v (buf len %v, cap %d)", t, id, len(buf), cap(buf)) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// lookup packs | 
					
						
							| 
									
										
										
										
											2020-11-05 22:18:00 +01:00
										 |  |  | 	blobs := r.idx.Lookup(restic.BlobHandle{ID: id, Type: t}) | 
					
						
							| 
									
										
										
										
											2020-06-14 13:26:10 +02:00
										 |  |  | 	if len(blobs) == 0 { | 
					
						
							| 
									
										
										
										
											2018-01-25 20:49:41 +01:00
										 |  |  | 		debug.Log("id %v not found in index", id) | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 		return nil, errors.Errorf("id %v not found in repository", id) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-16 21:06:43 +02:00
										 |  |  | 	// try cached pack files first | 
					
						
							| 
									
										
										
										
											2020-03-10 15:56:08 +01:00
										 |  |  | 	sortCachedPacksFirst(r.Cache, blobs) | 
					
						
							| 
									
										
										
										
											2017-07-16 21:06:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 22:18:02 +02:00
										 |  |  | 	var lastError error | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 	for _, blob := range blobs { | 
					
						
							| 
									
										
										
										
											2018-01-25 20:49:41 +01:00
										 |  |  | 		debug.Log("blob %v/%v found: %v", t, id, blob) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if blob.Type != t { | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 			debug.Log("blob %v has wrong block type, want %v", blob, t) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// load blob from pack | 
					
						
							| 
									
										
										
										
											2020-07-28 10:13:11 +02:00
										 |  |  | 		bt := t | 
					
						
							|  |  |  | 		if r.idx.IsMixedPack(blob.PackID) { | 
					
						
							|  |  |  | 			bt = restic.InvalidBlob | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		h := restic.Handle{Type: restic.PackFile, | 
					
						
							|  |  |  | 			Name: blob.PackID.String(), ContainedBlobType: bt} | 
					
						
							| 
									
										
										
										
											2017-01-24 11:27:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case cap(buf) < int(blob.Length): | 
					
						
							|  |  |  | 			buf = make([]byte, blob.Length) | 
					
						
							|  |  |  | 		case len(buf) != int(blob.Length): | 
					
						
							|  |  |  | 			buf = buf[:blob.Length] | 
					
						
							| 
									
										
										
										
											2017-01-24 11:27:36 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 		n, err := restic.ReadAt(ctx, r.be, h, int64(blob.Offset), buf) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 			debug.Log("error loading blob %v: %v", blob, err) | 
					
						
							| 
									
										
										
										
											2016-08-28 22:18:02 +02:00
										 |  |  | 			lastError = err | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if uint(n) != blob.Length { | 
					
						
							| 
									
										
										
										
											2016-08-28 22:18:02 +02:00
										 |  |  | 			lastError = errors.Errorf("error loading blob %v: wrong length returned, want %d, got %d", | 
					
						
							|  |  |  | 				id.Str(), blob.Length, uint(n)) | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 			debug.Log("lastError: %v", lastError) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// decrypt | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 		nonce, ciphertext := buf[:r.key.NonceSize()], buf[r.key.NonceSize():] | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 		plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-28 22:18:02 +02:00
										 |  |  | 			lastError = errors.Errorf("decrypting blob %v failed: %v", id, err) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 		if blob.IsCompressed() { | 
					
						
							| 
									
										
										
										
											2022-02-19 21:15:31 +01:00
										 |  |  | 			plaintext, err = r.getZstdDecoder().DecodeAll(plaintext, make([]byte, 0, blob.DataLength())) | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				lastError = errors.Errorf("decompressing blob %v failed: %v", id, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 		// check hash | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 		if !restic.Hash(plaintext).Equal(id) { | 
					
						
							| 
									
										
										
										
											2016-08-28 22:18:02 +02:00
										 |  |  | 			lastError = errors.Errorf("blob %v returned invalid hash", id) | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 		if len(plaintext) > cap(buf) { | 
					
						
							|  |  |  | 			return plaintext, nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 		// move decrypted data to the start of the buffer | 
					
						
							| 
									
										
										
										
											2022-06-06 16:26:38 +02:00
										 |  |  | 		buf = buf[:len(plaintext)] | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 		copy(buf, plaintext) | 
					
						
							| 
									
										
										
										
											2022-06-06 16:26:38 +02:00
										 |  |  | 		return buf, nil | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 22:18:02 +02:00
										 |  |  | 	if lastError != nil { | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 		return nil, lastError | 
					
						
							| 
									
										
										
										
											2016-08-28 22:18:02 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 	return nil, errors.Errorf("loading blob %v from %v packs failed", id.Str(), len(blobs)) | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-04 20:39:45 +02:00
										 |  |  | // LoadJSONUnpacked decrypts the data and afterwards calls json.Unmarshal on | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | // the item. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) LoadJSONUnpacked(ctx context.Context, t restic.FileType, id restic.ID, item interface{}) (err error) { | 
					
						
							| 
									
										
										
										
											2022-02-13 00:01:27 +01:00
										 |  |  | 	buf, err := r.LoadUnpacked(ctx, nil, t, id) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 00:12:09 +01:00
										 |  |  | 	return json.Unmarshal(buf, item) | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-26 16:43:42 +02:00
										 |  |  | // LookupBlobSize returns the size of blob id. | 
					
						
							| 
									
										
										
										
											2018-01-12 01:20:12 -05:00
										 |  |  | func (r *Repository) LookupBlobSize(id restic.ID, tpe restic.BlobType) (uint, bool) { | 
					
						
							| 
									
										
										
										
											2020-11-05 22:18:00 +01:00
										 |  |  | 	return r.idx.LookupSize(restic.BlobHandle{ID: id, Type: tpe}) | 
					
						
							| 
									
										
										
										
											2015-07-26 16:43:42 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-19 21:15:31 +01:00
										 |  |  | func (r *Repository) getZstdEncoder() *zstd.Encoder { | 
					
						
							|  |  |  | 	r.allocEnc.Do(func() { | 
					
						
							| 
									
										
										
										
											2022-04-13 20:34:05 +02:00
										 |  |  | 		level := zstd.SpeedDefault | 
					
						
							|  |  |  | 		if r.opts.Compression == CompressionMax { | 
					
						
							|  |  |  | 			level = zstd.SpeedBestCompression | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-20 20:46:11 +02:00
										 |  |  | 		opts := []zstd.EOption{ | 
					
						
							|  |  |  | 			// Set the compression level configured. | 
					
						
							|  |  |  | 			zstd.WithEncoderLevel(level), | 
					
						
							|  |  |  | 			// Disable CRC, we have enough checks in place, makes the | 
					
						
							|  |  |  | 			// compressed data four bytes shorter. | 
					
						
							|  |  |  | 			zstd.WithEncoderCRC(false), | 
					
						
							|  |  |  | 			// Set a window of 512kbyte, so we have good lookbehind for usual | 
					
						
							|  |  |  | 			// blob sizes. | 
					
						
							|  |  |  | 			zstd.WithWindowSize(512 * 1024), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		enc, err := zstd.NewWriter(nil, opts...) | 
					
						
							| 
									
										
										
										
											2022-02-19 21:15:31 +01:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		r.enc = enc | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return r.enc | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *Repository) getZstdDecoder() *zstd.Decoder { | 
					
						
							|  |  |  | 	r.allocDec.Do(func() { | 
					
						
							| 
									
										
										
										
											2022-04-20 20:46:11 +02:00
										 |  |  | 		opts := []zstd.DOption{ | 
					
						
							|  |  |  | 			// Use all available cores. | 
					
						
							|  |  |  | 			zstd.WithDecoderConcurrency(0), | 
					
						
							|  |  |  | 			// Limit the maximum decompressed memory. Set to a very high, | 
					
						
							|  |  |  | 			// conservative value. | 
					
						
							|  |  |  | 			zstd.WithDecoderMaxMemory(16 * 1024 * 1024 * 1024), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dec, err := zstd.NewReader(nil, opts...) | 
					
						
							| 
									
										
										
										
											2022-02-19 21:15:31 +01:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		r.dec = dec | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return r.dec | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 00:05:14 +01:00
										 |  |  | // saveAndEncrypt encrypts data and stores it to the backend as type t. If data | 
					
						
							| 
									
										
										
										
											2022-05-01 14:26:57 +02:00
										 |  |  | // is small enough, it will be packed together with other small blobs. The | 
					
						
							|  |  |  | // caller must ensure that the id matches the data. Returned is the size data | 
					
						
							|  |  |  | // occupies in the repo (compressed or not, including the encryption overhead). | 
					
						
							|  |  |  | func (r *Repository) saveAndEncrypt(ctx context.Context, t restic.BlobType, data []byte, id restic.ID) (size int, err error) { | 
					
						
							| 
									
										
										
										
											2018-01-25 20:49:41 +01:00
										 |  |  | 	debug.Log("save id %v (%v, %d bytes)", id, t, len(data)) | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 	uncompressedLength := 0 | 
					
						
							|  |  |  | 	if r.cfg.Version > 1 { | 
					
						
							| 
									
										
										
										
											2022-04-13 20:34:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// we have a repo v2, so compression is available. if the user opts to | 
					
						
							|  |  |  | 		// not compress, we won't compress any data, but everything else is | 
					
						
							|  |  |  | 		// compressed. | 
					
						
							|  |  |  | 		if r.opts.Compression != CompressionOff || t != restic.DataBlob { | 
					
						
							|  |  |  | 			uncompressedLength = len(data) | 
					
						
							|  |  |  | 			data = r.getZstdEncoder().EncodeAll(data, nil) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	nonce := crypto.NewRandomNonce() | 
					
						
							| 
									
										
										
										
											2020-02-26 23:26:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ciphertext := make([]byte, 0, restic.CiphertextLength(len(data))) | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	ciphertext = append(ciphertext, nonce...) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | 	// encrypt blob | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	ciphertext = r.key.Seal(ciphertext, nonce, data, nil) | 
					
						
							| 
									
										
										
										
											2015-01-10 23:40:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	// find suitable packer and add blob | 
					
						
							| 
									
										
										
										
											2017-07-16 20:16:02 +02:00
										 |  |  | 	var pm *packerManager | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch t { | 
					
						
							|  |  |  | 	case restic.TreeBlob: | 
					
						
							|  |  |  | 		pm = r.treePM | 
					
						
							|  |  |  | 	case restic.DataBlob: | 
					
						
							|  |  |  | 		pm = r.dataPM | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic(fmt.Sprintf("invalid type: %v", t)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	return pm.SaveBlob(ctx, t, id, ciphertext, uncompressedLength) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SaveJSONUnpacked serialises item as JSON and encrypts and saves it in the | 
					
						
							|  |  |  | // backend as type t, without a pack. It returns the storage hash. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) SaveJSONUnpacked(ctx context.Context, t restic.FileType, item interface{}) (restic.ID, error) { | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 	debug.Log("save new blob %v", t) | 
					
						
							| 
									
										
										
										
											2016-01-24 18:50:41 +01:00
										 |  |  | 	plaintext, err := json.Marshal(item) | 
					
						
							| 
									
										
										
										
											2015-02-15 17:26:08 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-31 20:29:54 +02:00
										 |  |  | 		return restic.ID{}, errors.Wrap(err, "json.Marshal") | 
					
						
							| 
									
										
										
										
											2015-02-15 17:26:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | 	return r.SaveUnpacked(ctx, t, plaintext) | 
					
						
							| 
									
										
										
										
											2016-01-24 18:52:11 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | func (r *Repository) compressUnpacked(p []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	// compression is only available starting from version 2 | 
					
						
							|  |  |  | 	if r.cfg.Version < 2 { | 
					
						
							|  |  |  | 		return p, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// version byte | 
					
						
							|  |  |  | 	out := []byte{2} | 
					
						
							| 
									
										
										
										
											2022-02-19 21:15:31 +01:00
										 |  |  | 	out = r.getZstdEncoder().EncodeAll(p, out) | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | 	return out, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *Repository) decompressUnpacked(p []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	// compression is only available starting from version 2 | 
					
						
							|  |  |  | 	if r.cfg.Version < 2 { | 
					
						
							|  |  |  | 		return p, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-16 21:05:15 +02:00
										 |  |  | 	if len(p) == 0 { | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | 		// too short for version header | 
					
						
							|  |  |  | 		return p, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if p[0] == '[' || p[0] == '{' { | 
					
						
							|  |  |  | 		// probably raw JSON | 
					
						
							|  |  |  | 		return p, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// version | 
					
						
							|  |  |  | 	if p[0] != 2 { | 
					
						
							|  |  |  | 		return nil, errors.New("not supported encoding format") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-19 21:15:31 +01:00
										 |  |  | 	return r.getZstdDecoder().DecodeAll(p[1:], nil) | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 18:52:11 +01:00
										 |  |  | // SaveUnpacked encrypts data and stores it in the backend. Returned is the | 
					
						
							|  |  |  | // storage hash. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) SaveUnpacked(ctx context.Context, t restic.FileType, p []byte) (id restic.ID, err error) { | 
					
						
							| 
									
										
										
										
											2022-02-13 00:12:40 +01:00
										 |  |  | 	if t != restic.ConfigFile { | 
					
						
							|  |  |  | 		p, err = r.compressUnpacked(p) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return restic.ID{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-13 12:57:05 +01:00
										 |  |  | 	ciphertext := restic.NewBlobBuffer(len(p)) | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	ciphertext = ciphertext[:0] | 
					
						
							|  |  |  | 	nonce := crypto.NewRandomNonce() | 
					
						
							|  |  |  | 	ciphertext = append(ciphertext, nonce...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ciphertext = r.key.Seal(ciphertext, nonce, p, nil) | 
					
						
							| 
									
										
										
										
											2015-02-15 17:26:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 14:29:34 +01:00
										 |  |  | 	if t == restic.ConfigFile { | 
					
						
							|  |  |  | 		id = restic.ID{} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		id = restic.Hash(ciphertext) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-01 21:19:30 +02:00
										 |  |  | 	h := restic.Handle{Type: t, Name: id.String()} | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-19 12:39:48 +01:00
										 |  |  | 	err = r.be.Save(ctx, h, restic.NewByteReader(ciphertext, r.be.Hasher())) | 
					
						
							| 
									
										
										
										
											2015-02-15 17:26:08 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 		debug.Log("error saving blob %v: %v", h, err) | 
					
						
							| 
									
										
										
										
											2016-08-31 20:29:54 +02:00
										 |  |  | 		return restic.ID{}, err | 
					
						
							| 
									
										
										
										
											2015-02-15 17:26:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 	debug.Log("blob %v saved", h) | 
					
						
							| 
									
										
										
										
											2016-01-24 18:50:41 +01:00
										 |  |  | 	return id, nil | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | // Flush saves all remaining packs and the index | 
					
						
							| 
									
										
										
										
											2017-11-22 06:27:29 -05:00
										 |  |  | func (r *Repository) Flush(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2022-05-26 13:30:52 +02:00
										 |  |  | 	if err := r.flushPacks(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-10 18:54:13 +02:00
										 |  |  | 	// Save index after flushing only if noAutoIndexUpdate is not set | 
					
						
							|  |  |  | 	if r.noAutoIndexUpdate { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-26 12:38:18 +02:00
										 |  |  | 	return r.idx.SaveIndex(ctx, r) | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | func (r *Repository) StartPackUploader(ctx context.Context, wg *errgroup.Group) { | 
					
						
							|  |  |  | 	if r.packerWg != nil { | 
					
						
							|  |  |  | 		panic("uploader already started") | 
					
						
							| 
									
										
										
										
											2017-07-16 20:24:37 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	innerWg, ctx := errgroup.WithContext(ctx) | 
					
						
							|  |  |  | 	r.packerWg = innerWg | 
					
						
							|  |  |  | 	r.uploader = newPackerUploader(ctx, innerWg, r, r.be.Connections()) | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	r.treePM = newPackerManager(r.key, restic.TreeBlob, r.uploader.QueuePacker) | 
					
						
							|  |  |  | 	r.dataPM = newPackerManager(r.key, restic.DataBlob, r.uploader.QueuePacker) | 
					
						
							| 
									
										
										
										
											2017-07-16 20:16:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	wg.Go(func() error { | 
					
						
							|  |  |  | 		return innerWg.Wait() | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FlushPacks saves all remaining packs. | 
					
						
							|  |  |  | func (r *Repository) flushPacks(ctx context.Context) error { | 
					
						
							|  |  |  | 	if r.packerWg == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err := r.treePM.Flush(ctx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = r.dataPM.Flush(ctx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	r.uploader.TriggerShutdown() | 
					
						
							|  |  |  | 	err = r.packerWg.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	r.treePM = nil | 
					
						
							|  |  |  | 	r.dataPM = nil | 
					
						
							|  |  |  | 	r.uploader = nil | 
					
						
							|  |  |  | 	r.packerWg = nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 22:53:03 +02:00
										 |  |  | // Backend returns the backend for the repository. | 
					
						
							| 
									
										
										
										
											2016-08-31 20:29:54 +02:00
										 |  |  | func (r *Repository) Backend() restic.Backend { | 
					
						
							| 
									
										
										
										
											2015-07-02 21:52:57 +02:00
										 |  |  | 	return r.be | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | // Index returns the currently used MasterIndex. | 
					
						
							| 
									
										
										
										
											2020-07-25 21:19:46 +02:00
										 |  |  | func (r *Repository) Index() restic.MasterIndex { | 
					
						
							| 
									
										
										
										
											2015-07-02 21:52:57 +02:00
										 |  |  | 	return r.idx | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-09 13:25:52 +02:00
										 |  |  | // SetIndex instructs the repository to use the given index. | 
					
						
							| 
									
										
										
										
											2020-07-25 21:19:46 +02:00
										 |  |  | func (r *Repository) SetIndex(i restic.MasterIndex) error { | 
					
						
							| 
									
										
										
										
											2016-08-31 22:39:36 +02:00
										 |  |  | 	r.idx = i.(*MasterIndex) | 
					
						
							| 
									
										
										
										
											2021-08-16 16:00:36 +02:00
										 |  |  | 	return r.PrepareCache() | 
					
						
							| 
									
										
										
										
											2015-02-08 22:54:45 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | // LoadIndex loads all index files from the backend in parallel and stores them | 
					
						
							|  |  |  | // in the master index. The first error that occurred is returned. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) LoadIndex(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 	debug.Log("Loading index") | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 18:50:19 +01:00
										 |  |  | 	err := ForAllIndexes(ctx, r, func(id restic.ID, idx *Index, oldFormat bool, err error) error { | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2015-07-04 18:38:32 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-07 18:50:19 +01:00
										 |  |  | 		r.idx.Insert(idx) | 
					
						
							| 
									
										
										
										
											2019-03-24 21:27:28 +01:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-06-30 21:34:53 +02:00
										 |  |  | 		return errors.Fatal(err.Error()) | 
					
						
							| 
									
										
										
										
											2015-07-04 18:38:32 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-30 16:35:05 +01:00
										 |  |  | 	err = r.idx.MergeFinalIndexes() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-07 18:50:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 	if r.cfg.Version < 2 { | 
					
						
							|  |  |  | 		// sanity check | 
					
						
							|  |  |  | 		ctx, cancel := context.WithCancel(ctx) | 
					
						
							|  |  |  | 		defer cancel() | 
					
						
							|  |  |  | 		for blob := range r.idx.Each(ctx) { | 
					
						
							|  |  |  | 			if blob.IsCompressed() { | 
					
						
							|  |  |  | 				return errors.Fatal("index uses feature not supported by repository version 1") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 21:27:28 +01:00
										 |  |  | 	// remove index files from the cache which have been removed in the repo | 
					
						
							| 
									
										
										
										
											2021-08-16 16:00:36 +02:00
										 |  |  | 	return r.PrepareCache() | 
					
						
							| 
									
										
										
										
											2018-03-31 09:50:45 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-07-18 23:16:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-10 21:31:40 +02:00
										 |  |  | const listPackParallelism = 10 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateIndexFromPacks creates a new index by reading all given pack files (with sizes). | 
					
						
							|  |  |  | // The index is added to the MasterIndex but not marked as finalized. | 
					
						
							|  |  |  | // Returned is the list of pack files which could not be read. | 
					
						
							|  |  |  | func (r *Repository) CreateIndexFromPacks(ctx context.Context, packsize map[restic.ID]int64, p *progress.Counter) (invalid restic.IDs, err error) { | 
					
						
							|  |  |  | 	var m sync.Mutex | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("Loading index from pack files") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// track spawned goroutines using wg, create a new context which is | 
					
						
							|  |  |  | 	// cancelled as soon as an error occurs. | 
					
						
							|  |  |  | 	wg, ctx := errgroup.WithContext(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	type FileInfo struct { | 
					
						
							|  |  |  | 		restic.ID | 
					
						
							|  |  |  | 		Size int64 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ch := make(chan FileInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// send list of pack files through ch, which is closed afterwards | 
					
						
							|  |  |  | 	wg.Go(func() error { | 
					
						
							|  |  |  | 		defer close(ch) | 
					
						
							|  |  |  | 		for id, size := range packsize { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2022-05-10 22:17:50 +02:00
										 |  |  | 				return ctx.Err() | 
					
						
							| 
									
										
										
										
											2020-10-10 21:31:40 +02:00
										 |  |  | 			case ch <- FileInfo{id, size}: | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// a worker receives an pack ID from ch, reads the pack contents, and adds them to idx | 
					
						
							|  |  |  | 	worker := func() error { | 
					
						
							|  |  |  | 		for fi := range ch { | 
					
						
							|  |  |  | 			entries, _, err := r.ListPack(ctx, fi.ID, fi.Size) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				debug.Log("unable to list pack file %v", fi.ID.Str()) | 
					
						
							|  |  |  | 				m.Lock() | 
					
						
							|  |  |  | 				invalid = append(invalid, fi.ID) | 
					
						
							|  |  |  | 				m.Unlock() | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-05-26 16:13:41 +02:00
										 |  |  | 			r.idx.StorePack(fi.ID, entries) | 
					
						
							| 
									
										
										
										
											2020-10-10 21:31:40 +02:00
										 |  |  | 			p.Add(1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// run workers on ch | 
					
						
							| 
									
										
										
										
											2022-05-10 22:17:50 +02:00
										 |  |  | 	for i := 0; i < listPackParallelism; i++ { | 
					
						
							|  |  |  | 		wg.Go(worker) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-10 21:31:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err = wg.Wait() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return invalid, errors.Fatal(err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return invalid, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 09:50:45 +02:00
										 |  |  | // PrepareCache initializes the local cache. indexIDs is the list of IDs of | 
					
						
							|  |  |  | // index files still present in the repo. | 
					
						
							| 
									
										
										
										
											2021-08-16 16:00:36 +02:00
										 |  |  | func (r *Repository) PrepareCache() error { | 
					
						
							| 
									
										
										
										
											2018-03-31 09:50:45 +02:00
										 |  |  | 	if r.Cache == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-16 16:00:36 +02:00
										 |  |  | 	indexIDs := r.idx.IDs() | 
					
						
							| 
									
										
										
										
											2018-03-31 10:02:09 +02:00
										 |  |  | 	debug.Log("prepare cache with %d index files", len(indexIDs)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 09:50:45 +02:00
										 |  |  | 	// clear old index files | 
					
						
							|  |  |  | 	err := r.Cache.Clear(restic.IndexFile, indexIDs) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "error clearing index files in cache: %v\n", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-16 16:01:11 +02:00
										 |  |  | 	packs := r.idx.Packs(restic.NewIDSet()) | 
					
						
							| 
									
										
										
										
											2017-09-24 22:54:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-16 11:16:38 +02:00
										 |  |  | 	// clear old packs | 
					
						
							|  |  |  | 	err = r.Cache.Clear(restic.PackFile, packs) | 
					
						
							| 
									
										
										
										
											2018-03-31 09:50:45 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-08-16 11:16:38 +02:00
										 |  |  | 		fmt.Fprintf(os.Stderr, "error clearing pack files in cache: %v\n", err) | 
					
						
							| 
									
										
										
										
											2018-03-31 09:50:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-04 20:39:45 +02:00
										 |  |  | // SearchKey finds a key with the supplied password, afterwards the config is | 
					
						
							| 
									
										
										
										
											2016-08-21 13:09:31 +02:00
										 |  |  | // read and parsed. It tries at most maxKeys key files in the repo. | 
					
						
							| 
									
										
										
										
											2018-11-25 09:10:45 -05:00
										 |  |  | func (r *Repository) SearchKey(ctx context.Context, password string, maxKeys int, keyHint string) error { | 
					
						
							|  |  |  | 	key, err := SearchKey(ctx, r, password, maxKeys, keyHint) | 
					
						
							| 
									
										
										
										
											2014-12-21 18:10:19 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 21:52:57 +02:00
										 |  |  | 	r.key = key.master | 
					
						
							|  |  |  | 	r.keyName = key.Name() | 
					
						
							| 
									
										
										
										
											2022-04-29 23:12:43 +02:00
										 |  |  | 	cfg, err := restic.LoadConfig(ctx, r) | 
					
						
							| 
									
										
										
										
											2022-04-09 22:22:40 +02:00
										 |  |  | 	if err == crypto.ErrUnauthenticated { | 
					
						
							|  |  |  | 		return errors.Fatalf("config or key %v is damaged: %v", key.Name(), err) | 
					
						
							|  |  |  | 	} else if err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-09 21:05:14 +01:00
										 |  |  | 		return errors.Fatalf("config cannot be loaded: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-04-29 23:12:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	r.setConfig(cfg) | 
					
						
							| 
									
										
										
										
											2018-03-09 21:05:14 +01:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-05-03 16:36:52 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-12-21 18:10:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 22:36:31 +02:00
										 |  |  | // Init creates a new master key with the supplied password, initializes and | 
					
						
							|  |  |  | // saves the repository config. | 
					
						
							| 
									
										
										
										
											2022-02-13 00:52:03 +01:00
										 |  |  | func (r *Repository) Init(ctx context.Context, version uint, password string, chunkerPolynomial *chunker.Pol) error { | 
					
						
							|  |  |  | 	if version > restic.MaxRepoVersion { | 
					
						
							|  |  |  | 		return fmt.Errorf("repo version %v too high", version) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if version < restic.MinRepoVersion { | 
					
						
							|  |  |  | 		return fmt.Errorf("repo version %v too low", version) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | 	has, err := r.be.Test(ctx, restic.Handle{Type: restic.ConfigFile}) | 
					
						
							| 
									
										
										
										
											2015-05-03 17:46:18 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if has { | 
					
						
							|  |  |  | 		return errors.New("repository master key and config already initialized") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 00:52:03 +01:00
										 |  |  | 	cfg, err := restic.CreateConfig(version) | 
					
						
							| 
									
										
										
										
											2016-07-31 16:27:36 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-19 12:41:52 +02:00
										 |  |  | 	if chunkerPolynomial != nil { | 
					
						
							|  |  |  | 		cfg.ChunkerPolynomial = *chunkerPolynomial | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-31 16:27:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | 	return r.init(ctx, password, cfg) | 
					
						
							| 
									
										
										
										
											2016-07-31 16:27:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // init creates a new master key with the supplied password and uses it to save | 
					
						
							|  |  |  | // the config into the repo. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) init(ctx context.Context, password string, cfg restic.Config) error { | 
					
						
							| 
									
										
										
										
											2020-04-10 11:37:39 +02:00
										 |  |  | 	key, err := createMasterKey(ctx, r, password) | 
					
						
							| 
									
										
										
										
											2015-05-03 16:36:52 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 21:52:57 +02:00
										 |  |  | 	r.key = key.master | 
					
						
							|  |  |  | 	r.keyName = key.Name() | 
					
						
							| 
									
										
										
										
											2022-04-29 23:12:43 +02:00
										 |  |  | 	r.setConfig(cfg) | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | 	_, err = r.SaveJSONUnpacked(ctx, restic.ConfigFile, cfg) | 
					
						
							| 
									
										
										
										
											2015-07-02 22:36:31 +02:00
										 |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2014-12-21 18:10:19 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 22:53:03 +02:00
										 |  |  | // Key returns the current master key. | 
					
						
							| 
									
										
										
										
											2015-07-02 21:52:57 +02:00
										 |  |  | func (r *Repository) Key() *crypto.Key { | 
					
						
							|  |  |  | 	return r.key | 
					
						
							| 
									
										
										
										
											2014-12-21 18:10:19 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 22:53:03 +02:00
										 |  |  | // KeyName returns the name of the current key in the backend. | 
					
						
							| 
									
										
										
										
											2015-07-02 21:52:57 +02:00
										 |  |  | func (r *Repository) KeyName() string { | 
					
						
							|  |  |  | 	return r.keyName | 
					
						
							| 
									
										
										
										
											2015-05-03 18:04:13 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-21 17:25:36 +01:00
										 |  |  | // List runs fn for all files of type t in the repo. | 
					
						
							|  |  |  | func (r *Repository) List(ctx context.Context, t restic.FileType, fn func(restic.ID, int64) error) error { | 
					
						
							|  |  |  | 	return r.be.List(ctx, t, func(fi restic.FileInfo) error { | 
					
						
							|  |  |  | 		id, err := restic.ParseID(fi.Name) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			debug.Log("unable to parse %v as an ID", fi.Name) | 
					
						
							| 
									
										
										
										
											2018-02-26 20:53:38 +01:00
										 |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2017-03-06 22:19:38 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-21 17:25:36 +01:00
										 |  |  | 		return fn(id, fi.Size) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-07 21:56:42 +02:00
										 |  |  | // ListPack returns the list of blobs saved in the pack id and the length of | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | // the the pack header. | 
					
						
							|  |  |  | func (r *Repository) ListPack(ctx context.Context, id restic.ID, size int64) ([]restic.Blob, uint32, error) { | 
					
						
							| 
									
										
										
										
											2020-08-16 11:16:38 +02:00
										 |  |  | 	h := restic.Handle{Type: restic.PackFile, Name: id.String()} | 
					
						
							| 
									
										
										
										
											2016-08-07 21:56:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | 	return pack.List(r.Key(), restic.ReaderAt(ctx, r.Backend(), h), size) | 
					
						
							| 
									
										
										
										
											2016-05-08 13:51:21 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 22:53:03 +02:00
										 |  |  | // Delete calls backend.Delete() if implemented, and returns an error | 
					
						
							|  |  |  | // otherwise. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) Delete(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2017-10-14 15:56:38 +02:00
										 |  |  | 	return r.be.Delete(ctx) | 
					
						
							| 
									
										
										
										
											2014-12-21 17:02:49 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-14 11:56:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-02 22:53:03 +02:00
										 |  |  | // Close closes the repository by closing the backend. | 
					
						
							| 
									
										
										
										
											2015-07-02 21:52:57 +02:00
										 |  |  | func (r *Repository) Close() error { | 
					
						
							|  |  |  | 	return r.be.Close() | 
					
						
							| 
									
										
										
										
											2015-03-28 11:50:23 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-09-03 11:22:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | // SaveBlob saves a blob of type t into the repository. | 
					
						
							|  |  |  | // It takes care that no duplicates are saved; this can be overwritten | 
					
						
							|  |  |  | // by setting storeDuplicate to true. | 
					
						
							|  |  |  | // If id is the null id, it will be computed and returned. | 
					
						
							| 
									
										
										
										
											2022-05-01 14:26:57 +02:00
										 |  |  | // Also returns if the blob was already known before. | 
					
						
							|  |  |  | // If the blob was not known before, it returns the number of bytes the blob | 
					
						
							|  |  |  | // occupies in the repo (compressed or not, including encryption overhead). | 
					
						
							|  |  |  | func (r *Repository) SaveBlob(ctx context.Context, t restic.BlobType, buf []byte, id restic.ID, storeDuplicate bool) (newID restic.ID, known bool, size int, err error) { | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// compute plaintext hash if not already set | 
					
						
							|  |  |  | 	if id.IsNull() { | 
					
						
							|  |  |  | 		newID = restic.Hash(buf) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		newID = id | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// first try to add to pending blobs; if not successful, this blob is already known | 
					
						
							| 
									
										
										
										
											2020-11-05 22:18:00 +01:00
										 |  |  | 	known = !r.idx.addPending(restic.BlobHandle{ID: newID, Type: t}) | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-05 23:13:38 +02:00
										 |  |  | 	// only save when needed or explicitly told | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 	if !known || storeDuplicate { | 
					
						
							| 
									
										
										
										
											2022-05-01 14:26:57 +02:00
										 |  |  | 		size, err = r.saveAndEncrypt(ctx, t, buf, newID) | 
					
						
							| 
									
										
										
										
											2016-09-03 20:55:22 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 14:26:57 +02:00
										 |  |  | 	return newID, known, size, err | 
					
						
							| 
									
										
										
										
											2016-09-03 20:55:22 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 11:22:01 +02:00
										 |  |  | // LoadTree loads a tree from the repository. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) LoadTree(ctx context.Context, id restic.ID) (*restic.Tree, error) { | 
					
						
							| 
									
										
										
										
											2018-01-25 20:49:41 +01:00
										 |  |  | 	debug.Log("load tree %v", id) | 
					
						
							| 
									
										
										
										
											2016-09-03 13:34:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 17:52:14 +01:00
										 |  |  | 	buf, err := r.LoadBlob(ctx, restic.TreeBlob, id, nil) | 
					
						
							| 
									
										
										
										
											2016-09-03 11:22:01 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t := &restic.Tree{} | 
					
						
							|  |  |  | 	err = json.Unmarshal(buf, t) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return t, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 20:55:22 +02:00
										 |  |  | // SaveTree stores a tree into the repository and returns the ID. The ID is | 
					
						
							|  |  |  | // checked against the index. The tree is only stored when the index does not | 
					
						
							|  |  |  | // contain the ID. | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | func (r *Repository) SaveTree(ctx context.Context, t *restic.Tree) (restic.ID, error) { | 
					
						
							| 
									
										
										
										
											2016-09-03 20:55:22 +02:00
										 |  |  | 	buf, err := json.Marshal(t) | 
					
						
							| 
									
										
										
										
											2016-09-03 11:22:01 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-09-03 20:55:22 +02:00
										 |  |  | 		return restic.ID{}, errors.Wrap(err, "MarshalJSON") | 
					
						
							| 
									
										
										
										
											2016-09-03 11:22:01 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 20:55:22 +02:00
										 |  |  | 	// append a newline so that the data is always consistent (json.Encoder | 
					
						
							|  |  |  | 	// adds a newline after each object) | 
					
						
							|  |  |  | 	buf = append(buf, '\n') | 
					
						
							| 
									
										
										
										
											2016-09-03 11:22:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 14:26:57 +02:00
										 |  |  | 	id, _, _, err := r.SaveBlob(ctx, restic.TreeBlob, buf, restic.ID{}, false) | 
					
						
							| 
									
										
										
										
											2016-09-03 20:55:22 +02:00
										 |  |  | 	return id, err | 
					
						
							| 
									
										
										
										
											2016-09-03 20:11:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-02-11 22:41:59 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | type BackendLoadFn func(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-05 12:20:07 +02:00
										 |  |  | // StreamPack loads the listed blobs from the specified pack file. The plaintext blob is passed to | 
					
						
							|  |  |  | // the handleBlobFn callback or an error if decryption failed or the blob hash does not match. In | 
					
						
							|  |  |  | // case of download errors handleBlobFn might be called multiple times for the same blob. If the | 
					
						
							|  |  |  | // callback returns an error, then StreamPack will abort and not retry it. | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | func StreamPack(ctx context.Context, beLoad BackendLoadFn, key *crypto.Key, packID restic.ID, blobs []restic.Blob, handleBlobFn func(blob restic.BlobHandle, buf []byte, err error) error) error { | 
					
						
							|  |  |  | 	if len(blobs) == 0 { | 
					
						
							|  |  |  | 		// nothing to do | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sort.Slice(blobs, func(i, j int) bool { | 
					
						
							|  |  |  | 		return blobs[i].Offset < blobs[j].Offset | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:12:38 +02:00
										 |  |  | 	h := restic.Handle{Type: restic.PackFile, Name: packID.String(), ContainedBlobType: restic.DataBlob} | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	dataStart := blobs[0].Offset | 
					
						
							|  |  |  | 	dataEnd := blobs[len(blobs)-1].Offset + blobs[len(blobs)-1].Length | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("streaming pack %v (%d to %d bytes), blobs: %v", packID, dataStart, dataEnd, len(blobs)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 	dec, err := zstd.NewReader(nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(dec) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer dec.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-05 12:20:07 +02:00
										 |  |  | 	ctx, cancel := context.WithCancel(ctx) | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 	// stream blobs in pack | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 	err = beLoad(ctx, h, int(dataEnd-dataStart), int64(dataStart), func(rd io.Reader) error { | 
					
						
							| 
									
										
										
										
											2021-09-05 12:20:07 +02:00
										 |  |  | 		// prevent callbacks after cancelation | 
					
						
							|  |  |  | 		if ctx.Err() != nil { | 
					
						
							|  |  |  | 			return ctx.Err() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 		bufferSize := int(dataEnd - dataStart) | 
					
						
							| 
									
										
										
										
											2021-08-20 16:15:40 +02:00
										 |  |  | 		if bufferSize > MaxStreamBufferSize { | 
					
						
							|  |  |  | 			bufferSize = MaxStreamBufferSize | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-22 14:38:27 +02:00
										 |  |  | 		// create reader here to allow reusing the buffered reader from checker.checkData | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 		bufRd := bufio.NewReaderSize(rd, bufferSize) | 
					
						
							|  |  |  | 		currentBlobEnd := dataStart | 
					
						
							|  |  |  | 		var buf []byte | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 		var decode []byte | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 		for _, entry := range blobs { | 
					
						
							|  |  |  | 			skipBytes := int(entry.Offset - currentBlobEnd) | 
					
						
							|  |  |  | 			if skipBytes < 0 { | 
					
						
							|  |  |  | 				return errors.Errorf("overlapping blobs in pack %v", packID) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			_, err := bufRd.Discard(skipBytes) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			h := restic.BlobHandle{ID: entry.ID, Type: entry.Type} | 
					
						
							|  |  |  | 			debug.Log("  process blob %v, skipped %d, %v", h, skipBytes, entry) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if uint(cap(buf)) < entry.Length { | 
					
						
							|  |  |  | 				buf = make([]byte, entry.Length) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			buf = buf[:entry.Length] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			n, err := io.ReadFull(bufRd, buf) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				debug.Log("    read error %v", err) | 
					
						
							|  |  |  | 				return errors.Wrap(err, "ReadFull") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if n != len(buf) { | 
					
						
							|  |  |  | 				return errors.Errorf("read blob %v from %v: not enough bytes read, want %v, got %v", | 
					
						
							|  |  |  | 					h, packID.Str(), len(buf), n) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			currentBlobEnd = entry.Offset + entry.Length | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 16:16:45 +02:00
										 |  |  | 			if int(entry.Length) <= key.NonceSize() { | 
					
						
							|  |  |  | 				debug.Log("%v", blobs) | 
					
						
							|  |  |  | 				return errors.Errorf("invalid blob length %v", entry) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 			// decryption errors are likely permanent, give the caller a chance to skip them | 
					
						
							|  |  |  | 			nonce, ciphertext := buf[:key.NonceSize()], buf[key.NonceSize():] | 
					
						
							|  |  |  | 			plaintext, err := key.Open(ciphertext[:0], nonce, ciphertext, nil) | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 			if err == nil && entry.IsCompressed() { | 
					
						
							| 
									
										
										
										
											2022-04-20 20:55:43 +02:00
										 |  |  | 				// DecodeAll will allocate a slice if it is not large enough since it | 
					
						
							|  |  |  | 				// knows the decompressed size (because we're using EncodeAll) | 
					
						
							| 
									
										
										
										
											2022-02-13 17:24:09 +01:00
										 |  |  | 				decode, err = dec.DecodeAll(plaintext, decode[:0]) | 
					
						
							|  |  |  | 				plaintext = decode | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					err = errors.Errorf("decompressing blob %v failed: %v", h, err) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				id := restic.Hash(plaintext) | 
					
						
							|  |  |  | 				if !id.Equal(entry.ID) { | 
					
						
							|  |  |  | 					debug.Log("read blob %v/%v from %v: wrong data returned, hash is %v", | 
					
						
							|  |  |  | 						h.Type, h.ID, packID.Str(), id) | 
					
						
							|  |  |  | 					err = errors.Errorf("read blob %v from %v: wrong data returned, hash is %v", | 
					
						
							|  |  |  | 						h, packID.Str(), id) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			err = handleBlobFn(entry.BlobHandle, plaintext, err) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-05 12:20:07 +02:00
										 |  |  | 				cancel() | 
					
						
							| 
									
										
										
										
											2021-09-04 16:09:34 +02:00
										 |  |  | 				return backoff.Permanent(err) | 
					
						
							| 
									
										
										
										
											2021-08-20 23:21:05 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return errors.Wrap(err, "StreamPack") | 
					
						
							|  |  |  | } |