1//go:build !sqlite3_flock
2
3package vfs
4
5import (
6 "io"
7 "os"
8 "runtime"
9 "time"
10
11 "golang.org/x/sys/unix"
12)
13
14const (
15 // https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
16 _F_OFD_SETLK = 90
17 _F_OFD_SETLKW = 91
18 _F_OFD_SETLKWTIMEOUT = 93
19)
20
21type flocktimeout_t struct {
22 fl unix.Flock_t
23 timeout unix.Timespec
24}
25
26func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error {
27 if fullsync {
28 return file.Sync()
29 }
30 for {
31 err := unix.Fsync(int(file.Fd()))
32 if err != unix.EINTR {
33 return err
34 }
35 }
36}
37
38func osAllocate(file *os.File, size int64) error {
39 off, err := file.Seek(0, io.SeekEnd)
40 if err != nil {
41 return err
42 }
43 if size <= off {
44 return nil
45 }
46
47 store := unix.Fstore_t{
48 Flags: unix.F_ALLOCATEALL | unix.F_ALLOCATECONTIG,
49 Posmode: unix.F_PEOFPOSMODE,
50 Offset: 0,
51 Length: size - off,
52 }
53
54 // Try to get a continuous chunk of disk space.
55 err = unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
56 if err != nil {
57 // OK, perhaps we are too fragmented, allocate non-continuous.
58 store.Flags = unix.F_ALLOCATEALL
59 unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
60 }
61 return file.Truncate(size)
62}
63
64func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
65 return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
66}
67
68func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
69 return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
70}
71
72func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
73 lock := &flocktimeout_t{fl: unix.Flock_t{
74 Type: typ,
75 Start: start,
76 Len: len,
77 }}
78 var err error
79 switch {
80 case timeout == 0:
81 err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock.fl)
82 case timeout < 0:
83 err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKW, &lock.fl)
84 default:
85 lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond))
86 err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl)
87 runtime.KeepAlive(lock)
88 }
89 return osLockErrorCode(err, def)
90}
91
92func osUnlock(file *os.File, start, len int64) _ErrorCode {
93 lock := unix.Flock_t{
94 Type: unix.F_UNLCK,
95 Start: start,
96 Len: len,
97 }
98 for {
99 err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock)
100 if err == nil {
101 return _OK
102 }
103 if err != unix.EINTR {
104 return _IOERR_UNLOCK
105 }
106 }
107}