mirror of
https://github.com/decompals/wibo.git
synced 2025-10-16 15:15:10 +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/user32.cpp
|
||||||
dll/vcruntime.cpp
|
dll/vcruntime.cpp
|
||||||
dll/version.cpp
|
dll/version.cpp
|
||||||
|
errors.cpp
|
||||||
files.cpp
|
files.cpp
|
||||||
handles.cpp
|
handles.cpp
|
||||||
loader.cpp
|
loader.cpp
|
||||||
@ -151,6 +152,17 @@ if(BUILD_TESTING)
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_heap.c
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_heap.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
${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
|
add_custom_target(wibo_test_fixtures
|
||||||
DEPENDS
|
DEPENDS
|
||||||
${WIBO_TEST_BIN_DIR}/external_exports.dll
|
${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_bcrypt.exe
|
||||||
${WIBO_TEST_BIN_DIR}/test_resources.exe
|
${WIBO_TEST_BIN_DIR}/test_resources.exe
|
||||||
${WIBO_TEST_BIN_DIR}/test_threading.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)
|
if(CMAKE_CONFIGURATION_TYPES)
|
||||||
set(_wibo_fixture_build_command
|
set(_wibo_fixture_build_command
|
||||||
@ -199,6 +212,12 @@ if(BUILD_TESTING)
|
|||||||
set_tests_properties(wibo.test_heap PROPERTIES
|
set_tests_properties(wibo.test_heap PROPERTIES
|
||||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||||
DEPENDS wibo.build_fixtures)
|
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()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
55
common.h
55
common.h
@ -60,55 +60,30 @@ typedef size_t SIZE_T;
|
|||||||
typedef SIZE_T *PSIZE_T;
|
typedef SIZE_T *PSIZE_T;
|
||||||
typedef unsigned char BYTE;
|
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 TRUE 1
|
||||||
#define FALSE 0
|
#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 STILL_ACTIVE 259
|
||||||
|
|
||||||
#define TIME_ZONE_ID_UNKNOWN 0
|
#define TIME_ZONE_ID_UNKNOWN 0
|
||||||
#define TIME_ZONE_ID_STANDARD 1
|
#define TIME_ZONE_ID_STANDARD 1
|
||||||
#define TIME_ZONE_ID_DAYLIGHT 2
|
#define TIME_ZONE_ID_DAYLIGHT 2
|
||||||
|
|
||||||
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
#define FILE_FLAG_OVERLAPPED 0x40000000
|
||||||
#define INVALID_HANDLE_VALUE ((HANDLE)-1)
|
#define FILE_FLAG_NO_BUFFERING 0x20000000
|
||||||
|
|
||||||
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 MAX_PATH (260)
|
#define MAX_PATH (260)
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/random.h>
|
#include <sys/random.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
#include <sys/random.h>
|
#include <sys/random.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
263
dll/kernel32.cpp
263
dll/kernel32.cpp
@ -1,5 +1,6 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "processes.h"
|
#include "processes.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
@ -454,26 +455,42 @@ namespace kernel32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setLastErrorFromErrno() {
|
void setLastErrorFromErrno() {
|
||||||
switch (errno) {
|
wibo::lastError = wibo::winErrorFromErrno(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
uint32_t WIN_FUNC GetLastError() {
|
||||||
@ -1244,10 +1261,10 @@ namespace kernel32 {
|
|||||||
(void)dwDesiredAccess;
|
(void)dwDesiredAccess;
|
||||||
(void)bInheritHandle;
|
(void)bInheritHandle;
|
||||||
(void)dwOptions;
|
(void)dwOptions;
|
||||||
FILE *fp = files::fpFromHandle(hSourceHandle);
|
auto file = files::fileHandleFromHandle(hSourceHandle);
|
||||||
if (fp == stdin || fp == stdout || fp == stderr) {
|
if (file && (file->fp == stdin || file->fp == stdout || file->fp == stderr)) {
|
||||||
// we never close standard handles so they are fine to duplicate
|
// 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);
|
DEBUG_LOG("-> %p\n", handle);
|
||||||
*lpTargetHandle = handle;
|
*lpTargetHandle = handle;
|
||||||
return 1;
|
return 1;
|
||||||
@ -1261,9 +1278,13 @@ namespace kernel32 {
|
|||||||
DEBUG_LOG("CloseHandle(%p)\n", hObject);
|
DEBUG_LOG("CloseHandle(%p)\n", hObject);
|
||||||
auto data = handles::dataFromHandle(hObject, true);
|
auto data = handles::dataFromHandle(hObject, true);
|
||||||
if (data.type == handles::TYPE_FILE) {
|
if (data.type == handles::TYPE_FILE) {
|
||||||
FILE *fp = (FILE *) data.ptr;
|
auto file = reinterpret_cast<files::FileHandle *>(data.ptr);
|
||||||
if (!(fp == stdin || fp == stdout || fp == stderr)) {
|
if (file) {
|
||||||
fclose(fp);
|
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) {
|
} else if (data.type == handles::TYPE_MAPPED) {
|
||||||
auto *mapping = reinterpret_cast<MappingObject *>(data.ptr);
|
auto *mapping = reinterpret_cast<MappingObject *>(data.ptr);
|
||||||
@ -1820,27 +1841,66 @@ namespace kernel32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned int WIN_FUNC WriteFile(void *hFile, const void *lpBuffer, unsigned int nNumberOfBytesToWrite, unsigned int *lpNumberOfBytesWritten, void *lpOverlapped) {
|
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);
|
DEBUG_LOG("WriteFile(%p, %u)\n", hFile, nNumberOfBytesToWrite);
|
||||||
assert(!lpOverlapped);
|
wibo::lastError = ERROR_SUCCESS;
|
||||||
wibo::lastError = 0;
|
|
||||||
|
|
||||||
FILE *fp = files::fpFromHandle(hFile);
|
auto file = files::fileHandleFromHandle(hFile);
|
||||||
size_t written = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, fp);
|
if (!file || !file->fp) {
|
||||||
if (lpNumberOfBytesWritten)
|
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||||
*lpNumberOfBytesWritten = written;
|
return FALSE;
|
||||||
|
|
||||||
#if 0
|
|
||||||
printf("writing:\n");
|
|
||||||
for (unsigned int i = 0; i < nNumberOfBytesToWrite; i++) {
|
|
||||||
printf("%c", ((const char*)lpBuffer)[i]);
|
|
||||||
}
|
}
|
||||||
printf("\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (written == 0)
|
bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||||
wibo::lastError = 29; // ERROR_WRITE_FAULT
|
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) {
|
BOOL WIN_FUNC FlushFileBuffers(HANDLE hFile) {
|
||||||
@ -1850,12 +1910,17 @@ namespace kernel32 {
|
|||||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||||
return FALSE;
|
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) {
|
if (fflush(fp) != 0) {
|
||||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
int fd = fileno(fp);
|
int fd = file->fd;
|
||||||
if (fd >= 0 && fsync(fd) != 0) {
|
if (fd >= 0 && fsync(fd) != 0) {
|
||||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||||
return FALSE;
|
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) {
|
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);
|
DEBUG_LOG("ReadFile(%p, %u)\n", hFile, nNumberOfBytesToRead);
|
||||||
assert(!lpOverlapped);
|
wibo::lastError = ERROR_SUCCESS;
|
||||||
wibo::lastError = 0;
|
|
||||||
|
|
||||||
FILE *fp = files::fpFromHandle(hFile);
|
auto file = files::fileHandleFromHandle(hFile);
|
||||||
size_t read = fread(lpBuffer, 1, nNumberOfBytesToRead, fp);
|
if (!file || !file->fp) {
|
||||||
*lpNumberOfBytesRead = read;
|
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||||
return 1;
|
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 {
|
enum {
|
||||||
@ -1961,7 +2082,7 @@ namespace kernel32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fp) {
|
if (fp) {
|
||||||
void *handle = files::allocFpHandle(fp);
|
void *handle = files::allocFpHandle(fp, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, true);
|
||||||
DEBUG_LOG("-> %p\n", handle);
|
DEBUG_LOG("-> %p\n", handle);
|
||||||
return handle;
|
return handle;
|
||||||
} else {
|
} else {
|
||||||
@ -4665,8 +4786,42 @@ namespace kernel32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOL WIN_FUNC GetOverlappedResult(void *hFile, void *lpOverlapped, int *lpNumberOfBytesTransferred, BOOL bWait) {
|
BOOL WIN_FUNC GetOverlappedResult(void *hFile, void *lpOverlapped, int *lpNumberOfBytesTransferred, BOOL bWait) {
|
||||||
// DEBUG_LOG("GetOverlappedResult(%p, %p, %p, %u)\n", hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait);
|
(void)hFile;
|
||||||
return 1;
|
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 "common.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#define PIO_APC_ROUTINE void *
|
#define PIO_APC_ROUTINE void *
|
||||||
|
|
||||||
typedef struct _IO_STATUS_BLOCK {
|
typedef struct _IO_STATUS_BLOCK {
|
||||||
@ -13,6 +16,11 @@ typedef struct _IO_STATUS_BLOCK {
|
|||||||
ULONG_PTR Information;
|
ULONG_PTR Information;
|
||||||
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
|
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
|
||||||
|
|
||||||
|
namespace kernel32 {
|
||||||
|
BOOL WIN_FUNC SetEvent(HANDLE hEvent);
|
||||||
|
BOOL WIN_FUNC ResetEvent(HANDLE hEvent);
|
||||||
|
} // namespace kernel32
|
||||||
|
|
||||||
namespace ntdll {
|
namespace ntdll {
|
||||||
|
|
||||||
NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
|
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) {
|
PULONG Key) {
|
||||||
DEBUG_LOG("NtReadFile(%p, %p, %p, %p, %p, %p, %u, %p, %p) ", FileHandle, Event, ApcRoutine, ApcContext,
|
DEBUG_LOG("NtReadFile(%p, %p, %p, %p, %p, %p, %u, %p, %p) ", FileHandle, Event, ApcRoutine, ApcContext,
|
||||||
IoStatusBlock, Buffer, Length, ByteOffset, Key);
|
IoStatusBlock, Buffer, Length, ByteOffset, Key);
|
||||||
assert(Event == nullptr);
|
(void)ApcRoutine;
|
||||||
assert(ApcRoutine == nullptr);
|
(void)ApcContext;
|
||||||
assert(ApcContext == nullptr);
|
(void)Key;
|
||||||
assert(ByteOffset == nullptr);
|
|
||||||
assert(Key == nullptr);
|
|
||||||
|
|
||||||
wibo::lastError = 0;
|
if (!IoStatusBlock) {
|
||||||
FILE *fp = files::fpFromHandle(FileHandle);
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
if (!fp) {
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file = files::fileHandleFromHandle(FileHandle);
|
||||||
|
if (!file || !file->fp) {
|
||||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||||
|
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
||||||
|
IoStatusBlock->Information = 0;
|
||||||
return STATUS_INVALID_HANDLE;
|
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;
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
if (read < Length) {
|
if (io.unixError != 0) {
|
||||||
if (feof(fp)) {
|
winError = wibo::winErrorFromErrno(io.unixError);
|
||||||
wibo::lastError = ERROR_HANDLE_EOF;
|
status = wibo::statusFromWinError(winError);
|
||||||
|
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
||||||
|
winError = ERROR_HANDLE_EOF;
|
||||||
status = STATUS_END_OF_FILE;
|
status = STATUS_END_OF_FILE;
|
||||||
} else {
|
|
||||||
wibo::lastError = ERROR_READ_FAULT; // ?
|
|
||||||
status = STATUS_UNEXPECTED_IO_ERROR;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (IoStatusBlock) {
|
|
||||||
IoStatusBlock->Status = status;
|
IoStatusBlock->Status = status;
|
||||||
IoStatusBlock->Information = read;
|
IoStatusBlock->Information = static_cast<ULONG_PTR>(io.bytesTransferred);
|
||||||
|
wibo::lastError = winError;
|
||||||
|
|
||||||
|
if (Event) {
|
||||||
|
kernel32::SetEvent(Event);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG("-> 0x%x\n", status);
|
DEBUG_LOG("-> 0x%x\n", status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
|
|
||||||
namespace psapi {
|
namespace psapi {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "strutil.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 "handles.h"
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cerrno>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
@ -120,20 +121,183 @@ namespace files {
|
|||||||
return str;
|
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) {
|
FILE *fpFromHandle(void *handle, bool pop) {
|
||||||
handles::Data data = handles::dataFromHandle(handle, pop);
|
handles::Data data = handles::dataFromHandle(handle, pop);
|
||||||
if (data.type == handles::TYPE_FILE) {
|
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) {
|
} else if (data.type == handles::TYPE_UNUSED && pop) {
|
||||||
return 0;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
printf("Invalid file handle %p\n", handle);
|
printf("Invalid file handle %p\n", handle);
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *allocFpHandle(FILE *fp) {
|
void *allocFpHandle(FILE *fp, unsigned int desiredAccess, unsigned int shareMode, unsigned int flags, bool closeOnDestroy) {
|
||||||
return handles::allocDataHandle(handles::Data{handles::TYPE_FILE, fp, 0});
|
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) {
|
void *getStdHandle(uint32_t nStdHandle) {
|
||||||
@ -167,9 +331,9 @@ namespace files {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
stdinHandle = allocFpHandle(stdin);
|
stdinHandle = allocFpHandle(stdin, 0, 0, 0, false);
|
||||||
stdoutHandle = allocFpHandle(stdout);
|
stdoutHandle = allocFpHandle(stdout, 0, 0, 0, false);
|
||||||
stderrHandle = allocFpHandle(stderr);
|
stderrHandle = allocFpHandle(stderr, 0, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
|
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
|
||||||
|
24
files.h
24
files.h
@ -1,14 +1,36 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace files {
|
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::filesystem::path pathFromWindows(const char *inStr);
|
||||||
std::string pathToWindows(const std::filesystem::path &path);
|
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);
|
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);
|
void *getStdHandle(uint32_t nStdHandle);
|
||||||
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle);
|
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle);
|
||||||
void init();
|
void init();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
namespace {
|
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