Skip to content
This repository has been archived by the owner on Mar 9, 2019. It is now read-only.

Commit

Permalink
add additional meta page tests
Browse files Browse the repository at this point in the history
  • Loading branch information
benbjohnson committed Apr 24, 2016
1 parent 5e55b6c commit a5aec31
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 49 deletions.
27 changes: 13 additions & 14 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,10 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
// If we can't read the page size, we can assume it's the same
// as the OS -- since that's how the page size was chosen in the
// first place.
// XXX: Does this cause issues with opening a database on a
// different OS than the one it was created on?
//
// If the first page is invalid and this OS uses a different
// page size than what the database was created with then we
// are out of luck and cannot access the database.
db.pageSize = os.Getpagesize()
} else {
db.pageSize = int(m.pageSize)
Expand Down Expand Up @@ -286,7 +288,7 @@ func (db *DB) mmap(minsz int) error {
err0 := db.meta0.validate()
err1 := db.meta1.validate()
if err0 != nil && err1 != nil {
return fmt.Errorf("meta0(%v) meta1(%v)", err0, err1)
return err0
}

return nil
Expand Down Expand Up @@ -358,6 +360,7 @@ func (db *DB) init() error {
m.root = bucket{root: 3}
m.pgid = 4
m.txid = txid(i)
m.checksum = m.sum64()
}

// Write an empty freelist at page 3.
Expand Down Expand Up @@ -807,20 +810,16 @@ func (db *DB) meta() *meta {
metaB = db.meta0
}

errA := metaA.validate()
errB := metaB.validate()

if errA == nil {
// Use higher meta page if valid. Otherwise fallback to previous, if valid.
if err := metaA.validate(); err == nil {
return metaA
}

if errB == nil {
} else if err := metaB.validate(); err == nil {
return metaB
}

// This should never be reached, because both meta1 and meta0 were validated
// on mmap() and we do fsync() on every write.
panic("both meta0 and meta1 could not be validated in DB.meta()!")
panic("bolt.DB.meta(): invalid meta pages")
}

// allocate returns a contiguous block of memory starting at a given page.
Expand Down Expand Up @@ -981,12 +980,12 @@ type meta struct {

// validate checks the marker bytes and version of the meta page to ensure it matches this binary.
func (m *meta) validate() error {
if m.checksum != 0 && m.checksum != m.sum64() {
return ErrChecksum
} else if m.magic != magic {
if m.magic != magic {
return ErrInvalid
} else if m.version != version {
return ErrVersionMismatch
} else if m.checksum != 0 && m.checksum != m.sum64() {
return ErrChecksum
}
return nil
}
Expand Down
90 changes: 56 additions & 34 deletions db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type meta struct {
_ uint32
_ [16]byte
_ uint64
_ uint64
pgid uint64
_ uint64
checksum uint64
}
Expand Down Expand Up @@ -85,75 +85,97 @@ func TestOpen_ErrNotExists(t *testing.T) {
}
}

// Ensure that opening a file with wrong checksum returns ErrChecksum.
func TestOpen_ErrChecksum(t *testing.T) {
buf := make([]byte, pageSize)
meta := (*meta)(unsafe.Pointer(&buf[0]))
meta.magic = magic
meta.version = version
meta.checksum = 123

// Ensure that opening a file that is not a Bolt database returns ErrInvalid.
func TestOpen_ErrInvalid(t *testing.T) {
path := tempfile()

f, err := os.Create(path)
if err != nil {
t.Fatal(err)
}
if _, err := f.WriteAt(buf, pageHeaderSize); err != nil {
if _, err := fmt.Fprintln(f, "this is not a bolt database"); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
defer os.Remove(path)

if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrChecksum {
if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrInvalid {
t.Fatalf("unexpected error: %s", err)
}
}

// Ensure that opening a file that is not a Bolt database returns ErrInvalid.
func TestOpen_ErrInvalid(t *testing.T) {
path := tempfile()
// Ensure that opening a file with two invalid versions returns ErrVersionMismatch.
func TestOpen_ErrVersionMismatch(t *testing.T) {
if pageSize != os.Getpagesize() {
t.Skip("page size mismatch")
}

f, err := os.Create(path)
if err != nil {
// Create empty database.
db := MustOpenDB()
path := db.Path()
defer db.MustClose()

// Close database.
if err := db.DB.Close(); err != nil {
t.Fatal(err)
}
if _, err := fmt.Fprintln(f, "this is not a bolt database"); err != nil {

// Read data file.
buf, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {

// Rewrite meta pages.
meta0 := (*meta)(unsafe.Pointer(&buf[pageHeaderSize]))
meta0.version++
meta1 := (*meta)(unsafe.Pointer(&buf[pageSize+pageHeaderSize]))
meta1.version++
if err := ioutil.WriteFile(path, buf, 0666); err != nil {
t.Fatal(err)
}
defer os.Remove(path)

if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrInvalid {
// Reopen data file.
if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrVersionMismatch {
t.Fatalf("unexpected error: %s", err)
}
}

// Ensure that opening a file created with a different version of Bolt returns
// ErrVersionMismatch.
func TestOpen_ErrVersionMismatch(t *testing.T) {
buf := make([]byte, pageSize)
meta := (*meta)(unsafe.Pointer(&buf[0]))
meta.magic = magic
meta.version = version + 100
// Ensure that opening a file with two invalid checksums returns ErrChecksum.
func TestOpen_ErrChecksum(t *testing.T) {
if pageSize != os.Getpagesize() {
t.Skip("page size mismatch")
}

path := tempfile()
f, err := os.Create(path)
if err != nil {
// Create empty database.
db := MustOpenDB()
path := db.Path()
defer db.MustClose()

// Close database.
if err := db.DB.Close(); err != nil {
t.Fatal(err)
}
if _, err := f.WriteAt(buf, pageHeaderSize); err != nil {

// Read data file.
buf, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {

// Rewrite meta pages.
meta0 := (*meta)(unsafe.Pointer(&buf[pageHeaderSize]))
meta0.pgid++
meta1 := (*meta)(unsafe.Pointer(&buf[pageSize+pageHeaderSize]))
meta1.pgid++
if err := ioutil.WriteFile(path, buf, 0666); err != nil {
t.Fatal(err)
}
defer os.Remove(path)

if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrVersionMismatch {
// Reopen data file.
if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrChecksum {
t.Fatalf("unexpected error: %s", err)
}
}
Expand Down
3 changes: 2 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ var (
// already open.
ErrDatabaseOpen = errors.New("database already open")

// ErrInvalid is returned when a data file is not a Bolt-formatted database.
// ErrInvalid is returned when both meta pages on a database are invalid.
// This typically occurs when a file is not a bolt database.
ErrInvalid = errors.New("invalid database")

// ErrVersionMismatch is returned when the data file was created with a
Expand Down

0 comments on commit a5aec31

Please sign in to comment.