2021-02-26 23:37:07 +01:00
|
|
|
package fs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2023-03-25 19:43:19 +01:00
|
|
|
"reflect"
|
|
|
|
"sync"
|
2023-05-03 10:47:02 +02:00
|
|
|
"sync/atomic"
|
2021-02-26 23:37:07 +01:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2023-03-25 19:43:19 +01:00
|
|
|
kernelDLL = windows.MustLoadDLL("kernel32.dll")
|
|
|
|
procLock = kernelDLL.MustFindProc("LockFileEx")
|
|
|
|
procEvent = kernelDLL.MustFindProc("CreateEventW")
|
|
|
|
procDisk = kernelDLL.MustFindProc("GetDiskFreeSpaceExW")
|
2021-02-26 23:37:07 +01:00
|
|
|
)
|
|
|
|
|
2023-03-25 19:43:19 +01:00
|
|
|
// at windows only files could be synced
|
|
|
|
// Sync for directories is not supported.
|
|
|
|
func mustSyncPath(path string) {
|
2021-02-26 23:37:07 +01:00
|
|
|
}
|
|
|
|
|
2023-05-03 10:47:02 +02:00
|
|
|
func mustRemoveDirAtomic(dir string) {
|
|
|
|
if !IsPathExist(dir) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
n := atomic.AddUint64(&atomicDirRemoveCounter, 1)
|
|
|
|
tmpDir := fmt.Sprintf("%s.must-remove.%d", dir, n)
|
|
|
|
if err := os.Rename(dir, tmpDir); err != nil {
|
|
|
|
logger.Panicf("FATAL: cannot move %s to %s: %s", dir, tmpDir, err)
|
|
|
|
}
|
|
|
|
err := os.RemoveAll(tmpDir)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warnf("cannot remove dir: %q: %s, restart VictoriaMetrics process to complete file deletion", tmpDir, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 23:37:07 +01:00
|
|
|
const (
|
|
|
|
lockfileExclusiveLock = 2
|
|
|
|
fileFlagNormal = 0x00000080
|
|
|
|
)
|
|
|
|
|
|
|
|
// https://github.com/juju/fslock/blob/master/fslock_windows.go
|
2021-02-27 00:01:47 +01:00
|
|
|
func createFlockFile(flockFile string) (*os.File, error) {
|
2021-02-26 23:37:07 +01:00
|
|
|
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
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-03-25 19:43:19 +01:00
|
|
|
var (
|
2023-03-25 19:57:37 +01:00
|
|
|
mmapByAddrLock sync.Mutex
|
|
|
|
mmapByAddr = map[uintptr]windows.Handle{}
|
2023-03-25 19:43:19 +01:00
|
|
|
)
|
|
|
|
|
2021-02-27 00:01:47 +01:00
|
|
|
func mmap(fd int, length int) ([]byte, error) {
|
2023-03-25 19:43:19 +01:00
|
|
|
flProtect := uint32(windows.PAGE_READONLY)
|
|
|
|
dwDesiredAccess := uint32(windows.FILE_MAP_READ)
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/memory/creating-a-file-mapping-object#file-mapping-size
|
|
|
|
// do not specify any length params, windows will set it according to the file size.
|
|
|
|
// If length > file size, truncate is required according to api definition, we don't want it.
|
|
|
|
h, errno := windows.CreateFileMapping(windows.Handle(fd), nil, flProtect, 0, 0, nil)
|
|
|
|
if h == 0 {
|
|
|
|
return nil, os.NewSyscallError("CreateFileMapping", errno)
|
|
|
|
}
|
|
|
|
addr, errno := windows.MapViewOfFile(h, dwDesiredAccess, 0, 0, 0)
|
|
|
|
if addr == 0 {
|
|
|
|
windows.CloseHandle(h)
|
|
|
|
return nil, os.NewSyscallError("MapViewOfFile", errno)
|
|
|
|
}
|
|
|
|
data := make([]byte, 0)
|
|
|
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
|
|
|
hdr.Data = addr
|
|
|
|
hdr.Len = length
|
|
|
|
hdr.Cap = hdr.Len
|
|
|
|
|
2023-03-25 19:57:37 +01:00
|
|
|
mmapByAddrLock.Lock()
|
|
|
|
mmapByAddr[addr] = h
|
|
|
|
mmapByAddrLock.Unlock()
|
|
|
|
|
2023-03-25 19:43:19 +01:00
|
|
|
return data, nil
|
2021-02-26 23:37:07 +01:00
|
|
|
}
|
|
|
|
|
2023-03-25 19:43:19 +01:00
|
|
|
func mUnmap(data []byte) error {
|
|
|
|
// flush is not needed, since we perform only reading operation.
|
|
|
|
// In case of write, additional call FlushViewOfFile must be performed.
|
|
|
|
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
|
|
|
addr := header.Data
|
|
|
|
|
2023-03-25 19:57:37 +01:00
|
|
|
mmapByAddrLock.Lock()
|
|
|
|
h, ok := mmapByAddr[addr]
|
2023-03-25 19:43:19 +01:00
|
|
|
if !ok {
|
|
|
|
logger.Fatalf("BUG: unmapping for non exist addr: %d", addr)
|
|
|
|
}
|
2023-03-25 19:57:37 +01:00
|
|
|
delete(mmapByAddr, addr)
|
|
|
|
mmapByAddrLock.Unlock()
|
2023-03-25 19:43:19 +01:00
|
|
|
|
2023-03-25 19:57:37 +01:00
|
|
|
if err := windows.UnmapViewOfFile(addr); err != nil {
|
|
|
|
return fmt.Errorf("cannot unmap memory mapped file: %w", err)
|
|
|
|
}
|
|
|
|
errno := windows.CloseHandle(h)
|
|
|
|
return os.NewSyscallError("CloseHandle", errno)
|
2021-02-26 23:37:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2023-03-25 19:57:37 +01:00
|
|
|
logger.Panicf("FATAL: cannot get free space for %q : %s", path, err)
|
2021-02-26 23:37:07 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|