mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-12-11 09:30:59 +01:00
176 lines
6.3 KiB
Go
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
|
||
|
}
|