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  }