|
1 |
| -use crate::ffi::OsStr; |
2 | 1 | use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
|
| 2 | +use crate::ops::Neg; |
3 | 3 | use crate::os::windows::prelude::*;
|
4 |
| -use crate::path::Path; |
5 |
| -use crate::random::{DefaultRandomSource, Random}; |
6 |
| -use crate::sync::atomic::Ordering::Relaxed; |
7 |
| -use crate::sync::atomic::{Atomic, AtomicUsize}; |
| 4 | +use crate::sys::api::utf16; |
8 | 5 | use crate::sys::c;
|
9 |
| -use crate::sys::fs::{File, OpenOptions}; |
10 | 6 | use crate::sys::handle::Handle;
|
11 |
| -use crate::sys::pal::windows::api::{self, WinError}; |
12 | 7 | use crate::sys_common::{FromInner, IntoInner};
|
13 | 8 | use crate::{mem, ptr};
|
14 | 9 |
|
@@ -62,92 +57,113 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
|
62 | 57 |
|
63 | 58 | // Note that we specifically do *not* use `CreatePipe` here because
|
64 | 59 | // unfortunately the anonymous pipes returned do not support overlapped
|
65 |
| - // operations. Instead, we create a "hopefully unique" name and create a |
66 |
| - // named pipe which has overlapped operations enabled. |
| 60 | + // operations. Instead, we use `NtCreateNamedPipeFile` to create the |
| 61 | + // anonymous pipe with overlapped support. |
67 | 62 | //
|
68 |
| - // Once we do this, we connect do it as usual via `CreateFileW`, and then |
| 63 | + // Once we do this, we connect to it via `NtOpenFile`, and then |
69 | 64 | // we return those reader/writer halves. Note that the `ours` pipe return
|
70 | 65 | // value is always the named pipe, whereas `theirs` is just the normal file.
|
71 | 66 | // This should hopefully shield us from child processes which assume their
|
72 | 67 | // stdout is a named pipe, which would indeed be odd!
|
73 | 68 | unsafe {
|
74 |
| - let ours; |
75 |
| - let mut name; |
76 |
| - let mut tries = 0; |
77 |
| - loop { |
78 |
| - tries += 1; |
79 |
| - name = format!( |
80 |
| - r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}", |
81 |
| - c::GetCurrentProcessId(), |
82 |
| - random_number(), |
| 69 | + let mut io_status = c::IO_STATUS_BLOCK::default(); |
| 70 | + let mut object_attributes = c::OBJECT_ATTRIBUTES::default(); |
| 71 | + object_attributes.Length = size_of::<c::OBJECT_ATTRIBUTES>() as u32; |
| 72 | + |
| 73 | + // Open a handle to the pipe filesystem (`\??\PIPE\`). |
| 74 | + // This will be used when creating a new annon pipe. |
| 75 | + let pipe_fs = { |
| 76 | + let path = c::UNICODE_STRING::from_ref(utf16!(r"\??\PIPE\")); |
| 77 | + object_attributes.ObjectName = &path; |
| 78 | + let mut pipe_fs = ptr::null_mut(); |
| 79 | + let status = c::NtOpenFile( |
| 80 | + &mut pipe_fs, |
| 81 | + c::SYNCHRONIZE | c::GENERIC_READ, |
| 82 | + &object_attributes, |
| 83 | + &mut io_status, |
| 84 | + c::FILE_SHARE_READ | c::FILE_SHARE_WRITE, |
| 85 | + c::FILE_SYNCHRONOUS_IO_NONALERT, // synchronous access |
83 | 86 | );
|
84 |
| - let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::<Vec<_>>(); |
85 |
| - let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED; |
86 |
| - if ours_readable { |
87 |
| - flags |= c::PIPE_ACCESS_INBOUND; |
| 87 | + if c::nt_success(status) { |
| 88 | + Handle::from_raw_handle(pipe_fs) |
88 | 89 | } else {
|
89 |
| - flags |= c::PIPE_ACCESS_OUTBOUND; |
| 90 | + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); |
90 | 91 | }
|
| 92 | + }; |
91 | 93 |
|
92 |
| - let handle = c::CreateNamedPipeW( |
93 |
| - wide_name.as_ptr(), |
94 |
| - flags, |
95 |
| - c::PIPE_TYPE_BYTE |
96 |
| - | c::PIPE_READMODE_BYTE |
97 |
| - | c::PIPE_WAIT |
98 |
| - | c::PIPE_REJECT_REMOTE_CLIENTS, |
| 94 | + // From now on we're using handles instead of paths to create and open pipes. |
| 95 | + // So set the `ObjectName` to a zero length string. |
| 96 | + let empty = c::UNICODE_STRING::default(); |
| 97 | + object_attributes.ObjectName = ∅ |
| 98 | + |
| 99 | + // Create our side of the pipe for async access. |
| 100 | + let ours = { |
| 101 | + // Use the pipe filesystem as the root directory. |
| 102 | + // With no name provided, an anonymous pipe will be created. |
| 103 | + object_attributes.RootDirectory = pipe_fs.as_raw_handle(); |
| 104 | + |
| 105 | + // A negative timeout value is a relative time (rather than an absolute time). |
| 106 | + // The time is given in 100's of nanoseconds so this is 50 milliseconds. |
| 107 | + // This value was chosen to be consistent with the default timeout set by `CreateNamedPipeW` |
| 108 | + // See: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew |
| 109 | + let timeout = (50_i64 * 10000).neg() as u64; |
| 110 | + |
| 111 | + let mut ours = ptr::null_mut(); |
| 112 | + let status = c::NtCreateNamedPipeFile( |
| 113 | + &mut ours, |
| 114 | + c::SYNCHRONIZE | if ours_readable { c::GENERIC_READ } else { c::GENERIC_WRITE }, |
| 115 | + &object_attributes, |
| 116 | + &mut io_status, |
| 117 | + if ours_readable { c::FILE_SHARE_WRITE } else { c::FILE_SHARE_READ }, |
| 118 | + c::FILE_CREATE, |
| 119 | + 0, |
| 120 | + c::FILE_PIPE_BYTE_STREAM_TYPE, |
| 121 | + c::FILE_PIPE_BYTE_STREAM_MODE, |
| 122 | + c::FILE_PIPE_QUEUE_OPERATION, |
| 123 | + // only allow one client pipe |
99 | 124 | 1,
|
100 | 125 | PIPE_BUFFER_CAPACITY,
|
101 | 126 | PIPE_BUFFER_CAPACITY,
|
102 |
| - 0, |
103 |
| - ptr::null_mut(), |
| 127 | + &timeout, |
104 | 128 | );
|
105 |
| - |
106 |
| - // We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're |
107 |
| - // also just doing a best effort at selecting a unique name. If |
108 |
| - // `ERROR_ACCESS_DENIED` is returned then it could mean that we |
109 |
| - // accidentally conflicted with an already existing pipe, so we try |
110 |
| - // again. |
111 |
| - // |
112 |
| - // Don't try again too much though as this could also perhaps be a |
113 |
| - // legit error. |
114 |
| - if handle == c::INVALID_HANDLE_VALUE { |
115 |
| - let error = api::get_last_error(); |
116 |
| - if tries < 10 && error == WinError::ACCESS_DENIED { |
117 |
| - continue; |
118 |
| - } else { |
119 |
| - return Err(io::Error::from_raw_os_error(error.code as i32)); |
120 |
| - } |
| 129 | + if c::nt_success(status) { |
| 130 | + Handle::from_raw_handle(ours) |
| 131 | + } else { |
| 132 | + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); |
121 | 133 | }
|
| 134 | + }; |
122 | 135 |
|
123 |
| - ours = Handle::from_raw_handle(handle); |
124 |
| - break; |
125 |
| - } |
| 136 | + // Open their side of the pipe for synchronous access. |
| 137 | + let theirs = { |
| 138 | + // We can reopen the anonymous pipe without a name by setting |
| 139 | + // RootDirectory to the pipe handle and not setting a path name, |
| 140 | + object_attributes.RootDirectory = ours.as_raw_handle(); |
126 | 141 |
|
127 |
| - // Connect to the named pipe we just created. This handle is going to be |
128 |
| - // returned in `theirs`, so if `ours` is readable we want this to be |
129 |
| - // writable, otherwise if `ours` is writable we want this to be |
130 |
| - // readable. |
131 |
| - // |
132 |
| - // Additionally we don't enable overlapped mode on this because most |
133 |
| - // client processes aren't enabled to work with that. |
134 |
| - let mut opts = OpenOptions::new(); |
135 |
| - opts.write(ours_readable); |
136 |
| - opts.read(!ours_readable); |
137 |
| - opts.share_mode(0); |
138 |
| - let size = size_of::<c::SECURITY_ATTRIBUTES>(); |
139 |
| - let mut sa = c::SECURITY_ATTRIBUTES { |
140 |
| - nLength: size as u32, |
141 |
| - lpSecurityDescriptor: ptr::null_mut(), |
142 |
| - bInheritHandle: their_handle_inheritable as i32, |
| 142 | + if their_handle_inheritable { |
| 143 | + object_attributes.Attributes |= c::OBJ_INHERIT; |
| 144 | + } |
| 145 | + let mut theirs = ptr::null_mut(); |
| 146 | + let status = c::NtOpenFile( |
| 147 | + &mut theirs, |
| 148 | + c::SYNCHRONIZE |
| 149 | + | if ours_readable { |
| 150 | + c::GENERIC_WRITE | c::FILE_READ_ATTRIBUTES |
| 151 | + } else { |
| 152 | + c::GENERIC_READ |
| 153 | + }, |
| 154 | + &object_attributes, |
| 155 | + &mut io_status, |
| 156 | + 0, |
| 157 | + c::FILE_NON_DIRECTORY_FILE | c::FILE_SYNCHRONOUS_IO_NONALERT, |
| 158 | + ); |
| 159 | + if c::nt_success(status) { |
| 160 | + Handle::from_raw_handle(theirs) |
| 161 | + } else { |
| 162 | + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); |
| 163 | + } |
143 | 164 | };
|
144 |
| - opts.security_attributes(&mut sa); |
145 |
| - let theirs = File::open(Path::new(&name), &opts)?; |
146 | 165 |
|
147 |
| - Ok(Pipes { |
148 |
| - ours: AnonPipe { inner: ours }, |
149 |
| - theirs: AnonPipe { inner: theirs.into_inner() }, |
150 |
| - }) |
| 166 | + Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } }) |
151 | 167 | }
|
152 | 168 | }
|
153 | 169 |
|
@@ -191,17 +207,6 @@ pub fn spawn_pipe_relay(
|
191 | 207 | Ok(theirs)
|
192 | 208 | }
|
193 | 209 |
|
194 |
| -fn random_number() -> usize { |
195 |
| - static N: Atomic<usize> = AtomicUsize::new(0); |
196 |
| - loop { |
197 |
| - if N.load(Relaxed) != 0 { |
198 |
| - return N.fetch_add(1, Relaxed); |
199 |
| - } |
200 |
| - |
201 |
| - N.store(usize::random(&mut DefaultRandomSource), Relaxed); |
202 |
| - } |
203 |
| -} |
204 |
| - |
205 | 210 | impl AnonPipe {
|
206 | 211 | pub fn handle(&self) -> &Handle {
|
207 | 212 | &self.inner
|
|
0 commit comments