| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | package repository | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2017-06-04 11:16:55 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-12-19 12:39:48 +01:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2022-05-09 22:43:26 +02:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 	"github.com/restic/restic/internal/errors" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/hashing" | 
					
						
							| 
									
										
										
										
											2017-07-24 17:42:25 +02:00
										 |  |  | 	"github.com/restic/restic/internal/restic" | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/crypto" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/debug" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/fs" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/pack" | 
					
						
							| 
									
										
										
										
											2020-03-19 11:27:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/sha256-simd" | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-22 17:53:00 +01:00
										 |  |  | // Packer holds a pack.Packer together with a hash writer. | 
					
						
							|  |  |  | type Packer struct { | 
					
						
							|  |  |  | 	*pack.Packer | 
					
						
							|  |  |  | 	tmpfile *os.File | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	bufWr   *bufio.Writer | 
					
						
							| 
									
										
										
										
											2017-01-22 17:53:00 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | // packerManager keeps a list of open packs and creates new on demand. | 
					
						
							|  |  |  | type packerManager struct { | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	tpe     restic.BlobType | 
					
						
							|  |  |  | 	key     *crypto.Key | 
					
						
							|  |  |  | 	queueFn func(ctx context.Context, t restic.BlobType, p *Packer) error | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-02 23:30:26 +02:00
										 |  |  | 	pm       sync.Mutex | 
					
						
							|  |  |  | 	packer   *Packer | 
					
						
							|  |  |  | 	packSize uint | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 14:20:48 +01:00
										 |  |  | // newPackerManager returns an new packer manager which writes temporary files | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | // to a temporary directory | 
					
						
							| 
									
										
										
										
											2022-07-02 23:30:26 +02:00
										 |  |  | func newPackerManager(key *crypto.Key, tpe restic.BlobType, packSize uint, queueFn func(ctx context.Context, t restic.BlobType, p *Packer) error) *packerManager { | 
					
						
							| 
									
										
										
										
											2016-03-06 13:14:06 +01:00
										 |  |  | 	return &packerManager{ | 
					
						
							| 
									
										
										
										
											2022-07-02 23:30:26 +02:00
										 |  |  | 		tpe:      tpe, | 
					
						
							|  |  |  | 		key:      key, | 
					
						
							|  |  |  | 		queueFn:  queueFn, | 
					
						
							|  |  |  | 		packSize: packSize, | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *packerManager) Flush(ctx context.Context) error { | 
					
						
							|  |  |  | 	r.pm.Lock() | 
					
						
							|  |  |  | 	defer r.pm.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	if r.packer != nil { | 
					
						
							|  |  |  | 		debug.Log("manually flushing pending pack") | 
					
						
							|  |  |  | 		err := r.queueFn(ctx, r.tpe, r.packer) | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 		r.packer = nil | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *packerManager) SaveBlob(ctx context.Context, t restic.BlobType, id restic.ID, ciphertext []byte, uncompressedLength int) (int, error) { | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	r.pm.Lock() | 
					
						
							|  |  |  | 	defer r.pm.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	packer := r.packer | 
					
						
							| 
									
										
										
										
											2023-09-28 20:58:45 +02:00
										 |  |  | 	// use separate packer if compressed length is larger than the packsize | 
					
						
							|  |  |  | 	// this speeds up the garbage collection of oversized blobs and reduces the cache size | 
					
						
							|  |  |  | 	// as the oversize blobs are only downloaded if necessary | 
					
						
							|  |  |  | 	if len(ciphertext) >= int(r.packSize) || r.packer == nil { | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 		packer, err = r.newPacker() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-09-28 20:58:45 +02:00
										 |  |  | 		// don't store packer for oversized blob | 
					
						
							|  |  |  | 		if r.packer == nil { | 
					
						
							|  |  |  | 			r.packer = packer | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// save ciphertext | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	// Add only appends bytes in memory to avoid being a scaling bottleneck | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	size, err := packer.Add(t, id, ciphertext, uncompressedLength) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-30 15:05:20 -07:00
										 |  |  | 	// if the pack and header is not full enough, put back to the list | 
					
						
							|  |  |  | 	if packer.Size() < r.packSize && !packer.HeaderFull() { | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		debug.Log("pack is not full enough (%d bytes)", packer.Size()) | 
					
						
							|  |  |  | 		return size, nil | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-09-28 20:58:45 +02:00
										 |  |  | 	if packer == r.packer { | 
					
						
							|  |  |  | 		// forget full packer | 
					
						
							|  |  |  | 		r.packer = nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	// call while holding lock to prevent findPacker from creating new packers if the uploaders are busy | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	// else write the pack to the backend | 
					
						
							|  |  |  | 	err = r.queueFn(ctx, t, packer) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return size + packer.HeaderOverhead(), nil | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | // findPacker returns a packer for a new blob of size bytes. Either a new one is | 
					
						
							|  |  |  | // created or one is returned that already has some blobs. | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | func (r *packerManager) newPacker() (packer *Packer, err error) { | 
					
						
							| 
									
										
										
										
											2017-07-16 20:16:02 +02:00
										 |  |  | 	debug.Log("create new pack") | 
					
						
							| 
									
										
										
										
											2017-05-10 19:48:22 +02:00
										 |  |  | 	tmpfile, err := fs.TempFile("", "restic-temp-pack-") | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-10-16 11:32:38 +02:00
										 |  |  | 		return nil, errors.WithStack(err) | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	bufWr := bufio.NewWriter(tmpfile) | 
					
						
							|  |  |  | 	p := pack.NewPacker(r.key, bufWr) | 
					
						
							| 
									
										
										
										
											2017-01-22 17:53:00 +01:00
										 |  |  | 	packer = &Packer{ | 
					
						
							|  |  |  | 		Packer:  p, | 
					
						
							|  |  |  | 		tmpfile: tmpfile, | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 		bufWr:   bufWr, | 
					
						
							| 
									
										
										
										
											2017-01-22 17:53:00 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return packer, nil | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // savePacker stores p in the backend. | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | func (r *Repository) savePacker(ctx context.Context, t restic.BlobType, p *Packer) error { | 
					
						
							| 
									
										
										
										
											2017-07-16 20:24:37 +02:00
										 |  |  | 	debug.Log("save packer for %v with %d blobs (%d bytes)\n", t, p.Packer.Count(), p.Packer.Size()) | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	err := p.Packer.Finalize() | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	err = p.bufWr.Flush() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// calculate sha256 hash in a second pass | 
					
						
							|  |  |  | 	var rd io.Reader | 
					
						
							|  |  |  | 	rd, err = restic.NewFileReader(p.tmpfile, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	beHasher := r.be.Hasher() | 
					
						
							|  |  |  | 	var beHr *hashing.Reader | 
					
						
							|  |  |  | 	if beHasher != nil { | 
					
						
							|  |  |  | 		beHr = hashing.NewReader(rd, beHasher) | 
					
						
							|  |  |  | 		rd = beHr | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	hr := hashing.NewReader(rd, sha256.New()) | 
					
						
							| 
									
										
										
										
											2022-12-02 19:36:43 +01:00
										 |  |  | 	_, err = io.Copy(io.Discard, hr) | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	id := restic.IDFromHash(hr.Sum(nil)) | 
					
						
							| 
									
										
										
										
											2022-06-12 14:45:42 +02:00
										 |  |  | 	h := restic.Handle{Type: restic.PackFile, Name: id.String(), ContainedBlobType: t} | 
					
						
							| 
									
										
										
										
											2020-12-19 12:39:48 +01:00
										 |  |  | 	var beHash []byte | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	if beHr != nil { | 
					
						
							|  |  |  | 		beHash = beHr.Sum(nil) | 
					
						
							| 
									
										
										
										
											2020-12-19 12:39:48 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	rrd, err := restic.NewFileReader(p.tmpfile, beHash) | 
					
						
							| 
									
										
										
										
											2018-03-03 14:20:54 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2018-03-03 14:20:54 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-22 15:10:00 +02:00
										 |  |  | 	err = r.be.Save(ctx, h, rrd) | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 		debug.Log("Save(%v) error: %v", h, err) | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-27 22:35:08 +02:00
										 |  |  | 	debug.Log("saved as %v", h) | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-23 18:45:15 +01:00
										 |  |  | 	err = p.tmpfile.Close() | 
					
						
							| 
									
										
										
										
											2017-01-22 17:53:00 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		return errors.Wrap(err, "close tempfile") | 
					
						
							| 
									
										
										
										
											2017-01-22 17:53:00 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-09 22:43:26 +02:00
										 |  |  | 	// on windows the tempfile is automatically deleted on close | 
					
						
							|  |  |  | 	if runtime.GOOS != "windows" { | 
					
						
							|  |  |  | 		err = fs.RemoveIfExists(p.tmpfile.Name()) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-10-16 11:32:38 +02:00
										 |  |  | 			return errors.WithStack(err) | 
					
						
							| 
									
										
										
										
											2022-05-09 22:43:26 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-03-06 13:14:06 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 	// update blobs in the index | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 	debug.Log("  updating blobs %v to pack %v", p.Packer.Blobs(), id) | 
					
						
							|  |  |  | 	r.idx.StorePack(id, p.Packer.Blobs()) | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 	// Save index if full | 
					
						
							| 
									
										
										
										
											2020-06-12 09:24:38 +02:00
										 |  |  | 	if r.noAutoIndexUpdate { | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2020-06-12 09:24:38 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	return r.idx.SaveFullIndex(ctx, r) | 
					
						
							| 
									
										
										
										
											2015-11-18 20:20:25 +01:00
										 |  |  | } |