aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-02-21 19:28:39 +0800
committerGravatar Runxi Yu2026-02-21 19:28:39 +0800
commitcc1af64ae2908cdbc4c65a54950ec6dfc6293455 (patch)
tree4ff06b616788f610e1dab1d29ca48e63894435a2
parentformat/pack/checksum: Move things about pack trailers here (diff)
signatureNo signature
objectstore/packed: Verify that the index matches the pack
-rw-r--r--objectstore/packed/store.go33
1 files changed, 33 insertions, 0 deletions
diff --git a/objectstore/packed/store.go b/objectstore/packed/store.go
index ace11782..4b2b011e 100644
--- a/objectstore/packed/store.go
+++ b/objectstore/packed/store.go
@@ -3,9 +3,11 @@ package packed
import (
"errors"
+ "fmt"
"os"
"sync"
+ packchecksum "codeberg.org/lindenii/furgit/format/pack/checksum"
"codeberg.org/lindenii/furgit/objectid"
"codeberg.org/lindenii/furgit/objectstore"
)
@@ -27,6 +29,8 @@ type Store struct {
indexesLoaded bool
// indexes stores parsed .idx handles.
indexes []*idxFile
+ // indexByPack maps one pack basename to its parsed index.
+ indexByPack map[string]*idxFile
// stateMu guards index publication, pack cache, and close state.
stateMu sync.RWMutex
@@ -98,8 +102,13 @@ func (store *Store) Close() error {
func (store *Store) ensureIndexes() error {
store.loadOnce.Do(func() {
indexes, err := store.loadIndexes()
+ indexByPack := make(map[string]*idxFile, len(indexes))
+ for _, index := range indexes {
+ indexByPack[index.packName] = index
+ }
store.stateMu.Lock()
store.indexes = indexes
+ store.indexByPack = indexByPack
store.loadErr = err
store.indexesLoaded = true
store.stateMu.Unlock()
@@ -157,6 +166,10 @@ func (store *Store) openPack(name string) (*packFile, error) {
_ = file.Close()
return nil, err
}
+ if err := store.verifyPackMatchesIndexes(pack); err != nil {
+ _ = pack.close()
+ return nil, err
+ }
store.stateMu.Lock()
if existing, ok := store.packs[name]; ok {
@@ -169,6 +182,26 @@ func (store *Store) openPack(name string) (*packFile, error) {
return pack, nil
}
+// verifyPackMatchesIndexes checks that one opened pack's trailer hash matches
+// every loaded index that references the same pack name.
+func (store *Store) verifyPackMatchesIndexes(pack *packFile) error {
+ store.stateMu.RLock()
+ index := store.indexByPack[pack.name]
+ indexesLoaded := store.indexesLoaded
+ store.stateMu.RUnlock()
+
+ if !indexesLoaded {
+ return nil
+ }
+ if index == nil {
+ return nil
+ }
+ if err := packchecksum.VerifyPackMatchesIdx(pack.data, index.data, store.algo); err != nil {
+ return fmt.Errorf("objectstore/packed: pack %q does not match idx %q: %w", pack.name, index.idxName, err)
+ }
+ return nil
+}
+
// entryMetaAt parses one pack entry header at location.
func (store *Store) entryMetaAt(loc location) (*packFile, entryMeta, error) {
pack, err := store.openPack(loc.packName)