summaryrefslogtreecommitdiff
path: root/win32
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2022-11-17 14:50:25 -0800
committerGitHub <noreply@github.com>2022-11-17 14:50:25 -0800
commitea8a7287e2b96b9c24e5e89fe863e5bfa60bfdda (patch)
tree1935b65502eb16b6913d6183fc11c6f1cf4db34d /win32
parent4e4b29b1a9e534554594b6f18fc0bdc462638934 (diff)
downloadruby-ea8a7287e2b96b9c24e5e89fe863e5bfa60bfdda.tar.gz
Add support for `sockaddr_un` on Windows. (#6513)
* Windows: Fix warning about undefined if_indextoname() * Windows: Fix UNIXSocket on MINGW and make .pair more reliable * Windows: Use nonblock=true for read tests with scheduler * Windows: Move socket detection from File.socket? to File.stat Add S_IFSOCK to Windows and interpret reparse points accordingly. Enable tests that work now. * Windows: Use wide-char functions to UNIXSocket This fixes behaviour with non-ASCII characters. It also fixes deletion of temporary UNIXSocket.pair files. * Windows: Add UNIXSocket tests for specifics of Windows impl. * Windows: fix VC build due to missing _snwprintf Avoid usage of _snwprintf, since it fails linking ruby.dll like so: linking shared-library x64-vcruntime140-ruby320.dll x64-vcruntime140-ruby320.def : error LNK2001: unresolved external symbol snwprintf x64-vcruntime140-ruby320.def : error LNK2001: unresolved external symbol vsnwprintf_l whereas linking miniruby.exe succeeds. This patch uses snprintf on the UTF-8 string instead. Also remove branch GetWindowsDirectoryW, since it doesn't work. * Windows: Fix dangling symlink test failures Co-authored-by: Lars Kanis <kanis@comcard.de>
Diffstat (limited to 'win32')
-rw-r--r--win32/Makefile.sub3
-rw-r--r--win32/file.h4
-rw-r--r--win32/win32.c154
3 files changed, 142 insertions, 19 deletions
diff --git a/win32/Makefile.sub b/win32/Makefile.sub
index e84f978bb7..4b8904c536 100644
--- a/win32/Makefile.sub
+++ b/win32/Makefile.sub
@@ -629,6 +629,9 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_STDDEF_H 1
#define HAVE_STRING_H 1
#define HAVE_MEMORY_H 1
+!if $(MSC_VER) >= 1920
+#define HAVE_AFUNIX_H 1
+!endif
!if $(MSC_VER) >= 1400
#define HAVE_LONG_LONG 1
!else
diff --git a/win32/file.h b/win32/file.h
index ef701487dd..4f1f36a75c 100644
--- a/win32/file.h
+++ b/win32/file.h
@@ -1,6 +1,10 @@
#ifndef RUBY_WIN32_FILE_H
#define RUBY_WIN32_FILE_H
+#ifndef IO_REPARSE_TAG_AF_UNIX
+# define IO_REPARSE_TAG_AF_UNIX 0x80000023
+#endif
+
enum {
MINIMUM_REPARSE_BUFFER_PATH_LEN = 100
};
diff --git a/win32/win32.c b/win32/win32.c
index 3ddfe9bfdf..fee31677f0 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -49,6 +49,9 @@
#ifdef __MINGW32__
#include <mswsock.h>
#endif
+#ifdef HAVE_AFUNIX_H
+# include <afunix.h>
+#endif
#include "ruby/win32.h"
#include "ruby/vm.h"
#include "win32/dir.h"
@@ -4018,15 +4021,93 @@ rb_w32_getservbyport(int port, const char *proto)
return r;
}
+#ifdef HAVE_AFUNIX_H
+
+/* License: Ruby's */
+static size_t
+socketpair_unix_path(struct sockaddr_un *sock_un)
+{
+ SOCKET listener;
+ WCHAR wpath[sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path)] = L"";
+
+ /* AF_UNIX/SOCK_STREAM became available in Windows 10
+ * See https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows
+ */
+ listener = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (listener == INVALID_SOCKET)
+ return 0;
+
+ memset(sock_un, 0, sizeof(*sock_un));
+ sock_un->sun_family = AF_UNIX;
+
+ /* Abstract sockets (filesystem-independent) don't work, contrary to
+ * the claims of the aforementioned blog post:
+ * https://github.com/microsoft/WSL/issues/4240#issuecomment-549663217
+ *
+ * So we must use a named path, and that comes with all the attendant
+ * problems of permissions and collisions. Trying various temporary
+ * directories and putting high-res time and PID in the filename.
+ */
+ for (int try = 0; ; try++) {
+ LARGE_INTEGER ticks;
+ size_t path_len = 0;
+ const size_t maxpath = sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path);
+
+ switch (try) {
+ case 0:
+ /* user temp dir from TMP or TEMP env var, it ends with a backslash */
+ path_len = GetTempPathW(maxpath, wpath);
+ break;
+ case 1:
+ wcsncpy(wpath, L"C:/Temp/", maxpath);
+ path_len = lstrlenW(wpath);
+ break;
+ case 2:
+ /* Current directory */
+ path_len = 0;
+ break;
+ case 3:
+ closesocket(listener);
+ return 0;
+ }
+
+ /* Windows UNIXSocket implementation expects UTF-8 instead of UTF16 */
+ path_len = WideCharToMultiByte(CP_UTF8, 0, wpath, path_len, sock_un->sun_path, maxpath, NULL, NULL);
+ QueryPerformanceCounter(&ticks);
+ path_len += snprintf(sock_un->sun_path + path_len,
+ maxpath - path_len,
+ "%lld-%ld.($)",
+ ticks.QuadPart,
+ GetCurrentProcessId());
+
+ /* Convert to UTF16 for DeleteFileW */
+ MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
+
+ if (bind(listener, (struct sockaddr *)sock_un, sizeof(*sock_un)) != SOCKET_ERROR)
+ break;
+ }
+ closesocket(listener);
+ DeleteFileW(wpath);
+ return sizeof(*sock_un);
+}
+#endif
+
/* License: Ruby's */
static int
socketpair_internal(int af, int type, int protocol, SOCKET *sv)
{
SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
struct sockaddr_in sock_in4;
+
#ifdef INET6
struct sockaddr_in6 sock_in6;
#endif
+
+#ifdef HAVE_AFUNIX_H
+ struct sockaddr_un sock_un = {0, {0}};
+ WCHAR wpath[sizeof(sock_un.sun_path)/sizeof(*sock_un.sun_path)] = L"";
+#endif
+
struct sockaddr *addr;
int ret = -1;
int len;
@@ -4051,6 +4132,15 @@ socketpair_internal(int af, int type, int protocol, SOCKET *sv)
len = sizeof(sock_in6);
break;
#endif
+#ifdef HAVE_AFUNIX_H
+ case AF_UNIX:
+ addr = (struct sockaddr *)&sock_un;
+ len = socketpair_unix_path(&sock_un);
+ MultiByteToWideChar(CP_UTF8, 0, sock_un.sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
+ if (len)
+ break;
+ /* fall through */
+#endif
default:
errno = EAFNOSUPPORT;
return -1;
@@ -4101,6 +4191,10 @@ socketpair_internal(int af, int type, int protocol, SOCKET *sv)
}
if (svr != INVALID_SOCKET)
closesocket(svr);
+#ifdef HAVE_AFUNIX_H
+ if (sock_un.sun_family == AF_UNIX)
+ DeleteFileW(wpath);
+#endif
}
return ret;
@@ -5632,10 +5726,8 @@ fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode)
/* format is already set */
}
else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
- if (rb_w32_reparse_symlink_p(path))
- mode |= S_IFLNK | S_IEXEC;
- else
- mode |= S_IFDIR | S_IEXEC;
+ /* Only used by stat_by_find in the case the file can not be opened.
+ * In this case we can't get more details. */
}
else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
mode |= S_IFDIR | S_IEXEC;
@@ -5710,14 +5802,6 @@ stat_by_find(const WCHAR *path, struct stati128 *st)
{
HANDLE h;
WIN32_FIND_DATAW wfd;
- /* GetFileAttributesEx failed; check why. */
- int e = GetLastError();
-
- if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME)
- || (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) {
- errno = map_errno(e);
- return -1;
- }
/* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
h = FindFirstFileW(path, &wfd);
@@ -5753,9 +5837,24 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
HANDLE f;
WCHAR finalname[PATH_MAX];
+ int open_error;
memset(st, 0, sizeof(*st));
f = open_special(path, 0, flags);
+ open_error = GetLastError();
+ if (f == INVALID_HANDLE_VALUE && !lstat) {
+ /* Support stat (not only lstat) of UNIXSocket */
+ FILE_ATTRIBUTE_TAG_INFO attr_info;
+ DWORD e;
+
+ f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
+ e = GetFileInformationByHandleEx( f, FileAttributeTagInfo,
+ &attr_info, sizeof(attr_info));
+ if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
+ CloseHandle(f);
+ f = INVALID_HANDLE_VALUE;
+ }
+ }
if (f != INVALID_HANDLE_VALUE) {
DWORD attr = stati128_handle(f, st);
const DWORD len = get_final_path(f, finalname, numberof(finalname), 0);
@@ -5767,15 +5866,26 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
case FILE_TYPE_PIPE:
mode = S_IFIFO;
break;
+ default:
+ if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
+ FILE_ATTRIBUTE_TAG_INFO attr_info;
+ DWORD e;
+
+ e = GetFileInformationByHandleEx( f, FileAttributeTagInfo,
+ &attr_info, sizeof(attr_info));
+ if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) {
+ st->st_size = 0;
+ mode |= S_IFSOCK;
+ } else if (rb_w32_reparse_symlink_p(path)) {
+ /* TODO: size in which encoding? */
+ st->st_size = 0;
+ mode |= S_IFLNK | S_IEXEC;
+ } else {
+ mode |= S_IFDIR | S_IEXEC;
+ }
+ }
}
CloseHandle(f);
- if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
- /* TODO: size in which encoding? */
- if (rb_w32_reparse_symlink_p(path))
- st->st_size = 0;
- else
- attr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
- }
if (attr & FILE_ATTRIBUTE_DIRECTORY) {
if (check_valid_dir(path)) return -1;
}
@@ -5788,6 +5898,12 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
}
}
else {
+ if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME)
+ || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) {
+ errno = map_errno(open_error);
+ return -1;
+ }
+
if (stat_by_find(path, st)) return -1;
}