github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/filelock/filelock_windows.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build windows 6 7 package filelock 8 9 import ( 10 "io/fs" 11 "syscall" 12 "unsafe" 13 ) 14 15 type lockType uint32 16 17 const ( 18 LOCKFILE_EXCLUSIVE_LOCK = 0x00000002 // from internal/syscall/windows 19 20 readLock lockType = 0 21 writeLock lockType = LOCKFILE_EXCLUSIVE_LOCK 22 ) 23 24 const ( 25 reserved = 0 26 allBytes = ^uint32(0) 27 ) 28 29 func lock(f File, lt lockType) error { 30 // Per https://golang.org/issue/19098, “Programs currently expect the Fd 31 // method to return a handle that uses ordinary synchronous I/O.” 32 // However, LockFileEx still requires an OVERLAPPED structure, 33 // which contains the file offset of the beginning of the lock range. 34 // We want to lock the entire file, so we leave the offset as zero. 35 ol := new(syscall.Overlapped) 36 37 err := LockFileEx(syscall.Handle(f.Fd()), uint32(lt), reserved, allBytes, allBytes, ol) 38 if err != nil { 39 return &fs.PathError{ 40 Op: lt.String(), 41 Path: f.Name(), 42 Err: err, 43 } 44 } 45 return nil 46 } 47 48 func unlock(f File) error { 49 ol := new(syscall.Overlapped) 50 err := UnlockFileEx(syscall.Handle(f.Fd()), reserved, allBytes, allBytes, ol) 51 if err != nil { 52 return &fs.PathError{ 53 Op: "Unlock", 54 Path: f.Name(), 55 Err: err, 56 } 57 } 58 return nil 59 } 60 61 func isNotSupported(err error) bool { 62 switch err { 63 case ERROR_NOT_SUPPORTED, ERROR_CALL_NOT_IMPLEMENTED, ErrNotSupported: 64 return true 65 default: 66 return false 67 } 68 } 69 70 // portions of internal/syscall/windows below here for LockFileEx/UnlockFileEx 71 var _ unsafe.Pointer 72 73 // Do the interface allocations only once for common 74 // Errno values. 75 const ( 76 errnoERROR_IO_PENDING = 997 77 ) 78 79 var ( 80 errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) 81 errERROR_EINVAL error = syscall.EINVAL 82 ) 83 84 // errnoErr returns common boxed Errno values, to prevent 85 // allocations at runtime. 86 func errnoErr(e syscall.Errno) error { 87 switch e { 88 case 0: 89 return errERROR_EINVAL 90 case errnoERROR_IO_PENDING: 91 return errERROR_IO_PENDING 92 } 93 // TODO: add more here, after collecting data on the common 94 // error values see on Windows. (perhaps when running 95 // all.bat?) 96 return e 97 } 98 99 const ( 100 ERROR_NOT_SUPPORTED syscall.Errno = 50 101 ERROR_CALL_NOT_IMPLEMENTED syscall.Errno = 120 102 ) 103 104 var ( 105 modkernel32 = syscall.NewLazyDLL("kernel32.dll") 106 procLockFileEx = modkernel32.NewProc("LockFileEx") 107 procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") 108 ) 109 110 func LockFileEx(file syscall.Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) { 111 r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) 112 if r1 == 0 { 113 err = errnoErr(e1) 114 } 115 return 116 } 117 118 func UnlockFileEx(file syscall.Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) { 119 r1, _, e1 := syscall.Syscall6(procUnlockFileEx.Addr(), 5, uintptr(file), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped)), 0) 120 if r1 == 0 { 121 err = errnoErr(e1) 122 } 123 return 124 }