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"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2016-08-31 18:58:57 +00:00
|
|
|
"restic"
|
2015-04-26 13:36:49 +00:00
|
|
|
"sync"
|
|
|
|
|
2016-08-21 15:46:23 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
2016-02-14 14:29:28 +00:00
|
|
|
"restic/crypto"
|
2015-04-26 13:36:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Packer is used to create a new Pack.
|
|
|
|
type Packer struct {
|
2016-08-31 20:39:36 +00:00
|
|
|
blobs []restic.Blob
|
2015-04-26 13:36:49 +00:00
|
|
|
|
|
|
|
bytes uint
|
|
|
|
k *crypto.Key
|
2016-03-06 11:26:25 +00:00
|
|
|
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
|
2016-03-06 11:26:25 +00:00
|
|
|
// together. If wr is nil, a bytes.Buffer is used.
|
|
|
|
func NewPacker(k *crypto.Key, wr io.Writer) *Packer {
|
2016-03-06 11:34:23 +00:00
|
|
|
if wr == nil {
|
|
|
|
wr = bytes.NewBuffer(nil)
|
|
|
|
}
|
2016-03-06 11:26:25 +00:00
|
|
|
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.
|
2016-08-31 18:58:57 +00:00
|
|
|
func (p *Packer) Add(t restic.BlobType, id restic.ID, data []byte) (int, error) {
|
2015-04-26 13:36:49 +00:00
|
|
|
p.m.Lock()
|
|
|
|
defer p.m.Unlock()
|
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
c := restic.Blob{Type: t, ID: id}
|
2015-04-26 13:36:49 +00:00
|
|
|
|
2016-03-06 11:26:25 +00:00
|
|
|
n, err := p.wr.Write(data)
|
2015-08-08 11:47:08 +00:00
|
|
|
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)
|
|
|
|
|
2016-08-29 20:16:58 +00:00
|
|
|
return n, errors.Wrap(err, "Write")
|
2015-04-26 13:36:49 +00:00
|
|
|
}
|
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
var entrySize = uint(binary.Size(restic.BlobType(0)) + binary.Size(uint32(0)) + restic.IDSize)
|
2015-04-26 13:36:49 +00:00
|
|
|
|
|
|
|
// headerEntry is used with encoding/binary to read and write header entries
|
|
|
|
type headerEntry struct {
|
2016-08-04 16:40:31 +00:00
|
|
|
Type uint8
|
2015-04-26 13:36:49 +00:00
|
|
|
Length uint32
|
2016-08-31 20:39:36 +00:00
|
|
|
ID [restic.IDSize]byte
|
2015-04-26 13:36:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finalize writes the header for all added blobs and finalizes the pack.
|
2016-03-06 11:26:25 +00:00
|
|
|
// 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 {
|
2016-03-06 11:26:25 +00:00
|
|
|
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 {
|
2016-03-06 11:26:25 +00:00
|
|
|
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
|
2016-03-06 11:26:25 +00:00
|
|
|
n, err := p.wr.Write(encryptedHeader)
|
2015-04-26 13:36:49 +00:00
|
|
|
if err != nil {
|
2016-08-29 20:16:58 +00:00
|
|
|
return 0, errors.Wrap(err, "Write")
|
2016-01-24 18:30:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hdrBytes := bytesHeader + crypto.Extension
|
|
|
|
if uint(n) != hdrBytes {
|
2016-03-06 11:26:25 +00:00
|
|
|
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
|
2016-03-06 11:26:25 +00:00
|
|
|
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 {
|
2016-08-29 20:16:58 +00:00
|
|
|
return 0, errors.Wrap(err, "binary.Write")
|
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
|
|
|
|
2015-04-29 22:06:08 +00:00
|
|
|
p.bytes = uint(bytesWritten)
|
2015-04-26 13:36:49 +00:00
|
|
|
|
2016-03-06 11:26:25 +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{
|
2015-08-08 11:47:08 +00:00
|
|
|
Length: uint32(b.Length),
|
2015-07-25 15:05:45 +00:00
|
|
|
ID: b.ID,
|
2015-04-29 22:36:36 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 16:40:31 +00:00
|
|
|
switch b.Type {
|
2016-08-31 18:58:57 +00:00
|
|
|
case restic.DataBlob:
|
2016-08-04 16:40:31 +00:00
|
|
|
entry.Type = 0
|
2016-08-31 18:58:57 +00:00
|
|
|
case restic.TreeBlob:
|
2016-08-04 16:40:31 +00:00
|
|
|
entry.Type = 1
|
|
|
|
default:
|
2016-08-21 15:48:36 +00:00
|
|
|
return 0, errors.Errorf("invalid blob type %v", b.Type)
|
2016-08-04 16:40:31 +00:00
|
|
|
}
|
|
|
|
|
2015-04-29 22:36:36 +00:00
|
|
|
err := binary.Write(wr, binary.LittleEndian, entry)
|
|
|
|
if err != nil {
|
2016-08-29 20:16:58 +00:00
|
|
|
return bytesWritten, errors.Wrap(err, "binary.Write")
|
2015-04-29 22:36:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2016-08-31 20:39:36 +00:00
|
|
|
func (p *Packer) Blobs() []restic.Blob {
|
2015-04-26 13:36:49 +00:00
|
|
|
p.m.Lock()
|
|
|
|
defer p.m.Unlock()
|
|
|
|
|
|
|
|
return p.blobs
|
|
|
|
}
|
|
|
|
|
2016-03-06 11:26:25 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
// readHeaderLength returns the header length read from the end of the file
|
|
|
|
// encoded in little endian.
|
|
|
|
func readHeaderLength(rd io.ReaderAt, size int64) (uint32, error) {
|
|
|
|
off := size - int64(binary.Size(uint32(0)))
|
2016-08-07 11:12:52 +00:00
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
buf := make([]byte, binary.Size(uint32(0)))
|
|
|
|
n, err := rd.ReadAt(buf, off)
|
|
|
|
if err != nil {
|
2016-08-29 20:16:58 +00:00
|
|
|
return 0, errors.Wrap(err, "ReadAt")
|
2016-08-25 19:51:07 +00:00
|
|
|
}
|
2016-08-07 14:24:02 +00:00
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
if n != len(buf) {
|
|
|
|
return 0, errors.New("not enough bytes read")
|
2016-08-07 14:24:02 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
return binary.LittleEndian.Uint32(buf), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const maxHeaderSize = 16 * 1024 * 1024
|
|
|
|
|
|
|
|
// readHeader reads the header at the end of rd. size is the length of the
|
|
|
|
// whole data accessible in rd.
|
|
|
|
func readHeader(rd io.ReaderAt, size int64) ([]byte, error) {
|
|
|
|
hl, err := readHeaderLength(rd, size)
|
2016-08-07 11:12:52 +00:00
|
|
|
if err != nil {
|
2016-08-25 19:51:07 +00:00
|
|
|
return nil, err
|
2016-08-07 11:12:52 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
if int64(hl) > size-int64(binary.Size(hl)) {
|
|
|
|
return nil, errors.New("header is larger than file")
|
|
|
|
}
|
2015-04-26 13:36:49 +00:00
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
if int64(hl) > maxHeaderSize {
|
|
|
|
return nil, errors.New("header is larger than maxHeaderSize")
|
2016-08-23 20:21:29 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
buf := make([]byte, int(hl))
|
|
|
|
n, err := rd.ReadAt(buf, size-int64(hl)-int64(binary.Size(hl)))
|
|
|
|
if err != nil {
|
2016-08-29 20:16:58 +00:00
|
|
|
return nil, errors.Wrap(err, "ReadAt")
|
2016-08-25 19:51:07 +00:00
|
|
|
}
|
2016-08-23 20:21:29 +00:00
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
if n != len(buf) {
|
|
|
|
return nil, errors.New("not enough bytes read")
|
2016-08-07 11:12:52 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 19:51:07 +00:00
|
|
|
return buf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// List returns the list of entries found in a pack file.
|
2016-08-31 20:39:36 +00:00
|
|
|
func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, err error) {
|
2016-08-25 19:51:07 +00:00
|
|
|
buf, err := readHeader(rd, size)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-04-26 13:36:49 +00:00
|
|
|
|
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-25 19:51:07 +00:00
|
|
|
hdrRd := bytes.NewReader(hdr)
|
2016-08-07 12:50:24 +00:00
|
|
|
|
2015-10-25 17:07:51 +00:00
|
|
|
pos := uint(0)
|
|
|
|
for {
|
|
|
|
e := headerEntry{}
|
2016-08-25 19:51:07 +00:00
|
|
|
err = binary.Read(hdrRd, binary.LittleEndian, &e)
|
2016-08-29 17:18:57 +00:00
|
|
|
if errors.Cause(err) == io.EOF {
|
2015-10-25 17:07:51 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2016-08-29 20:16:58 +00:00
|
|
|
return nil, errors.Wrap(err, "binary.Read")
|
2015-04-26 13:36:49 +00:00
|
|
|
}
|
2015-10-25 17:07:51 +00:00
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
entry := restic.Blob{
|
2015-10-25 17:07:51 +00:00
|
|
|
Length: uint(e.Length),
|
|
|
|
ID: e.ID,
|
|
|
|
Offset: pos,
|
2016-08-04 16:40:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch e.Type {
|
|
|
|
case 0:
|
2016-08-31 18:58:57 +00:00
|
|
|
entry.Type = restic.DataBlob
|
2016-08-04 16:40:31 +00:00
|
|
|
case 1:
|
2016-08-31 18:58:57 +00:00
|
|
|
entry.Type = restic.TreeBlob
|
2016-08-04 16:40:31 +00:00
|
|
|
default:
|
2016-08-21 15:48:36 +00:00
|
|
|
return nil, errors.Errorf("invalid type %d", e.Type)
|
2016-08-04 16:40:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
entries = append(entries, entry)
|
2015-10-25 17:07:51 +00:00
|
|
|
|
|
|
|
pos += uint(e.Length)
|
2015-04-26 13:36:49 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 19:08:16 +00:00
|
|
|
return entries, nil
|
2015-04-26 13:36:49 +00:00
|
|
|
}
|