Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/config/file_lock_windows.go
3447 views
1
//go:build windows
2
3
package config
4
5
import (
6
"fmt"
7
"os"
8
"syscall"
9
"unsafe"
10
)
11
12
type fileLock struct {
13
path string
14
f *os.File
15
}
16
17
func newFileLock(targetPath string) *fileLock {
18
return &fileLock{path: targetPath + ".lock"}
19
}
20
21
// Minimal OVERLAPPED compatible with Windows API.
22
type overlapped struct {
23
Internal uintptr
24
InternalHigh uintptr
25
Offset uint32
26
OffsetHigh uint32
27
HEvent syscall.Handle
28
}
29
30
var (
31
kernel32 = syscall.NewLazyDLL("kernel32.dll")
32
procLockFileEx = kernel32.NewProc("LockFileEx")
33
procUnlockFileEx = kernel32.NewProc("UnlockFileEx")
34
lockfileExclusive = uintptr(0x00000002) // LOCKFILE_EXCLUSIVE_LOCK
35
)
36
37
func (l *fileLock) Lock() error {
38
f, err := os.OpenFile(l.path, os.O_CREATE|os.O_RDWR, 0600)
39
if err != nil {
40
return fmt.Errorf("open lock file: %w", err)
41
}
42
l.f = f
43
44
h := syscall.Handle(f.Fd())
45
var ov overlapped
46
47
// Lock 1 byte at offset 0. This is a common pattern for advisory locks.
48
r1, _, e1 := procLockFileEx.Call(
49
uintptr(h),
50
lockfileExclusive, // exclusive lock
51
0, // reserved
52
1, // nNumberOfBytesToLockLow
53
0, // nNumberOfBytesToLockHigh
54
uintptr(unsafe.Pointer(&ov)),
55
)
56
if r1 == 0 {
57
_ = f.Close()
58
l.f = nil
59
// e1 is syscall.Errno
60
if e1 != nil && e1 != syscall.Errno(0) {
61
return fmt.Errorf("LockFileEx: %w", e1)
62
}
63
return fmt.Errorf("LockFileEx: failed")
64
}
65
66
return nil
67
}
68
69
func (l *fileLock) Unlock() error {
70
if l.f == nil {
71
return nil
72
}
73
74
h := syscall.Handle(l.f.Fd())
75
var ov overlapped
76
77
r1, _, e1 := procUnlockFileEx.Call(
78
uintptr(h),
79
0, // reserved
80
1, // nNumberOfBytesToUnlockLow
81
0, // nNumberOfBytesToUnlockHigh
82
uintptr(unsafe.Pointer(&ov)),
83
)
84
85
// Always close even if unlock fails.
86
errClose := l.f.Close()
87
l.f = nil
88
89
if r1 == 0 {
90
if e1 != nil && e1 != syscall.Errno(0) {
91
return fmt.Errorf("UnlockFileEx: %w", e1)
92
}
93
return fmt.Errorf("UnlockFileEx: failed")
94
}
95
return errClose
96
}
97
98