Skip to content

Commit b7cd5e6

Browse files
committed
add test check page
Signed-off-by: Mustafa Elbehery <melbeher@redhat.com>
1 parent 8ede234 commit b7cd5e6

File tree

2 files changed

+127
-3
lines changed

2 files changed

+127
-3
lines changed

db_whitebox_test.go

+127-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package bbolt
22

33
import (
4+
"bytes"
5+
"encoding/binary"
6+
"fmt"
47
"path/filepath"
58
"testing"
69

10+
"go.etcd.io/bbolt/errors"
11+
12+
"go.etcd.io/bbolt/internal/common"
13+
"go.etcd.io/bbolt/internal/guts_cli"
14+
715
"github.com/stretchr/testify/assert"
816
"github.com/stretchr/testify/require"
9-
10-
"go.etcd.io/bbolt/errors"
1117
)
1218

1319
func TestOpenWithPreLoadFreelist(t *testing.T) {
@@ -112,6 +118,125 @@ func TestMethodPage(t *testing.T) {
112118
}
113119
}
114120

121+
func TestTx_Check_CorruptedLeafPage(t *testing.T) {
122+
fileName, err := prepareData(t)
123+
require.NoError(t, err)
124+
db, err := Open(fileName, 0666, DefaultOptions)
125+
require.NoError(t, err)
126+
defer func() {
127+
require.NoError(t, db.Close())
128+
}()
129+
bucketKey := "testBucket"
130+
131+
// arrange
132+
if err := db.Update(func(tx *Tx) error {
133+
b, err := tx.CreateBucketIfNotExists([]byte(bucketKey))
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
// insert random data
138+
generateSampleDataInBucket(t, b, 3)
139+
return nil
140+
}); err != nil {
141+
t.Fatal(err)
142+
}
143+
144+
// act : corrupt a leaf page
145+
victimPageId, _ := corruptLeafPage(t, db)
146+
147+
// assert: expected an error after check
148+
if err := db.View(func(tx *Tx) error {
149+
for err := range tx.Check() {
150+
require.ErrorContains(t, err, fmt.Sprintf("leaf page(%d)", victimPageId))
151+
}
152+
return nil
153+
}); err != nil {
154+
t.Fatal(err)
155+
}
156+
}
157+
158+
// corruptLeafPage write an invalid leafPageElement into the victim page.
159+
func corruptLeafPage(t testing.TB, db *DB) (victimPageId common.Pgid, validPageIds []common.Pgid) {
160+
t.Helper()
161+
162+
victimPageId, validPageIds = findVictimPageId(t, db)
163+
164+
victimPage, victimBuf, err := guts_cli.ReadPage(db.Path(), uint64(victimPageId))
165+
require.NoError(t, err)
166+
require.NotNil(t, victimPage)
167+
require.NotNil(t, victimBuf)
168+
require.True(t, victimPage.IsLeafPage())
169+
require.True(t, victimPage.Count() > 0)
170+
171+
// Insert LeafPageElement which violates the BTree range.
172+
invalidLPE := common.NewLeafPageElement(0, 0, 0, 0)
173+
var buf bytes.Buffer
174+
binary.Write(&buf, binary.BigEndian, invalidLPE)
175+
copy(victimBuf[16:], buf.Bytes())
176+
// Write the corrupt page to db file.
177+
err = guts_cli.WritePage(db.Path(), victimBuf)
178+
require.NoError(t, err)
179+
180+
return victimPageId, validPageIds
181+
}
182+
183+
// findVictimPageId finds all the leaf pages of a bucket and picks a leaf page to be corrupted.
184+
func findVictimPageId(t testing.TB, db *DB) (victimPageId common.Pgid, validPageIds []common.Pgid) {
185+
t.Helper()
186+
// Read DB's RootPage.
187+
rootPageId, _, err := guts_cli.GetRootPage(db.Path())
188+
require.NoError(t, err)
189+
rootPage, _, err := guts_cli.ReadPage(db.Path(), uint64(rootPageId))
190+
require.NoError(t, err)
191+
require.True(t, rootPage.IsLeafPage())
192+
require.Equal(t, 1, len(rootPage.LeafPageElements()))
193+
// Find Bucket's RootPage.
194+
lpe := rootPage.LeafPageElement(uint16(0))
195+
require.Equal(t, uint32(common.BranchPageFlag), lpe.Flags())
196+
k := lpe.Key()
197+
require.Equal(t, "testBucket", string(k))
198+
bucketRootPageId := lpe.Bucket().RootPage()
199+
// Read Bucket's RootPage.
200+
bucketRootPage, _, err := guts_cli.ReadPage(db.Path(), uint64(bucketRootPageId))
201+
require.NoError(t, err)
202+
require.Equal(t, uint16(common.BranchPageFlag), bucketRootPage.Flags())
203+
// Retrieve Bucket's PageIds
204+
var bucketPageIds []common.Pgid
205+
for _, bpe := range bucketRootPage.BranchPageElements() {
206+
bucketPageIds = append(bucketPageIds, bpe.Pgid())
207+
}
208+
209+
return bucketPageIds[0], bucketPageIds[1:]
210+
}
211+
212+
// generateSampleDataInBucket fill in sample data into given bucket to create the given
213+
// number of leafPages. To control the number of leafPages, sample data are generated in order.
214+
func generateSampleDataInBucket(t testing.TB, bk *Bucket, lPages int) {
215+
t.Helper()
216+
currentKey := 1
217+
currentVal := 100
218+
for i := 0; i < lPages; i++ {
219+
currentSize := common.PageHeaderSize
220+
for {
221+
err := bk.Put(convertIntIntoBytes(t, currentKey), convertIntIntoBytes(t, currentVal))
222+
require.NoError(t, err)
223+
currentSize += 16 + 4 + 4
224+
if currentSize >= 4096 {
225+
break
226+
}
227+
currentKey++
228+
currentVal++
229+
}
230+
}
231+
}
232+
233+
func convertIntIntoBytes(t testing.TB, i int) []byte {
234+
t.Helper()
235+
buf := make([]byte, 4)
236+
binary.BigEndian.PutUint32(buf, uint32(i))
237+
return buf
238+
}
239+
115240
func prepareData(t *testing.T) (string, error) {
116241
fileName := filepath.Join(t.TempDir(), "db")
117242
db, err := Open(fileName, 0666, nil)

tx_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212

1313
"github.com/stretchr/testify/assert"
1414
"github.com/stretchr/testify/require"
15-
1615
bolt "go.etcd.io/bbolt"
1716
berrors "go.etcd.io/bbolt/errors"
1817
"go.etcd.io/bbolt/internal/btesting"

0 commit comments

Comments
 (0)