aboutsummaryrefslogtreecommitdiff
path: root/src/dedo.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/dedo.go')
-rw-r--r--src/dedo.go90
1 files changed, 40 insertions, 50 deletions
diff --git a/src/dedo.go b/src/dedo.go
index ad6ef15..8745584 100644
--- a/src/dedo.go
+++ b/src/dedo.go
@@ -1,6 +1,7 @@
package dedo
import (
+ "io/fs"
"errors"
"fmt"
"hash/fnv"
@@ -142,7 +143,7 @@ type DB struct {
path string
file *os.File
lockfile *os.File // windows only
- dataref []byte // mmap'ed readonly, write throws SEGV
+ dataref []byte // mmap'ed read-only via PROT_READ, write throws SEGV
data *[maxMapSize]byte
datasz int
filesz int // current on disk file size
@@ -168,10 +169,6 @@ type DB struct {
ops struct {
writeAt func(b []byte, off int64) (n int, err error)
}
-
- // Read only mode.
- // When true, Update() and Begin(true) return ErrDatabaseReadOnly immediately.
- readOnly bool
}
type call struct {
@@ -197,10 +194,6 @@ type Options struct {
// available on Darwin and Linux.
Timeout time.Duration
- // Open database in read-only mode. Uses flock(..., LOCK_SH |LOCK_NB) to
- // grab a shared lock (UNIX).
- ReadOnly bool
-
// Sets the DB.MmapFlags flag before memory mapping the file.
MmapFlags int
@@ -597,11 +590,6 @@ var (
// that has already been committed or rolled back.
ErrTxClosed = errors.New("tx closed")
- // ErrDatabaseReadOnly is returned when a mutating transaction is started on a
- // read-only database.
- ErrDatabaseReadOnly = errors.New("database is in read-only mode")
-
-
//
// These errors can occur when putting or deleting a value or a bucket.
//
@@ -684,7 +672,9 @@ func fdatasync(db *DB) error {
// flock acquires an advisory lock on a file descriptor.
-func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
+func flock(db *DB, mode os.FileMode, timeout time.Duration) error {
+ const exclusive = true // FIXME: allow multiple processes to cooperate
+
var t time.Time
for {
// If we're beyond our timeout then return an error.
@@ -1844,11 +1834,33 @@ func (db *DB) String() string {
return fmt.Sprintf("DB<%q>", db.path)
}
+func openFile(path string) (*os.File, error) {
+ const (
+ fileMode = 0o666
+ flagsRW = os.O_RDWR | os.O_CREATE
+ flagsRO = os.O_RDONLY
+ )
+
+ file, err := os.OpenFile(path, flagsRW, fileMode)
+ if err == nil {
+ return file, nil
+ }
+
+ if !errors.Is(err, fs.ErrPermission) {
+ return nil, err
+ }
+
+ return os.OpenFile(path, flagsRO, fileMode)
+}
+
// Open creates and opens a database at the given path.
// If the file does not exist then it will be created automatically.
// Passing in nil options will cause Bolt to open the database with the default options.
func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
- var db = &DB{opened: true}
+ var db = &DB{
+ opened: true,
+ path: path,
+ }
// Set default options if no options are provided.
if options == nil {
@@ -1861,19 +1873,12 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
db.MaxBatchDelay = DefaultMaxBatchDelay
db.AllocSize = DefaultAllocSize
- flag := os.O_RDWR
- if options.ReadOnly {
- flag = os.O_RDONLY
- db.readOnly = true
- }
-
- // Open data file and separate sync handler for metadata writes.
- db.path = path
- var err error
- if db.file, err = os.OpenFile(db.path, flag|os.O_CREATE, mode); err != nil {
+ file, err := openFile(path)
+ if err != nil {
_ = db.close()
return nil, err
}
+ db.file = file
// Lock file so that other processes using Bolt in read-write mode cannot
// use the database at the same time. This would cause corruption since
@@ -1882,7 +1887,7 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
// if !options.ReadOnly.
// The database file is locked using the shared lock (more than one process may
// hold a lock at the same time) otherwise (options.ReadOnly is set).
- if err := flock(db, mode, !db.readOnly, options.Timeout); err != nil {
+ if err := flock(db, mode, options.Timeout); err != nil {
_ = db.close()
return nil, err
}
@@ -2120,11 +2125,9 @@ func (db *DB) close() error {
// Close file handles.
if db.file != nil {
// No need to unlock read-only file.
- if !db.readOnly {
- // Unlock the file.
- if err := funlock(db); err != nil {
- log.Printf("bolt.Close(): funlock error: %s", err)
- }
+ // Unlock the file.
+ if err := funlock(db); err != nil {
+ log.Printf("bolt.Close(): funlock error: %s", err)
}
// Close the file descriptor.
@@ -2201,11 +2204,6 @@ func (db *DB) beginTx() (*Tx, error) {
}
func (db *DB) beginRWTx() (*Tx, error) {
- // If the database was opened with Options.ReadOnly, return an error.
- if db.readOnly {
- return nil, ErrDatabaseReadOnly
- }
-
// Obtain writer lock. This is released by the transaction when it closes.
// This enforces only one writer transaction at a time.
db.rwlock.Lock()
@@ -2544,25 +2542,17 @@ func (db *DB) grow(sz int) error {
// Truncate and fsync to ensure file size metadata is flushed.
// https://github.com/boltdb/bolt/issues/284
- if !db.readOnly {
- if runtime.GOOS != "windows" {
- if err := db.file.Truncate(int64(sz)); err != nil {
- return fmt.Errorf("file resize error: %s", err)
- }
- }
- if err := db.file.Sync(); err != nil {
- return fmt.Errorf("file sync error: %s", err)
- }
+ if err := db.file.Truncate(int64(sz)); err != nil {
+ return fmt.Errorf("file resize error: %s", err)
+ }
+ if err := db.file.Sync(); err != nil {
+ return fmt.Errorf("file sync error: %s", err)
}
db.filesz = sz
return nil
}
-func (db *DB) IsReadOnly() bool {
- return db.readOnly
-}
-
// Sub calculates and returns the difference between two sets of database stats.
// This is useful when obtaining stats at two different points and time and
// you need the performance counters that occurred within that time span.