github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/sysfs/file_windows.go (about) 1 package sysfs 2 3 import ( 4 "errors" 5 "syscall" 6 "unsafe" 7 8 "github.com/tetratelabs/wazero/experimental/sys" 9 ) 10 11 const ( 12 nonBlockingFileReadSupported = true 13 nonBlockingFileWriteSupported = false 14 15 _ERROR_IO_INCOMPLETE = syscall.Errno(996) 16 ) 17 18 var kernel32 = syscall.NewLazyDLL("kernel32.dll") 19 20 // procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe 21 var ( 22 // procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe 23 procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe") 24 // procGetOverlappedResult is the syscall.LazyProc in kernel32 for GetOverlappedResult 25 procGetOverlappedResult = kernel32.NewProc("GetOverlappedResult") 26 // procCreateEventW is the syscall.LazyProc in kernel32 for CreateEventW 27 procCreateEventW = kernel32.NewProc("CreateEventW") 28 ) 29 30 // readFd returns ENOSYS on unsupported platforms. 31 // 32 // PeekNamedPipe: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe 33 // "GetFileType can assist in determining what device type the handle refers to. A console handle presents as FILE_TYPE_CHAR." 34 // https://learn.microsoft.com/en-us/windows/console/console-handles 35 func readFd(fd uintptr, buf []byte) (int, sys.Errno) { 36 handle := syscall.Handle(fd) 37 fileType, err := syscall.GetFileType(handle) 38 if err != nil { 39 return 0, sys.UnwrapOSError(err) 40 } 41 if fileType&syscall.FILE_TYPE_CHAR == 0 { 42 return -1, sys.ENOSYS 43 } 44 n, errno := peekNamedPipe(handle) 45 if errno == syscall.ERROR_BROKEN_PIPE { 46 return 0, 0 47 } 48 if n == 0 { 49 return -1, sys.EAGAIN 50 } 51 un, err := syscall.Read(handle, buf[0:n]) 52 return un, sys.UnwrapOSError(err) 53 } 54 55 func writeFd(fd uintptr, buf []byte) (int, sys.Errno) { 56 return -1, sys.ENOSYS 57 } 58 59 func readSocket(h uintptr, buf []byte) (int, sys.Errno) { 60 // Poll the socket to ensure that we never perform a blocking/overlapped Read. 61 // 62 // When the socket is closed by the remote peer, wsaPoll will return n=1 and 63 // errno=0, and syscall.ReadFile will return n=0 and errno=0 -- which indicates 64 // io.EOF. 65 if n, errno := wsaPoll( 66 []pollFd{newPollFd(h, _POLLIN, 0)}, 0); !errors.Is(errno, sys.Errno(0)) { 67 return 0, sys.UnwrapOSError(errno) 68 } else if n <= 0 { 69 return 0, sys.EAGAIN 70 } 71 72 // Properly use overlapped result. 73 // 74 // If hFile was opened with FILE_FLAG_OVERLAPPED, the following conditions are in effect: 75 // - The lpOverlapped parameter must point to a valid and unique OVERLAPPED structure, 76 // otherwise the function can incorrectly report that the read operation is complete. 77 // - The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult 78 // function to get the actual number of bytes read. If the hFile parameter is associated 79 // with an I/O completion port, you can also get the number of bytes read by calling the 80 // GetQueuedCompletionStatus function. 81 // 82 // We are currently skipping checking if hFile was opened with FILE_FLAG_OVERLAPPED but using 83 // both lpOverlapped and lpNumberOfBytesRead. 84 var overlapped syscall.Overlapped 85 86 // Create an event to wait on. 87 if hEvent, err := createEventW(nil, true, false, nil); err != 0 { 88 return 0, sys.UnwrapOSError(err) 89 } else { 90 overlapped.HEvent = syscall.Handle(hEvent) 91 } 92 93 var done uint32 94 errno := syscall.ReadFile(syscall.Handle(h), buf, &done, &overlapped) 95 if errors.Is(errno, syscall.ERROR_IO_PENDING) { 96 errno = syscall.CancelIo(syscall.Handle(h)) 97 if errno != nil { 98 return 0, sys.UnwrapOSError(errno) // This is a fatal error. CancelIo failed. 99 } 100 101 done, errno = getOverlappedResult(syscall.Handle(h), &overlapped, true) // wait for I/O to complete(cancel or finish). Overwrite done and errno. 102 if errors.Is(errno, syscall.ERROR_OPERATION_ABORTED) { 103 return int(done), sys.EAGAIN // This is one of the expected behavior, I/O was cancelled(completed) before finished. 104 } 105 } 106 107 return int(done), sys.UnwrapOSError(errno) 108 } 109 110 func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) { 111 var done uint32 112 var overlapped syscall.Overlapped 113 errno := syscall.WriteFile(syscall.Handle(fd), buf, &done, &overlapped) 114 if errors.Is(errno, syscall.ERROR_IO_PENDING) { 115 errno = syscall.EAGAIN 116 } 117 return int(done), sys.UnwrapOSError(errno) 118 } 119 120 // peekNamedPipe partially exposes PeekNamedPipe from the Win32 API 121 // see https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe 122 func peekNamedPipe(handle syscall.Handle) (uint32, syscall.Errno) { 123 var totalBytesAvail uint32 124 totalBytesPtr := unsafe.Pointer(&totalBytesAvail) 125 _, _, errno := syscall.SyscallN( 126 procPeekNamedPipe.Addr(), 127 uintptr(handle), // [in] HANDLE hNamedPipe, 128 0, // [out, optional] LPVOID lpBuffer, 129 0, // [in] DWORD nBufferSize, 130 0, // [out, optional] LPDWORD lpBytesRead 131 uintptr(totalBytesPtr), // [out, optional] LPDWORD lpTotalBytesAvail, 132 0) // [out, optional] LPDWORD lpBytesLeftThisMessage 133 return totalBytesAvail, errno 134 } 135 136 func rmdir(path string) sys.Errno { 137 err := syscall.Rmdir(path) 138 return sys.UnwrapOSError(err) 139 } 140 141 func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, wait bool) (uint32, syscall.Errno) { 142 var totalBytesAvail uint32 143 var bwait uintptr 144 if wait { 145 bwait = 0xFFFFFFFF 146 } 147 totalBytesPtr := unsafe.Pointer(&totalBytesAvail) 148 _, _, errno := syscall.SyscallN( 149 procGetOverlappedResult.Addr(), 150 uintptr(handle), // [in] HANDLE hFile, 151 uintptr(unsafe.Pointer(overlapped)), // [in] LPOVERLAPPED lpOverlapped, 152 uintptr(totalBytesPtr), // [out] LPDWORD lpNumberOfBytesTransferred, 153 bwait) // [in] BOOL bWait 154 return totalBytesAvail, errno 155 } 156 157 func createEventW(lpEventAttributes *syscall.SecurityAttributes, bManualReset bool, bInitialState bool, lpName *uint16) (uintptr, syscall.Errno) { 158 var manualReset uintptr 159 var initialState uintptr 160 if bManualReset { 161 manualReset = 1 162 } 163 if bInitialState { 164 initialState = 1 165 } 166 handle, _, errno := syscall.SyscallN( 167 procCreateEventW.Addr(), 168 uintptr(unsafe.Pointer(lpEventAttributes)), // [in] LPSECURITY_ATTRIBUTES lpEventAttributes, 169 manualReset, // [in] BOOL bManualReset, 170 initialState, // [in] BOOL bInitialState, 171 uintptr(unsafe.Pointer(lpName)), // [in, opt]LPCWSTR lpName, 172 ) 173 174 return handle, errno 175 }