|
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 |
|
@@ -65,89 +60,108 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res |
65 | 60 | // operations. Instead, we create a "hopefully unique" name and create a |
66 | 61 | // named pipe which has overlapped operations enabled. |
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 | + let timeout = (50_i64 * 10000).neg() as u64; |
| 108 | + |
| 109 | + let mut ours = ptr::null_mut(); |
| 110 | + let status = c::NtCreateNamedPipeFile( |
| 111 | + &mut ours, |
| 112 | + c::SYNCHRONIZE | if ours_readable { c::GENERIC_READ } else { c::GENERIC_WRITE }, |
| 113 | + &object_attributes, |
| 114 | + &mut io_status, |
| 115 | + if ours_readable { c::FILE_SHARE_WRITE } else { c::FILE_SHARE_READ }, |
| 116 | + c::FILE_CREATE, |
| 117 | + 0, |
| 118 | + c::FILE_PIPE_BYTE_STREAM_TYPE, |
| 119 | + c::FILE_PIPE_BYTE_STREAM_MODE, |
| 120 | + c::FILE_PIPE_QUEUE_OPERATION, |
| 121 | + // only allow one client pipe |
99 | 122 | 1, |
100 | 123 | PIPE_BUFFER_CAPACITY, |
101 | 124 | PIPE_BUFFER_CAPACITY, |
102 | | - 0, |
103 | | - ptr::null_mut(), |
| 125 | + &timeout, |
104 | 126 | ); |
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 | | - } |
| 127 | + if c::nt_success(status) { |
| 128 | + Handle::from_raw_handle(ours) |
| 129 | + } else { |
| 130 | + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); |
121 | 131 | } |
| 132 | + }; |
122 | 133 |
|
123 | | - ours = Handle::from_raw_handle(handle); |
124 | | - break; |
125 | | - } |
| 134 | + // Open their side of the pipe for synchronous access. |
| 135 | + let theirs = { |
| 136 | + // We can reopen the anonymous pipe without a name by setting |
| 137 | + // RootDirectory to the pipe handle and not setting a path name, |
| 138 | + object_attributes.RootDirectory = ours.as_raw_handle(); |
126 | 139 |
|
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, |
| 140 | + if their_handle_inheritable { |
| 141 | + object_attributes.Attributes |= c::OBJ_INHERIT; |
| 142 | + } |
| 143 | + let mut theirs = ptr::null_mut(); |
| 144 | + let status = c::NtOpenFile( |
| 145 | + &mut theirs, |
| 146 | + c::SYNCHRONIZE |
| 147 | + | if ours_readable { |
| 148 | + c::GENERIC_WRITE | c::FILE_READ_ATTRIBUTES |
| 149 | + } else { |
| 150 | + c::GENERIC_READ |
| 151 | + }, |
| 152 | + &object_attributes, |
| 153 | + &mut io_status, |
| 154 | + 0, |
| 155 | + c::FILE_NON_DIRECTORY_FILE | c::FILE_SYNCHRONOUS_IO_NONALERT, |
| 156 | + ); |
| 157 | + if c::nt_success(status) { |
| 158 | + Handle::from_raw_handle(theirs) |
| 159 | + } else { |
| 160 | + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); |
| 161 | + } |
143 | 162 | }; |
144 | | - opts.security_attributes(&mut sa); |
145 | | - let theirs = File::open(Path::new(&name), &opts)?; |
146 | 163 |
|
147 | | - Ok(Pipes { |
148 | | - ours: AnonPipe { inner: ours }, |
149 | | - theirs: AnonPipe { inner: theirs.into_inner() }, |
150 | | - }) |
| 164 | + Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } }) |
151 | 165 | } |
152 | 166 | } |
153 | 167 |
|
@@ -191,17 +205,6 @@ pub fn spawn_pipe_relay( |
191 | 205 | Ok(theirs) |
192 | 206 | } |
193 | 207 |
|
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 | 208 | impl AnonPipe { |
206 | 209 | pub fn handle(&self) -> &Handle { |
207 | 210 | &self.inner |
|
0 commit comments