interprocess_docfix/os/windows/
unnamed_pipe.rs

1//! Platform-specific functionality for unnamed pipes.
2//!
3//! Currently, this consists of only the [`UnnamedPipeCreationOptions`] builder, but more might be added.
4//!
5//! [`UnnamedPipeCreationOptions`]: struct.UnnamedPipeCreationOptions.html " "
6
7// TODO add examples
8
9use super::imports::*;
10use super::FileHandleOps;
11use crate::unnamed_pipe::{UnnamedPipeReader as PubReader, UnnamedPipeWriter as PubWriter};
12use std::{
13    fmt::{self, Debug, Formatter},
14    io::{self, Read, Write},
15    mem::{size_of, zeroed, ManuallyDrop},
16    num::NonZeroUsize,
17    ptr,
18};
19
20/// Builder used to create unnamed pipes while supplying additional options.
21///
22/// You can use this instead of the simple [`pipe` function] to supply additional Windows-specific parameters to a pipe.
23///
24/// [`pipe` function]: ../../../unnamed_pipe/fn.pipe.html " "
25#[non_exhaustive]
26#[derive(Copy, Clone, Debug)]
27pub struct UnnamedPipeCreationOptions {
28    /// Specifies whether the resulting pipe can be inherited by child processes.
29    ///
30    /// The default value is `true` and you probably shouldn't modify this, unless you want all child processes to explicitly be unable to use the pipe even if they attempt to use various fishy methods to find the handle in the parent process.
31    pub inheritable: bool,
32    /// A pointer to the [security descriptor] for the pipe. Leave this at the default `NULL` unless you want something specific.
33    ///
34    /// [security descriptor]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor " "
35    pub security_descriptor: LPVOID,
36    /// A hint on the buffer size for the pipe. There is no way to ensure or check that the system actually uses this exact size, since it's only a hint. Set to `None` to disable the hint and rely entirely on the system's default buffer size.
37    pub buffer_size_hint: Option<NonZeroUsize>,
38}
39impl UnnamedPipeCreationOptions {
40    /// Starts with the default parameters for the pipe. Identical to `Default::default()`.
41    pub const fn new() -> Self {
42        Self {
43            inheritable: true,
44            security_descriptor: ptr::null_mut(),
45            buffer_size_hint: None,
46        }
47    }
48    /// Specifies whether the resulting pipe can be inherited by child processes.
49    ///
50    /// See the [associated field] for more.
51    ///
52    /// [associated field]: #structfield.inheritable " "
53    #[must_use = "this is not an in-place operation"]
54    pub fn inheritable(mut self, inheritable: bool) -> Self {
55        self.inheritable = inheritable;
56        self
57    }
58    /// Specifies the pointer to the security descriptor for the pipe.
59    ///
60    /// See the [associated field] for more.
61    ///
62    /// [associated field]: #structfield.security_descriptor " "
63    #[must_use = "this is not an in-place operation"]
64    pub fn security_descriptor(mut self, security_descriptor: LPVOID) -> Self {
65        self.security_descriptor = security_descriptor;
66        self
67    }
68    /// Specifies the hint on the buffer size for the pipe.
69    ///
70    /// See the [associated field] for more.
71    ///
72    /// [associated field]: #structfield.buffer_size_hint " "
73    #[must_use = "this is not an in-place operation"]
74    pub fn buffer_size_hint(mut self, buffer_size_hint: Option<NonZeroUsize>) -> Self {
75        self.buffer_size_hint = buffer_size_hint;
76        self
77    }
78
79    /// Extracts the [`SECURITY_ATTRIBUTES`] from the builder. Primarily an implementation detail, but has other uses.
80    ///
81    /// [`SECURITY_ATTRIBUTES`]: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)
82    pub fn extract_security_attributes(self) -> SECURITY_ATTRIBUTES {
83        // Safe because WinAPI parameter structs are typically rejected if a required field is zero
84        let mut security_attributes = unsafe { zeroed::<SECURITY_ATTRIBUTES>() };
85        security_attributes.nLength = size_of::<SECURITY_ATTRIBUTES>() as u32;
86        security_attributes.lpSecurityDescriptor = self.security_descriptor;
87        security_attributes.bInheritHandle = self.inheritable as i32;
88        security_attributes
89    }
90
91    /// Creates the pipe and returns its writing and reading ends, or the error if one occurred.
92    ///
93    /// # Safety
94    /// The [`security_descriptor`] field is passed directly to Win32 which is then dereferenced there, resulting in undefined behavior if it was an invalid non-null pointer. For the default configuration, this should never be a concern.
95    ///
96    /// [`security_descriptor`]: #field.security_descriptor " "
97    // TODO have safe and unsafe versions, since most folks don't need security_attributes
98    pub unsafe fn build(self) -> io::Result<(PubWriter, PubReader)> {
99        let hint_raw = match self.buffer_size_hint {
100            Some(num) => num.get(),
101            None => 0,
102        } as u32;
103        let [mut writer, mut reader] = [INVALID_HANDLE_VALUE; 2];
104        let success = unsafe {
105            CreatePipe(
106                &mut reader as *mut _,
107                &mut writer as *mut _,
108                &mut self.extract_security_attributes() as *mut _,
109                hint_raw,
110            )
111        } != 0;
112        if success {
113            let (writer, reader) = unsafe {
114                // SAFETY: we just created those handles which means that we own them
115                let writer = PubWriter {
116                    inner: UnnamedPipeWriter::from_raw_handle(writer),
117                };
118                let reader = PubReader {
119                    inner: UnnamedPipeReader::from_raw_handle(reader),
120                };
121                (writer, reader)
122            };
123            Ok((writer, reader))
124        } else {
125            Err(io::Error::last_os_error())
126        }
127    }
128}
129impl Default for UnnamedPipeCreationOptions {
130    fn default() -> Self {
131        Self::new()
132    }
133}
134unsafe impl Send for UnnamedPipeCreationOptions {}
135unsafe impl Sync for UnnamedPipeCreationOptions {}
136
137pub(crate) fn pipe() -> io::Result<(PubWriter, PubReader)> {
138    unsafe { UnnamedPipeCreationOptions::default().build() }
139}
140
141pub(crate) struct UnnamedPipeReader(FileHandleOps);
142impl Read for UnnamedPipeReader {
143    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
144        self.0.read(buf)
145    }
146}
147#[cfg(windows)]
148impl AsRawHandle for UnnamedPipeReader {
149    fn as_raw_handle(&self) -> HANDLE {
150        self.0.as_raw_handle()
151    }
152}
153#[cfg(windows)]
154impl IntoRawHandle for UnnamedPipeReader {
155    fn into_raw_handle(self) -> HANDLE {
156        let self_ = ManuallyDrop::new(self);
157        self_.as_raw_handle()
158    }
159}
160#[cfg(windows)]
161impl FromRawHandle for UnnamedPipeReader {
162    unsafe fn from_raw_handle(handle: HANDLE) -> Self {
163        let fho = unsafe {
164            // SAFETY: validity guaranteed by safety contract
165            FileHandleOps::from_raw_handle(handle)
166        };
167        Self(fho)
168    }
169}
170impl Debug for UnnamedPipeReader {
171    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
172        f.debug_struct("UnnamedPipeReader")
173            .field("handle", &self.as_raw_handle())
174            .finish()
175    }
176}
177
178pub(crate) struct UnnamedPipeWriter(FileHandleOps);
179impl Write for UnnamedPipeWriter {
180    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
181        self.0.write(buf)
182    }
183    fn flush(&mut self) -> io::Result<()> {
184        self.0.flush()
185    }
186}
187#[cfg(windows)]
188impl AsRawHandle for UnnamedPipeWriter {
189    fn as_raw_handle(&self) -> HANDLE {
190        self.0.as_raw_handle()
191    }
192}
193#[cfg(windows)]
194impl IntoRawHandle for UnnamedPipeWriter {
195    fn into_raw_handle(self) -> HANDLE {
196        let self_ = ManuallyDrop::new(self);
197        self_.as_raw_handle()
198    }
199}
200#[cfg(windows)]
201impl FromRawHandle for UnnamedPipeWriter {
202    unsafe fn from_raw_handle(handle: HANDLE) -> Self {
203        Self(FileHandleOps(handle))
204    }
205}
206impl Debug for UnnamedPipeWriter {
207    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
208        f.debug_struct("UnnamedPipeWriter")
209            .field("handle", &self.as_raw_handle())
210            .finish()
211    }
212}