Skip to content

Commit 3e7b6f8

Browse files
authored
Merge pull request #44 from taruti/taruti/file-locking
Add support for file locking
2 parents d0dc16d + 45e34c8 commit 3e7b6f8

File tree

6 files changed

+81
-2
lines changed

6 files changed

+81
-2
lines changed

fs.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,9 @@ type File interface {
135135
io.ReaderAt
136136
io.Seeker
137137
io.Closer
138+
// Lock locks the file like e.g. flock. It protects against access from
139+
// other processes.
140+
Lock() error
141+
// Unlock unlocks the file.
142+
Unlock() error
138143
}

memfs/memory.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,15 @@ func (f *file) Stat() (os.FileInfo, error) {
297297
}, nil
298298
}
299299

300+
// Lock protects file from access from other processes. Which is a no-op
301+
// for this memory only filesystem.
302+
func (f *file) Lock() error {
303+
return nil
304+
}
305+
func (f *file) Unlock() error {
306+
return nil
307+
}
308+
300309
type fileInfo struct {
301310
name string
302311
size int

osfs/os.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ func New(baseDir string) billy.Filesystem {
2323
return chroot.New(&OS{}, baseDir)
2424
}
2525

26+
// file is a wrapper for an os.File which adds support for file locking.
27+
type file struct {
28+
*os.File
29+
}
30+
2631
func (fs *OS) Create(filename string) (billy.File, error) {
2732
return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode)
2833
}
@@ -34,7 +39,11 @@ func (fs *OS) OpenFile(filename string, flag int, perm os.FileMode) (billy.File,
3439
}
3540
}
3641

37-
return os.OpenFile(filename, flag, perm)
42+
f, err := os.OpenFile(filename, flag, perm)
43+
if err != nil {
44+
return nil, err
45+
}
46+
return file{f}, err
3847
}
3948

4049
func (fs *OS) createDir(fullpath string) error {
@@ -87,7 +96,11 @@ func (fs *OS) TempFile(dir, prefix string) (billy.File, error) {
8796
return nil, err
8897
}
8998

90-
return ioutil.TempFile(dir, prefix)
99+
f, err := ioutil.TempFile(dir, prefix)
100+
if err != nil {
101+
return nil, err
102+
}
103+
return file{f}, nil
91104
}
92105

93106
func (fs *OS) Join(elem ...string) string {

osfs/os_posix.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@ package osfs
44

55
import (
66
"os"
7+
"syscall"
78
)
89

910
// Stat returns the FileInfo structure describing file.
1011
func (fs *OS) Stat(filename string) (os.FileInfo, error) {
1112
return os.Stat(filename)
1213
}
14+
15+
// Lock protects file from access from other processes.
16+
func (f file) Lock() error {
17+
return syscall.Flock(int(f.File.Fd()), syscall.LOCK_EX)
18+
}
19+
func (f file) Unlock() error {
20+
return syscall.Flock(int(f.File.Fd()), syscall.LOCK_UN)
21+
}

osfs/os_windows.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ package osfs
55
import (
66
"os"
77
"path/filepath"
8+
"runtime"
89
"strings"
10+
"unsafe"
11+
12+
"golang.org/x/sys/windows"
913
)
1014

1115
// Stat returns the FileInfo structure describing file.
@@ -39,3 +43,34 @@ type fileInfo struct {
3943
func (fi *fileInfo) Name() string {
4044
return fi.name
4145
}
46+
47+
var (
48+
kernel32DLL = windows.NewLazySystemDLL("kernel32.dll")
49+
lockFileExProc = kernel32DLL.NewProc("LockFileEx")
50+
unlockFileProc = kernel32DLL.NewProc("UnlockFile")
51+
)
52+
53+
const (
54+
lockfileExclusiveLock = 0x2
55+
)
56+
57+
// Lock protects file from access from other processes.
58+
func (f file) Lock() error {
59+
var overlapped windows.Overlapped
60+
// err is always non-nil as per sys/windows semantics.
61+
ret, _, err := lockFileExProc.Call(f.File.Fd(), lockfileExclusiveLock, 0, 0xFFFFFFFF, 0,
62+
uintptr(unsafe.Pointer(&overlapped)))
63+
runtime.KeepAlive(&overlapped)
64+
if ret == 0 {
65+
return err
66+
}
67+
return nil
68+
}
69+
func (f file) Unlock() error {
70+
// err is always non-nil as per sys/windows semantics.
71+
ret, _, err := unlockFileProc.Call(f.File.Fd(), 0, 0, 0xFFFFFFFF, 0)
72+
if ret == 0 {
73+
return err
74+
}
75+
return nil
76+
}

test/mock.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,11 @@ func (*FileMock) Seek(offset int64, whence int) (int64, error) {
122122
func (*FileMock) Close() error {
123123
return nil
124124
}
125+
126+
func (*FileMock) Lock() error {
127+
return nil
128+
}
129+
130+
func (*FileMock) Unlock() error {
131+
return nil
132+
}

0 commit comments

Comments
 (0)