Skip to content

Commit

Permalink
Merge branch 'windows-rs' of https://github.com/ncihnegn/interprocess
Browse files Browse the repository at this point in the history
…into windows_rs
  • Loading branch information
kotauskas committed Jan 25, 2024
2 parents 5850069 + 2559b2d commit 7d47640
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 150 deletions.
32 changes: 15 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ to_method = "1.1"
cfg-if = "1.0.0"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = [
"std",
"winbase",
"winerror",
"processthreadsapi",
"fileapi",
"handleapi",
"namedpipeapi",
windows = "0.51.1" # TODO I don't need this dep
windows-sys = { version = "0.48.0", features = [
"Win32_Foundation",
"Win32_Security",
"Win32_Storage_FileSystem",
"Win32_System_IO",
"Win32_System_Pipes",
"Win32_System_Threading",
] }
recvmsg = "1.0.0"

Expand Down
20 changes: 9 additions & 11 deletions src/os/windows/c_wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,31 @@ use std::{
io,
mem::{size_of, zeroed},
};
use winapi::um::{
handleapi::DuplicateHandle, minwinbase::SECURITY_ATTRIBUTES, processthreadsapi::GetCurrentProcess,
winnt::DUPLICATE_SAME_ACCESS,
use windows_sys::Win32::{
Foundation::{DuplicateHandle, DUPLICATE_SAME_ACCESS},
Security::SECURITY_ATTRIBUTES,
System::Threading::GetCurrentProcess,
};

pub fn duplicate_handle(handle: BorrowedHandle<'_>) -> io::Result<OwnedHandle> {
let raw = duplicate_handle_inner(handle, None)?;
unsafe { Ok(OwnedHandle::from_raw_handle(raw)) }
unsafe { Ok(OwnedHandle::from_raw_handle(raw as RawHandle)) }
}
pub fn duplicate_handle_to_foreign(
handle: BorrowedHandle<'_>,
other_process: BorrowedHandle<'_>,
) -> io::Result<RawHandle> {
) -> io::Result<HANDLE> {
duplicate_handle_inner(handle, Some(other_process))
}

fn duplicate_handle_inner(
handle: BorrowedHandle<'_>,
other_process: Option<BorrowedHandle<'_>>,
) -> io::Result<RawHandle> {
fn duplicate_handle_inner(handle: BorrowedHandle<'_>, other_process: Option<BorrowedHandle<'_>>) -> io::Result<HANDLE> {
let mut new_handle = INVALID_HANDLE_VALUE;
let success = unsafe {
let proc = GetCurrentProcess();
DuplicateHandle(
proc,
handle.as_raw_handle(),
other_process.map(|h| h.as_raw_handle()).unwrap_or(proc),
handle.as_int_handle(),
other_process.map(|h| h.as_int_handle()).unwrap_or(proc),
&mut new_handle,
0,
0,
Expand Down
22 changes: 12 additions & 10 deletions src/os/windows/file_handle.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use super::{c_wrappers, downgrade_eof, winprelude::*};
use crate::TryClone;
use std::{io, mem::MaybeUninit, ptr};
use winapi::um::fileapi::{FlushFileBuffers, ReadFile, WriteFile};
use windows_sys::Win32::Storage::FileSystem::{FlushFileBuffers, ReadFile, WriteFile};

/// Newtype wrapper which defines file I/O operations on a `HANDLE` to a file.
#[repr(transparent)]
pub(crate) struct FileHandle(pub(crate) OwnedHandle);
pub(crate) struct FileHandle(OwnedHandle);
impl FileHandle {
pub fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
let len = DWORD::try_from(buf.len()).unwrap_or(DWORD::MAX);
let len = u32::try_from(buf.len()).unwrap_or(u32::MAX);

let (success, num_bytes_read) = unsafe {
let mut num_bytes_read: DWORD = 0;
let mut num_bytes_read: u32 = 0;
let result = ReadFile(
self.0.as_raw_handle(),
self.as_int_handle(),
buf.as_mut_ptr().cast(),
len,
&mut num_bytes_read as *mut _,
Expand All @@ -24,12 +24,12 @@ impl FileHandle {
downgrade_eof(ok_or_ret_errno!(success => num_bytes_read))
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let len = DWORD::try_from(buf.len()).unwrap_or(DWORD::MAX);
let len = u32::try_from(buf.len()).unwrap_or(u32::MAX);

let (success, bytes_written) = unsafe {
let mut bytes_written: DWORD = 0;
let mut bytes_written: u32 = 0;
let result = WriteFile(
self.0.as_raw_handle(),
self.as_int_handle(),
buf.as_ptr().cast(),
len,
&mut bytes_written as *mut _,
Expand All @@ -41,7 +41,7 @@ impl FileHandle {
}
#[inline(always)]
pub fn flush(&self) -> io::Result<()> {
Self::flush_hndl(self.0.as_raw_handle())
Self::flush_hndl(self.as_int_handle())
}
#[inline]
pub fn flush_hndl(handle: HANDLE) -> io::Result<()> {
Expand All @@ -51,11 +51,13 @@ impl FileHandle {
}
impl TryClone for FileHandle {
fn try_clone(&self) -> io::Result<Self> {
c_wrappers::duplicate_handle(self.0.as_handle()).map(Self)
c_wrappers::duplicate_handle(self.as_handle()).map(Self)
}
}

multimacro! {
FileHandle,
forward_handle,
forward_debug,
derive_raw,
}
28 changes: 15 additions & 13 deletions src/os/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@ use std::{
task::Poll,
};
mod winprelude {
pub use std::os::windows::prelude::*;
pub use winapi::{
shared::{
minwindef::{BOOL, DWORD, LPVOID},
ntdef::HANDLE,
},
um::handleapi::INVALID_HANDLE_VALUE,
};
pub(crate) use super::AsRawHandleExt;
pub(crate) use std::os::windows::prelude::*;
pub(crate) use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
}
use winprelude::*;

Expand All @@ -32,7 +27,7 @@ mod c_wrappers;
///
/// On Windows, like with most other operating systems, handles belong to specific processes. You shouldn't just send
/// the value of a handle to another process (with a named pipe, for example) and expect it to work on the other side.
/// For this to work, you need [`DuplicateHandle`](winapi::um::handleapi::DuplicateHandle) – the Win32 API function
/// For this to work, you need [`DuplicateHandle`](windows_sys::Win32::Foundation::DuplicateHandle) – the Win32 API function
/// which duplicates a handle into the handle table of the specified process (the receiver is referred to by its
/// handle). This trait exposes the `DuplicateHandle` functionality in a safe manner.
///
Expand All @@ -47,7 +42,7 @@ pub trait ShareHandle: AsHandle {
/// the only way to use any form of IPC other than named pipes to communicate between two processes which do not
/// have a parent-child relationship or if the handle wasn't created as inheritable.
///
/// Backed by [`DuplicateHandle`](winapi::um::handleapi::DuplicateHandle). Doesn't require unsafe code since
/// Backed by [`DuplicateHandle`](windows_sys::Win32::Foundation::DuplicateHandle). Doesn't require unsafe code since
/// `DuplicateHandle` never leads to undefined behavior if the `lpTargetHandle` parameter is a valid pointer, only
/// creates an error.
#[allow(clippy::not_unsafe_ptr_arg_deref)] // Handles are not pointers, they have handle checks
Expand All @@ -59,10 +54,9 @@ impl ShareHandle for crate::unnamed_pipe::UnnamedPipeReader {}
impl ShareHandle for crate::unnamed_pipe::UnnamedPipeWriter {}

fn decode_eof<T>(r: io::Result<T>) -> io::Result<T> {
use windows_sys::Win32::Foundation::ERROR_PIPE_NOT_CONNECTED;
match r {
Err(e) if e.raw_os_error() == Some(winapi::shared::winerror::ERROR_PIPE_NOT_CONNECTED as _) => {
Err(io::Error::from(BrokenPipe))
}
Err(e) if e.raw_os_error() == Some(ERROR_PIPE_NOT_CONNECTED as _) => Err(io::Error::from(BrokenPipe)),
els => els,
}
}
Expand All @@ -76,3 +70,11 @@ fn downgrade_eof<T: Default>(r: io::Result<T>) -> io::Result<T> {
fn downgrade_poll_eof<T: Default>(r: Poll<io::Result<T>>) -> Poll<io::Result<T>> {
r.map(downgrade_eof)
}

pub(crate) trait AsRawHandleExt: AsRawHandle {
#[inline(always)]
fn as_int_handle(&self) -> HANDLE {
self.as_raw_handle() as HANDLE
}
}
impl<T: AsRawHandle + ?Sized> AsRawHandleExt for T {}
29 changes: 14 additions & 15 deletions src/os/windows/named_pipe/enums.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use super::PipeModeTag;
use crate::os::windows::winprelude::*;
use std::{convert::TryFrom, mem};
use winapi::um::winbase::{
PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, PIPE_ACCESS_OUTBOUND, PIPE_READMODE_BYTE, PIPE_READMODE_MESSAGE,
PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE,
use windows_sys::Win32::{
Storage::FileSystem::{PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, PIPE_ACCESS_OUTBOUND},
System::Pipes::{PIPE_READMODE_BYTE, PIPE_READMODE_MESSAGE, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE},
};

/// The direction of a named pipe connection, designating who can read data and who can write it. This describes the
/// direction of the data flow unambiguously, so that the meaning of the values is the same for the client and server –
/// [`ClientToServer`](PipeDirection::ClientToServer) always means client → server, for example.
#[repr(u32)]
// We depend on the fact that DWORD always maps to u32, which, thankfully, will always stay true
// We depend on the fact that u32 always maps to u32, which, thankfully, will always stay true
// since the public WinAPI is supposed to be ABI-compatible. Just keep in mind that the
// #[repr(u32)] means that we can transmute this enumeration to the Windows DWORD type.
// #[repr(u32)] means that we can transmute this enumeration to the Windows u32 type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum PipeDirection {
/// Represents server ← client data flow: clients write data, the server reads it.
Expand Down Expand Up @@ -75,19 +74,19 @@ impl PipeDirection {
}
}
}
impl TryFrom<DWORD> for PipeDirection {
impl TryFrom<u32> for PipeDirection {
type Error = ();
/// Converts a Windows constant to a `PipeDirection` if it's in range.
///
/// # Errors
/// Returns `Err` if the value is not a valid pipe direction constant.
fn try_from(op: DWORD) -> Result<Self, ()> {
fn try_from(op: u32) -> Result<Self, ()> {
assert!((1..=3).contains(&op));
// See the comment block above for why this is safe.
unsafe { mem::transmute(op) }
}
}
impl From<PipeDirection> for DWORD {
impl From<PipeDirection> for u32 {
fn from(op: PipeDirection) -> Self {
unsafe { mem::transmute(op) }
}
Expand Down Expand Up @@ -180,28 +179,28 @@ pub enum PipeMode {
Messages = PIPE_TYPE_MESSAGE,
}
impl PipeMode {
/// Converts the value into a raw `DWORD`-typed constant, either `PIPE_TYPE_BYTE` or `PIPE_TYPE_MESSAGE` depending
/// Converts the value into a raw `u32`-typed constant, either `PIPE_TYPE_BYTE` or `PIPE_TYPE_MESSAGE` depending
/// on the value.
pub const fn to_pipe_type(self) -> DWORD {
pub const fn to_pipe_type(self) -> u32 {
self as _
}
/// Converts the value into a raw `DWORD`-typed constant, either `PIPE_READMODE_BYTE` or `PIPE_READMODE_MESSAGE`
/// Converts the value into a raw `u32`-typed constant, either `PIPE_READMODE_BYTE` or `PIPE_READMODE_MESSAGE`
/// depending on the value.
pub const fn to_readmode(self) -> DWORD {
pub const fn to_readmode(self) -> u32 {
match self {
Self::Bytes => PIPE_READMODE_BYTE,
Self::Messages => PIPE_READMODE_MESSAGE,
}
}
}
impl TryFrom<DWORD> for PipeMode {
impl TryFrom<u32> for PipeMode {
type Error = ();
/// Converts a Windows constant to a `PipeMode` if it's in range. Both `PIPE_TYPE_*` and `PIPE_READMODE_*` are
/// supported.
///
/// # Errors
/// Returns `Err` if the value is not a valid pipe stream mode constant.
fn try_from(op: DWORD) -> Result<Self, ()> {
fn try_from(op: u32) -> Result<Self, ()> {
// It's nicer to only match than to check and transmute
#[allow(unreachable_patterns)] // PIPE_READMODE_BYTE and PIPE_TYPE_BYTE are equal
match op {
Expand Down
Loading

0 comments on commit 7d47640

Please sign in to comment.