package fs import ( "fmt" "os" "unsafe" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" "golang.org/x/sys/windows" ) var ( kernelDLL = windows.MustLoadDLL("kernel32.dll") procLock = kernelDLL.MustFindProc("LockFileEx") procEvent = kernelDLL.MustFindProc("CreateEventW") procDisk = kernelDLL.MustFindProc("GetDiskFreeSpaceExW") ntDLL = windows.MustLoadDLL("ntdll.dll") ntSetInformationProc = ntDLL.MustFindProc("NtSetInformationFile") ) // panic at windows, if file already open by another process. // one of possible solutions - change files opening process with correct flags. // https://github.com/dgraph-io/badger/issues/699 // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers func mustSyncPath(string) { } const ( lockfileExclusiveLock = 2 fileFlagNormal = 0x00000080 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information_ex fileDispositionPosixSemantics = 0x00000002 fileDispositionIgnoreReadonlyAttribute = 0x00000010 ) // https://github.com/juju/fslock/blob/master/fslock_windows.go func createFlockFile(flockFile string) (*os.File, error) { name, err := windows.UTF16PtrFromString(flockFile) if err != nil { return nil, err } handle, err := windows.CreateFile( name, windows.GENERIC_READ|windows.DELETE, windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE, nil, windows.OPEN_ALWAYS, windows.FILE_FLAG_OVERLAPPED|fileFlagNormal, 0) if err != nil { return nil, fmt.Errorf("cannot create lock file %q: %w", flockFile, err) } ol, err := newOverlapped() if err != nil { return nil, fmt.Errorf("cannot create Overlapped handler: %w", err) } // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex // overlapped is dropped? r1, _, err := procLock.Call(uintptr(handle), uintptr(lockfileExclusiveLock), uintptr(0), uintptr(1), uintptr(0), uintptr(unsafe.Pointer(ol))) if r1 == 0 { return nil, err } return os.NewFile(uintptr(handle), flockFile), nil } // stub func mmap(fd int, length int) ([]byte, error) { return nil, nil } // stub func mUnmap([]byte) error { return nil } func mustGetFreeSpace(path string) uint64 { var freeBytes int64 r, _, err := procDisk.Call(uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))), uintptr(unsafe.Pointer(&freeBytes))) if r == 0 { logger.Errorf("cannot get free space: %v", err) return 0 } return uint64(freeBytes) } // stub func fadviseSequentialRead(f *os.File, prefetch bool) error { return nil } // copied from https://github.com/juju/fslock/blob/master/fslock_windows.go // https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped func newOverlapped() (*windows.Overlapped, error) { event, err := createEvent(nil, nil) if err != nil { return nil, err } return &windows.Overlapped{HEvent: event}, nil } // copied from https://github.com/juju/fslock/blob/master/fslock_windows.go // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa func createEvent(sa *windows.SecurityAttributes, name *uint16) (windows.Handle, error) { r0, _, err := procEvent.Call(uintptr(unsafe.Pointer(sa)), uintptr(1), uintptr(1), uintptr(unsafe.Pointer(name))) handle := windows.Handle(r0) if handle == windows.InvalidHandle { return 0, err } return handle, nil } // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information_ex type fileDispositionInformationEx struct { Flags uint32 } // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_status_block type ioStatusBlock struct { Status, Information uintptr } // UpdateFileHandle - changes file deletion semantic at windows to posix-like. func UpdateFileHandle(path string) error { handle, err := windows.Open(path, windows.GENERIC_READ|windows.DELETE, windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE) if err != nil { return err } return setPosixDelete(handle) } // supported starting with Windows 10, version 1709. // supported by NTFS only. func setPosixDelete(handle windows.Handle) error { var iosb ioStatusBlock flags := fileDispositionInformationEx{ Flags: fileDispositionPosixSemantics | fileDispositionIgnoreReadonlyAttribute, } // class FileDispositionInformationEx, // 64 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class r0, _, err := ntSetInformationProc.Call(uintptr(handle), uintptr(unsafe.Pointer(&iosb)), uintptr(unsafe.Pointer(&flags)), unsafe.Sizeof(flags), uintptr(64)) if r0 == 0 { return nil } return fmt.Errorf("cannot set file disposition information: NT_STATUS: 0x%X, error: %w", r0, err) }