Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibCore/System: Implement conversion between handles and file descriptors #2946

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 29 additions & 20 deletions AK/Error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#ifdef AK_OS_WINDOWS
# include <AK/ByteString.h>
# include <AK/HashMap.h>
# include <stdio.h>
# include <windows.h>
#endif

Expand All @@ -21,31 +22,39 @@ Error Error::from_string_view_or_print_error_and_return_errno(StringView string_
}

#ifdef AK_OS_WINDOWS
Error Error::from_windows_error(DWORD code)
Error Error::from_windows_error(int code)
{
static HashMap<DWORD, ByteString> windows_errors;
static HashMap<int, ByteString> windows_errors;

auto string = windows_errors.get(code);
if (string.has_value()) {
if (string.has_value())
return Error::from_string_view(string->view());
} else {
char* message = nullptr;
auto size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&message,
0,
nullptr);

if (size == 0)
return Error::from_string_view_or_print_error_and_return_errno("Unknown error"sv, code);

windows_errors.set(code, { message, size });
LocalFree(message);
return from_windows_error(code);

char* message = 0;
auto size = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&message,
0,
NULL);

if (size == 0) {
static char buffer[128];
snprintf(buffer, _countof(buffer), "Error 0x%08lX while getting text of error 0x%08X", GetLastError(), code);
return Error::from_string_view({ buffer, _countof(buffer) });
}

windows_errors.set(code, { message, size });
LocalFree(message);
return from_windows_error(code);
}

// This can be used both for generic Windows errors and for winsock errors because WSAGetLastError is forwarded to GetLastError.
Error Error::from_windows_error()
{
return from_windows_error(GetLastError());
}
#endif

Expand Down
8 changes: 2 additions & 6 deletions AK/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,8 @@
#pragma once

#include <AK/StringView.h>
#include <AK/Try.h>
#include <AK/Variant.h>
#include <errno.h>
#include <string.h>
#ifdef AK_OS_WINDOWS
typedef unsigned long DWORD;
#endif

namespace AK {

Expand All @@ -29,7 +24,8 @@ class [[nodiscard]] Error {
}

#ifdef AK_OS_WINDOWS
static Error from_windows_error(DWORD code);
static Error from_windows_error(int code);
static Error from_windows_error();
#endif

// NOTE: For calling this method from within kernel code, we will simply print
Expand Down
16 changes: 9 additions & 7 deletions Libraries/LibCore/AnonymousBufferWindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
*/

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

#include <AK/Windows.h>

namespace Core {

Expand All @@ -23,25 +25,25 @@ 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)
{
HANDLE map_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, size >> 31 >> 1, size & 0xFFFFFFFF, NULL);
if (!map_handle)
return Error::from_windows_error(GetLastError());
return Error::from_windows_error();

return create((int)(intptr_t)map_handle, size);
return create(handle_to_fd(map_handle, System::FileMappingHandle), size);
}

ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(int fd, size_t size)
{
void* ptr = MapViewOfFile((HANDLE)(intptr_t)fd, FILE_MAP_ALL_ACCESS, 0, 0, size);
void* ptr = MapViewOfFile(System::fd_to_handle(fd), FILE_MAP_ALL_ACCESS, 0, 0, size);
if (!ptr)
return Error::from_windows_error(GetLastError());
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
7 changes: 4 additions & 3 deletions Libraries/LibCore/EventLoopImplementationWindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

#include <LibCore/EventLoopImplementationWindows.h>
#include <LibCore/Notifier.h>
#include <LibCore/System.h>
#include <LibCore/ThreadEventQueue.h>
#include <WinSock2.h>
#include <io.h>

#include <AK/Windows.h>

struct Handle {
HANDLE handle = NULL;
Expand Down Expand Up @@ -181,7 +182,7 @@ void EventLoopManagerWindows::register_notifier(Notifier& notifier)
{
HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
VERIFY(event);
SOCKET socket = _get_osfhandle(notifier.fd());
SOCKET socket = (SOCKET)System::fd_to_handle(notifier.fd());
VERIFY(socket != INVALID_SOCKET);
int rc = WSAEventSelect(socket, event, notifier_type_to_network_event(notifier.type()));
VERIFY(rc == 0);
Expand Down
8 changes: 4 additions & 4 deletions Libraries/LibCore/ProcessWindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
&process_info);

if (!result)
return Error::from_windows_error(GetLastError());
return Error::from_windows_error();

CloseHandle(process_info.hThread);

Expand Down Expand Up @@ -108,7 +108,7 @@ ErrorOr<String> Process::get_name()

DWORD length = GetModuleFileNameW(NULL, path, MAX_PATH);
if (!length)
return Error::from_windows_error(GetLastError());
return Error::from_windows_error();

return String::from_utf16(Utf16View { { (u16*)path, length } });
}
Expand Down Expand Up @@ -150,11 +150,11 @@ ErrorOr<int> Process::wait_for_termination()
{
auto result = WaitForSingleObject(m_handle, INFINITE);
if (result == WAIT_FAILED)
return Error::from_windows_error(GetLastError());
return Error::from_windows_error();

DWORD exit_code = 0;
if (!GetExitCodeProcess(m_handle, &exit_code))
return Error::from_windows_error(GetLastError());
return Error::from_windows_error();

return exit_code;
}
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);
}

}
13 changes: 13 additions & 0 deletions Libraries/LibCore/System.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ using socklen_t = int;

namespace Core::System {

#ifdef AK_OS_WINDOWS
enum HandleType {
FileHandle,
DirectoryHandle,
SocketHandle,
FileMappingHandle
};
int handle_to_fd(intptr_t handle, HandleType);
int handle_to_fd(void* handle, HandleType);
void* fd_to_handle(int fd);
#endif

#if !defined(AK_OS_MACOS) && !defined(AK_OS_HAIKU)
ErrorOr<int> accept4(int sockfd, struct sockaddr*, socklen_t*, int flags);
#endif
Expand Down Expand Up @@ -180,5 +192,6 @@ ErrorOr<void> set_resource_limits(int resource, rlim_t limit);
#endif

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

}
107 changes: 99 additions & 8 deletions Libraries/LibCore/SystemWindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,58 @@
#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();

int handle_to_fd(HANDLE handle, HandleType type)
{
return handle_to_fd((intptr_t)handle, type);
}

int handle_to_fd(intptr_t handle, HandleType type)
{
if (type != SocketHandle && type != FileMappingHandle)
return _open_osfhandle(handle, 0);

// Special treatment for socket and file mapping handles because:
// * _open_osfhandle doesn't support file mapping handles
// * _close doesn't properly support socket handles (it calls CloseHandle instead of closesocket)
// Handle value is held in lower 31 bits, and sign bit is set to indicate this is not a regular fd.
VERIFY((handle >> 31) == 0); // must be 0 ⩽ handle ⩽ 0x7FFFFFFF
return (1 << 31) | handle;
}

HANDLE fd_to_handle(int fd)
{
if (fd >= 0)
return (HANDLE)_get_osfhandle(fd);
if (fd == -1)
return INVALID_HANDLE_VALUE;
return (HANDLE)(intptr_t)(fd & ~(1 << 31));
}

ErrorOr<int> open(StringView path, int options, mode_t mode)
{
ByteString string_path = path;
Expand All @@ -30,10 +75,8 @@ ErrorOr<int> open(StringView path, int options, mode_t mode)
if (::stat(sz_path, &st) == 0 && (st.st_mode & S_IFDIR)) {
HANDLE dir_handle = CreateFile(sz_path, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (dir_handle == INVALID_HANDLE_VALUE)
return Error::from_windows_error(GetLastError());
int dir_fd = _open_osfhandle((intptr_t)dir_handle, 0);
if (dir_fd != -1)
return dir_fd;
return Error::from_windows_error();
return handle_to_fd(dir_handle, DirectoryHandle);
}
return Error::from_syscall("open"sv, -error);
}
Expand All @@ -42,6 +85,20 @@ ErrorOr<int> open(StringView path, int options, mode_t mode)

ErrorOr<void> close(int fd)
{
if (fd < 0) {
HANDLE handle = fd_to_handle(fd);
if (handle == INVALID_HANDLE_VALUE)
return Error::from_string_literal("Invalid file descriptor");
if (is_socket(fd)) {
if (closesocket((SOCKET)handle))
return Error::from_windows_error();
} else {
if (!CloseHandle(handle))
return Error::from_windows_error();
}
return {};
}

if (_close(fd) < 0)
return Error::from_syscall("close"sv, -errno);
return {};
Expand Down Expand Up @@ -83,8 +140,8 @@ ErrorOr<void> ftruncate(int fd, off_t length)
if (result.is_error())
return result.release_error();

if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0)
return Error::from_windows_error(GetLastError());
if (SetEndOfFile(fd_to_handle(fd)) == 0)
return Error::from_windows_error();
return {};
}

Expand Down Expand Up @@ -189,4 +246,38 @@ int getpid()
return GetCurrentProcessId();
}

ErrorOr<int> dup(int fd)
{
if (fd < 0) {
HANDLE handle = fd_to_handle(fd);
if (handle == INVALID_HANDLE_VALUE)
return Error::from_string_literal("Invalid file descriptor");

if (is_socket(fd)) {
WSAPROTOCOL_INFO pi = {};
if (WSADuplicateSocket((SOCKET)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 handle_to_fd(socket, SocketHandle);
} else {
if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
return Error::from_windows_error();
return handle_to_fd(handle, FileMappingHandle);
}
}

int new_fd = _dup(fd);
if (new_fd < 0)
return Error::from_syscall("dup"sv, -errno);
return new_fd;
}

bool is_socket(int fd)
{
int val, len = sizeof(val);
return !::getsockopt((SOCKET)fd_to_handle(fd), SOL_SOCKET, SO_TYPE, (char*)&val, &len);
}

}
Loading