|
|
|
@ -1,12 +1,9 @@ |
|
|
|
|
package repository |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"crypto/rand" |
|
|
|
|
"encoding/binary" |
|
|
|
|
"hash/maphash" |
|
|
|
|
|
|
|
|
|
"github.com/restic/restic/internal/restic" |
|
|
|
|
|
|
|
|
|
"github.com/dchest/siphash" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// An indexMap is a chained hash table that maps blob IDs to indexEntries.
|
|
|
|
@ -23,7 +20,7 @@ type indexMap struct { |
|
|
|
|
buckets []*indexEntry |
|
|
|
|
numentries uint |
|
|
|
|
|
|
|
|
|
key0, key1 uint64 // Key for hash randomization.
|
|
|
|
|
mh maphash.Hash |
|
|
|
|
|
|
|
|
|
free *indexEntry // Free list.
|
|
|
|
|
} |
|
|
|
@ -113,25 +110,20 @@ func (m *indexMap) grow() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m *indexMap) hash(id restic.ID) uint { |
|
|
|
|
// We use siphash with a randomly generated 128-bit key, to prevent
|
|
|
|
|
// backups of specially crafted inputs from degrading performance.
|
|
|
|
|
// We use maphash to prevent backups of specially crafted inputs
|
|
|
|
|
// from degrading performance.
|
|
|
|
|
// While SHA-256 should be collision-resistant, for hash table indices
|
|
|
|
|
// we use only a few bits of it and finding collisions for those is
|
|
|
|
|
// much easier than breaking the whole algorithm.
|
|
|
|
|
h := uint(siphash.Hash(m.key0, m.key1, id[:])) |
|
|
|
|
m.mh.Reset() |
|
|
|
|
_, _ = m.mh.Write(id[:]) |
|
|
|
|
h := uint(m.mh.Sum64()) |
|
|
|
|
return h & uint(len(m.buckets)-1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m *indexMap) init() { |
|
|
|
|
const initialBuckets = 64 |
|
|
|
|
m.buckets = make([]*indexEntry, initialBuckets) |
|
|
|
|
|
|
|
|
|
var buf [16]byte |
|
|
|
|
if _, err := rand.Read(buf[:]); err != nil { |
|
|
|
|
panic(err) // Very little we can do here.
|
|
|
|
|
} |
|
|
|
|
m.key0 = binary.LittleEndian.Uint64(buf[:8]) |
|
|
|
|
m.key1 = binary.LittleEndian.Uint64(buf[8:]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m *indexMap) len() uint { return m.numentries } |
|
|
|
|