| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | package pack | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/binary" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 	"github.com/restic/restic/internal/debug" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/errors" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Packer is used to create a new Pack. | 
					
						
							|  |  |  | type Packer struct { | 
					
						
							| 
									
										
										
										
											2016-08-31 22:39:36 +02:00
										 |  |  | 	blobs []restic.Blob | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	bytes uint | 
					
						
							|  |  |  | 	k     *crypto.Key | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	wr    io.Writer | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	m sync.Mutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-09 14:25:55 +01:00
										 |  |  | // NewPacker returns a new Packer that can be used to pack blobs together. | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | func NewPacker(k *crypto.Key, wr io.Writer) *Packer { | 
					
						
							|  |  |  | 	return &Packer{k: k, wr: wr} | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02: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 20:58:57 +02:00
										 |  |  | func (p *Packer) Add(t restic.BlobType, id restic.ID, data []byte) (int, error) { | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	p.m.Lock() | 
					
						
							|  |  |  | 	defer p.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-05 21:52:34 +01:00
										 |  |  | 	c := restic.Blob{BlobHandle: restic.BlobHandle{Type: t, ID: id}} | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	n, err := p.wr.Write(data) | 
					
						
							| 
									
										
										
										
											2015-08-08 13:47:08 +02:00
										 |  |  | 	c.Length = uint(n) | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	c.Offset = p.bytes | 
					
						
							|  |  |  | 	p.bytes += uint(n) | 
					
						
							|  |  |  | 	p.blobs = append(p.blobs, c) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 22:16:58 +02:00
										 |  |  | 	return n, errors.Wrap(err, "Write") | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | var EntrySize = uint(binary.Size(restic.BlobType(0)) + headerLengthSize + len(restic.ID{})) | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | // headerEntry describes the format of header entries. It serves only as | 
					
						
							|  |  |  | // documentation. | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | type headerEntry struct { | 
					
						
							| 
									
										
										
										
											2016-08-04 18:40:31 +02:00
										 |  |  | 	Type   uint8 | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	Length uint32 | 
					
						
							| 
									
										
										
										
											2016-09-01 21:37:59 +02:00
										 |  |  | 	ID     restic.ID | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Finalize writes the header for all added blobs and finalizes the pack. | 
					
						
							| 
									
										
										
										
											2020-03-09 14:11:25 +01:00
										 |  |  | // Returned are the number of bytes written, including the header. | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | func (p *Packer) Finalize() (uint, error) { | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	p.m.Lock() | 
					
						
							|  |  |  | 	defer p.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 19:30:14 +01:00
										 |  |  | 	bytesWritten := p.bytes | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 	header, err := p.makeHeader() | 
					
						
							| 
									
										
										
										
											2015-04-30 00:36:36 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 	encryptedHeader := make([]byte, 0, len(header)+p.k.Overhead()+p.k.NonceSize()) | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	nonce := crypto.NewRandomNonce() | 
					
						
							|  |  |  | 	encryptedHeader = append(encryptedHeader, nonce...) | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 	encryptedHeader = p.k.Seal(encryptedHeader, nonce, header, nil) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:41:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 19:30:14 +01:00
										 |  |  | 	// append the header | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	n, err := p.wr.Write(encryptedHeader) | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-29 22:16:58 +02:00
										 |  |  | 		return 0, errors.Wrap(err, "Write") | 
					
						
							| 
									
										
										
										
											2016-01-24 19:30:14 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 	hdrBytes := restic.CiphertextLength(len(header)) | 
					
						
							| 
									
										
										
										
											2017-01-13 12:57:05 +01:00
										 |  |  | 	if n != hdrBytes { | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 		return 0, errors.New("wrong number of bytes written") | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-13 12:57:05 +01:00
										 |  |  | 	bytesWritten += uint(hdrBytes) | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// write length | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	err = binary.Write(p.wr, binary.LittleEndian, uint32(restic.CiphertextLength(len(p.blobs)*int(EntrySize)))) | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-29 22:16:58 +02:00
										 |  |  | 		return 0, errors.Wrap(err, "binary.Write") | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-30 00:19:56 +02:00
										 |  |  | 	bytesWritten += uint(binary.Size(uint32(0))) | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-30 00:06:08 +02:00
										 |  |  | 	p.bytes = uint(bytesWritten) | 
					
						
							| 
									
										
										
										
											2016-03-06 12:26:25 +01:00
										 |  |  | 	return bytesWritten, nil | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | // makeHeader constructs the header for p. | 
					
						
							|  |  |  | func (p *Packer) makeHeader() ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	buf := make([]byte, 0, len(p.blobs)*int(EntrySize)) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:36:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 	for _, b := range p.blobs { | 
					
						
							| 
									
										
										
										
											2016-08-04 18:40:31 +02:00
										 |  |  | 		switch b.Type { | 
					
						
							| 
									
										
										
										
											2016-08-31 20:58:57 +02:00
										 |  |  | 		case restic.DataBlob: | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 			buf = append(buf, 0) | 
					
						
							| 
									
										
										
										
											2016-08-31 20:58:57 +02:00
										 |  |  | 		case restic.TreeBlob: | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 			buf = append(buf, 1) | 
					
						
							| 
									
										
										
										
											2016-08-04 18:40:31 +02:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 			return nil, errors.Errorf("invalid blob type %v", b.Type) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:36:36 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 		var lenLE [4]byte | 
					
						
							|  |  |  | 		binary.LittleEndian.PutUint32(lenLE[:], uint32(b.Length)) | 
					
						
							|  |  |  | 		buf = append(buf, lenLE[:]...) | 
					
						
							|  |  |  | 		buf = append(buf, b.ID[:]...) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:36:36 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 	return buf, nil | 
					
						
							| 
									
										
										
										
											2015-04-30 00:36:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02: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 22:39:36 +02:00
										 |  |  | func (p *Packer) Blobs() []restic.Blob { | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	p.m.Lock() | 
					
						
							|  |  |  | 	defer p.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return p.blobs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Packer) String() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("<Packer %d blobs, %d bytes>", len(p.blobs), p.bytes) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	// we require at least one entry in the header, and one blob for a pack file | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	minFileSize = EntrySize + crypto.Extension + uint(headerLengthSize) | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2020-05-01 22:56:34 +02:00
										 |  |  | 	// size of the header-length field at the end of the file; it is a uint32 | 
					
						
							|  |  |  | 	headerLengthSize = 4 | 
					
						
							| 
									
										
										
										
											2021-01-30 20:45:57 +01:00
										 |  |  | 	// HeaderSize is the header's constant overhead (independent of #entries) | 
					
						
							| 
									
										
										
										
											2020-05-01 22:56:34 +02:00
										 |  |  | 	HeaderSize = headerLengthSize + crypto.Extension | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	maxHeaderSize = 16 * 1024 * 1024 | 
					
						
							|  |  |  | 	// number of header enries to download as part of header-length request | 
					
						
							|  |  |  | 	eagerEntries = 15 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | // readRecords reads up to max records from the underlying ReaderAt, returning | 
					
						
							|  |  |  | // the raw header, the total number of records in the header, and any error. | 
					
						
							|  |  |  | // If the header contains fewer than max entries, the header is truncated to | 
					
						
							|  |  |  | // the appropriate size. | 
					
						
							|  |  |  | func readRecords(rd io.ReaderAt, size int64, max int) ([]byte, int, error) { | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	var bufsize int | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	bufsize += max * int(EntrySize) | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	bufsize += crypto.Extension | 
					
						
							|  |  |  | 	bufsize += headerLengthSize | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if bufsize > int(size) { | 
					
						
							|  |  |  | 		bufsize = int(size) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-25 21:51:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	b := make([]byte, bufsize) | 
					
						
							|  |  |  | 	off := size - int64(bufsize) | 
					
						
							|  |  |  | 	if _, err := rd.ReadAt(b, off); err != nil { | 
					
						
							|  |  |  | 		return nil, 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	hlen := binary.LittleEndian.Uint32(b[len(b)-headerLengthSize:]) | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	b = b[:len(b)-headerLengthSize] | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	debug.Log("header length: %v", hlen) | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	switch { | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	case hlen == 0: | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 		err = InvalidFileError{Message: "header length is zero"} | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	case hlen < crypto.Extension: | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 		err = InvalidFileError{Message: "header length is too small"} | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	case (hlen-crypto.Extension)%uint32(EntrySize) != 0: | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 		err = InvalidFileError{Message: "header length is invalid"} | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	case int64(hlen) > size-int64(headerLengthSize): | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 		err = InvalidFileError{Message: "header is larger than file"} | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	case int64(hlen) > maxHeaderSize: | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 		err = InvalidFileError{Message: "header is larger than maxHeaderSize"} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, 0, errors.Wrap(err, "readHeader") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-20 21:28:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	total := (int(hlen) - crypto.Extension) / int(EntrySize) | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	if total < max { | 
					
						
							|  |  |  | 		// truncate to the beginning of the pack header | 
					
						
							|  |  |  | 		b = b[len(b)-int(hlen):] | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-08 20:40:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 17:37:10 +00:00
										 |  |  | 	return b, total, nil | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-23 18:51:22 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-25 21:51:07 +02:00
										 |  |  | // 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) { | 
					
						
							| 
									
										
										
										
											2017-06-08 21:04:07 +02:00
										 |  |  | 	debug.Log("size: %v", size) | 
					
						
							| 
									
										
										
										
											2017-06-08 20:40:12 +02:00
										 |  |  | 	if size < int64(minFileSize) { | 
					
						
							| 
									
										
										
										
											2017-06-08 21:04:07 +02:00
										 |  |  | 		err := InvalidFileError{Message: "file is too small"} | 
					
						
							| 
									
										
										
										
											2017-06-08 20:40:12 +02:00
										 |  |  | 		return nil, errors.Wrap(err, "readHeader") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 18:51:22 -05:00
										 |  |  | 	// assuming extra request is significantly slower than extra bytes download, | 
					
						
							|  |  |  | 	// eagerly download eagerEntries header entries as part of header-length request. | 
					
						
							|  |  |  | 	// only make second request if actual number of entries is greater than eagerEntries | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	b, c, err := readRecords(rd, size, eagerEntries) | 
					
						
							| 
									
										
										
										
											2016-08-07 13:12:52 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-25 21:51:07 +02:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-07 13:12:52 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	if c <= eagerEntries { | 
					
						
							|  |  |  | 		// eager read sufficed, return what we got | 
					
						
							|  |  |  | 		return b, nil | 
					
						
							| 
									
										
										
										
											2016-08-23 22:21:29 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	b, _, err = readRecords(rd, size, c) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-07 13:12:52 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-21 23:22:18 +00:00
										 |  |  | 	return b, nil | 
					
						
							| 
									
										
										
										
											2016-08-25 21:51:07 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-08 20:40:12 +02:00
										 |  |  | // InvalidFileError is return when a file is found that is not a pack file. | 
					
						
							|  |  |  | type InvalidFileError struct { | 
					
						
							|  |  |  | 	Message string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e InvalidFileError) Error() string { | 
					
						
							|  |  |  | 	return e.Message | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | // List returns the list of entries found in a pack file and the length of the | 
					
						
							|  |  |  | // header (including header size and crypto overhead) | 
					
						
							|  |  |  | func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, hdrSize uint32, err error) { | 
					
						
							| 
									
										
										
										
											2016-08-25 21:51:07 +02:00
										 |  |  | 	buf, err := readHeader(rd, size) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | 		return nil, 0, err | 
					
						
							| 
									
										
										
										
											2016-08-25 21:51:07 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	if len(buf) < k.NonceSize()+k.Overhead() { | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | 		return nil, 0, errors.New("invalid header, too small") | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | 	hdrSize = headerLengthSize + uint32(len(buf)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 11:33:57 +01:00
										 |  |  | 	nonce, buf := buf[:k.NonceSize()], buf[k.NonceSize():] | 
					
						
							|  |  |  | 	buf, err = k.Open(buf[:0], nonce, buf, nil) | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | 		return nil, 0, err | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	entries = make([]restic.Blob, 0, uint(len(buf))/EntrySize) | 
					
						
							| 
									
										
										
										
											2017-01-15 15:27:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-25 18:07:51 +01:00
										 |  |  | 	pos := uint(0) | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 	for len(buf) > 0 { | 
					
						
							|  |  |  | 		entry, err := parseHeaderEntry(buf) | 
					
						
							| 
									
										
										
										
											2015-10-25 18:07:51 +01:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | 			return nil, 0, err | 
					
						
							| 
									
										
										
										
											2016-08-04 18:40:31 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 		entry.Offset = pos | 
					
						
							| 
									
										
										
										
											2016-08-04 18:40:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		entries = append(entries, entry) | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 		pos += entry.Length | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 		buf = buf[EntrySize:] | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 04:03:45 +01:00
										 |  |  | 	return entries, hdrSize, nil | 
					
						
							| 
									
										
										
										
											2015-04-26 15:36:49 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-01 22:56:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // PackedSizeOfBlob returns the size a blob actually uses when saved in a pack | 
					
						
							|  |  |  | func PackedSizeOfBlob(blobLength uint) uint { | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	return blobLength + EntrySize | 
					
						
							| 
									
										
										
										
											2020-05-01 22:56:34 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func parseHeaderEntry(p []byte) (b restic.Blob, err error) { | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	if uint(len(p)) < EntrySize { | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 		err = errors.Errorf("parseHeaderEntry: buffer of size %d too short", len(p)) | 
					
						
							|  |  |  | 		return b, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-16 04:18:55 +01:00
										 |  |  | 	p = p[:EntrySize] | 
					
						
							| 
									
										
										
										
											2020-07-22 18:46:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch p[0] { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		b.Type = restic.DataBlob | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		b.Type = restic.TreeBlob | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return b, errors.Errorf("invalid type %d", p[0]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b.Length = uint(binary.LittleEndian.Uint32(p[1:5])) | 
					
						
							|  |  |  | 	copy(b.ID[:], p[5:]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return b, nil | 
					
						
							|  |  |  | } |