...

Source file src/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_windows.go

Documentation: go.etcd.io/etcd/client/pkg/v3/fileutil

     1  // Copyright 2015 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build windows
    16  // +build windows
    17  
    18  package fileutil
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"syscall"
    25  	"unsafe"
    26  )
    27  
    28  var (
    29  	modkernel32    = syscall.NewLazyDLL("kernel32.dll")
    30  	procLockFileEx = modkernel32.NewProc("LockFileEx")
    31  
    32  	errLocked = errors.New("the process cannot access the file because another process has locked a portion of the file")
    33  )
    34  
    35  const (
    36  	// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
    37  	LOCKFILE_EXCLUSIVE_LOCK   = 2
    38  	LOCKFILE_FAIL_IMMEDIATELY = 1
    39  
    40  	// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
    41  	errLockViolation syscall.Errno = 0x21
    42  )
    43  
    44  func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
    45  	f, err := open(path, flag, perm)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	if err := lockFile(syscall.Handle(f.Fd()), LOCKFILE_FAIL_IMMEDIATELY); err != nil {
    50  		f.Close()
    51  		return nil, err
    52  	}
    53  	return &LockedFile{f}, nil
    54  }
    55  
    56  func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
    57  	f, err := open(path, flag, perm)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	if err := lockFile(syscall.Handle(f.Fd()), 0); err != nil {
    62  		f.Close()
    63  		return nil, err
    64  	}
    65  	return &LockedFile{f}, nil
    66  }
    67  
    68  func open(path string, flag int, perm os.FileMode) (*os.File, error) {
    69  	if path == "" {
    70  		return nil, fmt.Errorf("cannot open empty filename")
    71  	}
    72  	var access uint32
    73  	switch flag {
    74  	case syscall.O_RDONLY:
    75  		access = syscall.GENERIC_READ
    76  	case syscall.O_WRONLY:
    77  		access = syscall.GENERIC_WRITE
    78  	case syscall.O_RDWR:
    79  		access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
    80  	case syscall.O_WRONLY | syscall.O_CREAT:
    81  		access = syscall.GENERIC_ALL
    82  	default:
    83  		panic(fmt.Errorf("flag %v is not supported", flag))
    84  	}
    85  	fd, err := syscall.CreateFile(&(syscall.StringToUTF16(path)[0]),
    86  		access,
    87  		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
    88  		nil,
    89  		syscall.OPEN_ALWAYS,
    90  		syscall.FILE_ATTRIBUTE_NORMAL,
    91  		0)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	return os.NewFile(uintptr(fd), path), nil
    96  }
    97  
    98  func lockFile(fd syscall.Handle, flags uint32) error {
    99  	var flag uint32 = LOCKFILE_EXCLUSIVE_LOCK
   100  	flag |= flags
   101  	if fd == syscall.InvalidHandle {
   102  		return nil
   103  	}
   104  	err := lockFileEx(fd, flag, 1, 0, &syscall.Overlapped{})
   105  	if err == nil {
   106  		return nil
   107  	} else if err.Error() == errLocked.Error() {
   108  		return ErrLocked
   109  	} else if err != errLockViolation {
   110  		return err
   111  	}
   112  	return nil
   113  }
   114  
   115  func lockFileEx(h syscall.Handle, flags, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
   116  	var reserved uint32 = 0
   117  	r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
   118  	if r1 == 0 {
   119  		if e1 != 0 {
   120  			err = error(e1)
   121  		} else {
   122  			err = syscall.EINVAL
   123  		}
   124  	}
   125  	return err
   126  }
   127  

View as plain text