Browse Source

LibCore/System: Add dup, fd_to_handle, handle_to_fd on Windows

Also:
 * teach close and dup to work with pseudo file descriptors
 * implement and call init_crt_and_wsa
 * add is_socket
stasoid 7 months ago
parent
commit
78e619804b

+ 7 - 5
Libraries/LibCore/AnonymousBufferWindows.cpp

@@ -6,7 +6,9 @@
  */
  */
 
 
 #include <LibCore/AnonymousBuffer.h>
 #include <LibCore/AnonymousBuffer.h>
-#include <windows.h>
+#include <LibCore/System.h>
+
+#include <AK/Windows.h>
 
 
 namespace Core {
 namespace Core {
 
 
@@ -23,7 +25,7 @@ AnonymousBufferImpl::~AnonymousBufferImpl()
         VERIFY(UnmapViewOfFile(m_data));
         VERIFY(UnmapViewOfFile(m_data));
 
 
     if (m_fd != -1)
     if (m_fd != -1)
-        VERIFY(CloseHandle((HANDLE)(intptr_t)m_fd));
+        MUST(System::close(m_fd));
 }
 }
 
 
 ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t size)
 ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t size)
@@ -32,16 +34,16 @@ ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t s
     if (!map_handle)
     if (!map_handle)
         return Error::from_windows_error();
         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)
 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)
     if (!ptr)
         return Error::from_windows_error();
         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)
 ErrorOr<AnonymousBuffer> AnonymousBuffer::create_with_size(size_t size)

+ 4 - 3
Libraries/LibCore/EventLoopImplementationWindows.cpp

@@ -7,9 +7,10 @@
 
 
 #include <LibCore/EventLoopImplementationWindows.h>
 #include <LibCore/EventLoopImplementationWindows.h>
 #include <LibCore/Notifier.h>
 #include <LibCore/Notifier.h>
+#include <LibCore/System.h>
 #include <LibCore/ThreadEventQueue.h>
 #include <LibCore/ThreadEventQueue.h>
-#include <WinSock2.h>
-#include <io.h>
+
+#include <AK/Windows.h>
 
 
 struct Handle {
 struct Handle {
     HANDLE handle = NULL;
     HANDLE handle = NULL;
@@ -181,7 +182,7 @@ void EventLoopManagerWindows::register_notifier(Notifier& notifier)
 {
 {
     HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
     HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
     VERIFY(event);
     VERIFY(event);
-    SOCKET socket = _get_osfhandle(notifier.fd());
+    SOCKET socket = (SOCKET)System::fd_to_handle(notifier.fd());
     VERIFY(socket != INVALID_SOCKET);
     VERIFY(socket != INVALID_SOCKET);
     int rc = WSAEventSelect(socket, event, notifier_type_to_network_event(notifier.type()));
     int rc = WSAEventSelect(socket, event, notifier_type_to_network_event(notifier.type()));
     VERIFY(rc == 0);
     VERIFY(rc == 0);

+ 6 - 0
Libraries/LibCore/System.cpp

@@ -1004,4 +1004,10 @@ int getpid()
     return ::getpid();
     return ::getpid();
 }
 }
 
 
+bool is_socket(int fd)
+{
+    auto result = fstat(fd);
+    return !result.is_error() && S_ISSOCK(result.value().st_mode);
+}
+
 }
 }

+ 13 - 0
Libraries/LibCore/System.h

@@ -52,6 +52,18 @@ using socklen_t = int;
 
 
 namespace Core::System {
 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)
 #if !defined(AK_OS_MACOS) && !defined(AK_OS_HAIKU)
 ErrorOr<int> accept4(int sockfd, struct sockaddr*, socklen_t*, int flags);
 ErrorOr<int> accept4(int sockfd, struct sockaddr*, socklen_t*, int flags);
 #endif
 #endif
@@ -180,5 +192,6 @@ ErrorOr<void> set_resource_limits(int resource, rlim_t limit);
 #endif
 #endif
 
 
 int getpid();
 int getpid();
+bool is_socket(int fd);
 
 
 }
 }

+ 97 - 6
Libraries/LibCore/SystemWindows.cpp

@@ -12,13 +12,58 @@
 #include <AK/ByteString.h>
 #include <AK/ByteString.h>
 #include <AK/ScopeGuard.h>
 #include <AK/ScopeGuard.h>
 #include <LibCore/System.h>
 #include <LibCore/System.h>
-#include <Windows.h>
 #include <direct.h>
 #include <direct.h>
-#include <io.h>
 #include <sys/mman.h>
 #include <sys/mman.h>
 
 
+#include <AK/Windows.h>
+
 namespace Core::System {
 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)
 ErrorOr<int> open(StringView path, int options, mode_t mode)
 {
 {
     ByteString string_path = path;
     ByteString string_path = path;
@@ -31,9 +76,7 @@ ErrorOr<int> open(StringView path, int options, mode_t mode)
             HANDLE dir_handle = CreateFile(sz_path, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
             HANDLE dir_handle = CreateFile(sz_path, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
             if (dir_handle == INVALID_HANDLE_VALUE)
             if (dir_handle == INVALID_HANDLE_VALUE)
                 return Error::from_windows_error();
                 return Error::from_windows_error();
-            int dir_fd = _open_osfhandle((intptr_t)dir_handle, 0);
-            if (dir_fd != -1)
-                return dir_fd;
+            return handle_to_fd(dir_handle, DirectoryHandle);
         }
         }
         return Error::from_syscall("open"sv, -error);
         return Error::from_syscall("open"sv, -error);
     }
     }
@@ -42,6 +85,20 @@ ErrorOr<int> open(StringView path, int options, mode_t mode)
 
 
 ErrorOr<void> close(int fd)
 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)
     if (_close(fd) < 0)
         return Error::from_syscall("close"sv, -errno);
         return Error::from_syscall("close"sv, -errno);
     return {};
     return {};
@@ -83,7 +140,7 @@ ErrorOr<void> ftruncate(int fd, off_t length)
     if (result.is_error())
     if (result.is_error())
         return result.release_error();
         return result.release_error();
 
 
-    if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0)
+    if (SetEndOfFile(fd_to_handle(fd)) == 0)
         return Error::from_windows_error();
         return Error::from_windows_error();
     return {};
     return {};
 }
 }
@@ -189,4 +246,38 @@ int getpid()
     return GetCurrentProcessId();
     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);
+}
+
 }
 }