mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
Overlapped I/O support (fixes ProDG compilers)
This commit is contained in:
parent
cb154f3118
commit
62d8daccac
@ -45,6 +45,7 @@ add_executable(wibo
|
||||
dll/user32.cpp
|
||||
dll/vcruntime.cpp
|
||||
dll/version.cpp
|
||||
errors.cpp
|
||||
files.cpp
|
||||
handles.cpp
|
||||
loader.cpp
|
||||
@ -151,6 +152,17 @@ if(BUILD_TESTING)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_heap.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-o test_overlapped_io.exe
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_overlapped_io.c
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_overlapped_io.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_target(wibo_test_fixtures
|
||||
DEPENDS
|
||||
${WIBO_TEST_BIN_DIR}/external_exports.dll
|
||||
@ -158,7 +170,8 @@ if(BUILD_TESTING)
|
||||
${WIBO_TEST_BIN_DIR}/test_bcrypt.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_resources.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_threading.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_heap.exe)
|
||||
${WIBO_TEST_BIN_DIR}/test_heap.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe)
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
set(_wibo_fixture_build_command
|
||||
@ -199,6 +212,12 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(wibo.test_heap PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_overlapped_io
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe)
|
||||
set_tests_properties(wibo.test_overlapped_io PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
55
common.h
55
common.h
@ -60,55 +60,30 @@ typedef size_t SIZE_T;
|
||||
typedef SIZE_T *PSIZE_T;
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
typedef struct _OVERLAPPED {
|
||||
ULONG_PTR Internal;
|
||||
ULONG_PTR InternalHigh;
|
||||
union {
|
||||
struct {
|
||||
DWORD Offset;
|
||||
DWORD OffsetHigh;
|
||||
};
|
||||
PVOID Pointer;
|
||||
};
|
||||
HANDLE hEvent;
|
||||
} OVERLAPPED, *LPOVERLAPPED;
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define ERROR_SUCCESS 0
|
||||
#define ERROR_FILE_NOT_FOUND 2
|
||||
#define ERROR_PATH_NOT_FOUND 3
|
||||
#define ERROR_ACCESS_DENIED 5
|
||||
#define ERROR_INVALID_HANDLE 6
|
||||
#define ERROR_NOT_ENOUGH_MEMORY 8
|
||||
#define ERROR_NO_MORE_FILES 18
|
||||
#define ERROR_READ_FAULT 30
|
||||
#define ERROR_HANDLE_EOF 38
|
||||
#define ERROR_NOT_SUPPORTED 50
|
||||
#define ERROR_INVALID_PARAMETER 87
|
||||
#define ERROR_CALL_NOT_IMPLEMENTED 120
|
||||
#define ERROR_BUFFER_OVERFLOW 111
|
||||
#define ERROR_INSUFFICIENT_BUFFER 122
|
||||
#define ERROR_NONE_MAPPED 1332
|
||||
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
||||
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
||||
#define ERROR_RESOURCE_NAME_NOT_FOUND 1814
|
||||
#define ERROR_RESOURCE_LANG_NOT_FOUND 1815
|
||||
#define ERROR_MOD_NOT_FOUND 126
|
||||
#define ERROR_PROC_NOT_FOUND 127
|
||||
#define ERROR_NEGATIVE_SEEK 131
|
||||
#define ERROR_BAD_EXE_FORMAT 193
|
||||
#define ERROR_ALREADY_EXISTS 183
|
||||
#define ERROR_NOT_OWNER 288
|
||||
|
||||
#define STILL_ACTIVE 259
|
||||
|
||||
#define TIME_ZONE_ID_UNKNOWN 0
|
||||
#define TIME_ZONE_ID_STANDARD 1
|
||||
#define TIME_ZONE_ID_DAYLIGHT 2
|
||||
|
||||
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
||||
#define INVALID_HANDLE_VALUE ((HANDLE)-1)
|
||||
|
||||
typedef int NTSTATUS;
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000)
|
||||
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008)
|
||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000D)
|
||||
#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002)
|
||||
#define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011)
|
||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
|
||||
#define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS)0xC00000E9)
|
||||
|
||||
typedef int HRESULT;
|
||||
#define S_OK ((HRESULT)0x00000000)
|
||||
#define FILE_FLAG_OVERLAPPED 0x40000000
|
||||
#define FILE_FLAG_NO_BUFFERING 0x20000000
|
||||
|
||||
#define MAX_PATH (260)
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "handles.h"
|
||||
#include "strutil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sys/random.h>
|
||||
#include <vector>
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
|
||||
#include <sys/random.h>
|
||||
#include <vector>
|
||||
|
265
dll/kernel32.cpp
265
dll/kernel32.cpp
@ -1,5 +1,6 @@
|
||||
#include "common.h"
|
||||
#include "files.h"
|
||||
#include "errors.h"
|
||||
#include "processes.h"
|
||||
#include "handles.h"
|
||||
#include "resources.h"
|
||||
@ -454,26 +455,42 @@ namespace kernel32 {
|
||||
}
|
||||
|
||||
void setLastErrorFromErrno() {
|
||||
switch (errno) {
|
||||
case 0:
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
break;
|
||||
case EACCES:
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EEXIST:
|
||||
wibo::lastError = ERROR_ALREADY_EXISTS;
|
||||
break;
|
||||
case ENOENT:
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
wibo::lastError = ERROR_PATH_NOT_FOUND;
|
||||
break;
|
||||
default:
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
break;
|
||||
wibo::lastError = wibo::winErrorFromErrno(errno);
|
||||
}
|
||||
|
||||
static void setEventSignaledState(HANDLE hEvent, bool signaled) {
|
||||
if (!hEvent) {
|
||||
return;
|
||||
}
|
||||
auto data = handles::dataFromHandle(hEvent, false);
|
||||
if (data.type != handles::TYPE_EVENT || data.ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
EventObject *obj = reinterpret_cast<EventObject *>(data.ptr);
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
obj->signaled = signaled;
|
||||
if (signaled) {
|
||||
if (obj->manualReset) {
|
||||
pthread_cond_broadcast(&obj->cond);
|
||||
} else {
|
||||
pthread_cond_signal(&obj->cond);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
}
|
||||
|
||||
static void resetOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (!ov || !ov->hEvent) {
|
||||
return;
|
||||
}
|
||||
setEventSignaledState(ov->hEvent, false);
|
||||
}
|
||||
|
||||
static void signalOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (!ov || !ov->hEvent) {
|
||||
return;
|
||||
}
|
||||
setEventSignaledState(ov->hEvent, true);
|
||||
}
|
||||
|
||||
uint32_t WIN_FUNC GetLastError() {
|
||||
@ -1244,10 +1261,10 @@ namespace kernel32 {
|
||||
(void)dwDesiredAccess;
|
||||
(void)bInheritHandle;
|
||||
(void)dwOptions;
|
||||
FILE *fp = files::fpFromHandle(hSourceHandle);
|
||||
if (fp == stdin || fp == stdout || fp == stderr) {
|
||||
auto file = files::fileHandleFromHandle(hSourceHandle);
|
||||
if (file && (file->fp == stdin || file->fp == stdout || file->fp == stderr)) {
|
||||
// we never close standard handles so they are fine to duplicate
|
||||
void *handle = files::allocFpHandle(fp);
|
||||
void *handle = files::duplicateFileHandle(file, false);
|
||||
DEBUG_LOG("-> %p\n", handle);
|
||||
*lpTargetHandle = handle;
|
||||
return 1;
|
||||
@ -1261,9 +1278,13 @@ namespace kernel32 {
|
||||
DEBUG_LOG("CloseHandle(%p)\n", hObject);
|
||||
auto data = handles::dataFromHandle(hObject, true);
|
||||
if (data.type == handles::TYPE_FILE) {
|
||||
FILE *fp = (FILE *) data.ptr;
|
||||
if (!(fp == stdin || fp == stdout || fp == stderr)) {
|
||||
fclose(fp);
|
||||
auto file = reinterpret_cast<files::FileHandle *>(data.ptr);
|
||||
if (file) {
|
||||
if (file->closeOnDestroy && file->fp &&
|
||||
!(file->fp == stdin || file->fp == stdout || file->fp == stderr)) {
|
||||
fclose(file->fp);
|
||||
}
|
||||
delete file;
|
||||
}
|
||||
} else if (data.type == handles::TYPE_MAPPED) {
|
||||
auto *mapping = reinterpret_cast<MappingObject *>(data.ptr);
|
||||
@ -1272,7 +1293,7 @@ namespace kernel32 {
|
||||
tryReleaseMapping(mapping);
|
||||
}
|
||||
} else if (data.type == handles::TYPE_PROCESS) {
|
||||
delete (processes::Process*) data.ptr;
|
||||
delete (processes::Process *)data.ptr;
|
||||
} else if (data.type == handles::TYPE_TOKEN) {
|
||||
advapi32::releaseToken(data.ptr);
|
||||
} else if (data.type == handles::TYPE_MUTEX) {
|
||||
@ -1820,27 +1841,66 @@ namespace kernel32 {
|
||||
}
|
||||
|
||||
unsigned int WIN_FUNC WriteFile(void *hFile, const void *lpBuffer, unsigned int nNumberOfBytesToWrite, unsigned int *lpNumberOfBytesWritten, void *lpOverlapped) {
|
||||
DEBUG_LOG("WriteFile(%p, %d)\n", hFile, nNumberOfBytesToWrite);
|
||||
assert(!lpOverlapped);
|
||||
wibo::lastError = 0;
|
||||
DEBUG_LOG("WriteFile(%p, %u)\n", hFile, nNumberOfBytesToWrite);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
size_t written = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, fp);
|
||||
if (lpNumberOfBytesWritten)
|
||||
*lpNumberOfBytesWritten = written;
|
||||
|
||||
#if 0
|
||||
printf("writing:\n");
|
||||
for (unsigned int i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
printf("%c", ((const char*)lpBuffer)[i]);
|
||||
auto file = files::fileHandleFromHandle(hFile);
|
||||
if (!file || !file->fp) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
if (written == 0)
|
||||
wibo::lastError = 29; // ERROR_WRITE_FAULT
|
||||
bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||
auto *overlapped = reinterpret_cast<OVERLAPPED *>(lpOverlapped);
|
||||
bool usingOverlapped = overlapped != nullptr;
|
||||
if (!usingOverlapped && lpNumberOfBytesWritten == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return (written == nNumberOfBytesToWrite);
|
||||
std::optional<uint64_t> offset;
|
||||
bool updateFilePointer = true;
|
||||
if (usingOverlapped) {
|
||||
offset = (static_cast<uint64_t>(overlapped->Offset) | (static_cast<uint64_t>(overlapped->OffsetHigh) << 32));
|
||||
overlapped->Internal = STATUS_PENDING;
|
||||
overlapped->InternalHigh = 0;
|
||||
updateFilePointer = !handleOverlapped;
|
||||
resetOverlappedEvent(overlapped);
|
||||
}
|
||||
|
||||
auto io = files::write(file, lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer);
|
||||
DWORD completionStatus = STATUS_SUCCESS;
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::winErrorFromErrno(io.unixError);
|
||||
wibo::lastError = completionStatus;
|
||||
if (lpNumberOfBytesWritten) {
|
||||
*lpNumberOfBytesWritten = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(overlapped);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpNumberOfBytesWritten && (!handleOverlapped || !usingOverlapped)) {
|
||||
*lpNumberOfBytesWritten = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
if (!handleOverlapped) {
|
||||
uint64_t baseOffset = offset.value_or(0);
|
||||
uint64_t newOffset = baseOffset + io.bytesTransferred;
|
||||
overlapped->Offset = static_cast<DWORD>(newOffset & 0xFFFFFFFFu);
|
||||
overlapped->OffsetHigh = static_cast<DWORD>(newOffset >> 32);
|
||||
}
|
||||
signalOverlappedEvent(overlapped);
|
||||
}
|
||||
|
||||
return (io.bytesTransferred == nNumberOfBytesToWrite);
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC FlushFileBuffers(HANDLE hFile) {
|
||||
@ -1850,12 +1910,17 @@ namespace kernel32 {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
FILE *fp = reinterpret_cast<FILE *>(data.ptr);
|
||||
auto file = reinterpret_cast<files::FileHandle *>(data.ptr);
|
||||
if (!file || !file->fp) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
FILE *fp = file->fp;
|
||||
if (fflush(fp) != 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
}
|
||||
int fd = fileno(fp);
|
||||
int fd = file->fd;
|
||||
if (fd >= 0 && fsync(fd) != 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
@ -1865,14 +1930,70 @@ namespace kernel32 {
|
||||
}
|
||||
|
||||
unsigned int WIN_FUNC ReadFile(void *hFile, void *lpBuffer, unsigned int nNumberOfBytesToRead, unsigned int *lpNumberOfBytesRead, void *lpOverlapped) {
|
||||
DEBUG_LOG("ReadFile %p %d\n", hFile, nNumberOfBytesToRead);
|
||||
assert(!lpOverlapped);
|
||||
wibo::lastError = 0;
|
||||
DEBUG_LOG("ReadFile(%p, %u)\n", hFile, nNumberOfBytesToRead);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
size_t read = fread(lpBuffer, 1, nNumberOfBytesToRead, fp);
|
||||
*lpNumberOfBytesRead = read;
|
||||
return 1;
|
||||
auto file = files::fileHandleFromHandle(hFile);
|
||||
if (!file || !file->fp) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||
auto *overlapped = reinterpret_cast<OVERLAPPED *>(lpOverlapped);
|
||||
bool usingOverlapped = overlapped != nullptr;
|
||||
if (!usingOverlapped && lpNumberOfBytesRead == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> offset;
|
||||
bool updateFilePointer = true;
|
||||
if (usingOverlapped) {
|
||||
offset = (static_cast<uint64_t>(overlapped->Offset) | (static_cast<uint64_t>(overlapped->OffsetHigh) << 32));
|
||||
overlapped->Internal = STATUS_PENDING;
|
||||
overlapped->InternalHigh = 0;
|
||||
updateFilePointer = !handleOverlapped;
|
||||
resetOverlappedEvent(overlapped);
|
||||
}
|
||||
|
||||
auto io = files::read(file, lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer);
|
||||
DWORD completionStatus = STATUS_SUCCESS;
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::winErrorFromErrno(io.unixError);
|
||||
wibo::lastError = completionStatus;
|
||||
if (lpNumberOfBytesRead) {
|
||||
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(overlapped);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (io.reachedEnd && io.bytesTransferred == 0 && handleOverlapped) {
|
||||
completionStatus = ERROR_HANDLE_EOF;
|
||||
}
|
||||
|
||||
if (lpNumberOfBytesRead && (!handleOverlapped || !usingOverlapped)) {
|
||||
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
if (!handleOverlapped) {
|
||||
uint64_t baseOffset = offset.value_or(0);
|
||||
uint64_t newOffset = baseOffset + io.bytesTransferred;
|
||||
overlapped->Offset = static_cast<DWORD>(newOffset & 0xFFFFFFFFu);
|
||||
overlapped->OffsetHigh = static_cast<DWORD>(newOffset >> 32);
|
||||
}
|
||||
signalOverlappedEvent(overlapped);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
enum {
|
||||
@ -1961,7 +2082,7 @@ namespace kernel32 {
|
||||
}
|
||||
|
||||
if (fp) {
|
||||
void *handle = files::allocFpHandle(fp);
|
||||
void *handle = files::allocFpHandle(fp, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, true);
|
||||
DEBUG_LOG("-> %p\n", handle);
|
||||
return handle;
|
||||
} else {
|
||||
@ -4665,8 +4786,42 @@ namespace kernel32 {
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC GetOverlappedResult(void *hFile, void *lpOverlapped, int *lpNumberOfBytesTransferred, BOOL bWait) {
|
||||
// DEBUG_LOG("GetOverlappedResult(%p, %p, %p, %u)\n", hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait);
|
||||
return 1;
|
||||
(void)hFile;
|
||||
OVERLAPPED *overlapped = reinterpret_cast<OVERLAPPED *>(lpOverlapped);
|
||||
if (!overlapped) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
if (bWait && overlapped->Internal == STATUS_PENDING) {
|
||||
if (overlapped->hEvent) {
|
||||
WaitForSingleObject(overlapped->hEvent, 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD status = static_cast<DWORD>(overlapped->Internal);
|
||||
if (status == STATUS_PENDING) {
|
||||
wibo::lastError = ERROR_IO_INCOMPLETE;
|
||||
if (lpNumberOfBytesTransferred) {
|
||||
*lpNumberOfBytesTransferred = static_cast<int>(overlapped->InternalHigh);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpNumberOfBytesTransferred) {
|
||||
*lpNumberOfBytesTransferred = static_cast<int>(overlapped->InternalHigh);
|
||||
}
|
||||
|
||||
if (status == STATUS_SUCCESS) {
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
if (status == STATUS_END_OF_FILE || status == ERROR_HANDLE_EOF) {
|
||||
wibo::lastError = ERROR_HANDLE_EOF;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wibo::lastError = status;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#define PIO_APC_ROUTINE void *
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
@ -13,6 +16,11 @@ typedef struct _IO_STATUS_BLOCK {
|
||||
ULONG_PTR Information;
|
||||
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
|
||||
|
||||
namespace kernel32 {
|
||||
BOOL WIN_FUNC SetEvent(HANDLE hEvent);
|
||||
BOOL WIN_FUNC ResetEvent(HANDLE hEvent);
|
||||
} // namespace kernel32
|
||||
|
||||
namespace ntdll {
|
||||
|
||||
NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
|
||||
@ -20,34 +28,55 @@ NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE Ap
|
||||
PULONG Key) {
|
||||
DEBUG_LOG("NtReadFile(%p, %p, %p, %p, %p, %p, %u, %p, %p) ", FileHandle, Event, ApcRoutine, ApcContext,
|
||||
IoStatusBlock, Buffer, Length, ByteOffset, Key);
|
||||
assert(Event == nullptr);
|
||||
assert(ApcRoutine == nullptr);
|
||||
assert(ApcContext == nullptr);
|
||||
assert(ByteOffset == nullptr);
|
||||
assert(Key == nullptr);
|
||||
(void)ApcRoutine;
|
||||
(void)ApcContext;
|
||||
(void)Key;
|
||||
|
||||
wibo::lastError = 0;
|
||||
FILE *fp = files::fpFromHandle(FileHandle);
|
||||
if (!fp) {
|
||||
if (!IoStatusBlock) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto file = files::fileHandleFromHandle(FileHandle);
|
||||
if (!file || !file->fp) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
||||
IoStatusBlock->Information = 0;
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
size_t read = fread(Buffer, 1, Length, fp);
|
||||
bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||
std::optional<uint64_t> offset;
|
||||
bool updateFilePointer = !handleOverlapped;
|
||||
if (ByteOffset) {
|
||||
offset = static_cast<uint64_t>(*ByteOffset);
|
||||
} else if (handleOverlapped) {
|
||||
updateFilePointer = false;
|
||||
}
|
||||
|
||||
if (Event) {
|
||||
kernel32::ResetEvent(Event);
|
||||
}
|
||||
|
||||
auto io = files::read(file, Buffer, Length, offset, updateFilePointer);
|
||||
DWORD winError = ERROR_SUCCESS;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
if (read < Length) {
|
||||
if (feof(fp)) {
|
||||
wibo::lastError = ERROR_HANDLE_EOF;
|
||||
status = STATUS_END_OF_FILE;
|
||||
} else {
|
||||
wibo::lastError = ERROR_READ_FAULT; // ?
|
||||
status = STATUS_UNEXPECTED_IO_ERROR;
|
||||
}
|
||||
if (io.unixError != 0) {
|
||||
winError = wibo::winErrorFromErrno(io.unixError);
|
||||
status = wibo::statusFromWinError(winError);
|
||||
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
||||
winError = ERROR_HANDLE_EOF;
|
||||
status = STATUS_END_OF_FILE;
|
||||
}
|
||||
if (IoStatusBlock) {
|
||||
IoStatusBlock->Status = status;
|
||||
IoStatusBlock->Information = read;
|
||||
|
||||
IoStatusBlock->Status = status;
|
||||
IoStatusBlock->Information = static_cast<ULONG_PTR>(io.bytesTransferred);
|
||||
wibo::lastError = winError;
|
||||
|
||||
if (Event) {
|
||||
kernel32::SetEvent(Event);
|
||||
}
|
||||
|
||||
DEBUG_LOG("-> 0x%x\n", status);
|
||||
return status;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "handles.h"
|
||||
|
||||
namespace psapi {
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
#include "resources.h"
|
||||
#include "strutil.h"
|
||||
|
53
errors.cpp
Normal file
53
errors.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
|
||||
#include "errors.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
namespace wibo {
|
||||
|
||||
DWORD winErrorFromErrno(int err) {
|
||||
switch (err) {
|
||||
case 0:
|
||||
return ERROR_SUCCESS;
|
||||
case EACCES:
|
||||
return ERROR_ACCESS_DENIED;
|
||||
case EEXIST:
|
||||
return ERROR_ALREADY_EXISTS;
|
||||
case ENOENT:
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
case ENOTDIR:
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case ENOMEM:
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
case EINVAL:
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
case EINTR:
|
||||
return ERROR_OPERATION_ABORTED;
|
||||
case EIO:
|
||||
return ERROR_READ_FAULT;
|
||||
case EPIPE:
|
||||
return ERROR_BROKEN_PIPE;
|
||||
case ESPIPE:
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
default:
|
||||
DEBUG_LOG("Unhandled errno %d -> ERROR_NOT_SUPPORTED\n", err);
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS statusFromWinError(DWORD error) {
|
||||
switch (error) {
|
||||
case ERROR_SUCCESS:
|
||||
return STATUS_SUCCESS;
|
||||
case ERROR_INVALID_HANDLE:
|
||||
return STATUS_INVALID_HANDLE;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
case ERROR_HANDLE_EOF:
|
||||
return STATUS_END_OF_FILE;
|
||||
default:
|
||||
return STATUS_UNEXPECTED_IO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wibo
|
54
errors.h
Normal file
54
errors.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define ERROR_SUCCESS 0
|
||||
#define ERROR_FILE_NOT_FOUND 2
|
||||
#define ERROR_PATH_NOT_FOUND 3
|
||||
#define ERROR_ACCESS_DENIED 5
|
||||
#define ERROR_INVALID_HANDLE 6
|
||||
#define ERROR_NOT_ENOUGH_MEMORY 8
|
||||
#define ERROR_NO_MORE_FILES 18
|
||||
#define ERROR_READ_FAULT 30
|
||||
#define ERROR_HANDLE_EOF 38
|
||||
#define ERROR_BROKEN_PIPE 109
|
||||
#define ERROR_NOT_SUPPORTED 50
|
||||
#define ERROR_INVALID_PARAMETER 87
|
||||
#define ERROR_CALL_NOT_IMPLEMENTED 120
|
||||
#define ERROR_BUFFER_OVERFLOW 111
|
||||
#define ERROR_INSUFFICIENT_BUFFER 122
|
||||
#define ERROR_IO_INCOMPLETE 996
|
||||
#define ERROR_IO_PENDING 997
|
||||
#define ERROR_OPERATION_ABORTED 995
|
||||
#define ERROR_NONE_MAPPED 1332
|
||||
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
||||
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
||||
#define ERROR_RESOURCE_NAME_NOT_FOUND 1814
|
||||
#define ERROR_RESOURCE_LANG_NOT_FOUND 1815
|
||||
#define ERROR_MOD_NOT_FOUND 126
|
||||
#define ERROR_PROC_NOT_FOUND 127
|
||||
#define ERROR_NEGATIVE_SEEK 131
|
||||
#define ERROR_BAD_EXE_FORMAT 193
|
||||
#define ERROR_ALREADY_EXISTS 183
|
||||
#define ERROR_NOT_OWNER 288
|
||||
|
||||
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
||||
#define INVALID_HANDLE_VALUE ((HANDLE)-1)
|
||||
|
||||
typedef int NTSTATUS;
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000)
|
||||
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008)
|
||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000D)
|
||||
#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002)
|
||||
#define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011)
|
||||
#define STATUS_PENDING ((NTSTATUS)0x00000103)
|
||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
|
||||
#define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS)0xC00000E9)
|
||||
|
||||
typedef int HRESULT;
|
||||
#define S_OK ((HRESULT)0x00000000)
|
||||
|
||||
namespace wibo {
|
||||
DWORD winErrorFromErrno(int err);
|
||||
NTSTATUS statusFromWinError(DWORD error);
|
||||
}
|
178
files.cpp
178
files.cpp
@ -3,6 +3,7 @@
|
||||
#include "handles.h"
|
||||
#include "strutil.h"
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <strings.h>
|
||||
@ -120,20 +121,183 @@ namespace files {
|
||||
return str;
|
||||
}
|
||||
|
||||
FileHandle *fileHandleFromHandle(void *handle) {
|
||||
handles::Data data = handles::dataFromHandle(handle, false);
|
||||
if (data.type != handles::TYPE_FILE) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<FileHandle *>(data.ptr);
|
||||
}
|
||||
|
||||
FILE *fpFromHandle(void *handle, bool pop) {
|
||||
handles::Data data = handles::dataFromHandle(handle, pop);
|
||||
if (data.type == handles::TYPE_FILE) {
|
||||
return (FILE*)data.ptr;
|
||||
return reinterpret_cast<FileHandle *>(data.ptr)->fp;
|
||||
} else if (data.type == handles::TYPE_UNUSED && pop) {
|
||||
return 0;
|
||||
return nullptr;
|
||||
} else {
|
||||
printf("Invalid file handle %p\n", handle);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void *allocFpHandle(FILE *fp) {
|
||||
return handles::allocDataHandle(handles::Data{handles::TYPE_FILE, fp, 0});
|
||||
void *allocFpHandle(FILE *fp, unsigned int desiredAccess, unsigned int shareMode, unsigned int flags, bool closeOnDestroy) {
|
||||
auto *handle = new FileHandle();
|
||||
handle->fp = fp;
|
||||
handle->fd = (fp ? fileno(fp) : -1);
|
||||
handle->desiredAccess = desiredAccess;
|
||||
handle->shareMode = shareMode;
|
||||
handle->flags = flags;
|
||||
handle->closeOnDestroy = closeOnDestroy;
|
||||
return handles::allocDataHandle({handles::TYPE_FILE, handle, 0});
|
||||
}
|
||||
|
||||
void *duplicateFileHandle(FileHandle *source, bool closeOnDestroy) {
|
||||
if (!source) {
|
||||
return nullptr;
|
||||
}
|
||||
auto *clone = new FileHandle();
|
||||
clone->fp = source->fp;
|
||||
clone->fd = source->fd;
|
||||
clone->desiredAccess = source->desiredAccess;
|
||||
clone->shareMode = source->shareMode;
|
||||
clone->flags = source->flags;
|
||||
clone->closeOnDestroy = closeOnDestroy;
|
||||
return handles::allocDataHandle({handles::TYPE_FILE, clone, 0});
|
||||
}
|
||||
|
||||
IOResult read(FileHandle *handle, void *buffer, size_t bytesToRead, const std::optional<uint64_t> &offset, bool updateFilePointer) {
|
||||
IOResult result{};
|
||||
if (!handle || !handle->fp) {
|
||||
result.unixError = EBADF;
|
||||
return result;
|
||||
}
|
||||
if (bytesToRead == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool useOffset = offset.has_value();
|
||||
if (useOffset && !updateFilePointer && handle->fd >= 0) {
|
||||
size_t total = 0;
|
||||
size_t remaining = bytesToRead;
|
||||
off_t pos = static_cast<off_t>(*offset);
|
||||
while (remaining > 0) {
|
||||
ssize_t rc = pread(handle->fd, static_cast<uint8_t *>(buffer) + total, remaining, pos);
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
result.unixError = errno ? errno : EIO;
|
||||
return result;
|
||||
}
|
||||
if (rc == 0) {
|
||||
result.bytesTransferred = total;
|
||||
result.reachedEnd = true;
|
||||
return result;
|
||||
}
|
||||
total += static_cast<size_t>(rc);
|
||||
remaining -= static_cast<size_t>(rc);
|
||||
pos += rc;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t originalPos = -1;
|
||||
std::unique_lock<std::mutex> lock(handle->mutex);
|
||||
if (useOffset) {
|
||||
originalPos = ftello(handle->fp);
|
||||
if (!updateFilePointer && originalPos == -1) {
|
||||
result.unixError = errno ? errno : ESPIPE;
|
||||
return result;
|
||||
}
|
||||
if (fseeko(handle->fp, static_cast<off_t>(*offset), SEEK_SET) != 0) {
|
||||
result.unixError = errno ? errno : EINVAL;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
size_t readCount = fread(buffer, 1, bytesToRead, handle->fp);
|
||||
result.bytesTransferred = readCount;
|
||||
if (readCount < bytesToRead) {
|
||||
if (feof(handle->fp)) {
|
||||
result.reachedEnd = true;
|
||||
clearerr(handle->fp);
|
||||
} else if (ferror(handle->fp)) {
|
||||
result.unixError = errno ? errno : EIO;
|
||||
clearerr(handle->fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (useOffset && !updateFilePointer) {
|
||||
if (originalPos != -1) {
|
||||
fseeko(handle->fp, originalPos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
IOResult write(FileHandle *handle, const void *buffer, size_t bytesToWrite, const std::optional<uint64_t> &offset, bool updateFilePointer) {
|
||||
IOResult result{};
|
||||
if (!handle || !handle->fp) {
|
||||
result.unixError = EBADF;
|
||||
return result;
|
||||
}
|
||||
if (bytesToWrite == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool useOffset = offset.has_value();
|
||||
if (useOffset && !updateFilePointer && handle->fd >= 0) {
|
||||
size_t total = 0;
|
||||
size_t remaining = bytesToWrite;
|
||||
off_t pos = static_cast<off_t>(*offset);
|
||||
while (remaining > 0) {
|
||||
ssize_t rc = pwrite(handle->fd, static_cast<const uint8_t *>(buffer) + total, remaining, pos);
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
result.unixError = errno ? errno : EIO;
|
||||
return result;
|
||||
}
|
||||
total += static_cast<size_t>(rc);
|
||||
remaining -= static_cast<size_t>(rc);
|
||||
pos += rc;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t originalPos = -1;
|
||||
std::unique_lock<std::mutex> lock(handle->mutex);
|
||||
if (useOffset) {
|
||||
originalPos = ftello(handle->fp);
|
||||
if (!updateFilePointer && originalPos == -1) {
|
||||
result.unixError = errno ? errno : ESPIPE;
|
||||
return result;
|
||||
}
|
||||
if (fseeko(handle->fp, static_cast<off_t>(*offset), SEEK_SET) != 0) {
|
||||
result.unixError = errno ? errno : EINVAL;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
size_t writeCount = fwrite(buffer, 1, bytesToWrite, handle->fp);
|
||||
result.bytesTransferred = writeCount;
|
||||
if (writeCount < bytesToWrite) {
|
||||
result.unixError = errno ? errno : EIO;
|
||||
clearerr(handle->fp);
|
||||
}
|
||||
|
||||
if (useOffset && !updateFilePointer) {
|
||||
if (originalPos != -1) {
|
||||
fseeko(handle->fp, originalPos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *getStdHandle(uint32_t nStdHandle) {
|
||||
@ -167,9 +331,9 @@ namespace files {
|
||||
}
|
||||
|
||||
void init() {
|
||||
stdinHandle = allocFpHandle(stdin);
|
||||
stdoutHandle = allocFpHandle(stdout);
|
||||
stderrHandle = allocFpHandle(stderr);
|
||||
stdinHandle = allocFpHandle(stdin, 0, 0, 0, false);
|
||||
stdoutHandle = allocFpHandle(stdout, 0, 0, 0, false);
|
||||
stderrHandle = allocFpHandle(stderr, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
|
||||
|
24
files.h
24
files.h
@ -1,14 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace files {
|
||||
struct FileHandle {
|
||||
FILE *fp = nullptr;
|
||||
int fd = -1;
|
||||
unsigned int desiredAccess = 0;
|
||||
unsigned int shareMode = 0;
|
||||
unsigned int flags = 0;
|
||||
bool closeOnDestroy = true;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
struct IOResult {
|
||||
size_t bytesTransferred = 0;
|
||||
int unixError = 0;
|
||||
bool reachedEnd = false;
|
||||
};
|
||||
|
||||
std::filesystem::path pathFromWindows(const char *inStr);
|
||||
std::string pathToWindows(const std::filesystem::path &path);
|
||||
void *allocFpHandle(FILE *fp);
|
||||
void *allocFpHandle(FILE *fp, unsigned int desiredAccess = 0, unsigned int shareMode = 0, unsigned int flags = 0, bool closeOnDestroy = true);
|
||||
void *duplicateFileHandle(FileHandle *handle, bool closeOnDestroy);
|
||||
FILE *fpFromHandle(void *handle, bool pop = false);
|
||||
FileHandle *fileHandleFromHandle(void *handle);
|
||||
IOResult read(FileHandle *handle, void *buffer, size_t bytesToRead, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
IOResult write(FileHandle *handle, const void *buffer, size_t bytesToWrite, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
void *getStdHandle(uint32_t nStdHandle);
|
||||
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle);
|
||||
void init();
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
#include "strutil.h"
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "resources.h"
|
||||
#include "errors.h"
|
||||
#include "common.h"
|
||||
|
||||
namespace {
|
||||
|
165
test/test_overlapped_io.c
Normal file
165
test/test_overlapped_io.c
Normal file
@ -0,0 +1,165 @@
|
||||
#include "test_assert.h"
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef STATUS_PENDING
|
||||
#define STATUS_PENDING ((DWORD)0x00000103)
|
||||
#endif
|
||||
|
||||
static const char *kFilename = "overlapped_test.tmp";
|
||||
|
||||
static void write_fixture_file(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_WRITE | GENERIC_READ, 0, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
const char contents[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
DWORD written = 0;
|
||||
TEST_CHECK(WriteFile(file, contents, (DWORD)(sizeof(contents) - 1), &written, NULL));
|
||||
TEST_CHECK_EQ(sizeof(contents) - 1, written);
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_synchronous_overlapped_read(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 5;
|
||||
|
||||
char buffer[16] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
TEST_CHECK(ReadFile(file, buffer, 7, &bytesRead, &ov));
|
||||
TEST_CHECK_EQ(7, bytesRead);
|
||||
buffer[7] = '\0';
|
||||
TEST_CHECK_STR_EQ("56789AB", buffer);
|
||||
|
||||
DWORD pos = SetFilePointer(file, 0, NULL, FILE_CURRENT);
|
||||
TEST_CHECK_EQ(12, (int)pos);
|
||||
|
||||
unsigned long long trackedOffset = ((unsigned long long)ov.OffsetHigh << 32) | ov.Offset;
|
||||
// Wine leaves OVERLAPPED.Offset unchanged for synchronous handles, even though the Win32 docs state the runtime should advance both the
|
||||
// file pointer and the OVERLAPPED offsets. We intentionally skip asserting the
|
||||
// offset here so the fixture passes under both wibo and wine.
|
||||
// TEST_CHECK_U64_EQ(12ULL, trackedOffset);
|
||||
(void)trackedOffset;
|
||||
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_overlapped_read_with_event(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 10;
|
||||
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
char buffer[16] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, 6, NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(6U, transferred);
|
||||
buffer[6] = '\0';
|
||||
TEST_CHECK_STR_EQ("ABCDEF", buffer);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_overlapped_eof(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 80; /* beyond end */
|
||||
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
char buffer[8] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, sizeof(buffer), NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 1234;
|
||||
TEST_CHECK(!GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(ERROR_HANDLE_EOF, GetLastError());
|
||||
TEST_CHECK_EQ(0U, transferred);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_getoverlappedresult_pending(void) {
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Internal = STATUS_PENDING;
|
||||
ov.InternalHigh = 42;
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(!GetOverlappedResult(NULL, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(ERROR_IO_INCOMPLETE, GetLastError());
|
||||
// Wine leaves the caller-supplied transfer count untouched for the
|
||||
// pending case, so we avoid asserting on the value here.
|
||||
// TEST_CHECK_EQ(42U, transferred);
|
||||
}
|
||||
|
||||
static void test_overlapped_write(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 2;
|
||||
ov.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
const char patch[] = "zz";
|
||||
BOOL issued = WriteFile(file, patch, (DWORD)(sizeof(patch) - 1), NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ((DWORD)(sizeof(patch) - 1), transferred);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
|
||||
HANDLE verify = CreateFileA(kFilename, GENERIC_READ, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(verify != INVALID_HANDLE_VALUE);
|
||||
|
||||
TEST_CHECK(SetFilePointer(verify, 2, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||
char buffer[3] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
TEST_CHECK(ReadFile(verify, buffer, sizeof(patch) - 1, &bytesRead, NULL));
|
||||
TEST_CHECK_EQ((DWORD)(sizeof(patch) - 1), bytesRead);
|
||||
TEST_CHECK(buffer[0] == 'z' && buffer[1] == 'z');
|
||||
|
||||
TEST_CHECK(CloseHandle(verify));
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
DeleteFileA(kFilename);
|
||||
write_fixture_file();
|
||||
test_synchronous_overlapped_read();
|
||||
test_overlapped_read_with_event();
|
||||
test_overlapped_eof();
|
||||
test_getoverlappedresult_pending();
|
||||
test_overlapped_write();
|
||||
TEST_CHECK(DeleteFileA(kFilename));
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user