1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package fileutil
19
20 import (
21 "errors"
22 "fmt"
23 "os"
24 "syscall"
25 "unsafe"
26 )
27
28 var (
29 modkernel32 = syscall.NewLazyDLL("kernel32.dll")
30 procLockFileEx = modkernel32.NewProc("LockFileEx")
31
32 errLocked = errors.New("the process cannot access the file because another process has locked a portion of the file")
33 )
34
35 const (
36
37 LOCKFILE_EXCLUSIVE_LOCK = 2
38 LOCKFILE_FAIL_IMMEDIATELY = 1
39
40
41 errLockViolation syscall.Errno = 0x21
42 )
43
44 func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
45 f, err := open(path, flag, perm)
46 if err != nil {
47 return nil, err
48 }
49 if err := lockFile(syscall.Handle(f.Fd()), LOCKFILE_FAIL_IMMEDIATELY); err != nil {
50 f.Close()
51 return nil, err
52 }
53 return &LockedFile{f}, nil
54 }
55
56 func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
57 f, err := open(path, flag, perm)
58 if err != nil {
59 return nil, err
60 }
61 if err := lockFile(syscall.Handle(f.Fd()), 0); err != nil {
62 f.Close()
63 return nil, err
64 }
65 return &LockedFile{f}, nil
66 }
67
68 func open(path string, flag int, perm os.FileMode) (*os.File, error) {
69 if path == "" {
70 return nil, fmt.Errorf("cannot open empty filename")
71 }
72 var access uint32
73 switch flag {
74 case syscall.O_RDONLY:
75 access = syscall.GENERIC_READ
76 case syscall.O_WRONLY:
77 access = syscall.GENERIC_WRITE
78 case syscall.O_RDWR:
79 access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
80 case syscall.O_WRONLY | syscall.O_CREAT:
81 access = syscall.GENERIC_ALL
82 default:
83 panic(fmt.Errorf("flag %v is not supported", flag))
84 }
85 fd, err := syscall.CreateFile(&(syscall.StringToUTF16(path)[0]),
86 access,
87 syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
88 nil,
89 syscall.OPEN_ALWAYS,
90 syscall.FILE_ATTRIBUTE_NORMAL,
91 0)
92 if err != nil {
93 return nil, err
94 }
95 return os.NewFile(uintptr(fd), path), nil
96 }
97
98 func lockFile(fd syscall.Handle, flags uint32) error {
99 var flag uint32 = LOCKFILE_EXCLUSIVE_LOCK
100 flag |= flags
101 if fd == syscall.InvalidHandle {
102 return nil
103 }
104 err := lockFileEx(fd, flag, 1, 0, &syscall.Overlapped{})
105 if err == nil {
106 return nil
107 } else if err.Error() == errLocked.Error() {
108 return ErrLocked
109 } else if err != errLockViolation {
110 return err
111 }
112 return nil
113 }
114
115 func lockFileEx(h syscall.Handle, flags, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
116 var reserved uint32 = 0
117 r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
118 if r1 == 0 {
119 if e1 != 0 {
120 err = error(e1)
121 } else {
122 err = syscall.EINVAL
123 }
124 }
125 return err
126 }
127
View as plain text