aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--db.go2
-rw-r--r--db_test.go35
-rw-r--r--meta.go28
3 files changed, 55 insertions, 10 deletions
diff --git a/db.go b/db.go
index c43eb5c..ee3a24b 100644
--- a/db.go
+++ b/db.go
@@ -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)
}
diff --git a/db_test.go b/db_test.go
index df1aad3..e5b8c1a 100644
--- a/db_test.go
+++ b/db_test.go
@@ -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
diff --git a/meta.go b/meta.go
index cc62637..abb2a93 100644
--- a/meta.go
+++ b/meta.go
@@ -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()
+}