From 6b90c695abfd4308ccb4c6d6a1b09cacab774e43 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Fri, 18 Mar 2022 11:00:30 +0100 Subject: [PATCH 01/12] Replace winapi/ntapi with windows-sys This updates miow to 0.4, which now uses the windows-sys crate instead of winapi, as the former is maintained and updated frequently as opposed to winapi. The windows-sys crate also covers more of the Windows API surface, which also allowed me to remove the dependency on ntapi (as it still depends on winapi). There was only a single function, `NtCancelIoFileEx` that was present in ntapi but not windows-sys, so I merely added the extern declaration in the one place it was used as it is not worth bringing in a dependency just for that. --- Cargo.toml | 16 +++++-- src/sys/windows/afd.rs | 69 ++++++++++++++++++------------ src/sys/windows/io_status_block.rs | 6 +-- src/sys/windows/named_pipe.rs | 7 +-- src/sys/windows/net.rs | 56 ++++++++++++------------ src/sys/windows/overlapped.rs | 4 +- src/sys/windows/selector.rs | 39 +++++++++-------- src/sys/windows/tcp.rs | 14 +++--- src/sys/windows/udp.rs | 20 ++++----- tests/util/mod.rs | 12 +++--- tests/win_named_pipe.rs | 3 +- 11 files changed, 137 insertions(+), 109 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4f6ff4d9b..82c2df2de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ default = [] # Enables the `Poll` and `Registry` types. os-poll = [] # Enables additional OS specific extensions, e.g. Unix `pipe(2)`. -os-ext = ["os-poll"] +os-ext = ["os-poll", "windows-sys/Win32_System_Pipes"] # Enables `mio::net` module containing networking primitives. net = [] @@ -45,9 +45,17 @@ log = "0.4.8" libc = "0.2.86" [target.'cfg(windows)'.dependencies] -miow = "0.3.6" -winapi = { version = "0.3", features = ["winsock2", "mswsock"] } -ntapi = "0.3" +miow = "0.4" + +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.28" # This matches the version in miow, but it's fairly outdated +features = [ + "Win32_Storage_FileSystem", # Enables NtCreateFile + "Win32_Foundation", # Basic types eg HANDLE + "Win32_Networking_WinSock", # winsock2 types/functions + "Win32_System_IO", # IO types like OVERLAPPED etc + "Win32_System_WindowsProgramming", # General future used for various types/funcs +] [target.'cfg(target_os = "wasi")'.dependencies] wasi = "0.11.0" diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs index 6eae3bc03..2c7a99cde 100644 --- a/src/sys/windows/afd.rs +++ b/src/sys/windows/afd.rs @@ -1,17 +1,28 @@ -use ntapi::ntioapi::{IO_STATUS_BLOCK_u, IO_STATUS_BLOCK}; -use ntapi::ntioapi::{NtCancelIoFileEx, NtDeviceIoControlFile}; -use ntapi::ntrtl::RtlNtStatusToDosError; +use std::ffi::c_void; use std::fmt; use std::fs::File; use std::io; use std::mem::size_of; use std::os::windows::io::AsRawHandle; use std::ptr::null_mut; -use winapi::shared::ntdef::{HANDLE, LARGE_INTEGER, NTSTATUS, PVOID, ULONG}; -use winapi::shared::ntstatus::{STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS}; - -const IOCTL_AFD_POLL: ULONG = 0x00012024; - +use windows_sys::Win32::{ + Foundation::{ + RtlNtStatusToDosError, HANDLE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS, + }, + System::WindowsProgramming::{NtDeviceIoControlFile, IO_STATUS_BLOCK, IO_STATUS_BLOCK_0}, +}; + +const IOCTL_AFD_POLL: u32 = 0x00012024; + +/// +#[link(name = "ntdll")] +extern "system" { + fn NtCancelIoFileEx( + FileHandle: HANDLE, + IoRequestToCancel: *mut IO_STATUS_BLOCK, + IoStatusBlock: *mut IO_STATUS_BLOCK, + ) -> NTSTATUS; +} /// Winsock2 AFD driver instance. /// /// All operations are unsafe due to IO_STATUS_BLOCK parameter are being used by Afd driver during STATUS_PENDING before I/O Completion Port returns its result. @@ -24,7 +35,7 @@ pub struct Afd { #[derive(Debug)] pub struct AfdPollHandleInfo { pub handle: HANDLE, - pub events: ULONG, + pub events: u32, pub status: NTSTATUS, } @@ -32,10 +43,10 @@ unsafe impl Send for AfdPollHandleInfo {} #[repr(C)] pub struct AfdPollInfo { - pub timeout: LARGE_INTEGER, + pub timeout: i64, // Can have only value 1. - pub number_of_handles: ULONG, - pub exclusive: ULONG, + pub number_of_handles: u32, + pub exclusive: u32, pub handles: [AfdPollHandleInfo; 1], } @@ -58,10 +69,10 @@ impl Afd { &self, info: &mut AfdPollInfo, iosb: *mut IO_STATUS_BLOCK, - overlapped: PVOID, + overlapped: *mut c_void, ) -> io::Result { - let info_ptr: PVOID = info as *mut _ as PVOID; - (*iosb).u.Status = STATUS_PENDING; + let info_ptr = info as *mut _ as *mut c_void; + (*iosb).Anonymous.Status = STATUS_PENDING; let status = NtDeviceIoControlFile( self.fd.as_raw_handle(), null_mut(), @@ -93,12 +104,12 @@ impl Afd { /// Use it only with request is still being polled so that you have valid `IO_STATUS_BLOCK` to use. /// User should NOT deallocate there overlapped value after the `cancel` to prevent double free. pub unsafe fn cancel(&self, iosb: *mut IO_STATUS_BLOCK) -> io::Result<()> { - if (*iosb).u.Status != STATUS_PENDING { + if (*iosb).Anonymous.Status != STATUS_PENDING { return Ok(()); } let mut cancel_iosb = IO_STATUS_BLOCK { - u: IO_STATUS_BLOCK_u { Status: 0 }, + Anonymous: IO_STATUS_BLOCK_0 { Status: 0 }, Information: 0, }; let status = NtCancelIoFileEx(self.fd.as_raw_handle(), iosb, &mut cancel_iosb); @@ -117,14 +128,16 @@ cfg_io_source! { use std::sync::atomic::{AtomicUsize, Ordering}; use miow::iocp::CompletionPort; - use ntapi::ntioapi::{NtCreateFile, FILE_OPEN}; - use winapi::shared::ntdef::{OBJECT_ATTRIBUTES, UNICODE_STRING, USHORT, WCHAR}; - use winapi::um::handleapi::INVALID_HANDLE_VALUE; - use winapi::um::winbase::{SetFileCompletionNotificationModes, FILE_SKIP_SET_EVENT_ON_HANDLE}; - use winapi::um::winnt::{SYNCHRONIZE, FILE_SHARE_READ, FILE_SHARE_WRITE}; + use windows_sys::Win32::{ + Foundation::{UNICODE_STRING, INVALID_HANDLE_VALUE}, + System::WindowsProgramming::{ + OBJECT_ATTRIBUTES, FILE_SKIP_SET_EVENT_ON_HANDLE, + }, + Storage::FileSystem::{FILE_OPEN, NtCreateFile, SetFileCompletionNotificationModes, SYNCHRONIZE, FILE_SHARE_READ, FILE_SHARE_WRITE}, + }; const AFD_HELPER_ATTRIBUTES: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES { - Length: size_of::() as ULONG, + Length: size_of::() as u32, RootDirectory: null_mut(), ObjectName: &AFD_OBJ_NAME as *const _ as *mut _, Attributes: 0, @@ -133,12 +146,12 @@ cfg_io_source! { }; const AFD_OBJ_NAME: UNICODE_STRING = UNICODE_STRING { - Length: (AFD_HELPER_NAME.len() * size_of::()) as USHORT, - MaximumLength: (AFD_HELPER_NAME.len() * size_of::()) as USHORT, + Length: (AFD_HELPER_NAME.len() * size_of::()) as u16, + MaximumLength: (AFD_HELPER_NAME.len() * size_of::()) as u16, Buffer: AFD_HELPER_NAME.as_ptr() as *mut _, }; - const AFD_HELPER_NAME: &[WCHAR] = &[ + const AFD_HELPER_NAME: &[u16] = &[ '\\' as _, 'D' as _, 'e' as _, @@ -169,7 +182,7 @@ cfg_io_source! { pub fn new(cp: &CompletionPort) -> io::Result { let mut afd_helper_handle: HANDLE = INVALID_HANDLE_VALUE; let mut iosb = IO_STATUS_BLOCK { - u: IO_STATUS_BLOCK_u { Status: 0 }, + Anonymous: IO_STATUS_BLOCK_0 { Status: 0 }, Information: 0, }; @@ -204,7 +217,7 @@ cfg_io_source! { cp.add_handle(token, &afd.fd)?; match SetFileCompletionNotificationModes( afd_helper_handle, - FILE_SKIP_SET_EVENT_ON_HANDLE, + FILE_SKIP_SET_EVENT_ON_HANDLE as u8 // This is just 2, so fits in u8 ) { 0 => Err(io::Error::last_os_error()), _ => Ok(afd), diff --git a/src/sys/windows/io_status_block.rs b/src/sys/windows/io_status_block.rs index 3e6033496..d7eda6a6d 100644 --- a/src/sys/windows/io_status_block.rs +++ b/src/sys/windows/io_status_block.rs @@ -1,17 +1,17 @@ use std::fmt; use std::ops::{Deref, DerefMut}; -use ntapi::ntioapi::IO_STATUS_BLOCK; +use windows_sys::Win32::System::WindowsProgramming::IO_STATUS_BLOCK; pub struct IoStatusBlock(IO_STATUS_BLOCK); cfg_io_source! { - use ntapi::ntioapi::IO_STATUS_BLOCK_u; + use windows_sys::Win32::System::WindowsProgramming::{IO_STATUS_BLOCK_0}; impl IoStatusBlock { pub fn zeroed() -> Self { Self(IO_STATUS_BLOCK { - u: IO_STATUS_BLOCK_u { Status: 0 }, + Anonymous: IO_STATUS_BLOCK_0 { Status: 0 }, Information: 0, }) } diff --git a/src/sys/windows/named_pipe.rs b/src/sys/windows/named_pipe.rs index adda51f23..f17dc5bfc 100644 --- a/src/sys/windows/named_pipe.rs +++ b/src/sys/windows/named_pipe.rs @@ -8,9 +8,10 @@ use std::{fmt, mem, slice}; use miow::iocp::{CompletionPort, CompletionStatus}; use miow::pipe; -use winapi::shared::winerror::{ERROR_BROKEN_PIPE, ERROR_PIPE_LISTENING}; -use winapi::um::ioapiset::CancelIoEx; -use winapi::um::minwinbase::{OVERLAPPED, OVERLAPPED_ENTRY}; +use windows_sys::Win32::{ + Foundation::{ERROR_BROKEN_PIPE, ERROR_PIPE_LISTENING}, + System::IO::{CancelIoEx, OVERLAPPED, OVERLAPPED_ENTRY}, +}; use crate::event::Source; use crate::sys::windows::{Event, Overlapped}; diff --git a/src/sys/windows/net.rs b/src/sys/windows/net.rs index db1896f19..2df4b6894 100644 --- a/src/sys/windows/net.rs +++ b/src/sys/windows/net.rs @@ -3,12 +3,16 @@ use std::mem; use std::net::SocketAddr; use std::sync::Once; -use winapi::ctypes::c_int; -use winapi::shared::in6addr::{in6_addr_u, IN6_ADDR}; -use winapi::shared::inaddr::{in_addr_S_un, IN_ADDR}; -use winapi::shared::ws2def::{ADDRESS_FAMILY, AF_INET, AF_INET6, SOCKADDR, SOCKADDR_IN}; -use winapi::shared::ws2ipdef::{SOCKADDR_IN6_LH_u, SOCKADDR_IN6_LH}; -use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET}; +use windows_sys::Win32::Networking::WinSock::{ + ioctlsocket, socket, FIONBIO, IN6_ADDR, IN6_ADDR_0, INVALID_SOCKET, IN_ADDR, IN_ADDR_0, + SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6, SOCKADDR_IN6_0, SOCKET, +}; + +// AF_INET/6 are in the Win32_NetworkManagement_IpHelper feature for some reason, +// so just directly define them here rather than import that whole feature set +// these constants won't ever change +pub(crate) const AF_INET: u16 = 2; +pub(crate) const AF_INET6: u16 = 23; /// Initialise the network stack for Windows. pub(crate) fn init() { @@ -22,20 +26,18 @@ pub(crate) fn init() { } /// Create a new non-blocking socket. -pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: c_int) -> io::Result { - use winapi::um::winsock2::{PF_INET, PF_INET6}; - +pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: u16) -> io::Result { let domain = match addr { - SocketAddr::V4(..) => PF_INET, - SocketAddr::V6(..) => PF_INET6, + SocketAddr::V4(..) => AF_INET, + SocketAddr::V6(..) => AF_INET6, }; new_socket(domain, socket_type) } -pub(crate) fn new_socket(domain: c_int, socket_type: c_int) -> io::Result { +pub(crate) fn new_socket(domain: u16, socket_type: u16) -> io::Result { syscall!( - socket(domain, socket_type, 0), + socket(domain as i32, socket_type as i32, 0), PartialEq::eq, INVALID_SOCKET ) @@ -51,7 +53,7 @@ pub(crate) fn new_socket(domain: c_int, socket_type: c_int) -> io::Result (SocketAddrCRepr, c_int) { +pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, i32) { match addr { SocketAddr::V4(ref addr) => { // `s_addr` is stored as BE on all machine and the array is in BE order. // So the native endian conversion method is used so that it's never swapped. let sin_addr = unsafe { - let mut s_un = mem::zeroed::(); - *s_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets()); + let mut s_un = mem::zeroed::(); + s_un.S_addr = u32::from_ne_bytes(addr.ip().octets()); IN_ADDR { S_un: s_un } }; let sockaddr_in = SOCKADDR_IN { - sin_family: AF_INET as ADDRESS_FAMILY, + sin_family: AF_INET, sin_port: addr.port().to_be(), sin_addr, sin_zero: [0; 8], }; let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; - (sockaddr, mem::size_of::() as c_int) + (sockaddr, mem::size_of::() as i32) } SocketAddr::V6(ref addr) => { let sin6_addr = unsafe { - let mut u = mem::zeroed::(); - *u.Byte_mut() = addr.ip().octets(); + let mut u = mem::zeroed::(); + u.Byte = addr.ip().octets(); IN6_ADDR { u } }; let u = unsafe { - let mut u = mem::zeroed::(); - *u.sin6_scope_id_mut() = addr.scope_id(); + let mut u = mem::zeroed::(); + u.sin6_scope_id = addr.scope_id(); u }; - let sockaddr_in6 = SOCKADDR_IN6_LH { - sin6_family: AF_INET6 as ADDRESS_FAMILY, + let sockaddr_in6 = SOCKADDR_IN6 { + sin6_family: AF_INET6, sin6_port: addr.port().to_be(), sin6_addr, sin6_flowinfo: addr.flowinfo(), - u, + Anonymous: u, }; let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; - (sockaddr, mem::size_of::() as c_int) + (sockaddr, mem::size_of::() as i32) } } } diff --git a/src/sys/windows/overlapped.rs b/src/sys/windows/overlapped.rs index 837b78b60..992c84a01 100644 --- a/src/sys/windows/overlapped.rs +++ b/src/sys/windows/overlapped.rs @@ -4,8 +4,8 @@ use std::cell::UnsafeCell; use std::fmt; #[cfg(feature = "os-ext")] -use winapi::um::minwinbase::OVERLAPPED; -use winapi::um::minwinbase::OVERLAPPED_ENTRY; +use windows_sys::Win32::System::IO::OVERLAPPED; +use windows_sys::Win32::System::IO::OVERLAPPED_ENTRY; #[repr(C)] pub(crate) struct Overlapped { diff --git a/src/sys/windows/selector.rs b/src/sys/windows/selector.rs index 133fefe89..63bcfbca0 100644 --- a/src/sys/windows/selector.rs +++ b/src/sys/windows/selector.rs @@ -12,6 +12,7 @@ cfg_net! { use miow::iocp::{CompletionPort, CompletionStatus}; use std::collections::VecDeque; +use std::ffi::c_void; use std::io; use std::marker::PhantomPinned; use std::os::windows::io::RawSocket; @@ -21,11 +22,11 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; -use winapi::shared::ntdef::NT_SUCCESS; -use winapi::shared::ntdef::{HANDLE, PVOID}; -use winapi::shared::ntstatus::STATUS_CANCELLED; -use winapi::shared::winerror::{ERROR_INVALID_HANDLE, ERROR_IO_PENDING, WAIT_TIMEOUT}; -use winapi::um::minwinbase::OVERLAPPED; + +use windows_sys::Win32::{ + Foundation::{ERROR_INVALID_HANDLE, ERROR_IO_PENDING, HANDLE, STATUS_CANCELLED, WAIT_TIMEOUT}, + System::IO::OVERLAPPED, +}; #[derive(Debug)] struct AfdGroup { @@ -141,7 +142,7 @@ impl SockState { /* No poll operation is pending; start one. */ self.poll_info.exclusive = 0; self.poll_info.number_of_handles = 1; - *unsafe { self.poll_info.timeout.QuadPart_mut() } = std::i64::MAX; + self.poll_info.timeout = i64::MAX; self.poll_info.handles[0].handle = self.base_socket as HANDLE; self.poll_info.handles[0].status = 0; self.poll_info.handles[0].events = self.user_evts | afd::POLL_LOCAL_CLOSE; @@ -204,9 +205,9 @@ impl SockState { unsafe { if self.delete_pending { return None; - } else if self.iosb.u.Status == STATUS_CANCELLED { + } else if self.iosb.Anonymous.Status == STATUS_CANCELLED { /* The poll request was cancelled by CancelIoEx. */ - } else if !NT_SUCCESS(self.iosb.u.Status) { + } else if self.iosb.Anonymous.Status < 0 { /* The overlapped request itself failed in an unexpected way. */ afd_events = afd::POLL_CONNECT_FAIL; } else if self.poll_info.number_of_handles < 1 { @@ -295,7 +296,7 @@ impl Drop for SockState { /// Converts the pointer to a `SockState` into a raw pointer. /// To revert see `from_overlapped`. -fn into_overlapped(sock_state: Pin>>) -> PVOID { +fn into_overlapped(sock_state: Pin>>) -> *mut c_void { let overlapped_ptr: *const Mutex = unsafe { Arc::into_raw(Pin::into_inner_unchecked(sock_state)) }; overlapped_ptr as *mut _ @@ -534,9 +535,13 @@ impl SelectorInner { cfg_io_source! { use std::mem::size_of; use std::ptr::null_mut; - use winapi::um::mswsock; - use winapi::um::winsock2::WSAGetLastError; - use winapi::um::winsock2::{WSAIoctl, SOCKET_ERROR}; + + use windows_sys::Win32::Networking::WinSock::{WSAGetLastError, SOCKET_ERROR, WSAIoctl, IOC_OUT, IOC_WS2}; + + const SIO_BSP_HANDLE: u32 = IOC_OUT | IOC_WS2 | 27; // 1_207_959_579u32 + const SIO_BSP_HANDLE_SELECT: u32 = IOC_OUT | IOC_WS2 | 28; // 1_207_959_580u32 + const SIO_BSP_HANDLE_POLL: u32 = IOC_OUT | IOC_WS2 | 29; // 1_207_959_581u32 + const SIO_BASE_HANDLE: u32 = IOC_OUT | IOC_WS2 | 34; // 1_207_959_586u32 impl SelectorInner { fn register( @@ -640,7 +645,7 @@ cfg_io_source! { ioctl, null_mut(), 0, - &mut base_socket as *mut _ as PVOID, + &mut base_socket as *mut _ as *mut c_void, size_of::() as u32, &mut bytes, null_mut(), @@ -655,7 +660,7 @@ cfg_io_source! { } fn get_base_socket(raw_socket: RawSocket) -> io::Result { - let res = try_get_base_socket(raw_socket, mswsock::SIO_BASE_HANDLE); + let res = try_get_base_socket(raw_socket, SIO_BASE_HANDLE); if let Ok(base_socket) = res { return Ok(base_socket); } @@ -666,9 +671,9 @@ cfg_io_source! { // However, at least one known LSP deliberately breaks it, so we try // some alternative IOCTLs, starting with the most appropriate one. for &ioctl in &[ - mswsock::SIO_BSP_HANDLE_SELECT, - mswsock::SIO_BSP_HANDLE_POLL, - mswsock::SIO_BSP_HANDLE, + SIO_BSP_HANDLE_SELECT, + SIO_BSP_HANDLE_POLL, + SIO_BSP_HANDLE, ] { if let Ok(base_socket) = try_get_base_socket(raw_socket, ioctl) { // Since we know now that we're dealing with an LSP (otherwise diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs index b3f05aec6..e452e875b 100644 --- a/src/sys/windows/tcp.rs +++ b/src/sys/windows/tcp.rs @@ -2,21 +2,21 @@ use std::io; use std::net::{self, SocketAddr}; use std::os::windows::io::AsRawSocket; -use winapi::um::winsock2::{self, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR, SOCK_STREAM}; +use windows_sys::Win32::Networking::WinSock::{self, SOCKET, SOCKET_ERROR, SOCK_STREAM}; -use crate::sys::windows::net::{init, new_socket, socket_addr}; +use crate::sys::windows::net::{init, new_socket, socket_addr, AF_INET, AF_INET6}; pub(crate) fn new_for_addr(address: SocketAddr) -> io::Result { init(); let domain = match address { - SocketAddr::V4(_) => PF_INET, - SocketAddr::V6(_) => PF_INET6, + SocketAddr::V4(_) => AF_INET, + SocketAddr::V6(_) => AF_INET6, }; new_socket(domain, SOCK_STREAM) } pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()> { - use winsock2::bind; + use WinSock::bind; let (raw_addr, raw_addr_length) = socket_addr(&addr); syscall!( @@ -32,7 +32,7 @@ pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<() } pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<()> { - use winsock2::connect; + use WinSock::connect; let (raw_addr, raw_addr_length) = socket_addr(&addr); let res = syscall!( @@ -53,7 +53,7 @@ pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<( pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> { use std::convert::TryInto; - use winsock2::listen; + use WinSock::listen; let backlog = backlog.try_into().unwrap_or(i32::max_value()); syscall!( diff --git a/src/sys/windows/udp.rs b/src/sys/windows/udp.rs index 825ecccff..91516ccc2 100644 --- a/src/sys/windows/udp.rs +++ b/src/sys/windows/udp.rs @@ -2,14 +2,12 @@ use std::io; use std::mem::{self, MaybeUninit}; use std::net::{self, SocketAddr}; use std::os::windows::io::{AsRawSocket, FromRawSocket}; -use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64. - -use winapi::ctypes::c_int; -use winapi::shared::ws2def::IPPROTO_IPV6; -use winapi::shared::ws2ipdef::IPV6_V6ONLY; -use winapi::um::winsock2::{bind as win_bind, closesocket, getsockopt, SOCKET_ERROR, SOCK_DGRAM}; +use std::os::windows::raw::SOCKET as StdSocket; // windows-sys uses usize, stdlib uses u32/u64. use crate::sys::windows::net::{init, new_ip_socket, socket_addr}; +use windows_sys::Win32::Networking::WinSock::{ + bind as win_bind, closesocket, getsockopt, IPPROTO_IPV6, IPV6_V6ONLY, SOCKET_ERROR, SOCK_DGRAM, +}; pub fn bind(addr: SocketAddr) -> io::Result { init(); @@ -31,14 +29,14 @@ pub fn bind(addr: SocketAddr) -> io::Result { } pub(crate) fn only_v6(socket: &net::UdpSocket) -> io::Result { - let mut optval: MaybeUninit = MaybeUninit::uninit(); - let mut optlen = mem::size_of::() as c_int; + let mut optval: MaybeUninit = MaybeUninit::uninit(); + let mut optlen = mem::size_of::() as i32; syscall!( getsockopt( socket.as_raw_socket() as usize, - IPPROTO_IPV6 as c_int, - IPV6_V6ONLY as c_int, + IPPROTO_IPV6 as i32, + IPV6_V6ONLY as i32, optval.as_mut_ptr().cast(), &mut optlen, ), @@ -46,7 +44,7 @@ pub(crate) fn only_v6(socket: &net::UdpSocket) -> io::Result { SOCKET_ERROR )?; - debug_assert_eq!(optlen as usize, mem::size_of::()); + debug_assert_eq!(optlen as usize, mem::size_of::()); // Safety: `getsockopt` initialised `optval` for us. let optval = unsafe { optval.assume_init() }; Ok(optval != 0) diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 7eedc345f..9233dbe72 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -263,9 +263,11 @@ pub fn set_linger_zero(socket: &TcpStream) { #[cfg(windows)] pub fn set_linger_zero(socket: &TcpStream) { use std::os::windows::io::AsRawSocket; - use winapi::um::winsock2::{linger, setsockopt, SOCKET_ERROR, SOL_SOCKET, SO_LINGER}; + use windows_sys::Win32::Networking::WinSock::{ + linger, setsockopt, SOCKET_ERROR, SOL_SOCKET, SO_LINGER, + }; - let val = linger { + let mut val = linger { l_onoff: 1, l_linger: 0, }; @@ -273,9 +275,9 @@ pub fn set_linger_zero(socket: &TcpStream) { let res = unsafe { setsockopt( socket.as_raw_socket() as _, - SOL_SOCKET, - SO_LINGER, - &val as *const _ as *const _, + SOL_SOCKET as i32, + SO_LINGER as i32, + &mut val as *mut _ as *mut _, size_of::() as _, ) }; diff --git a/tests/win_named_pipe.rs b/tests/win_named_pipe.rs index e1451f0df..50f281317 100644 --- a/tests/win_named_pipe.rs +++ b/tests/win_named_pipe.rs @@ -9,8 +9,7 @@ use std::time::Duration; use mio::windows::NamedPipe; use mio::{Events, Interest, Poll, Token}; use rand::Rng; -use winapi::shared::winerror::*; -use winapi::um::winbase::FILE_FLAG_OVERLAPPED; +use windows_sys::Win32::{Foundation::ERROR_NO_DATA, Storage::FileSystem::FILE_FLAG_OVERLAPPED}; fn _assert_kinds() { fn _assert_send() {} From a29c3f40cdbbb262c5216ff3c001cb73fc009801 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Fri, 18 Mar 2022 11:25:05 +0100 Subject: [PATCH 02/12] Remove unneeded doc comment --- src/sys/windows/afd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs index 2c7a99cde..8b87ca7c8 100644 --- a/src/sys/windows/afd.rs +++ b/src/sys/windows/afd.rs @@ -14,7 +14,7 @@ use windows_sys::Win32::{ const IOCTL_AFD_POLL: u32 = 0x00012024; -/// +// #[link(name = "ntdll")] extern "system" { fn NtCancelIoFileEx( From 61a071b47ac00a1e40fd7dcbfe02a7d0118fc24c Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 08:22:43 +0100 Subject: [PATCH 03/12] Remove miow dependency --- Cargo.toml | 11 +- src/sys/windows/afd.rs | 12 +- src/sys/windows/event.rs | 2 +- src/sys/windows/iocp.rs | 280 ++++++++++++++++++++++++++++++++++ src/sys/windows/mod.rs | 2 + src/sys/windows/named_pipe.rs | 278 +++++++++++++++++++++++++++++---- src/sys/windows/overlapped.rs | 10 +- src/sys/windows/selector.rs | 2 +- src/sys/windows/waker.rs | 2 +- 9 files changed, 549 insertions(+), 50 deletions(-) create mode 100644 src/sys/windows/iocp.rs diff --git a/Cargo.toml b/Cargo.toml index 82c2df2de..a1a473396 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,11 @@ default = [] # Enables the `Poll` and `Registry` types. os-poll = [] # Enables additional OS specific extensions, e.g. Unix `pipe(2)`. -os-ext = ["os-poll", "windows-sys/Win32_System_Pipes"] +os-ext = [ + "os-poll", + "windows-sys/Win32_System_Pipes", + "windows-sys/Win32_Security", +] # Enables `mio::net` module containing networking primitives. net = [] @@ -44,11 +48,8 @@ log = "0.4.8" [target.'cfg(unix)'.dependencies] libc = "0.2.86" -[target.'cfg(windows)'.dependencies] -miow = "0.4" - [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.28" # This matches the version in miow, but it's fairly outdated +version = "0.34" features = [ "Win32_Storage_FileSystem", # Enables NtCreateFile "Win32_Foundation", # Basic types eg HANDLE diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs index 8b87ca7c8..bb02d9148 100644 --- a/src/sys/windows/afd.rs +++ b/src/sys/windows/afd.rs @@ -74,8 +74,8 @@ impl Afd { let info_ptr = info as *mut _ as *mut c_void; (*iosb).Anonymous.Status = STATUS_PENDING; let status = NtDeviceIoControlFile( - self.fd.as_raw_handle(), - null_mut(), + self.fd.as_raw_handle() as HANDLE, + 0, None, overlapped, iosb, @@ -112,7 +112,7 @@ impl Afd { Anonymous: IO_STATUS_BLOCK_0 { Status: 0 }, Information: 0, }; - let status = NtCancelIoFileEx(self.fd.as_raw_handle(), iosb, &mut cancel_iosb); + let status = NtCancelIoFileEx(self.fd.as_raw_handle() as HANDLE, iosb, &mut cancel_iosb); if status == STATUS_SUCCESS || status == STATUS_NOT_FOUND { return Ok(()); } @@ -127,7 +127,7 @@ cfg_io_source! { use std::os::windows::io::{FromRawHandle, RawHandle}; use std::sync::atomic::{AtomicUsize, Ordering}; - use miow::iocp::CompletionPort; + use super::iocp::CompletionPort; use windows_sys::Win32::{ Foundation::{UNICODE_STRING, INVALID_HANDLE_VALUE}, System::WindowsProgramming::{ @@ -138,7 +138,7 @@ cfg_io_source! { const AFD_HELPER_ATTRIBUTES: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES { Length: size_of::() as u32, - RootDirectory: null_mut(), + RootDirectory: 0, ObjectName: &AFD_OBJ_NAME as *const _ as *mut _, Attributes: 0, SecurityDescriptor: null_mut(), @@ -179,7 +179,7 @@ cfg_io_source! { impl Afd { /// Create new Afd instance. - pub fn new(cp: &CompletionPort) -> io::Result { + pub(crate) fn new(cp: &CompletionPort) -> io::Result { let mut afd_helper_handle: HANDLE = INVALID_HANDLE_VALUE; let mut iosb = IO_STATUS_BLOCK { Anonymous: IO_STATUS_BLOCK_0 { Status: 0 }, diff --git a/src/sys/windows/event.rs b/src/sys/windows/event.rs index a49252a29..c3d1f6cf9 100644 --- a/src/sys/windows/event.rs +++ b/src/sys/windows/event.rs @@ -1,6 +1,6 @@ use std::fmt; -use miow::iocp::CompletionStatus; +use super::iocp::CompletionStatus; use super::afd; use crate::Token; diff --git a/src/sys/windows/iocp.rs b/src/sys/windows/iocp.rs new file mode 100644 index 000000000..143368b9f --- /dev/null +++ b/src/sys/windows/iocp.rs @@ -0,0 +1,280 @@ +//! Bindings to IOCP, I/O Completion Ports + +use crate::sys::windows::Overlapped; +use std::cmp; +use std::fmt; +use std::io; +use std::mem; +use std::os::windows::io::*; +use std::time::Duration; + +use windows_sys::Win32::{ + Foundation::{HANDLE, INVALID_HANDLE_VALUE}, + System::IO::{ + CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, + OVERLAPPED, OVERLAPPED_ENTRY, + }, +}; + +/// A handle to an Windows I/O Completion Port. +#[derive(Debug)] +pub(crate) struct CompletionPort { + handle: HANDLE, +} + +/// A status message received from an I/O completion port. +/// +/// These statuses can be created via the `new` or `empty` constructors and then +/// provided to a completion port, or they are read out of a completion port. +/// The fields of each status are read through its accessor methods. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct CompletionStatus(OVERLAPPED_ENTRY); + +impl fmt::Debug for CompletionStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CompletionStatus(OVERLAPPED_ENTRY)") + } +} + +unsafe impl Send for CompletionStatus {} +unsafe impl Sync for CompletionStatus {} + +impl CompletionPort { + /// Creates a new I/O completion port with the specified concurrency value. + /// + /// The number of threads given corresponds to the level of concurrency + /// allowed for threads associated with this port. Consult the Windows + /// documentation for more information about this value. + pub fn new(threads: u32) -> io::Result { + let ret = unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, threads) }; + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(CompletionPort { handle: ret }) + } + } + + /// Associates a new `HANDLE` to this I/O completion port. + /// + /// This function will associate the given handle to this port with the + /// given `token` to be returned in status messages whenever it receives a + /// notification. + /// + /// Any object which is convertible to a `HANDLE` via the `AsRawHandle` + /// trait can be provided to this function, such as `std::fs::File` and + /// friends. + pub fn add_handle(&self, token: usize, t: &T) -> io::Result<()> { + self._add(token, t.as_raw_handle() as HANDLE) + } + + // /// Associates a new `SOCKET` to this I/O completion port. + // /// + // /// This function will associate the given socket to this port with the + // /// given `token` to be returned in status messages whenever it receives a + // /// notification. + // /// + // /// Any object which is convertible to a `SOCKET` via the `AsRawSocket` + // /// trait can be provided to this function, such as `std::net::TcpStream` + // /// and friends. + // pub fn add_socket(&self, token: usize, t: &T) -> io::Result<()> { + // self._add(token, t.as_raw_socket() as HANDLE) + // } + + fn _add(&self, token: usize, handle: HANDLE) -> io::Result<()> { + assert_eq!(mem::size_of_val(&token), mem::size_of::()); + let ret = unsafe { CreateIoCompletionPort(handle, self.handle, token as usize, 0) }; + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + debug_assert_eq!(ret, self.handle); + Ok(()) + } + } + + /// Dequeues a number of completion statuses from this I/O completion port. + /// + /// This function is the same as `get` except that it may return more than + /// one status. A buffer of "zero" statuses is provided (the contents are + /// not read) and then on success this function will return a sub-slice of + /// statuses which represent those which were dequeued from this port. This + /// function does not wait to fill up the entire list of statuses provided. + /// + /// Like with `get`, a timeout may be specified for this operation. + pub fn get_many<'a>( + &self, + list: &'a mut [CompletionStatus], + timeout: Option, + ) -> io::Result<&'a mut [CompletionStatus]> { + debug_assert_eq!( + mem::size_of::(), + mem::size_of::() + ); + let mut removed = 0; + let timeout = duration_millis(timeout); + let len = cmp::min(list.len(), ::max_value() as usize) as u32; + let ret = unsafe { + GetQueuedCompletionStatusEx( + self.handle, + list.as_ptr() as *mut _, + len, + &mut removed, + timeout, + 0, + ) + }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(&mut list[..removed as usize]) + } + } + + /// Posts a new completion status onto this I/O completion port. + /// + /// This function will post the given status, with custom parameters, to the + /// port. Threads blocked in `get` or `get_many` will eventually receive + /// this status. + pub fn post(&self, status: CompletionStatus) -> io::Result<()> { + let ret = unsafe { + PostQueuedCompletionStatus( + self.handle, + status.0.dwNumberOfBytesTransferred, + status.0.lpCompletionKey, + status.0.lpOverlapped, + ) + }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } +} + +impl AsRawHandle for CompletionPort { + fn as_raw_handle(&self) -> RawHandle { + self.handle as RawHandle + } +} + +impl FromRawHandle for CompletionPort { + unsafe fn from_raw_handle(handle: RawHandle) -> CompletionPort { + CompletionPort { + handle: handle as HANDLE, + } + } +} + +impl IntoRawHandle for CompletionPort { + fn into_raw_handle(self) -> RawHandle { + self.handle as RawHandle + } +} + +impl CompletionStatus { + /// Creates a new completion status with the provided parameters. + /// + /// This function is useful when creating a status to send to a port with + /// the `post` method. The parameters are opaquely passed through and not + /// interpreted by the system at all. + pub(crate) fn new(bytes: u32, token: usize, overlapped: *mut Overlapped) -> Self { + CompletionStatus(OVERLAPPED_ENTRY { + dwNumberOfBytesTransferred: bytes, + lpCompletionKey: token, + lpOverlapped: overlapped as *mut _, + Internal: 0, + }) + } + + /// Creates a new borrowed completion status from the borrowed + /// `OVERLAPPED_ENTRY` argument provided. + /// + /// This method will wrap the `OVERLAPPED_ENTRY` in a `CompletionStatus`, + /// returning the wrapped structure. + pub fn from_entry(entry: &OVERLAPPED_ENTRY) -> &Self { + // Safety: CompletionStatus is repr(transparent) w/ OVERLAPPED_ENTRY, so + // a reference to one is guaranteed to be layout compatible with the + // reference to another. + unsafe { &*(entry as *const _ as *const _) } + } + + /// Creates a new "zero" completion status. + /// + /// This function is useful when creating a stack buffer or vector of + /// completion statuses to be passed to the `get_many` function. + pub fn zero() -> Self { + Self::new(0, 0, std::ptr::null_mut()) + } + + /// Returns the number of bytes that were transferred for the I/O operation + /// associated with this completion status. + pub fn bytes_transferred(&self) -> u32 { + self.0.dwNumberOfBytesTransferred + } + + /// Returns the completion key value associated with the file handle whose + /// I/O operation has completed. + /// + /// A completion key is a per-handle key that is specified when it is added + /// to an I/O completion port via `add_handle` or `add_socket`. + pub fn token(&self) -> usize { + self.0.lpCompletionKey as usize + } + + /// Returns a pointer to the `Overlapped` structure that was specified when + /// the I/O operation was started. + pub fn overlapped(&self) -> *mut OVERLAPPED { + self.0.lpOverlapped + } + + /// Returns a pointer to the internal `OVERLAPPED_ENTRY` object. + pub fn entry(&self) -> &OVERLAPPED_ENTRY { + &self.0 + } +} + +#[inline] +fn duration_millis(dur: Option) -> u32 { + if let Some(dur) = dur { + std::cmp::min(dur.as_millis(), u32::MAX as u128) as u32 + } else { + u32::MAX + } +} + +#[cfg(test)] +mod tests { + use super::{CompletionPort, CompletionStatus}; + + #[test] + fn is_send_sync() { + fn is_send_sync() {} + is_send_sync::(); + } + + #[test] + fn get_many() { + let c = CompletionPort::new(1).unwrap(); + + c.post(CompletionStatus::new(1, 2, 3 as *mut _)).unwrap(); + c.post(CompletionStatus::new(4, 5, 6 as *mut _)).unwrap(); + + let mut s = vec![CompletionStatus::zero(); 4]; + { + let s = c.get_many(&mut s, None).unwrap(); + assert_eq!(s.len(), 2); + assert_eq!(s[0].bytes_transferred(), 1); + assert_eq!(s[0].token(), 2); + assert_eq!(s[0].overlapped(), 3 as *mut _); + assert_eq!(s[1].bytes_transferred(), 4); + assert_eq!(s[1].token(), 5); + assert_eq!(s[1].overlapped(), 6 as *mut _); + } + assert_eq!(s[2].bytes_transferred(), 0); + assert_eq!(s[2].token(), 0); + assert_eq!(s[2].overlapped(), 0 as *mut _); + } +} diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs index 704835135..aeacabc2d 100644 --- a/src/sys/windows/mod.rs +++ b/src/sys/windows/mod.rs @@ -10,6 +10,8 @@ pub use selector::{Selector, SelectorInner, SockState}; mod overlapped; use overlapped::Overlapped; +mod iocp; + // Macros must be defined before the modules that use them cfg_net! { /// Helper macro to execute a system call that returns an `io::Result`. diff --git a/src/sys/windows/named_pipe.rs b/src/sys/windows/named_pipe.rs index f17dc5bfc..9cd501afb 100644 --- a/src/sys/windows/named_pipe.rs +++ b/src/sys/windows/named_pipe.rs @@ -1,20 +1,34 @@ use std::ffi::OsStr; use std::io::{self, Read, Write}; -use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle}; use std::sync::atomic::Ordering::{Relaxed, SeqCst}; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::{Arc, Mutex}; use std::{fmt, mem, slice}; -use miow::iocp::{CompletionPort, CompletionStatus}; -use miow::pipe; use windows_sys::Win32::{ - Foundation::{ERROR_BROKEN_PIPE, ERROR_PIPE_LISTENING}, - System::IO::{CancelIoEx, OVERLAPPED, OVERLAPPED_ENTRY}, + Foundation::{ + ERROR_BROKEN_PIPE, ERROR_IO_INCOMPLETE, ERROR_IO_PENDING, ERROR_NO_DATA, + ERROR_PIPE_CONNECTED, ERROR_PIPE_LISTENING, HANDLE, INVALID_HANDLE_VALUE, + }, + Storage::FileSystem::{ + ReadFile, WriteFile, FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, + PIPE_ACCESS_DUPLEX, + }, + System::{ + Pipes::{ + ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, + }, + IO::{CancelIoEx, GetOverlappedResult, OVERLAPPED, OVERLAPPED_ENTRY}, + }, }; use crate::event::Source; -use crate::sys::windows::{Event, Overlapped}; +use crate::sys::windows::{ + iocp::{CompletionPort, CompletionStatus}, + Event, Overlapped, +}; use crate::Registry; use crate::{Interest, Token}; @@ -76,7 +90,7 @@ struct Inner { read: Overlapped, write: Overlapped, // END NOTE. - handle: pipe::NamedPipe, + handle: HANDLE, connecting: AtomicBool, io: Mutex, pool: Mutex, @@ -104,6 +118,193 @@ impl Inner { // `read` is after `connect: Overlapped` and `read: Overlapped`. (ptr as *mut Overlapped).wrapping_sub(2) as *const Inner } + + /// Issue a connection request with the specified overlapped operation. + /// + /// This function will issue a request to connect a client to this server, + /// returning immediately after starting the overlapped operation. + /// + /// If this function immediately succeeds then `Ok(true)` is returned. If + /// the overlapped operation is enqueued and pending, then `Ok(false)` is + /// returned. Otherwise an error is returned indicating what went wrong. + /// + /// # Unsafety + /// + /// This function is unsafe because the kernel requires that the + /// `overlapped` pointer is valid until the end of the I/O operation. The + /// kernel also requires that `overlapped` is unique for this I/O operation + /// and is not in use for any other I/O. + /// + /// To safely use this function callers must ensure that this pointer is + /// valid until the I/O operation is completed, typically via completion + /// ports and waiting to receive the completion notification on the port. + pub unsafe fn connect_overlapped(&self, overlapped: *mut OVERLAPPED) -> io::Result { + if ConnectNamedPipe(self.handle, overlapped) != 0 { + return Ok(true); + } + + let err = io::Error::last_os_error(); + + match err.raw_os_error().map(|e| e as u32) { + Some(ERROR_PIPE_CONNECTED) => Ok(true), + Some(ERROR_NO_DATA) => Ok(true), + Some(ERROR_IO_PENDING) => Ok(false), + _ => Err(err), + } + } + + /// Disconnects this named pipe from any connected client. + pub fn disconnect(&self) -> io::Result<()> { + if unsafe { DisconnectNamedPipe(self.handle) } == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + + /// Issues an overlapped read operation to occur on this pipe. + /// + /// This function will issue an asynchronous read to occur in an overlapped + /// fashion, returning immediately. The `buf` provided will be filled in + /// with data and the request is tracked by the `overlapped` function + /// provided. + /// + /// If the operation succeeds immediately, `Ok(Some(n))` is returned where + /// `n` is the number of bytes read. If an asynchronous operation is + /// enqueued, then `Ok(None)` is returned. Otherwise if an error occurred + /// it is returned. + /// + /// When this operation completes (or if it completes immediately), another + /// mechanism must be used to learn how many bytes were transferred (such as + /// looking at the filed in the IOCP status message). + /// + /// # Unsafety + /// + /// This function is unsafe because the kernel requires that the `buf` and + /// `overlapped` pointers to be valid until the end of the I/O operation. + /// The kernel also requires that `overlapped` is unique for this I/O + /// operation and is not in use for any other I/O. + /// + /// To safely use this function callers must ensure that the pointers are + /// valid until the I/O operation is completed, typically via completion + /// ports and waiting to receive the completion notification on the port. + pub unsafe fn read_overlapped( + &self, + buf: &mut [u8], + overlapped: *mut OVERLAPPED, + ) -> io::Result> { + let len = std::cmp::min(buf.len(), u32::MAX as usize) as u32; + let res = ReadFile( + self.handle, + buf.as_mut_ptr() as *mut _, + len, + std::ptr::null_mut(), + overlapped, + ); + if res == 0 { + let err = io::Error::last_os_error(); + if err.raw_os_error() != Some(ERROR_IO_PENDING as i32) { + return Err(err); + } + } + + let mut bytes = 0; + let res = GetOverlappedResult(self.handle, overlapped, &mut bytes, 0); + if res == 0 { + let err = io::Error::last_os_error(); + if err.raw_os_error() == Some(ERROR_IO_INCOMPLETE as i32) { + Ok(None) + } else { + Err(err) + } + } else { + Ok(Some(bytes as usize)) + } + } + + /// Issues an overlapped write operation to occur on this pipe. + /// + /// This function will issue an asynchronous write to occur in an overlapped + /// fashion, returning immediately. The `buf` provided will be filled in + /// with data and the request is tracked by the `overlapped` function + /// provided. + /// + /// If the operation succeeds immediately, `Ok(Some(n))` is returned where + /// `n` is the number of bytes written. If an asynchronous operation is + /// enqueued, then `Ok(None)` is returned. Otherwise if an error occurred + /// it is returned. + /// + /// When this operation completes (or if it completes immediately), another + /// mechanism must be used to learn how many bytes were transferred (such as + /// looking at the filed in the IOCP status message). + /// + /// # Unsafety + /// + /// This function is unsafe because the kernel requires that the `buf` and + /// `overlapped` pointers to be valid until the end of the I/O operation. + /// The kernel also requires that `overlapped` is unique for this I/O + /// operation and is not in use for any other I/O. + /// + /// To safely use this function callers must ensure that the pointers are + /// valid until the I/O operation is completed, typically via completion + /// ports and waiting to receive the completion notification on the port. + pub unsafe fn write_overlapped( + &self, + buf: &[u8], + overlapped: *mut OVERLAPPED, + ) -> io::Result> { + let len = std::cmp::min(buf.len(), u32::MAX as usize) as u32; + let res = WriteFile( + self.handle, + buf.as_ptr() as *const _, + len, + std::ptr::null_mut(), + overlapped, + ); + if res == 0 { + let err = io::Error::last_os_error(); + if err.raw_os_error() != Some(ERROR_IO_PENDING as i32) { + return Err(err); + } + } + + let mut bytes = 0; + let res = GetOverlappedResult(self.handle, overlapped, &mut bytes, 0); + if res == 0 { + let err = io::Error::last_os_error(); + if err.raw_os_error() == Some(ERROR_IO_INCOMPLETE as i32) { + Ok(None) + } else { + Err(err) + } + } else { + Ok(Some(bytes as usize)) + } + } + + /// Calls the `GetOverlappedResult` function to get the result of an + /// overlapped operation for this handle. + /// + /// This function takes the `OVERLAPPED` argument which must have been used + /// to initiate an overlapped I/O operation, and returns either the + /// successful number of bytes transferred during the operation or an error + /// if one occurred. + /// + /// # Unsafety + /// + /// This function is unsafe as `overlapped` must have previously been used + /// to execute an operation for this handle, and it must also be a valid + /// pointer to an `Overlapped` instance. + #[inline] + unsafe fn result(&self, overlapped: *mut OVERLAPPED) -> io::Result { + let mut transferred = 0; + let r = GetOverlappedResult(self.handle, overlapped, &mut transferred, 0); + if r == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(transferred as usize) + } + } } #[test] @@ -159,10 +360,30 @@ impl NamedPipe { /// Creates a new named pipe at the specified `addr` given a "reasonable /// set" of initial configuration options. pub fn new>(addr: A) -> io::Result { - let pipe = pipe::NamedPipe::new(addr)?; - // Safety: nothing actually unsafe about this. The trait fn includes - // `unsafe`. - Ok(unsafe { NamedPipe::from_raw_handle(pipe.into_raw_handle()) }) + use std::os::windows::ffi::OsStrExt; + let name: Vec<_> = addr.as_ref().encode_wide().chain(Some(0)).collect(); + + // Safety: syscall + let h = unsafe { + CreateNamedPipeW( + name.as_ptr(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, + 65536, + 65536, + 0, + std::ptr::null_mut(), + ) + }; + + if h == INVALID_HANDLE_VALUE { + Err(io::Error::last_os_error()) + } else { + // Safety: nothing actually unsafe about this. The trait fn includes + // `unsafe`. + Ok(unsafe { Self::from_raw_handle(h as RawHandle) }) + } } /// Attempts to call `ConnectNamedPipe`, if possible. @@ -197,7 +418,7 @@ impl NamedPipe { // internal state accordingly. let res = unsafe { let overlapped = self.inner.connect.as_ptr() as *mut _; - self.inner.handle.connect_overlapped(overlapped) + self.inner.connect_overlapped(overlapped) }; match res { @@ -249,7 +470,7 @@ impl NamedPipe { /// After a `disconnect` is issued, then a `connect` may be called again to /// connect to another client. pub fn disconnect(&self) -> io::Result<()> { - self.inner.handle.disconnect() + self.inner.disconnect() } } @@ -257,10 +478,7 @@ impl FromRawHandle for NamedPipe { unsafe fn from_raw_handle(handle: RawHandle) -> NamedPipe { NamedPipe { inner: Arc::new(Inner { - // Safety: not really unsafe - handle: pipe::NamedPipe::from_raw_handle(handle), - // transmutes to straddle winapi versions (mio 0.6 is on an - // older winapi) + handle: handle as HANDLE, connect: Overlapped::new(connect_done), connecting: AtomicBool::new(false), read: Overlapped::new(read_done), @@ -402,10 +620,7 @@ impl Source for NamedPipe { io.cp = Some(selector.clone_port()); let inner_token = NEXT_TOKEN.fetch_add(2, Relaxed) + 2; - selector - .inner - .cp - .add_handle(inner_token, &self.inner.handle)?; + selector.inner.cp.add_handle(inner_token, self)?; } io.token = Some(token); @@ -448,7 +663,7 @@ impl Source for NamedPipe { impl AsRawHandle for NamedPipe { fn as_raw_handle(&self) -> RawHandle { - self.inner.handle.as_raw_handle() + self.inner.handle as RawHandle } } @@ -464,12 +679,12 @@ impl Drop for NamedPipe { // everything is flushed out. unsafe { if self.inner.connecting.load(SeqCst) { - drop(cancel(&self.inner.handle, &self.inner.connect)); + drop(cancel(self, &self.inner.connect)); } let io = self.inner.io.lock().unwrap(); if let State::Pending(..) = io.read { - drop(cancel(&self.inner.handle, &self.inner.read)); + drop(cancel(self, &self.inner.read)); } } } @@ -495,7 +710,7 @@ impl Inner { let e = unsafe { let overlapped = me.read.as_ptr() as *mut _; let slice = slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.capacity()); - me.handle.read_overlapped(slice, overlapped) + me.read_overlapped(slice, overlapped) }; match e { @@ -534,7 +749,7 @@ impl Inner { // Very similar to `schedule_read` above, just done for the write half. let e = unsafe { let overlapped = me.write.as_ptr() as *mut _; - me.handle.write_overlapped(&buf[pos..], overlapped) + me.write_overlapped(&buf[pos..], overlapped) }; // See `connect` above for the rationale behind `forget` @@ -602,7 +817,10 @@ impl Inner { } unsafe fn cancel(handle: &T, overlapped: &Overlapped) -> io::Result<()> { - let ret = CancelIoEx(handle.as_raw_handle(), overlapped.as_ptr() as *mut _); + let ret = CancelIoEx( + handle.as_raw_handle() as windows_sys::Win32::Foundation::HANDLE, + overlapped.as_ptr() as *mut _, + ); // `CancelIoEx` returns 0 on error: // https://docs.microsoft.com/en-us/windows/win32/fileio/cancelioex-func if ret == 0 { @@ -627,7 +845,7 @@ fn connect_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec>) { // Stash away our connect error if one happened debug_assert_eq!(status.bytes_transferred(), 0); unsafe { - match me.handle.result(status.overlapped()) { + match me.result(status.overlapped()) { Ok(n) => debug_assert_eq!(n, 0), Err(e) => me.io.lock().unwrap().connect_error = Some(e), } @@ -653,7 +871,7 @@ fn read_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec>) { _ => unreachable!(), }; unsafe { - match me.handle.result(status.overlapped()) { + match me.result(status.overlapped()) { Ok(n) => { debug_assert_eq!(status.bytes_transferred() as usize, n); buf.set_len(status.bytes_transferred() as usize); @@ -693,7 +911,7 @@ fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec>) { }; unsafe { - match me.handle.result(status.overlapped()) { + match me.result(status.overlapped()) { Ok(n) => { debug_assert_eq!(status.bytes_transferred() as usize, n); let new_pos = pos + (status.bytes_transferred() as usize); diff --git a/src/sys/windows/overlapped.rs b/src/sys/windows/overlapped.rs index 992c84a01..d1456ded4 100644 --- a/src/sys/windows/overlapped.rs +++ b/src/sys/windows/overlapped.rs @@ -3,13 +3,11 @@ use crate::sys::windows::Event; use std::cell::UnsafeCell; use std::fmt; -#[cfg(feature = "os-ext")] -use windows_sys::Win32::System::IO::OVERLAPPED; -use windows_sys::Win32::System::IO::OVERLAPPED_ENTRY; +use windows_sys::Win32::System::IO::{OVERLAPPED, OVERLAPPED_ENTRY}; #[repr(C)] pub(crate) struct Overlapped { - inner: UnsafeCell, + inner: UnsafeCell, pub(crate) callback: fn(&OVERLAPPED_ENTRY, Option<&mut Vec>), } @@ -17,13 +15,13 @@ pub(crate) struct Overlapped { impl Overlapped { pub(crate) fn new(cb: fn(&OVERLAPPED_ENTRY, Option<&mut Vec>)) -> Overlapped { Overlapped { - inner: UnsafeCell::new(miow::Overlapped::zero()), + inner: UnsafeCell::new(unsafe { std::mem::zeroed() }), callback: cb, } } pub(crate) fn as_ptr(&self) -> *const OVERLAPPED { - unsafe { (*self.inner.get()).raw() } + self.inner.get() } } diff --git a/src/sys/windows/selector.rs b/src/sys/windows/selector.rs index 63bcfbca0..91069849d 100644 --- a/src/sys/windows/selector.rs +++ b/src/sys/windows/selector.rs @@ -10,7 +10,7 @@ cfg_net! { use crate::Interest; } -use miow::iocp::{CompletionPort, CompletionStatus}; +use super::iocp::{CompletionPort, CompletionStatus}; use std::collections::VecDeque; use std::ffi::c_void; use std::io; diff --git a/src/sys/windows/waker.rs b/src/sys/windows/waker.rs index ab12c3c68..103aa01a7 100644 --- a/src/sys/windows/waker.rs +++ b/src/sys/windows/waker.rs @@ -2,7 +2,7 @@ use crate::sys::windows::Event; use crate::sys::windows::Selector; use crate::Token; -use miow::iocp::CompletionPort; +use super::iocp::CompletionPort; use std::io; use std::sync::Arc; From 21dbc06da997ae38b7bfd37b969cada7f411357e Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 08:29:30 +0100 Subject: [PATCH 04/12] Fix unused import --- src/sys/windows/afd.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs index bb02d9148..5fa22967c 100644 --- a/src/sys/windows/afd.rs +++ b/src/sys/windows/afd.rs @@ -4,7 +4,7 @@ use std::fs::File; use std::io; use std::mem::size_of; use std::os::windows::io::AsRawHandle; -use std::ptr::null_mut; + use windows_sys::Win32::{ Foundation::{ RtlNtStatusToDosError, HANDLE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS, @@ -125,6 +125,7 @@ impl Afd { cfg_io_source! { use std::mem::zeroed; use std::os::windows::io::{FromRawHandle, RawHandle}; + use std::ptr::null_mut; use std::sync::atomic::{AtomicUsize, Ordering}; use super::iocp::CompletionPort; From 6f2efb035b2d81aab6c1fe68a7a30d69eaa35049 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 09:23:12 +0100 Subject: [PATCH 05/12] Add Handle wrapper --- src/sys/windows/handle.rs | 30 +++++++++++++++++++++++++ src/sys/windows/iocp.rs | 42 ++++++++++++----------------------- src/sys/windows/mod.rs | 11 +++++---- src/sys/windows/named_pipe.rs | 33 +++++++++++++-------------- 4 files changed, 66 insertions(+), 50 deletions(-) create mode 100644 src/sys/windows/handle.rs diff --git a/src/sys/windows/handle.rs b/src/sys/windows/handle.rs new file mode 100644 index 000000000..5b9ac0b62 --- /dev/null +++ b/src/sys/windows/handle.rs @@ -0,0 +1,30 @@ +use std::os::windows::io::RawHandle; +use windows_sys::Win32::Foundation::{CloseHandle, HANDLE}; + +/// Wrapper around a Windows HANDLE so that we close it upon drop in all scenarios +#[derive(Debug)] +pub struct Handle(HANDLE); + +impl Handle { + #[inline] + pub fn new(handle: HANDLE) -> Self { + Self(handle) + } + + pub fn raw(&self) -> HANDLE { + self.0 + } + + pub fn into_raw(self) -> RawHandle { + let ret = self.0; + // This is super important so that drop is not called! + std::mem::forget(self); + ret as RawHandle + } +} + +impl Drop for Handle { + fn drop(&mut self) { + unsafe { CloseHandle(self.0) }; + } +} diff --git a/src/sys/windows/iocp.rs b/src/sys/windows/iocp.rs index 143368b9f..5c6c2f2bf 100644 --- a/src/sys/windows/iocp.rs +++ b/src/sys/windows/iocp.rs @@ -1,6 +1,6 @@ //! Bindings to IOCP, I/O Completion Ports -use crate::sys::windows::Overlapped; +use super::{Handle, Overlapped}; use std::cmp; use std::fmt; use std::io; @@ -19,7 +19,7 @@ use windows_sys::Win32::{ /// A handle to an Windows I/O Completion Port. #[derive(Debug)] pub(crate) struct CompletionPort { - handle: HANDLE, + handle: Handle, } /// A status message received from an I/O completion port. @@ -51,7 +51,9 @@ impl CompletionPort { if ret == 0 { Err(io::Error::last_os_error()) } else { - Ok(CompletionPort { handle: ret }) + Ok(CompletionPort { + handle: Handle::new(ret), + }) } } @@ -64,30 +66,14 @@ impl CompletionPort { /// Any object which is convertible to a `HANDLE` via the `AsRawHandle` /// trait can be provided to this function, such as `std::fs::File` and /// friends. + #[cfg(any(feature = "net", feature = "os-ext"))] pub fn add_handle(&self, token: usize, t: &T) -> io::Result<()> { - self._add(token, t.as_raw_handle() as HANDLE) - } - - // /// Associates a new `SOCKET` to this I/O completion port. - // /// - // /// This function will associate the given socket to this port with the - // /// given `token` to be returned in status messages whenever it receives a - // /// notification. - // /// - // /// Any object which is convertible to a `SOCKET` via the `AsRawSocket` - // /// trait can be provided to this function, such as `std::net::TcpStream` - // /// and friends. - // pub fn add_socket(&self, token: usize, t: &T) -> io::Result<()> { - // self._add(token, t.as_raw_socket() as HANDLE) - // } - - fn _add(&self, token: usize, handle: HANDLE) -> io::Result<()> { - assert_eq!(mem::size_of_val(&token), mem::size_of::()); - let ret = unsafe { CreateIoCompletionPort(handle, self.handle, token as usize, 0) }; + let ret = unsafe { + CreateIoCompletionPort(t.as_raw_handle() as HANDLE, self.handle.raw(), token, 0) + }; if ret == 0 { Err(io::Error::last_os_error()) } else { - debug_assert_eq!(ret, self.handle); Ok(()) } } @@ -115,7 +101,7 @@ impl CompletionPort { let len = cmp::min(list.len(), ::max_value() as usize) as u32; let ret = unsafe { GetQueuedCompletionStatusEx( - self.handle, + self.handle.raw(), list.as_ptr() as *mut _, len, &mut removed, @@ -139,7 +125,7 @@ impl CompletionPort { pub fn post(&self, status: CompletionStatus) -> io::Result<()> { let ret = unsafe { PostQueuedCompletionStatus( - self.handle, + self.handle.raw(), status.0.dwNumberOfBytesTransferred, status.0.lpCompletionKey, status.0.lpOverlapped, @@ -156,21 +142,21 @@ impl CompletionPort { impl AsRawHandle for CompletionPort { fn as_raw_handle(&self) -> RawHandle { - self.handle as RawHandle + self.handle.raw() as RawHandle } } impl FromRawHandle for CompletionPort { unsafe fn from_raw_handle(handle: RawHandle) -> CompletionPort { CompletionPort { - handle: handle as HANDLE, + handle: Handle::new(handle as HANDLE), } } } impl IntoRawHandle for CompletionPort { fn into_raw_handle(self) -> RawHandle { - self.handle as RawHandle + self.handle.into_raw() } } diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs index aeacabc2d..f8b72fc49 100644 --- a/src/sys/windows/mod.rs +++ b/src/sys/windows/mod.rs @@ -1,16 +1,19 @@ mod afd; -mod io_status_block; pub mod event; pub use event::{Event, Events}; -mod selector; -pub use selector::{Selector, SelectorInner, SockState}; +mod handle; +use handle::Handle; + +mod io_status_block; +mod iocp; mod overlapped; use overlapped::Overlapped; -mod iocp; +mod selector; +pub use selector::{Selector, SelectorInner, SockState}; // Macros must be defined before the modules that use them cfg_net! { diff --git a/src/sys/windows/named_pipe.rs b/src/sys/windows/named_pipe.rs index 9cd501afb..29413d2dd 100644 --- a/src/sys/windows/named_pipe.rs +++ b/src/sys/windows/named_pipe.rs @@ -27,7 +27,7 @@ use windows_sys::Win32::{ use crate::event::Source; use crate::sys::windows::{ iocp::{CompletionPort, CompletionStatus}, - Event, Overlapped, + Event, Handle, Overlapped, }; use crate::Registry; use crate::{Interest, Token}; @@ -90,7 +90,7 @@ struct Inner { read: Overlapped, write: Overlapped, // END NOTE. - handle: HANDLE, + handle: Handle, connecting: AtomicBool, io: Mutex, pool: Mutex, @@ -139,7 +139,7 @@ impl Inner { /// valid until the I/O operation is completed, typically via completion /// ports and waiting to receive the completion notification on the port. pub unsafe fn connect_overlapped(&self, overlapped: *mut OVERLAPPED) -> io::Result { - if ConnectNamedPipe(self.handle, overlapped) != 0 { + if ConnectNamedPipe(self.handle.raw(), overlapped) != 0 { return Ok(true); } @@ -155,7 +155,7 @@ impl Inner { /// Disconnects this named pipe from any connected client. pub fn disconnect(&self) -> io::Result<()> { - if unsafe { DisconnectNamedPipe(self.handle) } == 0 { + if unsafe { DisconnectNamedPipe(self.handle.raw()) } == 0 { Err(io::Error::last_os_error()) } else { Ok(()) @@ -195,7 +195,7 @@ impl Inner { ) -> io::Result> { let len = std::cmp::min(buf.len(), u32::MAX as usize) as u32; let res = ReadFile( - self.handle, + self.handle.raw(), buf.as_mut_ptr() as *mut _, len, std::ptr::null_mut(), @@ -209,7 +209,7 @@ impl Inner { } let mut bytes = 0; - let res = GetOverlappedResult(self.handle, overlapped, &mut bytes, 0); + let res = GetOverlappedResult(self.handle.raw(), overlapped, &mut bytes, 0); if res == 0 { let err = io::Error::last_os_error(); if err.raw_os_error() == Some(ERROR_IO_INCOMPLETE as i32) { @@ -255,7 +255,7 @@ impl Inner { ) -> io::Result> { let len = std::cmp::min(buf.len(), u32::MAX as usize) as u32; let res = WriteFile( - self.handle, + self.handle.raw(), buf.as_ptr() as *const _, len, std::ptr::null_mut(), @@ -269,7 +269,7 @@ impl Inner { } let mut bytes = 0; - let res = GetOverlappedResult(self.handle, overlapped, &mut bytes, 0); + let res = GetOverlappedResult(self.handle.raw(), overlapped, &mut bytes, 0); if res == 0 { let err = io::Error::last_os_error(); if err.raw_os_error() == Some(ERROR_IO_INCOMPLETE as i32) { @@ -298,7 +298,7 @@ impl Inner { #[inline] unsafe fn result(&self, overlapped: *mut OVERLAPPED) -> io::Result { let mut transferred = 0; - let r = GetOverlappedResult(self.handle, overlapped, &mut transferred, 0); + let r = GetOverlappedResult(self.handle.raw(), overlapped, &mut transferred, 0); if r == 0 { Err(io::Error::last_os_error()) } else { @@ -478,7 +478,7 @@ impl FromRawHandle for NamedPipe { unsafe fn from_raw_handle(handle: RawHandle) -> NamedPipe { NamedPipe { inner: Arc::new(Inner { - handle: handle as HANDLE, + handle: Handle::new(handle as HANDLE), connect: Overlapped::new(connect_done), connecting: AtomicBool::new(false), read: Overlapped::new(read_done), @@ -663,7 +663,7 @@ impl Source for NamedPipe { impl AsRawHandle for NamedPipe { fn as_raw_handle(&self) -> RawHandle { - self.inner.handle as RawHandle + self.inner.handle.raw() as RawHandle } } @@ -679,12 +679,12 @@ impl Drop for NamedPipe { // everything is flushed out. unsafe { if self.inner.connecting.load(SeqCst) { - drop(cancel(self, &self.inner.connect)); + drop(cancel(&self.inner.handle, &self.inner.connect)); } let io = self.inner.io.lock().unwrap(); if let State::Pending(..) = io.read { - drop(cancel(self, &self.inner.read)); + drop(cancel(&self.inner.handle, &self.inner.read)); } } } @@ -816,11 +816,8 @@ impl Inner { } } -unsafe fn cancel(handle: &T, overlapped: &Overlapped) -> io::Result<()> { - let ret = CancelIoEx( - handle.as_raw_handle() as windows_sys::Win32::Foundation::HANDLE, - overlapped.as_ptr() as *mut _, - ); +unsafe fn cancel(handle: &Handle, overlapped: &Overlapped) -> io::Result<()> { + let ret = CancelIoEx(handle.raw(), overlapped.as_ptr()); // `CancelIoEx` returns 0 on error: // https://docs.microsoft.com/en-us/windows/win32/fileio/cancelioex-func if ret == 0 { From fa81f31c9428128fe887a887a51de9b738786834 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 09:30:43 +0100 Subject: [PATCH 06/12] Gate from_entry --- src/sys/windows/iocp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sys/windows/iocp.rs b/src/sys/windows/iocp.rs index 5c6c2f2bf..cb0a28001 100644 --- a/src/sys/windows/iocp.rs +++ b/src/sys/windows/iocp.rs @@ -180,6 +180,7 @@ impl CompletionStatus { /// /// This method will wrap the `OVERLAPPED_ENTRY` in a `CompletionStatus`, /// returning the wrapped structure. + #[cfg(feature = "os-ext")] pub fn from_entry(entry: &OVERLAPPED_ENTRY) -> &Self { // Safety: CompletionStatus is repr(transparent) w/ OVERLAPPED_ENTRY, so // a reference to one is guaranteed to be layout compatible with the From c7d7c678eda07b665586dc4e6edd09870101ee20 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 17:10:22 +0100 Subject: [PATCH 07/12] Loosen version restriction --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a1a473396..319673a3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ log = "0.4.8" libc = "0.2.86" [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.34" +version = ">=0.32, <=0.34" features = [ "Win32_Storage_FileSystem", # Enables NtCreateFile "Win32_Foundation", # Basic types eg HANDLE From b5f7ae615d05942d2aa6ecf6c4c978c9a7fd0cf7 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 17:24:32 +0100 Subject: [PATCH 08/12] Clarify undocumented NT function --- src/sys/windows/afd.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs index 5fa22967c..642521e24 100644 --- a/src/sys/windows/afd.rs +++ b/src/sys/windows/afd.rs @@ -14,9 +14,13 @@ use windows_sys::Win32::{ const IOCTL_AFD_POLL: u32 = 0x00012024; -// #[link(name = "ntdll")] extern "system" { + /// See + /// + /// This is an undocumented API and as such not part of + /// from which `windows-sys` is generated, and also unlikely to be added, so + /// we manually declare it here fn NtCancelIoFileEx( FileHandle: HANDLE, IoRequestToCancel: *mut IO_STATUS_BLOCK, From e96edc56cb55928661d50e684992c2f6c2409d61 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 17:24:42 +0100 Subject: [PATCH 09/12] Remove nested imports --- src/sys/windows/afd.rs | 10 +++++----- src/sys/windows/event.rs | 3 +-- src/sys/windows/iocp.rs | 10 ++++------ src/sys/windows/named_pipe.rs | 35 +++++++++++++++-------------------- src/sys/windows/selector.rs | 6 +++--- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs index 642521e24..0308e2fd5 100644 --- a/src/sys/windows/afd.rs +++ b/src/sys/windows/afd.rs @@ -5,11 +5,11 @@ use std::io; use std::mem::size_of; use std::os::windows::io::AsRawHandle; -use windows_sys::Win32::{ - Foundation::{ - RtlNtStatusToDosError, HANDLE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS, - }, - System::WindowsProgramming::{NtDeviceIoControlFile, IO_STATUS_BLOCK, IO_STATUS_BLOCK_0}, +use windows_sys::Win32::Foundation::{ + RtlNtStatusToDosError, HANDLE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS, +}; +use windows_sys::Win32::System::WindowsProgramming::{ + NtDeviceIoControlFile, IO_STATUS_BLOCK, IO_STATUS_BLOCK_0, }; const IOCTL_AFD_POLL: u32 = 0x00012024; diff --git a/src/sys/windows/event.rs b/src/sys/windows/event.rs index c3d1f6cf9..731bd6067 100644 --- a/src/sys/windows/event.rs +++ b/src/sys/windows/event.rs @@ -1,8 +1,7 @@ use std::fmt; -use super::iocp::CompletionStatus; - use super::afd; +use super::iocp::CompletionStatus; use crate::Token; #[derive(Clone)] diff --git a/src/sys/windows/iocp.rs b/src/sys/windows/iocp.rs index cb0a28001..396a6a1fd 100644 --- a/src/sys/windows/iocp.rs +++ b/src/sys/windows/iocp.rs @@ -8,12 +8,10 @@ use std::mem; use std::os::windows::io::*; use std::time::Duration; -use windows_sys::Win32::{ - Foundation::{HANDLE, INVALID_HANDLE_VALUE}, - System::IO::{ - CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, - OVERLAPPED, OVERLAPPED_ENTRY, - }, +use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE}; +use windows_sys::Win32System::IO::{ + CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, OVERLAPPED, + OVERLAPPED_ENTRY, }; /// A handle to an Windows I/O Completion Port. diff --git a/src/sys/windows/named_pipe.rs b/src/sys/windows/named_pipe.rs index 29413d2dd..23f85d1eb 100644 --- a/src/sys/windows/named_pipe.rs +++ b/src/sys/windows/named_pipe.rs @@ -6,29 +6,24 @@ use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::{Arc, Mutex}; use std::{fmt, mem, slice}; -use windows_sys::Win32::{ - Foundation::{ - ERROR_BROKEN_PIPE, ERROR_IO_INCOMPLETE, ERROR_IO_PENDING, ERROR_NO_DATA, - ERROR_PIPE_CONNECTED, ERROR_PIPE_LISTENING, HANDLE, INVALID_HANDLE_VALUE, - }, - Storage::FileSystem::{ - ReadFile, WriteFile, FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, - PIPE_ACCESS_DUPLEX, - }, - System::{ - Pipes::{ - ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, PIPE_TYPE_BYTE, - PIPE_UNLIMITED_INSTANCES, - }, - IO::{CancelIoEx, GetOverlappedResult, OVERLAPPED, OVERLAPPED_ENTRY}, - }, +use windows_sys::Win32::Foundation::{ + ERROR_BROKEN_PIPE, ERROR_IO_INCOMPLETE, ERROR_IO_PENDING, ERROR_NO_DATA, ERROR_PIPE_CONNECTED, + ERROR_PIPE_LISTENING, HANDLE, INVALID_HANDLE_VALUE, +}; +use windows_sys::Win32::Storage::FileSystem::{ + ReadFile, WriteFile, FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX, +}; +use windows_sys::Win32::System::Pipes::{ + ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, +}; +use windows_sys::Win32::System::IO::{ + CancelIoEx, GetOverlappedResult, OVERLAPPED, OVERLAPPED_ENTRY, }; use crate::event::Source; -use crate::sys::windows::{ - iocp::{CompletionPort, CompletionStatus}, - Event, Handle, Overlapped, -}; +use crate::sys::windows::iocp::{CompletionPort, CompletionStatus}; +use crate::sys::windows::{Event, Handle, Overlapped}; use crate::Registry; use crate::{Interest, Token}; diff --git a/src/sys/windows/selector.rs b/src/sys/windows/selector.rs index 91069849d..022cd9095 100644 --- a/src/sys/windows/selector.rs +++ b/src/sys/windows/selector.rs @@ -23,10 +23,10 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; -use windows_sys::Win32::{ - Foundation::{ERROR_INVALID_HANDLE, ERROR_IO_PENDING, HANDLE, STATUS_CANCELLED, WAIT_TIMEOUT}, - System::IO::OVERLAPPED, +use windows_sys::Win32::Foundation::{ + ERROR_INVALID_HANDLE, ERROR_IO_PENDING, HANDLE, STATUS_CANCELLED, WAIT_TIMEOUT, }; +use windows_sys::Win32::System::IO::OVERLAPPED; #[derive(Debug)] struct AfdGroup { From d039deb69d76ce0d738426f59986cb09cce06e40 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 17:51:21 +0100 Subject: [PATCH 10/12] Use Win32_NetworkManagement_IpHelper feature --- Cargo.toml | 11 ++++++----- src/sys/windows/net.rs | 17 ++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 319673a3e..f4eb182b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,11 +51,12 @@ libc = "0.2.86" [target.'cfg(windows)'.dependencies.windows-sys] version = ">=0.32, <=0.34" features = [ - "Win32_Storage_FileSystem", # Enables NtCreateFile - "Win32_Foundation", # Basic types eg HANDLE - "Win32_Networking_WinSock", # winsock2 types/functions - "Win32_System_IO", # IO types like OVERLAPPED etc - "Win32_System_WindowsProgramming", # General future used for various types/funcs + "Win32_Storage_FileSystem", # Enables NtCreateFile + "Win32_Foundation", # Basic types eg HANDLE + "Win32_Networking_WinSock", # winsock2 types/functions + "Win32_NetworkManagement_IpHelper", # AF_INET and AF_INET6 constants + "Win32_System_IO", # IO types like OVERLAPPED etc + "Win32_System_WindowsProgramming", # General future used for various types/funcs ] [target.'cfg(target_os = "wasi")'.dependencies] diff --git a/src/sys/windows/net.rs b/src/sys/windows/net.rs index 2df4b6894..f727042e8 100644 --- a/src/sys/windows/net.rs +++ b/src/sys/windows/net.rs @@ -7,12 +7,11 @@ use windows_sys::Win32::Networking::WinSock::{ ioctlsocket, socket, FIONBIO, IN6_ADDR, IN6_ADDR_0, INVALID_SOCKET, IN_ADDR, IN_ADDR_0, SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6, SOCKADDR_IN6_0, SOCKET, }; - -// AF_INET/6 are in the Win32_NetworkManagement_IpHelper feature for some reason, -// so just directly define them here rather than import that whole feature set -// these constants won't ever change -pub(crate) const AF_INET: u16 = 2; -pub(crate) const AF_INET6: u16 = 23; +// We need to import a giant feature set for these 2 constants, they will hopefully +// be moved to `Win32::Networking::WinSock` in the future, since this needlessly +// increases compile times for all downstream users +// +pub(crate) use windows_sys::Win32::NetworkManagement::IpHelper::{AF_INET, AF_INET6}; /// Initialise the network stack for Windows. pub(crate) fn init() { @@ -35,7 +34,7 @@ pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: u16) -> io::Result io::Result { +pub(crate) fn new_socket(domain: u32, socket_type: u16) -> io::Result { syscall!( socket(domain as i32, socket_type as i32, 0), PartialEq::eq, @@ -74,7 +73,7 @@ pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, i32) { }; let sockaddr_in = SOCKADDR_IN { - sin_family: AF_INET, + sin_family: AF_INET as u16, // 1 sin_port: addr.port().to_be(), sin_addr, sin_zero: [0; 8], @@ -96,7 +95,7 @@ pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, i32) { }; let sockaddr_in6 = SOCKADDR_IN6 { - sin6_family: AF_INET6, + sin6_family: AF_INET6 as u16, // 23 sin6_port: addr.port().to_be(), sin6_addr, sin6_flowinfo: addr.flowinfo(), From a38e1a5f6d72fc153cadb4845ee092058527073f Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 17:51:28 +0100 Subject: [PATCH 11/12] Oops --- src/sys/windows/iocp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/windows/iocp.rs b/src/sys/windows/iocp.rs index 396a6a1fd..d75f3826e 100644 --- a/src/sys/windows/iocp.rs +++ b/src/sys/windows/iocp.rs @@ -9,7 +9,7 @@ use std::os::windows::io::*; use std::time::Duration; use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE}; -use windows_sys::Win32System::IO::{ +use windows_sys::Win32::System::IO::{ CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, OVERLAPPED, OVERLAPPED_ENTRY, }; From d9f499d822f9e07b7e43877169f81245b3e3c1b0 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 21 Mar 2022 17:51:43 +0100 Subject: [PATCH 12/12] Note issue for adding SIO_* constants --- src/sys/windows/selector.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sys/windows/selector.rs b/src/sys/windows/selector.rs index 022cd9095..0ac259157 100644 --- a/src/sys/windows/selector.rs +++ b/src/sys/windows/selector.rs @@ -538,6 +538,9 @@ cfg_io_source! { use windows_sys::Win32::Networking::WinSock::{WSAGetLastError, SOCKET_ERROR, WSAIoctl, IOC_OUT, IOC_WS2}; + // These constants were part of winapi but are not yet in windows-sys + // + // https://github.com/microsoft/win32metadata/issues/844 const SIO_BSP_HANDLE: u32 = IOC_OUT | IOC_WS2 | 27; // 1_207_959_579u32 const SIO_BSP_HANDLE_SELECT: u32 = IOC_OUT | IOC_WS2 | 28; // 1_207_959_580u32 const SIO_BSP_HANDLE_POLL: u32 = IOC_OUT | IOC_WS2 | 29; // 1_207_959_581u32