os_bsd.go

  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}