2015-11-18 19:20:25 +00:00
|
|
|
package repository
|
|
|
|
|
|
|
|
import (
|
2017-06-04 09:16:55 +00:00
|
|
|
"context"
|
2020-12-19 11:39:48 +00:00
|
|
|
"hash"
|
|
|
|
"io"
|
2016-03-06 11:26:25 +00:00
|
|
|
"os"
|
2022-05-09 20:43:26 +00:00
|
|
|
"runtime"
|
2015-11-18 19:20:25 +00:00
|
|
|
"sync"
|
|
|
|
|
2017-07-23 12:21:03 +00:00
|
|
|
"github.com/restic/restic/internal/errors"
|
|
|
|
"github.com/restic/restic/internal/hashing"
|
2017-07-24 15:42:25 +00:00
|
|
|
"github.com/restic/restic/internal/restic"
|
2017-07-23 12:21:03 +00: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 10:27:19 +00:00
|
|
|
|
|
|
|
"github.com/minio/sha256-simd"
|
2015-11-18 19:20:25 +00:00
|
|
|
)
|
|
|
|
|
2017-01-22 16:53:00 +00:00
|
|
|
// Packer holds a pack.Packer together with a hash writer.
|
|
|
|
type Packer struct {
|
|
|
|
*pack.Packer
|
|
|
|
hw *hashing.Writer
|
2020-12-19 11:39:48 +00:00
|
|
|
beHw *hashing.Writer
|
2017-01-22 16:53:00 +00:00
|
|
|
tmpfile *os.File
|
|
|
|
}
|
|
|
|
|
2015-11-18 19:20:25 +00:00
|
|
|
// packerManager keeps a list of open packs and creates new on demand.
|
|
|
|
type packerManager struct {
|
2021-08-07 20:52:05 +00:00
|
|
|
tpe restic.BlobType
|
|
|
|
key *crypto.Key
|
|
|
|
hasherFn func() hash.Hash
|
|
|
|
queueFn func(ctx context.Context, t restic.BlobType, p *Packer) error
|
|
|
|
|
2017-01-22 16:53:00 +00:00
|
|
|
pm sync.Mutex
|
|
|
|
packers []*Packer
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|
|
|
|
|
2016-02-22 20:09:21 +00:00
|
|
|
const minPackSize = 4 * 1024 * 1024
|
2015-11-18 19:20:25 +00:00
|
|
|
|
2016-03-06 13:20:48 +00:00
|
|
|
// newPackerManager returns an new packer manager which writes temporary files
|
2016-03-06 11:26:25 +00:00
|
|
|
// to a temporary directory
|
2021-08-07 20:52:05 +00:00
|
|
|
func newPackerManager(key *crypto.Key, hasherFn func() hash.Hash, tpe restic.BlobType, queueFn func(ctx context.Context, t restic.BlobType, p *Packer) error) *packerManager {
|
2016-03-06 12:14:06 +00:00
|
|
|
return &packerManager{
|
2021-08-07 20:52:05 +00:00
|
|
|
tpe: tpe,
|
|
|
|
key: key,
|
|
|
|
hasherFn: hasherFn,
|
|
|
|
queueFn: queueFn,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *packerManager) Flush(ctx context.Context) error {
|
|
|
|
r.pm.Lock()
|
|
|
|
defer r.pm.Unlock()
|
|
|
|
|
|
|
|
debug.Log("manually flushing %d packs", len(r.packers))
|
|
|
|
for _, packer := range r.packers {
|
|
|
|
err := r.queueFn(ctx, r.tpe, packer)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.packers = r.packers[:0]
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *packerManager) SaveBlob(ctx context.Context, t restic.BlobType, id restic.ID, ciphertext []byte, uncompressedLength int) (int, error) {
|
|
|
|
packer, err := r.findPacker()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// save ciphertext
|
|
|
|
size, err := packer.Add(t, id, ciphertext, uncompressedLength)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the pack is not full enough, put back to the list
|
|
|
|
if packer.Size() < minPackSize {
|
|
|
|
debug.Log("pack is not full enough (%d bytes)", packer.Size())
|
|
|
|
r.insertPacker(packer)
|
|
|
|
return size, nil
|
2016-03-06 11:26:25 +00:00
|
|
|
}
|
2021-08-07 20:52:05 +00: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 11:26:25 +00:00
|
|
|
}
|
|
|
|
|
2015-11-18 19:20:25 +00: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.
|
2017-07-16 18:16:02 +00:00
|
|
|
func (r *packerManager) findPacker() (packer *Packer, err error) {
|
2015-11-18 19:20:25 +00:00
|
|
|
r.pm.Lock()
|
|
|
|
defer r.pm.Unlock()
|
|
|
|
|
|
|
|
// search for a suitable packer
|
2017-01-22 16:53:00 +00:00
|
|
|
if len(r.packers) > 0 {
|
2017-07-16 18:02:59 +00:00
|
|
|
p := r.packers[0]
|
2020-07-22 16:46:55 +00:00
|
|
|
last := len(r.packers) - 1
|
|
|
|
r.packers[0] = r.packers[last]
|
|
|
|
r.packers[last] = nil // Allow GC of stale reference.
|
|
|
|
r.packers = r.packers[:last]
|
2017-07-16 18:02:59 +00:00
|
|
|
return p, nil
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// no suitable packer found, return new
|
2017-07-16 18:16:02 +00:00
|
|
|
debug.Log("create new pack")
|
2017-05-10 17:48:22 +00:00
|
|
|
tmpfile, err := fs.TempFile("", "restic-temp-pack-")
|
2016-03-06 11:26:25 +00:00
|
|
|
if err != nil {
|
2017-05-10 17:48:22 +00:00
|
|
|
return nil, errors.Wrap(err, "fs.TempFile")
|
2016-03-06 11:26:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-19 11:39:48 +00:00
|
|
|
w := io.Writer(tmpfile)
|
2021-08-07 20:52:05 +00:00
|
|
|
beHasher := r.hasherFn()
|
2020-12-19 11:39:48 +00:00
|
|
|
var beHw *hashing.Writer
|
|
|
|
if beHasher != nil {
|
|
|
|
beHw = hashing.NewWriter(w, beHasher)
|
|
|
|
w = beHw
|
|
|
|
}
|
|
|
|
|
|
|
|
hw := hashing.NewWriter(w, sha256.New())
|
2017-01-22 16:53:00 +00:00
|
|
|
p := pack.NewPacker(r.key, hw)
|
|
|
|
packer = &Packer{
|
|
|
|
Packer: p,
|
2020-12-19 11:39:48 +00:00
|
|
|
beHw: beHw,
|
2017-01-22 16:53:00 +00:00
|
|
|
hw: hw,
|
|
|
|
tmpfile: tmpfile,
|
|
|
|
}
|
|
|
|
|
|
|
|
return packer, nil
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// insertPacker appends p to s.packs.
|
2017-01-22 16:53:00 +00:00
|
|
|
func (r *packerManager) insertPacker(p *Packer) {
|
2015-11-18 19:20:25 +00:00
|
|
|
r.pm.Lock()
|
|
|
|
defer r.pm.Unlock()
|
|
|
|
|
2017-01-22 16:53:00 +00:00
|
|
|
r.packers = append(r.packers, p)
|
|
|
|
debug.Log("%d packers\n", len(r.packers))
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// savePacker stores p in the backend.
|
2021-08-07 20:52:05 +00:00
|
|
|
func (r *Repository) savePacker(ctx context.Context, t restic.BlobType, p *Packer) error {
|
2017-07-16 18:24:37 +00:00
|
|
|
debug.Log("save packer for %v with %d blobs (%d bytes)\n", t, p.Packer.Count(), p.Packer.Size())
|
2021-08-07 20:52:05 +00:00
|
|
|
err := p.Packer.Finalize()
|
2015-11-18 19:20:25 +00:00
|
|
|
if err != nil {
|
2021-08-07 20:52:05 +00:00
|
|
|
return err
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 16:53:00 +00:00
|
|
|
id := restic.IDFromHash(p.hw.Sum(nil))
|
2020-07-28 08:13:11 +00:00
|
|
|
h := restic.Handle{Type: restic.PackFile, Name: id.String(),
|
|
|
|
ContainedBlobType: t}
|
2020-12-19 11:39:48 +00:00
|
|
|
var beHash []byte
|
|
|
|
if p.beHw != nil {
|
|
|
|
beHash = p.beHw.Sum(nil)
|
|
|
|
}
|
|
|
|
rd, err := restic.NewFileReader(p.tmpfile, beHash)
|
2018-03-03 13:20:54 +00:00
|
|
|
if err != nil {
|
2021-08-07 20:52:05 +00:00
|
|
|
return err
|
2018-03-03 13:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = r.be.Save(ctx, h, rd)
|
2015-11-18 19:20:25 +00:00
|
|
|
if err != nil {
|
2016-09-27 20:35:08 +00:00
|
|
|
debug.Log("Save(%v) error: %v", h, err)
|
2021-08-07 20:52:05 +00:00
|
|
|
return err
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 20:35:08 +00:00
|
|
|
debug.Log("saved as %v", h)
|
2015-11-18 19:20:25 +00:00
|
|
|
|
2017-01-23 17:45:15 +00:00
|
|
|
err = p.tmpfile.Close()
|
2017-01-22 16:53:00 +00:00
|
|
|
if err != nil {
|
2021-08-07 20:52:05 +00:00
|
|
|
return errors.Wrap(err, "close tempfile")
|
2017-01-22 16:53:00 +00:00
|
|
|
}
|
|
|
|
|
2022-05-09 20:43:26 +00:00
|
|
|
// on windows the tempfile is automatically deleted on close
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
err = fs.RemoveIfExists(p.tmpfile.Name())
|
|
|
|
if err != nil {
|
2021-08-07 20:52:05 +00:00
|
|
|
return errors.Wrap(err, "Remove")
|
2022-05-09 20:43:26 +00:00
|
|
|
}
|
2016-03-06 12:14:06 +00:00
|
|
|
}
|
|
|
|
|
2015-11-18 19:20:25 +00:00
|
|
|
// update blobs in the index
|
2020-06-06 20:20:44 +00:00
|
|
|
debug.Log(" updating blobs %v to pack %v", p.Packer.Blobs(), id)
|
|
|
|
r.idx.StorePack(id, p.Packer.Blobs())
|
2015-11-18 19:20:25 +00:00
|
|
|
|
2020-06-06 20:20:44 +00:00
|
|
|
// Save index if full
|
2020-06-12 07:24:38 +00:00
|
|
|
if r.noAutoIndexUpdate {
|
2021-08-07 20:52:05 +00:00
|
|
|
return nil
|
2020-06-12 07:24:38 +00:00
|
|
|
}
|
2021-08-07 20:52:05 +00:00
|
|
|
return r.idx.SaveFullIndex(ctx, r)
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// countPacker returns the number of open (unfinished) packers.
|
|
|
|
func (r *packerManager) countPacker() int {
|
|
|
|
r.pm.Lock()
|
|
|
|
defer r.pm.Unlock()
|
|
|
|
|
2017-01-22 16:53:00 +00:00
|
|
|
return len(r.packers)
|
2015-11-18 19:20:25 +00:00
|
|
|
}
|