diff --git a/db.go b/db.go index 4ff07ae..d5ad715 100644 --- a/db.go +++ b/db.go @@ -177,7 +177,7 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) { // Initialize the database if it doesn't exist. if info, err := db.file.Stat(); err != nil { - return nil, fmt.Errorf("stat error: %s", err) + return nil, err } else if info.Size() == 0 { // Initialize new files with meta pages. if err := db.init(); err != nil { @@ -189,7 +189,7 @@ func Open(path string, mode os.FileMode, options *Options) (*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("meta0 error: %s", err) + return nil, err } db.pageSize = int(m.pageSize) } @@ -253,10 +253,10 @@ func (db *DB) mmap(minsz int) error { // Validate the meta pages. if err := db.meta0.validate(); err != nil { - return fmt.Errorf("meta0 error: %s", err) + return err } if err := db.meta1.validate(); err != nil { - return fmt.Errorf("meta1 error: %s", err) + return err } return nil diff --git a/db_test.go b/db_test.go index 5e325ef..aa8a2f2 100644 --- a/db_test.go +++ b/db_test.go @@ -7,29 +7,48 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "regexp" "runtime" "sort" "strings" "testing" "time" + "unsafe" "github.com/boltdb/bolt" ) var statsFlag = flag.Bool("stats", false, "show performance stats") -// Ensure that opening a database with a bad path returns an error. -func TestOpen_BadPath(t *testing.T) { - db, err := bolt.Open("", 0666, nil) - assert(t, err != nil, "err: %s", err) - assert(t, db == nil, "") +// version is the data file format version. +const version = 2 + +// magic is the marker value to indicate that a file is a Bolt DB. +const magic uint32 = 0xED0CDAED + +// pageSize is the size of one page in the data file. +const pageSize = 4096 + +// pageHeaderSize is the size of a page header. +const pageHeaderSize = 16 + +// meta represents a simplified version of a database meta page for testing. +type meta struct { + magic uint32 + version uint32 + _ uint32 + _ uint32 + _ [16]byte + _ uint64 + _ uint64 + _ uint64 + checksum uint64 } // Ensure that a database can be opened without error. func TestOpen(t *testing.T) { path := tempfile() - defer os.Remove(path) db, err := bolt.Open(path, 0666, nil) assert(t, db != nil, "") ok(t, err) @@ -37,6 +56,73 @@ func TestOpen(t *testing.T) { ok(t, db.Close()) } +// Ensure that opening a database with a bad path returns an error. +func TestOpen_BadPath(t *testing.T) { + for _, path := range []string{ + "", + filepath.Join(tempfile(), "youre-not-my-real-parent"), + } { + t.Logf("path = %q", path) + db, err := bolt.Open(path, 0666, nil) + assert(t, err != nil, "err: %s", err) + equals(t, path, err.(*os.PathError).Path) + equals(t, "open", err.(*os.PathError).Op) + equals(t, (*bolt.DB)(nil), db) + } +} + +// 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 + + path := tempfile() + f, err := os.Create(path) + equals(t, nil, err) + f.WriteAt(buf, pageHeaderSize) + f.Close() + defer os.Remove(path) + + _, err = bolt.Open(path, 0666, nil) + equals(t, bolt.ErrChecksum, err) +} + +// 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) + equals(t, nil, err) + fmt.Fprintln(f, "this is not a bolt database") + f.Close() + defer os.Remove(path) + + _, err = bolt.Open(path, 0666, nil) + equals(t, bolt.ErrInvalid, 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 + + path := tempfile() + f, err := os.Create(path) + equals(t, nil, err) + f.WriteAt(buf, pageHeaderSize) + f.Close() + defer os.Remove(path) + + _, err = bolt.Open(path, 0666, nil) + equals(t, bolt.ErrVersionMismatch, err) +} + // Ensure that opening an already open database file will timeout. func TestOpen_Timeout(t *testing.T) { if runtime.GOOS == "solaris" { @@ -44,7 +130,6 @@ func TestOpen_Timeout(t *testing.T) { } path := tempfile() - defer os.Remove(path) // Open a data file. db0, err := bolt.Open(path, 0666, nil) @@ -68,7 +153,6 @@ func TestOpen_Wait(t *testing.T) { } path := tempfile() - defer os.Remove(path) // Open a data file. db0, err := bolt.Open(path, 0666, nil) @@ -179,7 +263,6 @@ func TestOpen_Size_Large(t *testing.T) { // Ensure that a re-opened database is consistent. func TestOpen_Check(t *testing.T) { path := tempfile() - defer os.Remove(path) db, err := bolt.Open(path, 0666, nil) ok(t, err) @@ -192,26 +275,14 @@ func TestOpen_Check(t *testing.T) { db.Close() } -// Ensure that the database returns an error if the file handle cannot be open. -func TestDB_Open_FileError(t *testing.T) { - path := tempfile() - defer os.Remove(path) - - _, err := bolt.Open(path+"/youre-not-my-real-parent", 0666, nil) - assert(t, err.(*os.PathError) != nil, "") - equals(t, path+"/youre-not-my-real-parent", err.(*os.PathError).Path) - equals(t, "open", err.(*os.PathError).Op) -} - // Ensure that write errors to the meta file handler during initialization are returned. -func TestDB_Open_MetaInitWriteError(t *testing.T) { +func TestOpen_MetaInitWriteError(t *testing.T) { t.Skip("pending") } // Ensure that a database that is too small returns an error. -func TestDB_Open_FileTooSmall(t *testing.T) { +func TestOpen_FileTooSmall(t *testing.T) { path := tempfile() - defer os.Remove(path) db, err := bolt.Open(path, 0666, nil) ok(t, err) @@ -235,7 +306,6 @@ func TestOpen_ReadOnly(t *testing.T) { bucket, key, value := []byte(`bucket`), []byte(`key`), []byte(`value`) path := tempfile() - defer os.Remove(path) // Open in read-write mode. db, err := bolt.Open(path, 0666, nil)