Skip to content

Commit

Permalink
LibCore: Consistently treat file descriptors as handles on Windows
Browse files Browse the repository at this point in the history
Also:
 * implement dup and is_socket
 * implement and call init_crt_and_wsa
  • Loading branch information
stasoid committed Jan 7, 2025
1 parent 53afb66 commit 2f3acb9
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 42 deletions.
10 changes: 6 additions & 4 deletions Libraries/LibCore/AnonymousBufferWindows.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/*
* Copyright (c) 2021, Andreas Kling <[email protected]>
* Copyright (c) 2024, stasoid <[email protected]>
* Copyright (c) 2024-2025, stasoid <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibCore/AnonymousBuffer.h>
#include <windows.h>
#include <LibCore/System.h>

#include <AK/Windows.h>

namespace Core {

Expand All @@ -23,7 +25,7 @@ AnonymousBufferImpl::~AnonymousBufferImpl()
VERIFY(UnmapViewOfFile(m_data));

if (m_fd != -1)
VERIFY(CloseHandle((HANDLE)(intptr_t)m_fd));
MUST(System::close(m_fd));
}

ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t size)
Expand All @@ -41,7 +43,7 @@ ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(int fd,
if (!ptr)
return Error::from_windows_error();

return adopt_nonnull_ref_or_enomem(new (nothrow) AnonymousBufferImpl(fd, size, ptr));
return adopt_ref(*new AnonymousBufferImpl(fd, size, ptr));
}

ErrorOr<AnonymousBuffer> AnonymousBuffer::create_with_size(size_t size)
Expand Down
8 changes: 3 additions & 5 deletions Libraries/LibCore/EventLoopImplementationWindows.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Andreas Kling <[email protected]>
* Copyright (c) 2024, stasoid <[email protected]>
* Copyright (c) 2024-2025, stasoid <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
Expand Down Expand Up @@ -180,10 +180,8 @@ void EventLoopManagerWindows::register_notifier(Notifier& notifier)
{
HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
VERIFY(event);
SOCKET socket = _get_osfhandle(notifier.fd());
VERIFY(socket != INVALID_SOCKET);
int rc = WSAEventSelect(socket, event, notifier_type_to_network_event(notifier.type()));
VERIFY(rc == 0);
int rc = WSAEventSelect(notifier.fd(), event, notifier_type_to_network_event(notifier.type()));
VERIFY(!rc);

auto& notifiers = ThreadData::the().notifiers;
VERIFY(!notifiers.get(event).has_value());
Expand Down
6 changes: 6 additions & 0 deletions Libraries/LibCore/System.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,4 +1004,10 @@ int getpid()
return ::getpid();
}

bool is_socket(int fd)
{
auto result = fstat(fd);
return !result.is_error() && S_ISSOCK(result.value().st_mode);
}

}
1 change: 1 addition & 0 deletions Libraries/LibCore/System.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,6 @@ ErrorOr<void> set_resource_limits(int resource, rlim_t limit);
#endif

int getpid();
bool is_socket(int fd);

}
114 changes: 81 additions & 33 deletions Libraries/LibCore/SystemWindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,102 @@
* Copyright (c) 2021-2022, Sam Atkins <[email protected]>
* Copyright (c) 2022, Matthias Zimmerman <[email protected]>
* Copyright (c) 2023, Cameron Youell <[email protected]>
* Copyright (c) 2024, stasoid <[email protected]>
* Copyright (c) 2024-2025, stasoid <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/ByteString.h>
#include <AK/ScopeGuard.h>
#include <LibCore/System.h>
#include <Windows.h>
#include <direct.h>
#include <io.h>
#include <sys/mman.h>

#include <AK/Windows.h>

namespace Core::System {

static void invalid_parameter_handler(wchar_t const*, wchar_t const*, wchar_t const*, unsigned int, uintptr_t)
{
}

static int init_crt_and_wsa()
{
WSADATA wsa;
WORD version = MAKEWORD(2, 2);
int rc = WSAStartup(version, &wsa);
VERIFY(!rc && wsa.wVersion == version);

// Make _get_osfhandle return -1 instead of crashing on invalid fd in release (debug still __debugbreak's)
_set_invalid_parameter_handler(invalid_parameter_handler);
return 0;
}

static auto dummy = init_crt_and_wsa();

ErrorOr<int> open(StringView path, int options, mode_t mode)
{
int fd = _open(ByteString(path).characters(), options | O_BINARY | _O_OBTAIN_DIR, mode);
if (fd < 0)
return Error::from_syscall("open"sv, -errno);
return fd;
ScopeGuard guard = [&] { _close(fd); };
return dup(_get_osfhandle(fd));
}

ErrorOr<void> close(int fd)
{
if (_close(fd) < 0)
return Error::from_syscall("close"sv, -errno);
if (is_socket(fd)) {
if (closesocket(fd))
return Error::from_windows_error();
} else {
if (!CloseHandle((HANDLE)(intptr_t)fd))
return Error::from_windows_error();
}
return {};
}

ErrorOr<ssize_t> read(int fd, Bytes buffer)
ErrorOr<ssize_t> read(int handle, Bytes buffer)
{
int rc = _read(fd, buffer.data(), buffer.size());
if (rc < 0)
return Error::from_syscall("read"sv, -errno);
return rc;
DWORD n_read = 0;
if (!ReadFile((HANDLE)(intptr_t)handle, buffer.data(), buffer.size(), &n_read, NULL))
return Error::from_windows_error();
return n_read;
}

ErrorOr<ssize_t> write(int fd, ReadonlyBytes buffer)
ErrorOr<ssize_t> write(int handle, ReadonlyBytes buffer)
{
int rc = _write(fd, buffer.data(), buffer.size());
if (rc < 0)
return Error::from_syscall("write"sv, -errno);
return rc;
DWORD n_written = 0;
if (!WriteFile((HANDLE)(intptr_t)handle, buffer.data(), buffer.size(), &n_written, NULL))
return Error::from_windows_error();
return n_written;
}

ErrorOr<off_t> lseek(int fd, off_t offset, int whence)
ErrorOr<off_t> lseek(int handle, off_t offset, int origin)
{
long rc = _lseek(fd, offset, whence);
if (rc < 0)
return Error::from_syscall("lseek"sv, -errno);
return rc;
static_assert(FILE_BEGIN == SEEK_SET && FILE_CURRENT == SEEK_CUR && FILE_END == SEEK_END, "SetFilePointerEx origin values are incompatible with lseek");
LARGE_INTEGER new_pointer = {};
if (!SetFilePointerEx((HANDLE)(intptr_t)handle, {.QuadPart = offset}, &new_pointer, origin))
return Error::from_windows_error();
return new_pointer.QuadPart;
}

ErrorOr<void> ftruncate(int fd, off_t length)
ErrorOr<void> ftruncate(int handle, off_t length)
{
long position = _tell(fd);
if (position == -1)
return Error::from_errno(errno);

ScopeGuard restore_position { [&] { _lseek(fd, position, SEEK_SET); } };
auto position = TRY(lseek(handle, 0, SEEK_CUR));
ScopeGuard restore_position = [&] { MUST(lseek(handle, position, SEEK_SET)); };

auto result = lseek(fd, length, SEEK_SET);
if (result.is_error())
return result.release_error();
TRY(lseek(handle, length, SEEK_SET));

if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0)
if (!SetEndOfFile((HANDLE)(intptr_t)handle))
return Error::from_windows_error();
return {};
}

ErrorOr<struct stat> fstat(int fd)
ErrorOr<struct stat> fstat(int handle)
{
struct stat st = {};
int fd = _open_osfhandle(TRY(dup(handle)), 0);
ScopeGuard guard = [&] { _close(fd); };
if (::fstat(fd, &st) < 0)
return Error::from_syscall("fstat"sv, -errno);
return st;
Expand Down Expand Up @@ -154,10 +176,12 @@ ErrorOr<struct stat> fstatat(int, StringView, int)
VERIFY_NOT_REACHED();
}

ErrorOr<void*> mmap(void* address, size_t size, int protection, int flags, int fd, off_t offset, size_t alignment, StringView)
ErrorOr<void*> mmap(void* address, size_t size, int protection, int flags, int file_handle, off_t offset, size_t alignment, StringView)
{
// custom alignment is not supported
VERIFY(!alignment);
int fd = _open_osfhandle(TRY(dup(file_handle)), 0);
ScopeGuard guard = [&] { _close(fd); };
void* ptr = ::mmap(address, size, protection, flags, fd, offset);
if (ptr == MAP_FAILED)
return Error::from_syscall("mmap"sv, -errno);
Expand All @@ -176,4 +200,28 @@ int getpid()
return GetCurrentProcessId();
}

ErrorOr<int> dup(int handle)
{
if (is_socket(handle)) {
WSAPROTOCOL_INFO pi = {};
if (WSADuplicateSocket(handle, GetCurrentProcessId(), &pi))
return Error::from_windows_error();
SOCKET socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, &pi, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT);
if (socket == INVALID_SOCKET)
return Error::from_windows_error();
return socket;
} else {
HANDLE new_handle = 0;
if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)(intptr_t)handle, GetCurrentProcess(), &new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
return Error::from_windows_error();
return (int)(intptr_t)new_handle;
}
}

bool is_socket(int handle)
{
// FILE_TYPE_PIPE is returned for sockets and pipes. We don't use Windows pipes.
return GetFileType((HANDLE)(intptr_t)handle) == FILE_TYPE_PIPE;
}

}

0 comments on commit 2f3acb9

Please sign in to comment.