github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/pack/pack.go (about)

     1  package pack
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  	"sync"
     9  
    10  	"github.com/restic/restic/internal/debug"
    11  	"github.com/restic/restic/internal/errors"
    12  	"github.com/restic/restic/internal/restic"
    13  
    14  	"github.com/restic/restic/internal/crypto"
    15  )
    16  
    17  // Packer is used to create a new Pack.
    18  type Packer struct {
    19  	blobs []restic.Blob
    20  
    21  	bytes uint
    22  	k     *crypto.Key
    23  	wr    io.Writer
    24  
    25  	m sync.Mutex
    26  }
    27  
    28  // NewPacker returns a new Packer that can be used to pack blobs
    29  // together. If wr is nil, a bytes.Buffer is used.
    30  func NewPacker(k *crypto.Key, wr io.Writer) *Packer {
    31  	if wr == nil {
    32  		wr = bytes.NewBuffer(nil)
    33  	}
    34  	return &Packer{k: k, wr: wr}
    35  }
    36  
    37  // Add saves the data read from rd as a new blob to the packer. Returned is the
    38  // number of bytes written to the pack.
    39  func (p *Packer) Add(t restic.BlobType, id restic.ID, data []byte) (int, error) {
    40  	p.m.Lock()
    41  	defer p.m.Unlock()
    42  
    43  	c := restic.Blob{Type: t, ID: id}
    44  
    45  	n, err := p.wr.Write(data)
    46  	c.Length = uint(n)
    47  	c.Offset = p.bytes
    48  	p.bytes += uint(n)
    49  	p.blobs = append(p.blobs, c)
    50  
    51  	return n, errors.Wrap(err, "Write")
    52  }
    53  
    54  var entrySize = uint(binary.Size(restic.BlobType(0)) + binary.Size(uint32(0)) + len(restic.ID{}))
    55  
    56  // headerEntry is used with encoding/binary to read and write header entries
    57  type headerEntry struct {
    58  	Type   uint8
    59  	Length uint32
    60  	ID     restic.ID
    61  }
    62  
    63  // Finalize writes the header for all added blobs and finalizes the pack.
    64  // Returned are the number of bytes written, including the header. If the
    65  // underlying writer implements io.Closer, it is closed.
    66  func (p *Packer) Finalize() (uint, error) {
    67  	p.m.Lock()
    68  	defer p.m.Unlock()
    69  
    70  	bytesWritten := p.bytes
    71  
    72  	hdrBuf := bytes.NewBuffer(nil)
    73  	bytesHeader, err := p.writeHeader(hdrBuf)
    74  	if err != nil {
    75  		return 0, err
    76  	}
    77  
    78  	encryptedHeader := make([]byte, 0, hdrBuf.Len()+p.k.Overhead()+p.k.NonceSize())
    79  	nonce := crypto.NewRandomNonce()
    80  	encryptedHeader = append(encryptedHeader, nonce...)
    81  	encryptedHeader = p.k.Seal(encryptedHeader, nonce, hdrBuf.Bytes(), nil)
    82  
    83  	// append the header
    84  	n, err := p.wr.Write(encryptedHeader)
    85  	if err != nil {
    86  		return 0, errors.Wrap(err, "Write")
    87  	}
    88  
    89  	hdrBytes := restic.CiphertextLength(int(bytesHeader))
    90  	if n != hdrBytes {
    91  		return 0, errors.New("wrong number of bytes written")
    92  	}
    93  
    94  	bytesWritten += uint(hdrBytes)
    95  
    96  	// write length
    97  	err = binary.Write(p.wr, binary.LittleEndian, uint32(restic.CiphertextLength(len(p.blobs)*int(entrySize))))
    98  	if err != nil {
    99  		return 0, errors.Wrap(err, "binary.Write")
   100  	}
   101  	bytesWritten += uint(binary.Size(uint32(0)))
   102  
   103  	p.bytes = uint(bytesWritten)
   104  
   105  	if w, ok := p.wr.(io.Closer); ok {
   106  		return bytesWritten, w.Close()
   107  	}
   108  
   109  	return bytesWritten, nil
   110  }
   111  
   112  // writeHeader constructs and writes the header to wr.
   113  func (p *Packer) writeHeader(wr io.Writer) (bytesWritten uint, err error) {
   114  	for _, b := range p.blobs {
   115  		entry := headerEntry{
   116  			Length: uint32(b.Length),
   117  			ID:     b.ID,
   118  		}
   119  
   120  		switch b.Type {
   121  		case restic.DataBlob:
   122  			entry.Type = 0
   123  		case restic.TreeBlob:
   124  			entry.Type = 1
   125  		default:
   126  			return 0, errors.Errorf("invalid blob type %v", b.Type)
   127  		}
   128  
   129  		err := binary.Write(wr, binary.LittleEndian, entry)
   130  		if err != nil {
   131  			return bytesWritten, errors.Wrap(err, "binary.Write")
   132  		}
   133  
   134  		bytesWritten += entrySize
   135  	}
   136  
   137  	return
   138  }
   139  
   140  // Size returns the number of bytes written so far.
   141  func (p *Packer) Size() uint {
   142  	p.m.Lock()
   143  	defer p.m.Unlock()
   144  
   145  	return p.bytes
   146  }
   147  
   148  // Count returns the number of blobs in this packer.
   149  func (p *Packer) Count() int {
   150  	p.m.Lock()
   151  	defer p.m.Unlock()
   152  
   153  	return len(p.blobs)
   154  }
   155  
   156  // Blobs returns the slice of blobs that have been written.
   157  func (p *Packer) Blobs() []restic.Blob {
   158  	p.m.Lock()
   159  	defer p.m.Unlock()
   160  
   161  	return p.blobs
   162  }
   163  
   164  // Writer return the underlying writer.
   165  func (p *Packer) Writer() io.Writer {
   166  	return p.wr
   167  }
   168  
   169  func (p *Packer) String() string {
   170  	return fmt.Sprintf("<Packer %d blobs, %d bytes>", len(p.blobs), p.bytes)
   171  }
   172  
   173  // readHeaderLength returns the header length read from the end of the file
   174  // encoded in little endian.
   175  func readHeaderLength(rd io.ReaderAt, size int64) (uint32, error) {
   176  	off := size - int64(binary.Size(uint32(0)))
   177  
   178  	buf := make([]byte, binary.Size(uint32(0)))
   179  	n, err := rd.ReadAt(buf, off)
   180  	if err != nil {
   181  		return 0, errors.Wrap(err, "ReadAt")
   182  	}
   183  
   184  	if n != len(buf) {
   185  		return 0, errors.New("not enough bytes read")
   186  	}
   187  
   188  	return binary.LittleEndian.Uint32(buf), nil
   189  }
   190  
   191  const maxHeaderSize = 16 * 1024 * 1024
   192  
   193  // we require at least one entry in the header, and one blob for a pack file
   194  var minFileSize = entrySize + crypto.Extension
   195  
   196  // readHeader reads the header at the end of rd. size is the length of the
   197  // whole data accessible in rd.
   198  func readHeader(rd io.ReaderAt, size int64) ([]byte, error) {
   199  	debug.Log("size: %v", size)
   200  	if size == 0 {
   201  		err := InvalidFileError{Message: "file is empty"}
   202  		return nil, errors.Wrap(err, "readHeader")
   203  	}
   204  
   205  	if size < int64(minFileSize) {
   206  		err := InvalidFileError{Message: "file is too small"}
   207  		return nil, errors.Wrap(err, "readHeader")
   208  	}
   209  
   210  	hl, err := readHeaderLength(rd, size)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	debug.Log("header length: %v", size)
   216  
   217  	if hl == 0 {
   218  		err := InvalidFileError{Message: "header length is zero"}
   219  		return nil, errors.Wrap(err, "readHeader")
   220  	}
   221  
   222  	if hl < crypto.Extension {
   223  		err := InvalidFileError{Message: "header length is too small"}
   224  		return nil, errors.Wrap(err, "readHeader")
   225  	}
   226  
   227  	if (hl-crypto.Extension)%uint32(entrySize) != 0 {
   228  		err := InvalidFileError{Message: "header length is invalid"}
   229  		return nil, errors.Wrap(err, "readHeader")
   230  	}
   231  
   232  	if int64(hl) > size-int64(binary.Size(hl)) {
   233  		err := InvalidFileError{Message: "header is larger than file"}
   234  		return nil, errors.Wrap(err, "readHeader")
   235  	}
   236  
   237  	if int64(hl) > maxHeaderSize {
   238  		err := InvalidFileError{Message: "header is larger than maxHeaderSize"}
   239  		return nil, errors.Wrap(err, "readHeader")
   240  	}
   241  
   242  	buf := make([]byte, int(hl))
   243  	n, err := rd.ReadAt(buf, size-int64(hl)-int64(binary.Size(hl)))
   244  	if err != nil {
   245  		return nil, errors.Wrap(err, "ReadAt")
   246  	}
   247  
   248  	if n != len(buf) {
   249  		return nil, errors.New("not enough bytes read")
   250  	}
   251  
   252  	return buf, nil
   253  }
   254  
   255  // InvalidFileError is return when a file is found that is not a pack file.
   256  type InvalidFileError struct {
   257  	Message string
   258  }
   259  
   260  func (e InvalidFileError) Error() string {
   261  	return e.Message
   262  }
   263  
   264  // List returns the list of entries found in a pack file.
   265  func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, err error) {
   266  	buf, err := readHeader(rd, size)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	if len(buf) < k.NonceSize()+k.Overhead() {
   272  		return nil, errors.New("invalid header, too small")
   273  	}
   274  
   275  	nonce, buf := buf[:k.NonceSize()], buf[k.NonceSize():]
   276  	buf, err = k.Open(buf[:0], nonce, buf, nil)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	hdrRd := bytes.NewReader(buf)
   282  
   283  	entries = make([]restic.Blob, 0, uint(len(buf))/entrySize)
   284  
   285  	pos := uint(0)
   286  	for {
   287  		e := headerEntry{}
   288  		err = binary.Read(hdrRd, binary.LittleEndian, &e)
   289  		if errors.Cause(err) == io.EOF {
   290  			break
   291  		}
   292  
   293  		if err != nil {
   294  			return nil, errors.Wrap(err, "binary.Read")
   295  		}
   296  
   297  		entry := restic.Blob{
   298  			Length: uint(e.Length),
   299  			ID:     e.ID,
   300  			Offset: pos,
   301  		}
   302  
   303  		switch e.Type {
   304  		case 0:
   305  			entry.Type = restic.DataBlob
   306  		case 1:
   307  			entry.Type = restic.TreeBlob
   308  		default:
   309  			return nil, errors.Errorf("invalid type %d", e.Type)
   310  		}
   311  
   312  		entries = append(entries, entry)
   313  
   314  		pos += uint(e.Length)
   315  	}
   316  
   317  	return entries, nil
   318  }