github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/locking/locker_windows.go (about)

     1  // Windows file locking implementation based on (but heavily modified from)
     2  // https://github.com/golang/build/blob/4821e1d4e1dd5d386f53f1e869ced293dd18f44a/cmd/builder/filemutex_windows.go.
     3  //
     4  // The original code license:
     5  //
     6  // Copyright (c) 2009 The Go Authors. All rights reserved.
     7  //
     8  // Redistribution and use in source and binary forms, with or without
     9  // modification, are permitted provided that the following conditions are
    10  // met:
    11  //
    12  //    * Redistributions of source code must retain the above copyright
    13  // notice, this list of conditions and the following disclaimer.
    14  //    * Redistributions in binary form must reproduce the above
    15  // copyright notice, this list of conditions and the following disclaimer
    16  // in the documentation and/or other materials provided with the
    17  // distribution.
    18  //    * Neither the name of Google Inc. nor the names of its
    19  // contributors may be used to endorse or promote products derived from
    20  // this software without specific prior written permission.
    21  //
    22  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    23  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    24  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    25  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    26  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    27  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    28  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    29  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    30  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    31  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    32  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    33  //
    34  // The original license header inside the code itself:
    35  //
    36  // Copyright 2013 The Go Authors. All rights reserved.
    37  // Use of this source code is governed by a BSD-style
    38  // license that can be found in the LICENSE file.
    39  
    40  package locking
    41  
    42  import (
    43  	"errors"
    44  	"syscall"
    45  	"unsafe"
    46  
    47  	"golang.org/x/sys/windows"
    48  )
    49  
    50  var (
    51  	kernel32     = windows.NewLazySystemDLL("kernel32.dll")
    52  	lockFileEx   = kernel32.NewProc("LockFileEx")
    53  	unlockFileEx = kernel32.NewProc("UnlockFileEx")
    54  )
    55  
    56  const (
    57  	LOCKFILE_EXCLUSIVE_LOCK   = 2
    58  	LOCKFILE_FAIL_IMMEDIATELY = 1
    59  )
    60  
    61  func callLockFileEx(
    62  	handle syscall.Handle,
    63  	flags,
    64  	reserved,
    65  	lockLow,
    66  	lockHigh uint32,
    67  	overlapped *syscall.Overlapped,
    68  ) (err error) {
    69  	r1, _, e1 := syscall.Syscall6(
    70  		lockFileEx.Addr(),
    71  		6,
    72  		uintptr(handle),
    73  		uintptr(flags),
    74  		uintptr(reserved),
    75  		uintptr(lockLow),
    76  		uintptr(lockHigh),
    77  		uintptr(unsafe.Pointer(overlapped)),
    78  	)
    79  	if r1 == 0 {
    80  		if e1 != 0 {
    81  			err = error(e1)
    82  		} else {
    83  			err = syscall.EINVAL
    84  		}
    85  	}
    86  	return
    87  }
    88  
    89  func callunlockFileEx(
    90  	handle syscall.Handle,
    91  	reserved,
    92  	lockLow,
    93  	lockHigh uint32,
    94  	overlapped *syscall.Overlapped,
    95  ) (err error) {
    96  	r1, _, e1 := syscall.Syscall6(
    97  		unlockFileEx.Addr(),
    98  		5,
    99  		uintptr(handle),
   100  		uintptr(reserved),
   101  		uintptr(lockLow),
   102  		uintptr(lockHigh),
   103  		uintptr(unsafe.Pointer(overlapped)),
   104  		0,
   105  	)
   106  	if r1 == 0 {
   107  		if e1 != 0 {
   108  			err = error(e1)
   109  		} else {
   110  			err = syscall.EINVAL
   111  		}
   112  	}
   113  	return
   114  }
   115  
   116  // Lock attempts to acquire the file lock.
   117  func (l *Locker) Lock(block bool) error {
   118  	// Verify that we don't already hold the lock.
   119  	if l.held {
   120  		return errors.New("lock already held")
   121  	}
   122  
   123  	// Create an overlapped structure to manage overlapped I/O.
   124  	var ol syscall.Overlapped
   125  
   126  	// Set up the lock and blocking flags.
   127  	flags := uint32(LOCKFILE_EXCLUSIVE_LOCK)
   128  	if !block {
   129  		flags |= LOCKFILE_FAIL_IMMEDIATELY
   130  	}
   131  
   132  	// Attempt to perform locking.
   133  	err := callLockFileEx(syscall.Handle(l.file.Fd()), flags, 0, 1, 0, &ol)
   134  
   135  	// Check for success and update the internal state.
   136  	if err == nil {
   137  		l.held = true
   138  	}
   139  
   140  	// Done.
   141  	return err
   142  }
   143  
   144  // Unlock releases the file lock.
   145  func (l *Locker) Unlock() error {
   146  	// Verify that we hold the lock.
   147  	if !l.held {
   148  		return errors.New("lock not held")
   149  	}
   150  
   151  	// Create an overlapped structure to manage overlapped I/O.
   152  	var ol syscall.Overlapped
   153  
   154  	// Attempt to perform unlocking.
   155  	err := callunlockFileEx(syscall.Handle(l.file.Fd()), 0, 1, 0, &ol)
   156  
   157  	// Check for success and update the internal state.
   158  	if err == nil {
   159  		l.held = false
   160  	}
   161  
   162  	// Done.
   163  	return err
   164  }