mirror of
https://github.com/decompals/wibo.git
synced 2025-10-16 15:15:10 +00:00
Fix pipe reads; add tests for NtReadFile & pipes
This commit is contained in:
parent
85cf4a74c7
commit
01ed50c4b4
@ -298,6 +298,28 @@ if(BUILD_TESTING)
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_ntquery.c
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_ntquery.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_ntreadfile.exe
|
||||||
|
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||||
|
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||||
|
-o test_ntreadfile.exe
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_ntreadfile.c
|
||||||
|
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||||
|
DEPENDS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_ntreadfile.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${WIBO_TEST_BIN_DIR}/test_pipe_io.exe
|
||||||
|
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||||
|
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||||
|
-o test_pipe_io.exe
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_pipe_io.c
|
||||||
|
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||||
|
DEPENDS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_pipe_io.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_sysdir.exe
|
OUTPUT ${WIBO_TEST_BIN_DIR}/test_sysdir.exe
|
||||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||||
@ -328,6 +350,8 @@ if(BUILD_TESTING)
|
|||||||
${WIBO_TEST_BIN_DIR}/test_virtualquery.exe
|
${WIBO_TEST_BIN_DIR}/test_virtualquery.exe
|
||||||
${WIBO_TEST_BIN_DIR}/test_rtl.exe
|
${WIBO_TEST_BIN_DIR}/test_rtl.exe
|
||||||
${WIBO_TEST_BIN_DIR}/test_ntquery.exe
|
${WIBO_TEST_BIN_DIR}/test_ntquery.exe
|
||||||
|
${WIBO_TEST_BIN_DIR}/test_ntreadfile.exe
|
||||||
|
${WIBO_TEST_BIN_DIR}/test_pipe_io.exe
|
||||||
${WIBO_TEST_BIN_DIR}/test_sysdir.exe)
|
${WIBO_TEST_BIN_DIR}/test_sysdir.exe)
|
||||||
|
|
||||||
if(CMAKE_CONFIGURATION_TYPES)
|
if(CMAKE_CONFIGURATION_TYPES)
|
||||||
@ -412,6 +436,18 @@ if(BUILD_TESTING)
|
|||||||
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_ntreadfile
|
||||||
|
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_ntreadfile.exe)
|
||||||
|
set_tests_properties(wibo.test_ntreadfile PROPERTIES
|
||||||
|
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||||
|
DEPENDS wibo.build_fixtures)
|
||||||
|
|
||||||
|
add_test(NAME wibo.test_pipe_io
|
||||||
|
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_pipe_io.exe)
|
||||||
|
set_tests_properties(wibo.test_pipe_io PROPERTIES
|
||||||
|
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||||
|
DEPENDS wibo.build_fixtures)
|
||||||
|
|
||||||
add_test(NAME wibo.test_sysdir
|
add_test(NAME wibo.test_sysdir
|
||||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_sysdir.exe)
|
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_sysdir.exe)
|
||||||
set_tests_properties(wibo.test_sysdir PROPERTIES
|
set_tests_properties(wibo.test_sysdir PROPERTIES
|
||||||
|
@ -279,10 +279,10 @@ void WIN_ENTRY exit(int status) {
|
|||||||
(*it)();
|
(*it)();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
::exit(status);
|
::_exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WIN_ENTRY _cexit(void) {
|
void WIN_ENTRY _cexit() {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
DEBUG_LOG("_cexit()\n");
|
DEBUG_LOG("_cexit()\n");
|
||||||
TIB *tib = wibo::getThreadTibForHost();
|
TIB *tib = wibo::getThreadTibForHost();
|
||||||
|
@ -521,7 +521,8 @@ BOOL WIN_FUNC FlushFileBuffers(HANDLE hFile) {
|
|||||||
BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead,
|
BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead,
|
||||||
LPOVERLAPPED lpOverlapped) {
|
LPOVERLAPPED lpOverlapped) {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
DEBUG_LOG("ReadFile(%p, %u)\n", hFile, nNumberOfBytesToRead);
|
DEBUG_LOG("ReadFile(%p, %p, %u, %p, %p)\n", hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead,
|
||||||
|
lpOverlapped);
|
||||||
wibo::lastError = ERROR_SUCCESS;
|
wibo::lastError = ERROR_SUCCESS;
|
||||||
|
|
||||||
HandleMeta meta{};
|
HandleMeta meta{};
|
||||||
@ -543,6 +544,10 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lpNumberOfBytesRead && (!file->overlapped || lpOverlapped == nullptr)) {
|
||||||
|
*lpNumberOfBytesRead = 0;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<off64_t> offset;
|
std::optional<off64_t> offset;
|
||||||
bool updateFilePointer = true;
|
bool updateFilePointer = true;
|
||||||
if (lpOverlapped != nullptr) {
|
if (lpOverlapped != nullptr) {
|
||||||
@ -561,6 +566,16 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
|||||||
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
||||||
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
||||||
completionStatus = STATUS_END_OF_FILE;
|
completionStatus = STATUS_END_OF_FILE;
|
||||||
|
if (file->isPipe) {
|
||||||
|
wibo::lastError = ERROR_BROKEN_PIPE;
|
||||||
|
if (lpOverlapped != nullptr) {
|
||||||
|
lpOverlapped->Internal = completionStatus;
|
||||||
|
lpOverlapped->InternalHigh = 0;
|
||||||
|
signalOverlappedEvent(lpOverlapped);
|
||||||
|
}
|
||||||
|
DEBUG_LOG("-> ERROR_BROKEN_PIPE\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lpNumberOfBytesRead && (!file->overlapped || lpOverlapped == nullptr)) {
|
if (lpNumberOfBytesRead && (!file->overlapped || lpOverlapped == nullptr)) {
|
||||||
@ -573,6 +588,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
|||||||
signalOverlappedEvent(lpOverlapped);
|
signalOverlappedEvent(lpOverlapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("-> %u bytes read, error %d\n", io.bytesTransferred, wibo::lastError);
|
||||||
return io.unixError == 0;
|
return io.unixError == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,13 +29,13 @@ struct FileObject final : FsObject {
|
|||||||
off64_t filePos = 0;
|
off64_t filePos = 0;
|
||||||
bool overlapped = false;
|
bool overlapped = false;
|
||||||
bool appendOnly = false;
|
bool appendOnly = false;
|
||||||
bool seekable = true;
|
bool isPipe = false;
|
||||||
|
|
||||||
explicit FileObject(int fd) : FsObject(kType, fd) {
|
explicit FileObject(int fd) : FsObject(kType, fd) {
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
off64_t pos = lseek64(fd, 0, SEEK_CUR);
|
off64_t pos = lseek64(fd, 0, SEEK_CUR);
|
||||||
if (pos == -1 && errno == ESPIPE) {
|
if (pos == -1 && errno == ESPIPE) {
|
||||||
seekable = false;
|
isPipe = true;
|
||||||
} else if (pos >= 0) {
|
} else if (pos >= 0) {
|
||||||
filePos = pos;
|
filePos = pos;
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,9 @@ BOOL WIN_FUNC ResetEvent(HANDLE hEvent);
|
|||||||
|
|
||||||
namespace ntdll {
|
namespace ntdll {
|
||||||
|
|
||||||
|
constexpr LARGE_INTEGER FILE_WRITE_TO_END_OF_FILE = static_cast<LARGE_INTEGER>(-1);
|
||||||
|
constexpr LARGE_INTEGER FILE_USE_FILE_POINTER_POSITION = static_cast<LARGE_INTEGER>(-2);
|
||||||
|
|
||||||
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,
|
||||||
PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset,
|
PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset,
|
||||||
PULONG Key) {
|
PULONG Key) {
|
||||||
@ -132,48 +135,59 @@ NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE Ap
|
|||||||
(void)Key;
|
(void)Key;
|
||||||
|
|
||||||
if (!IoStatusBlock) {
|
if (!IoStatusBlock) {
|
||||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
IoStatusBlock->Information = 0;
|
||||||
|
|
||||||
auto file = wibo::handles().getAs<FileObject>(FileHandle);
|
auto file = wibo::handles().getAs<FileObject>(FileHandle);
|
||||||
if (!file || !file->valid()) {
|
if (!file || !file->valid()) {
|
||||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
|
||||||
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
||||||
IoStatusBlock->Information = 0;
|
IoStatusBlock->Information = 0;
|
||||||
return STATUS_INVALID_HANDLE;
|
return STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleOverlapped = file->overlapped;
|
bool useOverlapped = file->overlapped;
|
||||||
std::optional<uint64_t> offset;
|
bool useCurrentFilePosition = (ByteOffset == nullptr);
|
||||||
bool updateFilePointer = !handleOverlapped;
|
if (!useCurrentFilePosition && *ByteOffset == FILE_USE_FILE_POINTER_POSITION) {
|
||||||
if (ByteOffset) {
|
useCurrentFilePosition = true;
|
||||||
offset = static_cast<uint64_t>(*ByteOffset);
|
|
||||||
} else if (handleOverlapped) {
|
|
||||||
updateFilePointer = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<off64_t> offset;
|
||||||
|
if (!useCurrentFilePosition) {
|
||||||
|
offset = static_cast<off64_t>(*ByteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useOverlapped && useCurrentFilePosition) {
|
||||||
|
IoStatusBlock->Status = STATUS_INVALID_PARAMETER;
|
||||||
|
IoStatusBlock->Information = 0;
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pin<kernel32::EventObject> ev;
|
||||||
if (Event) {
|
if (Event) {
|
||||||
kernel32::ResetEvent(Event);
|
ev = wibo::handles().getAs<kernel32::EventObject>(Event);
|
||||||
|
if (!ev) {
|
||||||
|
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
||||||
|
IoStatusBlock->Information = 0;
|
||||||
|
return STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
ev->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool updateFilePointer = !useOverlapped;
|
||||||
auto io = files::read(file.get(), Buffer, Length, offset, updateFilePointer);
|
auto io = files::read(file.get(), Buffer, Length, offset, updateFilePointer);
|
||||||
DWORD winError = ERROR_SUCCESS;
|
|
||||||
NTSTATUS status = STATUS_SUCCESS;
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
if (io.unixError != 0) {
|
if (io.unixError != 0) {
|
||||||
winError = wibo::winErrorFromErrno(io.unixError);
|
status = wibo::statusFromErrno(io.unixError);
|
||||||
status = wibo::statusFromWinError(winError);
|
|
||||||
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
||||||
winError = ERROR_HANDLE_EOF;
|
status = file->isPipe ? STATUS_PIPE_BROKEN : STATUS_END_OF_FILE;
|
||||||
status = STATUS_END_OF_FILE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IoStatusBlock->Status = status;
|
IoStatusBlock->Status = status;
|
||||||
IoStatusBlock->Information = static_cast<ULONG_PTR>(io.bytesTransferred);
|
IoStatusBlock->Information = static_cast<ULONG_PTR>(io.bytesTransferred);
|
||||||
wibo::lastError = winError;
|
|
||||||
|
|
||||||
if (Event) {
|
if (ev && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE)) {
|
||||||
kernel32::SetEvent(Event);
|
ev->set();
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG("-> 0x%x\n", status);
|
DEBUG_LOG("-> 0x%x\n", status);
|
||||||
|
@ -55,6 +55,7 @@ typedef int NTSTATUS;
|
|||||||
#define STATUS_PENDING ((NTSTATUS)0x00000103)
|
#define STATUS_PENDING ((NTSTATUS)0x00000103)
|
||||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
|
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
|
||||||
#define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS)0xC00000E9)
|
#define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS)0xC00000E9)
|
||||||
|
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014B)
|
||||||
|
|
||||||
typedef int HRESULT;
|
typedef int HRESULT;
|
||||||
#define S_OK ((HRESULT)0x00000000)
|
#define S_OK ((HRESULT)0x00000000)
|
||||||
|
@ -156,6 +156,30 @@ IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::opt
|
|||||||
// Sanity check: if no offset is given, we must update the file pointer
|
// Sanity check: if no offset is given, we must update the file pointer
|
||||||
assert(offset.has_value() || updateFilePointer);
|
assert(offset.has_value() || updateFilePointer);
|
||||||
|
|
||||||
|
if (file->isPipe) {
|
||||||
|
std::lock_guard lk(file->m);
|
||||||
|
size_t chunk = bytesToRead > SSIZE_MAX ? SSIZE_MAX : bytesToRead;
|
||||||
|
uint8_t *in = static_cast<uint8_t *>(buffer);
|
||||||
|
ssize_t rc;
|
||||||
|
while (true) {
|
||||||
|
rc = ::read(file->fd, in, chunk);
|
||||||
|
if (rc == -1 && errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (rc == -1) {
|
||||||
|
result.unixError = errno ? errno : EIO;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (rc == 0) {
|
||||||
|
result.reachedEnd = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.bytesTransferred = static_cast<size_t>(rc);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const auto doRead = [&](off64_t pos) {
|
const auto doRead = [&](off64_t pos) {
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
size_t remaining = bytesToRead;
|
size_t remaining = bytesToRead;
|
||||||
@ -167,14 +191,12 @@ IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::opt
|
|||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
result.bytesTransferred = total;
|
|
||||||
result.unixError = errno ? errno : EIO;
|
result.unixError = errno ? errno : EIO;
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
result.bytesTransferred = total;
|
|
||||||
result.reachedEnd = true;
|
result.reachedEnd = true;
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
total += static_cast<size_t>(rc);
|
total += static_cast<size_t>(rc);
|
||||||
remaining -= static_cast<size_t>(rc);
|
remaining -= static_cast<size_t>(rc);
|
||||||
@ -185,7 +207,7 @@ IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::opt
|
|||||||
|
|
||||||
if (updateFilePointer || !offset.has_value()) {
|
if (updateFilePointer || !offset.has_value()) {
|
||||||
std::lock_guard lk(file->m);
|
std::lock_guard lk(file->m);
|
||||||
off64_t pos = offset.value_or(file->filePos);
|
const off64_t pos = offset.value_or(file->filePos);
|
||||||
doRead(pos);
|
doRead(pos);
|
||||||
if (updateFilePointer) {
|
if (updateFilePointer) {
|
||||||
file->filePos = pos + static_cast<off64_t>(result.bytesTransferred);
|
file->filePos = pos + static_cast<off64_t>(result.bytesTransferred);
|
||||||
@ -211,7 +233,7 @@ IOResult write(FileObject *file, const void *buffer, size_t bytesToWrite, const
|
|||||||
// Sanity check: if no offset is given, we must update the file pointer
|
// Sanity check: if no offset is given, we must update the file pointer
|
||||||
assert(offset.has_value() || updateFilePointer);
|
assert(offset.has_value() || updateFilePointer);
|
||||||
|
|
||||||
if (file->appendOnly || !file->seekable) {
|
if (file->appendOnly || file->isPipe) {
|
||||||
std::lock_guard lk(file->m);
|
std::lock_guard lk(file->m);
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
size_t remaining = bytesToWrite;
|
size_t remaining = bytesToWrite;
|
||||||
@ -234,7 +256,7 @@ IOResult write(FileObject *file, const void *buffer, size_t bytesToWrite, const
|
|||||||
}
|
}
|
||||||
result.bytesTransferred = total;
|
result.bytesTransferred = total;
|
||||||
if (updateFilePointer) {
|
if (updateFilePointer) {
|
||||||
off64_t pos = file->seekable ? lseek64(file->fd, 0, SEEK_CUR) : 0;
|
off64_t pos = file->isPipe ? 0 : lseek64(file->fd, 0, SEEK_CUR);
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
file->filePos = pos;
|
file->filePos = pos;
|
||||||
} else if (result.unixError == 0) {
|
} else if (result.unixError == 0) {
|
||||||
|
@ -29,7 +29,7 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetLastError(0);
|
SetLastError(0);
|
||||||
void *inPlace = HeapReAlloc(processHeap, HEAP_REALLOC_IN_PLACE_ONLY, grown, 128);
|
void *inPlace = HeapReAlloc(processHeap, HEAP_REALLOC_IN_PLACE_ONLY, grown, 2048);
|
||||||
TEST_CHECK(inPlace == NULL);
|
TEST_CHECK(inPlace == NULL);
|
||||||
TEST_CHECK_EQ(ERROR_NOT_ENOUGH_MEMORY, GetLastError());
|
TEST_CHECK_EQ(ERROR_NOT_ENOUGH_MEMORY, GetLastError());
|
||||||
|
|
||||||
|
88
test/test_ntreadfile.c
Normal file
88
test/test_ntreadfile.c
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef STATUS_SUCCESS
|
||||||
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STATUS_END_OF_FILE
|
||||||
|
#define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *kTempFileName = "ntreadfile_fixture.tmp";
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI *NtReadFile_t)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG,
|
||||||
|
PLARGE_INTEGER, PULONG);
|
||||||
|
|
||||||
|
static NtReadFile_t load_ntreadfile(void) {
|
||||||
|
HMODULE mod = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (!mod) {
|
||||||
|
mod = LoadLibraryW(L"ntdll.dll");
|
||||||
|
}
|
||||||
|
TEST_CHECK(mod != NULL);
|
||||||
|
FARPROC proc = GetProcAddress(mod, "NtReadFile");
|
||||||
|
TEST_CHECK(proc != NULL);
|
||||||
|
NtReadFile_t fn = NULL;
|
||||||
|
TEST_CHECK(sizeof(fn) == sizeof(proc));
|
||||||
|
memcpy(&fn, &proc, sizeof(fn));
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_fixture(HANDLE file, const char *data, DWORD length) {
|
||||||
|
DWORD written = 0;
|
||||||
|
TEST_CHECK(WriteFile(file, data, length, &written, NULL));
|
||||||
|
TEST_CHECK_EQ(length, written);
|
||||||
|
TEST_CHECK(SetFilePointer(file, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
NtReadFile_t fn = load_ntreadfile();
|
||||||
|
|
||||||
|
DeleteFileA(kTempFileName);
|
||||||
|
HANDLE file = CreateFileA(kTempFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
const char payload[] = "hello";
|
||||||
|
write_fixture(file, payload, (DWORD)(sizeof(payload) - 1));
|
||||||
|
|
||||||
|
HANDLE event = CreateEventA(NULL, TRUE, TRUE, NULL);
|
||||||
|
TEST_CHECK(event != NULL);
|
||||||
|
|
||||||
|
IO_STATUS_BLOCK iosb;
|
||||||
|
memset(&iosb, 0, sizeof(iosb));
|
||||||
|
char buffer[6];
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
SetLastError(ERROR_GEN_FAILURE);
|
||||||
|
DWORD before = GetLastError();
|
||||||
|
NTSTATUS status = fn(file, event, NULL, NULL, &iosb, buffer, 5, NULL, NULL);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ(5u, (unsigned int)iosb.Information);
|
||||||
|
TEST_CHECK(memcmp(buffer, payload, 5) == 0);
|
||||||
|
TEST_CHECK_EQ(before, GetLastError());
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
|
||||||
|
TEST_CHECK(ResetEvent(event));
|
||||||
|
|
||||||
|
LARGE_INTEGER useCurrent;
|
||||||
|
useCurrent.QuadPart = -2;
|
||||||
|
IO_STATUS_BLOCK eofIosb;
|
||||||
|
memset(&eofIosb, 0, sizeof(eofIosb));
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
SetLastError(ERROR_GEN_FAILURE);
|
||||||
|
before = GetLastError();
|
||||||
|
status = fn(file, event, NULL, NULL, &eofIosb, buffer, sizeof(buffer), &useCurrent, NULL);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_END_OF_FILE, status);
|
||||||
|
TEST_CHECK_EQ(0u, (unsigned int)eofIosb.Information);
|
||||||
|
TEST_CHECK_EQ(before, GetLastError());
|
||||||
|
TEST_CHECK(buffer[0] == 0);
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(event));
|
||||||
|
TEST_CHECK(CloseHandle(file));
|
||||||
|
TEST_CHECK(DeleteFileA(kTempFileName));
|
||||||
|
return 0;
|
||||||
|
}
|
96
test/test_pipe_io.c
Normal file
96
test/test_pipe_io.c
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef STATUS_SUCCESS
|
||||||
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STATUS_PIPE_BROKEN
|
||||||
|
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI *NtReadFile_t)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG,
|
||||||
|
PLARGE_INTEGER, PULONG);
|
||||||
|
|
||||||
|
static NtReadFile_t load_ntreadfile(void) {
|
||||||
|
HMODULE mod = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (!mod) {
|
||||||
|
TEST_CHECK_EQ(ERROR_MOD_NOT_FOUND, GetLastError());
|
||||||
|
mod = LoadLibraryW(L"ntdll.dll");
|
||||||
|
}
|
||||||
|
TEST_CHECK(mod != NULL);
|
||||||
|
FARPROC proc = GetProcAddress(mod, "NtReadFile");
|
||||||
|
TEST_CHECK(proc != NULL);
|
||||||
|
NtReadFile_t fn = NULL;
|
||||||
|
TEST_CHECK(sizeof(fn) == sizeof(proc));
|
||||||
|
memcpy(&fn, &proc, sizeof(fn));
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_bytes(HANDLE handle, const char *data, size_t length) {
|
||||||
|
DWORD written = 0;
|
||||||
|
TEST_CHECK(WriteFile(handle, data, (DWORD)length, &written, NULL));
|
||||||
|
TEST_CHECK_EQ((DWORD)length, written);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
NtReadFile_t fn = load_ntreadfile();
|
||||||
|
|
||||||
|
HANDLE readPipe = NULL;
|
||||||
|
HANDLE writePipe = NULL;
|
||||||
|
TEST_CHECK(CreatePipe(&readPipe, &writePipe, NULL, 0));
|
||||||
|
|
||||||
|
const char msgReadFile[] = "pipe-read";
|
||||||
|
write_bytes(writePipe, msgReadFile, strlen(msgReadFile));
|
||||||
|
|
||||||
|
char buffer[64];
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
DWORD bytesRead = 0;
|
||||||
|
TEST_CHECK(ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL));
|
||||||
|
TEST_CHECK_EQ((DWORD)strlen(msgReadFile), bytesRead);
|
||||||
|
TEST_CHECK(memcmp(buffer, msgReadFile, bytesRead) == 0);
|
||||||
|
|
||||||
|
const char msgNtRead[] = "ntread";
|
||||||
|
write_bytes(writePipe, msgNtRead, strlen(msgNtRead));
|
||||||
|
|
||||||
|
HANDLE event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
TEST_CHECK(event != NULL);
|
||||||
|
|
||||||
|
IO_STATUS_BLOCK iosb;
|
||||||
|
memset(&iosb, 0, sizeof(iosb));
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
NTSTATUS status = fn(readPipe, event, NULL, NULL, &iosb, buffer, sizeof(buffer), NULL, NULL);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ((ULONG_PTR)strlen(msgNtRead), iosb.Information);
|
||||||
|
TEST_CHECK(memcmp(buffer, msgNtRead, iosb.Information) == 0);
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(writePipe));
|
||||||
|
writePipe = NULL;
|
||||||
|
|
||||||
|
bytesRead = 123;
|
||||||
|
SetLastError(ERROR_GEN_FAILURE);
|
||||||
|
BOOL ok = ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL);
|
||||||
|
TEST_CHECK(!ok);
|
||||||
|
TEST_CHECK_EQ(0u, (unsigned int)bytesRead);
|
||||||
|
TEST_CHECK_EQ(ERROR_BROKEN_PIPE, GetLastError());
|
||||||
|
TEST_CHECK(ResetEvent(event));
|
||||||
|
|
||||||
|
memset(&iosb, 0, sizeof(iosb));
|
||||||
|
status = fn(readPipe, event, NULL, NULL, &iosb, buffer, sizeof(buffer), NULL, NULL);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_PIPE_BROKEN, status);
|
||||||
|
TEST_CHECK_EQ(0u, (unsigned int)iosb.Information);
|
||||||
|
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(event, 0));
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(event));
|
||||||
|
TEST_CHECK(CloseHandle(readPipe));
|
||||||
|
if (writePipe) {
|
||||||
|
TEST_CHECK(CloseHandle(writePipe));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user