Merge pull request #113 from benbjohnson/meta-checksum

Add meta page checksums.
pull/34/head
Ben Johnson 2014-04-02 17:02:41 -06:00
commit 71fa5d8610
3 changed files with 55 additions and 10 deletions

2
db.go
View File

@ -105,7 +105,7 @@ func Open(path string, mode os.FileMode) (*DB, error) {
if _, err := db.file.ReadAt(buf[:], 0); err == nil {
m := db.pageInBuffer(buf[:], 0).meta()
if err := m.validate(); err != nil {
return nil, fmt.Errorf("meta error: %s", err)
return nil, fmt.Errorf("meta0 error: %s", err)
}
db.pageSize = int(m.pageSize)
}

View File

@ -117,10 +117,43 @@ func TestDBCorruptMeta0(t *testing.T) {
// Open the database.
_, err = Open(path, 0666)
assert.Equal(t, err, errors.New("meta error: invalid database"))
assert.Equal(t, err, errors.New("meta0 error: invalid database"))
})
}
// Ensure that a corrupt meta page checksum causes the open to fail.
func TestDBMetaChecksumError(t *testing.T) {
for i := 0; i < 2; i++ {
withTempPath(func(path string) {
db, err := Open(path, 0600)
pageSize := db.pageSize
db.Update(func(tx *Tx) error {
return tx.CreateBucket("widgets")
})
db.Update(func(tx *Tx) error {
return tx.CreateBucket("woojits")
})
db.Close()
// Change a single byte in the meta page.
f, _ := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
f.WriteAt([]byte{1}, int64((i*pageSize)+(pageHeaderSize+12)))
f.Sync()
f.Close()
// Reopen the database.
_, err = Open(path, 0600)
if assert.Error(t, err) {
if i == 0 {
assert.Equal(t, "meta0 error: checksum error", err.Error())
} else {
assert.Equal(t, "meta1 error: checksum error", err.Error())
}
}
})
}
}
// Ensure that a database cannot open a transaction when it's not open.
func TestDBTxErrDatabaseNotOpen(t *testing.T) {
var db DB

28
meta.go
View File

@ -2,6 +2,8 @@ package bolt
import (
"errors"
"hash/fnv"
"unsafe"
)
const magic uint32 = 0xED0CDAED
@ -13,6 +15,9 @@ var (
// ErrVersionMismatch is returned when the data file was created with a
// different version of Bolt.
ErrVersionMismatch = errors.New("version mismatch")
// ErrChecksum is returned when either meta page checksum does not match.
ErrChecksum = errors.New("checksum error")
)
type meta struct {
@ -24,11 +29,14 @@ type meta struct {
freelist pgid
pgid pgid
txid txid
checksum uint64
}
// validate checks the marker bytes and version of the meta page to ensure it matches this binary.
func (m *meta) validate() error {
if m.magic != magic {
if m.checksum != 0 && m.checksum != m.sum64() {
return ErrChecksum
} else if m.magic != magic {
return ErrInvalid
} else if m.version != version {
return ErrVersionMismatch
@ -38,13 +46,7 @@ func (m *meta) validate() error {
// copy copies one meta object to another.
func (m *meta) copy(dest *meta) {
dest.magic = m.magic
dest.version = m.version
dest.pageSize = m.pageSize
dest.buckets = m.buckets
dest.freelist = m.freelist
dest.pgid = m.pgid
dest.txid = m.txid
*dest = *m
}
// write writes the meta onto a page.
@ -53,5 +55,15 @@ func (m *meta) write(p *page) {
p.id = pgid(m.txid % 2)
p.flags |= metaPageFlag
// Calculate the checksum.
m.checksum = m.sum64()
m.copy(p.meta())
}
// generates the checksum for the meta.
func (m *meta) sum64() uint64 {
var h = fnv.New64a()
_, _ = h.Write((*[unsafe.Offsetof(meta{}.checksum)]byte)(unsafe.Pointer(m))[:])
return h.Sum64()
}