mirror of https://github.com/etcd-io/bbolt.git
commit
71fa5d8610
2
db.go
2
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)
|
||||
}
|
||||
|
|
35
db_test.go
35
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
|
||||
|
|
28
meta.go
28
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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue