2
2
mirror of https://github.com/octoleo/restic.git synced 2024-06-21 02:02:21 +00:00
restic/src/restic/pack/pack.go

329 lines
6.3 KiB
Go
Raw Normal View History

2015-04-26 13:36:49 +00:00
package pack
import (
2016-01-24 18:30:14 +00:00
"bytes"
2015-04-26 13:36:49 +00:00
"encoding/binary"
"errors"
"fmt"
"io"
"sync"
"restic/backend"
"restic/crypto"
2015-04-26 13:36:49 +00:00
)
2016-01-24 18:30:14 +00:00
// BlobType specifies what a blob stored in a pack is.
2015-04-26 13:36:49 +00:00
type BlobType uint8
2016-01-24 18:30:14 +00:00
// These are the blob types that can be stored in a pack.
2015-04-26 13:36:49 +00:00
const (
Invalid BlobType = iota
Data
Tree
2015-04-26 13:36:49 +00:00
)
func (t BlobType) String() string {
switch t {
case Data:
return "data"
case Tree:
return "tree"
}
return fmt.Sprintf("<BlobType %d>", t)
}
2016-01-24 18:30:14 +00:00
// MarshalJSON encodes the BlobType into JSON.
2015-04-26 13:36:49 +00:00
func (t BlobType) MarshalJSON() ([]byte, error) {
switch t {
case Data:
return []byte(`"data"`), nil
case Tree:
return []byte(`"tree"`), nil
}
return nil, errors.New("unknown blob type")
}
2016-01-24 18:30:14 +00:00
// UnmarshalJSON decodes the BlobType from JSON.
2015-04-26 13:36:49 +00:00
func (t *BlobType) UnmarshalJSON(buf []byte) error {
switch string(buf) {
case `"data"`:
*t = Data
case `"tree"`:
*t = Tree
default:
return errors.New("unknown blob type")
}
return nil
}
// Blob is a blob within a pack.
type Blob struct {
Type BlobType
Length uint
2015-04-26 13:36:49 +00:00
ID backend.ID
Offset uint
}
2016-08-07 16:42:58 +00:00
func (b Blob) String() string {
return fmt.Sprintf("<Blob %v/%v len %v, off %v>",
b.ID.Str(), b.Type, b.Length, b.Offset)
}
2015-04-26 13:36:49 +00:00
// Packer is used to create a new Pack.
type Packer struct {
blobs []Blob
bytes uint
k *crypto.Key
wr io.Writer
2015-04-26 13:36:49 +00:00
m sync.Mutex
}
// NewPacker returns a new Packer that can be used to pack blobs
// together. If wr is nil, a bytes.Buffer is used.
func NewPacker(k *crypto.Key, wr io.Writer) *Packer {
if wr == nil {
wr = bytes.NewBuffer(nil)
}
return &Packer{k: k, wr: wr}
2015-04-26 13:36:49 +00:00
}
// Add saves the data read from rd as a new blob to the packer. Returned is the
// number of bytes written to the pack.
func (p *Packer) Add(t BlobType, id backend.ID, data []byte) (int, error) {
2015-04-26 13:36:49 +00:00
p.m.Lock()
defer p.m.Unlock()
c := Blob{Type: t, ID: id}
n, err := p.wr.Write(data)
c.Length = uint(n)
2015-04-26 13:36:49 +00:00
c.Offset = p.bytes
p.bytes += uint(n)
p.blobs = append(p.blobs, c)
return n, err
}
2015-04-29 22:19:56 +00:00
var entrySize = uint(binary.Size(BlobType(0)) + binary.Size(uint32(0)) + backend.IDSize)
2015-04-26 13:36:49 +00:00
// headerEntry is used with encoding/binary to read and write header entries
type headerEntry struct {
Type uint8
2015-04-26 13:36:49 +00:00
Length uint32
ID [backend.IDSize]byte
}
// Finalize writes the header for all added blobs and finalizes the pack.
// Returned are the number of bytes written, including the header. If the
// underlying writer implements io.Closer, it is closed.
func (p *Packer) Finalize() (uint, error) {
2015-04-26 13:36:49 +00:00
p.m.Lock()
defer p.m.Unlock()
2016-01-24 18:30:14 +00:00
bytesWritten := p.bytes
2015-04-26 13:36:49 +00:00
2016-01-24 18:30:14 +00:00
hdrBuf := bytes.NewBuffer(nil)
bytesHeader, err := p.writeHeader(hdrBuf)
2015-04-29 22:36:36 +00:00
if err != nil {
return 0, err
2015-04-26 13:36:49 +00:00
}
2016-01-24 18:30:14 +00:00
encryptedHeader, err := crypto.Encrypt(p.k, nil, hdrBuf.Bytes())
if err != nil {
return 0, err
2016-01-24 18:30:14 +00:00
}
2015-04-29 22:41:11 +00:00
2016-01-24 18:30:14 +00:00
// append the header
n, err := p.wr.Write(encryptedHeader)
2015-04-26 13:36:49 +00:00
if err != nil {
return 0, err
2016-01-24 18:30:14 +00:00
}
hdrBytes := bytesHeader + crypto.Extension
if uint(n) != hdrBytes {
return 0, errors.New("wrong number of bytes written")
2015-04-26 13:36:49 +00:00
}
2016-01-24 18:30:14 +00:00
bytesWritten += hdrBytes
2015-04-26 13:36:49 +00:00
// write length
err = binary.Write(p.wr, binary.LittleEndian, uint32(uint(len(p.blobs))*entrySize+crypto.Extension))
2015-04-26 13:36:49 +00:00
if err != nil {
return 0, err
2015-04-26 13:36:49 +00:00
}
2015-04-29 22:19:56 +00:00
bytesWritten += uint(binary.Size(uint32(0)))
2015-04-26 13:36:49 +00:00
p.bytes = uint(bytesWritten)
2015-04-26 13:36:49 +00:00
if w, ok := p.wr.(io.Closer); ok {
return bytesWritten, w.Close()
}
return bytesWritten, nil
2015-04-26 13:36:49 +00:00
}
2015-04-29 22:36:36 +00:00
// writeHeader constructs and writes the header to wr.
func (p *Packer) writeHeader(wr io.Writer) (bytesWritten uint, err error) {
for _, b := range p.blobs {
entry := headerEntry{
Length: uint32(b.Length),
ID: b.ID,
2015-04-29 22:36:36 +00:00
}
switch b.Type {
case Data:
entry.Type = 0
case Tree:
entry.Type = 1
default:
return 0, fmt.Errorf("invalid blob type %v", b.Type)
}
2015-04-29 22:36:36 +00:00
err := binary.Write(wr, binary.LittleEndian, entry)
if err != nil {
return bytesWritten, err
}
bytesWritten += entrySize
}
return
}
2015-04-26 13:36:49 +00:00
// Size returns the number of bytes written so far.
func (p *Packer) Size() uint {
p.m.Lock()
defer p.m.Unlock()
return p.bytes
}
// Count returns the number of blobs in this packer.
func (p *Packer) Count() int {
p.m.Lock()
defer p.m.Unlock()
return len(p.blobs)
}
// Blobs returns the slice of blobs that have been written.
func (p *Packer) Blobs() []Blob {
p.m.Lock()
defer p.m.Unlock()
return p.blobs
}
// Writer return the underlying writer.
func (p *Packer) Writer() io.Writer {
return p.wr
}
2015-04-26 13:36:49 +00:00
func (p *Packer) String() string {
return fmt.Sprintf("<Packer %d blobs, %d bytes>", len(p.blobs), p.bytes)
}
// Unpacker is used to read individual blobs from a pack.
type Unpacker struct {
rd io.ReadSeeker
Entries []Blob
k *crypto.Key
}
const preloadHeaderSize = 2048
2015-04-26 13:36:49 +00:00
// NewUnpacker returns a pointer to Unpacker which can be used to read
// individual Blobs from a pack.
2016-08-07 12:50:24 +00:00
func NewUnpacker(k *crypto.Key, ldr Loader) (*Unpacker, error) {
2015-04-26 13:36:49 +00:00
var err error
// read the last 2048 byte, this will mostly be enough for the header, so
// we do not need another round trip.
buf := make([]byte, preloadHeaderSize)
2016-08-07 12:50:24 +00:00
n, err := ldr.Load(buf, -int64(len(buf)))
2016-08-07 14:24:02 +00:00
if err == io.ErrUnexpectedEOF {
err = nil
buf = buf[:n]
}
if err != nil {
2016-08-07 12:50:24 +00:00
return nil, fmt.Errorf("Load at -%d failed: %v", len(buf), err)
}
2016-08-07 12:50:24 +00:00
buf = buf[:n]
2016-08-07 12:50:24 +00:00
bs := binary.Size(uint32(0))
p := len(buf) - bs
2016-08-07 12:50:24 +00:00
// read the length from the end of the buffer
length := int(binary.LittleEndian.Uint32(buf[p : p+bs]))
buf = buf[:p]
2015-04-26 13:36:49 +00:00
2016-08-07 12:50:24 +00:00
// if the header is longer than the preloaded buffer, call the loader again.
if length > len(buf) {
buf = make([]byte, length)
n, err := ldr.Load(buf, -int64(len(buf)+bs))
if err != nil {
return nil, fmt.Errorf("Load at -%d failed: %v", len(buf), err)
}
buf = buf[:n]
}
2016-08-07 12:50:24 +00:00
buf = buf[len(buf)-length:]
2015-04-26 13:36:49 +00:00
// read header
2016-08-07 12:50:24 +00:00
hdr, err := crypto.Decrypt(k, buf, buf)
2015-04-26 13:36:49 +00:00
if err != nil {
return nil, err
}
2016-08-07 12:50:24 +00:00
rd := bytes.NewReader(hdr)
var entries []Blob
pos := uint(0)
for {
e := headerEntry{}
2016-08-07 12:50:24 +00:00
err = binary.Read(rd, binary.LittleEndian, &e)
if err == io.EOF {
break
}
if err != nil {
return nil, err
2015-04-26 13:36:49 +00:00
}
entry := Blob{
Length: uint(e.Length),
ID: e.ID,
Offset: pos,
}
switch e.Type {
case 0:
entry.Type = Data
case 1:
entry.Type = Tree
default:
return nil, fmt.Errorf("invalid type %d", e.Type)
}
entries = append(entries, entry)
pos += uint(e.Length)
2015-04-26 13:36:49 +00:00
}
2016-08-07 12:50:24 +00:00
up := &Unpacker{
2015-04-26 13:36:49 +00:00
rd: rd,
k: k,
Entries: entries,
}
2016-08-07 12:50:24 +00:00
return up, nil
2015-04-26 13:36:49 +00:00
}