mirror of https://github.com/etcd-io/bbolt.git
add protection when mmap somehow fails
Signed-off-by: Benjamin Wang <wachao@vmware.com>pull/362/head
parent
63d0cb428d
commit
eabffad75a
27
db.go
27
db.go
|
@ -491,8 +491,19 @@ func (db *DB) mmap(minsz int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) invalidate() {
|
||||||
|
db.dataref = nil
|
||||||
|
db.data = nil
|
||||||
|
db.datasz = 0
|
||||||
|
|
||||||
|
db.meta0 = nil
|
||||||
|
db.meta1 = nil
|
||||||
|
}
|
||||||
|
|
||||||
// munmap unmaps the data file from memory.
|
// munmap unmaps the data file from memory.
|
||||||
func (db *DB) munmap() error {
|
func (db *DB) munmap() error {
|
||||||
|
defer db.invalidate()
|
||||||
|
|
||||||
if err := munmap(db); err != nil {
|
if err := munmap(db); err != nil {
|
||||||
return fmt.Errorf("unmap error: " + err.Error())
|
return fmt.Errorf("unmap error: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -701,6 +712,13 @@ func (db *DB) beginTx() (*Tx, error) {
|
||||||
return nil, ErrDatabaseNotOpen
|
return nil, ErrDatabaseNotOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit if the database is not correctly mapped.
|
||||||
|
if db.data == nil {
|
||||||
|
db.mmaplock.RUnlock()
|
||||||
|
db.metalock.Unlock()
|
||||||
|
return nil, ErrInvalidMapping
|
||||||
|
}
|
||||||
|
|
||||||
// Create a transaction associated with the database.
|
// Create a transaction associated with the database.
|
||||||
t := &Tx{}
|
t := &Tx{}
|
||||||
t.init(db)
|
t.init(db)
|
||||||
|
@ -742,6 +760,12 @@ func (db *DB) beginRWTx() (*Tx, error) {
|
||||||
return nil, ErrDatabaseNotOpen
|
return nil, ErrDatabaseNotOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit if the database is not correctly mapped.
|
||||||
|
if db.data == nil {
|
||||||
|
db.rwlock.Unlock()
|
||||||
|
return nil, ErrInvalidMapping
|
||||||
|
}
|
||||||
|
|
||||||
// Create a transaction associated with the database.
|
// Create a transaction associated with the database.
|
||||||
t := &Tx{writable: true}
|
t := &Tx{writable: true}
|
||||||
t.init(db)
|
t.init(db)
|
||||||
|
@ -1016,6 +1040,7 @@ func (db *DB) Stats() Stats {
|
||||||
// This is for internal access to the raw data bytes from the C cursor, use
|
// This is for internal access to the raw data bytes from the C cursor, use
|
||||||
// carefully, or not at all.
|
// carefully, or not at all.
|
||||||
func (db *DB) Info() *Info {
|
func (db *DB) Info() *Info {
|
||||||
|
_assert(db.data != nil, "database file isn't correctly mapped")
|
||||||
return &Info{uintptr(unsafe.Pointer(&db.data[0])), db.pageSize}
|
return &Info{uintptr(unsafe.Pointer(&db.data[0])), db.pageSize}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1042,7 +1067,7 @@ func (db *DB) meta() *meta {
|
||||||
metaB = db.meta0
|
metaB = db.meta0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use higher meta page if valid. Otherwise fallback to previous, if valid.
|
// Use higher meta page if valid. Otherwise, fallback to previous, if valid.
|
||||||
if err := metaA.validate(); err == nil {
|
if err := metaA.validate(); err == nil {
|
||||||
return metaA
|
return metaA
|
||||||
} else if err := metaB.validate(); err == nil {
|
} else if err := metaB.validate(); err == nil {
|
||||||
|
|
|
@ -1331,9 +1331,8 @@ func TestDBUnmap(t *testing.T) {
|
||||||
assert.True(t, data.IsNil())
|
assert.True(t, data.IsNil())
|
||||||
assert.True(t, datasz.IsZero())
|
assert.True(t, datasz.IsZero())
|
||||||
|
|
||||||
// We need to reopen the db, otherwise MustCheck may panic.
|
// Set db.DB to nil to prevent MustCheck from panicking.
|
||||||
db.DB = nil
|
db.DB = nil
|
||||||
db.MustReopen()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDB_Update() {
|
func ExampleDB_Update() {
|
||||||
|
|
|
@ -16,6 +16,9 @@ var (
|
||||||
// This typically occurs when a file is not a bolt database.
|
// This typically occurs when a file is not a bolt database.
|
||||||
ErrInvalid = errors.New("invalid database")
|
ErrInvalid = errors.New("invalid database")
|
||||||
|
|
||||||
|
// ErrInvalidMapping is returned when the database file fails to get mapped.
|
||||||
|
ErrInvalidMapping = errors.New("database isn't correctly mapped")
|
||||||
|
|
||||||
// ErrVersionMismatch is returned when the data file was created with a
|
// ErrVersionMismatch is returned when the data file was created with a
|
||||||
// different version of Bolt.
|
// different version of Bolt.
|
||||||
ErrVersionMismatch = errors.New("version mismatch")
|
ErrVersionMismatch = errors.New("version mismatch")
|
||||||
|
|
18
tx.go
18
tx.go
|
@ -276,13 +276,17 @@ func (tx *Tx) rollback() {
|
||||||
}
|
}
|
||||||
if tx.writable {
|
if tx.writable {
|
||||||
tx.db.freelist.rollback(tx.meta.txid)
|
tx.db.freelist.rollback(tx.meta.txid)
|
||||||
if !tx.db.hasSyncedFreelist() {
|
// When mmap fails, the `data`, `dataref` and `datasz` may be reset to
|
||||||
// Reconstruct free page list by scanning the DB to get the whole free page list.
|
// zero values, and there is no way to reload free page IDs in this case.
|
||||||
// Note: scaning the whole db is heavy if your db size is large in NoSyncFreeList mode.
|
if tx.db.data != nil {
|
||||||
tx.db.freelist.noSyncReload(tx.db.freepages())
|
if !tx.db.hasSyncedFreelist() {
|
||||||
} else {
|
// Reconstruct free page list by scanning the DB to get the whole free page list.
|
||||||
// Read free page list from freelist page.
|
// Note: scaning the whole db is heavy if your db size is large in NoSyncFreeList mode.
|
||||||
tx.db.freelist.reload(tx.db.page(tx.db.meta().freelist))
|
tx.db.freelist.noSyncReload(tx.db.freepages())
|
||||||
|
} else {
|
||||||
|
// Read free page list from freelist page.
|
||||||
|
tx.db.freelist.reload(tx.db.page(tx.db.meta().freelist))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.close()
|
tx.close()
|
||||||
|
|
Loading…
Reference in New Issue