From f72f3dbc6ac67376193ec0ff4a3a6abc45caaf0a Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 7 Aug 2016 13:12:52 +0200 Subject: [PATCH] Buffer last 2048 bytes of a file for unpack --- src/restic/pack/pack.go | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/restic/pack/pack.go b/src/restic/pack/pack.go index 815f9ddb8..91f12a63b 100644 --- a/src/restic/pack/pack.go +++ b/src/restic/pack/pack.go @@ -241,32 +241,55 @@ type Unpacker struct { k *crypto.Key } +const preloadHeaderSize = 2048 + // NewUnpacker returns a pointer to Unpacker which can be used to read // individual Blobs from a pack. func NewUnpacker(k *crypto.Key, rd io.ReadSeeker) (*Unpacker, error) { 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) + _, err = rd.Seek(-int64(len(buf)), 2) + if err != nil { + return nil, fmt.Errorf("seek to -%d failed: %v", len(buf), err) + } + + _, err = io.ReadFull(rd, buf) + if err != nil { + return nil, fmt.Errorf("error reading last %d bytes: %v", len(buf), err) + } + + hdrRd := io.ReadSeeker(bytes.NewReader(buf)) ls := binary.Size(uint32(0)) // reset to the end to read header length - _, err = rd.Seek(-int64(ls), 2) + _, err = hdrRd.Seek(-int64(ls), 2) if err != nil { return nil, fmt.Errorf("seeking to read header length failed: %v", err) } var length uint32 - err = binary.Read(rd, binary.LittleEndian, &length) + err = binary.Read(hdrRd, binary.LittleEndian, &length) if err != nil { return nil, fmt.Errorf("reading header length failed: %v", err) } + // if the header is longer than the preloaded buffer, use the original + // reader (and do another round trip) + if int(length) > preloadHeaderSize-ls { + hdrRd = rd + } + // reset to the beginning of the header - _, err = rd.Seek(-int64(ls)-int64(length), 2) + _, err = hdrRd.Seek(-int64(ls)-int64(length), 2) if err != nil { return nil, fmt.Errorf("seeking to read header length failed: %v", err) } // read header - hrd, err := crypto.DecryptFrom(k, io.LimitReader(rd, int64(length))) + hrd, err := crypto.DecryptFrom(k, io.LimitReader(hdrRd, int64(length))) if err != nil { return nil, err }