summaryrefslogtreecommitdiff
path: root/win32/win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/win32.c')
-rw-r--r--win32/win32.c525
1 files changed, 405 insertions, 120 deletions
diff --git a/win32/win32.c b/win32/win32.c
index edf89be4b1..c51d53595f 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"
@@ -541,7 +544,7 @@ rb_w32_system_tmpdir(WCHAR *path, UINT len)
afterwards with xfree.
Try:
- HOME, HOMEDRIVE + HOMEPATH and USERPROFILE environment variables
+ HOME, USERPROFILE, HOMEDRIVE + HOMEPATH environment variables
Special Folders - Profile and Personal
*/
WCHAR *
@@ -550,13 +553,17 @@ rb_w32_home_dir(void)
WCHAR *buffer = NULL;
size_t buffer_len = MAX_PATH, len = 0;
enum {
- HOME_NONE, ENV_HOME, ENV_DRIVEPATH, ENV_USERPROFILE
+ HOME_NONE, ENV_HOME, ENV_USERPROFILE, ENV_DRIVEPATH
} home_type = HOME_NONE;
if ((len = GetEnvironmentVariableW(L"HOME", NULL, 0)) != 0) {
buffer_len = len;
home_type = ENV_HOME;
}
+ else if ((len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) != 0) {
+ buffer_len = len;
+ home_type = ENV_USERPROFILE;
+ }
else if ((len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) != 0) {
buffer_len = len;
if ((len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) != 0) {
@@ -564,10 +571,6 @@ rb_w32_home_dir(void)
home_type = ENV_DRIVEPATH;
}
}
- else if ((len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) != 0) {
- buffer_len = len;
- home_type = ENV_USERPROFILE;
- }
/* allocate buffer */
buffer = ALLOC_N(WCHAR, buffer_len);
@@ -576,13 +579,13 @@ rb_w32_home_dir(void)
case ENV_HOME:
GetEnvironmentVariableW(L"HOME", buffer, buffer_len);
break;
+ case ENV_USERPROFILE:
+ GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len);
+ break;
case ENV_DRIVEPATH:
len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len);
GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len);
break;
- case ENV_USERPROFILE:
- GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len);
- break;
default:
if (!get_special_folder(CSIDL_PROFILE, buffer, buffer_len) &&
!get_special_folder(CSIDL_PERSONAL, buffer, buffer_len)) {
@@ -619,21 +622,24 @@ init_env(void)
if (!GetEnvironmentVariableW(L"HOME", env, numberof(env))) {
f = FALSE;
- if (GetEnvironmentVariableW(L"HOMEDRIVE", env, numberof(env)))
- len = lstrlenW(env);
- else
- len = 0;
- if (GetEnvironmentVariableW(L"HOMEPATH", env + len, numberof(env) - len) || len) {
- f = TRUE;
- }
- else if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
- f = TRUE;
- }
- else if (get_special_folder(CSIDL_PROFILE, env, numberof(env))) {
+ if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
f = TRUE;
}
- else if (get_special_folder(CSIDL_PERSONAL, env, numberof(env))) {
- f = TRUE;
+ else {
+ if (GetEnvironmentVariableW(L"HOMEDRIVE", env, numberof(env)))
+ len = lstrlenW(env);
+ else
+ len = 0;
+
+ if (GetEnvironmentVariableW(L"HOMEPATH", env + len, numberof(env) - len) || len) {
+ f = TRUE;
+ }
+ else if (get_special_folder(CSIDL_PROFILE, env, numberof(env))) {
+ f = TRUE;
+ }
+ else if (get_special_folder(CSIDL_PERSONAL, env, numberof(env))) {
+ f = TRUE;
+ }
}
if (f) {
regulate_path(env);
@@ -678,7 +684,10 @@ invalid_parameter(const wchar_t *expr, const wchar_t *func, const wchar_t *file,
int ruby_w32_rtc_error;
+# ifndef __MINGW32__
/* License: Ruby's */
+RBIMPL_ATTR_NONNULL((5))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6)
static int __cdecl
rtc_error_handler(int e, const char *src, int line, const char *exe, const char *fmt, ...)
{
@@ -694,6 +703,7 @@ rtc_error_handler(int e, const char *src, int line, const char *exe, const char
rb_write_error2(RSTRING_PTR(str), RSTRING_LEN(str));
return 0;
}
+# endif
#endif
static CRITICAL_SECTION select_mutex;
@@ -1345,10 +1355,10 @@ is_batch(const char *cmd)
#define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
/* License: Ruby's */
-MJIT_FUNC_EXPORTED HANDLE
+HANDLE
rb_w32_start_process(const char *abspath, char *const *argv, int out_fd)
{
- /* NOTE: This function is used by MJIT worker, so it can be used parallelly with
+ /* NOTE: This function is used by RJIT worker, so it can be used parallelly with
Ruby's main thread. So functions touching things shared with main thread can't
be used, like `ALLOCV` that may trigger GC or `FindFreeChildSlot` that finds
a slot from shared memory without atomic locks. */
@@ -1416,18 +1426,20 @@ w32_spawn(int mode, const char *cmd, const char *prog, UINT cp)
while (ISSPACE(*cmd)) cmd++;
if ((shell = w32_getenv("RUBYSHELL", cp)) && (redir = has_redirection(cmd, cp))) {
size_t shell_len = strlen(shell);
- char *tmp = ALLOCV(v, shell_len + strlen(cmd) + sizeof(" -c ") + 2);
+ size_t cmd_len = strlen(cmd) + sizeof(" -c ") + 2;
+ char *tmp = ALLOCV(v, shell_len + cmd_len);
memcpy(tmp, shell, shell_len + 1);
translate_char(tmp, '/', '\\', cp);
- sprintf(tmp + shell_len, " -c \"%s\"", cmd);
+ snprintf(tmp + shell_len, cmd_len, " -c \"%s\"", cmd);
cmd = tmp;
}
else if ((shell = w32_getenv("COMSPEC", cp)) &&
(nt = !is_command_com(shell),
(redir < 0 ? has_redirection(cmd, cp) : redir) ||
is_internal_cmd(cmd, nt))) {
- char *tmp = ALLOCV(v, strlen(shell) + strlen(cmd) + sizeof(" /c ") + (nt ? 2 : 0));
- sprintf(tmp, nt ? "%s /c \"%s\"" : "%s /c %s", shell, cmd);
+ size_t cmd_len = strlen(shell) + strlen(cmd) + sizeof(" /c ") + (nt ? 2 : 0);
+ char *tmp = ALLOCV(v, cmd_len);
+ snprintf(tmp, cmd_len, nt ? "%s /c \"%s\"" : "%s /c %s", shell, cmd);
cmd = tmp;
}
else {
@@ -1528,7 +1540,8 @@ rb_w32_uspawn(int mode, const char *cmd, const char *prog)
/* License: Artistic or GPL */
static rb_pid_t
-w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UINT cp)
+w32_spawn_process(int mode, const char *prog, char *const *argv,
+ int in_fd, int out_fd, int err_fd, DWORD flags, UINT cp)
{
int c_switch = 0;
size_t len;
@@ -1539,9 +1552,20 @@ w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UIN
int e = 0;
rb_pid_t ret = -1;
VALUE v = 0;
+ HANDLE in_handle = NULL, out_handle = NULL, err_handle = NULL;
if (check_spawn_mode(mode)) return -1;
+ if (in_fd >= 0) {
+ in_handle = (HANDLE)rb_w32_get_osfhandle(in_fd);
+ }
+ if (out_fd >= 0) {
+ out_handle = (HANDLE)rb_w32_get_osfhandle(out_fd);
+ }
+ if (err_fd >= 0) {
+ err_handle = (HANDLE)rb_w32_get_osfhandle(err_fd);
+ }
+
if (!prog) prog = argv[0];
if ((shell = w32_getenv("COMSPEC", cp)) &&
internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
@@ -1589,7 +1613,7 @@ w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UIN
if (!e) {
struct ChildRecord *child = FindFreeChildSlot();
- if (CreateChild(child, wcmd, wprog, NULL, NULL, NULL, flags)) {
+ if (CreateChild(child, wcmd, wprog, in_handle, out_handle, err_handle, flags)) {
ret = child_result(child, mode);
}
}
@@ -1604,21 +1628,21 @@ rb_pid_t
rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
{
/* assume ACP */
- return w32_aspawn_flags(mode, prog, argv, flags, filecp());
+ return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, filecp());
}
/* License: Ruby's */
rb_pid_t
rb_w32_uaspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
{
- return w32_aspawn_flags(mode, prog, argv, flags, CP_UTF8);
+ return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, CP_UTF8);
}
/* License: Ruby's */
rb_pid_t
rb_w32_aspawn(int mode, const char *prog, char *const *argv)
{
- return w32_aspawn_flags(mode, prog, argv, 0, filecp());
+ return w32_spawn_process(mode, prog, argv, -1, -1, -1, 0, filecp());
}
/* License: Ruby's */
@@ -1628,6 +1652,15 @@ rb_w32_uaspawn(int mode, const char *prog, char *const *argv)
return rb_w32_uaspawn_flags(mode, prog, argv, 0);
}
+/* License: Ruby's */
+rb_pid_t
+rb_w32_uspawn_process(int mode, const char *prog, char *const *argv,
+ int in_fd, int out_fd, int err_fd, DWORD flags)
+{
+ return w32_spawn_process(mode, prog, argv, in_fd, out_fd, err_fd,
+ flags, CP_UTF8);
+}
+
/* License: Artistic or GPL */
typedef struct _NtCmdLineElement {
struct _NtCmdLineElement *next;
@@ -2213,7 +2246,7 @@ rb_w32_wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
WCHAR *
rb_w32_mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
{
- /* This is used by MJIT worker. Do not trigger GC or call Ruby method here. */
+ /* This is used by RJIT worker. Do not trigger GC or call Ruby method here. */
WCHAR *ptr;
int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0);
if (!(ptr = malloc(sizeof(WCHAR) * len))) return 0;
@@ -2360,10 +2393,8 @@ readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, const WCHAR *, struct di
//
// first set up the structure to return
//
- if (dirp->dirstr.d_name)
- free(dirp->dirstr.d_name);
- if (dirp->dirstr.d_altname)
- free(dirp->dirstr.d_altname);
+ free(dirp->dirstr.d_name);
+ free(dirp->dirstr.d_altname);
dirp->dirstr.d_altname = 0;
dirp->dirstr.d_altlen = 0;
conv(dirp->curr, dirp->curr + lstrlenW(dirp->curr) + 1, &dirp->dirstr, enc);
@@ -2469,14 +2500,10 @@ void
rb_w32_closedir(DIR *dirp)
{
if (dirp) {
- if (dirp->dirstr.d_name)
- free(dirp->dirstr.d_name);
- if (dirp->dirstr.d_altname)
- free(dirp->dirstr.d_altname);
- if (dirp->start)
- free(dirp->start);
- if (dirp->bits)
- free(dirp->bits);
+ free(dirp->dirstr.d_name);
+ free(dirp->dirstr.d_altname);
+ free(dirp->start);
+ free(dirp->bits);
free(dirp);
}
}
@@ -2575,10 +2602,86 @@ set_pioinfo_extra(void)
# define UCRTBASE "ucrtbase.dll"
# endif
/* get __pioinfo addr with _isatty */
+ /*
+ * Why Ruby depends to _pioinfo is
+ * * to associate socket and fd: CRuby creates fd with dummy file handle
+ * and set socket to emulate Unix-like behavior. Without __pioinfo
+ * we need something which manages the fd number allocation
+ * * to implement overlapped I/O for Windows 2000/XP
+ * * to emulate fcntl(2)
+ *
+ * see also
+ * * https://bugs.ruby-lang.org/issues/11118
+ * * https://bugs.ruby-lang.org/issues/18605
+ */
char *p = (char*)get_proc_address(UCRTBASE, "_isatty", NULL);
- char *pend = p;
/* _osfile(fh) & FDEV */
+#ifdef _M_ARM64
+#define IS_INSN(pc, name) ((*(pc) & name##_mask) == name##_id)
+ const int max_num_inst = 500;
+ uint32_t *start = (uint32_t*)p;
+ uint32_t *end_limit = (start + max_num_inst);
+ uint32_t *pc = start;
+
+ if (!p) {
+ fprintf(stderr, "_isatty proc not found in " UCRTBASE "\n");
+ _exit(1);
+ }
+
+ /* end of function */
+ const uint32_t ret_id = 0xd65f0000;
+ const uint32_t ret_mask = 0xfffffc1f;
+ for(; pc < end_limit; pc++) {
+ if (IS_INSN(pc, ret)) {
+ break;
+ }
+ }
+ if (pc == end_limit) {
+ fprintf(stderr, "end of _isatty not found in " UCRTBASE "\n");
+ _exit(1);
+ }
+
+ /* pioinfo instruction mark */
+ const uint32_t adrp_id = 0x90000000;
+ const uint32_t adrp_mask = 0x9f000000;
+ const uint32_t add_id = 0x11000000;
+ const uint32_t add_mask = 0x3fc00000;
+ for(; pc > start; pc--) {
+ if (IS_INSN(pc, adrp) && IS_INSN(pc + 1, add)) {
+ break;
+ }
+ }
+ if(pc == start) {
+ fprintf(stderr, "pioinfo mark not found in " UCRTBASE "\n");
+ _exit(1);
+ }
+
+ /* We now point to instructions that load address of __pioinfo:
+ * adrp x8, 0x1801d8000
+ * add x8, x8, #0xdb0
+ * https://devblogs.microsoft.com/oldnewthing/20220809-00/?p=106955
+ * The last adrp/add sequence before ret is what we are looking for.
+ */
+ const uint32_t adrp_insn = *pc;
+ const uint32_t adrp_immhi = (adrp_insn & 0x00ffffe0) >> 5;
+ const uint32_t adrp_immlo = (adrp_insn & 0x60000000) >> (5 + 19 + 5);
+ /* imm = immhi:immlo:Zeros(12), 64 */
+ const uint64_t adrp_imm = ((adrp_immhi << 2) | adrp_immlo) << 12;
+ /* base = PC64<63:12>:Zeros(12) */
+ const uint64_t adrp_base = (uint64_t)pc & 0xfffffffffffff000;
+
+ const uint32_t add_insn = *(pc + 1);
+ const uint32_t add_sh = (add_insn & 0x400000) >> (12 + 5 + 5);
+ /* case sh of
+ when '0' imm = ZeroExtend(imm12, datasize);
+ when '1' imm = ZeroExtend(imm12:Zeros(12), datasize); */
+ const uint64_t add_imm = ((add_insn & 0x3ffc00) >> (5 + 5)) << (add_sh ? 12 : 0);
+
+ __pioinfo = (ioinfo**)(adrp_base + adrp_imm + add_imm);
+#else /* _M_ARM64 */
+ char *pend = p;
+
# ifdef _WIN64
int32_t rel;
char *rip;
@@ -2628,7 +2731,8 @@ set_pioinfo_extra(void)
#else
__pioinfo = *(ioinfo***)(p);
#endif
-#endif
+#endif /* _M_ARM64 */
+#endif /* RUBY_MSVCRT_VERSION */
int fd;
fd = _open("NUL", O_RDONLY);
@@ -3102,7 +3206,12 @@ is_readable_console(SOCKET sock) /* call this for console only */
RUBY_CRITICAL {
if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) {
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
- ir.Event.KeyEvent.uChar.AsciiChar) {
+ ir.Event.KeyEvent.uChar.UnicodeChar) {
+ ret = 1;
+ }
+ else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown &&
+ ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU /* ALT key */ &&
+ ir.Event.KeyEvent.uChar.UnicodeChar) {
ret = 1;
}
else {
@@ -4013,15 +4122,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;
@@ -4046,6 +4233,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;
@@ -4096,6 +4292,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;
@@ -4261,8 +4461,8 @@ freeifaddrs(struct ifaddrs *ifp)
{
while (ifp) {
struct ifaddrs *next = ifp->ifa_next;
- if (ifp->ifa_addr) ruby_xfree(ifp->ifa_addr);
- if (ifp->ifa_name) ruby_xfree(ifp->ifa_name);
+ ruby_xfree(ifp->ifa_addr);
+ ruby_xfree(ifp->ifa_name);
ruby_xfree(ifp);
ifp = next;
}
@@ -5139,32 +5339,35 @@ rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
static ssize_t
w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize)
{
- VALUE wtmp;
+ VALUE rp_buf, rp_buf_bigger = 0;
DWORD len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0);
- size_t size = rb_w32_reparse_buffer_size(len);
- WCHAR *wname, *wpath = ALLOCV(wtmp, size + sizeof(WCHAR) * len);
+ size_t size = rb_w32_reparse_buffer_size(bufsize);
+ WCHAR *wname;
+ WCHAR *wpath = ALLOCV(rp_buf, sizeof(WCHAR) * len + size);
rb_w32_reparse_buffer_t *rp = (void *)(wpath + len);
ssize_t ret;
int e;
MultiByteToWideChar(cp, 0, path, -1, wpath, len);
e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
- if (e && e != ERROR_MORE_DATA) {
- ALLOCV_END(wtmp);
- errno = map_errno(e);
+ if (e == ERROR_MORE_DATA) {
+ size = rb_w32_reparse_buffer_size(len + 1);
+ rp = ALLOCV(rp_buf_bigger, size);
+ e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
+ }
+ if (e) {
+ ALLOCV_END(rp_buf);
+ ALLOCV_END(rp_buf_bigger);
+ errno = e == -1 ? EINVAL : map_errno(e);
return -1;
}
- len = lstrlenW(wname) + 1;
+ len = lstrlenW(wname);
ret = WideCharToMultiByte(cp, 0, wname, len, buf, bufsize, NULL, NULL);
- ALLOCV_END(wtmp);
- if (e) {
+ ALLOCV_END(rp_buf);
+ ALLOCV_END(rp_buf_bigger);
+ if (!ret) {
ret = bufsize;
}
- else if (!ret) {
- e = GetLastError();
- errno = map_errno(e);
- ret = -1;
- }
return ret;
}
@@ -5624,10 +5827,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;
@@ -5702,14 +5903,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);
@@ -5745,9 +5938,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);
@@ -5759,15 +5967,28 @@ 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;
}
@@ -5780,6 +6001,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;
}
@@ -5899,8 +6126,8 @@ rb_w32_lstati128(const char *path, struct stati128 *st)
}
/* License: Ruby's */
-off_t
-rb_w32_lseek(int fd, off_t ofs, int whence)
+rb_off_t
+rb_w32_lseek(int fd, rb_off_t ofs, int whence)
{
SOCKET sock = TO_SOCKET(fd);
if (is_socket(sock) || is_pipe(sock)) {
@@ -5941,7 +6168,7 @@ rb_w32_uaccess(const char *path, int mode)
/* License: Ruby's */
static int
-rb_chsize(HANDLE h, off_t size)
+rb_chsize(HANDLE h, rb_off_t size)
{
long upos, lpos, usize, lsize;
int ret = -1;
@@ -5970,7 +6197,7 @@ rb_chsize(HANDLE h, off_t size)
/* License: Ruby's */
static int
-w32_truncate(const char *path, off_t length, UINT cp)
+w32_truncate(const char *path, rb_off_t length, UINT cp)
{
HANDLE h;
int ret;
@@ -5992,21 +6219,21 @@ w32_truncate(const char *path, off_t length, UINT cp)
/* License: Ruby's */
int
-rb_w32_utruncate(const char *path, off_t length)
+rb_w32_utruncate(const char *path, rb_off_t length)
{
return w32_truncate(path, length, CP_UTF8);
}
/* License: Ruby's */
int
-rb_w32_truncate(const char *path, off_t length)
+rb_w32_truncate(const char *path, rb_off_t length)
{
return w32_truncate(path, length, filecp());
}
/* License: Ruby's */
int
-rb_w32_ftruncate(int fd, off_t length)
+rb_w32_ftruncate(int fd, rb_off_t length)
{
HANDLE h;
@@ -7037,21 +7264,43 @@ rb_w32_close(int fd)
return 0;
}
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
static int
-setup_overlapped(OVERLAPPED *ol, int fd, int iswrite)
+setup_overlapped(OVERLAPPED *ol, int fd, int iswrite, rb_off_t *_offset)
{
memset(ol, 0, sizeof(*ol));
- if (!(_osfile(fd) & (FDEV | FPIPE))) {
+
+ // On mode:a, it can write only FILE_END.
+ // On mode:a+, though it can write only FILE_END,
+ // it can read from everywhere.
+ DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
+
+ if (_offset) {
+ // Explicit offset was provided (pread/pwrite) - use it:
+ uint64_t offset = *_offset;
+ ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
+ ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
+
+ // Update _offset with the current offset:
+ LARGE_INTEGER seek_offset = {0}, current_offset = {0};
+ if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, &current_offset, seek_method)) {
+ DWORD last_error = GetLastError();
+ if (last_error != NO_ERROR) {
+ errno = map_errno(last_error);
+ return -1;
+ }
+ }
+
+ // As we need to restore the current offset later, we save it here:
+ *_offset = current_offset.QuadPart;
+ }
+ else if (!(_osfile(fd) & (FDEV | FPIPE))) {
LONG high = 0;
- /* On mode:a, it can write only FILE_END.
- * On mode:a+, though it can write only FILE_END,
- * it can read from everywhere.
- */
- DWORD method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
- DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method);
-#ifndef INVALID_SET_FILE_POINTER
-#define INVALID_SET_FILE_POINTER ((DWORD)-1)
-#endif
+ DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method);
+
if (low == INVALID_SET_FILE_POINTER) {
DWORD err = GetLastError();
if (err != NO_ERROR) {
@@ -7059,9 +7308,11 @@ setup_overlapped(OVERLAPPED *ol, int fd, int iswrite)
return -1;
}
}
+
ol->Offset = low;
ol->OffsetHigh = high;
}
+
ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
if (!ol->hEvent) {
errno = map_errno(GetLastError());
@@ -7071,11 +7322,22 @@ setup_overlapped(OVERLAPPED *ol, int fd, int iswrite)
}
static void
-finish_overlapped(OVERLAPPED *ol, int fd, DWORD size)
+finish_overlapped(OVERLAPPED *ol, int fd, DWORD size, rb_off_t *_offset)
{
CloseHandle(ol->hEvent);
- if (!(_osfile(fd) & (FDEV | FPIPE))) {
+ if (_offset) {
+ // If we were doing a `pread`/`pwrite`, we need to restore the current that was saved in setup_overlapped:
+ DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN;
+
+ LARGE_INTEGER seek_offset = {0};
+ if (seek_method == FILE_BEGIN) {
+ seek_offset.QuadPart = *_offset;
+ }
+
+ SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method);
+ }
+ else if (!(_osfile(fd) & (FDEV | FPIPE))) {
LONG high = ol->OffsetHigh;
DWORD low = ol->Offset + size;
if (low < ol->Offset)
@@ -7086,8 +7348,8 @@ finish_overlapped(OVERLAPPED *ol, int fd, DWORD size)
#undef read
/* License: Ruby's */
-ssize_t
-rb_w32_read(int fd, void *buf, size_t size)
+static ssize_t
+rb_w32_read_internal(int fd, void *buf, size_t size, rb_off_t *offset)
{
SOCKET sock = TO_SOCKET(fd);
DWORD read;
@@ -7108,7 +7370,7 @@ rb_w32_read(int fd, void *buf, size_t size)
return -1;
}
- if (_osfile(fd) & FTEXT) {
+ if (!offset && _osfile(fd) & FTEXT) {
return _read(fd, buf, size);
}
@@ -7142,7 +7404,7 @@ rb_w32_read(int fd, void *buf, size_t size)
len = size;
size -= len;
- if (setup_overlapped(&ol, fd, FALSE)) {
+ if (setup_overlapped(&ol, fd, FALSE, offset)) {
rb_acrt_lowio_unlock_fh(fd);
return -1;
}
@@ -7205,7 +7467,7 @@ rb_w32_read(int fd, void *buf, size_t size)
errno = map_errno(err);
}
- finish_overlapped(&ol, fd, read);
+ finish_overlapped(&ol, fd, read, offset);
ret += read;
if (read >= len) {
@@ -7225,8 +7487,8 @@ rb_w32_read(int fd, void *buf, size_t size)
#undef write
/* License: Ruby's */
-ssize_t
-rb_w32_write(int fd, const void *buf, size_t size)
+static ssize_t
+rb_w32_write_internal(int fd, const void *buf, size_t size, rb_off_t *offset)
{
SOCKET sock = TO_SOCKET(fd);
DWORD written;
@@ -7244,7 +7506,8 @@ rb_w32_write(int fd, const void *buf, size_t size)
return -1;
}
- if ((_osfile(fd) & FTEXT) &&
+ // If an offset is given, we can't use `_write`.
+ if (!offset && (_osfile(fd) & FTEXT) &&
(!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
ssize_t w = _write(fd, buf, size);
if (w == (ssize_t)-1 && errno == EINVAL) {
@@ -7266,7 +7529,8 @@ rb_w32_write(int fd, const void *buf, size_t size)
size -= len;
retry2:
- if (setup_overlapped(&ol, fd, TRUE)) {
+ // Provide the requested offset.
+ if (setup_overlapped(&ol, fd, TRUE, offset)) {
rb_acrt_lowio_unlock_fh(fd);
return -1;
}
@@ -7305,7 +7569,7 @@ rb_w32_write(int fd, const void *buf, size_t size)
}
}
- finish_overlapped(&ol, fd, written);
+ finish_overlapped(&ol, fd, written, offset);
ret += written;
if (written == len) {
@@ -7329,6 +7593,30 @@ rb_w32_write(int fd, const void *buf, size_t size)
return ret;
}
+ssize_t
+rb_w32_read(int fd, void *buf, size_t size)
+{
+ return rb_w32_read_internal(fd, buf, size, NULL);
+}
+
+ssize_t
+rb_w32_write(int fd, const void *buf, size_t size)
+{
+ return rb_w32_write_internal(fd, buf, size, NULL);
+}
+
+ssize_t
+rb_w32_pread(int descriptor, void *base, size_t size, rb_off_t offset)
+{
+ return rb_w32_read_internal(descriptor, base, size, &offset);
+}
+
+ssize_t
+rb_w32_pwrite(int descriptor, const void *base, size_t size, rb_off_t offset)
+{
+ return rb_w32_write_internal(descriptor, base, size, &offset);
+}
+
/* License: Ruby's */
long
rb_w32_write_console(uintptr_t strarg, int fd)
@@ -7388,7 +7676,7 @@ rb_w32_write_console(uintptr_t strarg, int fd)
}
}
RB_GC_GUARD(str);
- if (wbuffer) free(wbuffer);
+ free(wbuffer);
return (long)reslen;
}
@@ -8064,10 +8352,7 @@ w32_io_info(VALUE *file, w32_io_info_t *st)
tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
if (!NIL_P(tmp)) {
- rb_io_t *fptr;
-
- GetOpenFile(tmp, fptr);
- f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
+ f = (HANDLE)rb_w32_get_osfhandle(rb_io_descriptor(tmp));
if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
}
else {
@@ -8214,7 +8499,7 @@ VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notim
#endif
void *
-rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
+rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset)
{
void *ptr;
//DWORD protect = 0;