diff options
Diffstat (limited to 'src/dedo.go')
-rw-r--r-- | src/dedo.go | 90 |
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. |