Skip to content

Commit 468915f

Browse files
committed
Fix db recover when repo.RefList is nil
It tries to find dangling references but repo has no reference list and it ended up with a nice trace ``` root@hostname:/usr/scratch/agustin/aptly-stress-tester# aptly db recover Recovering database... Checking database integrity... panic: runtime error: invalid memory address or nil pointer dereference [recovered] panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x13b9c24] goroutine 1 [running]: github.com/aptly-dev/aptly/cmd.Run.func1() /home/tin/projects/aptly/cmd/run.go:17 +0xc5 panic({0x15abf60?, 0x2da1650?}) /usr/local/go/src/runtime/panic.go:792 +0x132 github.com/aptly-dev/aptly/deb.(*PackageRefList).ForEach(...) /home/tin/projects/aptly/deb/reflist.go:83 github.com/aptly-dev/aptly/deb.FindDanglingReferences(...) /home/tin/projects/aptly/deb/find_dangling.go:15 github.com/aptly-dev/aptly/cmd.checkRepo(0xc0015827e0) /home/tin/projects/aptly/cmd/db_recover.go:63 +0xa4 github.com/aptly-dev/aptly/cmd.checkIntegrity.(*LocalRepoCollection).ForEach.func1({0xc0002fc2d0?, 0xc0010c2018?, 0x1?}, {0xc11004c000, 0xb9, 0xe0}) /home/tin/projects/aptly/deb/local.go:229 +0xa7 github.com/aptly-dev/aptly/database/goleveldb.(*storage).ProcessByPrefix(0xc0005ae140?, {0xc0010c2018, 0x1, 0x1}, 0xc00107be40) /home/tin/projects/aptly/database/goleveldb/storage.go:114 +0x184 github.com/aptly-dev/aptly/deb.(*LocalRepoCollection).ForEach(...) /home/tin/projects/aptly/deb/local.go:222 github.com/aptly-dev/aptly/cmd.checkIntegrity() /home/tin/projects/aptly/cmd/db_recover.go:51 +0x7f github.com/aptly-dev/aptly/cmd.aptlyDBRecover(0x40ed4e?, {0xc0003b5c30?, 0x2?, 0xc00057bdb0?}) /home/tin/projects/aptly/cmd/db_recover.go:27 +0x93 github.com/smira/commander.(*Command).Dispatch(0xc00052c900, {0xc0003b5c30, 0x0, 0x0}) /go/pkg/mod/github.com/smira/commander@v0.0.0-20140515201010-f408b00e68d5/commands.go:305 +0xd1 github.com/smira/commander.(*Command).Dispatch(0xc00052ca20, {0xc0003b5c30, 0x1, 0x1}) /go/pkg/mod/github.com/smira/commander@v0.0.0-20140515201010-f408b00e68d5/commands.go:283 +0x165 github.com/smira/commander.(*Command).Dispatch(0xc000516000, {0xc0003b5c20, 0x2, 0x2}) /go/pkg/mod/github.com/smira/commander@v0.0.0-20140515201010-f408b00e68d5/commands.go:283 +0x165 github.com/aptly-dev/aptly/cmd.Run(0xc000516000, {0xc000194280?, 0x0?, 0x24a5bc8?}, 0x1) /home/tin/projects/aptly/cmd/run.go:41 +0x1b9 main.main() /home/tin/projects/aptly/main.go:27 +0x10d ``` After applying the fix you get ``` root@hostname:~/aptly-stress-tester# aptly db recover Recovering database... Checking database integrity... Warning: Repo "stress_test_repo_3532" has no reference list (severely corrupted), initializing empty list Warning: Repo "stress_test_repo_9244" has no reference list (severely corrupted), initializing empty list ``` Also it tries better to avoid nil references in repos, and snapshots
1 parent ba65daf commit 468915f

File tree

4 files changed

+39
-7
lines changed

4 files changed

+39
-7
lines changed

cmd/db_recover.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,37 @@ func checkRepo(repo *deb.LocalRepo) error {
5757

5858
err := repos.LoadComplete(repo)
5959
if err != nil {
60-
return fmt.Errorf("load complete repo %q: %s", repo.Name, err)
60+
// If we can't load the repo, it might be severely corrupted
61+
// Log the error but continue with other repos
62+
context.Progress().Printf("Warning: Cannot load repo %q: %s\n", repo.Name, err)
63+
return nil
6164
}
6265

63-
dangling, err := deb.FindDanglingReferences(repo.RefList(), collectionFactory.PackageCollection())
66+
// Check if RefList is nil (severe corruption case)
67+
refList := repo.RefList()
68+
if refList == nil {
69+
context.Progress().Printf("Warning: Repo %q has no reference list (severely corrupted), initializing empty list\n", repo.Name)
70+
// Initialize with empty reflist
71+
repo.UpdateRefList(deb.NewPackageRefList())
72+
if err = repos.Update(repo); err != nil {
73+
return fmt.Errorf("update repo with empty reflist: %w", err)
74+
}
75+
return nil
76+
}
77+
78+
dangling, err := deb.FindDanglingReferences(refList, collectionFactory.PackageCollection())
6479
if err != nil {
65-
return fmt.Errorf("find dangling references: %w", err)
80+
// If we can't find dangling references, log but continue
81+
context.Progress().Printf("Warning: Cannot check dangling references for repo %q: %s\n", repo.Name, err)
82+
return nil
6683
}
6784

68-
if len(dangling.Refs) > 0 {
85+
if dangling != nil && len(dangling.Refs) > 0 {
6986
for _, ref := range dangling.Refs {
7087
context.Progress().Printf("Removing dangling database reference %q\n", ref)
7188
}
7289

73-
repo.UpdateRefList(repo.RefList().Subtract(dangling))
90+
repo.UpdateRefList(refList.Subtract(dangling))
7491

7592
if err = repos.Update(repo); err != nil {
7693
return fmt.Errorf("update repo: %w", err)

deb/local.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,15 @@ func (collection *LocalRepoCollection) Add(repo *LocalRepo) error {
160160
func (collection *LocalRepoCollection) Update(repo *LocalRepo) error {
161161
batch := collection.db.CreateBatch()
162162
_ = batch.Put(repo.Key(), repo.Encode())
163+
163164
if repo.packageRefs != nil {
164165
_ = batch.Put(repo.RefKey(), repo.packageRefs.Encode())
166+
} else {
167+
// Delete RefKey if packageRefs is nil
168+
// This prevents inconsistent state where RefKey exists but is corrupted
169+
_ = batch.Delete(repo.RefKey())
165170
}
171+
166172
return batch.Write()
167173
}
168174

deb/remote.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,11 +847,16 @@ func (collection *RemoteRepoCollection) Add(repo *RemoteRepo) error {
847847
// Update stores updated information about repo in DB
848848
func (collection *RemoteRepoCollection) Update(repo *RemoteRepo) error {
849849
batch := collection.db.CreateBatch()
850-
851850
_ = batch.Put(repo.Key(), repo.Encode())
851+
852852
if repo.packageRefs != nil {
853853
_ = batch.Put(repo.RefKey(), repo.packageRefs.Encode())
854+
} else {
855+
// Delete RefKey if packageRefs is nil
856+
// This prevents inconsistent state where RefKey exists but is corrupted
857+
_ = batch.Delete(repo.RefKey())
854858
}
859+
855860
return batch.Write()
856861
}
857862

deb/snapshot.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,14 @@ func (collection *SnapshotCollection) Add(snapshot *Snapshot) error {
227227
// Update stores updated information about snapshot in DB
228228
func (collection *SnapshotCollection) Update(snapshot *Snapshot) error {
229229
batch := collection.db.CreateBatch()
230-
231230
_ = batch.Put(snapshot.Key(), snapshot.Encode())
231+
232232
if snapshot.packageRefs != nil {
233233
_ = batch.Put(snapshot.RefKey(), snapshot.packageRefs.Encode())
234+
} else {
235+
// Delete RefKey if packageRefs is nil
236+
// This prevents inconsistent state where RefKey exists but is corrupted
237+
_ = batch.Delete(snapshot.RefKey())
234238
}
235239

236240
return batch.Write()

0 commit comments

Comments
 (0)