diff --git a/cmd/restic/cmd_cat.go b/cmd/restic/cmd_cat.go
index 8d31fa257..ec0535db0 100644
--- a/cmd/restic/cmd_cat.go
+++ b/cmd/restic/cmd_cat.go
@@ -79,7 +79,7 @@ func runCat(gopts GlobalOptions, args []string) error {
 		Println(string(buf))
 		return nil
 	case "index":
-		buf, err := repo.LoadAndDecrypt(gopts.ctx, nil, restic.IndexFile, id)
+		buf, err := repo.LoadUnpacked(gopts.ctx, nil, restic.IndexFile, id)
 		if err != nil {
 			return err
 		}
diff --git a/cmd/restic/cmd_prune.go b/cmd/restic/cmd_prune.go
index a03ae7474..76d7d7ea9 100644
--- a/cmd/restic/cmd_prune.go
+++ b/cmd/restic/cmd_prune.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/restic/restic/internal/debug"
 	"github.com/restic/restic/internal/errors"
+	"github.com/restic/restic/internal/pack"
 	"github.com/restic/restic/internal/repository"
 	"github.com/restic/restic/internal/restic"
 
@@ -245,7 +246,7 @@ func prune(opts PruneOptions, gopts GlobalOptions, repo restic.Repository, usedB
 	indexPack := make(map[restic.ID]packInfo)
 
 	// save computed pack header size
-	for pid, hdrSize := range repo.Index().PackSize(ctx, true) {
+	for pid, hdrSize := range pack.Size(ctx, repo.Index(), true) {
 		// initialize tpe with NumBlobTypes to indicate it's not set
 		indexPack[pid] = packInfo{tpe: restic.NumBlobTypes, usedSize: uint64(hdrSize)}
 	}
diff --git a/cmd/restic/cmd_rebuild_index.go b/cmd/restic/cmd_rebuild_index.go
index 718d2c767..0b3274ec4 100644
--- a/cmd/restic/cmd_rebuild_index.go
+++ b/cmd/restic/cmd_rebuild_index.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"github.com/restic/restic/internal/pack"
 	"github.com/restic/restic/internal/repository"
 	"github.com/restic/restic/internal/restic"
 
@@ -97,7 +98,7 @@ func rebuildIndex(opts RebuildIndexOptions, gopts GlobalOptions, repo *repositor
 		if err != nil {
 			return err
 		}
-		packSizeFromIndex = repo.Index().PackSize(ctx, false)
+		packSizeFromIndex = pack.Size(ctx, repo.Index(), false)
 	}
 
 	Verbosef("getting pack files to read...\n")
diff --git a/cmd/restic/main.go b/cmd/restic/main.go
index 0479124b1..2f962b966 100644
--- a/cmd/restic/main.go
+++ b/cmd/restic/main.go
@@ -102,7 +102,7 @@ func main() {
 		fmt.Fprintf(os.Stderr, "%v\nthe `unlock` command can be used to remove stale locks\n", err)
 	case err == ErrInvalidSourceData:
 		fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
-	case errors.IsFatal(errors.Cause(err)):
+	case errors.IsFatal(err):
 		fmt.Fprintf(os.Stderr, "%v\n", err)
 	case err != nil:
 		fmt.Fprintf(os.Stderr, "%+v\n", err)
diff --git a/internal/archiver/testing.go b/internal/archiver/testing.go
index 873e9ab5d..d8ad0a9e7 100644
--- a/internal/archiver/testing.go
+++ b/internal/archiver/testing.go
@@ -234,6 +234,7 @@ func TestEnsureFileContent(ctx context.Context, t testing.TB, repo restic.Reposi
 			return
 		}
 
+		copy(content[pos:pos+len(part)], part)
 		pos += len(part)
 	}
 
diff --git a/internal/cache/file.go b/internal/cache/file.go
index 0db1275a3..2ee66f520 100644
--- a/internal/cache/file.go
+++ b/internal/cache/file.go
@@ -8,7 +8,6 @@ import (
 	"runtime"
 
 	"github.com/pkg/errors"
-	"github.com/restic/restic/internal/crypto"
 	"github.com/restic/restic/internal/debug"
 	"github.com/restic/restic/internal/fs"
 	"github.com/restic/restic/internal/restic"
@@ -59,7 +58,7 @@ func (c *Cache) load(h restic.Handle, length int, offset int64) (io.ReadCloser,
 		return nil, errors.WithStack(err)
 	}
 
-	if fi.Size() <= crypto.Extension {
+	if fi.Size() <= int64(restic.CiphertextLength(0)) {
 		_ = f.Close()
 		_ = c.remove(h)
 		return nil, errors.Errorf("cached file %v is truncated, removing", h)
@@ -117,7 +116,7 @@ func (c *Cache) Save(h restic.Handle, rd io.Reader) error {
 		return errors.Wrap(err, "Copy")
 	}
 
-	if n <= crypto.Extension {
+	if n <= int64(restic.CiphertextLength(0)) {
 		_ = f.Close()
 		_ = fs.Remove(f.Name())
 		debug.Log("trying to cache truncated file %v, removing", h)
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index 5262789d0..ebf38b632 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -123,7 +123,7 @@ func (c *Checker) LoadIndex(ctx context.Context) (hints []error, errs []error) {
 	}
 
 	// compute pack size using index entries
-	c.packs = c.masterIndex.PackSize(ctx, false)
+	c.packs = pack.Size(ctx, c.masterIndex, false)
 
 	debug.Log("checking for duplicate packs")
 	for packID := range c.packs {
@@ -452,7 +452,7 @@ func checkPack(ctx context.Context, r restic.Repository, id restic.ID, blobs []r
 	sort.Slice(blobs, func(i, j int) bool {
 		return blobs[i].Offset < blobs[j].Offset
 	})
-	idxHdrSize := pack.HeaderSize + len(blobs)*int(pack.EntrySize)
+	idxHdrSize := pack.CalculateHeaderSize(blobs)
 	lastBlobEnd := 0
 	nonContinuousPack := false
 	for _, blob := range blobs {
@@ -542,7 +542,7 @@ func checkPack(ctx context.Context, r restic.Repository, id restic.ID, blobs []r
 		// Check if blob is contained in index and position is correct
 		idxHas := false
 		for _, pb := range idx.Lookup(blob.BlobHandle) {
-			if pb.PackID == id && pb.Offset == blob.Offset && pb.Length == blob.Length {
+			if pb.PackID == id && pb.Blob == blob {
 				idxHas = true
 				break
 			}
diff --git a/internal/errors/fatal.go b/internal/errors/fatal.go
index 02ffdaab4..5fb615cf1 100644
--- a/internal/errors/fatal.go
+++ b/internal/errors/fatal.go
@@ -23,6 +23,8 @@ type Fataler interface {
 // IsFatal returns true if err is a fatal message that should be printed to the
 // user. Then, the program should exit.
 func IsFatal(err error) bool {
+	// unwrap "Wrap" method
+	err = Cause(err)
 	e, ok := err.(Fataler)
 	return ok && e.Fatal()
 }
diff --git a/internal/errors/fatal_test.go b/internal/errors/fatal_test.go
new file mode 100644
index 000000000..41da8dee7
--- /dev/null
+++ b/internal/errors/fatal_test.go
@@ -0,0 +1,22 @@
+package errors_test
+
+import (
+	"testing"
+
+	"github.com/restic/restic/internal/errors"
+)
+
+func TestFatal(t *testing.T) {
+	for _, v := range []struct {
+		err      error
+		expected bool
+	}{
+		{errors.Fatal("broken"), true},
+		{errors.Fatalf("broken %d", 42), true},
+		{errors.New("error"), false},
+	} {
+		if errors.IsFatal(v.err) != v.expected {
+			t.Fatalf("IsFatal for %q, expected: %v, got: %v", v.err, v.expected, errors.IsFatal(v.err))
+		}
+	}
+}
diff --git a/internal/pack/pack.go b/internal/pack/pack.go
index 95f298acb..9fa209054 100644
--- a/internal/pack/pack.go
+++ b/internal/pack/pack.go
@@ -1,6 +1,7 @@
 package pack
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"io"
@@ -46,7 +47,7 @@ func (p *Packer) Add(t restic.BlobType, id restic.ID, data []byte) (int, error)
 	return n, errors.Wrap(err, "Write")
 }
 
-var EntrySize = uint(binary.Size(restic.BlobType(0)) + headerLengthSize + len(restic.ID{}))
+var entrySize = uint(binary.Size(restic.BlobType(0)) + headerLengthSize + len(restic.ID{}))
 
 // headerEntry describes the format of header entries. It serves only as
 // documentation.
@@ -88,7 +89,7 @@ func (p *Packer) Finalize() (uint, error) {
 	bytesWritten += uint(hdrBytes)
 
 	// write length
-	err = binary.Write(p.wr, binary.LittleEndian, uint32(restic.CiphertextLength(len(p.blobs)*int(EntrySize))))
+	err = binary.Write(p.wr, binary.LittleEndian, uint32(hdrBytes))
 	if err != nil {
 		return 0, errors.Wrap(err, "binary.Write")
 	}
@@ -100,7 +101,7 @@ func (p *Packer) Finalize() (uint, error) {
 
 // makeHeader constructs the header for p.
 func (p *Packer) makeHeader() ([]byte, error) {
-	buf := make([]byte, 0, len(p.blobs)*int(EntrySize))
+	buf := make([]byte, 0, len(p.blobs)*int(entrySize))
 
 	for _, b := range p.blobs {
 		switch b.Type {
@@ -151,14 +152,14 @@ func (p *Packer) String() string {
 
 var (
 	// we require at least one entry in the header, and one blob for a pack file
-	minFileSize = EntrySize + crypto.Extension + uint(headerLengthSize)
+	minFileSize = entrySize + crypto.Extension + uint(headerLengthSize)
 )
 
 const (
 	// size of the header-length field at the end of the file; it is a uint32
 	headerLengthSize = 4
-	// HeaderSize is the header's constant overhead (independent of #entries)
-	HeaderSize = headerLengthSize + crypto.Extension
+	// headerSize is the header's constant overhead (independent of #entries)
+	headerSize = headerLengthSize + crypto.Extension
 
 	// MaxHeaderSize is the max size of header including header-length field
 	MaxHeaderSize = 16*1024*1024 + headerLengthSize
@@ -172,7 +173,7 @@ const (
 // the appropriate size.
 func readRecords(rd io.ReaderAt, size int64, max int) ([]byte, int, error) {
 	var bufsize int
-	bufsize += max * int(EntrySize)
+	bufsize += max * int(entrySize)
 	bufsize += crypto.Extension
 	bufsize += headerLengthSize
 
@@ -196,7 +197,7 @@ func readRecords(rd io.ReaderAt, size int64, max int) ([]byte, int, error) {
 		err = InvalidFileError{Message: "header length is zero"}
 	case hlen < crypto.Extension:
 		err = InvalidFileError{Message: "header length is too small"}
-	case (hlen-crypto.Extension)%uint32(EntrySize) != 0:
+	case (hlen-crypto.Extension)%uint32(entrySize) != 0:
 		err = InvalidFileError{Message: "header length is invalid"}
 	case int64(hlen) > size-int64(headerLengthSize):
 		err = InvalidFileError{Message: "header is larger than file"}
@@ -207,7 +208,7 @@ func readRecords(rd io.ReaderAt, size int64, max int) ([]byte, int, error) {
 		return nil, 0, errors.Wrap(err, "readHeader")
 	}
 
-	total := (int(hlen) - crypto.Extension) / int(EntrySize)
+	total := (int(hlen) - crypto.Extension) / int(entrySize)
 	if total < max {
 		// truncate to the beginning of the pack header
 		b = b[len(b)-int(hlen):]
@@ -273,7 +274,7 @@ func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, hdr
 		return nil, 0, err
 	}
 
-	entries = make([]restic.Blob, 0, uint(len(buf))/EntrySize)
+	entries = make([]restic.Blob, 0, uint(len(buf))/entrySize)
 
 	pos := uint(0)
 	for len(buf) > 0 {
@@ -285,23 +286,18 @@ func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, hdr
 
 		entries = append(entries, entry)
 		pos += entry.Length
-		buf = buf[EntrySize:]
+		buf = buf[entrySize:]
 	}
 
 	return entries, hdrSize, nil
 }
 
-// PackedSizeOfBlob returns the size a blob actually uses when saved in a pack
-func PackedSizeOfBlob(blobLength uint) uint {
-	return blobLength + EntrySize
-}
-
 func parseHeaderEntry(p []byte) (b restic.Blob, err error) {
-	if uint(len(p)) < EntrySize {
+	if uint(len(p)) < entrySize {
 		err = errors.Errorf("parseHeaderEntry: buffer of size %d too short", len(p))
 		return b, err
 	}
-	p = p[:EntrySize]
+	p = p[:entrySize]
 
 	switch p[0] {
 	case 0:
@@ -317,3 +313,28 @@ func parseHeaderEntry(p []byte) (b restic.Blob, err error) {
 
 	return b, nil
 }
+
+func CalculateHeaderSize(blobs []restic.Blob) int {
+	return headerSize + len(blobs)*int(entrySize)
+}
+
+// Size returns the size of all packs computed by index information.
+// If onlyHdr is set to true, only the size of the header is returned
+// Note that this function only gives correct sizes, if there are no
+// duplicates in the index.
+func Size(ctx context.Context, mi restic.MasterIndex, onlyHdr bool) map[restic.ID]int64 {
+	packSize := make(map[restic.ID]int64)
+
+	for blob := range mi.Each(ctx) {
+		size, ok := packSize[blob.PackID]
+		if !ok {
+			size = headerSize
+		}
+		if !onlyHdr {
+			size += int64(blob.Length)
+		}
+		packSize[blob.PackID] = size + int64(entrySize)
+	}
+
+	return packSize
+}
diff --git a/internal/pack/pack_internal_test.go b/internal/pack/pack_internal_test.go
index b0a5d2862..b8078c829 100644
--- a/internal/pack/pack_internal_test.go
+++ b/internal/pack/pack_internal_test.go
@@ -41,7 +41,7 @@ func TestParseHeaderEntry(t *testing.T) {
 	buf.Reset()
 	_ = binary.Write(buf, binary.LittleEndian, &h)
 
-	b, err = parseHeaderEntry(buf.Bytes()[:EntrySize-1])
+	b, err = parseHeaderEntry(buf.Bytes()[:entrySize-1])
 	rtest.Assert(t, err != nil, "no error for short input")
 }
 
@@ -58,7 +58,7 @@ func (rd *countingReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
 func TestReadHeaderEagerLoad(t *testing.T) {
 
 	testReadHeader := func(dataSize, entryCount, expectedReadInvocationCount int) {
-		expectedHeader := rtest.Random(0, entryCount*int(EntrySize)+crypto.Extension)
+		expectedHeader := rtest.Random(0, entryCount*int(entrySize)+crypto.Extension)
 
 		buf := &bytes.Buffer{}
 		buf.Write(rtest.Random(0, dataSize))                                             // pack blobs data
@@ -83,8 +83,8 @@ func TestReadHeaderEagerLoad(t *testing.T) {
 	testReadHeader(100, eagerEntries+1, 2)
 
 	// file size == eager header load size
-	eagerLoadSize := int((eagerEntries * EntrySize) + crypto.Extension)
-	headerSize := int(1*EntrySize) + crypto.Extension
+	eagerLoadSize := int((eagerEntries * entrySize) + crypto.Extension)
+	headerSize := int(1*entrySize) + crypto.Extension
 	dataSize := eagerLoadSize - headerSize - binary.Size(uint32(0))
 	testReadHeader(dataSize-1, 1, 1)
 	testReadHeader(dataSize, 1, 1)
@@ -96,8 +96,8 @@ func TestReadHeaderEagerLoad(t *testing.T) {
 
 func TestReadRecords(t *testing.T) {
 	testReadRecords := func(dataSize, entryCount, totalRecords int) {
-		totalHeader := rtest.Random(0, totalRecords*int(EntrySize)+crypto.Extension)
-		off := len(totalHeader) - (entryCount*int(EntrySize) + crypto.Extension)
+		totalHeader := rtest.Random(0, totalRecords*int(entrySize)+crypto.Extension)
+		off := len(totalHeader) - (entryCount*int(entrySize) + crypto.Extension)
 		if off < 0 {
 			off = 0
 		}
@@ -127,8 +127,8 @@ func TestReadRecords(t *testing.T) {
 	testReadRecords(100, eagerEntries, eagerEntries+1)
 
 	// file size == eager header load size
-	eagerLoadSize := int((eagerEntries * EntrySize) + crypto.Extension)
-	headerSize := int(1*EntrySize) + crypto.Extension
+	eagerLoadSize := int((eagerEntries * entrySize) + crypto.Extension)
+	headerSize := int(1*entrySize) + crypto.Extension
 	dataSize := eagerLoadSize - headerSize - binary.Size(uint32(0))
 	testReadRecords(dataSize-1, 1, 1)
 	testReadRecords(dataSize, 1, 1)
diff --git a/internal/pack/pack_test.go b/internal/pack/pack_test.go
index c789e472b..2b7ec7fea 100644
--- a/internal/pack/pack_test.go
+++ b/internal/pack/pack_test.go
@@ -5,7 +5,6 @@ import (
 	"context"
 	"crypto/rand"
 	"crypto/sha256"
-	"encoding/binary"
 	"encoding/json"
 	"io"
 	"testing"
@@ -54,17 +53,18 @@ func verifyBlobs(t testing.TB, bufs []Buf, k *crypto.Key, rd io.ReaderAt, packSi
 	for _, buf := range bufs {
 		written += len(buf.data)
 	}
-	// header length + header + header crypto
-	headerSize := binary.Size(uint32(0)) + restic.CiphertextLength(len(bufs)*int(pack.EntrySize))
-	written += headerSize
-
-	// check length
-	rtest.Equals(t, uint(written), packSize)
 
 	// read and parse it again
 	entries, hdrSize, err := pack.List(k, rd, int64(packSize))
 	rtest.OK(t, err)
 	rtest.Equals(t, len(entries), len(bufs))
+
+	// check the head size calculation for consistency
+	headerSize := pack.CalculateHeaderSize(entries)
+	written += headerSize
+
+	// check length
+	rtest.Equals(t, uint(written), packSize)
 	rtest.Equals(t, headerSize, int(hdrSize))
 
 	var buf []byte
diff --git a/internal/repository/index.go b/internal/repository/index.go
index fdd57b052..3db19b3b8 100644
--- a/internal/repository/index.go
+++ b/internal/repository/index.go
@@ -543,7 +543,7 @@ func (idx *Index) merge(idx2 *Index) error {
 			m.foreachWithID(e2.id, func(e *indexEntry) {
 				b := idx.toPackedBlob(e, restic.BlobType(typ))
 				b2 := idx2.toPackedBlob(e2, restic.BlobType(typ))
-				if b.Length == b2.Length && b.Offset == b2.Offset && b.PackID == b2.PackID {
+				if b == b2 {
 					found = true
 				}
 			})
diff --git a/internal/repository/index_parallel.go b/internal/repository/index_parallel.go
index ffe03d427..545a05921 100644
--- a/internal/repository/index_parallel.go
+++ b/internal/repository/index_parallel.go
@@ -53,7 +53,7 @@ func ForAllIndexes(ctx context.Context, repo restic.Repository,
 			var idx *Index
 			oldFormat := false
 
-			buf, err = repo.LoadAndDecrypt(ctx, buf[:0], restic.IndexFile, fi.ID)
+			buf, err = repo.LoadUnpacked(ctx, buf[:0], restic.IndexFile, fi.ID)
 			if err == nil {
 				idx, oldFormat, err = DecodeIndex(buf, fi.ID)
 			}
diff --git a/internal/repository/key.go b/internal/repository/key.go
index 5de154195..8c62d7e36 100644
--- a/internal/repository/key.go
+++ b/internal/repository/key.go
@@ -262,7 +262,7 @@ func AddKey(ctx context.Context, s *Repository, password, username, hostname str
 	}
 
 	nonce := crypto.NewRandomNonce()
-	ciphertext := make([]byte, 0, len(buf)+newkey.user.Overhead()+newkey.user.NonceSize())
+	ciphertext := make([]byte, 0, restic.CiphertextLength(len(buf)))
 	ciphertext = append(ciphertext, nonce...)
 	ciphertext = newkey.user.Seal(ciphertext, nonce, buf, nil)
 	newkey.Data = ciphertext
diff --git a/internal/repository/master_index.go b/internal/repository/master_index.go
index adc110df8..9056528a2 100644
--- a/internal/repository/master_index.go
+++ b/internal/repository/master_index.go
@@ -6,7 +6,6 @@ import (
 	"sync"
 
 	"github.com/restic/restic/internal/debug"
-	"github.com/restic/restic/internal/pack"
 	"github.com/restic/restic/internal/restic"
 	"github.com/restic/restic/internal/ui/progress"
 	"golang.org/x/sync/errgroup"
@@ -131,27 +130,6 @@ func (mi *MasterIndex) Packs(packBlacklist restic.IDSet) restic.IDSet {
 	return packs
 }
 
-// PackSize returns the size of all packs computed by index information.
-// If onlyHdr is set to true, only the size of the header is returned
-// Note that this function only gives correct sizes, if there are no
-// duplicates in the index.
-func (mi *MasterIndex) PackSize(ctx context.Context, onlyHdr bool) map[restic.ID]int64 {
-	packSize := make(map[restic.ID]int64)
-
-	for blob := range mi.Each(ctx) {
-		size, ok := packSize[blob.PackID]
-		if !ok {
-			size = pack.HeaderSize
-		}
-		if !onlyHdr {
-			size += int64(blob.Length)
-		}
-		packSize[blob.PackID] = size + int64(pack.EntrySize)
-	}
-
-	return packSize
-}
-
 // Count returns the number of blobs of type t in the index.
 func (mi *MasterIndex) Count(t restic.BlobType) (n uint) {
 	mi.idxMutex.RLock()
diff --git a/internal/repository/repository.go b/internal/repository/repository.go
index 39953cd88..32583ad15 100644
--- a/internal/repository/repository.go
+++ b/internal/repository/repository.go
@@ -86,10 +86,10 @@ func (r *Repository) PrefixLength(ctx context.Context, t restic.FileType) (int,
 	return restic.PrefixLength(ctx, r.be, t)
 }
 
-// LoadAndDecrypt loads and decrypts the file with the given type and ID, using
+// LoadUnpacked loads and decrypts the file with the given type and ID, using
 // the supplied buffer (which must be empty). If the buffer is nil, a new
 // buffer will be allocated and returned.
-func (r *Repository) LoadAndDecrypt(ctx context.Context, buf []byte, t restic.FileType, id restic.ID) ([]byte, error) {
+func (r *Repository) LoadUnpacked(ctx context.Context, buf []byte, t restic.FileType, id restic.ID) ([]byte, error) {
 	if len(buf) != 0 {
 		panic("buf is not empty")
 	}
@@ -239,7 +239,7 @@ func (r *Repository) LoadBlob(ctx context.Context, t restic.BlobType, id restic.
 // LoadJSONUnpacked decrypts the data and afterwards calls json.Unmarshal on
 // the item.
 func (r *Repository) LoadJSONUnpacked(ctx context.Context, t restic.FileType, id restic.ID, item interface{}) (err error) {
-	buf, err := r.LoadAndDecrypt(ctx, nil, t, id)
+	buf, err := r.LoadUnpacked(ctx, nil, t, id)
 	if err != nil {
 		return err
 	}
@@ -252,10 +252,10 @@ func (r *Repository) LookupBlobSize(id restic.ID, tpe restic.BlobType) (uint, bo
 	return r.idx.LookupSize(restic.BlobHandle{ID: id, Type: tpe})
 }
 
-// SaveAndEncrypt encrypts data and stores it to the backend as type t. If data
+// saveAndEncrypt encrypts data and stores it to the backend as type t. If data
 // is small enough, it will be packed together with other small blobs.
 // The caller must ensure that the id matches the data.
-func (r *Repository) SaveAndEncrypt(ctx context.Context, t restic.BlobType, data []byte, id restic.ID) error {
+func (r *Repository) saveAndEncrypt(ctx context.Context, t restic.BlobType, data []byte, id restic.ID) error {
 	debug.Log("save id %v (%v, %d bytes)", id, t, len(data))
 
 	nonce := crypto.NewRandomNonce()
@@ -698,7 +698,7 @@ func (r *Repository) SaveBlob(ctx context.Context, t restic.BlobType, buf []byte
 
 	// only save when needed or explicitly told
 	if !known || storeDuplicate {
-		err = r.SaveAndEncrypt(ctx, t, buf, newID)
+		err = r.saveAndEncrypt(ctx, t, buf, newID)
 	}
 
 	return newID, known, err
diff --git a/internal/repository/repository_test.go b/internal/repository/repository_test.go
index 167ffa535..7cc593e04 100644
--- a/internal/repository/repository_test.go
+++ b/internal/repository/repository_test.go
@@ -218,7 +218,7 @@ func BenchmarkLoadBlob(b *testing.B) {
 	}
 }
 
-func BenchmarkLoadAndDecrypt(b *testing.B) {
+func BenchmarkLoadUnpacked(b *testing.B) {
 	repo, cleanup := repository.TestRepository(b)
 	defer cleanup()
 
@@ -237,7 +237,7 @@ func BenchmarkLoadAndDecrypt(b *testing.B) {
 	b.SetBytes(int64(length))
 
 	for i := 0; i < b.N; i++ {
-		data, err := repo.LoadAndDecrypt(context.TODO(), nil, restic.PackFile, storageID)
+		data, err := repo.LoadUnpacked(context.TODO(), nil, restic.PackFile, storageID)
 		rtest.OK(b, err)
 
 		// See comment in BenchmarkLoadBlob.
@@ -300,7 +300,7 @@ func TestRepositoryLoadIndex(t *testing.T) {
 
 // loadIndex loads the index id from backend and returns it.
 func loadIndex(ctx context.Context, repo restic.Repository, id restic.ID) (*repository.Index, error) {
-	buf, err := repo.LoadAndDecrypt(ctx, nil, restic.IndexFile, id)
+	buf, err := repo.LoadUnpacked(ctx, nil, restic.IndexFile, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/restic/config.go b/internal/restic/config.go
index 4f3c6c4bc..ded98ac1b 100644
--- a/internal/restic/config.go
+++ b/internal/restic/config.go
@@ -78,7 +78,7 @@ func LoadConfig(ctx context.Context, r JSONUnpackedLoader) (Config, error) {
 	}
 
 	if cfg.Version != RepoVersion {
-		return Config{}, errors.New("unsupported repository version")
+		return Config{}, errors.Errorf("unsupported repository version %v", cfg.Version)
 	}
 
 	if checkPolynomial {
diff --git a/internal/restic/repository.go b/internal/restic/repository.go
index c2a6e9f74..35f2f997b 100644
--- a/internal/restic/repository.go
+++ b/internal/restic/repository.go
@@ -43,10 +43,10 @@ type Repository interface {
 	SaveJSONUnpacked(context.Context, FileType, interface{}) (ID, error)
 
 	LoadJSONUnpacked(ctx context.Context, t FileType, id ID, dest interface{}) error
-	// LoadAndDecrypt loads and decrypts the file with the given type and ID,
+	// LoadUnpacked loads and decrypts the file with the given type and ID,
 	// using the supplied buffer (which must be empty). If the buffer is nil, a
 	// new buffer will be allocated and returned.
-	LoadAndDecrypt(ctx context.Context, buf []byte, t FileType, id ID) (data []byte, err error)
+	LoadUnpacked(ctx context.Context, buf []byte, t FileType, id ID) (data []byte, err error)
 
 	LoadBlob(context.Context, BlobType, ID, []byte) ([]byte, error)
 	SaveBlob(context.Context, BlobType, []byte, ID, bool) (ID, bool, error)
@@ -70,7 +70,6 @@ type MasterIndex interface {
 	Has(BlobHandle) bool
 	Lookup(BlobHandle) []PackedBlob
 	Count(BlobType) uint
-	PackSize(ctx context.Context, onlyHdr bool) map[ID]int64
 
 	// Each returns a channel that yields all blobs known to the index. When
 	// the context is cancelled, the background goroutine terminates. This
diff --git a/internal/restorer/filerestorer.go b/internal/restorer/filerestorer.go
index 84c7834df..206703ce3 100644
--- a/internal/restorer/filerestorer.go
+++ b/internal/restorer/filerestorer.go
@@ -195,7 +195,7 @@ func (r *fileRestorer) downloadPack(ctx context.Context, pack *packInfo) error {
 				if packID.Equal(pack.id) {
 					addBlob(blob, fileOffset)
 				}
-				fileOffset += int64(blob.Length) - crypto.Extension
+				fileOffset += int64(restic.PlaintextLength(int(blob.Length)))
 			})
 			if err != nil {
 				// restoreFiles should have caught this error before