package lockutil
import (
"fmt"
"os"
"syscall"
"unsafe"
"github.com/sirupsen/logrus"
)
var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procLockFileEx = modkernel32.NewProc("LockFileEx")
procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
)
const flagLockfileExclusiveLock = 0x00000002
func WithDirLock(dir string, fn func() error) error {
dirFile, err := os.OpenFile(dir+".lock", os.O_CREATE, 0o644)
if err != nil {
return err
}
defer dirFile.Close()
if err := lockFileEx(
syscall.Handle(dirFile.Fd()),
flagLockfileExclusiveLock,
0,
1,
0,
&syscall.Overlapped{},
); err != nil {
return fmt.Errorf("failed to lock %q: %w", dir, err)
}
defer func() {
if err := unlockFileEx(
syscall.Handle(dirFile.Fd()),
0,
1,
0,
&syscall.Overlapped{},
); err != nil {
logrus.WithError(err).Errorf("failed to unlock %q", dir)
}
}()
return fn()
}
func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
r, _, err := procLockFileEx.Call(uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
if r == 0 {
return err
}
return nil
}
func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
r, _, err := procUnlockFileEx.Call(uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0)
if r == 0 {
return err
}
return nil
}