mirror of
https://github.com/decompals/wibo.git
synced 2025-12-12 14:46:09 +00:00
Implement CreateNamedPipeA
This commit is contained in:
@@ -276,6 +276,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS)
|
|||||||
wibo_add_fixture_bin(NAME test_ntquery SOURCES test/test_ntquery.c)
|
wibo_add_fixture_bin(NAME test_ntquery SOURCES test/test_ntquery.c)
|
||||||
wibo_add_fixture_bin(NAME test_ntreadfile SOURCES test/test_ntreadfile.c)
|
wibo_add_fixture_bin(NAME test_ntreadfile SOURCES test/test_ntreadfile.c)
|
||||||
wibo_add_fixture_bin(NAME test_pipe_io SOURCES test/test_pipe_io.c)
|
wibo_add_fixture_bin(NAME test_pipe_io SOURCES test/test_pipe_io.c)
|
||||||
|
wibo_add_fixture_bin(NAME test_namedpipe SOURCES test/test_namedpipe.c)
|
||||||
wibo_add_fixture_bin(NAME test_sysdir SOURCES test/test_sysdir.c)
|
wibo_add_fixture_bin(NAME test_sysdir SOURCES test/test_sysdir.c)
|
||||||
|
|
||||||
# DLLs for fixture tests
|
# DLLs for fixture tests
|
||||||
|
|||||||
@@ -198,6 +198,8 @@ void *resolveByName(const char *name) {
|
|||||||
// namedpipeapi.h
|
// namedpipeapi.h
|
||||||
if (strcmp(name, "CreatePipe") == 0)
|
if (strcmp(name, "CreatePipe") == 0)
|
||||||
return (void *)kernel32::CreatePipe;
|
return (void *)kernel32::CreatePipe;
|
||||||
|
if (strcmp(name, "CreateNamedPipeA") == 0)
|
||||||
|
return (void *)kernel32::CreateNamedPipeA;
|
||||||
|
|
||||||
// winbase.h
|
// winbase.h
|
||||||
if (strcmp(name, "FindAtomA") == 0)
|
if (strcmp(name, "FindAtomA") == 0)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "files.h"
|
#include "files.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include "namedpipeapi.h"
|
||||||
#include "overlapped_util.h"
|
#include "overlapped_util.h"
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
#include "timeutil.h"
|
#include "timeutil.h"
|
||||||
@@ -895,6 +896,13 @@ HANDLE WIN_FUNC CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwSh
|
|||||||
return consoleHandle;
|
return consoleHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HANDLE pipeHandle = INVALID_HANDLE_VALUE;
|
||||||
|
if (kernel32::tryCreateFileNamedPipeA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||||
|
dwCreationDisposition, dwFlagsAndAttributes, pipeHandle)) {
|
||||||
|
DEBUG_LOG("CreateFileA(pipe=%s) -> %p (err=%u)\n", lpFileName, pipeHandle, wibo::lastError);
|
||||||
|
return pipeHandle;
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path hostPath = files::pathFromWindows(lpFileName);
|
std::filesystem::path hostPath = files::pathFromWindows(lpFileName);
|
||||||
std::string hostPathStr = hostPath.string();
|
std::string hostPathStr = hostPath.string();
|
||||||
DEBUG_LOG("CreateFileA(filename=%s (%s), desiredAccess=0x%x, shareMode=%u, securityAttributes=%p, "
|
DEBUG_LOG("CreateFileA(filename=%s (%s), desiredAccess=0x%x, shareMode=%u, securityAttributes=%p, "
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ struct FsObject : ObjectBase {
|
|||||||
explicit FsObject(ObjectType type, int fd) : ObjectBase(type), fd(fd) {}
|
explicit FsObject(ObjectType type, int fd) : ObjectBase(type), fd(fd) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileObject final : FsObject {
|
struct FileObject : FsObject {
|
||||||
static constexpr ObjectType kType = ObjectType::File;
|
static constexpr ObjectType kType = ObjectType::File;
|
||||||
|
|
||||||
off_t filePos = 0;
|
off_t filePos = 0;
|
||||||
@@ -41,6 +41,8 @@ struct FileObject final : FsObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~FileObject() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DirectoryObject final : FsObject {
|
struct DirectoryObject final : FsObject {
|
||||||
|
|||||||
@@ -5,14 +5,26 @@
|
|||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include "strutil.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace kernel32 {
|
namespace kernel32 {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
class NamedPipeInstance;
|
||||||
|
|
||||||
void configureInheritability(int fd, bool inherit) {
|
void configureInheritability(int fd, bool inherit) {
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
return;
|
return;
|
||||||
@@ -29,8 +41,289 @@ void configureInheritability(int fd, bool inherit) {
|
|||||||
fcntl(fd, F_SETFD, flags);
|
fcntl(fd, F_SETFD, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ParsedPipeName {
|
||||||
|
std::string key;
|
||||||
|
std::u16string namespaceKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<ParsedPipeName> parsePipeName(LPCSTR name, DWORD &error) {
|
||||||
|
error = ERROR_SUCCESS;
|
||||||
|
if (!name) {
|
||||||
|
error = ERROR_PATH_NOT_FOUND;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::string_view input{name};
|
||||||
|
if (input.empty()) {
|
||||||
|
error = ERROR_INVALID_NAME;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
if (input.size() > 256) {
|
||||||
|
error = ERROR_INVALID_PARAMETER;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::string lower;
|
||||||
|
lower.reserve(input.size());
|
||||||
|
for (char ch : input) {
|
||||||
|
lower.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(ch))));
|
||||||
|
}
|
||||||
|
constexpr std::string_view kLocalPrefix = "\\\\.\\pipe\\";
|
||||||
|
constexpr std::string_view kNtPrefix = "\\\\?\\pipe\\";
|
||||||
|
size_t prefixLen = 0;
|
||||||
|
if (lower.rfind(kLocalPrefix, 0) == 0) {
|
||||||
|
prefixLen = kLocalPrefix.size();
|
||||||
|
} else if (lower.rfind(kNtPrefix, 0) == 0) {
|
||||||
|
prefixLen = kNtPrefix.size();
|
||||||
|
} else {
|
||||||
|
// Not a pipe path; treat as non-match without error.
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::string raw = std::string(input.substr(prefixLen));
|
||||||
|
if (raw.empty()) {
|
||||||
|
error = ERROR_INVALID_HANDLE;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
if (raw.find('\\') != std::string::npos || raw.find('/') != std::string::npos) {
|
||||||
|
error = ERROR_INVALID_NAME;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::string key = lower.substr(prefixLen);
|
||||||
|
return ParsedPipeName{std::move(key), stringToUtf16(lower.substr(prefixLen))};
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD normalizeMaxInstances(DWORD value) {
|
||||||
|
if (value == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (value >= PIPE_UNLIMITED_INSTANCES) {
|
||||||
|
return PIPE_UNLIMITED_INSTANCES;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NamedPipeState : ObjectBase {
|
||||||
|
static constexpr ObjectType kType = ObjectType::NamedPipe;
|
||||||
|
|
||||||
|
std::mutex mutex;
|
||||||
|
std::string key;
|
||||||
|
DWORD accessMode = PIPE_ACCESS_DUPLEX;
|
||||||
|
DWORD pipeType = PIPE_TYPE_BYTE;
|
||||||
|
DWORD defaultTimeout = 0;
|
||||||
|
DWORD maxInstances = PIPE_UNLIMITED_INSTANCES;
|
||||||
|
uint32_t instanceCount = 0;
|
||||||
|
std::vector<class NamedPipeInstance *> instances;
|
||||||
|
|
||||||
|
explicit NamedPipeState(std::string k) : ObjectBase(kType), key(std::move(k)) {}
|
||||||
|
~NamedPipeState() override { wibo::g_namespace.remove(this); }
|
||||||
|
|
||||||
|
void registerInstance(class NamedPipeInstance *inst) {
|
||||||
|
std::lock_guard lk(mutex);
|
||||||
|
instances.push_back(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterInstance(class NamedPipeInstance *inst) {
|
||||||
|
std::lock_guard lk(mutex);
|
||||||
|
auto it = std::find(instances.begin(), instances.end(), inst);
|
||||||
|
if (it != instances.end()) {
|
||||||
|
instances.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reserveInstance(DWORD access, DWORD type, DWORD timeout, DWORD maxAllowed, bool firstFlag, bool isNew,
|
||||||
|
DWORD &error) {
|
||||||
|
error = ERROR_SUCCESS;
|
||||||
|
std::lock_guard lk(mutex);
|
||||||
|
if (isNew) {
|
||||||
|
accessMode = access;
|
||||||
|
pipeType = type;
|
||||||
|
defaultTimeout = timeout;
|
||||||
|
maxInstances = maxAllowed;
|
||||||
|
} else {
|
||||||
|
if (accessMode != access || pipeType != type || defaultTimeout != timeout) {
|
||||||
|
error = ERROR_ACCESS_DENIED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (maxInstances != maxAllowed) {
|
||||||
|
error = ERROR_ACCESS_DENIED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (firstFlag && instanceCount > 0) {
|
||||||
|
error = ERROR_ACCESS_DENIED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (maxInstances != PIPE_UNLIMITED_INSTANCES && instanceCount >= maxInstances) {
|
||||||
|
error = ERROR_PIPE_BUSY;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++instanceCount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool releaseInstance() {
|
||||||
|
std::lock_guard lk(mutex);
|
||||||
|
if (instanceCount > 0) {
|
||||||
|
--instanceCount;
|
||||||
|
}
|
||||||
|
return instanceCount == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NamedPipeInstance final : FileObject {
|
||||||
|
Pin<NamedPipeState> state;
|
||||||
|
int companionFd = -1;
|
||||||
|
DWORD accessMode;
|
||||||
|
DWORD pipeMode;
|
||||||
|
bool clientConnected = false;
|
||||||
|
|
||||||
|
NamedPipeInstance(int fd, Pin<NamedPipeState> st, int companion, DWORD open, DWORD mode)
|
||||||
|
: FileObject(fd), state(std::move(st)), companionFd(companion), accessMode(open), pipeMode(mode) {
|
||||||
|
if (state) {
|
||||||
|
state->registerInstance(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~NamedPipeInstance() override {
|
||||||
|
if (companionFd >= 0) {
|
||||||
|
close(companionFd);
|
||||||
|
companionFd = -1;
|
||||||
|
}
|
||||||
|
if (state) {
|
||||||
|
state->unregisterInstance(this);
|
||||||
|
}
|
||||||
|
if (state) {
|
||||||
|
state->releaseInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canAcceptClient(DWORD desiredAccess) const {
|
||||||
|
if (companionFd < 0 || clientConnected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD access = accessMode & PIPE_ACCESS_DUPLEX;
|
||||||
|
switch (access) {
|
||||||
|
case PIPE_ACCESS_DUPLEX:
|
||||||
|
return (desiredAccess & (GENERIC_READ | GENERIC_WRITE)) != 0;
|
||||||
|
case PIPE_ACCESS_INBOUND:
|
||||||
|
return (desiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0;
|
||||||
|
case PIPE_ACCESS_OUTBOUND:
|
||||||
|
return (desiredAccess & (GENERIC_READ | FILE_READ_DATA)) != 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int takeCompanion() {
|
||||||
|
if (companionFd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int fd = companionFd;
|
||||||
|
companionFd = -1;
|
||||||
|
clientConnected = true;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreCompanion(int fd) {
|
||||||
|
companionFd = fd;
|
||||||
|
if (fd >= 0) {
|
||||||
|
clientConnected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Pin<NamedPipeInstance> acquireConnectableInstance(Pin<NamedPipeState> &state, DWORD desiredAccess, DWORD &error) {
|
||||||
|
if (!state) {
|
||||||
|
error = ERROR_FILE_NOT_FOUND;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::lock_guard lk(state->mutex);
|
||||||
|
for (auto *inst : state->instances) {
|
||||||
|
if (!inst) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inst->canAcceptClient(desiredAccess)) {
|
||||||
|
return Pin<NamedPipeInstance>::acquire(inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = ERROR_PIPE_BUSY;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
bool tryCreateFileNamedPipeA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||||
|
DWORD dwFlagsAndAttributes, HANDLE &outHandle) {
|
||||||
|
(void)dwShareMode;
|
||||||
|
(void)dwCreationDisposition;
|
||||||
|
|
||||||
|
DWORD parseError = ERROR_SUCCESS;
|
||||||
|
auto parsed = parsePipeName(lpFileName, parseError);
|
||||||
|
if (!parsed) {
|
||||||
|
if (parseError != ERROR_SUCCESS) {
|
||||||
|
wibo::lastError = parseError;
|
||||||
|
outHandle = INVALID_HANDLE_VALUE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto state = wibo::g_namespace.getAs<NamedPipeState>(parsed->namespaceKey);
|
||||||
|
if (!state) {
|
||||||
|
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||||
|
outHandle = INVALID_HANDLE_VALUE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD acquireError = ERROR_SUCCESS;
|
||||||
|
auto instancePin = acquireConnectableInstance(state, dwDesiredAccess, acquireError);
|
||||||
|
if (!instancePin) {
|
||||||
|
wibo::lastError = acquireError;
|
||||||
|
outHandle = INVALID_HANDLE_VALUE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clientFd = instancePin->takeCompanion();
|
||||||
|
if (clientFd < 0) {
|
||||||
|
wibo::lastError = ERROR_PIPE_BUSY;
|
||||||
|
outHandle = INVALID_HANDLE_VALUE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inherit = lpSecurityAttributes && lpSecurityAttributes->bInheritHandle;
|
||||||
|
configureInheritability(clientFd, inherit);
|
||||||
|
|
||||||
|
auto clientObj = make_pin<FileObject>(clientFd);
|
||||||
|
if (!clientObj) {
|
||||||
|
instancePin->restoreCompanion(clientFd);
|
||||||
|
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
outHandle = INVALID_HANDLE_VALUE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
clientFd = -1;
|
||||||
|
|
||||||
|
clientObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||||
|
clientObj->overlapped = (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) != 0;
|
||||||
|
|
||||||
|
uint32_t grantedAccess = SYNCHRONIZE;
|
||||||
|
switch (instancePin->accessMode & PIPE_ACCESS_DUPLEX) {
|
||||||
|
case PIPE_ACCESS_DUPLEX:
|
||||||
|
grantedAccess |= FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||||
|
break;
|
||||||
|
case PIPE_ACCESS_INBOUND:
|
||||||
|
grantedAccess |= FILE_GENERIC_WRITE;
|
||||||
|
break;
|
||||||
|
case PIPE_ACCESS_OUTBOUND:
|
||||||
|
grantedAccess |= FILE_GENERIC_READ;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t handleFlags = inherit ? HANDLE_FLAG_INHERIT : 0;
|
||||||
|
outHandle = wibo::handles().alloc(std::move(clientObj), grantedAccess, handleFlags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize) {
|
BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize) {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
DEBUG_LOG("CreatePipe(%p, %p, %p, %u)\n", hReadPipe, hWritePipe, lpPipeAttributes, nSize);
|
DEBUG_LOG("CreatePipe(%p, %p, %p, %u)\n", hReadPipe, hWritePipe, lpPipeAttributes, nSize);
|
||||||
@@ -66,4 +359,147 @@ BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRI
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HANDLE WIN_FUNC CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
|
||||||
|
DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
|
||||||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes) {
|
||||||
|
HOST_CONTEXT_GUARD();
|
||||||
|
DEBUG_LOG("CreateNamedPipeA(%s, 0x%08x, 0x%08x, %u, %u, %u, %u, %p)\n", lpName ? lpName : "(null)", dwOpenMode,
|
||||||
|
dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut, lpSecurityAttributes);
|
||||||
|
|
||||||
|
DWORD parseError = ERROR_SUCCESS;
|
||||||
|
std::optional<ParsedPipeName> parsed = parsePipeName(lpName, parseError);
|
||||||
|
if (!parsed) {
|
||||||
|
wibo::lastError = (parseError == ERROR_SUCCESS) ? ERROR_INVALID_NAME : parseError;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr DWORD kAllowedOpenFlags = PIPE_ACCESS_DUPLEX | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY |
|
||||||
|
FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED;
|
||||||
|
if ((dwOpenMode & ~kAllowedOpenFlags) != 0) {
|
||||||
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD accessMode = dwOpenMode & PIPE_ACCESS_DUPLEX;
|
||||||
|
if (accessMode != PIPE_ACCESS_DUPLEX && accessMode != PIPE_ACCESS_INBOUND && accessMode != PIPE_ACCESS_OUTBOUND) {
|
||||||
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool firstInstanceFlag = (dwOpenMode & FILE_FLAG_FIRST_PIPE_INSTANCE) != 0;
|
||||||
|
const bool inheritHandles = lpSecurityAttributes && lpSecurityAttributes->bInheritHandle;
|
||||||
|
const bool overlapped = (dwOpenMode & FILE_FLAG_OVERLAPPED) != 0;
|
||||||
|
|
||||||
|
constexpr DWORD kAllowedPipeModeFlags =
|
||||||
|
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS;
|
||||||
|
if ((dwPipeMode & ~kAllowedPipeModeFlags) != 0) {
|
||||||
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
if ((dwPipeMode & PIPE_READMODE_MESSAGE) != 0 && (dwPipeMode & PIPE_TYPE_MESSAGE) == 0) {
|
||||||
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD pipeType = (dwPipeMode & PIPE_TYPE_MESSAGE) != 0 ? PIPE_TYPE_MESSAGE : PIPE_TYPE_BYTE;
|
||||||
|
DWORD normalizedMaxInstances = normalizeMaxInstances(nMaxInstances);
|
||||||
|
|
||||||
|
auto [state, isNewState] = wibo::g_namespace.getOrCreate(
|
||||||
|
parsed->namespaceKey, [&]() -> NamedPipeState * { return new NamedPipeState(parsed->key); });
|
||||||
|
if (!state) {
|
||||||
|
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool instanceReserved = false;
|
||||||
|
DWORD reserveError = ERROR_SUCCESS;
|
||||||
|
if (!state->reserveInstance(accessMode, pipeType, nDefaultTimeOut, normalizedMaxInstances, firstInstanceFlag,
|
||||||
|
isNewState, reserveError)) {
|
||||||
|
wibo::lastError = reserveError;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
instanceReserved = true;
|
||||||
|
|
||||||
|
int serverFd = -1;
|
||||||
|
int companionFd = -1;
|
||||||
|
auto fail = [&](DWORD err) -> HANDLE {
|
||||||
|
if (serverFd >= 0) {
|
||||||
|
close(serverFd);
|
||||||
|
serverFd = -1;
|
||||||
|
}
|
||||||
|
if (companionFd >= 0) {
|
||||||
|
close(companionFd);
|
||||||
|
companionFd = -1;
|
||||||
|
}
|
||||||
|
if (instanceReserved && state) {
|
||||||
|
state->releaseInstance();
|
||||||
|
instanceReserved = false;
|
||||||
|
}
|
||||||
|
wibo::lastError = err;
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
};
|
||||||
|
|
||||||
|
int fds[2] = {-1, -1};
|
||||||
|
if (accessMode == PIPE_ACCESS_DUPLEX) {
|
||||||
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
|
||||||
|
int savedErrno = errno;
|
||||||
|
return fail(wibo::winErrorFromErrno(savedErrno));
|
||||||
|
}
|
||||||
|
serverFd = fds[0];
|
||||||
|
companionFd = fds[1];
|
||||||
|
} else {
|
||||||
|
if (pipe(fds) != 0) {
|
||||||
|
int savedErrno = errno;
|
||||||
|
return fail(wibo::winErrorFromErrno(savedErrno));
|
||||||
|
}
|
||||||
|
if (accessMode == PIPE_ACCESS_INBOUND) {
|
||||||
|
serverFd = fds[0];
|
||||||
|
companionFd = fds[1];
|
||||||
|
if (nInBufferSize != 0) {
|
||||||
|
fcntl(serverFd, F_SETPIPE_SZ, static_cast<int>(nInBufferSize));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serverFd = fds[1];
|
||||||
|
companionFd = fds[0];
|
||||||
|
if (nOutBufferSize != 0) {
|
||||||
|
fcntl(serverFd, F_SETPIPE_SZ, static_cast<int>(nOutBufferSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configureInheritability(serverFd, inheritHandles);
|
||||||
|
if (companionFd >= 0) {
|
||||||
|
configureInheritability(companionFd, inheritHandles);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pipeObj = make_pin<NamedPipeInstance>(serverFd, state.clone(), companionFd, accessMode, dwPipeMode);
|
||||||
|
if (!pipeObj) {
|
||||||
|
return fail(ERROR_NOT_ENOUGH_MEMORY);
|
||||||
|
}
|
||||||
|
serverFd = -1;
|
||||||
|
companionFd = -1;
|
||||||
|
instanceReserved = false;
|
||||||
|
|
||||||
|
pipeObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||||
|
pipeObj->overlapped = overlapped;
|
||||||
|
|
||||||
|
uint32_t grantedAccess = SYNCHRONIZE;
|
||||||
|
switch (accessMode) {
|
||||||
|
case PIPE_ACCESS_DUPLEX:
|
||||||
|
grantedAccess |= FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||||
|
break;
|
||||||
|
case PIPE_ACCESS_INBOUND:
|
||||||
|
grantedAccess |= FILE_GENERIC_READ;
|
||||||
|
break;
|
||||||
|
case PIPE_ACCESS_OUTBOUND:
|
||||||
|
grantedAccess |= FILE_GENERIC_WRITE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t handleFlags = inheritHandles ? HANDLE_FLAG_INHERIT : 0;
|
||||||
|
return wibo::handles().alloc(std::move(pipeObj), grantedAccess, handleFlags);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace kernel32
|
} // namespace kernel32
|
||||||
|
|||||||
@@ -6,5 +6,11 @@
|
|||||||
namespace kernel32 {
|
namespace kernel32 {
|
||||||
|
|
||||||
BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
|
BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
|
||||||
|
HANDLE WIN_FUNC CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
|
||||||
|
DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
|
||||||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||||
|
bool tryCreateFileNamedPipeA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||||
|
DWORD dwFlagsAndAttributes, HANDLE &outHandle);
|
||||||
|
|
||||||
} // namespace kernel32
|
} // namespace kernel32
|
||||||
|
|||||||
19
src/common.h
19
src/common.h
@@ -111,8 +111,10 @@ constexpr DWORD STILL_ACTIVE = 259;
|
|||||||
|
|
||||||
constexpr DWORD FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
constexpr DWORD FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
||||||
constexpr DWORD FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
|
constexpr DWORD FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
|
||||||
|
constexpr DWORD FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000;
|
||||||
constexpr DWORD FILE_FLAG_NO_BUFFERING = 0x20000000;
|
constexpr DWORD FILE_FLAG_NO_BUFFERING = 0x20000000;
|
||||||
constexpr DWORD FILE_FLAG_OVERLAPPED = 0x40000000;
|
constexpr DWORD FILE_FLAG_OVERLAPPED = 0x40000000;
|
||||||
|
constexpr DWORD FILE_FLAG_WRITE_THROUGH = 0x80000000;
|
||||||
|
|
||||||
constexpr DWORD STD_INPUT_HANDLE = ((DWORD)-10);
|
constexpr DWORD STD_INPUT_HANDLE = ((DWORD)-10);
|
||||||
constexpr DWORD STD_OUTPUT_HANDLE = ((DWORD)-11);
|
constexpr DWORD STD_OUTPUT_HANDLE = ((DWORD)-11);
|
||||||
@@ -135,6 +137,9 @@ constexpr DWORD FILE_WRITE_ATTRIBUTES = 0x00000100;
|
|||||||
|
|
||||||
constexpr DWORD SYNCHRONIZE = 0x00100000;
|
constexpr DWORD SYNCHRONIZE = 0x00100000;
|
||||||
constexpr DWORD DELETE = 0x00010000;
|
constexpr DWORD DELETE = 0x00010000;
|
||||||
|
constexpr DWORD WRITE_DAC = 0x00040000;
|
||||||
|
constexpr DWORD WRITE_OWNER = 0x00080000;
|
||||||
|
constexpr DWORD ACCESS_SYSTEM_SECURITY = 0x01000000;
|
||||||
|
|
||||||
constexpr DWORD STANDARD_RIGHTS_READ = 0x00020000;
|
constexpr DWORD STANDARD_RIGHTS_READ = 0x00020000;
|
||||||
constexpr DWORD STANDARD_RIGHTS_WRITE = 0x00020000;
|
constexpr DWORD STANDARD_RIGHTS_WRITE = 0x00020000;
|
||||||
@@ -201,6 +206,20 @@ constexpr DWORD FILE_SHARE_READ = 0x00000001;
|
|||||||
constexpr DWORD FILE_SHARE_WRITE = 0x00000002;
|
constexpr DWORD FILE_SHARE_WRITE = 0x00000002;
|
||||||
constexpr DWORD FILE_SHARE_DELETE = 0x00000004;
|
constexpr DWORD FILE_SHARE_DELETE = 0x00000004;
|
||||||
|
|
||||||
|
constexpr DWORD PIPE_ACCESS_INBOUND = 0x00000001;
|
||||||
|
constexpr DWORD PIPE_ACCESS_OUTBOUND = 0x00000002;
|
||||||
|
constexpr DWORD PIPE_ACCESS_DUPLEX = 0x00000003;
|
||||||
|
|
||||||
|
constexpr DWORD PIPE_TYPE_BYTE = 0x00000000;
|
||||||
|
constexpr DWORD PIPE_TYPE_MESSAGE = 0x00000004;
|
||||||
|
constexpr DWORD PIPE_READMODE_BYTE = 0x00000000;
|
||||||
|
constexpr DWORD PIPE_READMODE_MESSAGE = 0x00000002;
|
||||||
|
constexpr DWORD PIPE_WAIT = 0x00000000;
|
||||||
|
constexpr DWORD PIPE_NOWAIT = 0x00000001;
|
||||||
|
constexpr DWORD PIPE_ACCEPT_REMOTE_CLIENTS = 0x00000000;
|
||||||
|
constexpr DWORD PIPE_REJECT_REMOTE_CLIENTS = 0x00000008;
|
||||||
|
constexpr DWORD PIPE_UNLIMITED_INSTANCES = 255;
|
||||||
|
|
||||||
struct UNICODE_STRING {
|
struct UNICODE_STRING {
|
||||||
unsigned short Length;
|
unsigned short Length;
|
||||||
unsigned short MaximumLength;
|
unsigned short MaximumLength;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#define ERROR_IO_INCOMPLETE 996
|
#define ERROR_IO_INCOMPLETE 996
|
||||||
#define ERROR_IO_PENDING 997
|
#define ERROR_IO_PENDING 997
|
||||||
#define ERROR_OPERATION_ABORTED 995
|
#define ERROR_OPERATION_ABORTED 995
|
||||||
|
#define ERROR_PIPE_BUSY 231
|
||||||
#define ERROR_NONE_MAPPED 1332
|
#define ERROR_NONE_MAPPED 1332
|
||||||
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
||||||
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ enum class ObjectType : uint16_t {
|
|||||||
Thread,
|
Thread,
|
||||||
Heap,
|
Heap,
|
||||||
RegistryKey,
|
RegistryKey,
|
||||||
|
NamedPipe,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ObjectFlags : uint16_t {
|
enum ObjectFlags : uint16_t {
|
||||||
|
|||||||
@@ -232,6 +232,15 @@ std::vector<uint16_t> stringToWideString(const char *src, size_t length) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::u16string stringToUtf16(std::string_view str) {
|
||||||
|
std::u16string result;
|
||||||
|
result.reserve(str.size());
|
||||||
|
for (unsigned char ch : str) {
|
||||||
|
result.push_back(static_cast<char16_t>(ch));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
long wstrtol(const uint16_t *string, uint16_t **end_ptr, int base) {
|
long wstrtol(const uint16_t *string, uint16_t **end_ptr, int base) {
|
||||||
if (!string) {
|
if (!string) {
|
||||||
if (end_ptr)
|
if (end_ptr)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ uint16_t *wstrcpy(uint16_t *dest, const uint16_t *src);
|
|||||||
size_t wstrncpy(uint16_t *dst, const uint16_t *src, size_t n);
|
size_t wstrncpy(uint16_t *dst, const uint16_t *src, size_t n);
|
||||||
std::string wideStringToString(const uint16_t *src, int len = -1);
|
std::string wideStringToString(const uint16_t *src, int len = -1);
|
||||||
std::vector<uint16_t> stringToWideString(const char *src, size_t length = static_cast<size_t>(-1));
|
std::vector<uint16_t> stringToWideString(const char *src, size_t length = static_cast<size_t>(-1));
|
||||||
|
std::u16string stringToUtf16(std::string_view str);
|
||||||
long wstrtol(const uint16_t *string, uint16_t **end_ptr, int base);
|
long wstrtol(const uint16_t *string, uint16_t **end_ptr, int base);
|
||||||
unsigned long wstrtoul(const uint16_t *string, uint16_t **end_ptr, int base);
|
unsigned long wstrtoul(const uint16_t *string, uint16_t **end_ptr, int base);
|
||||||
void toLowerInPlace(std::string &str);
|
void toLowerInPlace(std::string &str);
|
||||||
|
|||||||
74
test/test_namedpipe.c
Normal file
74
test/test_namedpipe.c
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char *kPipeName = "\\\\.\\pipe\\wibo_test_namedpipe";
|
||||||
|
|
||||||
|
static void write_checked(HANDLE handle, const char *msg) {
|
||||||
|
DWORD written = 0;
|
||||||
|
TEST_CHECK(WriteFile(handle, msg, (DWORD)strlen(msg), &written, NULL));
|
||||||
|
TEST_CHECK_EQ((DWORD)strlen(msg), written);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_checked(HANDLE handle, const char *expected) {
|
||||||
|
char buffer[64] = {0};
|
||||||
|
DWORD read = 0;
|
||||||
|
TEST_CHECK(ReadFile(handle, buffer, sizeof(buffer), &read, NULL));
|
||||||
|
TEST_CHECK_EQ((DWORD)strlen(expected), read);
|
||||||
|
TEST_CHECK(memcmp(buffer, expected, read) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
SetLastError(0xdeadbeefu);
|
||||||
|
HANDLE missing = CreateFileA(kPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
TEST_CHECK(missing == INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_FILE_NOT_FOUND, GetLastError());
|
||||||
|
|
||||||
|
HANDLE pipe = CreateNamedPipeA(kPipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,
|
||||||
|
1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(pipe != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
HANDLE client =
|
||||||
|
CreateFileA(kPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
TEST_CHECK(client != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
write_checked(client, "ping");
|
||||||
|
read_checked(pipe, "ping");
|
||||||
|
|
||||||
|
write_checked(pipe, "pong");
|
||||||
|
read_checked(client, "pong");
|
||||||
|
|
||||||
|
HANDLE invalidPrefix = CreateNamedPipeA("\\\\.\\pipe\\", PIPE_ACCESS_DUPLEX,
|
||||||
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(invalidPrefix == INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
|
||||||
|
|
||||||
|
SetLastError(0xdeadbeefu);
|
||||||
|
HANDLE busy = CreateFileA(kPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
TEST_CHECK(busy == INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_PIPE_BUSY, GetLastError());
|
||||||
|
|
||||||
|
SetLastError(0xdeadbeefu);
|
||||||
|
HANDLE invalidName = CreateNamedPipeA("invalid", PIPE_ACCESS_DUPLEX,
|
||||||
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(invalidName == INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_INVALID_NAME, GetLastError());
|
||||||
|
|
||||||
|
HANDLE badMode = CreateNamedPipeA(kPipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||||
|
1, 1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(badMode == INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
|
||||||
|
|
||||||
|
HANDLE nullName = CreateNamedPipeA(NULL, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,
|
||||||
|
1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(nullName == INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_PATH_NOT_FOUND, GetLastError());
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(client));
|
||||||
|
TEST_CHECK(CloseHandle(pipe));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user