diff --git a/AK/Error.cpp b/AK/Error.cpp index 7ef941449f8..36a5a545ef8 100644 --- a/AK/Error.cpp +++ b/AK/Error.cpp @@ -10,6 +10,7 @@ #ifdef AK_OS_WINDOWS # include # include +# include # include #endif @@ -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 windows_errors; + static HashMap 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); + 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); - windows_errors.set(code, { message, size }); - LocalFree(message); - return from_windows_error(code); + 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 diff --git a/AK/Error.h b/AK/Error.h index fd2b704432d..36a44dbea0d 100644 --- a/AK/Error.h +++ b/AK/Error.h @@ -7,13 +7,8 @@ #pragma once #include -#include #include #include -#include -#ifdef AK_OS_WINDOWS -typedef unsigned long DWORD; -#endif namespace AK { @@ -29,7 +24,8 @@ public: } #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 diff --git a/Libraries/LibCore/AnonymousBufferWindows.cpp b/Libraries/LibCore/AnonymousBufferWindows.cpp index a0089a290ec..e6868ffb2fd 100644 --- a/Libraries/LibCore/AnonymousBufferWindows.cpp +++ b/Libraries/LibCore/AnonymousBufferWindows.cpp @@ -6,7 +6,9 @@ */ #include -#include +#include + +#include namespace Core { @@ -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> 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> 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::create_with_size(size_t size) diff --git a/Libraries/LibCore/EventLoopImplementationWindows.cpp b/Libraries/LibCore/EventLoopImplementationWindows.cpp index d233347533a..1a69da12a13 100644 --- a/Libraries/LibCore/EventLoopImplementationWindows.cpp +++ b/Libraries/LibCore/EventLoopImplementationWindows.cpp @@ -7,9 +7,10 @@ #include #include +#include #include -#include -#include + +#include struct Handle { HANDLE handle = NULL; @@ -180,7 +181,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); diff --git a/Libraries/LibCore/ProcessWindows.cpp b/Libraries/LibCore/ProcessWindows.cpp index 61219a3eeef..59cdeb80444 100644 --- a/Libraries/LibCore/ProcessWindows.cpp +++ b/Libraries/LibCore/ProcessWindows.cpp @@ -71,7 +71,7 @@ ErrorOr Process::spawn(ProcessSpawnOptions const& options) &process_info); if (!result) - return Error::from_windows_error(GetLastError()); + return Error::from_windows_error(); CloseHandle(process_info.hThread); @@ -108,7 +108,7 @@ ErrorOr 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 } }); } @@ -150,11 +150,11 @@ ErrorOr 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; } diff --git a/Libraries/LibCore/System.cpp b/Libraries/LibCore/System.cpp index 500e8eaec08..4d6d65f4dc6 100644 --- a/Libraries/LibCore/System.cpp +++ b/Libraries/LibCore/System.cpp @@ -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); +} + } diff --git a/Libraries/LibCore/System.h b/Libraries/LibCore/System.h index 850e9324e8b..f8640275ef8 100644 --- a/Libraries/LibCore/System.h +++ b/Libraries/LibCore/System.h @@ -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 accept4(int sockfd, struct sockaddr*, socklen_t*, int flags); #endif @@ -180,5 +192,6 @@ ErrorOr set_resource_limits(int resource, rlim_t limit); #endif int getpid(); +bool is_socket(int fd); } diff --git a/Libraries/LibCore/SystemWindows.cpp b/Libraries/LibCore/SystemWindows.cpp index 8fe6f69021f..5844bbc64ad 100644 --- a/Libraries/LibCore/SystemWindows.cpp +++ b/Libraries/LibCore/SystemWindows.cpp @@ -12,13 +12,58 @@ #include #include #include -#include #include -#include #include +#include + 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 open(StringView path, int options, mode_t mode) { ByteString string_path = path; @@ -30,10 +75,8 @@ ErrorOr 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); } @@ -42,6 +85,20 @@ ErrorOr open(StringView path, int options, mode_t mode) ErrorOr 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 {}; @@ -83,8 +140,8 @@ ErrorOr 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 {}; } @@ -189,4 +246,38 @@ int getpid() return GetCurrentProcessId(); } +ErrorOr 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); +} + }