aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/pipe.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2024-01-10 23:48:40 -0800
committerRob Mensching <rob@firegiant.com>2024-01-11 18:24:06 -0800
commit57b7689b2dc5e971911d1c6d48eebb5424ebca89 (patch)
tree5a40c4ec7cec3548a119c6ef1a2a274c2258f627 /src/burn/engine/pipe.cpp
parentf676ceeaedbba4125d3e0c691959afcba182d008 (diff)
downloadwix-57b7689b2dc5e971911d1c6d48eebb5424ebca89.tar.gz
wix-57b7689b2dc5e971911d1c6d48eebb5424ebca89.tar.bz2
wix-57b7689b2dc5e971911d1c6d48eebb5424ebca89.zip
Rename inter-Burn communication related pipe file
When BootstrapperApplications move out of proc there will be another set of pipes in Burn that behave differently from the inter-Burn communication pipes that exist now. So renaming the existing pipe related files now to better reflect that it is related to Burn talking to Burn when elevated or embedded.
Diffstat (limited to 'src/burn/engine/pipe.cpp')
-rw-r--r--src/burn/engine/pipe.cpp782
1 files changed, 0 insertions, 782 deletions
diff --git a/src/burn/engine/pipe.cpp b/src/burn/engine/pipe.cpp
deleted file mode 100644
index 5003622c..00000000
--- a/src/burn/engine/pipe.cpp
+++ /dev/null
@@ -1,782 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static const DWORD PIPE_64KB = 64 * 1024;
6static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second,
7static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes.
8
9static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls";
10static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache";
11static const LPCWSTR LOGGING_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Log";
12
13static HRESULT AllocatePipeMessage(
14 __in DWORD dwMessage,
15 __in_bcount_opt(cbData) LPVOID pvData,
16 __in SIZE_T cbData,
17 __out_bcount(cb) LPVOID* ppvMessage,
18 __out SIZE_T* pcbMessage
19 );
20static void FreePipeMessage(
21 __in BURN_PIPE_MESSAGE *pMsg
22 );
23static HRESULT WritePipeMessage(
24 __in HANDLE hPipe,
25 __in DWORD dwMessage,
26 __in_bcount_opt(cbData) LPVOID pvData,
27 __in SIZE_T cbData
28 );
29static HRESULT GetPipeMessage(
30 __in HANDLE hPipe,
31 __in BURN_PIPE_MESSAGE* pMsg
32 );
33static HRESULT ChildPipeConnected(
34 __in HANDLE hPipe,
35 __in_z LPCWSTR wzSecret,
36 __inout DWORD* pdwProcessId
37 );
38
39
40
41/*******************************************************************
42 PipeConnectionInitialize - initialize pipe connection data.
43
44*******************************************************************/
45void PipeConnectionInitialize(
46 __in BURN_PIPE_CONNECTION* pConnection
47 )
48{
49 memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
50 pConnection->hPipe = INVALID_HANDLE_VALUE;
51 pConnection->hCachePipe = INVALID_HANDLE_VALUE;
52 pConnection->hLoggingPipe = INVALID_HANDLE_VALUE;
53}
54
55/*******************************************************************
56 PipeConnectionUninitialize - free data in a pipe connection.
57
58*******************************************************************/
59void PipeConnectionUninitialize(
60 __in BURN_PIPE_CONNECTION* pConnection
61 )
62{
63 ReleaseFileHandle(pConnection->hLoggingPipe);
64 ReleaseFileHandle(pConnection->hCachePipe);
65 ReleaseFileHandle(pConnection->hPipe);
66 ReleaseHandle(pConnection->hProcess);
67 ReleaseStr(pConnection->sczSecret);
68 ReleaseStr(pConnection->sczName);
69
70 PipeConnectionInitialize(pConnection);
71}
72
73/*******************************************************************
74 PipeSendMessage -
75
76*******************************************************************/
77extern "C" HRESULT PipeSendMessage(
78 __in HANDLE hPipe,
79 __in DWORD dwMessage,
80 __in_bcount_opt(cbData) LPVOID pvData,
81 __in SIZE_T cbData,
82 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
83 __in_opt LPVOID pvContext,
84 __out DWORD* pdwResult
85 )
86{
87 HRESULT hr = S_OK;
88 BURN_PIPE_RESULT result = { };
89
90 hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData);
91 ExitOnFailure(hr, "Failed to write send message to pipe.");
92
93 hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result);
94 ExitOnFailure(hr, "Failed to pump messages during send message to pipe.");
95
96 *pdwResult = result.dwResult;
97
98LExit:
99 return hr;
100}
101
102/*******************************************************************
103 PipePumpMessages -
104
105*******************************************************************/
106extern "C" HRESULT PipePumpMessages(
107 __in HANDLE hPipe,
108 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
109 __in_opt LPVOID pvContext,
110 __in BURN_PIPE_RESULT* pResult
111 )
112{
113 HRESULT hr = S_OK;
114 BURN_PIPE_MESSAGE msg = { };
115 SIZE_T iData = 0;
116 LPSTR sczMessage = NULL;
117 DWORD dwResult = 0;
118
119 // Pump messages from child process.
120 while (S_OK == (hr = GetPipeMessage(hPipe, &msg)))
121 {
122 switch (msg.dwMessage)
123 {
124 case BURN_PIPE_MESSAGE_TYPE_LOG:
125 iData = 0;
126
127 hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage);
128 ExitOnFailure(hr, "Failed to read log message.");
129
130 hr = LogStringWorkRaw(sczMessage);
131 ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage);
132
133 dwResult = static_cast<DWORD>(hr);
134 break;
135
136 case BURN_PIPE_MESSAGE_TYPE_COMPLETE:
137 if (!msg.pvData || sizeof(DWORD) != msg.cbData)
138 {
139 hr = E_INVALIDARG;
140 ExitOnRootFailure(hr, "No status returned to PipePumpMessages()");
141 }
142
143 pResult->dwResult = *static_cast<DWORD*>(msg.pvData);
144 ExitFunction1(hr = S_OK); // exit loop.
145
146 case BURN_PIPE_MESSAGE_TYPE_TERMINATE:
147 iData = 0;
148
149 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult);
150 ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()");
151
152 if (sizeof(DWORD) * 2 == msg.cbData)
153 {
154 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart);
155 ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()");
156 }
157
158 ExitFunction1(hr = S_OK); // exit loop.
159
160 default:
161 if (pfnCallback)
162 {
163 hr = pfnCallback(&msg, pvContext, &dwResult);
164 }
165 else
166 {
167 hr = E_INVALIDARG;
168 }
169 ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage);
170 break;
171 }
172
173 // post result
174 hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult));
175 ExitOnFailure(hr, "Failed to post result to child process.");
176
177 FreePipeMessage(&msg);
178 }
179 ExitOnFailure(hr, "Failed to get message over pipe");
180
181 if (S_FALSE == hr)
182 {
183 hr = S_OK;
184 }
185
186LExit:
187 ReleaseStr(sczMessage);
188 FreePipeMessage(&msg);
189
190 return hr;
191}
192
193/*******************************************************************
194 PipeCreateNameAndSecret -
195
196*******************************************************************/
197extern "C" HRESULT PipeCreateNameAndSecret(
198 __out_z LPWSTR *psczConnectionName,
199 __out_z LPWSTR *psczSecret
200 )
201{
202 HRESULT hr = S_OK;
203 WCHAR wzGuid[GUID_STRING_LENGTH];
204 LPWSTR sczConnectionName = NULL;
205 LPWSTR sczSecret = NULL;
206
207 // Create the unique pipe name.
208 hr = GuidFixedCreate(wzGuid);
209 ExitOnRootFailure(hr, "Failed to create pipe guid.");
210
211 hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid);
212 ExitOnFailure(hr, "Failed to allocate pipe name.");
213
214 // Create the unique client secret.
215 hr = GuidFixedCreate(wzGuid);
216 ExitOnRootFailure(hr, "Failed to create pipe secret.");
217
218 hr = StrAllocString(&sczSecret, wzGuid, 0);
219 ExitOnFailure(hr, "Failed to allocate pipe secret.");
220
221 *psczConnectionName = sczConnectionName;
222 sczConnectionName = NULL;
223 *psczSecret = sczSecret;
224 sczSecret = NULL;
225
226LExit:
227 ReleaseStr(sczSecret);
228 ReleaseStr(sczConnectionName);
229
230 return hr;
231}
232
233/*******************************************************************
234 PipeCreatePipes - create the pipes and event to signal child process.
235
236*******************************************************************/
237extern "C" HRESULT PipeCreatePipes(
238 __in BURN_PIPE_CONNECTION* pConnection,
239 __in BOOL fCompanion
240 )
241{
242 Assert(pConnection->sczName);
243 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
244 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
245 Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe);
246
247 HRESULT hr = S_OK;
248 PSECURITY_DESCRIPTOR psd = NULL;
249 SECURITY_ATTRIBUTES sa = { };
250 LPWSTR sczFullPipeName = NULL;
251 HANDLE hPipe = INVALID_HANDLE_VALUE;
252 HANDLE hCachePipe = INVALID_HANDLE_VALUE;
253 HANDLE hLoggingPipe = INVALID_HANDLE_VALUE;
254
255 // Only grant special rights when the pipe is being used for "embedded" scenarios.
256 if (!fCompanion)
257 {
258 // Create the security descriptor that grants read/write/sync access to Everyone.
259 // TODO: consider locking down "WD" to LogonIds (logon session)
260 LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)";
261 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL))
262 {
263 ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe.");
264 }
265
266 sa.nLength = sizeof(sa);
267 sa.lpSecurityDescriptor = psd;
268 sa.bInheritHandle = FALSE;
269 }
270
271 // Create the pipe.
272 hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
273 ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName);
274
275 // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such.
276 hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL);
277 if (INVALID_HANDLE_VALUE == hPipe)
278 {
279 ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
280 }
281
282 if (fCompanion)
283 {
284 // Create the cache pipe.
285 hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
286 ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName);
287
288 hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL);
289 if (INVALID_HANDLE_VALUE == hCachePipe)
290 {
291 ExitWithLastError(hr, "Failed to create cache pipe: %ls", sczFullPipeName);
292 }
293
294 // Create the logging pipe.
295 hr = StrAllocFormatted(&sczFullPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
296 ExitOnFailure(hr, "Failed to allocate full name of logging pipe: %ls", pConnection->sczName);
297
298 hLoggingPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL);
299 if (INVALID_HANDLE_VALUE == hLoggingPipe)
300 {
301 ExitWithLastError(hr, "Failed to create logging pipe: %ls", sczFullPipeName);
302 }
303 }
304
305 pConnection->hLoggingPipe = hLoggingPipe;
306 hLoggingPipe = INVALID_HANDLE_VALUE;
307
308 pConnection->hCachePipe = hCachePipe;
309 hCachePipe = INVALID_HANDLE_VALUE;
310
311 pConnection->hPipe = hPipe;
312 hPipe = INVALID_HANDLE_VALUE;
313
314LExit:
315 ReleaseFileHandle(hLoggingPipe);
316 ReleaseFileHandle(hCachePipe);
317 ReleaseFileHandle(hPipe);
318 ReleaseStr(sczFullPipeName);
319
320 if (psd)
321 {
322 ::LocalFree(psd);
323 }
324
325 return hr;
326}
327
328/*******************************************************************
329 PipeWaitForChildConnect -
330
331*******************************************************************/
332extern "C" HRESULT PipeWaitForChildConnect(
333 __in BURN_PIPE_CONNECTION* pConnection
334 )
335{
336 HRESULT hr = S_OK;
337 HANDLE hPipes[3] = { pConnection->hPipe, pConnection->hCachePipe, pConnection->hLoggingPipe};
338 LPCWSTR wzSecret = pConnection->sczSecret;
339 DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR);
340 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
341 DWORD dwAck = 0;
342
343 for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i)
344 {
345 HANDLE hPipe = hPipes[i];
346 DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT;
347
348 // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever
349 // if the child decides not to show up.
350 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
351 {
352 ExitWithLastError(hr, "Failed to set pipe to non-blocking.");
353 }
354
355 // Loop for a while waiting for a connection from child process.
356 DWORD cRetry = 0;
357 do
358 {
359 if (!::ConnectNamedPipe(hPipe, NULL))
360 {
361 DWORD er = ::GetLastError();
362 if (ERROR_PIPE_CONNECTED == er)
363 {
364 hr = S_OK;
365 break;
366 }
367 else if (ERROR_PIPE_LISTENING == er)
368 {
369 if (cRetry < PIPE_RETRY_FOR_CONNECTION)
370 {
371 hr = HRESULT_FROM_WIN32(er);
372 }
373 else
374 {
375 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
376 break;
377 }
378
379 ++cRetry;
380 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
381 }
382 else
383 {
384 hr = HRESULT_FROM_WIN32(er);
385 break;
386 }
387 }
388 } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr);
389 ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe.");
390
391 // Put the pipe back in blocking mode.
392 dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT;
393 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
394 {
395 ExitWithLastError(hr, "Failed to reset pipe to blocking.");
396 }
397
398 // Prove we are the one that created the elevated process by passing the secret.
399 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&cbSecret), sizeof(cbSecret));
400 ExitOnFailure(hr, "Failed to write secret length to pipe.");
401
402 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(wzSecret), cbSecret);
403 ExitOnFailure(hr, "Failed to write secret to pipe.");
404
405 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwCurrentProcessId), sizeof(dwCurrentProcessId));
406 ExitOnFailure(hr, "Failed to write our process id to pipe.");
407
408 // Wait until the elevated process responds that it is ready to go.
409 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwAck), sizeof(dwAck));
410 ExitOnFailure(hr, "Failed to read ACK from pipe.");
411
412 // The ACK should match out expected child process id.
413 //if (pConnection->dwProcessId != dwAck)
414 //{
415 // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
416 // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck);
417 //}
418 }
419
420LExit:
421 return hr;
422}
423
424/*******************************************************************
425 PipeTerminateLoggingPipe -
426
427*******************************************************************/
428extern "C" HRESULT PipeTerminateLoggingPipe(
429 __in HANDLE hLoggingPipe,
430 __in DWORD dwParentExitCode
431 )
432{
433 HRESULT hr = S_OK;
434 BYTE* pbData = NULL;
435 SIZE_T cbData = 0;
436
437 // Prepare the exit message.
438 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
439 ExitOnFailure(hr, "Failed to write exit code to message buffer.");
440
441 hr = WritePipeMessage(hLoggingPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), pbData, cbData);
442 ExitOnFailure(hr, "Failed to post complete message to logging pipe.");
443
444LExit:
445 ReleaseBuffer(pbData);
446
447 return hr;
448}
449
450/*******************************************************************
451 PipeTerminateChildProcess -
452
453*******************************************************************/
454extern "C" HRESULT PipeTerminateChildProcess(
455 __in BURN_PIPE_CONNECTION* pConnection,
456 __in DWORD dwParentExitCode,
457 __in BOOL fRestart
458 )
459{
460 HRESULT hr = S_OK;
461 BYTE* pbData = NULL;
462 SIZE_T cbData = 0;
463 BOOL fTimedOut = FALSE;
464
465 // Prepare the exit message.
466 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
467 ExitOnFailure(hr, "Failed to write exit code to message buffer.");
468
469 hr = BuffWriteNumber(&pbData, &cbData, fRestart);
470 ExitOnFailure(hr, "Failed to write restart to message buffer.");
471
472 // Send the messages.
473 if (INVALID_HANDLE_VALUE != pConnection->hCachePipe)
474 {
475 hr = WritePipeMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
476 ExitOnFailure(hr, "Failed to post terminate message to child process cache thread.");
477 }
478
479 hr = WritePipeMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
480 ExitOnFailure(hr, "Failed to post terminate message to child process.");
481
482 // If we were able to get a handle to the other process, wait for it to exit.
483 if (pConnection->hProcess)
484 {
485 hr = AppWaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION);
486 ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for child process exit.");
487
488 AssertSz(!fTimedOut, "Timed out while waiting for child process to exit.");
489 }
490
491#ifdef DEBUG
492 if (pConnection->hProcess && !fTimedOut)
493 {
494 DWORD dwChildExitCode = 0;
495 HRESULT hrDebug = S_OK;
496
497 hrDebug = CoreWaitForProcCompletion(pConnection->hProcess, 0, &dwChildExitCode);
498 if (E_ACCESSDENIED != hrDebug && FAILED(hrDebug)) // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
499 {
500 TraceError(hrDebug, "Failed to wait for child process completion.");
501 }
502
503 AssertSz(E_ACCESSDENIED == hrDebug || dwChildExitCode == dwParentExitCode,
504 "Child elevated process did not return matching exit code to parent process.");
505 }
506#endif
507
508LExit:
509 ReleaseBuffer(pbData);
510
511 return hr;
512}
513
514/*******************************************************************
515 PipeChildConnect - Called from the child process to connect back
516 to the pipe provided by the parent process.
517
518*******************************************************************/
519extern "C" HRESULT PipeChildConnect(
520 __in BURN_PIPE_CONNECTION* pConnection,
521 __in BOOL fCompanion
522 )
523{
524 Assert(pConnection->sczName);
525 Assert(pConnection->sczSecret);
526 Assert(!pConnection->hProcess);
527 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
528 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
529 Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe);
530
531 HRESULT hr = S_OK;
532 LPWSTR sczPipeName = NULL;
533
534 // Try to connect to the parent.
535 hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
536 ExitOnFailure(hr, "Failed to allocate name of parent pipe.");
537
538 hr = E_UNEXPECTED;
539 for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
540 {
541 pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
542 if (INVALID_HANDLE_VALUE == pConnection->hPipe)
543 {
544 hr = HRESULT_FROM_WIN32(::GetLastError());
545 if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
546 {
547 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
548 }
549
550 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
551 }
552 else // we have a connection, go with it.
553 {
554 hr = S_OK;
555 }
556 }
557 ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName)
558
559 // Verify the parent and notify it that the child connected.
560 hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId);
561 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
562
563 if (fCompanion)
564 {
565 // Connect to the parent for the cache pipe.
566 hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
567 ExitOnFailure(hr, "Failed to allocate name of parent cache pipe.");
568
569 pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
570 if (INVALID_HANDLE_VALUE == pConnection->hCachePipe)
571 {
572 ExitWithLastError(hr, "Failed to open parent cache pipe: %ls", sczPipeName)
573 }
574
575 // Verify the parent and notify it that the child connected.
576 hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId);
577 ExitOnFailure(hr, "Failed to verify parent cache pipe: %ls", sczPipeName);
578
579 // Connect to the parent for the logging pipe.
580 hr = StrAllocFormatted(&sczPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
581 ExitOnFailure(hr, "Failed to allocate name of parent logging pipe.");
582
583 pConnection->hLoggingPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
584 if (INVALID_HANDLE_VALUE == pConnection->hLoggingPipe)
585 {
586 ExitWithLastError(hr, "Failed to open parent logging pipe: %ls", sczPipeName)
587 }
588
589 // Verify the parent and notify it that the child connected.
590 hr = ChildPipeConnected(pConnection->hLoggingPipe, pConnection->sczSecret, &pConnection->dwProcessId);
591 ExitOnFailure(hr, "Failed to verify parent logging pipe: %ls", sczPipeName);
592 }
593
594 pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId);
595 ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId);
596
597LExit:
598 ReleaseStr(sczPipeName);
599
600 return hr;
601}
602
603
604static HRESULT AllocatePipeMessage(
605 __in DWORD dwMessage,
606 __in_bcount_opt(cbData) LPVOID pvData,
607 __in SIZE_T cbData,
608 __out_bcount(cb) LPVOID* ppvMessage,
609 __out SIZE_T* pcbMessage
610 )
611{
612 HRESULT hr = S_OK;
613 LPVOID pv = NULL;
614 size_t cb = 0;
615 DWORD dwcbData = 0;
616
617 // If no data was provided, ensure the count of bytes is zero.
618 if (!pvData)
619 {
620 cbData = 0;
621 }
622 else if (MAXDWORD < cbData)
623 {
624 ExitWithRootFailure(hr, E_INVALIDDATA, "Pipe message is too large.");
625 }
626
627 hr = ::SizeTAdd(sizeof(dwMessage) + sizeof(dwcbData), cbData, &cb);
628 ExitOnRootFailure(hr, "Failed to calculate total pipe message size");
629
630 dwcbData = (DWORD)cbData;
631
632 // Allocate the message.
633 pv = MemAlloc(cb, FALSE);
634 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message.");
635
636 memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage));
637 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &dwcbData, sizeof(dwcbData));
638 if (dwcbData)
639 {
640 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage) + sizeof(dwcbData), cb - sizeof(dwMessage) - sizeof(dwcbData), pvData, dwcbData);
641 }
642
643 *pcbMessage = cb;
644 *ppvMessage = pv;
645 pv = NULL;
646
647LExit:
648 ReleaseMem(pv);
649 return hr;
650}
651
652static void FreePipeMessage(
653 __in BURN_PIPE_MESSAGE *pMsg
654 )
655{
656 if (pMsg->fAllocatedData)
657 {
658 ReleaseNullMem(pMsg->pvData);
659 pMsg->fAllocatedData = FALSE;
660 }
661}
662
663static HRESULT WritePipeMessage(
664 __in HANDLE hPipe,
665 __in DWORD dwMessage,
666 __in_bcount_opt(cbData) LPVOID pvData,
667 __in SIZE_T cbData
668 )
669{
670 HRESULT hr = S_OK;
671 LPVOID pv = NULL;
672 SIZE_T cb = 0;
673
674 hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb);
675 ExitOnFailure(hr, "Failed to allocate message to write.");
676
677 // Write the message.
678 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(pv), cb);
679 ExitOnFailure(hr, "Failed to write message type to pipe.");
680
681LExit:
682 ReleaseMem(pv);
683 return hr;
684}
685
686static HRESULT GetPipeMessage(
687 __in HANDLE hPipe,
688 __in BURN_PIPE_MESSAGE* pMsg
689 )
690{
691 HRESULT hr = S_OK;
692 BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(DWORD)] = { };
693
694 hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount));
695 if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr)
696 {
697 memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount));
698 hr = S_FALSE;
699 }
700 ExitOnFailure(hr, "Failed to read message from pipe.");
701
702 pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount);
703 pMsg->cbData = *(DWORD*)(pbMessageAndByteCount + sizeof(DWORD));
704 if (pMsg->cbData)
705 {
706 pMsg->pvData = MemAlloc(pMsg->cbData, FALSE);
707 ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message.");
708
709 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(pMsg->pvData), pMsg->cbData);
710 ExitOnFailure(hr, "Failed to read data for message.");
711
712 pMsg->fAllocatedData = TRUE;
713 }
714
715LExit:
716 if (!pMsg->fAllocatedData && pMsg->pvData)
717 {
718 MemFree(pMsg->pvData);
719 }
720
721 return hr;
722}
723
724static HRESULT ChildPipeConnected(
725 __in HANDLE hPipe,
726 __in_z LPCWSTR wzSecret,
727 __inout DWORD* pdwProcessId
728 )
729{
730 HRESULT hr = S_OK;
731 LPWSTR sczVerificationSecret = NULL;
732 DWORD cbVerificationSecret = 0;
733 DWORD dwVerificationProcessId = 0;
734 DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK.
735
736 // Read the verification secret.
737 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&cbVerificationSecret), sizeof(cbVerificationSecret));
738 ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe.");
739
740 if (255 < cbVerificationSecret / sizeof(WCHAR))
741 {
742 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
743 ExitOnRootFailure(hr, "Verification secret from parent is too big.");
744 }
745
746 hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1);
747 ExitOnFailure(hr, "Failed to allocate buffer for verification secret.");
748
749 FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(sczVerificationSecret), cbVerificationSecret);
750 ExitOnFailure(hr, "Failed to read verification secret from parent pipe.");
751
752 // Verify the secrets match.
753 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1))
754 {
755 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
756 ExitOnRootFailure(hr, "Verification secret from parent does not match.");
757 }
758
759 // Read the verification process id.
760 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwVerificationProcessId), sizeof(dwVerificationProcessId));
761 ExitOnFailure(hr, "Failed to read verification process id from parent pipe.");
762
763 // If a process id was not provided, we'll trust the process id from the parent.
764 if (*pdwProcessId == 0)
765 {
766 *pdwProcessId = dwVerificationProcessId;
767 }
768 else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match.
769 {
770 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
771 ExitOnRootFailure(hr, "Verification process id from parent does not match.");
772 }
773
774 // All is well, tell the parent process.
775 // TODO: consider sending BURN_PROTOCOL_VERSION as a way to verify compatibility.
776 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwAck), sizeof(dwAck));
777 ExitOnFailure(hr, "Failed to inform parent process that child is running.");
778
779LExit:
780 ReleaseStr(sczVerificationSecret);
781 return hr;
782}