lock_windows.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright 2015 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // +build windows
  15. package fileutil
  16. import (
  17. "errors"
  18. "fmt"
  19. "os"
  20. "syscall"
  21. "unsafe"
  22. )
  23. var (
  24. modkernel32 = syscall.NewLazyDLL("kernel32.dll")
  25. procLockFileEx = modkernel32.NewProc("LockFileEx")
  26. errLocked = errors.New("The process cannot access the file because another process has locked a portion of the file.")
  27. )
  28. const (
  29. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
  30. LOCKFILE_EXCLUSIVE_LOCK = 2
  31. LOCKFILE_FAIL_IMMEDIATELY = 1
  32. // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
  33. errLockViolation syscall.Errno = 0x21
  34. )
  35. func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
  36. f, err := open(path, flag, perm)
  37. if err != nil {
  38. return nil, err
  39. }
  40. if err := lockFile(syscall.Handle(f.Fd()), LOCKFILE_FAIL_IMMEDIATELY); err != nil {
  41. f.Close()
  42. return nil, err
  43. }
  44. return &LockedFile{f}, nil
  45. }
  46. func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
  47. f, err := open(path, flag, perm)
  48. if err != nil {
  49. return nil, err
  50. }
  51. if err := lockFile(syscall.Handle(f.Fd()), 0); err != nil {
  52. f.Close()
  53. return nil, err
  54. }
  55. return &LockedFile{f}, nil
  56. }
  57. func open(path string, flag int, perm os.FileMode) (*os.File, error) {
  58. if path == "" {
  59. return nil, fmt.Errorf("cannot open empty filename")
  60. }
  61. var access uint32
  62. switch flag {
  63. case syscall.O_RDONLY:
  64. access = syscall.GENERIC_READ
  65. case syscall.O_WRONLY:
  66. access = syscall.GENERIC_WRITE
  67. case syscall.O_RDWR:
  68. access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
  69. case syscall.O_WRONLY | syscall.O_CREAT:
  70. access = syscall.GENERIC_ALL
  71. default:
  72. panic(fmt.Errorf("flag %v is not supported", flag))
  73. }
  74. fd, err := syscall.CreateFile(&(syscall.StringToUTF16(path)[0]),
  75. access,
  76. syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
  77. nil,
  78. syscall.OPEN_ALWAYS,
  79. syscall.FILE_ATTRIBUTE_NORMAL,
  80. 0)
  81. if err != nil {
  82. return nil, err
  83. }
  84. return os.NewFile(uintptr(fd), path), nil
  85. }
  86. func lockFile(fd syscall.Handle, flags uint32) error {
  87. var flag uint32 = LOCKFILE_EXCLUSIVE_LOCK
  88. flag |= flags
  89. if fd == syscall.InvalidHandle {
  90. return nil
  91. }
  92. err := lockFileEx(fd, flag, 1, 0, &syscall.Overlapped{})
  93. if err == nil {
  94. return nil
  95. } else if err.Error() == errLocked.Error() {
  96. return ErrLocked
  97. } else if err != errLockViolation {
  98. return err
  99. }
  100. return nil
  101. }
  102. func lockFileEx(h syscall.Handle, flags, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
  103. var reserved uint32 = 0
  104. r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
  105. if r1 == 0 {
  106. if e1 != 0 {
  107. err = error(e1)
  108. } else {
  109. err = syscall.EINVAL
  110. }
  111. }
  112. return err
  113. }