Repository navigation
Expand file tree
/
Copy pathofd.go
More file actions
99 lines (93 loc) · 4.67 KB
/
Copy pathofd.go
File metadata and controls
99 lines (93 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// Copyright 2026 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sqlite // import "modernc.org/sqlite"
import "errors"
// ErrOFDLockingUnavailable is returned by [OFDLocking] where OFD locks do
// not exist: on every platform but Linux, and on Linux after the kernel or
// the filesystem has rejected them and the library has fallen back to POSIX
// locks for the remainder of the process.
var ErrOFDLockingUnavailable = errors.New(
"sqlite: OFD locking is not available on this platform or kernel")
// ErrOFDLockingTooLate is returned by [OFDLocking] when the locking mode can
// no longer be changed: a database file lock has already been attempted in
// this process, and from the first lock attempt on the mode is fixed,
// because locks of the two kinds do not release one another and switching
// with locks in the wild would leave them behind. Enable OFD locking before
// the first connection is opened.
var ErrOFDLockingTooLate = errors.New(
"sqlite: OFDLocking called after the first database file lock in this process")
// OFDLocking switches SQLite between POSIX record locks (the default) and
// Linux Open File Description (OFD) locks for database files, process-wide.
// It returns the setting previously in effect; when err is non-nil the
// returned prev is meaningless — use [OFDLockingEnabled] for the current
// state.
//
// A POSIX record lock belongs to the (process, inode) pair: the kernel drops
// every POSIX lock the process holds on a file at any close(2) of any
// descriptor of that file. Code as innocent as
//
// f, _ := os.Open(dbPath) // hash, back up, inspect, ...
// f.Close()
//
// anywhere in the process — a third-party library included — therefore
// silently strips SQLite's own transaction locks and leaves the database
// unprotected against other processes. An OFD lock belongs to the open file
// description through which it was placed and survives such a close. OFD
// locking is opt-in and off by default: unless it is enabled, nothing about
// locking changes. See https://gitlab.com/cznic/sqlite/-/issues/255 for
// background and measurements.
//
// The switch is process-wide by necessity, which is why it is not a DSN
// parameter: POSIX and OFD locks taken by one process are different owners
// to the kernel and conflict with each other, so every connection to a given
// database file inside one process must use the same kind.
//
// OFDLocking must be called before the first database file is opened. From
// the process's first lock attempt on, the mode is fixed and calls
// attempting to change it return [ErrOFDLockingTooLate]; querying with
// [OFDLockingEnabled], and calling OFDLocking with the value already in
// effect, always work. The call overrides the MODERNC_SQLITE_OFD_LOCK
// environment variable, which enables OFD locking when set to anything but
// the empty string or a value starting with "0" and which the library reads
// once, at initialization time, from the environment the process started
// with — to switch from Go, prefer this call over os.Setenv.
//
// OFD locks exist on Linux only; everywhere else OFDLocking returns
// [ErrOFDLockingUnavailable]. On Linux kernels older than 3.15, and on
// filesystems that reject OFD locks, the first lock attempt fails with
// EINVAL and the library permanently falls back to POSIX locks: OFDLocking
// returns [ErrOFDLockingUnavailable] from then on and [OFDLockingEnabled]
// reports false, which is how a caller can detect the fallback.
//
// Enabling OFD locking covers the locks on the database file itself; the
// locks coordinating WAL mode through the -shm file remain POSIX locks. And
// one visible behavior change comes with the immunity: code in the same
// process that takes fcntl record locks of its own on a database file used
// to share ownership with SQLite's locks — never conflicting, while quietly
// destroying them — whereas with OFD locking enabled such locks conflict
// with SQLite's and fail loudly instead.
//
// OFDLocking is safe for concurrent use.
func OFDLocking(on bool) (prev bool, err error) {
arg := int32(0)
if on {
arg = 1
}
switch rc := ofdLocking(arg); rc {
case -1:
return false, ErrOFDLockingUnavailable
case -2:
return false, ErrOFDLockingTooLate
default:
return rc != 0, nil
}
}
// OFDLockingEnabled reports whether Linux Open File Description locks are in
// effect for database files in this process; see [OFDLocking]. It reports
// false where OFD locks are unavailable: on every platform but Linux, and on
// Linux once the kernel has rejected them and the library has fallen back to
// POSIX locks.
func OFDLockingEnabled() bool {
return ofdLocking(-1) == 1
}