github.com/opentofu/opentofu@v1.7.1/internal/states/statemgr/filesystem_lock_windows.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  //go:build windows
     7  // +build windows
     8  
     9  package statemgr
    10  
    11  import (
    12  	"log"
    13  	"math"
    14  	"syscall"
    15  	"unsafe"
    16  )
    17  
    18  var (
    19  	modkernel32      = syscall.NewLazyDLL("kernel32.dll")
    20  	procLockFileEx   = modkernel32.NewProc("LockFileEx")
    21  	procCreateEventW = modkernel32.NewProc("CreateEventW")
    22  )
    23  
    24  const (
    25  	// dwFlags defined for LockFileEx
    26  	// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
    27  	_LOCKFILE_FAIL_IMMEDIATELY = 1
    28  	_LOCKFILE_EXCLUSIVE_LOCK   = 2
    29  )
    30  
    31  func (s *Filesystem) lock() error {
    32  	log.Printf("[TRACE] statemgr.Filesystem: locking %s using LockFileEx", s.path)
    33  
    34  	// even though we're failing immediately, an overlapped event structure is
    35  	// required
    36  	ol, err := newOverlapped()
    37  	if err != nil {
    38  		return err
    39  	}
    40  	defer syscall.CloseHandle(ol.HEvent)
    41  
    42  	return lockFileEx(
    43  		syscall.Handle(s.stateFileOut.Fd()),
    44  		_LOCKFILE_EXCLUSIVE_LOCK|_LOCKFILE_FAIL_IMMEDIATELY,
    45  		0,              // reserved
    46  		0,              // bytes low
    47  		math.MaxUint32, // bytes high
    48  		ol,
    49  	)
    50  }
    51  
    52  func (s *Filesystem) unlock() error {
    53  	log.Printf("[TRACE] statemgr.Filesystem: unlocked by closing %s", s.path)
    54  
    55  	// the file is closed in Unlock
    56  	return nil
    57  }
    58  
    59  func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
    60  	r1, _, e1 := syscall.Syscall6(
    61  		procLockFileEx.Addr(),
    62  		6,
    63  		uintptr(h),
    64  		uintptr(flags),
    65  		uintptr(reserved),
    66  		uintptr(locklow),
    67  		uintptr(lockhigh),
    68  		uintptr(unsafe.Pointer(ol)),
    69  	)
    70  	if r1 == 0 {
    71  		if e1 != 0 {
    72  			err = error(e1)
    73  		} else {
    74  			err = syscall.EINVAL
    75  		}
    76  	}
    77  	return
    78  }
    79  
    80  // newOverlapped creates a structure used to track asynchronous
    81  // I/O requests that have been issued.
    82  func newOverlapped() (*syscall.Overlapped, error) {
    83  	event, err := createEvent(nil, true, false, nil)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	return &syscall.Overlapped{HEvent: event}, nil
    88  }
    89  
    90  func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) {
    91  	var _p0 uint32
    92  	if manualReset {
    93  		_p0 = 1
    94  	}
    95  	var _p1 uint32
    96  	if initialState {
    97  		_p1 = 1
    98  	}
    99  
   100  	r0, _, e1 := syscall.Syscall6(
   101  		procCreateEventW.Addr(),
   102  		4,
   103  		uintptr(unsafe.Pointer(sa)),
   104  		uintptr(_p0),
   105  		uintptr(_p1),
   106  		uintptr(unsafe.Pointer(name)),
   107  		0,
   108  		0,
   109  	)
   110  	handle = syscall.Handle(r0)
   111  	if handle == syscall.InvalidHandle {
   112  		if e1 != 0 {
   113  			err = error(e1)
   114  		} else {
   115  			err = syscall.EINVAL
   116  		}
   117  	}
   118  	return
   119  }