diff --git a/lib/winapi/Makefile b/lib/winapi/Makefile index 78e993d73..47e8d2f1d 100644 --- a/lib/winapi/Makefile +++ b/lib/winapi/Makefile @@ -10,6 +10,7 @@ WINAPI_SRCS := \ $(NXDK_DIR)/lib/winapi/filemanip.c \ $(NXDK_DIR)/lib/winapi/findfile.c \ $(NXDK_DIR)/lib/winapi/handleapi.c \ + $(NXDK_DIR)/lib/winapi/ioapi.c \ $(NXDK_DIR)/lib/winapi/memory.c \ $(NXDK_DIR)/lib/winapi/libloaderapi.c \ $(NXDK_DIR)/lib/winapi/profiling.c \ diff --git a/lib/winapi/fileio.c b/lib/winapi/fileio.c index 5e600ad46..5eb4bf0a9 100644 --- a/lib/winapi/fileio.c +++ b/lib/winapi/fileio.c @@ -103,12 +103,42 @@ BOOL ReadFile (HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWOR NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; - // overlapped I/O not supported yet - assert(!lpOverlapped); + if (lpOverlapped == NULL) { + // A null pointer makes the code crash on Windows, we'd rather catch it + assert(lpNumberOfBytesRead); + } + + if (lpNumberOfBytesRead) { + *lpNumberOfBytesRead = 0; + } + + if (lpOverlapped) { + LARGE_INTEGER overlappedOffset = { + .LowPart = lpOverlapped->Offset, + .HighPart = lpOverlapped->OffsetHigh, + }; + + // Internal contains the status code for the I/O request. Set to STATUS_PENDING at the start of the request. + lpOverlapped->Internal = (DWORD)STATUS_PENDING; + // InternalHigh contains the actual number of bytes transferred for the I/O request + lpOverlapped->InternalHigh = 0; - // A null pointer makes the code crash on Windows, we'd rather catch it - assert(lpNumberOfBytesRead); - *lpNumberOfBytesRead = 0; + status = NtReadFile(hFile, lpOverlapped->hEvent, NULL, NULL, (PIO_STATUS_BLOCK)&lpOverlapped->Internal, + lpBuffer, nNumberOfBytesToRead, &overlappedOffset); + + // The read can finish immediately. Handle this case + if (NT_SUCCESS(status) && status != STATUS_PENDING) { + if (lpNumberOfBytesRead) { + *lpNumberOfBytesRead = (DWORD)lpOverlapped->InternalHigh; + } + return TRUE; + } else if (status == STATUS_END_OF_FILE) { + *lpNumberOfBytesRead = 0; + return TRUE; + } + SetLastError(RtlNtStatusToDosError(status)); + return FALSE; + } status = NtReadFile(hFile, NULL, NULL, NULL, &ioStatusBlock, lpBuffer, nNumberOfBytesToRead, NULL); @@ -139,12 +169,39 @@ BOOL WriteFile (HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPD NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; - // overlapped I/O not supported yet - assert(!lpOverlapped); + if (lpOverlapped == NULL) { + // A null pointer makes the code crash on Windows, we'd rather catch it + assert(lpNumberOfBytesWritten); + } + + if (lpNumberOfBytesWritten) { + *lpNumberOfBytesWritten = 0; + } + + if (lpOverlapped) { + LARGE_INTEGER overlappedOffset = { + .LowPart = lpOverlapped->Offset, + .HighPart = lpOverlapped->OffsetHigh, + }; + + // Internal contains the status code for the I/O request. Set to STATUS_PENDING at the start of the request + lpOverlapped->Internal = (DWORD)STATUS_PENDING; + // InternalHigh contains the actual number of bytes transferred for the I/O request + lpOverlapped->InternalHigh = 0; + + status = NtWriteFile(hFile, lpOverlapped->hEvent, NULL, NULL, (PIO_STATUS_BLOCK)&lpOverlapped->Internal, + (PVOID)lpBuffer, nNumberOfBytesToWrite, &overlappedOffset); - // A null pointer makes the code crash on Windows, we'd rather catch it - assert(lpNumberOfBytesWritten); - *lpNumberOfBytesWritten = 0; + // The write can finish immediately. Handle this case + if (NT_SUCCESS(status) && status != STATUS_PENDING) { + if (lpNumberOfBytesWritten) { + *lpNumberOfBytesWritten = (DWORD)lpOverlapped->InternalHigh; + } + return TRUE; + } + SetLastError(RtlNtStatusToDosError(status)); + return FALSE; + } status = NtWriteFile(hFile, NULL, NULL, NULL, &ioStatusBlock, (PVOID)lpBuffer, nNumberOfBytesToWrite, NULL); diff --git a/lib/winapi/ioapi.c b/lib/winapi/ioapi.c new file mode 100644 index 000000000..eae069803 --- /dev/null +++ b/lib/winapi/ioapi.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT + +// SPDX-FileCopyrightText: 2023 Ryan Wendland + +#include +#include +#include +#include + +BOOL GetOverlappedResult (HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait) +{ + DWORD status; + HANDLE waitHandle; + + status = (DWORD)lpOverlapped->Internal; + + if (status == STATUS_PENDING && bWait == FALSE) { + SetLastError(ERROR_IO_INCOMPLETE); + return FALSE; + } + + // If the hEvent member of the OVERLAPPED structure is NULL, the system + // uses the state of the hFile handle to signal when the operation has been completed + if (lpOverlapped->hEvent == NULL) { + waitHandle = hFile; + } else { + waitHandle = lpOverlapped->hEvent; + } + + if (status == STATUS_PENDING) { + if (WaitForSingleObject(waitHandle, INFINITE) != WAIT_OBJECT_0) { + SetLastError(ERROR_IO_INCOMPLETE); + return FALSE; + } + // Get final status of the transfer + status = (DWORD)lpOverlapped->Internal; + } + + // InternalHigh contains the actual number of bytes transferred for the I/O request + *lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh; + + if (!NT_SUCCESS(status)) { + SetLastError(RtlNtStatusToDosError(status)); + return FALSE; + } + return TRUE; +} diff --git a/lib/winapi/winbase.h b/lib/winapi/winbase.h index 063596fbe..e7afcd09a 100644 --- a/lib/winapi/winbase.h +++ b/lib/winapi/winbase.h @@ -134,6 +134,8 @@ void WINAPI OutputDebugStringA (LPCTSTR lpOutputString); BOOL IsBadWritePtr (LPVOID lp, UINT_PTR ucb); +BOOL GetOverlappedResult (HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait); + #ifndef UNICODE #define OutputDebugString OutputDebugStringA #else