genselect: verify any root's header, assert MaxLevel0TXID (Plan 7b review round 1)
Co-Authored-By: Codex <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+105
−0
internal/genselect/select.go+3 −0internal/genselect/select_test.go+102 −0
CI passed — run details
diff --git a/internal/genselect/select.go b/internal/genselect/select.go| index e956f8c67..0a9c21368 100644 |
| --- a/internal/genselect/select.go |
| +++ b/internal/genselect/select.go |
| @@ -389,6 +389,9 @@ func selectFiles(ctx context.Context, s Reader, bucket, base string, versions [] |
| break |
| } |
| if p.Root == nil { |
| + if err := checkRoot(ctx, s, bucket, *best, false); err != nil { |
| + return nil, err |
| + } |
| cp := *best |
| p.Root = &cp |
| } |
diff --git a/internal/genselect/select_test.go b/internal/genselect/select_test.go| index 3552a892c..82ea7a8c6 100644 |
| --- a/internal/genselect/select_test.go |
| +++ b/internal/genselect/select_test.go |
| @@ -301,6 +301,108 @@ func TestRefusals(t *testing.T) { |
| } |
| } |
| +func TestContinuationRootRefusals(t *testing.T) { |
| + for _, gen := range []string{"", "fresh"} { |
| + name := gen |
| + if name == "" { |
| + name = "origin" |
| + } |
| + for level := 0; level < 9; level++ { |
| + for _, corruption := range []string{"malformed_header", "truncated_file", "header_range_mismatch"} { |
| + t.Run(fmt.Sprintf("%s/level_%d/%s", name, level, corruption), func(t *testing.T) { |
| + f := newFixture(t) |
| + if gen == "" { |
| + f.origin() |
| + } else { |
| + f.marker.Op = gen |
| + f.marker.Pin = Pin{} |
| + f.req.Holder.Generation = gen |
| + f.publish() |
| + } |
| + root := f.file(gen, level, 1, 1) |
| + body := header(t, 1, 1) |
| + switch corruption { |
| + case "malformed_header": |
| + body[0] ^= 0xff |
| + case "truncated_file": |
| + body = body[:len(body)-1] |
| + case "header_range_mismatch": |
| + body = header(t, 1, 2) |
| + } |
| + f.b.Put("data", root.Key, body) |
| + before := f.b.Snapshot() |
| + got, err := Select(t.Context(), f.store, f.req) |
| + if got != nil || !errors.Is(err, ErrRoot) { |
| + t.Fatalf("got %+v / %v, want nil / %v", got, err, ErrRoot) |
| + } |
| + if f.b.Snapshot() != before { |
| + t.Fatal("selection mutated storage") |
| + } |
| + }) |
| + } |
| + } |
| + } |
| +} |
| + |
| +func TestMaxLevel0TXID(t *testing.T) { |
| + tests := []struct { |
| + name string |
| + setup func(*fixture) |
| + wantLevel0 uint64 |
| + wantRestored uint64 |
| + }{ |
| + {"current_anchor", func(f *fixture) {}, 11, 11}, |
| + {"current_continuation", func(f *fixture) { |
| + f.file("g0", 0, 12, 12) |
| + f.file("g0", 0, 13, 13) |
| + }, 13, 13}, |
| + {"noncurrent_data", func(f *fixture) { |
| + old := f.file("g0", 0, 30, 30) |
| + f.b.BareDelete("data", old.Key) |
| + }, 11, 11}, |
| + {"delete_marker_only", func(f *fixture) { |
| + old := f.file("g0", 0, 40, 40) |
| + f.b.BareDelete("data", old.Key) |
| + if err := f.b.DeleteVersion(f.t.Context(), "data", old.Key, old.VersionID); err != nil { |
| + f.t.Fatal(err) |
| + } |
| + }, 11, 11}, |
| + {"newer_restored_snapshot", func(f *fixture) { |
| + f.file("g0", 9, 1, 30) |
| + }, 11, 30}, |
| + {"no_current_level0", func(f *fixture) { |
| + anchor := f.file("g0", 0, 11, 11) |
| + f.b.BareDelete("data", anchor.Key) |
| + }, 0, 11}, |
| + } |
| + for _, entry := range []string{"ordinary", "prepared"} { |
| + for _, tt := range tests { |
| + t.Run(entry+"/"+tt.name, func(t *testing.T) { |
| + f := newFixture(t) |
| + tt.setup(f) |
| + var got *Plan |
| + var err error |
| + if entry == "ordinary" { |
| + got, err = Select(t.Context(), f.store, f.req) |
| + } else { |
| + p := f.marker.Pin |
| + got, err = SelectPrepared(t.Context(), f.store, f.req, PreparedPin{ |
| + Generation: "g0", SnapshotKey: p.SnapshotKey, SnapshotVersion: p.SnapshotVersion, |
| + SnapshotTXID: p.SnapshotTXID, SnapshotCRC64: p.SnapshotCRC64, |
| + }) |
| + } |
| + if err != nil || got == nil { |
| + t.Fatalf("got %+v / %v, want a plan", got, err) |
| + } |
| + if got.MaxLevel0TXID != tt.wantLevel0 || got.MaxTXID != tt.wantRestored { |
| + t.Fatalf("MaxLevel0TXID=%d, MaxTXID=%d; want %d, %d", |
| + got.MaxLevel0TXID, got.MaxTXID, tt.wantLevel0, tt.wantRestored) |
| + } |
| + }) |
| + } |
| + } |
| +} |
| + |
| func alterVersion(f *fixture, id string) { |
| f.file("g0", 0, 12, 12) |
| f.store = altered{Reader: f.b, list: func(vs []lease.ObjectVersion) ([]lease.ObjectVersion, error) { |