зеркало из https://github.com/github/ruby.git
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>
This commit is contained in:
Родитель
4e4b29b1a9
Коммит
ea8a7287e2
9
NEWS.md
9
NEWS.md
|
@ -103,7 +103,8 @@ Note that each entry is kept to a minimum, see links for details.
|
|||
Note: We're only listing outstanding class updates.
|
||||
|
||||
* Fiber::Scheduler
|
||||
* Introduce `Fiber::Scheduler#io_select` for non-blocking `IO.select`. [[Feature #19060]]
|
||||
* Introduce `Fiber::Scheduler#io_select` for non-blocking `IO.select`.
|
||||
[[Feature #19060]]
|
||||
|
||||
* IO
|
||||
* Introduce `IO#timeout=` and `IO#timeout` which can cause
|
||||
|
@ -115,6 +116,11 @@ Note: We're only listing outstanding class updates.
|
|||
STDIN.read # => Blocking operation timed out! (IO::TimeoutError)
|
||||
```
|
||||
|
||||
* UNIXSocket
|
||||
* Add support for UNIXSocket on Windows. Emulate anonymous sockets. Add
|
||||
support for `File.socket?` and `File::Stat#socket?` where possible.
|
||||
[[Feature #19135]]
|
||||
|
||||
* Class
|
||||
* `Class#attached_object`, which returns the object for which
|
||||
the receiver is the singleton class. Raises `TypeError` if the
|
||||
|
@ -417,3 +423,4 @@ The following deprecated APIs are removed.
|
|||
[Feature #19026]: https://bugs.ruby-lang.org/issues/19026
|
||||
[Feature #19060]: https://bugs.ruby-lang.org/issues/19060
|
||||
[Bug #19100]: https://bugs.ruby-lang.org/issues/19100
|
||||
[Feature #19135]: https://bugs.ruby-lang.org/issues/19135
|
||||
|
|
|
@ -1283,6 +1283,11 @@ dnl AC_HEADER_STDC has been checked in AC_USE_SYSTEM_EXTENSIONS
|
|||
AC_HEADER_STDBOOL
|
||||
AC_HEADER_SYS_WAIT
|
||||
|
||||
AC_CHECK_HEADERS([afunix.h], [], [],
|
||||
[#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
#endif
|
||||
])
|
||||
AC_CHECK_HEADERS(atomic.h)
|
||||
AC_CHECK_HEADERS(copyfile.h)
|
||||
AC_CHECK_HEADERS(direct.h)
|
||||
|
|
|
@ -316,6 +316,7 @@ end
|
|||
netpacket/packet.h
|
||||
net/ethernet.h
|
||||
sys/un.h
|
||||
afunix.h
|
||||
ifaddrs.h
|
||||
sys/ioctl.h
|
||||
sys/sockio.h
|
||||
|
|
|
@ -209,7 +209,7 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
|
|||
else
|
||||
return rb_assoc_new(str, Qnil);
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
case RECV_UNIX:
|
||||
return rb_assoc_new(str, rsock_unixaddr(&arg.buf.un, arg.alen));
|
||||
#endif
|
||||
|
|
|
@ -197,7 +197,7 @@ class Addrinfo
|
|||
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
|
||||
begin
|
||||
sock.ipv6only! if self.ipv6?
|
||||
sock.setsockopt(:SOCKET, :REUSEADDR, 1)
|
||||
sock.setsockopt(:SOCKET, :REUSEADDR, 1) unless self.pfamily == Socket::PF_UNIX
|
||||
sock.bind(self)
|
||||
sock.listen(backlog)
|
||||
rescue Exception
|
||||
|
|
|
@ -670,10 +670,10 @@ rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int
|
|||
{
|
||||
#if defined(HAVE_IF_INDEXTONAME)
|
||||
char ifbuf[IFNAMSIZ];
|
||||
if (if_indextoname(ifindex, ifbuf) == NULL)
|
||||
return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
|
||||
else
|
||||
if (if_indextoname(ifindex, ifbuf))
|
||||
return snprintf(buf, len, "%s%s", succ_prefix, ifbuf);
|
||||
else
|
||||
return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
|
||||
#else
|
||||
# ifndef IFNAMSIZ
|
||||
# define IFNAMSIZ (sizeof(unsigned int)*3+1)
|
||||
|
@ -1229,7 +1229,7 @@ sockopt_inspect(VALUE self)
|
|||
else
|
||||
rb_str_catf(ret, " optname:%d", optname);
|
||||
}
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
else if (family == AF_UNIX) {
|
||||
rb_str_catf(ret, " level:%d", level);
|
||||
|
||||
|
@ -1393,7 +1393,7 @@ sockopt_inspect(VALUE self)
|
|||
}
|
||||
break;
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
case AF_UNIX:
|
||||
switch (level) {
|
||||
case 0:
|
||||
|
|
|
@ -644,7 +644,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
|
|||
return ary;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
static long
|
||||
unixsocket_len(const struct sockaddr_un *su, socklen_t socklen)
|
||||
{
|
||||
|
@ -1017,7 +1017,7 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE
|
|||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
static void
|
||||
init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype)
|
||||
{
|
||||
|
@ -1146,7 +1146,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self)
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
case AF_UNIX: /* ["AF_UNIX", "/tmp/sock"] */
|
||||
{
|
||||
VALUE path = rb_ary_entry(sockaddr_ary, 1);
|
||||
|
@ -1286,7 +1286,7 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
case AF_UNIX:
|
||||
{
|
||||
struct sockaddr_un *addr = &sockaddr->un;
|
||||
|
@ -1622,7 +1622,7 @@ addrinfo_mdump(VALUE self)
|
|||
afamily = rb_id2str(id);
|
||||
|
||||
switch(afamily_int) {
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
case AF_UNIX:
|
||||
{
|
||||
sockaddr = rb_str_new(rai->addr.un.sun_path, rai_unixsocket_len(rai));
|
||||
|
@ -1715,7 +1715,7 @@ addrinfo_mload(VALUE self, VALUE ary)
|
|||
|
||||
v = rb_ary_entry(ary, 1);
|
||||
switch(afamily) {
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
case AF_UNIX:
|
||||
{
|
||||
struct sockaddr_un uaddr;
|
||||
|
@ -2343,7 +2343,7 @@ addrinfo_ipv6_to_ipv4(VALUE self)
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
/*
|
||||
* call-seq:
|
||||
* addrinfo.unix_path => path
|
||||
|
@ -2491,7 +2491,7 @@ addrinfo_s_udp(VALUE self, VALUE host, VALUE port)
|
|||
INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0));
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
|
@ -2629,7 +2629,7 @@ rsock_init_addrinfo(void)
|
|||
rb_define_singleton_method(rb_cAddrinfo, "ip", addrinfo_s_ip, 1);
|
||||
rb_define_singleton_method(rb_cAddrinfo, "tcp", addrinfo_s_tcp, 2);
|
||||
rb_define_singleton_method(rb_cAddrinfo, "udp", addrinfo_s_udp, 2);
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
rb_define_singleton_method(rb_cAddrinfo, "unix", addrinfo_s_unix, -1);
|
||||
#endif
|
||||
|
||||
|
@ -2670,7 +2670,7 @@ rsock_init_addrinfo(void)
|
|||
rb_define_method(rb_cAddrinfo, "ipv6_to_ipv4", addrinfo_ipv6_to_ipv4, 0);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
rb_define_method(rb_cAddrinfo, "unix_path", addrinfo_unix_path, 0);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# include <iphlpapi.h>
|
||||
# if defined(_MSC_VER)
|
||||
# undef HAVE_TYPE_STRUCT_SOCKADDR_DL
|
||||
# endif
|
||||
|
@ -69,6 +72,11 @@
|
|||
# include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_AFUNIX_H
|
||||
// Windows doesn't have sys/un.h, but it does have afunix.h just to be special:
|
||||
# include <afunix.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FCNTL)
|
||||
# ifdef HAVE_SYS_SELECT_H
|
||||
# include <sys/select.h>
|
||||
|
@ -268,7 +276,7 @@ extern VALUE rb_cIPSocket;
|
|||
extern VALUE rb_cTCPSocket;
|
||||
extern VALUE rb_cTCPServer;
|
||||
extern VALUE rb_cUDPSocket;
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
extern VALUE rb_cUNIXSocket;
|
||||
extern VALUE rb_cUNIXServer;
|
||||
#endif
|
||||
|
@ -336,7 +344,7 @@ VALUE rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len);
|
|||
|
||||
int rsock_revlookup_flag(VALUE revlookup, int *norevlookup);
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
VALUE rsock_unixpath_str(struct sockaddr_un *sockaddr, socklen_t len);
|
||||
VALUE rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len);
|
||||
socklen_t rsock_unix_sockaddr_len(VALUE path);
|
||||
|
|
|
@ -1383,7 +1383,7 @@ sock_s_unpack_sockaddr_in(VALUE self, VALUE addr)
|
|||
return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
|
@ -1471,7 +1471,7 @@ sockaddr_len(struct sockaddr *addr)
|
|||
return (socklen_t)sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
case AF_UNIX:
|
||||
return (socklen_t)sizeof(struct sockaddr_un);
|
||||
#endif
|
||||
|
@ -2020,7 +2020,7 @@ Init_socket(void)
|
|||
rb_define_singleton_method(rb_cSocket, "sockaddr_in", sock_s_pack_sockaddr_in, 2);
|
||||
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_in", sock_s_pack_sockaddr_in, 2);
|
||||
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_in", sock_s_unpack_sockaddr_in, 1);
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
rb_define_singleton_method(rb_cSocket, "sockaddr_un", sock_s_pack_sockaddr_un, 1);
|
||||
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1);
|
||||
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include "rubysocket.h"
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
/*
|
||||
* call-seq:
|
||||
* UNIXServer.new(path) => unixserver
|
||||
|
@ -101,7 +101,7 @@ unix_sysaccept(VALUE server)
|
|||
void
|
||||
rsock_init_unixserver(void)
|
||||
{
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
/*
|
||||
* Document-class: UNIXServer < UNIXSocket
|
||||
*
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include "rubysocket.h"
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
struct unixsock_arg {
|
||||
struct sockaddr_un *sockaddr;
|
||||
socklen_t sockaddrlen;
|
||||
|
@ -42,6 +42,10 @@ unixsock_path_value(VALUE path)
|
|||
return name; /* ignore encoding */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
/* UNIXSocket requires UTF-8 per spec. */
|
||||
path = rb_str_export_to_enc(path, rb_utf8_encoding());
|
||||
#endif
|
||||
return rb_get_path(path);
|
||||
}
|
||||
|
@ -571,7 +575,7 @@ unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
|
|||
void
|
||||
rsock_init_unixsocket(void)
|
||||
{
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
|
||||
/*
|
||||
* Document-class: UNIXSocket < BasicSocket
|
||||
*
|
||||
|
|
3
file.c
3
file.c
|
@ -1765,8 +1765,8 @@ rb_file_socket_p(VALUE obj, VALUE fname)
|
|||
|
||||
if (rb_stat(fname, &st) < 0) return Qfalse;
|
||||
if (S_ISSOCK(st.st_mode)) return Qtrue;
|
||||
|
||||
#endif
|
||||
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
|
@ -5600,6 +5600,7 @@ rb_stat_init(VALUE obj, VALUE fname)
|
|||
if (STAT(StringValueCStr(fname), &st) == -1) {
|
||||
rb_sys_fail_path(fname);
|
||||
}
|
||||
|
||||
if (DATA_PTR(obj)) {
|
||||
xfree(DATA_PTR(obj));
|
||||
DATA_PTR(obj) = NULL;
|
||||
|
|
|
@ -18,11 +18,6 @@ RUBY_SYMBOL_EXPORT_BEGIN
|
|||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Definitions for NT port of Perl
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Ok now we can include the normal include files.
|
||||
*/
|
||||
|
@ -392,6 +387,7 @@ scalb(double a, long b)
|
|||
#endif
|
||||
|
||||
#define S_IFLNK 0xa000
|
||||
#define S_IFSOCK 0xc000
|
||||
|
||||
/*
|
||||
* define this so we can do inplace editing
|
||||
|
|
|
@ -10,12 +10,6 @@ class TestFiberEnumerator < Test::Unit::TestCase
|
|||
|
||||
i, o = UNIXSocket.pair
|
||||
|
||||
unless i.nonblock? && o.nonblock?
|
||||
i.close
|
||||
o.close
|
||||
omit "I/O is not non-blocking!"
|
||||
end
|
||||
|
||||
message = String.new
|
||||
|
||||
thread = Thread.new do
|
||||
|
|
|
@ -6,14 +6,12 @@ class TestFiberIO < Test::Unit::TestCase
|
|||
MESSAGE = "Hello World"
|
||||
|
||||
def test_read
|
||||
omit "UNIXSocket is not defined!" unless defined?(UNIXSocket)
|
||||
omit unless defined?(UNIXSocket)
|
||||
|
||||
i, o = UNIXSocket.pair
|
||||
|
||||
unless i.nonblock? && o.nonblock?
|
||||
i.close
|
||||
o.close
|
||||
omit "I/O is not non-blocking!"
|
||||
if RUBY_PLATFORM=~/mswin|mingw/
|
||||
i.nonblock = true
|
||||
o.nonblock = true
|
||||
end
|
||||
|
||||
message = nil
|
||||
|
@ -46,6 +44,10 @@ class TestFiberIO < Test::Unit::TestCase
|
|||
16.times.map do
|
||||
Thread.new do
|
||||
i, o = UNIXSocket.pair
|
||||
if RUBY_PLATFORM=~/mswin|mingw/
|
||||
i.nonblock = true
|
||||
o.nonblock = true
|
||||
end
|
||||
|
||||
scheduler = Scheduler.new
|
||||
Fiber.set_scheduler scheduler
|
||||
|
@ -64,16 +66,11 @@ class TestFiberIO < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_epipe_on_read
|
||||
omit "UNIXSocket is not defined!" unless defined?(UNIXSocket)
|
||||
omit unless defined?(UNIXSocket)
|
||||
omit "nonblock=true isn't properly supported on Windows" if RUBY_PLATFORM=~/mswin|mingw/
|
||||
|
||||
i, o = UNIXSocket.pair
|
||||
|
||||
unless i.nonblock? && o.nonblock?
|
||||
i.close
|
||||
o.close
|
||||
omit "I/O is not non-blocking!"
|
||||
end
|
||||
|
||||
error = nil
|
||||
|
||||
thread = Thread.new do
|
||||
|
|
|
@ -472,10 +472,14 @@ class TestFileUtils < Test::Unit::TestCase
|
|||
else
|
||||
def test_cp_r_socket
|
||||
pend "Skipping socket test on JRuby" if RUBY_ENGINE == 'jruby'
|
||||
|
||||
Dir.mkdir('tmp/cpr_src')
|
||||
UNIXServer.new('tmp/cpr_src/socket').close
|
||||
cp_r 'tmp/cpr_src', 'tmp/cpr_dest'
|
||||
assert_equal(true, File.socket?('tmp/cpr_dest/socket'))
|
||||
rescue Errno::EINVAL => error
|
||||
# On some platforms (windows) sockets cannot be copied by FileUtils.
|
||||
omit error.message
|
||||
end if defined?(UNIXServer)
|
||||
end
|
||||
|
||||
|
|
|
@ -649,7 +649,7 @@ class TestFileExhaustive < Test::Unit::TestCase
|
|||
# ignore unsupporting filesystems
|
||||
rescue Errno::EPERM
|
||||
# Docker prohibits statx syscall by the default.
|
||||
skip("statx(2) is prohibited by seccomp")
|
||||
omit("statx(2) is prohibited by seccomp")
|
||||
end
|
||||
assert_raise(Errno::ENOENT) { File.birthtime(nofile) }
|
||||
end if File.respond_to?(:birthtime)
|
||||
|
|
|
@ -900,6 +900,10 @@ class TestIO < Test::Unit::TestCase
|
|||
end if defined? UNIXSocket
|
||||
|
||||
def test_copy_stream_socket4
|
||||
if RUBY_PLATFORM =~ /mingw|mswin/
|
||||
omit "pread(2) is not implemented."
|
||||
end
|
||||
|
||||
with_bigsrc {|bigsrc, bigcontent|
|
||||
File.open(bigsrc) {|f|
|
||||
assert_equal(0, f.pos)
|
||||
|
@ -916,9 +920,13 @@ class TestIO < Test::Unit::TestCase
|
|||
}
|
||||
}
|
||||
}
|
||||
end if defined? UNIXSocket
|
||||
end
|
||||
|
||||
def test_copy_stream_socket5
|
||||
if RUBY_PLATFORM =~ /mingw|mswin/
|
||||
omit "pread(2) is not implemented."
|
||||
end
|
||||
|
||||
with_bigsrc {|bigsrc, bigcontent|
|
||||
File.open(bigsrc) {|f|
|
||||
assert_equal(bigcontent[0,100], f.read(100))
|
||||
|
@ -936,9 +944,13 @@ class TestIO < Test::Unit::TestCase
|
|||
}
|
||||
}
|
||||
}
|
||||
end if defined? UNIXSocket
|
||||
end
|
||||
|
||||
def test_copy_stream_socket6
|
||||
if RUBY_PLATFORM =~ /mingw|mswin/
|
||||
omit "pread(2) is not implemented."
|
||||
end
|
||||
|
||||
mkcdtmpdir {
|
||||
megacontent = "abc" * 1234567
|
||||
File.open("megasrc", "w") {|f| f << megacontent }
|
||||
|
@ -959,9 +971,13 @@ class TestIO < Test::Unit::TestCase
|
|||
assert_equal(megacontent, result)
|
||||
}
|
||||
}
|
||||
end if defined? UNIXSocket
|
||||
end
|
||||
|
||||
def test_copy_stream_socket7
|
||||
if RUBY_PLATFORM =~ /mingw|mswin/
|
||||
omit "pread(2) is not implemented."
|
||||
end
|
||||
|
||||
GC.start
|
||||
mkcdtmpdir {
|
||||
megacontent = "abc" * 1234567
|
||||
|
@ -996,7 +1012,7 @@ class TestIO < Test::Unit::TestCase
|
|||
end
|
||||
}
|
||||
}
|
||||
end if defined? UNIXSocket and IO.method_defined?("nonblock=")
|
||||
end
|
||||
|
||||
def test_copy_stream_strio
|
||||
src = StringIO.new("abcd")
|
||||
|
|
|
@ -9,12 +9,6 @@ class TestIOTimeout < Test::Unit::TestCase
|
|||
begin
|
||||
i, o = UNIXSocket.pair
|
||||
|
||||
unless i.nonblock? && o.nonblock?
|
||||
i.close
|
||||
o.close
|
||||
omit "I/O is not non-blocking!"
|
||||
end
|
||||
|
||||
yield i, o
|
||||
ensure
|
||||
i.close
|
||||
|
|
|
@ -307,11 +307,13 @@ class TestSocketNonblock < Test::Unit::TestCase
|
|||
loop { s1.sendmsg_nonblock(buf) }
|
||||
end
|
||||
end
|
||||
rescue NotImplementedError, Errno::ENOSYS, Errno::EPROTONOSUPPORT
|
||||
rescue NotImplementedError, Errno::ENOSYS, Errno::EPROTONOSUPPORT, Errno::EPROTOTYPE
|
||||
omit "UNIXSocket.pair(:SEQPACKET) not implemented on this platform: #{$!}"
|
||||
end
|
||||
|
||||
def test_sendmsg_nonblock_no_exception
|
||||
omit "AF_UNIX + SEQPACKET is not supported on windows" if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
|
||||
buf = '*' * 4096
|
||||
UNIXSocket.pair(:SEQPACKET) do |s1, s2|
|
||||
n = 0
|
||||
|
|
|
@ -60,6 +60,8 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
|
|||
assert_not_equal s1.fileno, r.fileno
|
||||
r.close
|
||||
end
|
||||
rescue NotImplementedError => error
|
||||
omit error.message
|
||||
end
|
||||
|
||||
def test_fd_passing_n
|
||||
|
@ -334,62 +336,70 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_noname_path
|
||||
s1, s2 = UNIXSocket.pair
|
||||
assert_equal("", s1.path)
|
||||
assert_equal("", s2.path)
|
||||
ensure
|
||||
s1.close
|
||||
s2.close
|
||||
if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
omit "unnamed pipe is emulated on windows"
|
||||
end
|
||||
|
||||
UNIXSocket.pair do |s1, s2|
|
||||
assert_equal("", s1.path)
|
||||
assert_equal("", s2.path)
|
||||
end
|
||||
end
|
||||
|
||||
def test_noname_addr
|
||||
s1, s2 = UNIXSocket.pair
|
||||
assert_equal(["AF_UNIX", ""], s1.addr)
|
||||
assert_equal(["AF_UNIX", ""], s2.addr)
|
||||
ensure
|
||||
s1.close
|
||||
s2.close
|
||||
if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
omit "unnamed pipe is emulated on windows"
|
||||
end
|
||||
|
||||
UNIXSocket.pair do |s1, s2|
|
||||
assert_equal(["AF_UNIX", ""], s1.addr)
|
||||
assert_equal(["AF_UNIX", ""], s2.addr)
|
||||
end
|
||||
end
|
||||
|
||||
def test_noname_peeraddr
|
||||
s1, s2 = UNIXSocket.pair
|
||||
assert_equal(["AF_UNIX", ""], s1.peeraddr)
|
||||
assert_equal(["AF_UNIX", ""], s2.peeraddr)
|
||||
ensure
|
||||
s1.close
|
||||
s2.close
|
||||
if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
omit "unnamed pipe is emulated on windows"
|
||||
end
|
||||
|
||||
UNIXSocket.pair do |s1, s2|
|
||||
assert_equal(["AF_UNIX", ""], s1.peeraddr)
|
||||
assert_equal(["AF_UNIX", ""], s2.peeraddr)
|
||||
end
|
||||
end
|
||||
|
||||
def test_noname_unpack_sockaddr_un
|
||||
s1, s2 = UNIXSocket.pair
|
||||
n = nil
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getsockname) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getpeername) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getpeername) != ""
|
||||
ensure
|
||||
s1.close
|
||||
s2.close
|
||||
if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
omit "unnamed pipe is emulated on windows"
|
||||
end
|
||||
|
||||
UNIXSocket.pair do |s1, s2|
|
||||
n = nil
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getsockname) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getpeername) != ""
|
||||
assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getpeername) != ""
|
||||
end
|
||||
end
|
||||
|
||||
def test_noname_recvfrom
|
||||
s1, s2 = UNIXSocket.pair
|
||||
s2.write("a")
|
||||
assert_equal(["a", ["AF_UNIX", ""]], s1.recvfrom(10))
|
||||
ensure
|
||||
s1.close
|
||||
s2.close
|
||||
if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
omit "unnamed pipe is emulated on windows"
|
||||
end
|
||||
|
||||
UNIXSocket.pair do |s1, s2|
|
||||
s2.write("a")
|
||||
assert_equal(["a", ["AF_UNIX", ""]], s1.recvfrom(10))
|
||||
end
|
||||
end
|
||||
|
||||
def test_noname_recv_nonblock
|
||||
s1, s2 = UNIXSocket.pair
|
||||
s2.write("a")
|
||||
IO.select [s1]
|
||||
assert_equal("a", s1.recv_nonblock(10))
|
||||
ensure
|
||||
s1.close
|
||||
s2.close
|
||||
UNIXSocket.pair do |s1, s2|
|
||||
s2.write("a")
|
||||
IO.select [s1]
|
||||
assert_equal("a", s1.recv_nonblock(10))
|
||||
end
|
||||
end
|
||||
|
||||
def test_too_long_path
|
||||
|
@ -429,12 +439,18 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
|
|||
rv = s1.recv(100, 0, buf)
|
||||
assert_equal buf.object_id, rv.object_id
|
||||
assert_equal "BBBBBB", rv
|
||||
rescue Errno::EPROTOTYPE => error
|
||||
omit error.message
|
||||
ensure
|
||||
s1.close if s1
|
||||
s2.close if s2
|
||||
end
|
||||
|
||||
def test_dgram_pair_sendrecvmsg_errno_set
|
||||
if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
omit("AF_UNIX + SOCK_DGRAM is not supported on windows")
|
||||
end
|
||||
|
||||
s1, s2 = to_close = UNIXSocket.pair(Socket::SOCK_DGRAM)
|
||||
pipe = IO.pipe
|
||||
to_close.concat(pipe)
|
||||
|
@ -457,9 +473,17 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_epipe # [ruby-dev:34619]
|
||||
# This is a good example of why reporting the exact `errno` is a terrible
|
||||
# idea for platform abstractions.
|
||||
if RUBY_PLATFORM =~ /mswin|mingw/
|
||||
error = Errno::ESHUTDOWN
|
||||
else
|
||||
error = Errno::EPIPE
|
||||
end
|
||||
|
||||
UNIXSocket.pair {|s1, s2|
|
||||
s1.shutdown(Socket::SHUT_WR)
|
||||
assert_raise(Errno::EPIPE) { s1.write "a" }
|
||||
assert_raise(error) { s1.write "a" }
|
||||
assert_equal(nil, s2.read(1))
|
||||
s2.write "a"
|
||||
assert_equal("a", s1.read(1))
|
||||
|
@ -493,6 +517,45 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
|
|||
}
|
||||
end
|
||||
|
||||
if /mingw|mswin/ =~ RUBY_PLATFORM
|
||||
|
||||
def test_unix_socket_with_encoding
|
||||
Dir.mktmpdir do |tmpdir|
|
||||
path = "#{tmpdir}/sockäöü".encode("cp850")
|
||||
UNIXServer.open(path) do |serv|
|
||||
assert File.socket?(path)
|
||||
assert File.stat(path).socket?
|
||||
assert File.lstat(path).socket?
|
||||
assert_equal path.encode("utf-8"), serv.path
|
||||
UNIXSocket.open(path) do |s1|
|
||||
s2 = serv.accept
|
||||
s2.close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_windows_unix_socket_pair_with_umlaut
|
||||
otmp = ENV['TMP']
|
||||
ENV['TMP'] = File.join(Dir.tmpdir, "äöü€")
|
||||
FileUtils.mkdir_p ENV['TMP']
|
||||
|
||||
s1, s2 = UNIXSocket.pair
|
||||
assert !s1.path.empty?
|
||||
assert !File.exist?(s1.path)
|
||||
ensure
|
||||
FileUtils.rm_rf ENV['TMP']
|
||||
ENV['TMP'] = otmp
|
||||
end
|
||||
|
||||
def test_windows_unix_socket_pair_paths
|
||||
s1, s2 = UNIXSocket.pair
|
||||
assert !s1.path.empty?
|
||||
assert s2.path.empty?
|
||||
assert !File.exist?(s1.path)
|
||||
end
|
||||
end
|
||||
|
||||
def test_initialize
|
||||
Dir.mktmpdir {|d|
|
||||
Socket.open(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) {|s|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
154
win32/win32.c
154
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;
|
||||
|
@ -4050,6 +4131,15 @@ socketpair_internal(int af, int type, int protocol, SOCKET *sv)
|
|||
addr = (struct sockaddr *)&sock_in6;
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче