mirror of
https://github.com/decompals/wibo.git
synced 2025-12-12 14:46:09 +00:00
Implement ConnectNamedPipe
This commit is contained in:
@@ -200,6 +200,8 @@ void *resolveByName(const char *name) {
|
||||
return (void *)kernel32::CreatePipe;
|
||||
if (strcmp(name, "CreateNamedPipeA") == 0)
|
||||
return (void *)kernel32::CreateNamedPipeA;
|
||||
if (strcmp(name, "ConnectNamedPipe") == 0)
|
||||
return (void *)kernel32::ConnectNamedPipe;
|
||||
|
||||
// winbase.h
|
||||
if (strcmp(name, "FindAtomA") == 0)
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
#include "errors.h"
|
||||
#include "handles.h"
|
||||
#include "internal.h"
|
||||
#include "overlapped_util.h"
|
||||
#include "strutil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <condition_variable>
|
||||
#include <fcntl.h>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
@@ -174,6 +176,10 @@ struct NamedPipeInstance final : FileObject {
|
||||
DWORD accessMode;
|
||||
DWORD pipeMode;
|
||||
bool clientConnected = false;
|
||||
bool connectPending = false;
|
||||
LPOVERLAPPED pendingOverlapped = nullptr;
|
||||
std::mutex connectMutex;
|
||||
std::condition_variable connectCv;
|
||||
|
||||
NamedPipeInstance(int fd, Pin<NamedPipeState> st, int companion, DWORD open, DWORD mode)
|
||||
: FileObject(fd), state(std::move(st)), companionFd(companion), accessMode(open), pipeMode(mode) {
|
||||
@@ -183,9 +189,22 @@ struct NamedPipeInstance final : FileObject {
|
||||
}
|
||||
|
||||
~NamedPipeInstance() override {
|
||||
if (companionFd >= 0) {
|
||||
close(companionFd);
|
||||
int localCompanion = -1;
|
||||
{
|
||||
std::lock_guard lk(connectMutex);
|
||||
localCompanion = companionFd;
|
||||
companionFd = -1;
|
||||
if (pendingOverlapped) {
|
||||
pendingOverlapped->Internal = STATUS_PIPE_BROKEN;
|
||||
pendingOverlapped->InternalHigh = 0;
|
||||
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
||||
pendingOverlapped = nullptr;
|
||||
}
|
||||
connectPending = false;
|
||||
connectCv.notify_all();
|
||||
}
|
||||
if (localCompanion >= 0) {
|
||||
close(localCompanion);
|
||||
}
|
||||
if (state) {
|
||||
state->unregisterInstance(this);
|
||||
@@ -195,7 +214,8 @@ struct NamedPipeInstance final : FileObject {
|
||||
}
|
||||
}
|
||||
|
||||
bool canAcceptClient(DWORD desiredAccess) const {
|
||||
bool canAcceptClient(DWORD desiredAccess) {
|
||||
std::lock_guard lk(connectMutex);
|
||||
if (companionFd < 0 || clientConnected) {
|
||||
return false;
|
||||
}
|
||||
@@ -213,16 +233,29 @@ struct NamedPipeInstance final : FileObject {
|
||||
}
|
||||
|
||||
int takeCompanion() {
|
||||
if (companionFd < 0) {
|
||||
std::unique_lock lk(connectMutex);
|
||||
if (companionFd < 0 || clientConnected) {
|
||||
return -1;
|
||||
}
|
||||
int fd = companionFd;
|
||||
companionFd = -1;
|
||||
clientConnected = true;
|
||||
if (pendingOverlapped) {
|
||||
pendingOverlapped->Internal = STATUS_SUCCESS;
|
||||
pendingOverlapped->InternalHigh = 0;
|
||||
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
||||
pendingOverlapped = nullptr;
|
||||
}
|
||||
if (connectPending) {
|
||||
connectPending = false;
|
||||
connectCv.notify_all();
|
||||
}
|
||||
lk.unlock();
|
||||
return fd;
|
||||
}
|
||||
|
||||
void restoreCompanion(int fd) {
|
||||
std::lock_guard lk(connectMutex);
|
||||
companionFd = fd;
|
||||
if (fd >= 0) {
|
||||
clientConnected = false;
|
||||
@@ -502,4 +535,64 @@ HANDLE WIN_FUNC CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMo
|
||||
return wibo::handles().alloc(std::move(pipeObj), grantedAccess, handleFlags);
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped) {
|
||||
HOST_CONTEXT_GUARD();
|
||||
DEBUG_LOG("ConnectNamedPipe(%p, %p)\n", hNamedPipe, lpOverlapped);
|
||||
|
||||
auto pin = wibo::handles().get(hNamedPipe);
|
||||
auto *pipe = pin ? dynamic_cast<NamedPipeInstance *>(pin.get()) : nullptr;
|
||||
if (!pipe) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const bool isOverlappedHandle = pipe->overlapped;
|
||||
if (isOverlappedHandle && lpOverlapped == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::unique_lock lock(pipe->connectMutex);
|
||||
|
||||
if (pipe->clientConnected) {
|
||||
wibo::lastError = ERROR_PIPE_CONNECTED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pipe->companionFd < 0) {
|
||||
wibo::lastError = ERROR_PIPE_BUSY;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pipe->connectPending) {
|
||||
wibo::lastError = ERROR_PIPE_LISTENING;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((pipe->pipeMode & PIPE_NOWAIT) != 0) {
|
||||
wibo::lastError = ERROR_PIPE_LISTENING;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (isOverlappedHandle) {
|
||||
pipe->connectPending = true;
|
||||
pipe->pendingOverlapped = lpOverlapped;
|
||||
lpOverlapped->Internal = STATUS_PENDING;
|
||||
lpOverlapped->InternalHigh = 0;
|
||||
kernel32::detail::resetOverlappedEvent(lpOverlapped);
|
||||
lock.unlock();
|
||||
wibo::lastError = ERROR_IO_PENDING;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pipe->connectPending = true;
|
||||
pipe->connectCv.wait(lock, [&]() { return pipe->clientConnected || pipe->companionFd < 0; });
|
||||
pipe->connectPending = false;
|
||||
if (!pipe->clientConnected) {
|
||||
wibo::lastError = ERROR_NO_DATA;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} // namespace kernel32
|
||||
|
||||
@@ -9,6 +9,7 @@ BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRI
|
||||
HANDLE WIN_FUNC CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
|
||||
DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||
BOOL WIN_FUNC ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped);
|
||||
bool tryCreateFileNamedPipeA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes, HANDLE &outHandle);
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#define ERROR_IO_PENDING 997
|
||||
#define ERROR_OPERATION_ABORTED 995
|
||||
#define ERROR_PIPE_BUSY 231
|
||||
#define ERROR_NO_DATA 232
|
||||
#define ERROR_PIPE_CONNECTED 535
|
||||
#define ERROR_PIPE_LISTENING 536
|
||||
#define ERROR_NONE_MAPPED 1332
|
||||
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
||||
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
||||
|
||||
@@ -6,6 +6,38 @@
|
||||
#include <string.h>
|
||||
|
||||
static const char *kPipeName = "\\\\.\\pipe\\wibo_test_namedpipe";
|
||||
static const char *kPipeNameConnect = "\\\\.\\pipe\\wibo_test_namedpipe_connect";
|
||||
static const char *kPipeNameNowait = "\\\\.\\pipe\\wibo_test_namedpipe_nowait";
|
||||
static const char *kPipeNameOverlapped = "\\\\.\\pipe\\wibo_test_namedpipe_overlapped";
|
||||
|
||||
struct ConnectThreadArgs {
|
||||
const char *pipeName;
|
||||
const char *message;
|
||||
DWORD delayMs;
|
||||
HANDLE client;
|
||||
DWORD error;
|
||||
};
|
||||
|
||||
static DWORD WINAPI client_connect_thread(LPVOID parameter) {
|
||||
struct ConnectThreadArgs *args = (struct ConnectThreadArgs *)parameter;
|
||||
args->client = INVALID_HANDLE_VALUE;
|
||||
args->error = ERROR_SUCCESS;
|
||||
if (args->delayMs) {
|
||||
Sleep(args->delayMs);
|
||||
}
|
||||
HANDLE client =
|
||||
CreateFileA(args->pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
args->client = client;
|
||||
if (client == INVALID_HANDLE_VALUE) {
|
||||
args->error = GetLastError();
|
||||
return 0;
|
||||
}
|
||||
if (args->message) {
|
||||
DWORD written = 0;
|
||||
WriteFile(client, args->message, (DWORD)strlen(args->message), &written, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void write_checked(HANDLE handle, const char *msg) {
|
||||
DWORD written = 0;
|
||||
@@ -70,5 +102,82 @@ int main(void) {
|
||||
TEST_CHECK(CloseHandle(client));
|
||||
TEST_CHECK(CloseHandle(pipe));
|
||||
|
||||
HANDLE connectPipe = CreateNamedPipeA(kPipeNameConnect, PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
|
||||
TEST_CHECK(connectPipe != INVALID_HANDLE_VALUE);
|
||||
|
||||
struct ConnectThreadArgs connectArgs = {
|
||||
.pipeName = kPipeNameConnect,
|
||||
.message = "hello",
|
||||
.delayMs = 50,
|
||||
.client = INVALID_HANDLE_VALUE,
|
||||
.error = ERROR_SUCCESS,
|
||||
};
|
||||
|
||||
HANDLE connectThread = CreateThread(NULL, 0, client_connect_thread, &connectArgs, 0, NULL);
|
||||
TEST_CHECK(connectThread != NULL);
|
||||
TEST_CHECK(ConnectNamedPipe(connectPipe, NULL));
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(connectThread, 5000));
|
||||
TEST_CHECK(CloseHandle(connectThread));
|
||||
TEST_CHECK(connectArgs.client != INVALID_HANDLE_VALUE);
|
||||
TEST_CHECK_EQ(ERROR_SUCCESS, connectArgs.error);
|
||||
|
||||
read_checked(connectPipe, "hello");
|
||||
write_checked(connectPipe, "world");
|
||||
read_checked(connectArgs.client, "world");
|
||||
|
||||
SetLastError(0xdeadbeefu);
|
||||
TEST_CHECK(!ConnectNamedPipe(connectPipe, NULL));
|
||||
TEST_CHECK_EQ(ERROR_PIPE_CONNECTED, GetLastError());
|
||||
|
||||
TEST_CHECK(CloseHandle(connectArgs.client));
|
||||
TEST_CHECK(CloseHandle(connectPipe));
|
||||
|
||||
HANDLE nowaitPipe = CreateNamedPipeA(kPipeNameNowait, PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 1024, 1024, 0, NULL);
|
||||
TEST_CHECK(nowaitPipe != INVALID_HANDLE_VALUE);
|
||||
SetLastError(0xdeadbeefu);
|
||||
TEST_CHECK(!ConnectNamedPipe(nowaitPipe, NULL));
|
||||
TEST_CHECK_EQ(ERROR_PIPE_LISTENING, GetLastError());
|
||||
TEST_CHECK(CloseHandle(nowaitPipe));
|
||||
|
||||
HANDLE overlappedPipe = CreateNamedPipeA(kPipeNameOverlapped, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
|
||||
TEST_CHECK(overlappedPipe != INVALID_HANDLE_VALUE);
|
||||
|
||||
struct ConnectThreadArgs overlappedArgs = {
|
||||
.pipeName = kPipeNameOverlapped,
|
||||
.message = NULL,
|
||||
.delayMs = 50,
|
||||
.client = INVALID_HANDLE_VALUE,
|
||||
.error = ERROR_SUCCESS,
|
||||
};
|
||||
|
||||
OVERLAPPED ov = {0};
|
||||
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
HANDLE overlappedThread = CreateThread(NULL, 0, client_connect_thread, &overlappedArgs, 0, NULL);
|
||||
TEST_CHECK(overlappedThread != NULL);
|
||||
|
||||
SetLastError(0xdeadbeefu);
|
||||
TEST_CHECK(!ConnectNamedPipe(overlappedPipe, &ov));
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(overlappedThread, 5000));
|
||||
TEST_CHECK(CloseHandle(overlappedThread));
|
||||
TEST_CHECK(overlappedArgs.client != INVALID_HANDLE_VALUE);
|
||||
TEST_CHECK_EQ(ERROR_SUCCESS, overlappedArgs.error);
|
||||
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(ov.hEvent, 5000));
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(overlappedPipe, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ((DWORD)0, transferred);
|
||||
|
||||
TEST_CHECK(CloseHandle(overlappedArgs.client));
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(overlappedPipe));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user