Skip to content

Commit d688496

Browse files
file backend locking
1 parent c1ef25a commit d688496

File tree

3 files changed

+80
-18
lines changed

3 files changed

+80
-18
lines changed

internal/auth/file_token_storage.go

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,66 @@ import (
77
)
88

99
type fileTokenStorage struct {
10-
path string
10+
path string
11+
lockPath string
1112
}
1213

1314
func newFileTokenStorage(configDir string) *fileTokenStorage {
14-
return &fileTokenStorage{path: filepath.Join(configDir, "auth-token")}
15+
return &fileTokenStorage{
16+
path: filepath.Join(configDir, "auth-token"),
17+
lockPath: filepath.Join(configDir, "auth-token.lock"),
18+
}
1519
}
1620

17-
func (f *fileTokenStorage) GetAuthToken() (string, error) {
18-
data, err := os.ReadFile(f.path)
21+
func (f *fileTokenStorage) withLock(fn func() error) error {
22+
if err := os.MkdirAll(filepath.Dir(f.lockPath), 0700); err != nil {
23+
return err
24+
}
25+
lf, err := os.OpenFile(f.lockPath, os.O_CREATE|os.O_WRONLY, 0600)
1926
if err != nil {
20-
if errors.Is(err, os.ErrNotExist) {
21-
return "", ErrTokenNotFound
22-
}
23-
return "", err
27+
return err
2428
}
25-
return string(data), nil
26-
}
29+
defer func() { _ = lf.Close() }()
2730

28-
func (f *fileTokenStorage) SetAuthToken(token string) error {
29-
if err := os.MkdirAll(filepath.Dir(f.path), 0700); err != nil {
31+
if err := lockFile(lf); err != nil {
3032
return err
3133
}
32-
return os.WriteFile(f.path, []byte(token), 0600)
34+
defer func() { _ = unlockFile(lf) }()
35+
36+
return fn()
3337
}
3438

35-
func (f *fileTokenStorage) DeleteAuthToken() error {
36-
err := os.Remove(f.path)
37-
if errors.Is(err, os.ErrNotExist) {
39+
func (f *fileTokenStorage) GetAuthToken() (string, error) {
40+
var token string
41+
err := f.withLock(func() error {
42+
data, err := os.ReadFile(f.path)
43+
if err != nil {
44+
if errors.Is(err, os.ErrNotExist) {
45+
return ErrTokenNotFound
46+
}
47+
return err
48+
}
49+
token = string(data)
3850
return nil
39-
}
40-
return err
51+
})
52+
return token, err
53+
}
54+
55+
func (f *fileTokenStorage) SetAuthToken(token string) error {
56+
return f.withLock(func() error {
57+
if err := os.MkdirAll(filepath.Dir(f.path), 0700); err != nil {
58+
return err
59+
}
60+
return os.WriteFile(f.path, []byte(token), 0600)
61+
})
62+
}
63+
64+
func (f *fileTokenStorage) DeleteAuthToken() error {
65+
return f.withLock(func() error {
66+
err := os.Remove(f.path)
67+
if errors.Is(err, os.ErrNotExist) {
68+
return nil
69+
}
70+
return err
71+
})
4172
}

internal/auth/filelock_unix.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build !windows
2+
3+
package auth
4+
5+
import (
6+
"os"
7+
"syscall"
8+
)
9+
10+
func lockFile(f *os.File) error {
11+
return syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
12+
}
13+
14+
func unlockFile(f *os.File) error {
15+
return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
16+
}

internal/auth/filelock_windows.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//go:build windows
2+
3+
package auth
4+
5+
import "os"
6+
7+
func lockFile(f *os.File) error {
8+
// Windows LockFileEx requires unsafe/syscall wiring; for now token
9+
// file contention on Windows is unlikely enough to skip locking.
10+
return nil
11+
}
12+
13+
func unlockFile(f *os.File) error {
14+
return nil
15+
}

0 commit comments

Comments
 (0)