1//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_dotlk) || sqlite3_flock
2
3package vfs
4
5import (
6 "os"
7
8 "golang.org/x/sys/unix"
9)
10
11func osGetSharedLock(file *os.File) _ErrorCode {
12 return osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
13}
14
15func osGetReservedLock(file *os.File) _ErrorCode {
16 rc := osFlock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
17 if rc == _BUSY {
18 // The documentation states that a lock is upgraded by
19 // releasing the previous lock, then acquiring the new lock.
20 // Going over the source code of various BSDs, though,
21 // with LOCK_NB, the lock is not released,
22 // and EAGAIN is returned holding the shared lock.
23 // Still, if we're already in a transaction, we want to abort it,
24 // so return BUSY_SNAPSHOT here. If there's no transaction active,
25 // SQLite will change this back to SQLITE_BUSY,
26 // and invoke the busy handler if appropriate.
27 return _BUSY_SNAPSHOT
28 }
29 return rc
30}
31
32func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
33 if *state >= LOCK_RESERVED {
34 return _OK
35 }
36 return osGetReservedLock(file)
37}
38
39func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
40 rc := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
41 if rc == _BUSY {
42 // The documentation states that a lock is downgraded by
43 // releasing the previous lock then acquiring the new lock.
44 // Going over the source code of various BSDs, though,
45 // with LOCK_SH|LOCK_NB this should never happen.
46 // Return IOERR_RDLOCK, as BUSY would cause an assert to fail.
47 return _IOERR_RDLOCK
48 }
49 return _OK
50}
51
52func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
53 for {
54 err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
55 if err == nil {
56 return _OK
57 }
58 if err != unix.EINTR {
59 return _IOERR_UNLOCK
60 }
61 }
62}
63
64func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
65 // Test the RESERVED lock with fcntl(F_GETLK).
66 // This only works on systems where fcntl and flock are compatible.
67 // However, SQLite only calls this while holding a shared lock,
68 // so the difference is immaterial.
69 lock, rc := osTestLock(file, _RESERVED_BYTE, 1)
70 return lock == unix.F_WRLCK, rc
71}
72
73func osFlock(file *os.File, how int, def _ErrorCode) _ErrorCode {
74 err := unix.Flock(int(file.Fd()), how)
75 return osLockErrorCode(err, def)
76}
77
78func osReadLock(file *os.File, start, len int64) _ErrorCode {
79 return osLock(file, unix.F_RDLCK, start, len, _IOERR_RDLOCK)
80}
81
82func osWriteLock(file *os.File, start, len int64) _ErrorCode {
83 return osLock(file, unix.F_WRLCK, start, len, _IOERR_LOCK)
84}
85
86func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCode {
87 err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &unix.Flock_t{
88 Type: typ,
89 Start: start,
90 Len: len,
91 })
92 return osLockErrorCode(err, def)
93}
94
95func osUnlock(file *os.File, start, len int64) _ErrorCode {
96 lock := unix.Flock_t{
97 Type: unix.F_UNLCK,
98 Start: start,
99 Len: len,
100 }
101 for {
102 err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &lock)
103 if err == nil {
104 return _OK
105 }
106 if err != unix.EINTR {
107 return _IOERR_UNLOCK
108 }
109 }
110}