gotosocial/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go
kim 1e7b32490d
[experiment] add alternative wasm sqlite3 implementation available via build-tag ()
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
2024-05-27 17:46:15 +02:00

176 lines
6.3 KiB
Go

package sysfs
import (
"errors"
"syscall"
"unsafe"
"github.com/tetratelabs/wazero/experimental/sys"
)
const (
nonBlockingFileReadSupported = true
nonBlockingFileWriteSupported = false
_ERROR_IO_INCOMPLETE = syscall.Errno(996)
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
var (
// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe")
// procGetOverlappedResult is the syscall.LazyProc in kernel32 for GetOverlappedResult
procGetOverlappedResult = kernel32.NewProc("GetOverlappedResult")
// procCreateEventW is the syscall.LazyProc in kernel32 for CreateEventW
procCreateEventW = kernel32.NewProc("CreateEventW")
)
// readFd returns ENOSYS on unsupported platforms.
//
// PeekNamedPipe: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
// "GetFileType can assist in determining what device type the handle refers to. A console handle presents as FILE_TYPE_CHAR."
// https://learn.microsoft.com/en-us/windows/console/console-handles
func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
handle := syscall.Handle(fd)
fileType, err := syscall.GetFileType(handle)
if err != nil {
return 0, sys.UnwrapOSError(err)
}
if fileType&syscall.FILE_TYPE_CHAR == 0 {
return -1, sys.ENOSYS
}
n, errno := peekNamedPipe(handle)
if errno == syscall.ERROR_BROKEN_PIPE {
return 0, 0
}
if n == 0 {
return -1, sys.EAGAIN
}
un, err := syscall.Read(handle, buf[0:n])
return un, sys.UnwrapOSError(err)
}
func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
return -1, sys.ENOSYS
}
func readSocket(h uintptr, buf []byte) (int, sys.Errno) {
// Poll the socket to ensure that we never perform a blocking/overlapped Read.
//
// When the socket is closed by the remote peer, wsaPoll will return n=1 and
// errno=0, and syscall.ReadFile will return n=0 and errno=0 -- which indicates
// io.EOF.
if n, errno := wsaPoll(
[]pollFd{newPollFd(h, _POLLIN, 0)}, 0); !errors.Is(errno, sys.Errno(0)) {
return 0, sys.UnwrapOSError(errno)
} else if n <= 0 {
return 0, sys.EAGAIN
}
// Properly use overlapped result.
//
// If hFile was opened with FILE_FLAG_OVERLAPPED, the following conditions are in effect:
// - The lpOverlapped parameter must point to a valid and unique OVERLAPPED structure,
// otherwise the function can incorrectly report that the read operation is complete.
// - The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult
// function to get the actual number of bytes read. If the hFile parameter is associated
// with an I/O completion port, you can also get the number of bytes read by calling the
// GetQueuedCompletionStatus function.
//
// We are currently skipping checking if hFile was opened with FILE_FLAG_OVERLAPPED but using
// both lpOverlapped and lpNumberOfBytesRead.
var overlapped syscall.Overlapped
// Create an event to wait on.
if hEvent, err := createEventW(nil, true, false, nil); err != 0 {
return 0, sys.UnwrapOSError(err)
} else {
overlapped.HEvent = syscall.Handle(hEvent)
}
var done uint32
errno := syscall.ReadFile(syscall.Handle(h), buf, &done, &overlapped)
if errors.Is(errno, syscall.ERROR_IO_PENDING) {
errno = syscall.CancelIo(syscall.Handle(h))
if errno != nil {
return 0, sys.UnwrapOSError(errno) // This is a fatal error. CancelIo failed.
}
done, errno = getOverlappedResult(syscall.Handle(h), &overlapped, true) // wait for I/O to complete(cancel or finish). Overwrite done and errno.
if errors.Is(errno, syscall.ERROR_OPERATION_ABORTED) {
return int(done), sys.EAGAIN // This is one of the expected behavior, I/O was cancelled(completed) before finished.
}
}
return int(done), sys.UnwrapOSError(errno)
}
func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
var done uint32
var overlapped syscall.Overlapped
errno := syscall.WriteFile(syscall.Handle(fd), buf, &done, &overlapped)
if errors.Is(errno, syscall.ERROR_IO_PENDING) {
errno = syscall.EAGAIN
}
return int(done), sys.UnwrapOSError(errno)
}
// peekNamedPipe partially exposes PeekNamedPipe from the Win32 API
// see https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
func peekNamedPipe(handle syscall.Handle) (uint32, syscall.Errno) {
var totalBytesAvail uint32
totalBytesPtr := unsafe.Pointer(&totalBytesAvail)
_, _, errno := syscall.SyscallN(
procPeekNamedPipe.Addr(),
uintptr(handle), // [in] HANDLE hNamedPipe,
0, // [out, optional] LPVOID lpBuffer,
0, // [in] DWORD nBufferSize,
0, // [out, optional] LPDWORD lpBytesRead
uintptr(totalBytesPtr), // [out, optional] LPDWORD lpTotalBytesAvail,
0) // [out, optional] LPDWORD lpBytesLeftThisMessage
return totalBytesAvail, errno
}
func rmdir(path string) sys.Errno {
err := syscall.Rmdir(path)
return sys.UnwrapOSError(err)
}
func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, wait bool) (uint32, syscall.Errno) {
var totalBytesAvail uint32
var bwait uintptr
if wait {
bwait = 0xFFFFFFFF
}
totalBytesPtr := unsafe.Pointer(&totalBytesAvail)
_, _, errno := syscall.SyscallN(
procGetOverlappedResult.Addr(),
uintptr(handle), // [in] HANDLE hFile,
uintptr(unsafe.Pointer(overlapped)), // [in] LPOVERLAPPED lpOverlapped,
uintptr(totalBytesPtr), // [out] LPDWORD lpNumberOfBytesTransferred,
bwait) // [in] BOOL bWait
return totalBytesAvail, errno
}
func createEventW(lpEventAttributes *syscall.SecurityAttributes, bManualReset bool, bInitialState bool, lpName *uint16) (uintptr, syscall.Errno) {
var manualReset uintptr
var initialState uintptr
if bManualReset {
manualReset = 1
}
if bInitialState {
initialState = 1
}
handle, _, errno := syscall.SyscallN(
procCreateEventW.Addr(),
uintptr(unsafe.Pointer(lpEventAttributes)), // [in] LPSECURITY_ATTRIBUTES lpEventAttributes,
manualReset, // [in] BOOL bManualReset,
initialState, // [in] BOOL bInitialState,
uintptr(unsafe.Pointer(lpName)), // [in, opt]LPCWSTR lpName,
)
return handle, errno
}