1 /*
2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Class that manages an unidirectional anonymous byte stream pipe
5 * COPYRIGHT: Copyright 2015 Thomas Faber (thomas.faber@reactos.org)
6 * Copyright 2019 Colin Finck (colin@reactos.org)
7 */
8
9 #include "precomp.h"
10
11 LONG CPipe::m_lPipeCount = 0;
12
13
14 /**
15 * Constructs a CPipe object and initializes read and write handles.
16 */
CPipe()17 CPipe::CPipe()
18 {
19 SECURITY_ATTRIBUTES SecurityAttributes;
20
21 SecurityAttributes.nLength = sizeof(SecurityAttributes);
22 SecurityAttributes.bInheritHandle = TRUE;
23 SecurityAttributes.lpSecurityDescriptor = NULL;
24
25 // Construct a unique pipe name.
26 WCHAR wszPipeName[MAX_PATH];
27 InterlockedIncrement(&m_lPipeCount);
28 swprintf(wszPipeName, L"\\\\.\\pipe\\TestmanPipe%ld", m_lPipeCount);
29
30 // Create a named pipe with the default settings, but overlapped (asynchronous) operations.
31 // Latter feature is why we can't simply use CreatePipe.
32 const DWORD dwDefaultBufferSize = 4096;
33 const DWORD dwDefaultTimeoutMilliseconds = 120000;
34
35 m_hReadPipe = CreateNamedPipeW(wszPipeName,
36 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
37 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
38 1,
39 dwDefaultBufferSize,
40 dwDefaultBufferSize,
41 dwDefaultTimeoutMilliseconds,
42 &SecurityAttributes);
43 if (m_hReadPipe == INVALID_HANDLE_VALUE)
44 {
45 FATAL("CreateNamedPipe failed\n");
46 }
47
48 // Use CreateFileW to get the write handle to the pipe.
49 // Writing is done synchronously, so no FILE_FLAG_OVERLAPPED here!
50 m_hWritePipe = CreateFileW(wszPipeName,
51 GENERIC_WRITE,
52 0,
53 &SecurityAttributes,
54 OPEN_EXISTING,
55 FILE_ATTRIBUTE_NORMAL,
56 NULL);
57 if (m_hWritePipe == INVALID_HANDLE_VALUE)
58 {
59 FATAL("CreateFileW failed\n");
60 }
61
62 // Prepare the OVERLAPPED structure for reading.
63 ZeroMemory(&m_ReadOverlapped, sizeof(m_ReadOverlapped));
64 m_ReadOverlapped.hEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
65 if (!m_ReadOverlapped.hEvent)
66 {
67 FATAL("CreateEvent failed\n");
68 }
69 }
70
71 /**
72 * Destructs a CPipe object and closes all open handles.
73 */
~CPipe()74 CPipe::~CPipe()
75 {
76 if (m_hReadPipe)
77 CloseHandle(m_hReadPipe);
78 if (m_hWritePipe)
79 CloseHandle(m_hWritePipe);
80 }
81
82 /**
83 * Closes a CPipe's read pipe, leaving the write pipe unchanged.
84 */
85 void
CloseReadPipe()86 CPipe::CloseReadPipe()
87 {
88 if (!m_hReadPipe)
89 FATAL("Trying to close already closed read pipe\n");
90 CloseHandle(m_hReadPipe);
91 m_hReadPipe = NULL;
92 }
93
94 /**
95 * Closes a CPipe's write pipe, leaving the read pipe unchanged.
96 */
97 void
CloseWritePipe()98 CPipe::CloseWritePipe()
99 {
100 if (!m_hWritePipe)
101 FATAL("Trying to close already closed write pipe\n");
102 CloseHandle(m_hWritePipe);
103 m_hWritePipe = NULL;
104 }
105
106 /**
107 * Reads data from a pipe without advancing the read offset and/or retrieves information about available data.
108 *
109 * This function must not be called after CloseReadPipe.
110 *
111 * @param Buffer
112 * An optional buffer to read pipe data into.
113 *
114 * @param BufferSize
115 * The size of the buffer specified in Buffer, or 0 if no read should be performed.
116 *
117 * @param BytesRead
118 * On return, the number of bytes actually read from the pipe into Buffer.
119 *
120 * @param TotalBytesAvailable
121 * On return, the total number of bytes available to read from the pipe.
122 *
123 * @return
124 * True on success, false on failure; call GetLastError for error information.
125 *
126 * @see PeekNamedPipe
127 */
128 bool
Peek(PVOID Buffer,DWORD BufferSize,PDWORD BytesRead,PDWORD TotalBytesAvailable)129 CPipe::Peek(PVOID Buffer, DWORD BufferSize, PDWORD BytesRead, PDWORD TotalBytesAvailable)
130 {
131 if (!m_hReadPipe)
132 FATAL("Trying to peek from a closed read pipe\n");
133
134 return PeekNamedPipe(m_hReadPipe, Buffer, BufferSize, BytesRead, TotalBytesAvailable, NULL);
135 }
136
137 /**
138 * Reads data from the read pipe, advancing the read offset accordingly.
139 *
140 * This function must not be called after CloseReadPipe.
141 *
142 * @param Buffer
143 * Buffer to read pipe data into.
144 *
145 * @param NumberOfBytesToRead
146 * The number of bytes to read into Buffer.
147 *
148 * @param NumberOfBytesRead
149 * On return, the number of bytes actually read from the pipe into Buffer.
150 *
151 * @return
152 * Returns a Win32 error code. Expected error codes include:
153 * - ERROR_SUCCESS: The read has completed successfully.
154 * - WAIT_TIMEOUT: The given timeout has elapsed before any data was read.
155 * - ERROR_BROKEN_PIPE: The other end of the pipe has been closed.
156 *
157 * @see ReadFile
158 */
159 DWORD
Read(PVOID Buffer,DWORD NumberOfBytesToRead,PDWORD NumberOfBytesRead,DWORD TimeoutMilliseconds)160 CPipe::Read(PVOID Buffer, DWORD NumberOfBytesToRead, PDWORD NumberOfBytesRead, DWORD TimeoutMilliseconds)
161 {
162 if (!m_hReadPipe)
163 {
164 FATAL("Trying to read from a closed read pipe\n");
165 }
166
167 if (ReadFile(m_hReadPipe, Buffer, NumberOfBytesToRead, NumberOfBytesRead, &m_ReadOverlapped))
168 {
169 // The asynchronous read request could be satisfied immediately.
170 return ERROR_SUCCESS;
171 }
172
173 DWORD dwLastError = GetLastError();
174 if (dwLastError == ERROR_IO_PENDING)
175 {
176 // The asynchronous read request could not be satisfied immediately, so wait for it with the given timeout.
177 DWORD dwWaitResult = WaitForSingleObject(m_ReadOverlapped.hEvent, TimeoutMilliseconds);
178 if (dwWaitResult == WAIT_OBJECT_0)
179 {
180 // Fill NumberOfBytesRead.
181 if (GetOverlappedResult(m_hReadPipe, &m_ReadOverlapped, NumberOfBytesRead, FALSE))
182 {
183 // We successfully read NumberOfBytesRead bytes.
184 return ERROR_SUCCESS;
185 }
186
187 dwLastError = GetLastError();
188 if (dwLastError == ERROR_BROKEN_PIPE)
189 {
190 // The other end of the pipe has been closed.
191 return ERROR_BROKEN_PIPE;
192 }
193 else
194 {
195 // An unexpected error.
196 FATAL("GetOverlappedResult failed\n");
197 }
198 }
199 else
200 {
201 // This may be WAIT_TIMEOUT or an unexpected error.
202 return dwWaitResult;
203 }
204 }
205 else
206 {
207 // This may be ERROR_BROKEN_PIPE or an unexpected error.
208 return dwLastError;
209 }
210 }
211
212 /**
213 * Writes data to the write pipe.
214 *
215 * This function must not be called after CloseWritePipe.
216 *
217 * @param Buffer
218 * Buffer containing the data to write.
219 *
220 * @param NumberOfBytesToWrite
221 * The number of bytes to write to the pipe from Buffer.
222 *
223 * @param NumberOfBytesWritten
224 * On return, the number of bytes actually written to the pipe.
225 *
226 * @return
227 * True on success, false on failure; call GetLastError for error information.
228 *
229 * @see WriteFile
230 */
231 bool
Write(LPCVOID Buffer,DWORD NumberOfBytesToWrite,PDWORD NumberOfBytesWritten)232 CPipe::Write(LPCVOID Buffer, DWORD NumberOfBytesToWrite, PDWORD NumberOfBytesWritten)
233 {
234 if (!m_hWritePipe)
235 FATAL("Trying to write to a closed write pipe\n");
236
237 return WriteFile(m_hWritePipe, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten, NULL);
238 }
239